From 23fbcb03265bc7c9a2836bb76badc1f13c8aadae Mon Sep 17 00:00:00 2001 From: NicolasGensollen Date: Tue, 10 Oct 2023 17:56:53 +0200 Subject: [PATCH] try fixing issue of DWI pipeline writing stuffs in input data folders... --- ...rocessing_using_phasediff_fmap_pipeline.py | 2 + ...ocessing_using_phasediff_fmap_workflows.py | 5 ++ .../dwi_preprocessing_using_t1_pipeline.py | 1 + .../dwi_preprocessing_using_t1_utils.py | 42 ++++++++++++++ .../dwi_preprocessing_using_t1_workflows.py | 18 ++++-- clinica/utils/dwi.py | 57 +++++++++++-------- .../pipelines/dwi/preprocessing/test_t1.py | 1 + test/unittests/utils/test_dwi.py | 12 ++-- 8 files changed, 104 insertions(+), 34 deletions(-) diff --git a/clinica/pipelines/dwi_preprocessing_using_fmap/dwi_preprocessing_using_phasediff_fmap_pipeline.py b/clinica/pipelines/dwi_preprocessing_using_fmap/dwi_preprocessing_using_phasediff_fmap_pipeline.py index f713c2030..deb5edeae 100644 --- a/clinica/pipelines/dwi_preprocessing_using_fmap/dwi_preprocessing_using_phasediff_fmap_pipeline.py +++ b/clinica/pipelines/dwi_preprocessing_using_fmap/dwi_preprocessing_using_phasediff_fmap_pipeline.py @@ -316,6 +316,7 @@ def build_core_nodes(self): ) reference_b0 = compute_reference_b0( + self.base_dir, b_value_threshold=self.parameters["low_bval"], use_cuda=self.parameters["use_cuda"], initrand=self.parameters["initrand"], @@ -325,6 +326,7 @@ def build_core_nodes(self): # Step 3: Run FSL eddy # ==================== eddy = eddy_fsl_pipeline( + self.base_dir, use_cuda=self.parameters["use_cuda"], initrand=self.parameters["initrand"], image_id=True, diff --git a/clinica/pipelines/dwi_preprocessing_using_fmap/dwi_preprocessing_using_phasediff_fmap_workflows.py b/clinica/pipelines/dwi_preprocessing_using_fmap/dwi_preprocessing_using_phasediff_fmap_workflows.py index b6b741090..bbde348ef 100644 --- a/clinica/pipelines/dwi_preprocessing_using_fmap/dwi_preprocessing_using_phasediff_fmap_workflows.py +++ b/clinica/pipelines/dwi_preprocessing_using_fmap/dwi_preprocessing_using_phasediff_fmap_workflows.py @@ -159,6 +159,7 @@ def prepare_phasediff_fmap( def compute_reference_b0( + base_dir: str, b_value_threshold: float, use_cuda: bool, initrand: bool, @@ -175,6 +176,9 @@ def compute_reference_b0( Parameters ---------- + base_dir : str + The base directory. + b_value_threshold: float Threshold value to determine the B0 volumes in the DWI image @@ -249,6 +253,7 @@ def compute_reference_b0( # Run eddy without calibrated fmap pre_eddy = eddy_fsl_pipeline( + base_dir, use_cuda=use_cuda, initrand=initrand, image_id=True, diff --git a/clinica/pipelines/dwi_preprocessing_using_t1/dwi_preprocessing_using_t1_pipeline.py b/clinica/pipelines/dwi_preprocessing_using_t1/dwi_preprocessing_using_t1_pipeline.py index 580cd40a2..c0fafbb49 100644 --- a/clinica/pipelines/dwi_preprocessing_using_t1/dwi_preprocessing_using_t1_pipeline.py +++ b/clinica/pipelines/dwi_preprocessing_using_t1/dwi_preprocessing_using_t1_pipeline.py @@ -299,6 +299,7 @@ def build_core_nodes(self): # Head-motion correction + Eddy-currents correction eddy_fsl = eddy_fsl_pipeline( + self.base_dir, use_cuda=self.parameters["use_cuda"], initrand=self.parameters["initrand"], compute_mask=True, diff --git a/clinica/pipelines/dwi_preprocessing_using_t1/dwi_preprocessing_using_t1_utils.py b/clinica/pipelines/dwi_preprocessing_using_t1/dwi_preprocessing_using_t1_utils.py index f8e6c39b8..484d8629a 100644 --- a/clinica/pipelines/dwi_preprocessing_using_t1/dwi_preprocessing_using_t1_utils.py +++ b/clinica/pipelines/dwi_preprocessing_using_t1/dwi_preprocessing_using_t1_utils.py @@ -486,3 +486,45 @@ def extract_sub_ses_folder_name(file_path: str) -> str: from pathlib import Path return (Path(Path(file_path).parent).parent).name + + +def generate_index_file_task( + b_values_filename: str, + image_id=None, + output_dir=None, +) -> str: + """Wrapper for Nipype.""" + from pathlib import Path + + from clinica.utils.dwi import generate_index_file + + return str( + generate_index_file( + Path(b_values_filename), + image_id, + Path(output_dir) if output_dir else None, + ) + ) + + +def generate_acq_file_task( + dwi_filename: str, + fsl_phase_encoding_direction: str, + total_readout_time: str, + image_id=None, + output_dir=None, +) -> str: + """Wrapper for Nipype.""" + from pathlib import Path + + from clinica.utils.dwi import generate_acq_file + + return str( + generate_acq_file( + Path(dwi_filename), + fsl_phase_encoding_direction, + total_readout_time, + image_id, + Path(output_dir) if output_dir else None, + ) + ) diff --git a/clinica/pipelines/dwi_preprocessing_using_t1/dwi_preprocessing_using_t1_workflows.py b/clinica/pipelines/dwi_preprocessing_using_t1/dwi_preprocessing_using_t1_workflows.py index d46f16c50..0a9d7fd49 100644 --- a/clinica/pipelines/dwi_preprocessing_using_t1/dwi_preprocessing_using_t1_workflows.py +++ b/clinica/pipelines/dwi_preprocessing_using_t1/dwi_preprocessing_using_t1_workflows.py @@ -4,6 +4,7 @@ def eddy_fsl_pipeline( + base_dir: str, use_cuda: bool, initrand: bool, compute_mask: bool = True, @@ -18,6 +19,9 @@ def eddy_fsl_pipeline( Parameters ---------- + base_dir : str + The base directory. + use_cuda : bool Boolean to indicate whether cuda should be used or not. @@ -79,7 +83,10 @@ def eddy_fsl_pipeline( from nipype.interfaces.fsl import BET from nipype.interfaces.fsl.epi import Eddy - from clinica.utils.dwi import generate_acq_file, generate_index_file + from .dwi_preprocessing_using_t1_utils import ( + generate_acq_file_task, + generate_index_file_task, + ) inputnode = pe.Node( niu.IdentityInterface( @@ -109,21 +116,24 @@ def eddy_fsl_pipeline( "fsl_phase_encoding_direction", "total_readout_time", "image_id", + "output_dir", ], output_names=["out_file"], - function=generate_acq_file, + function=generate_acq_file_task, ), name="generate_acq", ) + generate_acq.inputs.output_dir = base_dir generate_index = pe.Node( niu.Function( - input_names=["b_values_filename"], + input_names=["b_values_filename", "output_dir"], output_names=["out_file"], - function=generate_index_file, + function=generate_index_file_task, ), name="generate_index", ) + generate_index.inputs.output_dir = base_dir eddy = pe.Node(interface=Eddy(), name="eddy_fsl") eddy.inputs.repol = True diff --git a/clinica/utils/dwi.py b/clinica/utils/dwi.py index e24743ca0..9cbb4380f 100644 --- a/clinica/utils/dwi.py +++ b/clinica/utils/dwi.py @@ -470,7 +470,11 @@ def check_dwi_volume(dwi_dataset: DWIDataset) -> None: ) -def generate_index_file(b_values_filename: str, image_id: str = None) -> str: +def generate_index_file( + b_values_filename: Path, + image_id: Optional[str] = None, + output_dir: Optional[Path] = None, +) -> Path: """Generate [`image_id`]_index.txt file for FSL eddy command. At the moment, all volumes are assumed to be acquired with the @@ -480,40 +484,44 @@ def generate_index_file(b_values_filename: str, image_id: str = None) -> str: Parameters ---------- - b_values_filename : str - Path to the b-values file. + b_values_filename : Path + The path to the b-values file. image_id : str, optional - Optional prefix for the output file name. + An optional prefix for the output file name. Defaults to None. + output_dir : Path, optional + The path to the directory in which the index file + should be written. If not provided, it will be written + in the same folder as the provided b values filename. + Returns ------- - index_filename: str - Path to output index file. [`image_id`]_index.txt or index.txt file. + index_filename: Path + The path to output index file. [`image_id`]_index.txt or index.txt file. """ - from pathlib import Path - import numpy as np - b_values_filename = Path(b_values_filename) if not b_values_filename.is_file(): raise FileNotFoundError(f"Unable to find b-values file: {b_values_filename}.") b_values = np.loadtxt(b_values_filename) index_filename = f"{image_id}_index.txt" if image_id else "index.txt" - index_filename = b_values_filename.parent / index_filename + output_dir = output_dir or b_values_filename.parent + index_filename = output_dir / index_filename np.savetxt(index_filename, np.ones(len(b_values)).T) - return str(index_filename) + return index_filename def generate_acq_file( - dwi_filename: str, + dwi_filename: Path, fsl_phase_encoding_direction: str, total_readout_time: str, - image_id=None, -) -> str: + image_id: Optional[str] = None, + output_dir: Optional[Path] = None, +) -> Path: """Generate [`image_id`]_acq.txt file for FSL eddy command. Parameters @@ -529,32 +537,33 @@ def generate_acq_file( Total readout time from BIDS specifications. image_id : str, optional - Optional prefix for the output file. Defaults to None. + The prefix for the output file. Defaults to None. + + output_dir : Path, optional + The path to the directory in which the acquisition file + should be written. If not provided, it will be written + in the same folder as the provided dwi filename. Returns ------- - acq_filename : str - Path to the acq.txt file. + acq_filename : Path + The path to the acquisition file. """ - from pathlib import Path - import numpy as np - from clinica.utils.dwi import _get_phase_basis_vector # noqa - if fsl_phase_encoding_direction not in ("x", "y", "z", "x-", "y-", "z-"): raise RuntimeError( f"FSL PhaseEncodingDirection (found value: {fsl_phase_encoding_direction}) " f"is unknown, it should be a value in (x, y, z, x-, y-, z-)" ) - dwi_filename = Path(dwi_filename) acq_filename = f"{image_id}_acq.txt" if image_id else "acq.txt" - acq_filename = dwi_filename.parent / acq_filename + output_dir = output_dir or dwi_filename.parent + acq_filename = output_dir / acq_filename basis_vector = _get_phase_basis_vector(fsl_phase_encoding_direction) basis_vector.append(float(total_readout_time)) np.savetxt(acq_filename, np.array([basis_vector]), fmt="%d " * 3 + "%f") - return str(acq_filename) + return acq_filename def _get_phase_basis_vector(phase: str) -> list: diff --git a/test/nonregression/pipelines/dwi/preprocessing/test_t1.py b/test/nonregression/pipelines/dwi/preprocessing/test_t1.py index 4e3f1e6d2..09cd18514 100644 --- a/test/nonregression/pipelines/dwi/preprocessing/test_t1.py +++ b/test/nonregression/pipelines/dwi/preprocessing/test_t1.py @@ -198,6 +198,7 @@ def test_dwi_eddy_fsl(cmdopt, tmp_path): input_dir, tmp_dir, ref_dir = configure_paths(base_dir, tmp_path, "DWIEddyFSL") (tmp_path / "tmp").mkdir() eddy_fsl = eddy_fsl_pipeline( + base_dir=str(base_dir), use_cuda=False, initrand=True, compute_mask=True, diff --git a/test/unittests/utils/test_dwi.py b/test/unittests/utils/test_dwi.py index 552f83eea..984627d76 100644 --- a/test/unittests/utils/test_dwi.py +++ b/test/unittests/utils/test_dwi.py @@ -91,9 +91,9 @@ def test_generate_acq_file(tmp_path, image_id, phase, expected): tmp_path / "dwi.nii.gz", phase, "16", image_id=image_id ) if image_id: - assert acq_file == str(tmp_path / f"{image_id}_acq.txt") + assert acq_file == tmp_path / f"{image_id}_acq.txt" else: - assert acq_file == str(tmp_path / "acq.txt") + assert acq_file == tmp_path / "acq.txt" with open(acq_file, "r") as fp: lines = fp.readlines() assert lines == expected @@ -106,7 +106,7 @@ def test_generate_index_file_bvalue_file_error(tmp_path): FileNotFoundError, match="Unable to find b-values file", ): - generate_index_file(str(tmp_path / "foo.txt")) + generate_index_file(tmp_path / "foo.txt") @pytest.mark.parametrize("image_id", [None, "foo", "foo_bar"]) @@ -114,11 +114,11 @@ def test_generate_index_file(tmp_path, image_id): from clinica.utils.dwi import generate_index_file np.savetxt(tmp_path / "foo.bval", [0] + [1000] * 7) - index_file = generate_index_file(str(tmp_path / "foo.bval"), image_id=image_id) + index_file = generate_index_file(tmp_path / "foo.bval", image_id=image_id) if image_id: - assert index_file == str(tmp_path / f"{image_id}_index.txt") + assert index_file == tmp_path / f"{image_id}_index.txt" else: - assert index_file == str(tmp_path / "index.txt") + assert index_file == tmp_path / "index.txt" index = np.loadtxt(index_file) assert_array_equal(index, np.ones(8))