Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade feature Intended for #207

Merged
merged 13 commits into from
Jun 12, 2023
47 changes: 26 additions & 21 deletions dcm2bids/acquisition.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ def __init__(
participant,
dataType,
modalityLabel,
indexSidecar=None,
customLabels="",
id=None,
srcSidecar=None,
sidecarChanges=None,
intendedFor=None,
Expand All @@ -38,9 +38,9 @@ def __init__(

self._modalityLabel = ""
self._customLabels = ""
self._id = ""
self._intendedFor = None
self._indexSidecar = None


self.participant = participant
self.dataType = dataType
self.modalityLabel = modalityLabel
Expand All @@ -57,6 +57,11 @@ def __init__(
else:
self.intendedFor = intendedFor

if id is None:
self.id = None
else:
self.id = id

self.dstFile = ''

def __eq__(self, other):
Expand All @@ -79,6 +84,18 @@ def modalityLabel(self, modalityLabel):
""" Prepend '_' if necessary"""
self._modalityLabel = self.prepend(modalityLabel)

@property
def id(self):
"""
Returns:
A string '_<id>'
"""
return self._id

@id.setter
def id(self, value):
self._id = value

@property
def customLabels(self):
"""
Expand Down Expand Up @@ -191,23 +208,7 @@ def intendedFor(self, value):
else:
self._intendedFor = [value]

@property
def indexSidecar(self):
"""
Returns:
A int '_<indexSidecar>'
"""
return self._indexSidecar

@indexSidecar.setter
def indexSidecar(self, value):
"""
Returns:
A int '_<indexSidecar>'
"""
self._indexSidecar = value

def dstSidecarData(self, descriptions, intendedForList):
def dstSidecarData(self, intendedForList):
"""
"""
data = self.srcSidecar.origData
Expand All @@ -218,7 +219,11 @@ def dstSidecarData(self, descriptions, intendedForList):
intendedValue = []

for index in self.intendedFor:
intendedValue = intendedValue + intendedForList[index]
if (isinstance(index, int)):
logging.error('Dcm2bids (>=3.0.0) does not support indexing anymore for intendedFor field.\n'
f'Please check {DEFAULT.link_doc_intended_for}')
else:
intendedValue = intendedValue + intendedForList[index]

if len(intendedValue) == 1:
data["IntendedFor"] = intendedValue[0]
Expand Down
29 changes: 17 additions & 12 deletions dcm2bids/dcm2bids_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
Reorganising NIfTI files from dcm2niix into the Brain Imaging Data Structure
"""

import argparse
import logging
import os
from pathlib import Path
Expand Down Expand Up @@ -133,10 +132,10 @@ def run(self):

self.logger.info("moving acquisitions into BIDS folder")

intendedForList = [[] for i in range(len(parser.descriptions))]
intendedForList = {}
for acq in parser.acquisitions:
acq.setDstFile()
intendedForList = self.move(acq, intendedForList)
self.move(acq, intendedForList, dcm2niix.options)

if self.bids_validate:
try:
Expand All @@ -149,10 +148,9 @@ def run(self):
"bids-validator may not been installed on your computer"
f"Please check: https://github.com/bids-standard/bids-validator#quickstart")

def move(self, acquisition, intendedForList):
def move(self, acquisition, intendedForList, dcm2niix_options):
"""Move an acquisition to BIDS format"""
for srcFile in glob(acquisition.srcRoot + ".*"):

ext = Path(srcFile).suffixes
ext = [curr_ext for curr_ext in ext if curr_ext in ['.nii', '.gz',
'.json',
Expand All @@ -174,7 +172,7 @@ def move(self, acquisition, intendedForList):
continue

# it's an anat nifti file and the user using a deface script
if (self.config.get("defaceTpl") and acquisition.dataType == "func" and ".nii" in ext):
if (self.config.get("defaceTpl") and acquisition.dataType == "anat" and ".nii" in ext):
try:
os.remove(dstFile)
except FileNotFoundError:
Expand All @@ -185,20 +183,27 @@ def move(self, acquisition, intendedForList):
cmd = [w.replace('dstFile', dstFile) for w in defaceTpl]
run_shell_command(cmd)

intendedForList[acquisition.indexSidecar].append(acquisition.dstIntendedFor + "".join(ext))
intendedForList[acquisition.id] = acquisition.dstIntendedFor + "".join(ext)

elif ".json" in ext:
data = acquisition.dstSidecarData(self.config["descriptions"],
intendedForList)
data = acquisition.dstSidecarData(intendedForList)
save_json(dstFile, data)
os.remove(srcFile)

# just move
else:
os.rename(srcFile, dstFile)

intendedFile = acquisition.dstIntendedFor + ".nii.gz"
if intendedFile not in intendedForList[acquisition.indexSidecar]:
intendedForList[acquisition.indexSidecar].append(intendedFile)
curr_img_ext = '.nii.gz'
if '-z n' in dcm2niix_options:
curr_img_ext = '.nii'

intendedFile = acquisition.dstIntendedFor + curr_img_ext
if acquisition.id:
if acquisition.id in intendedForList:
if intendedFile not in intendedForList[acquisition.id]:
intendedForList[acquisition.id] = intendedForList[acquisition.id] + intendedFile
else:
intendedForList[acquisition.id] = [intendedFile]

return intendedForList
2 changes: 0 additions & 2 deletions dcm2bids/sidecar.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ def build_acquisitions(self, participant):
desc = valid_descriptions[0]
acq = Acquisition(participant,
srcSidecar=sidecar, **desc)
acq.indexSidecar = self.descriptions.index(desc)
acq.setDstFile()

if acq.intendedFor != [None]:
Expand All @@ -245,7 +244,6 @@ def build_acquisitions(self, participant):
self.logger.warning("Several Pairing <- %s", sidecarName)
for desc in valid_descriptions:
acq = Acquisition(participant,
indexSidecar=self.descriptions.index(desc),
**desc)
self.logger.warning(" -> %s", acq.suffix)

Expand Down
3 changes: 2 additions & 1 deletion dcm2bids/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ class DEFAULT(object):
doc = "Documentation at https://unfmontreal.github.io/Dcm2Bids/"

link_bids_validator = "https://github.com/bids-standard/bids-validator#quickstart"

link_doc_intended_for = "https://unfmontreal.github.io/Dcm2Bids/docs/tutorial/first-steps/#populating-the-config-file"

# cli dcm2bids
cliSession = ""
cliOutputDir = os.getcwd()
Expand Down
18 changes: 9 additions & 9 deletions docs/how-to/create-config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
}
},
{
"id": "task-rest",
"dataType": "func",
"modalityLabel": "bold",
"customLabels": "task-rest",
Expand All @@ -28,12 +29,13 @@
{
"dataType": "fmap",
"modalityLabel": "fmap",
"intendedFor": 1,
"intendedFor": "task_rest",
"criteria": {
"ProtocolName": "*field_mapping*"
}
},
{
"id": "id_task_learning",
"dataType": "func",
"modalityLabel": "bold",
"customLabels": "task-learning",
Expand All @@ -50,7 +52,7 @@
"criteria": {
"SeriesDescription": "fmap_task-learning"
},
"IntendedFor": 2,
"IntendedFor": "id_task_learning",
"sidecarChanges": {
"TaskName": "learning"
}
Expand Down Expand Up @@ -148,13 +150,11 @@ Optional field to change or add information in a sidecar.

## intendedFor

Optional field to add an `IntendedFor` entry in the sidecar of a fieldmap. Just
put the index or a list of indices of the description(s) that's intended for.

Python index begins at `0` so in the example, **`1`** means it is intended for
`task-rest_bold` and **`2`** is intended for `task-learning` which will be
renamed to only `learning` because of the
`"sidecarChanges": { "TaskName": "learning" }` field.
Optional field to add an `IntendedFor` entry in the sidecar of a fieldmap.
You will need to set an id to the corresponding description and put the same id in the `IntendedFor` field.
Fo example, **`task_rest`** means it is intended for `task-rest_bold`
and **`id_task_learning`** is intended for `task-learning` which will be
renamed to only `learning` because of the `"sidecarChanges": { "TaskName": "learning" }` field.

## Multiple config files

Expand Down
13 changes: 7 additions & 6 deletions docs/tutorial/first-steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ good unique identifier.
=== "Command"

```sh
cat code/dcm2bids_config.json
cat tmp_dcm2bids/helper/004_In_DCM2NIIX_regression_test_20180918114023.json
```

=== "Output"
Expand Down Expand Up @@ -807,11 +807,11 @@ task name:
"modalityLabel": "bold",
"customLabels": "task-rest",
"criteria": {
"SeriesDescription": "Axial EPI-FMRI (Interleaved I to S)*",
"sidecarChanges": {
"SeriesDescription": "Axial EPI-FMRI (Interleaved I to S)*"
},
"sidecarChanges": {
"TaskName": "rest"
}
}
}
]
}
Expand Down Expand Up @@ -887,6 +887,7 @@ file with the appropriate info.
{
"descriptions": [
{
"id": "id_task-rest",
"dataType": "func",
"modalityLabel": "bold",
"customLabels": "task-rest",
Expand All @@ -904,7 +905,7 @@ file with the appropriate info.
"criteria": {
"SeriesDescription": "EPI PE=AP*"
},
"intendedFor": 0
"intendedFor": "id_task-rest"
},
{
"dataType": "fmap",
Expand All @@ -913,7 +914,7 @@ file with the appropriate info.
"criteria": {
"SeriesDescription": "EPI PE=PA*"
},
"intendedFor": 0
"intendedFor": "id_task-rest"
}
]
}
Expand Down
7 changes: 5 additions & 2 deletions tests/data/config_test.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
}
},
{
"id": "func_task-rest",
"dataType": "func",
"modalityLabel": "bold",
"customLabels": "task-rest",
Expand All @@ -21,13 +22,15 @@
}
},
{
"id": "T1",
"dataType": "anat",
"modalityLabel": "T1w",
"criteria": {
"SidecarFilename": "*MPRAGE*"
}
},
{
"id": "id_dwi",
"dataType": "dwi",
"modalityLabel": "dwi",
"criteria": {
Expand All @@ -42,7 +45,7 @@
"EchoNumber": 1,
"EchoTime": 0.00492
},
"intendedFor": [3,2]
"intendedFor": ["id_dwi", "T1"]
},
{
"dataType": "fmap",
Expand All @@ -53,7 +56,7 @@
"EchoTime": 0.00738,
"ImageType": ["ORIGINAL", "PRIMARY", "M", "ND"]
},
"IntendedFor": 3
"IntendedFor": "id_dwi"
},
{
"dataType": "dwi",
Expand Down