Skip to content

Commit

Permalink
♻️ Exclusively use custom Function Nodes + 🚨 Lint
Browse files Browse the repository at this point in the history
  • Loading branch information
shnizzedy committed Jul 8, 2024
1 parent c7819d1 commit f194377
Show file tree
Hide file tree
Showing 25 changed files with 385 additions and 254 deletions.
30 changes: 15 additions & 15 deletions CPAC/anat_preproc/anat_preproc.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

# You should have received a copy of the GNU Lesser General Public
# License along with C-PAC. If not, see <https://www.gnu.org/licenses/>.
# from copy import deepcopy
import os

from nipype.interfaces import afni, ants, freesurfer, fsl
Expand All @@ -36,6 +35,7 @@
)
from CPAC.pipeline import nipype_pipeline_engine as pe
from CPAC.pipeline.nodeblock import nodeblock
from CPAC.utils.interfaces import Function
from CPAC.utils.interfaces.fsl import Merge as fslMerge


Expand Down Expand Up @@ -138,7 +138,7 @@ def acpc_alignment(

aff_to_rig_imports = ["import os", "from numpy import *"]
aff_to_rig = pe.Node(
util.Function(
Function(
input_names=["in_xfm", "out_name"],
output_names=["out_mat"],
function=fsl_aff_to_rigid,
Expand Down Expand Up @@ -319,7 +319,7 @@ def T1wmulT2w_brain_norm_s_string(sigma, in_file):
return "-s %f -div %s" % (sigma, in_file)

T1wmulT2w_brain_norm_s_string = pe.Node(
util.Function(
Function(
input_names=["sigma", "in_file"],
output_names=["out_str"],
function=T1wmulT2w_brain_norm_s_string,
Expand Down Expand Up @@ -378,7 +378,7 @@ def form_lower_string(mean, std):
return "-thr %s -bin -ero -mul 255" % (lower)

form_lower_string = pe.Node(
util.Function(
Function(
input_names=["mean", "std"],
output_names=["out_str"],
function=form_lower_string,
Expand Down Expand Up @@ -444,7 +444,7 @@ def file_to_a_list(infile_1, infile_2):
return [infile_1, infile_2]

file_to_a_list = pe.Node(
util.Function(
Function(
input_names=["infile_1", "infile_2"],
output_names=["out_list"],
function=file_to_a_list,
Expand Down Expand Up @@ -544,7 +544,7 @@ def afni_brain_connector(wf, cfg, strat_pool, pipe_num, opt):
)

skullstrip_args = pe.Node(
util.Function(
Function(
input_names=[
"spat_norm",
"spat_norm_dxyz",
Expand Down Expand Up @@ -762,7 +762,7 @@ def fsl_brain_connector(wf, cfg, strat_pool, pipe_num, opt):
anat_robustfov.inputs.output_type = "NIFTI_GZ"

anat_pad_RobustFOV_cropped = pe.Node(
util.Function(
Function(
input_names=["cropped_image_path", "target_image_path"],
output_names=["padded_image_path"],
function=pad,
Expand Down Expand Up @@ -902,7 +902,7 @@ def unet_brain_connector(wf, cfg, strat_pool, pipe_num, opt):
from CPAC.unet.function import predict_volumes

unet_mask = pe.Node(
util.Function(
Function(
input_names=["model_path", "cimg_in"],
output_names=["out_path"],
function=predict_volumes,
Expand Down Expand Up @@ -1083,7 +1083,7 @@ def freesurfer_brain_connector(wf, cfg, strat_pool, pipe_num, opt):

# convert brain mask file from .mgz to .nii.gz
fs_brain_mask_to_nifti = pe.Node(
util.Function(
Function(
input_names=["in_file"], output_names=["out_file"], function=mri_convert
),
name=f"fs_brainmask_to_nifti_{pipe_num}",
Expand Down Expand Up @@ -1119,7 +1119,7 @@ def freesurfer_abcd_brain_connector(wf, cfg, strat_pool, pipe_num, opt):
Ref: https://github.com/DCAN-Labs/DCAN-HCP/blob/7927754/PostFreeSurfer/PostFreeSurferPipeline.sh#L151-L156
"""
wmparc_to_nifti = pe.Node(
util.Function(
Function(
input_names=["in_file", "reslice_like", "args"],
output_names=["out_file"],
function=mri_convert,
Expand All @@ -1130,7 +1130,7 @@ def freesurfer_abcd_brain_connector(wf, cfg, strat_pool, pipe_num, opt):
# Register wmparc file if ingressing FreeSurfer data
if strat_pool.check_rpool("pipeline-fs_xfm"):
wmparc_to_native = pe.Node(
util.Function(
Function(
input_names=["source_file", "target_file", "xfm", "out_file"],
output_names=["transformed_file"],
function=normalize_wmparc,
Expand Down Expand Up @@ -1168,7 +1168,7 @@ def freesurfer_abcd_brain_connector(wf, cfg, strat_pool, pipe_num, opt):
wf.connect(wmparc_to_nifti, "out_file", binary_mask, "in_file")

wb_command_fill_holes = pe.Node(
util.Function(
Function(
input_names=["in_file"], output_names=["out_file"], function=wb_command
),
name=f"wb_command_fill_holes_{pipe_num}",
Expand Down Expand Up @@ -1206,7 +1206,7 @@ def freesurfer_fsl_brain_connector(wf, cfg, strat_pool, pipe_num, opt):

# mri_convert -it mgz ${SUBJECTS_DIR}/${subject}/mri/brainmask.mgz -ot nii brainmask.nii.gz
convert_fs_brainmask_to_nifti = pe.Node(
util.Function(
Function(
input_names=["in_file"], output_names=["out_file"], function=mri_convert
),
name=f"convert_fs_brainmask_to_nifti_{node_id}",
Expand All @@ -1217,7 +1217,7 @@ def freesurfer_fsl_brain_connector(wf, cfg, strat_pool, pipe_num, opt):

# mri_convert -it mgz ${SUBJECTS_DIR}/${subject}/mri/T1.mgz -ot nii T1.nii.gz
convert_fs_T1_to_nifti = pe.Node(
util.Function(
Function(
input_names=["in_file"], output_names=["out_file"], function=mri_convert
),
name=f"convert_fs_T1_to_nifti_{node_id}",
Expand Down Expand Up @@ -2888,7 +2888,7 @@ def freesurfer_abcd_preproc(wf, cfg, strat_pool, pipe_num, opt=None):

# fslmaths "$T1wImageFile"_1mm.nii.gz -div $Mean -mul 150 -abs "$T1wImageFile"_1mm.nii.gz
normalize_head = pe.Node(
util.Function(
Function(
input_names=["in_file", "number", "out_file_suffix"],
output_names=["out_file"],
function=fslmaths_command,
Expand Down
28 changes: 22 additions & 6 deletions CPAC/anat_preproc/lesion_preproc.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2019-2023 C-PAC Developers

# This file is part of C-PAC.

# C-PAC is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.

# C-PAC is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.

# You should have received a copy of the GNU Lesser General Public
# License along with C-PAC. If not, see <https://www.gnu.org/licenses/>.
from nipype.interfaces import afni
import nipype.interfaces.utility as util

from CPAC.pipeline import nipype_pipeline_engine as pe
from CPAC.utils.interfaces import Function


def inverse_lesion(lesion_path):
"""
"""Replace non-zeroes with zeroes and zeroes with ones.
Check if the image contains more zeros than non-zeros, if so,
replaces non-zeros by zeros and zeros by ones.
Expand Down Expand Up @@ -38,13 +55,12 @@ def inverse_lesion(lesion_path):
nii = nu.inverse_nifti_values(image=lesion_path)
nib.save(nii, lesion_out)
return lesion_out
else:
return lesion_out
return lesion_out


def create_lesion_preproc(wf_name="lesion_preproc"):
"""
The main purpose of this workflow is to process lesions masks.
"""Process lesions masks.
Lesion mask file is deobliqued and reoriented in the same way as the T1 in
the anat_preproc function.
Expand Down Expand Up @@ -95,7 +111,7 @@ def create_lesion_preproc(wf_name="lesion_preproc"):
lesion_deoblique.inputs.deoblique = True

lesion_inverted = pe.Node(
interface=util.Function(
interface=Function(
input_names=["lesion_path"],
output_names=["lesion_out"],
function=inverse_lesion,
Expand Down
98 changes: 28 additions & 70 deletions CPAC/anat_preproc/utils.py
Original file line number Diff line number Diff line change
@@ -1,73 +1,34 @@
# -*- coding: utf-8 -*-
from numpy import zeros
from nibabel import load as nib_load, Nifti1Image
import nipype.interfaces.utility as util

from CPAC.pipeline import nipype_pipeline_engine as pe


def get_shape(nifti_image):
return nib_load(nifti_image).shape


def pad(cropped_image_path, target_image_path):
"""
Pad a cropped image to match the dimensions of a target image along the z-axis,
while keeping padded image aligned with target_image.
Parameters
----------
- cropped_image_path (str): The file path to the cropped image (NIfTI format).
- target_image_path (str): The file path to the target image (NIfTI format).
Returns
-------
- str: The file path to the saved padded image (NIfTI format).
# Copyright (C) 2018-2023 C-PAC Developers

The function loads cropped and target iamges, calculates the z-dimension shift required for alignment such
that the mask generated from padded image will work correctly on the target image. The result padded image is
saved as an NIfTI file in the working directory/node and file path is returned as output.
# This file is part of C-PAC.

Note: The function assumes that the input images are in NIfTI format and have compatible dimensions. The cropped
and target image should only differ in z-axis dimension.
"""
from os import getcwd, path
from typing import Optional
# C-PAC is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.

from numpy import asanyarray, ndarray, zeros_like
from nibabel import load, Nifti1Image, save
# C-PAC is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.

cropped_image: Optional[ndarray] = asanyarray(load(cropped_image_path).dataobj)
target_image: Optional[ndarray] = asanyarray(load(target_image_path).dataobj)
# You should have received a copy of the GNU Lesser General Public
# License along with C-PAC. If not, see <https://www.gnu.org/licenses/>.
import os

# Taking 1 slice to calculate the z dimension shift from top
center_row: int = target_image.shape[0] // 2
center_column: int = target_image.shape[1] // 2
z_slice_cropped_image: Optional[ndarray] = cropped_image[
center_row, center_column, :
]
z_slice_target_image: Optional[ndarray] = target_image[center_row, center_column, :]

for z_shift in range(len(z_slice_target_image) - len(z_slice_cropped_image) + 1):
if (
z_slice_target_image[z_shift : z_shift + len(z_slice_cropped_image)]
== z_slice_cropped_image
).all():
break
from numpy import *
from nibabel import load as nib_load
from nipype.interfaces.base import CommandLineInputSpec, File, TraitedSpec
import nipype.interfaces.utility as util
from nipype.interfaces.workbench.base import WBCommand

padded_image_matrix: Optional[ndarray] = zeros_like(target_image)
padded_image_matrix[:, :, z_shift : cropped_image.shape[2] + z_shift] = (
cropped_image
)
padded_image_path: str = path.join(getcwd(), "padded_image_T1w.nii.gz")
cropped_image = load(cropped_image_path)
save(
Nifti1Image(padded_image_matrix, affine=cropped_image.affine), padded_image_path
)
return padded_image_path
from CPAC.pipeline import nipype_pipeline_engine as pe
from CPAC.utils.interfaces import Function


def get_shape(nifti_image):
"""Return the shape of a NIfTI image."""
return nib_load(nifti_image).shape


Expand Down Expand Up @@ -286,7 +247,7 @@ def split_hemi(multi_file):

def split_hemi_interface() -> util.Function:
"""Return a function interface for split_hemi."""
return util.Function(
return Function(
input_names=["multi_file"], output_names=["lh", "rh"], function=split_hemi
)

Expand Down Expand Up @@ -587,12 +548,9 @@ def normalize_wmparc(source_file, target_file, xfm, out_file):
return os.path.join(os.getcwd(), out_file)


"""This module provides interfaces for workbench -volume-remove-islands commands"""
from nipype.interfaces.base import CommandLineInputSpec, File, TraitedSpec
from nipype.interfaces.workbench.base import WBCommand


class VolumeRemoveIslandsInputSpec(CommandLineInputSpec):
"""InputSpec for workbench -volume-remove-islands commands."""

in_file = File(
exists=True,
mandatory=True,
Expand All @@ -610,14 +568,14 @@ class VolumeRemoveIslandsInputSpec(CommandLineInputSpec):


class VolumeRemoveIslandsOutputSpec(TraitedSpec):
"""OutputSpec for workbench -volume-remove-islands commands."""

out_file = File(exists=True, desc="the output ROI volume")


class VolumeRemoveIslands(WBCommand):
"""
workbench
-volume-remove-islands
REMOVE ISLANDS FROM AN ROI VOLUME
"""Remove islandes from an ROI volume.
wb_command -volume-remove-islands
<volume-in> - the input ROI volume
<volume-out> - output - the output ROI volume.
Expand Down
Loading

0 comments on commit f194377

Please sign in to comment.