[ENH]: Refactor Preprocessors to sync data and their paths #408
9 changed files with 290 additions and 118 deletions
1
docs/changes/newsfragments/408.enh
Normal file
1
docs/changes/newsfragments/408.enh
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Preprocessors to sync data their paths so that the results are idempotent by `Synchon Mandal`_
|
||||||
1
docs/changes/newsfragments/408.feature
Normal file
1
docs/changes/newsfragments/408.feature
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Allow masks to be warped, if found, in :class:`.SpaceWarper` by `Synchon Mandal`_
|
||||||
|
|
@ -12,6 +12,7 @@ from typing import (
|
||||||
Union,
|
Union,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import nibabel as nib
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from nilearn import image as nimg
|
from nilearn import image as nimg
|
||||||
|
|
@ -19,6 +20,7 @@ from nilearn._utils.niimg_conversions import check_niimg_4d
|
||||||
|
|
||||||
from ...api.decorators import register_preprocessor
|
from ...api.decorators import register_preprocessor
|
||||||
from ...data import get_data
|
from ...data import get_data
|
||||||
|
from ...pipeline import WorkDirManager
|
||||||
from ...typing import Dependencies
|
from ...typing import Dependencies
|
||||||
from ...utils import logger, raise_error
|
from ...utils import logger, raise_error
|
||||||
from ..base import BasePreprocessor
|
from ..base import BasePreprocessor
|
||||||
|
|
@ -539,9 +541,17 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Read t_r from NIfTI header: {t_r}",
|
f"Read t_r from NIfTI header: {t_r}",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Create element-specific tempdir for storing generated data
|
||||||
|
# and / or mask
|
||||||
|
element_tempdir = WorkDirManager().get_element_tempdir(
|
||||||
|
prefix="fmriprep_confound_remover"
|
||||||
|
)
|
||||||
|
|
||||||
# Set mask data
|
# Set mask data
|
||||||
mask_img = None
|
mask_img = None
|
||||||
if self.masks is not None:
|
if self.masks is not None:
|
||||||
|
# Generate mask
|
||||||
logger.debug(f"Masking with {self.masks}")
|
logger.debug(f"Masking with {self.masks}")
|
||||||
mask_img = get_data(
|
mask_img = get_data(
|
||||||
kind="mask",
|
kind="mask",
|
||||||
|
|
@ -549,13 +559,21 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
||||||
target_data=input,
|
target_data=input,
|
||||||
extra_input=extra_input,
|
extra_input=extra_input,
|
||||||
)
|
)
|
||||||
# Return the BOLD mask and link it to the BOLD data type dict;
|
# Save generated mask for use later
|
||||||
|
generated_mask_img_path = element_tempdir / "generated_mask.nii.gz"
|
||||||
|
nib.save(mask_img, generated_mask_img_path)
|
||||||
|
|
||||||
|
# Save BOLD mask and link it to the BOLD data type dict;
|
||||||
# this allows to use "inherit" down the pipeline
|
# this allows to use "inherit" down the pipeline
|
||||||
logger.debug("Setting `BOLD.mask`")
|
logger.debug("Setting `BOLD.mask`")
|
||||||
input.update(
|
input.update(
|
||||||
{
|
{
|
||||||
"mask": {
|
"mask": {
|
||||||
|
# Update path to sync with "data"
|
||||||
|
"path": generated_mask_img_path,
|
||||||
|
# Update data
|
||||||
"data": mask_img,
|
"data": mask_img,
|
||||||
|
# Should be in the same space as target data
|
||||||
"space": input["space"],
|
"space": input["space"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -566,7 +584,9 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
||||||
logger.debug(f"\tstandardize: {self.standardize}")
|
logger.debug(f"\tstandardize: {self.standardize}")
|
||||||
logger.debug(f"\tlow_pass: {self.low_pass}")
|
logger.debug(f"\tlow_pass: {self.low_pass}")
|
||||||
logger.debug(f"\thigh_pass: {self.high_pass}")
|
logger.debug(f"\thigh_pass: {self.high_pass}")
|
||||||
input["data"] = nimg.clean_img(
|
|
||||||
|
# Deconfound data
|
||||||
|
cleaned_img = nimg.clean_img(
|
||||||
imgs=bold_img,
|
imgs=bold_img,
|
||||||
detrend=self.detrend,
|
detrend=self.detrend,
|
||||||
standardize=self.standardize,
|
standardize=self.standardize,
|
||||||
|
|
@ -576,5 +596,18 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
||||||
t_r=t_r,
|
t_r=t_r,
|
||||||
mask_img=mask_img,
|
mask_img=mask_img,
|
||||||
)
|
)
|
||||||
|
# Save deconfounded data
|
||||||
|
deconfounded_img_path = element_tempdir / "deconfounded_data.nii.gz"
|
||||||
|
nib.save(cleaned_img, deconfounded_img_path)
|
||||||
|
|
||||||
|
logger.debug("Updating `BOLD`")
|
||||||
|
input.update(
|
||||||
|
{
|
||||||
|
# Update path to sync with "data"
|
||||||
|
"path": deconfounded_img_path,
|
||||||
|
# Update data
|
||||||
|
"data": cleaned_img,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return input, None
|
return input, None
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
# License: AGPL
|
# License: AGPL
|
||||||
|
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
Any,
|
||||||
ClassVar,
|
ClassVar,
|
||||||
Union,
|
Union,
|
||||||
)
|
)
|
||||||
|
|
@ -16,10 +16,6 @@ from ...typing import Dependencies, ExternalDependencies
|
||||||
from ...utils import logger, run_ext_cmd
|
from ...utils import logger, run_ext_cmd
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from nibabel.nifti1 import Nifti1Image
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["AFNISmoothing"]
|
__all__ = ["AFNISmoothing"]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -41,23 +37,23 @@ class AFNISmoothing:
|
||||||
|
|
||||||
def preprocess(
|
def preprocess(
|
||||||
self,
|
self,
|
||||||
data: "Nifti1Image",
|
input: dict[str, Any],
|
||||||
fwhm: Union[int, float],
|
fwhm: Union[int, float],
|
||||||
) -> "Nifti1Image":
|
) -> dict[str, Any]:
|
||||||
"""Preprocess using AFNI.
|
"""Preprocess using AFNI.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
data : Niimg-like object
|
input : dict
|
||||||
Image(s) to preprocess.
|
A single input from the Junifer Data object in which to preprocess.
|
||||||
fwhm : int or float
|
fwhm : int or float
|
||||||
Smooth until the value. AFNI estimates the smoothing and then
|
Smooth until the value. AFNI estimates the smoothing and then
|
||||||
applies smoothing to reach ``fwhm``.
|
applies smoothing to reach ``fwhm``.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Niimg-like object
|
dict
|
||||||
The preprocessed image(s).
|
The ``input`` dictionary with updated values.
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
-----
|
-----
|
||||||
|
|
@ -71,18 +67,18 @@ class AFNISmoothing:
|
||||||
"""
|
"""
|
||||||
logger.info("Smoothing using AFNI")
|
logger.info("Smoothing using AFNI")
|
||||||
|
|
||||||
# Create component-scoped tempdir
|
# Create element-scoped tempdir so that the output is
|
||||||
tempdir = WorkDirManager().get_tempdir(prefix="afni_smoothing")
|
# available later as nibabel stores file path reference for
|
||||||
|
# loading on computation
|
||||||
# Save target data to a component-scoped tempfile
|
element_tempdir = WorkDirManager().get_element_tempdir(
|
||||||
nifti_in_file_path = tempdir / "input.nii" # needs to be .nii
|
prefix="afni_smoothing"
|
||||||
nib.save(data, nifti_in_file_path)
|
)
|
||||||
|
|
||||||
# Set 3dBlurToFWHM command
|
# Set 3dBlurToFWHM command
|
||||||
blur_out_path_prefix = tempdir / "blur"
|
blur_out_path_prefix = element_tempdir / "blur"
|
||||||
blur_cmd = [
|
blur_cmd = [
|
||||||
"3dBlurToFWHM",
|
"3dBlurToFWHM",
|
||||||
f"-input {nifti_in_file_path.resolve()}",
|
f"-input {input['path'].resolve()}",
|
||||||
f"-prefix {blur_out_path_prefix.resolve()}",
|
f"-prefix {blur_out_path_prefix.resolve()}",
|
||||||
"-automask",
|
"-automask",
|
||||||
f"-FWHM {fwhm}",
|
f"-FWHM {fwhm}",
|
||||||
|
|
@ -90,28 +86,34 @@ class AFNISmoothing:
|
||||||
# Call 3dBlurToFWHM
|
# Call 3dBlurToFWHM
|
||||||
run_ext_cmd(name="3dBlurToFWHM", cmd=blur_cmd)
|
run_ext_cmd(name="3dBlurToFWHM", cmd=blur_cmd)
|
||||||
|
|
||||||
# Create element-scoped tempdir so that the blurred output is
|
# Read header to get output suffix
|
||||||
# available later as nibabel stores file path reference for
|
header = input["data"].header
|
||||||
# loading on computation
|
sform_code = header.get_sform(coded=True)[1]
|
||||||
element_tempdir = WorkDirManager().get_element_tempdir(
|
if sform_code == 4:
|
||||||
prefix="afni_blur"
|
output_suffix = "tlrc"
|
||||||
)
|
else:
|
||||||
|
output_suffix = "orig"
|
||||||
|
|
||||||
# Convert afni to nifti
|
# Convert afni to nifti
|
||||||
blur_afni_to_nifti_out_path = (
|
blur_nifti_out_path = (
|
||||||
element_tempdir / "output.nii" # needs to be .nii
|
element_tempdir / "smoothed_data.nii" # needs to be .nii
|
||||||
)
|
)
|
||||||
convert_cmd = [
|
convert_cmd = [
|
||||||
"3dAFNItoNIFTI",
|
"3dAFNItoNIFTI",
|
||||||
f"-prefix {blur_afni_to_nifti_out_path.resolve()}",
|
f"-prefix {blur_nifti_out_path.resolve()}",
|
||||||
f"{blur_out_path_prefix}+orig.BRIK",
|
f"{blur_out_path_prefix}+{output_suffix}.BRIK",
|
||||||
]
|
]
|
||||||
# Call 3dAFNItoNIFTI
|
# Call 3dAFNItoNIFTI
|
||||||
run_ext_cmd(name="3dAFNItoNIFTI", cmd=convert_cmd)
|
run_ext_cmd(name="3dAFNItoNIFTI", cmd=convert_cmd)
|
||||||
|
|
||||||
|
logger.debug("Updating smoothed data")
|
||||||
|
input.update(
|
||||||
|
{
|
||||||
|
# Update path to sync with "data"
|
||||||
|
"path": blur_nifti_out_path,
|
||||||
# Load nifti
|
# Load nifti
|
||||||
output_data = nib.load(blur_afni_to_nifti_out_path)
|
"data": nib.load(blur_nifti_out_path),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Delete tempdir
|
return input
|
||||||
WorkDirManager().delete_tempdir(tempdir)
|
|
||||||
|
|
||||||
return output_data # type: ignore
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
# License: AGPL
|
# License: AGPL
|
||||||
|
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
Any,
|
||||||
ClassVar,
|
ClassVar,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -15,10 +15,6 @@ from ...typing import Dependencies, ExternalDependencies
|
||||||
from ...utils import logger, run_ext_cmd
|
from ...utils import logger, run_ext_cmd
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from nibabel.nifti1 import Nifti1Image
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["FSLSmoothing"]
|
__all__ = ["FSLSmoothing"]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -40,16 +36,16 @@ class FSLSmoothing:
|
||||||
|
|
||||||
def preprocess(
|
def preprocess(
|
||||||
self,
|
self,
|
||||||
data: "Nifti1Image",
|
input: dict[str, Any],
|
||||||
brightness_threshold: float,
|
brightness_threshold: float,
|
||||||
fwhm: float,
|
fwhm: float,
|
||||||
) -> "Nifti1Image":
|
) -> dict[str, Any]:
|
||||||
"""Preprocess using FSL.
|
"""Preprocess using FSL.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
data : Niimg-like object
|
input : dict
|
||||||
Image(s) to preprocess.
|
A single input from the Junifer Data object in which to preprocess.
|
||||||
brightness_threshold : float
|
brightness_threshold : float
|
||||||
Threshold to discriminate between noise and the underlying image.
|
Threshold to discriminate between noise and the underlying image.
|
||||||
The value should be set greater than the noise level and less than
|
The value should be set greater than the noise level and less than
|
||||||
|
|
@ -59,8 +55,8 @@ class FSLSmoothing:
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Niimg-like object
|
dict
|
||||||
The preprocessed image(s).
|
The ``input`` dictionary with updated values.
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
-----
|
-----
|
||||||
|
|
@ -76,24 +72,17 @@ class FSLSmoothing:
|
||||||
"""
|
"""
|
||||||
logger.info("Smoothing using FSL")
|
logger.info("Smoothing using FSL")
|
||||||
|
|
||||||
# Create component-scoped tempdir
|
|
||||||
tempdir = WorkDirManager().get_tempdir(prefix="fsl_smoothing")
|
|
||||||
|
|
||||||
# Save target data to a component-scoped tempfile
|
|
||||||
nifti_in_file_path = tempdir / "input.nii.gz"
|
|
||||||
nib.save(data, nifti_in_file_path)
|
|
||||||
|
|
||||||
# Create element-scoped tempdir so that the output is
|
# Create element-scoped tempdir so that the output is
|
||||||
# available later as nibabel stores file path reference for
|
# available later as nibabel stores file path reference for
|
||||||
# loading on computation
|
# loading on computation
|
||||||
element_tempdir = WorkDirManager().get_element_tempdir(
|
element_tempdir = WorkDirManager().get_element_tempdir(
|
||||||
prefix="fsl_susan"
|
prefix="fsl_susan"
|
||||||
)
|
)
|
||||||
susan_out_path = element_tempdir / "output.nii.gz"
|
susan_out_path = element_tempdir / "smoothed_data.nii.gz"
|
||||||
# Set susan command
|
# Set susan command
|
||||||
susan_cmd = [
|
susan_cmd = [
|
||||||
"susan",
|
"susan",
|
||||||
f"{nifti_in_file_path.resolve()}",
|
f"{input['path'].resolve()}",
|
||||||
f"{brightness_threshold}",
|
f"{brightness_threshold}",
|
||||||
f"{fwhm}",
|
f"{fwhm}",
|
||||||
"3", # dimension
|
"3", # dimension
|
||||||
|
|
@ -104,10 +93,14 @@ class FSLSmoothing:
|
||||||
# Call susan
|
# Call susan
|
||||||
run_ext_cmd(name="susan", cmd=susan_cmd)
|
run_ext_cmd(name="susan", cmd=susan_cmd)
|
||||||
|
|
||||||
|
logger.debug("Updating smoothed data")
|
||||||
|
input.update(
|
||||||
|
{
|
||||||
|
# Update path to sync with "data"
|
||||||
|
"path": susan_out_path,
|
||||||
# Load nifti
|
# Load nifti
|
||||||
output_data = nib.load(susan_out_path)
|
"data": nib.load(susan_out_path),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Delete tempdir
|
return input
|
||||||
WorkDirManager().delete_tempdir(tempdir)
|
|
||||||
|
|
||||||
return output_data # type: ignore
|
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,21 @@
|
||||||
# License: AGPL
|
# License: AGPL
|
||||||
|
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
Any,
|
||||||
ClassVar,
|
ClassVar,
|
||||||
Literal,
|
Literal,
|
||||||
Union,
|
Union,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import nibabel as nib
|
||||||
from nilearn import image as nimg
|
from nilearn import image as nimg
|
||||||
from numpy.typing import ArrayLike
|
from numpy.typing import ArrayLike
|
||||||
|
|
||||||
|
from ...pipeline import WorkDirManager
|
||||||
from ...typing import Dependencies
|
from ...typing import Dependencies
|
||||||
from ...utils import logger
|
from ...utils import logger
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from nibabel.nifti1 import Nifti1Image
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["NilearnSmoothing"]
|
__all__ = ["NilearnSmoothing"]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -35,15 +33,15 @@ class NilearnSmoothing:
|
||||||
|
|
||||||
def preprocess(
|
def preprocess(
|
||||||
self,
|
self,
|
||||||
data: "Nifti1Image",
|
input: dict[str, Any],
|
||||||
fwhm: Union[int, float, ArrayLike, Literal["fast"], None],
|
fwhm: Union[int, float, ArrayLike, Literal["fast"], None],
|
||||||
) -> "Nifti1Image":
|
) -> dict[str, Any]:
|
||||||
"""Preprocess using nilearn.
|
"""Preprocess using nilearn.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
data : Niimg-like object
|
input : dict
|
||||||
Image(s) to preprocess.
|
A single input from the Junifer Data object in which to preprocess.
|
||||||
fwhm : scalar, ``numpy.ndarray``, tuple or list of scalar, "fast" or \
|
fwhm : scalar, ``numpy.ndarray``, tuple or list of scalar, "fast" or \
|
||||||
None
|
None
|
||||||
Smoothing strength, as a full-width at half maximum, in
|
Smoothing strength, as a full-width at half maximum, in
|
||||||
|
|
@ -61,9 +59,32 @@ class NilearnSmoothing:
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Niimg-like object
|
dict
|
||||||
The preprocessed image(s).
|
The ``input`` dictionary with updated values.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
logger.info("Smoothing using nilearn")
|
logger.info("Smoothing using nilearn")
|
||||||
return nimg.smooth_img(imgs=data, fwhm=fwhm) # type: ignore
|
|
||||||
|
# Create element-scoped tempdir so that the output is
|
||||||
|
# available later as nibabel stores file path reference for
|
||||||
|
# loading on computation
|
||||||
|
element_tempdir = WorkDirManager().get_element_tempdir(
|
||||||
|
prefix="nilearn_smoothing"
|
||||||
|
)
|
||||||
|
|
||||||
|
smoothed_img = nimg.smooth_img(imgs=input["data"], fwhm=fwhm)
|
||||||
|
|
||||||
|
# Save smoothed output
|
||||||
|
smoothed_img_path = element_tempdir / "smoothed_data.nii.gz"
|
||||||
|
nib.save(smoothed_img, smoothed_img_path)
|
||||||
|
|
||||||
|
logger.debug("Updating smoothed data")
|
||||||
|
input.update(
|
||||||
|
{
|
||||||
|
# Update path to sync with "data"
|
||||||
|
"path": smoothed_img_path,
|
||||||
|
"data": smoothed_img,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return input
|
||||||
|
|
|
||||||
|
|
@ -164,12 +164,9 @@ class Smoothing(BasePreprocessor):
|
||||||
elif self.using == "fsl":
|
elif self.using == "fsl":
|
||||||
preprocessor = FSLSmoothing()
|
preprocessor = FSLSmoothing()
|
||||||
# Smooth
|
# Smooth
|
||||||
output = preprocessor.preprocess( # type: ignore
|
input = preprocessor.preprocess(
|
||||||
data=input["data"],
|
input=input,
|
||||||
**self.smoothing_params,
|
**self.smoothing_params,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Modify target data
|
|
||||||
input["data"] = output
|
|
||||||
|
|
||||||
return input, None
|
return input, None
|
||||||
|
|
|
||||||
|
|
@ -58,9 +58,7 @@ class ANTsWarper:
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
dict
|
dict
|
||||||
The ``input`` dictionary with modified ``data`` and ``space`` key
|
The ``input`` dictionary with updated values.
|
||||||
values and new ``reference`` key whose value points to the
|
|
||||||
reference file used for warping.
|
|
||||||
|
|
||||||
Raises
|
Raises
|
||||||
------
|
------
|
||||||
|
|
@ -110,7 +108,7 @@ class ANTsWarper:
|
||||||
run_ext_cmd(name="ResampleImage", cmd=resample_image_cmd)
|
run_ext_cmd(name="ResampleImage", cmd=resample_image_cmd)
|
||||||
|
|
||||||
# Create a tempfile for warped output
|
# Create a tempfile for warped output
|
||||||
apply_transforms_out_path = element_tempdir / "output.nii.gz"
|
apply_transforms_out_path = element_tempdir / "warped_data.nii.gz"
|
||||||
# Set antsApplyTransforms command
|
# Set antsApplyTransforms command
|
||||||
apply_transforms_cmd = [
|
apply_transforms_cmd = [
|
||||||
"antsApplyTransforms",
|
"antsApplyTransforms",
|
||||||
|
|
@ -126,14 +124,59 @@ class ANTsWarper:
|
||||||
# Call antsApplyTransforms
|
# Call antsApplyTransforms
|
||||||
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
||||||
|
|
||||||
|
logger.debug("Updating warped data")
|
||||||
|
input.update(
|
||||||
|
{
|
||||||
|
# Update path to sync with "data"
|
||||||
|
"path": apply_transforms_out_path,
|
||||||
# Load nifti
|
# Load nifti
|
||||||
input["data"] = nib.load(apply_transforms_out_path)
|
"data": nib.load(apply_transforms_out_path),
|
||||||
# Save resampled reference path
|
|
||||||
input["reference"] = {"path": resample_image_out_path}
|
|
||||||
# Keep pre-warp space for further operations
|
|
||||||
input["prewarp_space"] = input["space"]
|
|
||||||
# Use reference input's space as warped input's space
|
# Use reference input's space as warped input's space
|
||||||
input["space"] = extra_input["T1w"]["space"]
|
"space": extra_input["T1w"]["space"],
|
||||||
|
# Save resampled reference path
|
||||||
|
"reference": {"path": resample_image_out_path},
|
||||||
|
# Keep pre-warp space for further operations
|
||||||
|
"prewarp_space": input["space"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check for data type's mask and warp if found
|
||||||
|
if input.get("mask") is not None:
|
||||||
|
# Create a tempfile for warped mask output
|
||||||
|
apply_transforms_mask_out_path = (
|
||||||
|
element_tempdir / "warped_mask.nii.gz"
|
||||||
|
)
|
||||||
|
# Set antsApplyTransforms command
|
||||||
|
apply_transforms_mask_cmd = [
|
||||||
|
"antsApplyTransforms",
|
||||||
|
"-d 3",
|
||||||
|
"-e 3",
|
||||||
|
"-n 'GenericLabel[NearestNeighbor]'",
|
||||||
|
f"-i {input['mask']['path'].resolve()}",
|
||||||
|
# use resampled reference
|
||||||
|
f"-r {input['reference']['path'].resolve()}",
|
||||||
|
f"-t {warp_file_path.resolve()}",
|
||||||
|
f"-o {apply_transforms_mask_out_path.resolve()}",
|
||||||
|
]
|
||||||
|
# Call antsApplyTransforms
|
||||||
|
run_ext_cmd(
|
||||||
|
name="antsApplyTransforms", cmd=apply_transforms_mask_cmd
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug("Updating warped mask data")
|
||||||
|
input.update(
|
||||||
|
{
|
||||||
|
"mask": {
|
||||||
|
# Update path to sync with "data"
|
||||||
|
"path": apply_transforms_mask_out_path,
|
||||||
|
# Load nifti
|
||||||
|
"data": nib.load(apply_transforms_mask_out_path),
|
||||||
|
# Use reference input's space as warped input
|
||||||
|
# mask's space
|
||||||
|
"space": extra_input["T1w"]["space"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Template space warping
|
# Template space warping
|
||||||
else:
|
else:
|
||||||
|
|
@ -149,16 +192,15 @@ class ANTsWarper:
|
||||||
target_data=input,
|
target_data=input,
|
||||||
extra_input=None,
|
extra_input=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create component-scoped tempdir
|
|
||||||
tempdir = WorkDirManager().get_tempdir(prefix="ants_warper")
|
|
||||||
# Save template
|
# Save template
|
||||||
template_space_img_path = tempdir / f"{reference}_T1w.nii.gz"
|
template_space_img_path = (
|
||||||
|
element_tempdir / f"{reference}_T1w.nii.gz"
|
||||||
|
)
|
||||||
nib.save(template_space_img, template_space_img_path)
|
nib.save(template_space_img, template_space_img_path)
|
||||||
|
|
||||||
# Create a tempfile for warped output
|
# Create a tempfile for warped output
|
||||||
warped_output_path = element_tempdir / (
|
warped_output_path = element_tempdir / (
|
||||||
f"data_warped_from_{input['space']}_to_" f"{reference}.nii.gz"
|
f"warped_data_from_{input['space']}_to_{reference}.nii.gz"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set antsApplyTransforms command
|
# Set antsApplyTransforms command
|
||||||
|
|
@ -175,14 +217,58 @@ class ANTsWarper:
|
||||||
# Call antsApplyTransforms
|
# Call antsApplyTransforms
|
||||||
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
||||||
|
|
||||||
# Delete tempdir
|
logger.debug("Updating warped data")
|
||||||
WorkDirManager().delete_tempdir(tempdir)
|
input.update(
|
||||||
|
{
|
||||||
# Modify target data
|
# Update path to sync with "data"
|
||||||
input["data"] = nib.load(warped_output_path)
|
"path": warped_output_path,
|
||||||
# Keep pre-warp space for further operations
|
# Load nifti
|
||||||
input["prewarp_space"] = input["space"]
|
"data": nib.load(warped_output_path),
|
||||||
# Update warped input's space
|
# Update warped input's space
|
||||||
input["space"] = reference
|
"space": reference,
|
||||||
|
# Save reference path
|
||||||
|
"reference": {"path": template_space_img_path},
|
||||||
|
# Keep pre-warp space for further operations
|
||||||
|
"prewarp_space": input["space"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check for data type's mask and warp if found
|
||||||
|
if input.get("mask") is not None:
|
||||||
|
# Create a tempfile for warped mask output
|
||||||
|
apply_transforms_mask_out_path = element_tempdir / (
|
||||||
|
f"warped_mask_from_{input['space']}_to_"
|
||||||
|
f"{reference}.nii.gz"
|
||||||
|
)
|
||||||
|
# Set antsApplyTransforms command
|
||||||
|
apply_transforms_mask_cmd = [
|
||||||
|
"antsApplyTransforms",
|
||||||
|
"-d 3",
|
||||||
|
"-e 3",
|
||||||
|
"-n 'GenericLabel[NearestNeighbor]'",
|
||||||
|
f"-i {input['mask']['path'].resolve()}",
|
||||||
|
# use resampled reference
|
||||||
|
f"-r {input['reference']['path'].resolve()}",
|
||||||
|
f"-t {xfm_file_path.resolve()}",
|
||||||
|
f"-o {apply_transforms_mask_out_path.resolve()}",
|
||||||
|
]
|
||||||
|
# Call antsApplyTransforms
|
||||||
|
run_ext_cmd(
|
||||||
|
name="antsApplyTransforms", cmd=apply_transforms_mask_cmd
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug("Updating warped mask data")
|
||||||
|
input.update(
|
||||||
|
{
|
||||||
|
"mask": {
|
||||||
|
# Update path to sync with "data"
|
||||||
|
"path": apply_transforms_mask_out_path,
|
||||||
|
# Load nifti
|
||||||
|
"data": nib.load(apply_transforms_mask_out_path),
|
||||||
|
# Update warped input mask's space
|
||||||
|
"space": reference,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return input
|
return input
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,7 @@ class FSLWarper:
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
dict
|
dict
|
||||||
The ``input`` dictionary with modified ``data`` and ``space`` key
|
The ``input`` dictionary with updated values.
|
||||||
values and new ``reference`` key whose value points to the
|
|
||||||
reference file used for warping.
|
|
||||||
|
|
||||||
Raises
|
Raises
|
||||||
------
|
------
|
||||||
|
|
@ -100,26 +98,66 @@ class FSLWarper:
|
||||||
run_ext_cmd(name="flirt", cmd=flirt_cmd)
|
run_ext_cmd(name="flirt", cmd=flirt_cmd)
|
||||||
|
|
||||||
# Create a tempfile for warped output
|
# Create a tempfile for warped output
|
||||||
applywarp_out_path = element_tempdir / "output.nii.gz"
|
applywarp_out_path = element_tempdir / "warped_data.nii.gz"
|
||||||
# Set applywarp command
|
# Set applywarp command
|
||||||
applywarp_cmd = [
|
applywarp_cmd = [
|
||||||
"applywarp",
|
"applywarp",
|
||||||
"--interp=spline",
|
"--interp=spline",
|
||||||
f"-i {input['path'].resolve()}",
|
f"-i {input['path'].resolve()}",
|
||||||
f"-r {flirt_out_path.resolve()}", # use resampled reference
|
# use resampled reference
|
||||||
|
f"-r {flirt_out_path.resolve()}",
|
||||||
f"-w {warp_file_path.resolve()}",
|
f"-w {warp_file_path.resolve()}",
|
||||||
f"-o {applywarp_out_path.resolve()}",
|
f"-o {applywarp_out_path.resolve()}",
|
||||||
]
|
]
|
||||||
# Call applywarp
|
# Call applywarp
|
||||||
run_ext_cmd(name="applywarp", cmd=applywarp_cmd)
|
run_ext_cmd(name="applywarp", cmd=applywarp_cmd)
|
||||||
|
|
||||||
|
logger.debug("Updating warped data")
|
||||||
|
input.update(
|
||||||
|
{
|
||||||
|
# Update path to sync with "data"
|
||||||
|
"path": applywarp_out_path,
|
||||||
# Load nifti
|
# Load nifti
|
||||||
input["data"] = nib.load(applywarp_out_path)
|
"data": nib.load(applywarp_out_path),
|
||||||
# Save resampled reference path
|
|
||||||
input["reference"] = {"path": flirt_out_path}
|
|
||||||
# Keep pre-warp space for further operations
|
|
||||||
input["prewarp_space"] = input["space"]
|
|
||||||
# Use reference input's space as warped input's space
|
# Use reference input's space as warped input's space
|
||||||
input["space"] = extra_input["T1w"]["space"]
|
"space": extra_input["T1w"]["space"],
|
||||||
|
# Save resampled reference path
|
||||||
|
"reference": {"path": flirt_out_path},
|
||||||
|
# Keep pre-warp space for further operations
|
||||||
|
"prewarp_space": input["space"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check for data type's mask and warp if found
|
||||||
|
if input.get("mask") is not None:
|
||||||
|
# Create a tempfile for warped mask output
|
||||||
|
applywarp_mask_out_path = element_tempdir / "warped_mask.nii.gz"
|
||||||
|
# Set applywarp command
|
||||||
|
applywarp_mask_cmd = [
|
||||||
|
"applywarp",
|
||||||
|
"--interp=nn",
|
||||||
|
f"-i {input['mask']['path'].resolve()}",
|
||||||
|
# use resampled reference
|
||||||
|
f"-r {input['reference']['path'].resolve()}",
|
||||||
|
f"-w {warp_file_path.resolve()}",
|
||||||
|
f"-o {applywarp_mask_out_path.resolve()}",
|
||||||
|
]
|
||||||
|
# Call applywarp
|
||||||
|
run_ext_cmd(name="applywarp", cmd=applywarp_mask_cmd)
|
||||||
|
|
||||||
|
logger.debug("Updating warped mask data")
|
||||||
|
input.update(
|
||||||
|
{
|
||||||
|
"mask": {
|
||||||
|
# Update path to sync with "data"
|
||||||
|
"path": applywarp_mask_out_path,
|
||||||
|
# Load nifti
|
||||||
|
"data": nib.load(applywarp_mask_out_path),
|
||||||
|
# Use reference input's space as warped input mask's
|
||||||
|
# space
|
||||||
|
"space": extra_input["T1w"]["space"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return input
|
return input
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue