|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +"""ANTS Legacy Interfaces |
| 3 | +
|
| 4 | +These interfaces are for programs that have been deprecated by ANTs, but |
| 5 | +are preserved for backwards compatibility. |
| 6 | +""" |
| 7 | + |
| 8 | +from builtins import range |
| 9 | + |
| 10 | +import os |
| 11 | +from glob import glob |
| 12 | + |
| 13 | +from .base import ANTSCommand, ANTSCommandInputSpec |
| 14 | +from ..base import TraitedSpec, File, traits, isdefined, OutputMultiPath |
| 15 | +from ...utils.filemanip import split_filename |
| 16 | + |
| 17 | + |
| 18 | +class antsIntroductionInputSpec(ANTSCommandInputSpec): |
| 19 | + dimension = traits.Enum( |
| 20 | + 3, |
| 21 | + 2, |
| 22 | + argstr="-d %d", |
| 23 | + usedefault=True, |
| 24 | + desc="image dimension (2 or 3)", |
| 25 | + position=1, |
| 26 | + ) |
| 27 | + reference_image = File( |
| 28 | + exists=True, |
| 29 | + argstr="-r %s", |
| 30 | + desc="template file to warp to", |
| 31 | + mandatory=True, |
| 32 | + copyfile=True, |
| 33 | + ) |
| 34 | + input_image = File( |
| 35 | + exists=True, |
| 36 | + argstr="-i %s", |
| 37 | + desc="input image to warp to template", |
| 38 | + mandatory=True, |
| 39 | + copyfile=False, |
| 40 | + ) |
| 41 | + force_proceed = traits.Bool( |
| 42 | + argstr="-f 1", |
| 43 | + desc=("force script to proceed even if headers " "may be incompatible"), |
| 44 | + ) |
| 45 | + inverse_warp_template_labels = traits.Bool( |
| 46 | + argstr="-l", |
| 47 | + desc=( |
| 48 | + "Applies inverse warp to the template labels " |
| 49 | + "to estimate label positions in target space (use " |
| 50 | + "for template-based segmentation)" |
| 51 | + ), |
| 52 | + ) |
| 53 | + max_iterations = traits.List( |
| 54 | + traits.Int, |
| 55 | + argstr="-m %s", |
| 56 | + sep="x", |
| 57 | + desc=( |
| 58 | + "maximum number of iterations (must be " |
| 59 | + "list of integers in the form [J,K,L...]: " |
| 60 | + "J = coarsest resolution iterations, K = " |
| 61 | + "middle resolution interations, L = fine " |
| 62 | + "resolution iterations" |
| 63 | + ), |
| 64 | + ) |
| 65 | + bias_field_correction = traits.Bool( |
| 66 | + argstr="-n 1", desc=("Applies bias field correction to moving " "image") |
| 67 | + ) |
| 68 | + similarity_metric = traits.Enum( |
| 69 | + "PR", |
| 70 | + "CC", |
| 71 | + "MI", |
| 72 | + "MSQ", |
| 73 | + argstr="-s %s", |
| 74 | + desc=( |
| 75 | + "Type of similartiy metric used for registration " |
| 76 | + "(CC = cross correlation, MI = mutual information, " |
| 77 | + "PR = probability mapping, MSQ = mean square difference)" |
| 78 | + ), |
| 79 | + ) |
| 80 | + transformation_model = traits.Enum( |
| 81 | + "GR", |
| 82 | + "EL", |
| 83 | + "SY", |
| 84 | + "S2", |
| 85 | + "EX", |
| 86 | + "DD", |
| 87 | + "RI", |
| 88 | + "RA", |
| 89 | + argstr="-t %s", |
| 90 | + usedefault=True, |
| 91 | + desc=( |
| 92 | + "Type of transofmration model used for registration " |
| 93 | + "(EL = elastic transformation model, SY = SyN with time, " |
| 94 | + "arbitrary number of time points, S2 = SyN with time " |
| 95 | + "optimized for 2 time points, GR = greedy SyN, EX = " |
| 96 | + "exponential, DD = diffeomorphic demons style exponential " |
| 97 | + "mapping, RI = purely rigid, RA = affine rigid" |
| 98 | + ), |
| 99 | + ) |
| 100 | + out_prefix = traits.Str( |
| 101 | + "ants_", |
| 102 | + argstr="-o %s", |
| 103 | + usedefault=True, |
| 104 | + desc=("Prefix that is prepended to all output " "files (default = ants_)"), |
| 105 | + ) |
| 106 | + quality_check = traits.Bool( |
| 107 | + argstr="-q 1", desc="Perform a quality check of the result" |
| 108 | + ) |
| 109 | + |
| 110 | + |
| 111 | +class antsIntroductionOutputSpec(TraitedSpec): |
| 112 | + affine_transformation = File(exists=True, desc="affine (prefix_Affine.txt)") |
| 113 | + warp_field = File(exists=True, desc="warp field (prefix_Warp.nii)") |
| 114 | + inverse_warp_field = File( |
| 115 | + exists=True, desc="inverse warp field (prefix_InverseWarp.nii)" |
| 116 | + ) |
| 117 | + input_file = File(exists=True, desc="input image (prefix_repaired.nii)") |
| 118 | + output_file = File(exists=True, desc="output image (prefix_deformed.nii)") |
| 119 | + |
| 120 | + |
| 121 | +class antsIntroduction(ANTSCommand): |
| 122 | + """Uses ANTS to generate matrices to warp data from one space to another. |
| 123 | +
|
| 124 | + Examples |
| 125 | + -------- |
| 126 | +
|
| 127 | + >>> from nipype.interfaces.ants.legacy import antsIntroduction |
| 128 | + >>> warp = antsIntroduction() |
| 129 | + >>> warp.inputs.reference_image = 'Template_6.nii' |
| 130 | + >>> warp.inputs.input_image = 'structural.nii' |
| 131 | + >>> warp.inputs.max_iterations = [30,90,20] |
| 132 | + >>> warp.cmdline |
| 133 | + 'antsIntroduction.sh -d 3 -i structural.nii -m 30x90x20 -o ants_ -r Template_6.nii -t GR' |
| 134 | +
|
| 135 | + """ |
| 136 | + |
| 137 | + _cmd = "antsIntroduction.sh" |
| 138 | + input_spec = antsIntroductionInputSpec |
| 139 | + output_spec = antsIntroductionOutputSpec |
| 140 | + |
| 141 | + def _list_outputs(self): |
| 142 | + outputs = self._outputs().get() |
| 143 | + transmodel = self.inputs.transformation_model |
| 144 | + |
| 145 | + # When transform is set as 'RI'/'RA', wrap fields should not be expected |
| 146 | + # The default transformation is GR, which outputs the wrap fields |
| 147 | + if not isdefined(transmodel) or ( |
| 148 | + isdefined(transmodel) and transmodel not in ["RI", "RA"] |
| 149 | + ): |
| 150 | + outputs["warp_field"] = os.path.join( |
| 151 | + os.getcwd(), self.inputs.out_prefix + "Warp.nii.gz" |
| 152 | + ) |
| 153 | + outputs["inverse_warp_field"] = os.path.join( |
| 154 | + os.getcwd(), self.inputs.out_prefix + "InverseWarp.nii.gz" |
| 155 | + ) |
| 156 | + |
| 157 | + outputs["affine_transformation"] = os.path.join( |
| 158 | + os.getcwd(), self.inputs.out_prefix + "Affine.txt" |
| 159 | + ) |
| 160 | + outputs["input_file"] = os.path.join( |
| 161 | + os.getcwd(), self.inputs.out_prefix + "repaired.nii.gz" |
| 162 | + ) |
| 163 | + outputs["output_file"] = os.path.join( |
| 164 | + os.getcwd(), self.inputs.out_prefix + "deformed.nii.gz" |
| 165 | + ) |
| 166 | + |
| 167 | + return outputs |
| 168 | + |
| 169 | + |
| 170 | +# How do we make a pass through so that GenWarpFields is just an alias for antsIntroduction ? |
| 171 | + |
| 172 | + |
| 173 | +class GenWarpFields(antsIntroduction): |
| 174 | + pass |
| 175 | + |
| 176 | + |
| 177 | +class buildtemplateparallelInputSpec(ANTSCommandInputSpec): |
| 178 | + dimension = traits.Enum( |
| 179 | + 3, |
| 180 | + 2, |
| 181 | + 4, |
| 182 | + argstr="-d %d", |
| 183 | + usedefault=True, |
| 184 | + desc="image dimension (2, 3 or 4)", |
| 185 | + position=1, |
| 186 | + ) |
| 187 | + out_prefix = traits.Str( |
| 188 | + "antsTMPL_", |
| 189 | + argstr="-o %s", |
| 190 | + usedefault=True, |
| 191 | + desc=("Prefix that is prepended to all output " "files (default = antsTMPL_)"), |
| 192 | + ) |
| 193 | + in_files = traits.List( |
| 194 | + File(exists=True), |
| 195 | + mandatory=True, |
| 196 | + desc="list of images to generate template from", |
| 197 | + argstr="%s", |
| 198 | + position=-1, |
| 199 | + ) |
| 200 | + parallelization = traits.Enum( |
| 201 | + 0, |
| 202 | + 1, |
| 203 | + 2, |
| 204 | + argstr="-c %d", |
| 205 | + usedefault=True, |
| 206 | + desc=( |
| 207 | + "control for parallel processing (0 = " |
| 208 | + "serial, 1 = use PBS, 2 = use PEXEC, 3 = " |
| 209 | + "use Apple XGrid" |
| 210 | + ), |
| 211 | + ) |
| 212 | + gradient_step_size = traits.Float( |
| 213 | + argstr="-g %f", |
| 214 | + desc=("smaller magnitude results in " "more cautious steps (default = " ".25)"), |
| 215 | + ) |
| 216 | + iteration_limit = traits.Int( |
| 217 | + 4, argstr="-i %d", usedefault=True, desc="iterations of template construction" |
| 218 | + ) |
| 219 | + num_cores = traits.Int( |
| 220 | + argstr="-j %d", |
| 221 | + requires=["parallelization"], |
| 222 | + desc=( |
| 223 | + "Requires parallelization = 2 (PEXEC). " "Sets number of cpu cores to use" |
| 224 | + ), |
| 225 | + ) |
| 226 | + max_iterations = traits.List( |
| 227 | + traits.Int, |
| 228 | + argstr="-m %s", |
| 229 | + sep="x", |
| 230 | + desc=( |
| 231 | + "maximum number of iterations (must be " |
| 232 | + "list of integers in the form [J,K,L...]: " |
| 233 | + "J = coarsest resolution iterations, K = " |
| 234 | + "middle resolution interations, L = fine " |
| 235 | + "resolution iterations" |
| 236 | + ), |
| 237 | + ) |
| 238 | + bias_field_correction = traits.Bool( |
| 239 | + argstr="-n 1", desc=("Applies bias field correction to moving " "image") |
| 240 | + ) |
| 241 | + rigid_body_registration = traits.Bool( |
| 242 | + argstr="-r 1", |
| 243 | + desc=( |
| 244 | + "registers inputs before creating template " |
| 245 | + "(useful if no initial template available)" |
| 246 | + ), |
| 247 | + ) |
| 248 | + similarity_metric = traits.Enum( |
| 249 | + "PR", |
| 250 | + "CC", |
| 251 | + "MI", |
| 252 | + "MSQ", |
| 253 | + argstr="-s %s", |
| 254 | + desc=( |
| 255 | + "Type of similartiy metric used for registration " |
| 256 | + "(CC = cross correlation, MI = mutual information, " |
| 257 | + "PR = probability mapping, MSQ = mean square difference)" |
| 258 | + ), |
| 259 | + ) |
| 260 | + transformation_model = traits.Enum( |
| 261 | + "GR", |
| 262 | + "EL", |
| 263 | + "SY", |
| 264 | + "S2", |
| 265 | + "EX", |
| 266 | + "DD", |
| 267 | + argstr="-t %s", |
| 268 | + usedefault=True, |
| 269 | + desc=( |
| 270 | + "Type of transofmration model used for registration " |
| 271 | + "(EL = elastic transformation model, SY = SyN with time, " |
| 272 | + "arbitrary number of time points, S2 = SyN with time " |
| 273 | + "optimized for 2 time points, GR = greedy SyN, EX = " |
| 274 | + "exponential, DD = diffeomorphic demons style exponential " |
| 275 | + "mapping" |
| 276 | + ), |
| 277 | + ) |
| 278 | + use_first_as_target = traits.Bool( |
| 279 | + desc=( |
| 280 | + "uses first volume as target of " |
| 281 | + "all inputs. When not used, an " |
| 282 | + "unbiased average image is used " |
| 283 | + "to start." |
| 284 | + ) |
| 285 | + ) |
| 286 | + |
| 287 | + |
| 288 | +class buildtemplateparallelOutputSpec(TraitedSpec): |
| 289 | + final_template_file = File(exists=True, desc="final ANTS template") |
| 290 | + template_files = OutputMultiPath( |
| 291 | + File(exists=True), desc="Templates from different stages of iteration" |
| 292 | + ) |
| 293 | + subject_outfiles = OutputMultiPath( |
| 294 | + File(exists=True), |
| 295 | + desc=( |
| 296 | + "Outputs for each input image. Includes warp " |
| 297 | + "field, inverse warp, Affine, original image " |
| 298 | + "(repaired) and warped image (deformed)" |
| 299 | + ), |
| 300 | + ) |
| 301 | + |
| 302 | + |
| 303 | +class buildtemplateparallel(ANTSCommand): |
| 304 | + """Generate a optimal average template |
| 305 | +
|
| 306 | + .. warning:: |
| 307 | +
|
| 308 | + This can take a VERY long time to complete |
| 309 | +
|
| 310 | + Examples |
| 311 | + -------- |
| 312 | +
|
| 313 | + >>> from nipype.interfaces.ants.legacy import buildtemplateparallel |
| 314 | + >>> tmpl = buildtemplateparallel() |
| 315 | + >>> tmpl.inputs.in_files = ['T1.nii', 'structural.nii'] |
| 316 | + >>> tmpl.inputs.max_iterations = [30, 90, 20] |
| 317 | + >>> tmpl.cmdline |
| 318 | + 'buildtemplateparallel.sh -d 3 -i 4 -m 30x90x20 -o antsTMPL_ -c 0 -t GR T1.nii structural.nii' |
| 319 | +
|
| 320 | + """ |
| 321 | + |
| 322 | + _cmd = "buildtemplateparallel.sh" |
| 323 | + input_spec = buildtemplateparallelInputSpec |
| 324 | + output_spec = buildtemplateparallelOutputSpec |
| 325 | + |
| 326 | + def _format_arg(self, opt, spec, val): |
| 327 | + if opt == "num_cores": |
| 328 | + if self.inputs.parallelization == 2: |
| 329 | + return "-j " + str(val) |
| 330 | + else: |
| 331 | + return "" |
| 332 | + if opt == "in_files": |
| 333 | + if self.inputs.use_first_as_target: |
| 334 | + start = "-z " |
| 335 | + else: |
| 336 | + start = "" |
| 337 | + return start + " ".join(name for name in val) |
| 338 | + return super(buildtemplateparallel, self)._format_arg(opt, spec, val) |
| 339 | + |
| 340 | + def _list_outputs(self): |
| 341 | + outputs = self._outputs().get() |
| 342 | + outputs["template_files"] = [] |
| 343 | + for i in range(len(glob(os.path.realpath("*iteration*")))): |
| 344 | + temp = os.path.realpath( |
| 345 | + "%s_iteration_%d/%stemplate.nii.gz" |
| 346 | + % (self.inputs.transformation_model, i, self.inputs.out_prefix) |
| 347 | + ) |
| 348 | + os.rename( |
| 349 | + temp, |
| 350 | + os.path.realpath( |
| 351 | + "%s_iteration_%d/%stemplate_i%d.nii.gz" |
| 352 | + % (self.inputs.transformation_model, i, self.inputs.out_prefix, i) |
| 353 | + ), |
| 354 | + ) |
| 355 | + file_ = "%s_iteration_%d/%stemplate_i%d.nii.gz" % ( |
| 356 | + self.inputs.transformation_model, |
| 357 | + i, |
| 358 | + self.inputs.out_prefix, |
| 359 | + i, |
| 360 | + ) |
| 361 | + |
| 362 | + outputs["template_files"].append(os.path.realpath(file_)) |
| 363 | + outputs["final_template_file"] = os.path.realpath( |
| 364 | + "%stemplate.nii.gz" % self.inputs.out_prefix |
| 365 | + ) |
| 366 | + outputs["subject_outfiles"] = [] |
| 367 | + for filename in self.inputs.in_files: |
| 368 | + _, base, _ = split_filename(filename) |
| 369 | + temp = glob(os.path.realpath("%s%s*" % (self.inputs.out_prefix, base))) |
| 370 | + for file_ in temp: |
| 371 | + outputs["subject_outfiles"].append(file_) |
| 372 | + return outputs |
0 commit comments