Skip to content

Commit 8748be7

Browse files
committed
ENH: CiftiSeparate
1 parent 4564d9b commit 8748be7

File tree

1 file changed

+168
-1
lines changed

1 file changed

+168
-1
lines changed

nibabies/interfaces/workbench.py

+168-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from nipype.interfaces.base import CommandLineInputSpec, File, traits, TraitedSpec, Str
2-
from nipype.interfaces.base.traits_extension import InputMultiObject
2+
from nipype.interfaces.base.traits_extension import InputMultiObject, OutputMultiObject, isdefined
33
from nipype.interfaces.workbench.base import WBCommand
44

55

@@ -940,6 +940,173 @@ class CiftiResample(WBCommand):
940940
_cmd = "wb_command -cifti-resample"
941941

942942

943+
class CiftiSeparateInputSpec(CommandLineInputSpec):
944+
in_file = File(
945+
exists=True,
946+
mandatory=True,
947+
argstr="%s",
948+
position=0,
949+
desc="the cifti to separate a component of",
950+
)
951+
direction = traits.Enum(
952+
"ROW",
953+
"COLUMN",
954+
mandatory=True,
955+
argstr="%s",
956+
position=1,
957+
desc="which dimension to smooth along, ROW or COLUMN",
958+
)
959+
volume_all_file = File(
960+
argstr="-volume-all %s",
961+
position=2,
962+
desc="separate all volume structures into a volume file",
963+
)
964+
volume_all_roi = File(
965+
argstr="-roi %s",
966+
position=3,
967+
requires=["volume_all_file"],
968+
desc="output the roi of which voxels have data",
969+
)
970+
volume_all_label = File(
971+
argstr="-label %s",
972+
position=4,
973+
requires=["volume_all_file"],
974+
desc="output a volume label file indicating the location of structures",
975+
)
976+
volume_all_crop = traits.Bool(
977+
argstr="-crop",
978+
position=5,
979+
requires=["volume_all_file"],
980+
desc="crop volume to the size of the data rather than using the original volume size",
981+
)
982+
# the following can be repeated
983+
label = InputMultiObject(
984+
traits.Either(
985+
traits.Tuple(traits.Enum(VALID_STRUCTURES), File()),
986+
traits.Tuple(traits.Enum(VALID_STRUCTURES), File(), File()),
987+
),
988+
argstr="%s",
989+
position=6,
990+
desc="separate one or more surface models into a surface label file",
991+
)
992+
metric = InputMultiObject(
993+
traits.Either(
994+
traits.Tuple(traits.Enum(VALID_STRUCTURES), File()),
995+
traits.Tuple(traits.Enum(VALID_STRUCTURES), File(), File()), # -roi
996+
),
997+
argstr="%s",
998+
position=7,
999+
desc="separate one or more surface models into a metric file",
1000+
)
1001+
volume = InputMultiObject(
1002+
traits.Either(
1003+
traits.Tuple(traits.Enum(VALID_STRUCTURES), File()),
1004+
traits.Tuple(traits.Enum(VALID_STRUCTURES), File(), File()), # -roi
1005+
traits.Tuple(traits.Enum(VALID_STRUCTURES, File(), traits.Bool)), # -crop
1006+
traits.Tuple(traits.Enum(VALID_STRUCTURES), File(), File(), traits.Bool), # -roi -crop
1007+
),
1008+
argstr="%s",
1009+
position=8,
1010+
desc="separate one or more volume structures into a volume file",
1011+
)
1012+
1013+
1014+
class CiftiSeparateOutputSpec(TraitedSpec):
1015+
volume_all_file = File(desc="File containing all volume structures")
1016+
volume_all_roi_file = File(desc="Output the roi of which voxels have data")
1017+
volume_all_label_file = File(
1018+
desc="output a volume label file indicating the location of structures"
1019+
)
1020+
label_files = OutputMultiObject(File(), desc="Output label files")
1021+
label_roi_files = OutputMultiObject(File(), desc="Output label rois files")
1022+
metric_files = OutputMultiObject(File(), desc="Output metric files")
1023+
metric_roi_files = OutputMultiObject(File(), desc="Output metric rois files")
1024+
volume_files = OutputMultiObject(File(), desc="Output volume files")
1025+
volume_roi_files = OutputMultiObject(File(), desc="Output volume roi files")
1026+
1027+
1028+
class CiftiSeparate(WBCommand):
1029+
"""
1030+
Extract left or right hemisphere surface from CIFTI file (.dtseries)
1031+
other structure can also be extracted
1032+
The input cifti file must have a brain models mapping on the chosen
1033+
dimension, columns for .dtseries.
1034+
1035+
>>> separate = CiftiSeparate()
1036+
>>> separate.inputs.in_file = data_dir / "func.dtseries.nii"
1037+
>>> separate.inputs.direction = "COLUMN"
1038+
>>> separate.inputs.volume_all_file = "volume_all.nii.gz"
1039+
>>> separate.cmdline #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
1040+
'wb_command -cifti-separate .../func.dtseries.nii COLUMN \
1041+
-volume-all volume_all.nii.gz'
1042+
1043+
Metrics, labels, and volumes can also be freely extracted
1044+
>>> separate.inputs.metric = [("CORTEX_LEFT", "cortexleft.func.gii")]
1045+
>>> separate.inputs.volume = [("HIPPOCAMPUS_LEFT", "hippoL.nii.gz"), \
1046+
("HIPPOCAMPUS_RIGHT", "hippoR.nii.gz", "hippoR.roi.nii.gz")]
1047+
>>> separate.cmdline #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
1048+
'wb_command -cifti-separate .../func.dtseries.nii COLUMN \
1049+
-volume-all volume_all.nii.gz -metric CORTEX_LEFT cortexleft.func.gii \
1050+
-volume HIPPOCAMPUS_LEFT hippoL.nii.gz \
1051+
-volume HIPPOCAMPUS_RIGHT hippoR.nii.gz -roi hippoR.roi.nii.gz'
1052+
1053+
"""
1054+
input_spec = CiftiSeparateInputSpec
1055+
output_spec = CiftiSeparateOutputSpec
1056+
_cmd = "wb_command -cifti-separate"
1057+
_label_roi_files = []
1058+
_metric_roi_files = []
1059+
_volume_roi_files = []
1060+
1061+
def _format_arg(self, name, trait_spec, value):
1062+
if name in ("label", "metric", "volume"):
1063+
cmds = []
1064+
for i, val in enumerate(value):
1065+
if len(val) == 3:
1066+
if val[-1] is True:
1067+
val = val[:-1] + ("-crop",)
1068+
else:
1069+
val = val[:-1] + ("-roi", val[-1])
1070+
self._set_roi_file(name, val[-1])
1071+
elif len(val) == 4:
1072+
val = val[:-2] + ("-roi", val[-2]) + ("crop") if val[-1] is True else ()
1073+
self._set_roi_file(name, val[-2])
1074+
cmds.append(" ".join((f"-{name}",) + val))
1075+
return trait_spec.argstr % " ".join(cmds)
1076+
return super()._format_arg(name, trait_spec, value)
1077+
1078+
def _list_outputs(self):
1079+
outputs = super()._list_outputs()
1080+
if self.inputs.volume_all_file:
1081+
outputs["volume_all_file"] = self.inputs.volume_all_file
1082+
if self.inputs.volume_all_roi_file:
1083+
outputs["volume_all_roi_file"] = self.inputs.volume_all_roi_file
1084+
if self.inputs.volume_all_label_file:
1085+
outputs["volume_all_label_file"] = self.inputs.volume_all_label_file
1086+
if self.inputs.label:
1087+
for label in self.inputs.label:
1088+
outputs["label_files"] = (outputs["label_files"] or []) + \
1089+
self._gen_filename(label[2])
1090+
if self._label_roi_files:
1091+
outputs["label_roi_files"] = self._label_roi_files
1092+
if self.inputs.metric:
1093+
for metric in self.inputs.metric:
1094+
outputs["metric_files"] = (outputs["metric_files"] or []) + \
1095+
self._gen_filename(metric[2])
1096+
if self._metric_roi_files:
1097+
outputs["metric_roi_files"] = self._metric_roi_files
1098+
if self.inputs.volume:
1099+
for volume in self.inputs.volume:
1100+
outputs["volume_files"] = (outputs["volume_files"] or []) + \
1101+
self._gen_filename(volume[2])
1102+
if self._volume_roi_files:
1103+
outputs["volume_roi_files"] = self._volume_roi_files
1104+
1105+
def _set_roi_file(self, name, file):
1106+
rois = getattr(self, f"_{name}_roi_files")
1107+
rois.append(self._gen_filename(file))
1108+
1109+
9431110
class VolumeAffineResampleInputSpec(CommandLineInputSpec):
9441111
in_file = File(
9451112
exists=True,

0 commit comments

Comments
 (0)