Skip to content

Commit

Permalink
try fixing issue of DWI pipeline writing stuffs in input data folders...
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasGensollen committed Oct 10, 2023
1 parent b2eefff commit 23fbcb0
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ def prepare_phasediff_fmap(


def compute_reference_b0(
base_dir: str,
b_value_threshold: float,
use_cuda: bool,
initrand: bool,
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@


def eddy_fsl_pipeline(
base_dir: str,
use_cuda: bool,
initrand: bool,
compute_mask: bool = True,
Expand All @@ -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.
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down
57 changes: 33 additions & 24 deletions clinica/utils/dwi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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:
Expand Down
1 change: 1 addition & 0 deletions test/nonregression/pipelines/dwi/preprocessing/test_t1.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
12 changes: 6 additions & 6 deletions test/unittests/utils/test_dwi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -106,19 +106,19 @@ 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"])
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))

Expand Down

0 comments on commit 23fbcb0

Please sign in to comment.