Skip to content

Commit

Permalink
[REF] Extract Step 2 of DWIPreprocessingUsingPhasediff into its own…
Browse files Browse the repository at this point in the history
… workflow (#897)

* extract workflow

* add test

* some fixes

* linting

* Update test/nonregression/pipelines/test_run_pipelines_dwi.py

Co-authored-by: Matthieu Joulot <85217698+MatthieuJoulot@users.noreply.github.com>

---------

Co-authored-by: Matthieu Joulot <85217698+MatthieuJoulot@users.noreply.github.com>
  • Loading branch information
NicolasGensollen and MatthieuJoulot authored Apr 4, 2023
1 parent e5b1b96 commit fda6bf1
Show file tree
Hide file tree
Showing 3 changed files with 384 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,6 @@ def build_output_node(self):

def build_core_nodes(self):
"""Build and connect the core nodes of the pipeline."""
import nipype.interfaces.ants as ants
import nipype.interfaces.fsl as fsl
import nipype.interfaces.mrtrix3 as mrtrix3
import nipype.interfaces.utility as nutil
Expand All @@ -293,13 +292,10 @@ def build_core_nodes(self):
print_end_pipeline,
)
from .dwi_preprocessing_using_phasediff_fmap_workflows import (
calibrate_and_register_fmap,
compute_reference_b0,
prepare_phasediff_fmap,
)

# Step 0: Initialization
# ======================
# Initialize input parameters and print begin message
init_node = npe.Node(
interface=nutil.Function(
input_names=self.get_input_fields(),
Expand All @@ -324,38 +320,7 @@ def build_core_nodes(self):
use_cuda=self.parameters["use_cuda"],
initrand=self.parameters["initrand"],
)

# Step 2: Calibrate and register FMap
# ===================================
# Bias field correction of the magnitude image
bias_mag_fmap = npe.Node(
ants.N4BiasFieldCorrection(dimension=3), name="2a-N4MagnitudeFmap"
)
# Brain extraction of the magnitude image
bet_mag_fmap = npe.Node(
fsl.BET(frac=0.4, mask=True), name="2b-BetN4MagnitudeFmap"
)

# Calibrate FMap
calibrate_fmap = prepare_phasediff_fmap(name="2c-CalibrateFMap")

# Register the BET magnitude fmap onto the BET b0
bet_mag_fmap2b0 = npe.Node(
interface=fsl.FLIRT(), name="2d-RegistrationBetMagToB0"
)
bet_mag_fmap2b0.inputs.dof = 6
bet_mag_fmap2b0.inputs.output_type = "NIFTI_GZ"

# Apply the transformation on the calibrated fmap
fmap2b0 = npe.Node(interface=fsl.ApplyXFM(), name="2e-1-FMapToB0")
fmap2b0.inputs.output_type = "NIFTI_GZ"

# Apply the transformation on the magnitude image
mag_fmap2b0 = fmap2b0.clone("2e-2-MagFMapToB0")

# Smooth the registered (calibrated) fmap
smoothing = npe.Node(interface=fsl.maths.IsotropicSmooth(), name="2f-Smoothing")
smoothing.inputs.sigma = 4.0
fmap_calibration_and_registration = calibrate_and_register_fmap()

# Step 3: Run FSL eddy
# ====================
Expand Down Expand Up @@ -395,88 +360,112 @@ def build_core_nodes(self):
name="99-WriteEndMessage",
)

# Connection
# ==========
# fmt: off
self.connect(
[
# Step 0: Initialization
# ======================
# Initialize input parameters and print begin message
(self.input_node, init_node, [("dwi", "dwi"),
("bvec", "bvec"),
("bval", "bval"),
("dwi_json", "dwi_json"),
("fmap_magnitude", "fmap_magnitude"),
("fmap_phasediff", "fmap_phasediff"),
("fmap_phasediff_json", "fmap_phasediff_json")]),
# Step 1: Computation of the reference b0 (i.e. average b0 but with EPI distortions)
# =======================================
(init_node, reference_b0, [("bval", "inputnode.b_values_filename"),
("bvec", "inputnode.b_vectors_filename"),
("dwi", "inputnode.dwi_filename"),
("total_readout_time", "inputnode.total_readout_time"),
("phase_encoding_direction", "inputnode.phase_encoding_direction"),
("image_id", "inputnode.image_id")]),
# Step 2: Calibrate and register FMap
# ===================================
# Bias field correction of the magnitude image
(init_node, bias_mag_fmap, [("fmap_magnitude", "input_image")]),
# Brain extraction of the magnitude image
(bias_mag_fmap, bet_mag_fmap, [("output_image", "in_file")]),
# Calibration of the FMap
(bet_mag_fmap, calibrate_fmap, [("mask_file", "input_node.fmap_mask"),
("out_file", "input_node.fmap_magnitude")]),
(init_node, calibrate_fmap, [("fmap_phasediff", "input_node.fmap_phasediff"),
("delta_echo_time", "input_node.delta_echo_time")]),
# Register the BET magnitude fmap onto the BET b0
(bet_mag_fmap, bet_mag_fmap2b0, [("out_file", "in_file")]),
(reference_b0, bet_mag_fmap2b0, [("outputnode.reference_b0", "reference")]),
# Apply the transformation on the magnitude image
(bet_mag_fmap2b0, mag_fmap2b0, [("out_matrix_file", "in_matrix_file")]),
(bias_mag_fmap, mag_fmap2b0, [("output_image", "in_file")]),
(reference_b0, mag_fmap2b0, [("outputnode.reference_b0", "reference")]),
# Apply the transformation on the calibrated fmap
(bet_mag_fmap2b0, fmap2b0, [("out_matrix_file", "in_matrix_file")]),
(calibrate_fmap, fmap2b0, [("output_node.calibrated_fmap", "in_file")]),
(reference_b0, fmap2b0, [("outputnode.reference_b0", "reference")]),
# # Smooth the registered (calibrated) fmap
(fmap2b0, smoothing, [("out_file", "in_file")]),

# Step 3: Run FSL eddy
# ====================
(init_node, eddy, [("dwi", "inputnode.dwi_filename"),
("bval", "inputnode.b_values_filename"),
("bvec", "inputnode.b_vectors_filename"),
("image_id", "inputnode.image_id")]),
(smoothing, eddy, [("out_file", "inputnode.field")]),
(reference_b0, eddy, [("outputnode.brainmask", "inputnode.in_mask")]),

# Step 4: Bias correction
# =======================
(init_node, bias, [("bval", "in_bval")]),
(eddy, bias, [("outputnode.out_rotated_bvecs", "in_bvec"),
("outputnode.out_corrected", "in_file")]),
# Step 5: Final brainmask
# =======================
# Compute average b0 on corrected dataset (for brain mask extraction)
(init_node, compute_avg_b0, [("bval", "in_bval")]),
(bias, compute_avg_b0, [("out_file", "in_dwi")]),
# Compute b0 mask on corrected avg b0
(compute_avg_b0, mask_avg_b0, [("reference_b0", "in_file")]),

# Print end message
(init_node, print_end_message, [("image_id", "image_id")]),
(mask_avg_b0, print_end_message, [("mask_file", "final_file")]),

# Output node
(init_node, self.output_node, [("bval", "preproc_bval")]),
(eddy, self.output_node, [("outputnode.out_rotated_bvecs", "preproc_bvec")]),
(bias, self.output_node, [("out_file", "preproc_dwi")]),
(mask_avg_b0, self.output_node, [("mask_file", "b0_mask")]),
(bet_mag_fmap2b0, self.output_node, [("out_file", "magnitude_on_b0")]),
(fmap2b0, self.output_node, [("out_file", "calibrated_fmap_on_b0")]),
(smoothing, self.output_node, [("out_file", "smoothed_fmap_on_b0")]),
]
)
# fmt: on
connections = [
(
self.input_node,
init_node,
[
("dwi", "dwi"),
("bvec", "bvec"),
("bval", "bval"),
("dwi_json", "dwi_json"),
("fmap_magnitude", "fmap_magnitude"),
("fmap_phasediff", "fmap_phasediff"),
("fmap_phasediff_json", "fmap_phasediff_json"),
],
),
(
init_node,
reference_b0,
[
("bval", "inputnode.b_values_filename"),
("bvec", "inputnode.b_vectors_filename"),
("dwi", "inputnode.dwi_filename"),
("total_readout_time", "inputnode.total_readout_time"),
("phase_encoding_direction", "inputnode.phase_encoding_direction"),
("image_id", "inputnode.image_id"),
],
),
(
init_node,
fmap_calibration_and_registration,
[
("fmap_magnitude", "inputnode.bias_magnitude_fmap"),
("fmap_phasediff", "inputnode.fmap_phasediff"),
("delta_echo_time", "inputnode.delta_echo_time"),
],
),
(
reference_b0,
fmap_calibration_and_registration,
[("outputnode.reference_b0", "inputnode.reference_b0")],
),
(
init_node,
eddy,
[
("dwi", "inputnode.dwi_filename"),
("bval", "inputnode.b_values_filename"),
("bvec", "inputnode.b_vectors_filename"),
("image_id", "inputnode.image_id"),
],
),
(
fmap_calibration_and_registration,
eddy,
[("outputnode.smooth_calibrated_fmap", "inputnode.field")],
),
(reference_b0, eddy, [("outputnode.brainmask", "inputnode.in_mask")]),
# Step 4: Bias correction
# =======================
(init_node, bias, [("bval", "in_bval")]),
(
eddy,
bias,
[
("outputnode.out_rotated_bvecs", "in_bvec"),
("outputnode.out_corrected", "in_file"),
],
),
# Step 5: Final brainmask
# =======================
# Compute average b0 on corrected dataset (for brain mask extraction)
(init_node, compute_avg_b0, [("bval", "in_bval")]),
(bias, compute_avg_b0, [("out_file", "in_dwi")]),
# Compute b0 mask on corrected avg b0
(compute_avg_b0, mask_avg_b0, [("reference_b0", "in_file")]),
# Print end message
(init_node, print_end_message, [("image_id", "image_id")]),
(mask_avg_b0, print_end_message, [("mask_file", "final_file")]),
# Output node
(init_node, self.output_node, [("bval", "preproc_bval")]),
(
eddy,
self.output_node,
[("outputnode.out_rotated_bvecs", "preproc_bvec")],
),
(bias, self.output_node, [("out_file", "preproc_dwi")]),
(mask_avg_b0, self.output_node, [("mask_file", "b0_mask")]),
(
fmap_calibration_and_registration,
self.output_node,
[
(
"outputnode.bet_magnitude_fmap_registered_onto_b0",
"magnitude_on_b0",
)
],
),
(
fmap_calibration_and_registration,
self.output_node,
[("outputnode.registered_calibrated_fmap", "calibrated_fmap_on_b0")],
),
(
fmap_calibration_and_registration,
self.output_node,
[("outputnode.smooth_calibrated_fmap", "smoothed_fmap_on_b0")],
),
]

self.connect(connections)
Loading

0 comments on commit fda6bf1

Please sign in to comment.