Skip to content

Commit 24002ef

Browse files
authored
ENH: Allow --ignore fmap-jacobian to disable Jacobian determinant modulation during fieldmap correction (#3186)
There is some indication (nipreps/sdcflows#413) that fieldmap correction can lead to weird patches of intensity variation. This is almost certainly caused to modulation by the Jacobian determinant image, although the reason that this is not working as expected in these cases is unclear. In any case, this allows the behavior to be disabled with `--ignore fmap-jacobian`.
2 parents 7f4b302 + 3090f68 commit 24002ef

File tree

5 files changed

+21
-6
lines changed

5 files changed

+21
-6
lines changed

fmriprep/cli/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ def _slice_time_ref(value, parser):
285285
action="store",
286286
nargs="+",
287287
default=[],
288-
choices=["fieldmaps", "slicetiming", "sbref", "t2w", "flair"],
288+
choices=["fieldmaps", "slicetiming", "sbref", "t2w", "flair", "fmap-jacobian"],
289289
help="Ignore selected aspects of the input dataset to disable corresponding "
290290
"parts of the workflow (a space delimited list)",
291291
)

fmriprep/interfaces/resampling.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class ResampleSeriesInputSpec(TraitedSpec):
4949
"k-",
5050
desc="the phase-encoding direction corresponding to in_data",
5151
)
52+
jacobian = traits.Bool(mandatory=True, desc="Whether to apply Jacobian correction")
5253
num_threads = traits.Int(1, usedefault=True, desc="Number of threads to use for resampling")
5354
output_data_type = traits.Str("float32", usedefault=True, desc="Data type of output image")
5455
order = traits.Int(3, usedefault=True, desc="Order of interpolation (0=nearest, 3=cubic)")
@@ -105,6 +106,7 @@ def _run_interface(self, runtime):
105106
transforms=transforms,
106107
fieldmap=fieldmap,
107108
pe_info=pe_info,
109+
jacobian=self.inputs.jacobian,
108110
nthreads=self.inputs.num_threads,
109111
output_dtype=self.inputs.output_data_type,
110112
order=self.inputs.order,
@@ -217,6 +219,7 @@ def resample_vol(
217219
data: np.ndarray,
218220
coordinates: np.ndarray,
219221
pe_info: tuple[int, float],
222+
jacobian: bool,
220223
hmc_xfm: np.ndarray | None,
221224
fmap_hz: np.ndarray,
222225
output: np.dtype | np.ndarray | None = None,
@@ -282,8 +285,6 @@ def resample_vol(
282285
vsm = fmap_hz * pe_info[1]
283286
coordinates[pe_info[0], ...] += vsm
284287

285-
jacobian = 1 + np.gradient(vsm, axis=pe_info[0])
286-
287288
result = ndi.map_coordinates(
288289
data,
289290
coordinates,
@@ -293,14 +294,18 @@ def resample_vol(
293294
cval=cval,
294295
prefilter=prefilter,
295296
)
296-
result *= jacobian
297+
298+
if jacobian:
299+
result *= 1 + np.gradient(vsm, axis=pe_info[0])
300+
297301
return result
298302

299303

300304
async def resample_series_async(
301305
data: np.ndarray,
302306
coordinates: np.ndarray,
303307
pe_info: list[tuple[int, float]],
308+
jacobian: bool,
304309
hmc_xfms: list[np.ndarray] | None,
305310
fmap_hz: np.ndarray,
306311
output_dtype: np.dtype | None = None,
@@ -361,6 +366,7 @@ async def resample_series_async(
361366
data,
362367
coordinates,
363368
pe_info[0],
369+
jacobian,
364370
hmc_xfms[0] if hmc_xfms else None,
365371
fmap_hz,
366372
output_dtype,
@@ -384,6 +390,7 @@ async def resample_series_async(
384390
data=volume,
385391
coordinates=coordinates,
386392
pe_info=pe_info[volid],
393+
jacobian=jacobian,
387394
hmc_xfm=hmc_xfms[volid] if hmc_xfms else None,
388395
fmap_hz=fmap_hz,
389396
output=out_array[..., volid],
@@ -407,6 +414,7 @@ def resample_series(
407414
data: np.ndarray,
408415
coordinates: np.ndarray,
409416
pe_info: list[tuple[int, float]],
417+
jacobian: bool,
410418
hmc_xfms: list[np.ndarray] | None,
411419
fmap_hz: np.ndarray,
412420
output_dtype: np.dtype | None = None,
@@ -467,6 +475,7 @@ def resample_series(
467475
data=data,
468476
coordinates=coordinates,
469477
pe_info=pe_info,
478+
jacobian=jacobian,
470479
hmc_xfms=hmc_xfms,
471480
fmap_hz=fmap_hz,
472481
output_dtype=output_dtype,
@@ -485,6 +494,7 @@ def resample_image(
485494
transforms: nt.TransformChain,
486495
fieldmap: nb.Nifti1Image | None,
487496
pe_info: list[tuple[int, float]] | None,
497+
jacobian: bool = True,
488498
nthreads: int = 1,
489499
output_dtype: np.dtype | str | None = 'f4',
490500
order: int = 3,
@@ -566,6 +576,7 @@ def resample_image(
566576
data=source.get_fdata(dtype='f4'),
567577
coordinates=mapped_coordinates.T.reshape((3, *target.shape[:3])),
568578
pe_info=pe_info,
579+
jacobian=jacobian,
569580
hmc_xfms=hmc_xfms,
570581
fmap_hz=fieldmap.get_fdata(dtype='f4'),
571582
output_dtype=output_dtype,

fmriprep/workflows/bold/apply.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def init_bold_volumetric_resample_wf(
1616
*,
1717
metadata: dict,
1818
mem_gb: dict[str, float],
19+
jacobian: bool,
1920
fieldmap_id: str | None = None,
2021
omp_nthreads: int = 1,
2122
name: str = 'bold_volumetric_resample_wf',
@@ -123,7 +124,7 @@ def init_bold_volumetric_resample_wf(
123124
boldref2target = pe.Node(niu.Merge(2), name='boldref2target', run_without_submitting=True)
124125
bold2target = pe.Node(niu.Merge(2), name='bold2target', run_without_submitting=True)
125126
resample = pe.Node(
126-
ResampleSeries(),
127+
ResampleSeries(jacobian=jacobian),
127128
name="resample",
128129
n_procs=omp_nthreads,
129130
mem_gb=mem_gb['resampled'],

fmriprep/workflows/bold/base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ def init_bold_wf(
380380
fieldmap_id=fieldmap_id if not multiecho else None,
381381
omp_nthreads=omp_nthreads,
382382
mem_gb=mem_gb,
383+
jacobian='fmap-jacobian' not in config.workflow.ignore,
383384
name='bold_anat_wf',
384385
)
385386
bold_anat_wf.inputs.inputnode.resolution = "native"
@@ -437,6 +438,7 @@ def init_bold_wf(
437438
fieldmap_id=fieldmap_id if not multiecho else None,
438439
omp_nthreads=omp_nthreads,
439440
mem_gb=mem_gb,
441+
jacobian='fmap-jacobian' not in config.workflow.ignore,
440442
name='bold_std_wf',
441443
)
442444
ds_bold_std_wf = init_ds_volumes_wf(
@@ -521,6 +523,7 @@ def init_bold_wf(
521523
fieldmap_id=fieldmap_id if not multiecho else None,
522524
omp_nthreads=omp_nthreads,
523525
mem_gb=mem_gb,
526+
jacobian='fmap-jacobian' not in config.workflow.ignore,
524527
name='bold_MNI6_wf',
525528
)
526529

fmriprep/workflows/bold/fit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@ def init_bold_native_wf(
838838

839839
# Resample to boldref
840840
boldref_bold = pe.Node(
841-
ResampleSeries(),
841+
ResampleSeries(jacobian="fmap-jacobian" not in config.workflow.ignore),
842842
name="boldref_bold",
843843
n_procs=omp_nthreads,
844844
mem_gb=mem_gb["resampled"],

0 commit comments

Comments
 (0)