Skip to content

Commit

Permalink
generalization of defaceTpl to postOp
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaudbore committed Jul 4, 2023
1 parent 14f9143 commit 5812b5c
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 34 deletions.
26 changes: 12 additions & 14 deletions dcm2bids/dcm2bids_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ def run(self):
self.auto_extract_entities,
self.config.get("searchMethod", DEFAULT.searchMethod),
self.config.get("caseSensitive", DEFAULT.caseSensitive),
self.config.get("dupMethod", DEFAULT.dupMethod)
self.config.get("dupMethod", DEFAULT.dupMethod),
self.config.get("postOp", DEFAULT.postOp)
)
parser.build_graph()
parser.build_acquisitions(self.participant)
Expand All @@ -107,7 +108,7 @@ def run(self):

idList = {}
for acq in parser.acquisitions:
idList = self.move(acq, idList)
idList = self.move(acq, idList, parser.postOp)

if self.bids_validate:
try:
Expand All @@ -121,7 +122,7 @@ def run(self):
"computer. Please check: "
"https://github.com/bids-standard/bids-validator.")

def move(self, acquisition, idList):
def move(self, acquisition, idList, postOp):
"""Move an acquisition to BIDS format"""
for srcFile in glob(acquisition.srcRoot + ".*"):
ext = Path(srcFile).suffixes
Expand Down Expand Up @@ -151,18 +152,15 @@ def move(self, acquisition, idList):
else:
idList[acquisition.id] = [acquisition.dstId + "".join(ext)]

if (self.config.get("defaceTpl") and acquisition.datatype == "anat" and ".nii" in ext):
try:
os.remove(dstFile)
except FileNotFoundError:
pass
defaceTpl = self.config.get("defaceTpl")
for curr_postOp in postOp:
if acquisition.datatype in curr_postOp['datatype'] or 'any' in curr_postOp['datatype']:
if acquisition.suffix in curr_postOp['suffix'] or '_any' in curr_postOp['suffix']:
cmd = curr_postOp['cmd'].replace('srcFile', str(srcFile))
cmd = cmd.replace('dstFile', str(dstFile))
run_shell_command(cmd.split())
break

cmd = [w.replace('srcFile', srcFile) for w in defaceTpl]
cmd = [w.replace('dstFile', dstFile) for w in defaceTpl]
run_shell_command(cmd)

elif ".json" in ext:
if ".json" in ext:
data = acquisition.dstSidecarData(idList)
save_json(dstFile, data)
os.remove(srcFile)
Expand Down
47 changes: 46 additions & 1 deletion dcm2bids/sidecar.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,12 @@ def __init__(self,
auto_extractor=DEFAULT.auto_extract_entities,
searchMethod=DEFAULT.searchMethod,
caseSensitive=DEFAULT.caseSensitive,
dupMethod=DEFAULT.dupMethod):
dupMethod=DEFAULT.dupMethod,
postOp=DEFAULT.postOp):
self.logger = logging.getLogger(__name__)
self._searchMethod = ""
self._dupMethod = ""
self._postOp = ""
self.graph = OrderedDict()
self.acquisitions = []
self.extractors = extractors
Expand All @@ -109,6 +111,7 @@ def __init__(self,
self.searchMethod = searchMethod
self.caseSensitive = caseSensitive
self.dupMethod = dupMethod
self.postOp = postOp

@property
def searchMethod(self):
Expand Down Expand Up @@ -150,6 +153,48 @@ def dupMethod(self, value):
self.logger.warning(f"{value} is not a duplicate method implemented.")
self.logger.warning(f"Falling back to default: {DEFAULT.dupMethod}.")

@property
def postOp(self):
return self._postOp

@postOp.setter
def postOp(self, value):
"""
Checks if postOp commands don't overlap
"""
postOp = []
try:
pairs = []
for curr_postOp in value:
postOp.append(curr_postOp)
datatype = curr_postOp['datatype']
suffix = curr_postOp['suffix']

if isinstance(datatype, str):
postOp[-1]['datatype'] = [datatype]
datatype = [datatype]
if isinstance(suffix, str):
# It will be compare with acq.suffix which has a `_` character
postOp[-1]['suffix'] = ['_' + suffix]
suffix = [suffix]
else:
postOp[-1]['suffix'] = ['_' + curr_suffix for curr_suffix in suffix]

pairs = pairs + list(itertools.product(datatype, suffix))

res = list(set([ele for ele in pairs if pairs.count(ele) > 1]))
if res:
raise ValueError("Some post operations apply on "
"the same combination of datatype/suffix."
"Please fix postOp key in your config file."
f"{pairs}")

self._postOp = postOp

except Exception:
raise ValueError("postOp is not defined correctly."
"Please check the documentation.")

@property
def caseSensitive(self):
return self._caseSensitive
Expand Down
2 changes: 1 addition & 1 deletion dcm2bids/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class DEFAULT(object):
auto_extract_entities = False
clobber = False
force_dcm2niix = False
defaceTpl = None
postOp = []
logLevel = "WARNING"

entity_dir = {"j-": "AP",
Expand Down
3 changes: 0 additions & 3 deletions docs/get-started/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,6 @@ options:
--clobber Overwrite output if it exists
-l {DEBUG,INFO,WARNING,ERROR,CRITICAL}, --log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
Set logging level
-a, --anonymizer This option no longer exists from the script in this
release. See:https://github.com/unfmontreal/Dcm2Bids/blob/m
aster/README.md#defaceTpl
Documentation at https://github.com/unfmontreal/Dcm2Bids
```
Expand Down
38 changes: 29 additions & 9 deletions docs/how-to/use-advanced-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ same level as the `"descriptions"` entry.
"BodyPartExamined": ["(?P<bodypart>[a-zA-Z]+)"]},
"searchMethod": "fnmatch",
"caseSensitive": true,
"defaceTpl": ["pydeface", "--outfile", "dstFile", "srcFile"],
"postOp": {"cmd": "pydeface --outfile dstFile srcFile",
"datatype": "anat",
"suffix": ["T1w", "MP2RAGE"]},
"description": [
{
"datatype": "anat",
Expand Down Expand Up @@ -48,18 +50,37 @@ default: `"caseSensitive": "true"`
If false, comparisons between strings/lists will be not case sensitive. It's
only disabled when used with `"searchMethod": "fnmatch"`.

## defaceTpl
## postOp

default: `"defaceTpl": None`
default: `"postOp": []`

!!! danger The anonymizer option no longer exists from `v2.0.0`. It is still
possible to deface the anatomical nifti images.
postOp key allows you to run any post-processing analyses just before being moved
to there respective folders.

For example, if you use the last version of pydeface, add:
For example, if you want to deface your T1w images you could use pydeface by adding:
```
"postOp": [{"cmd": "pydeface --outfile dstFile srcFile",
"datatype": "anat",
"suffix": ["T1w", "MP2RAGE"]}],
```

It will specifically run the corresponding `cmd` to any image that follow the combinations
datatype/suffix: `(anat, T1w) or (anat, MP2RAGE)`.

Although you can add multiple commands the combination datatype/suffix has to be unique.

```
"postOp": [{"cmd": "pydeface --outfile dstFile srcFile",
"datatype": "anat",
"suffix": ["T1w", "MP2RAGE"]},
{"cmd": "my_new_script --input srcFile --output dstFile ",
"datatype": "fmap",
"suffix": ["any"]}],
```

`"defaceTpl": "pydeface --outfile {dstFile} {srcFile}"`
In this example the second command "my_new_script" will be running on any image which datatype is fmap.

It is a template string and dcm2bids will replace {srcFile} and {dstFile} by the
Finally, this is a template string and dcm2bids will replace srcFile and dstFile by the
source file (input) and the destination file (output).

## dcm2niixOptions
Expand Down Expand Up @@ -120,7 +141,6 @@ command.
--clobber Overwrite output if it exists
-l {DEBUG,INFO,WARNING,ERROR,CRITICAL}, --log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
Set logging level
-a, --anonymizer This option no longer exists from the script in this release. See:https://github.com/unfmontreal/Dcm2Bids/blob/master/README.md#defaceTpl

Documentation at https://github.com/unfmontreal/Dcm2Bids

Expand Down
5 changes: 0 additions & 5 deletions docs/tutorial/first-steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,8 @@ You can test it with any command but a safe way is to use the `--help` command.
--clobber Overwrite output if it exists
-l {DEBUG,INFO,WARNING,ERROR,CRITICAL}, --log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
Set logging level
-a, --anonymizer This option no longer exists from the script in this
release. See:https://github.com/unfmontreal/Dcm2Bids/blob/m
aster/README.md#defaceTpl

Documentation at https://github.com/unfmontreal/Dcm2Bids

```

??? bug "What you can do if you did not get this output"
Expand Down Expand Up @@ -978,7 +974,6 @@ command.
--clobber Overwrite output if it exists
-l {DEBUG,INFO,WARNING,ERROR,CRITICAL}, --log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
Set logging level
-a, --anonymizer This option no longer exists from the script in this release. See:https://github.com/unfmontreal/Dcm2Bids/blob/master/README.md#defaceTpl

Documentation at https://github.com/unfmontreal/Dcm2Bids

Expand Down
4 changes: 3 additions & 1 deletion example/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"searchMethod": "fnmatch",
"defaceTpl": "pydeface --outfile {dstFile} {srcFile}",
"postOp": [{"cmd" : "pydeface --outfile dstFile srcFile",
"datatype": "anat",
"suffix": ["T1w", "MP2RAGE"]}],
"descriptions": [
{
"datatype": "anat",
Expand Down

0 comments on commit 5812b5c

Please sign in to comment.