Skip to content

Commit 2c0edaa

Browse files
authored
Merge pull request #2430 from mgxd/enh/c4d
enh: c3d + c4d interface
2 parents ecb1f8f + ab9f3ea commit 2c0edaa

File tree

2 files changed

+216
-1
lines changed

2 files changed

+216
-1
lines changed

nipype/interfaces/c3.py

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,16 @@
1010
"""
1111
from __future__ import (print_function, division, unicode_literals,
1212
absolute_import)
13+
import os
14+
from glob import glob
1315

1416
from .base import (CommandLineInputSpec, traits, TraitedSpec, File,
15-
SEMLikeCommandLine)
17+
SEMLikeCommandLine, InputMultiPath, OutputMultiPath,
18+
CommandLine, isdefined)
19+
from ..utils.filemanip import split_filename
20+
from .. import logging
21+
22+
iflogger = logging.getLogger("interface")
1623

1724

1825
class C3dAffineToolInputSpec(CommandLineInputSpec):
@@ -52,3 +59,150 @@ class C3dAffineTool(SEMLikeCommandLine):
5259

5360
_cmd = 'c3d_affine_tool'
5461
_outputs_filenames = {'itk_transform': 'affine.txt'}
62+
63+
64+
class C3dInputSpec(CommandLineInputSpec):
65+
in_file = InputMultiPath(
66+
File(),
67+
position=1,
68+
argstr="%s",
69+
mandatory=True,
70+
desc="Input file (wildcard and multiple are supported).")
71+
out_file = File(
72+
exists=False,
73+
argstr="-o %s",
74+
position=-1,
75+
xor=["out_files"],
76+
desc="Output file of last image on the stack.")
77+
out_files = InputMultiPath(
78+
File(),
79+
argstr="-oo %s",
80+
xor=["out_file"],
81+
position=-1,
82+
desc=("Write all images on the convert3d stack as multiple files."
83+
" Supports both list of output files or a pattern for the output"
84+
" filenames (using %d substituion)."))
85+
pix_type = traits.Enum(
86+
"float", "char", "uchar", "short", "ushort", "int", "uint", "double",
87+
argstr="-type %s",
88+
desc=("Specifies the pixel type for the output image. By default,"
89+
" images are written in floating point (float) format"))
90+
scale = traits.Either(
91+
traits.Int(), traits.Float(),
92+
argstr="-scale %s",
93+
desc=("Multiplies the intensity of each voxel in the last image on the"
94+
" stack by the given factor."))
95+
shift = traits.Either(
96+
traits.Int(), traits.Float(),
97+
argstr="-shift %s",
98+
desc='Adds the given constant to every voxel.')
99+
interp = traits.Enum(
100+
"Linear", "NearestNeighbor", "Cubic", "Sinc", "Gaussian",
101+
argstr="-interpolation %s",
102+
desc=("Specifies the interpolation used with -resample and other"
103+
" commands. Default is Linear."))
104+
resample = traits.Str(
105+
argstr="-resample %s",
106+
desc=("Resamples the image, keeping the bounding box the same, but"
107+
" changing the number of voxels in the image. The dimensions can be"
108+
" specified as a percentage, for example to double the number of voxels"
109+
" in each direction. The -interpolation flag affects how sampling is"
110+
" performed."))
111+
smooth = traits.Str(
112+
argstr="-smooth %s",
113+
desc=("Applies Gaussian smoothing to the image. The parameter vector"
114+
" specifies the standard deviation of the Gaussian kernel."))
115+
multicomp_split = traits.Bool(
116+
False,
117+
usedefault=True,
118+
argstr="-mcr",
119+
position=0,
120+
desc="Enable reading of multi-component images.")
121+
is_4d = traits.Bool(
122+
False,
123+
usedefault=True,
124+
desc=("Changes command to support 4D file operations (default is"
125+
" false)."))
126+
127+
128+
class C3dOutputSpec(TraitedSpec):
129+
out_files = OutputMultiPath(File(exists=False))
130+
131+
132+
class C3d(CommandLine):
133+
"""
134+
Convert3d is a command-line tool for converting 3D (or 4D) images between
135+
common file formats. The tool also includes a growing list of commands for
136+
image manipulation, such as thresholding and resampling. The tool can also
137+
be used to obtain information about image files. More information on
138+
Convert3d can be found at:
139+
https://sourceforge.net/p/c3d/git/ci/master/tree/doc/c3d.md
140+
141+
142+
Example
143+
=======
144+
145+
>>> from nipype.interfaces.c3 import C3d
146+
>>> c3 = C3d()
147+
>>> c3.inputs.in_file = "T1.nii"
148+
>>> c3.inputs.pix_type = "short"
149+
>>> c3.inputs.out_file = "T1.img"
150+
>>> c3.cmdline
151+
'c3d T1.nii -type short -o T1.img'
152+
>>> c3.inputs.is_4d = True
153+
>>> c3.inputs.in_file = "epi.nii"
154+
>>> c3.inputs.out_file = "epi.img"
155+
>>> c3.cmdline
156+
'c4d epi.nii -type short -o epi.img'
157+
"""
158+
input_spec = C3dInputSpec
159+
output_spec = C3dOutputSpec
160+
161+
_cmd = "c3d"
162+
163+
def __init__(self, **inputs):
164+
super(C3d, self).__init__(**inputs)
165+
self.inputs.on_trait_change(self._is_4d, "is_4d")
166+
if self.inputs.is_4d:
167+
self._is_4d()
168+
169+
def _is_4d(self):
170+
self._cmd = "c4d" if self.inputs.is_4d else "c3d"
171+
172+
def _run_interface(self, runtime):
173+
cmd = self._cmd
174+
if (not isdefined(self.inputs.out_file)
175+
and not isdefined(self.inputs.out_files)):
176+
# Convert3d does not want to override file, by default
177+
# so we define a new output file
178+
self._gen_outfile()
179+
runtime = super(C3d, self)._run_interface(runtime)
180+
self._cmd = cmd
181+
return runtime
182+
183+
def _gen_outfile(self):
184+
# if many infiles, raise exception
185+
if (len(self.inputs.in_file) > 1) or ("*" in self.inputs.in_file[0]):
186+
raise AttributeError("Multiple in_files found - specify either"
187+
" `out_file` or `out_files`.")
188+
_, fn, ext = split_filename(self.inputs.in_file[0])
189+
self.inputs.out_file = fn + "_generated" + ext
190+
# if generated file will overwrite, raise error
191+
if os.path.exists(os.path.abspath(self.inputs.out_file)):
192+
raise IOError("File already found - to overwrite, use `out_file`.")
193+
iflogger.info("Generating `out_file`.")
194+
195+
def _list_outputs(self):
196+
outputs = self.output_spec().get()
197+
if isdefined(self.inputs.out_file):
198+
outputs["out_files"] = os.path.abspath(self.inputs.out_file)
199+
if isdefined(self.inputs.out_files):
200+
if len(self.inputs.out_files) == 1:
201+
_out_files = glob(os.path.abspath(self.inputs.out_files[0]))
202+
else:
203+
_out_files = [os.path.abspath(f) for f in self.inputs.out_files
204+
if os.path.exists(os.path.abspath(f))]
205+
outputs["out_files"] = _out_files
206+
207+
return outputs
208+
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from __future__ import unicode_literals
3+
from ..c3 import C3d
4+
5+
6+
def test_C3d_inputs():
7+
input_map = dict(
8+
args=dict(argstr='%s', ),
9+
environ=dict(
10+
nohash=True,
11+
usedefault=True,
12+
),
13+
ignore_exception=dict(
14+
deprecated='1.0.0',
15+
nohash=True,
16+
usedefault=True,
17+
),
18+
in_file=dict(
19+
argstr='%s',
20+
mandatory=True,
21+
position=1,
22+
),
23+
interp=dict(argstr='-interpolation %s', ),
24+
is_4d=dict(usedefault=True, ),
25+
multicomp_split=dict(
26+
argstr='-mcr',
27+
position=0,
28+
usedefault=True,
29+
),
30+
out_file=dict(
31+
argstr='-o %s',
32+
position=-1,
33+
xor=['out_files'],
34+
),
35+
out_files=dict(
36+
argstr='-oo %s',
37+
position=-1,
38+
xor=['out_file'],
39+
),
40+
pix_type=dict(argstr='-type %s', ),
41+
resample=dict(argstr='-resample %s', ),
42+
scale=dict(argstr='-scale %s', ),
43+
shift=dict(argstr='-shift %s', ),
44+
smooth=dict(argstr='-smooth %s', ),
45+
terminal_output=dict(
46+
deprecated='1.0.0',
47+
nohash=True,
48+
),
49+
)
50+
inputs = C3d.input_spec()
51+
52+
for key, metadata in list(input_map.items()):
53+
for metakey, value in list(metadata.items()):
54+
assert getattr(inputs.traits()[key], metakey) == value
55+
def test_C3d_outputs():
56+
output_map = dict(out_files=dict(), )
57+
outputs = C3d.output_spec()
58+
59+
for key, metadata in list(output_map.items()):
60+
for metakey, value in list(metadata.items()):
61+
assert getattr(outputs.traits()[key], metakey) == value

0 commit comments

Comments
 (0)