From efcd7ac9c2b98c49d025d381d19a9b875fca1909 Mon Sep 17 00:00:00 2001 From: Marcel Zwiers Date: Mon, 15 Jan 2024 14:35:16 +0100 Subject: [PATCH] Make the dcm2niix suffix fallback optional (pull request #214) --- bidscoin/bids.py | 14 +++++++++----- bidscoin/heuristics/bidsmap_dccn.yaml | 1 + bidscoin/heuristics/bidsmap_sst.yaml | 1 + bidscoin/heuristics/schema.json | 3 ++- bidscoin/plugins/dcm2niix2bids.py | 10 ++++++---- docs/options.rst | 1 + 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/bidscoin/bids.py b/bidscoin/bids.py index 7c13b94f..de2d33f9 100644 --- a/bidscoin/bids.py +++ b/bidscoin/bids.py @@ -1797,9 +1797,9 @@ def get_bidsname(subid: str, sesid: str, run: dict, validkeys: bool, runtime: bo def get_bidsvalue(bidsfile: Union[str, Path], bidskey: str, newvalue: str='') -> Union[Path, str]: """ - Sets the bidslabel, i.e. '*_bidskey-*_' is replaced with '*_bidskey-bidsvalue_'. If the key is not in the bidsname - then the newvalue is appended to the acquisition label. If newvalue is empty (= default), then the parsed existing - bidsvalue is returned and nothing is set + Sets the bidslabel, i.e. '*_bidskey-*_' is replaced with '*_bidskey-bidsvalue_'. If the key exists but is not in the + bidsname (e.g. 'fallback') then, as a fallback, the newvalue is appended to the acquisition label. If newvalue is empty + (= default), then the parsed existing bidsvalue is returned and nothing is set :param bidsfile: The bidsname (e.g. as returned from get_bidsname or fullpath) :param bidskey: The name of the bidskey, e.g. 'echo' or 'suffix' @@ -1807,6 +1807,10 @@ def get_bidsvalue(bidsfile: Union[str, Path], bidskey: str, newvalue: str='') -> :return: The bidsname with the new bidsvalue or, if newvalue is empty, the existing bidsvalue """ + # Check input + if not bidskey and newvalue: + return bidsfile # No fallback + bidspath = Path(bidsfile).parent bidsname = Path(bidsfile).with_suffix('').stem bidsext = ''.join(Path(bidsfile).suffixes) @@ -1827,8 +1831,8 @@ def get_bidsvalue(bidsfile: Union[str, Path], bidskey: str, newvalue: str='') -> # Replace the existing bidsvalue with the new value or append the newvalue to the acquisition value if newvalue: - if f'_{bidskey}-' not in bidsname + 'suffix': - if '_acq-' not in bidsname: # Insert the 'acq' key right after task, ses or sub key-value pair (i.e. order as in entities.yaml) + if f'_{bidskey}-' not in bidsname + 'suffix': # Fallback: Append the newvalue to the 'acq'-value + if '_acq-' not in bidsname: # Insert the 'acq' key right after task, ses or sub key-value pair (i.e. order as in entities.yaml) keyvals = bidsname.split('_') keyvals.insert(1 + ('_ses-' in bidsname) + ('_task-' in bidsname), 'acq-') bidsname = '_'.join(keyvals) diff --git a/bidscoin/heuristics/bidsmap_dccn.yaml b/bidscoin/heuristics/bidsmap_dccn.yaml index e5d5921d..ddcdfcdd 100644 --- a/bidscoin/heuristics/bidsmap_dccn.yaml +++ b/bidscoin/heuristics/bidsmap_dccn.yaml @@ -36,6 +36,7 @@ Options: args: -b y -z y -i n -l n # Argument string that is passed to dcm2niix. Tip: SPM users may want to use '-z n' (which produces unzipped NIfTI's, see dcm2niix -h for more information) anon: y # Set this anonymization flag to 'y' to round off age and discard acquisition date from the meta data meta: [.json, .tsv, .tsv.gz] # The file extensions of the equally named metadata sourcefiles that are copied over to the BIDS sidecar files + fallback: y # Appends unhandled dcm2niix suffixes to the `acq` label if 'y' (recommended, else the suffix data is discarded) DICOM: diff --git a/bidscoin/heuristics/bidsmap_sst.yaml b/bidscoin/heuristics/bidsmap_sst.yaml index 3439c19c..d6b831e5 100644 --- a/bidscoin/heuristics/bidsmap_sst.yaml +++ b/bidscoin/heuristics/bidsmap_sst.yaml @@ -35,6 +35,7 @@ Options: args: -b y -z y -i n -l n # Argument string that is passed to dcm2niix. Tip: SPM users may want to use '-z n' (which produces unzipped NIfTI's, see dcm2niix -h for more information) anon: y # Set this anonymization flag to 'y' to round off age and discard acquisition date from the meta data meta: [.json, .tsv, .tsv.gz] # The file extensions of the equally named metadata sourcefiles that are copied over to the BIDS sidecar files + fallback: y # Appends unhandled dcm2niix suffixes to the `acq` label if 'y' (recommended, else the suffix data is discarded) DICOM: diff --git a/bidscoin/heuristics/schema.json b/bidscoin/heuristics/schema.json index 33f7ca74..d9daa603 100644 --- a/bidscoin/heuristics/schema.json +++ b/bidscoin/heuristics/schema.json @@ -35,7 +35,8 @@ "command": { "type": "string" }, "args": { "type": ["string", "null"] }, "anon": { "type": ["string", "null"] }, - "meta": { "type": ["array"] } + "meta": { "type": ["array"] }, + "fallback": { "type": ["string", "null"] } } } } diff --git a/bidscoin/plugins/dcm2niix2bids.py b/bidscoin/plugins/dcm2niix2bids.py index aa6ddabe..cae65a6e 100644 --- a/bidscoin/plugins/dcm2niix2bids.py +++ b/bidscoin/plugins/dcm2niix2bids.py @@ -30,7 +30,8 @@ OPTIONS = {'command': 'dcm2niix', # Command to run dcm2niix, e.g. "module add dcm2niix/1.0.20180622; dcm2niix" or "PATH=/opt/dcm2niix/bin:$PATH; dcm2niix" or /opt/dcm2niix/bin/dcm2niix or 'C:\"Program Files"\dcm2niix\dcm2niix.exe' (use quotes to deal with whitespaces in the path) 'args': '-b y -z y -i n', # Argument string that is passed to dcm2niix. Tip: SPM users may want to use '-z n' (which produces unzipped NIfTI's, see dcm2niix -h for more information) 'anon': 'y', # Set this anonymization flag to 'y' to round off age and discard acquisition date from the metadata - 'meta': ['.json', '.tsv', '.tsv.gz']} # The file extensions of the equally named metadata sourcefiles that are copied over as BIDS sidecar files + 'meta': ['.json', '.tsv', '.tsv.gz'], # The file extensions of the equally named metadata sourcefiles that are copied over as BIDS sidecar files + 'fallback': 'y'} # Appends unhandled dcm2niix suffixes to the `acq` label if 'y' (recommended, else the suffix data is discarding) def test(options: dict=OPTIONS) -> int: @@ -199,6 +200,7 @@ def bidscoiner_plugin(session: Path, bidsmap: dict, bidsses: Path) -> Union[None # Get started and see what dataformat we have options = bidsmap['Options']['plugins']['dcm2niix2bids'] + fallback = 'fallback' if options.get('fallback','y').lower() in ('y', 'yes', 'true') else '' datasource = bids.get_datasource(session, {'dcm2niix2bids': options}) dataformat = datasource.dataformat if not dataformat: @@ -357,10 +359,10 @@ def bidscoiner_plugin(session: Path, bidsmap: dict, bidsses: Path) -> Union[None elif echonr[0:-1].isdecimal(): LOGGER.verbose(f"Splitting off echo-number {echonr[0:-1]} from the '{postfix}' postfix") newbidsname = bids.insert_bidskeyval(newbidsname, 'echo', echonr[0:-1].lstrip('0'), bidsignore) # Strip of the 'a', 'b', etc. from `e1a`, `e1b`, etc - newbidsname = bids.get_bidsvalue(newbidsname, 'dummy', echonr[-1]) # Append the 'a' to the acq-label + newbidsname = bids.get_bidsvalue(newbidsname, fallback, echonr[-1]) # Append the 'a' to the acq-label else: LOGGER.error(f"Unexpected postix '{postfix}' found in {dcm2niixfile}") - newbidsname = bids.get_bidsvalue(newbidsname, 'dummy', postfix) # Append the unknown postfix to the acq-label + newbidsname = bids.get_bidsvalue(newbidsname, fallback, postfix) # Append the unknown postfix to the acq-label # Patch the phase entity in the newbidsname with the dcm2niix mag/phase info elif 'part' in run['bids'] and postfix in ('ph','real','imaginary'): # e.g. part: ['', 'mag', 'phase', 'real', 'imag', 0] @@ -406,7 +408,7 @@ def bidscoiner_plugin(session: Path, bidsmap: dict, bidsses: Path) -> Union[None # Append the dcm2niix info to acq-label, may need to be improved / elaborated for future BIDS standards, supporting multi-coil data else: - newbidsname = bids.get_bidsvalue(newbidsname, 'dummy', postfix) + newbidsname = bids.get_bidsvalue(newbidsname, fallback, postfix) # Remove the added postfix from the new bidsname newbidsname = newbidsname.replace(f"_{postfix}_",'_') # If it is not last diff --git a/docs/options.rst b/docs/options.rst index ea126ed3..c3f74442 100644 --- a/docs/options.rst +++ b/docs/options.rst @@ -43,6 +43,7 @@ The `dcm2niix2bids plugin <./plugins.html#dcm2niix2bids>`__ is the default bidsc - ``args``: Argument string that is passed as input to dcm2niix to customize its behavior, e.g. ``-z n -i y`` for ignoring derived data and having uncompressed output data. - ``anon``: Set this anonymization flag to 'y' to round off age and to discard acquisition date from the meta data - ``meta``: The file extensions of the associated / equally named (meta)data sourcefiles that are copied over as BIDS (sidecar) files, such as ``['.json', '.tsv', '.tsv.gz']``. You can use this to enrich json sidecar files or add data that is not supported by this plugin. For instance, with each PET DICOM image you can put a small json file with key-value pairs that are not contained in the DICOM header (such as ``{InjectedRadioactivity: 400, InjectedMass: 10}``). NB: Data entered in the meta table of the bidseditor GUI always has priority over data in source json files, which itself has priority over dcm2niix-generated json data. +- ``fallback``: Appends unhandled dcm2niix suffixes to the `acq` label if 'y' (recommended, else the suffix data is discarded) .. tip:: - Use the [Set as default] button to put your custom dcm2niix command in your template bidsmap so that you don't have to adjust it anymore for every new study