Skip to content

Commit

Permalink
adding reorient nodes in anat, func, freesurfer and template ingress …
Browse files Browse the repository at this point in the history
…nodes
  • Loading branch information
birajstha committed Sep 26, 2024
1 parent 4d2f933 commit cf600c1
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 163 deletions.
17 changes: 3 additions & 14 deletions CPAC/anat_preproc/anat_preproc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1454,21 +1454,10 @@ def anatomical_init(wf, cfg, strat_pool, pipe_num, opt=None):
node, out = strat_pool.get_data("T1w")
wf.connect(node, out, anat_deoblique, "in_file")

anat_reorient = pe.Node(
interface=afni.Resample(),
name=f"anat_reorient_{pipe_num}",
mem_gb=0,
mem_x=(0.0115, "in_file", "t"),
)
anat_reorient.inputs.orientation = "RPI"
anat_reorient.inputs.outputtype = "NIFTI_GZ"

wf.connect(anat_deoblique, "out_file", anat_reorient, "in_file")

outputs = {
"desc-preproc_T1w": (anat_reorient, "out_file"),
"desc-reorient_T1w": (anat_reorient, "out_file"),
"desc-head_T1w": (anat_reorient, "out_file"),
"desc-preproc_T1w": (anat_deoblique, "out_file"),
"desc-reorient_T1w": (anat_deoblique, "out_file"),
"desc-head_T1w": (anat_deoblique, "out_file"),
}

return (wf, outputs)
Expand Down
16 changes: 2 additions & 14 deletions CPAC/func_preproc/func_preproc.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,21 +521,9 @@ def func_reorient(wf, cfg, strat_pool, pipe_num, opt=None):
node, out = strat_pool.get_data("bold")
wf.connect(node, out, func_deoblique, "in_file")

func_reorient = pe.Node(
interface=afni_utils.Resample(),
name=f"func_reorient_{pipe_num}",
mem_gb=0,
mem_x=(0.0115, "in_file", "t"),
)

func_reorient.inputs.orientation = "RPI"
func_reorient.inputs.outputtype = "NIFTI_GZ"

wf.connect(func_deoblique, "out_file", func_reorient, "in_file")

outputs = {
"desc-preproc_bold": (func_reorient, "out_file"),
"desc-reorient_bold": (func_reorient, "out_file"),
"desc-preproc_bold": (func_deoblique, "out_file"),
"desc-reorient_bold": (func_deoblique, "out_file"),
}

return (wf, outputs)
Expand Down
117 changes: 84 additions & 33 deletions CPAC/pipeline/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import warnings

from nipype import config, logging
from nipype.interfaces import afni
from nipype.interfaces.utility import Rename

from CPAC.image_utils.spatial_smoothing import spatial_smoothing
Expand All @@ -36,7 +37,6 @@
from CPAC.pipeline.check_outputs import ExpectedOutputs
from CPAC.pipeline.nodeblock import NodeBlockFunction
from CPAC.pipeline.utils import (
check_orientation,
MOVEMENT_FILTER_KEYS,
name_fork,
source_set,
Expand Down Expand Up @@ -1908,6 +1908,7 @@ def wrap_block(node_blocks, interface, wf, cfg, strat_pool, pipe_num, opt):


def ingress_raw_anat_data(wf, rpool, cfg, data_paths, unique_id, part_id, ses_id):
desired_orientation = cfg.pipeline_setup["desired_orientation"]
if "anat" not in data_paths:
WFLOGGER.warning("No anatomical data present.")
return rpool
Expand All @@ -1931,7 +1932,17 @@ def ingress_raw_anat_data(wf, rpool, cfg, data_paths, unique_id, part_id, ses_id
dl_dir=cfg.pipeline_setup["working_directory"]["path"],
img_type="anat",
)
rpool.set_data("T1w", anat_flow, "outputspec.anat", {}, "", "anat_ingress")
reorient = pe.Node(
interface=afni.Resample(),
name=f"reorient_T1w_{part_id}_{ses_id}",
)

reorient.inputs.orientation = desired_orientation
reorient.inputs.outputtype = "NIFTI_GZ"

wf.connect(anat_flow, "outputspec.anat", reorient, "in_file")

rpool.set_data("T1w", reorient, "out_file", {}, "", "anat_ingress")

if "T2w" in data_paths["anat"]:
anat_flow_T2 = create_anat_datasource(f"anat_T2w_gather_{part_id}_{ses_id}")
Expand All @@ -1942,7 +1953,17 @@ def ingress_raw_anat_data(wf, rpool, cfg, data_paths, unique_id, part_id, ses_id
dl_dir=cfg.pipeline_setup["working_directory"]["path"],
img_type="anat",
)
rpool.set_data("T2w", anat_flow_T2, "outputspec.anat", {}, "", "anat_ingress")
reorient = pe.Node(
interface=afni.Resample(),
name=f"reorient_T1w_{part_id}_{ses_id}",
)

reorient.inputs.orientation = desired_orientation
reorient.inputs.outputtype = "NIFTI_GZ"

wf.connect(anat_flow_T2, "outputspec.anat", reorient, "in_file")

rpool.set_data("T2w", reorient, "out_file", {}, "", "anat_ingress")

if cfg.surface_analysis["freesurfer"]["ingress_reconall"]:
rpool = ingress_freesurfer(
Expand Down Expand Up @@ -1989,13 +2010,28 @@ def ingress_freesurfer(wf, rpool, cfg, data_paths, unique_id, part_id, ses_id):
creds_path=data_paths["creds_path"],
dl_dir=cfg.pipeline_setup["working_directory"]["path"],
)
node = fs_ingress
out = "outputspec.data"
node_name = "freesurfer_config_ingress"

if fs_path.endswith(".nii.gz" or ".nii"):
reorient = pe.Node(
interface=afni.Resample(),
name=f"reorient_fs_{part_id}_{ses_id}",
)
reorient.inputs.orientation = cfg.pipeline_setup["desired_orientation"]
reorient.inputs.outputtype = "NIFTI_GZ"
wf.connect(fs_ingress, "outputspec.data", reorient, "in_file")
node = reorient
out = "out_file"
node_name = "reorient_fs"
rpool.set_data(
"freesurfer-subject-dir",
fs_ingress,
"outputspec.data",
node,
out,
{},
"",
"freesurfer_config_ingress",
node_name,
)

recon_outs = {
Expand Down Expand Up @@ -2058,8 +2094,18 @@ def ingress_raw_func_data(wf, rpool, cfg, data_paths, unique_id, part_id, ses_id
func_wf.get_node("inputnode").iterables = ("scan", list(func_paths_dct.keys()))

rpool.set_data("subject", func_wf, "outputspec.subject", {}, "", "func_ingress")
rpool.set_data("bold", func_wf, "outputspec.rest", {}, "", "func_ingress")
reorient = pe.Node(
interface=afni.Resample(),
name=f"reorient_func_{part_id}_{ses_id}",
)
reorient.inputs.orientation = cfg.pipeline_setup["desired_orientation"]
reorient.inputs.outputtype = "NIFTI_GZ"
wf.connect(func_wf, "outputspec.rest", reorient, "in_file")
rpool.set_data("bold", reorient, "out_file", {}, "", "func_ingress")
# rpool.set_data("bold", func_wf, "outputspec.rest", {}, "", "func_ingress")

rpool.set_data("scan", func_wf, "outputspec.scan", {}, "", "func_ingress")

rpool.set_data(
"scan-params", func_wf, "outputspec.scan_params", {}, "", "scan_params_ingress"
)
Expand Down Expand Up @@ -2474,22 +2520,29 @@ def ingress_pipeconfig_paths(wf, cfg, rpool, unique_id, creds_path=None):

resampled_template = pe.Node(
Function(
input_names=["resolution", "template", "template_name", "tag"],
input_names=[
"orientation",
"resolution",
"template",
"template_name",
"tag",
],
output_names=["resampled_template"],
function=resolve_resolution,
as_module=True,
),
name="resampled_" + key,
)

resampled_template.inputs.orientation = desired_orientation
resampled_template.inputs.resolution = resolution
resampled_template.inputs.template = val
resampled_template.inputs.template_name = key
resampled_template.inputs.tag = tag

node = resampled_template
output = "resampled_template"
node_name = f"{key}_resampled_template"
node_name = "template_resample"

elif val:
config_ingress = create_general_datasource(f"gather_{key}")
Expand All @@ -2503,31 +2556,29 @@ def ingress_pipeconfig_paths(wf, cfg, rpool, unique_id, creds_path=None):
output = "outputspec.data"
node_name = f"{key}_config_ingress"

if val.endswith(".nii.gz"):
# check if the output is in desired orientation, if not reorient it
check_orient = pe.Node(
Function(
input_names=["input_file", "desired_orientation", "reorient"],
output_names=["output_file"],
function=check_orientation,
imports=["from CPAC.pipeline.utils import reorient_image"],
),
name=f"check_orient_{key}",
)
wf.connect(node, output, check_orient, "input_file")
check_orient.inputs.desired_orientation = desired_orientation
check_orient.inputs.reorient = True
if val.endswith(".nii" or ".nii.gz"):
check_reorient = pe.Node(
interface=afni.Resample(),
name=f"reorient_{key}",
)

check_reorient.inputs.orientation = desired_orientation
check_reorient.inputs.outputtype = "NIFTI_GZ"

wf.connect(node, output, check_reorient, "in_file")
node = check_reorient
output = "out_file"
node_name = f"{key}_reorient"

rpool.set_data(
key,
node,
output,
json_info,
"",
node_name,
)

rpool.set_data(
key,
check_orient,
"output_file",
json_info,
"",
f"check_orient-{node_name}-{key}",
)
else:
rpool.set_data(key, node, output, json_info, "", node_name)
# templates, resampling from config
"""
template_keys = [
Expand Down
101 changes: 0 additions & 101 deletions CPAC/pipeline/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,110 +20,9 @@

from CPAC.func_preproc.func_motion import motion_estimate_filter
from CPAC.utils.bids_utils import insert_entity
from CPAC.utils.monitoring import IFLOGGER

MOVEMENT_FILTER_KEYS = motion_estimate_filter.outputs


def reorient_image(input_file, orientation):
"""Reorient the input image to the desired orientation. Replaces the original input_file with the reoriented image.
Parameters
----------
input_file : str
Input image file path
orientation : str
Desired orientation of the input image
Returns
-------
output_file : str
Reoriented image file path
"""
import os
import shutil
import subprocess

output_file = os.path.join(
os.getcwd(),
f"reoriented_{os.path.basename(input_file)}",
)
# if output file exist delete it
if os.path.exists(output_file):
os.remove(output_file)

# make a copy of the input file as temp file so that the original file is not modified
temp_file = os.path.join(
os.getcwd(),
f"temp_{os.path.basename(input_file)}",
)
shutil.copy(input_file, temp_file)

cmd_3drefit = ["3drefit", "-deoblique", temp_file]
cmd_3dresample = [
"3dresample",
"-orient",
orientation,
"-prefix",
output_file,
"-inset",
temp_file,
]
subprocess.run(cmd_3drefit, check=True)
subprocess.run(cmd_3dresample, check=True)

# remove the temporary file
os.remove(temp_file)

return output_file


def check_orientation(input_file, desired_orientation, reorient=True):
"""Find the orientation of the input file and reorient it if necessary. Does not modify the original input file.
Parameters
----------
input_file : str
Input file path
desired_orientation : str
Desired orientation of the input file
reorient : bool
Reorient the input file to the desired orientation
Returns
-------
output_file : str
Reoriented image file path
"""
import subprocess

cmd_3dinfo = ["3dinfo", "-orient", input_file]

orientation = (
subprocess.run(cmd_3dinfo, capture_output=True, text=True, check=False)
.stdout.strip()
.upper()
)
if orientation != desired_orientation and reorient:
IFLOGGER.info(
f"+++ Reorienting {input_file} from {orientation} to {desired_orientation} +++"
)
try:
output_file = reorient_image(input_file, desired_orientation)
except Exception as e:
IFLOGGER.error(
f"Error in reorienting the image: {input_file}.\nCould not reorient the image to {desired_orientation}"
)
IFLOGGER.error(f"Error: {e}")
output_file = input_file # return the original file ?
else:
IFLOGGER.info(
f"+++ Orientation of {input_file} is already {desired_orientation} +++"
)
output_file = input_file
return output_file


def name_fork(resource_idx, cfg, json_info, out_dct):
"""Create and insert entities for forkpoints.
Expand Down
3 changes: 2 additions & 1 deletion CPAC/utils/datasource.py
Original file line number Diff line number Diff line change
Expand Up @@ -1156,7 +1156,7 @@ def res_string_to_tuple(resolution):
return (float(resolution.replace("mm", "")),) * 3


def resolve_resolution(resolution, template, template_name, tag=None):
def resolve_resolution(orientation, resolution, template, template_name, tag=None):
"""Resample a template to a given resolution."""
from nipype.interfaces import afni

Expand Down Expand Up @@ -1203,6 +1203,7 @@ def resolve_resolution(resolution, template, template_name, tag=None):
resample.inputs.resample_mode = "Cu"
resample.inputs.in_file = local_path
resample.base_dir = "."
resample.orientation = orientation

resampled_template = resample.run()
local_path = resampled_template.outputs.out_file
Expand Down

0 comments on commit cf600c1

Please sign in to comment.