|
17 | 17 | from ...utils.filemanip import (fname_presuffix, ensure_list,
|
18 | 18 | simplify_list, split_filename)
|
19 | 19 | from ..base import (OutputMultiPath, TraitedSpec, isdefined,
|
20 |
| - traits, InputMultiPath, File, Str) |
| 20 | + traits, InputMultiPath, InputMultiObject, File, Str) |
21 | 21 | from .base import (SPMCommand, scans_for_fname, func_is_3d,
|
22 | 22 | scans_for_fnames, SPMCommandInputSpec, ImageFileSPM)
|
23 | 23 |
|
@@ -437,6 +437,252 @@ def _list_outputs(self):
|
437 | 437 | return outputs
|
438 | 438 |
|
439 | 439 |
|
| 440 | +class RealignUnwarpInputSpec(SPMCommandInputSpec): |
| 441 | + |
| 442 | + in_files = InputMultiObject( |
| 443 | + traits.Either(ImageFileSPM(exists=True), |
| 444 | + traits.List(ImageFileSPM(exists=True))), |
| 445 | + field='data.scans', |
| 446 | + mandatory=True, |
| 447 | + copyfile=True, |
| 448 | + desc='list of filenames to realign and unwarp') |
| 449 | + phase_map = File( |
| 450 | + field='data.pmscan', |
| 451 | + desc='Voxel displacement map to use in unwarping. Unlike SPM standard ' |
| 452 | + 'behaviour, the same map will be used for all sessions', |
| 453 | + copyfile=False) |
| 454 | + quality = traits.Range( |
| 455 | + low=0.0, |
| 456 | + high=1.0, |
| 457 | + field='eoptions.quality', |
| 458 | + desc='0.1 = fast, 1.0 = precise') |
| 459 | + fwhm = traits.Range( |
| 460 | + low=0.0, |
| 461 | + field='eoptions.fwhm', |
| 462 | + desc='gaussian smoothing kernel width') |
| 463 | + separation = traits.Range( |
| 464 | + low=0.0, |
| 465 | + field='eoptions.sep', |
| 466 | + desc='sampling separation in mm') |
| 467 | + register_to_mean = traits.Bool( |
| 468 | + field='eoptions.rtm', |
| 469 | + desc='Indicate whether realignment is done to the mean image') |
| 470 | + weight_img = File( |
| 471 | + exists=True, |
| 472 | + field='eoptions.weight', |
| 473 | + desc='filename of weighting image') |
| 474 | + interp = traits.Range( |
| 475 | + low=0, |
| 476 | + high=7, |
| 477 | + field='eoptions.einterp', |
| 478 | + desc='degree of b-spline used for interpolation') |
| 479 | + wrap = traits.List( |
| 480 | + traits.Int(), |
| 481 | + minlen=3, |
| 482 | + maxlen=3, |
| 483 | + field='eoptions.ewrap', |
| 484 | + desc='Check if interpolation should wrap in [x,y,z]') |
| 485 | + est_basis_func = traits.List( |
| 486 | + traits.Int(), |
| 487 | + minlen=2, |
| 488 | + maxlen=2, |
| 489 | + field='uweoptions.basfcn', |
| 490 | + desc='Number of basis functions to use for each dimension') |
| 491 | + est_reg_order = traits.Range( |
| 492 | + low=0, |
| 493 | + high=3, |
| 494 | + field='uweoptions.regorder', |
| 495 | + desc=('This parameter determines how to balance the compromise between likelihood ' |
| 496 | + 'maximization and smoothness maximization of the estimated field.')) |
| 497 | + est_reg_factor = traits.ListInt( |
| 498 | + [100000], |
| 499 | + field='uweoptions.lambda', |
| 500 | + minlen=1, |
| 501 | + maxlen=1, |
| 502 | + usedefault=True, |
| 503 | + desc='Regularisation factor. Default: 100000 (medium).') |
| 504 | + est_jacobian_deformations = traits.Bool( |
| 505 | + field='uweoptions.jm', |
| 506 | + desc=('Jacobian deformations. In theory a good idea to include them, ' |
| 507 | + ' in practice a bad idea. Default: No.')) |
| 508 | + est_first_order_effects = traits.List( |
| 509 | + traits.Int(), |
| 510 | + minlen=1, |
| 511 | + maxlen=6, |
| 512 | + field='uweoptions.fot', |
| 513 | + desc='First order effects should only depend on pitch and roll, i.e. [4 5]') |
| 514 | + est_second_order_effects = traits.List( |
| 515 | + traits.Int(), |
| 516 | + minlen=1, |
| 517 | + maxlen=6, |
| 518 | + field='uweoptions.sot', |
| 519 | + desc='List of second order terms to model second derivatives of.') |
| 520 | + est_unwarp_fwhm = traits.Range( |
| 521 | + low=0.0, |
| 522 | + field='uweoptions.uwfwhm', |
| 523 | + desc='gaussian smoothing kernel width for unwarp') |
| 524 | + est_re_est_mov_par = traits.Bool( |
| 525 | + field='uweoptions.rem', |
| 526 | + desc='Re-estimate movement parameters at each unwarping iteration.') |
| 527 | + est_num_of_interations = traits.ListInt( |
| 528 | + [5], |
| 529 | + field='uweoptions.noi', |
| 530 | + minlen=1, |
| 531 | + maxlen=1, |
| 532 | + usedfault=True, |
| 533 | + desc='Number of iterations.') |
| 534 | + est_taylor_expansion_point = traits.String( |
| 535 | + 'Average', |
| 536 | + field='uweoptions.expround', |
| 537 | + usedefault=True, |
| 538 | + desc='Point in position space to perform Taylor-expansion around.') |
| 539 | + reslice_which = traits.ListInt( |
| 540 | + [2, 1], |
| 541 | + field='uwroptions.uwwhich', |
| 542 | + minlen=2, |
| 543 | + maxlen=2, |
| 544 | + usedefault=True, |
| 545 | + desc='determines which images to reslice') |
| 546 | + reslice_interp = traits.Range( |
| 547 | + low=0, |
| 548 | + high=7, |
| 549 | + field='uwroptions.rinterp', |
| 550 | + desc='degree of b-spline used for interpolation') |
| 551 | + reslice_wrap = traits.List( |
| 552 | + traits.Int(), |
| 553 | + minlen=3, |
| 554 | + maxlen=3, |
| 555 | + field='uwroptions.wrap', |
| 556 | + desc='Check if interpolation should wrap in [x,y,z]') |
| 557 | + reslice_mask = traits.Bool( |
| 558 | + field='uwroptions.mask', |
| 559 | + desc='True/False mask output image') |
| 560 | + out_prefix = traits.String( |
| 561 | + 'u', |
| 562 | + field='uwroptions.prefix', |
| 563 | + usedefault=True, |
| 564 | + desc='realigned and unwarped output prefix') |
| 565 | + |
| 566 | + |
| 567 | +class RealignUnwarpOutputSpec(TraitedSpec): |
| 568 | + mean_image = File(exists=True, desc='Mean image file from the realignment & unwarping') |
| 569 | + modified_in_files = OutputMultiPath( |
| 570 | + traits.Either(traits.List(File(exists=True)), File(exists=True)), |
| 571 | + desc=('Copies of all files passed to ' |
| 572 | + 'in_files. Headers will have ' |
| 573 | + 'been modified to align all ' |
| 574 | + 'images with the first, or ' |
| 575 | + 'optionally to first do that, ' |
| 576 | + 'extract a mean image, and ' |
| 577 | + 're-align to that mean image.')) |
| 578 | + realigned_unwarped_files = OutputMultiPath( |
| 579 | + traits.Either(traits.List(File(exists=True)), File(exists=True)), |
| 580 | + desc='Realigned and unwarped files written to disc.') |
| 581 | + realignment_parameters = OutputMultiPath( |
| 582 | + File(exists=True), |
| 583 | + desc='Estimated translation and rotation parameters') |
| 584 | + |
| 585 | + |
| 586 | +class RealignUnwarp(SPMCommand): |
| 587 | + """Use spm_uw_estimate for estimating within subject registration and unwarping |
| 588 | + of time series. Function accepts only one single field map. If in_files is a |
| 589 | + list of files they will be treated as separate sessions but associated to the |
| 590 | + same fieldmap. |
| 591 | +
|
| 592 | + http://www.fil.ion.ucl.ac.uk/spm/doc/manual.pdf#page=31 |
| 593 | +
|
| 594 | + Examples |
| 595 | + -------- |
| 596 | +
|
| 597 | + >>> import nipype.interfaces.spm as spm |
| 598 | + >>> realignUnwarp = spm.RealignUnwarp() |
| 599 | + >>> realignUnwarp.inputs.in_files = ['functional.nii', 'functional2.nii'] |
| 600 | + >>> realignUnwarp.inputs.phase_map = 'voxeldisplacemap.vdm' |
| 601 | + >>> realignUnwarp.inputs.register_to_mean = True |
| 602 | + >>> realignUnwarp.run() # doctest: +SKIP |
| 603 | +
|
| 604 | + """ |
| 605 | + |
| 606 | + input_spec = RealignUnwarpInputSpec |
| 607 | + output_spec = RealignUnwarpOutputSpec |
| 608 | + |
| 609 | + _jobtype = 'spatial' |
| 610 | + _jobname = 'realignunwarp' |
| 611 | + |
| 612 | + def _format_arg(self, opt, spec, val): |
| 613 | + """Convert input to appropriate format for spm |
| 614 | + """ |
| 615 | + if opt == 'in_files': |
| 616 | + return scans_for_fnames(ensure_list(val), |
| 617 | + keep4d=False, |
| 618 | + separate_sessions=True) |
| 619 | + return super(RealignUnwarp, self)._format_arg(opt, spec, val) |
| 620 | + |
| 621 | + |
| 622 | + def _parse_inputs(self, skip=()): |
| 623 | + |
| 624 | + spmdict = super(RealignUnwarp, self)._parse_inputs(skip=())[0] |
| 625 | + |
| 626 | + if isdefined(self.inputs.phase_map): |
| 627 | + pmscan = spmdict['data']['pmscan'] |
| 628 | + else: |
| 629 | + pmscan = '' |
| 630 | + |
| 631 | + if isdefined(self.inputs.in_files): |
| 632 | + if isinstance(self.inputs.in_files, list): |
| 633 | + data = [dict(scans = sess, pmscan = pmscan) |
| 634 | + for sess in spmdict['data']['scans']] |
| 635 | + else: |
| 636 | + data = [dict(scans = spmdict['data']['scans'], pmscan = pmscan)] |
| 637 | + |
| 638 | + spmdict['data'] = data |
| 639 | + |
| 640 | + return [spmdict] |
| 641 | + |
| 642 | + |
| 643 | + def _list_outputs(self): |
| 644 | + outputs = self._outputs().get() |
| 645 | + resliced_all = self.inputs.reslice_which[0] > 0 |
| 646 | + resliced_mean = self.inputs.reslice_which[1] > 0 |
| 647 | + |
| 648 | + if isdefined(self.inputs.in_files): |
| 649 | + outputs['realignment_parameters'] = [] |
| 650 | + for imgf in self.inputs.in_files: |
| 651 | + if isinstance(imgf, list): |
| 652 | + tmp_imgf = imgf[0] |
| 653 | + else: |
| 654 | + tmp_imgf = imgf |
| 655 | + outputs['realignment_parameters'].append(fname_presuffix(tmp_imgf, |
| 656 | + prefix='rp_', |
| 657 | + suffix='.txt', |
| 658 | + use_ext=False)) |
| 659 | + if not isinstance(imgf, list) and func_is_3d(imgf): |
| 660 | + break |
| 661 | + |
| 662 | + if isinstance(self.inputs.in_files[0], list): |
| 663 | + first_image = self.inputs.in_files[0][0] |
| 664 | + else: |
| 665 | + first_image = self.inputs.in_files[0] |
| 666 | + |
| 667 | + if resliced_mean: |
| 668 | + outputs['mean_image'] = fname_presuffix(first_image, prefix='meanu') |
| 669 | + |
| 670 | + if resliced_all: |
| 671 | + outputs['realigned_unwarped_files'] = [] |
| 672 | + for idx, imgf in enumerate(ensure_list(self.inputs.in_files)): |
| 673 | + realigned_run = [] |
| 674 | + if isinstance(imgf, list): |
| 675 | + for i, inner_imgf in enumerate(ensure_list(imgf)): |
| 676 | + newfile = fname_presuffix(inner_imgf, |
| 677 | + prefix=self.inputs.out_prefix) |
| 678 | + realigned_run.append(newfile) |
| 679 | + else: |
| 680 | + realigned_run = fname_presuffix(imgf, |
| 681 | + prefix=self.inputs.out_prefix) |
| 682 | + outputs['realigned_unwarped_files'].append(realigned_run) |
| 683 | + return outputs |
| 684 | + |
| 685 | + |
440 | 686 | class CoregisterInputSpec(SPMCommandInputSpec):
|
441 | 687 | target = ImageFileSPM(
|
442 | 688 | exists=True,
|
|
0 commit comments