|
17 | 17 | from nipype.pipeline import engine as pe |
18 | 18 | from nipype.interfaces import utility as niu |
19 | 19 |
|
| 20 | +from niworkflows.utils.misc import select_first |
| 21 | + |
20 | 22 | from ...utils.meepi import combine_meepi_source |
21 | 23 |
|
22 | 24 | from ...interfaces import DerivativesDataSink |
@@ -141,52 +143,59 @@ def init_func_preproc_wf(bold_file): |
141 | 143 | from niworkflows.interfaces.utils import DictMerge |
142 | 144 | from sdcflows.workflows.base import init_sdc_estimate_wf, fieldmap_wrangler |
143 | 145 |
|
144 | | - ref_file = bold_file |
145 | 146 | mem_gb = {'filesize': 1, 'resampled': 1, 'largemem': 1} |
146 | 147 | bold_tlen = 10 |
147 | | - multiecho = isinstance(bold_file, list) |
148 | 148 |
|
149 | 149 | # Have some options handy |
150 | | - layout = config.execution.layout |
151 | 150 | omp_nthreads = config.nipype.omp_nthreads |
152 | 151 | freesurfer = config.workflow.run_reconall |
153 | 152 | spaces = config.workflow.spaces |
154 | 153 | output_dir = str(config.execution.output_dir) |
155 | 154 |
|
| 155 | + # Extract BIDS entities and metadata from bold reference file |
| 156 | + ref_file = select_first(bold_file) |
| 157 | + layout = config.execution.layout |
| 158 | + entities = layout.parse_file_entities(ref_file) |
| 159 | + metadata = layout.get_metadata(ref_file) |
| 160 | + |
| 161 | + # Drop echo entity for future queries, have a boolean shorthand |
| 162 | + multiecho = bool(entities.pop("echo", None)) |
| 163 | + if multiecho and (isinstance(bold_file, str) or len(bold_file) == 1): |
| 164 | + multiecho = False |
| 165 | + config.loggers.warning( |
| 166 | + f"Only one echo <{ref_file}> found in a seemingly multi-echo dataset. " |
| 167 | + "Falling back to single-echo mode." |
| 168 | + ) |
| 169 | + bold_file = ref_file # Just in case - drop the list |
| 170 | + |
156 | 171 | if multiecho: |
157 | | - tes = [layout.get_metadata(echo)['EchoTime'] for echo in bold_file] |
158 | | - ref_file = dict(zip(tes, bold_file))[min(tes)] |
| 172 | + # reorder echoes from shortest to largest |
| 173 | + tes, bold_file = zip(*sorted([ |
| 174 | + (layout.get_metadata(bf)["EchoTime"], bf) for bf in bold_file |
| 175 | + ])) |
| 176 | + ref_file = bold_file[0] # Reset reference to be the shortest TE |
159 | 177 |
|
160 | 178 | if os.path.isfile(ref_file): |
161 | 179 | bold_tlen, mem_gb = _create_mem_gb(ref_file) |
162 | 180 |
|
163 | 181 | wf_name = _get_wf_name(ref_file) |
164 | 182 | config.loggers.workflow.debug( |
165 | | - 'Creating bold processing workflow for "%s" (%.2f GB / %d TRs). ' |
| 183 | + 'Creating bold processing workflow for <%s> (%.2f GB / %d TRs). ' |
166 | 184 | 'Memory resampled/largemem=%.2f/%.2f GB.', |
167 | 185 | ref_file, mem_gb['filesize'], bold_tlen, mem_gb['resampled'], mem_gb['largemem']) |
168 | 186 |
|
169 | 187 | # Find associated sbref, if possible |
170 | | - entities = layout.parse_file_entities(ref_file) |
171 | | - if multiecho: |
172 | | - # Remove echo entity to collect all echoes |
173 | | - _ = entities.pop('echo', None) |
174 | 188 | entities['suffix'] = 'sbref' |
175 | 189 | entities['extension'] = ['nii', 'nii.gz'] # Overwrite extensions |
176 | | - |
177 | 190 | sbref_files = layout.get(return_type='file', **entities) |
178 | | - refbase = os.path.basename(ref_file) |
179 | | - if 'sbref' in config.workflow.ignore: |
180 | | - config.loggers.workflow.info("Single-band reference files ignored.") |
181 | | - elif sbref_files: |
182 | | - sbbase = ','.join([os.path.basename(sbf) for sbf in sbref_files]) |
183 | | - config.loggers.workflow.info("Using single-band reference file(s) %s.", |
184 | | - sbbase) |
185 | | - else: |
186 | | - config.loggers.workflow.info("No single-band-reference found for %s.", |
187 | | - refbase) |
188 | 191 |
|
189 | | - metadata = layout.get_metadata(ref_file) |
| 192 | + sbref_msg = f"No single-band-reference found for {os.path.basename(ref_file)}." |
| 193 | + if sbref_files and 'sbref' in config.workflow.ignore: |
| 194 | + sbref_msg = "Single-band reference file(s) found and ignored." |
| 195 | + elif sbref_files: |
| 196 | + sbref_msg = "Using single-band reference file(s) {}.".format( |
| 197 | + ','.join([os.path.basename(sbf) for sbf in sbref_files])) |
| 198 | + config.loggers.workflow.info(sbref_msg) |
190 | 199 |
|
191 | 200 | # Find fieldmaps. Options: (phase1|phase2|phasediff|epi|fieldmap|syn) |
192 | 201 | fmaps = None |
@@ -286,16 +295,13 @@ def init_func_preproc_wf(bold_file): |
286 | 295 | ]) |
287 | 296 |
|
288 | 297 | # Generate a tentative boldref |
289 | | - bold_reference_wf = init_bold_reference_wf(omp_nthreads=omp_nthreads) |
| 298 | + bold_reference_wf = init_bold_reference_wf( |
| 299 | + omp_nthreads=omp_nthreads, |
| 300 | + bold_file=bold_file, |
| 301 | + sbref_files=sbref_files, |
| 302 | + multiecho=multiecho, |
| 303 | + ) |
290 | 304 | bold_reference_wf.inputs.inputnode.dummy_scans = config.workflow.dummy_scans |
291 | | - if sbref_files is not None: |
292 | | - from niworkflows.interfaces.images import ValidateImage |
293 | | - val_sbref = pe.MapNode(ValidateImage(), name='val_sbref', |
294 | | - iterfield=['in_file']) |
295 | | - val_sbref.inputs.in_file = sbref_files |
296 | | - workflow.connect([ |
297 | | - (val_sbref, bold_reference_wf, [('out_file', 'inputnode.sbref_file')]), |
298 | | - ]) |
299 | 305 |
|
300 | 306 | # Top-level BOLD splitter |
301 | 307 | bold_split = pe.Node(FSLSplit(dimension='t'), name='bold_split', |
@@ -408,8 +414,6 @@ def init_func_preproc_wf(bold_file): |
408 | 414 | workflow.connect([ |
409 | 415 | (inputnode, t1w_brain, [('t1w_preproc', 'in_file'), |
410 | 416 | ('t1w_mask', 'in_mask')]), |
411 | | - # Generate early reference |
412 | | - (inputnode, bold_reference_wf, [('bold_file', 'inputnode.bold_file')]), |
413 | 417 | # BOLD buffer has slice-time corrected if it was run, original otherwise |
414 | 418 | (boldbuffer, bold_split, [('bold_file', 'in_file')]), |
415 | 419 | # HMC |
|
0 commit comments