rpipe 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. data/.document +5 -0
  2. data/.gitignore +23 -0
  3. data/LICENSE +20 -0
  4. data/README +0 -0
  5. data/README.rdoc +33 -0
  6. data/Rakefile +78 -0
  7. data/VERSION +1 -0
  8. data/bin/create_driver.rb +79 -0
  9. data/bin/rpipe +131 -0
  10. data/bin/swallow_batch_run.rb +21 -0
  11. data/lib/core_additions.rb +5 -0
  12. data/lib/custom_methods/JohnsonMerit220Visit1Preproc.m +26 -0
  13. data/lib/custom_methods/JohnsonMerit220Visit1Preproc.rb +43 -0
  14. data/lib/custom_methods/JohnsonMerit220Visit1Preproc_job.m +80 -0
  15. data/lib/custom_methods/JohnsonMerit220Visit1Stats.m +74 -0
  16. data/lib/custom_methods/JohnsonMerit220Visit1Stats.rb +63 -0
  17. data/lib/custom_methods/JohnsonMerit220Visit1Stats_job.m +63 -0
  18. data/lib/custom_methods/JohnsonTbiLongitudinalSnodPreproc.m +26 -0
  19. data/lib/custom_methods/JohnsonTbiLongitudinalSnodPreproc.rb +41 -0
  20. data/lib/custom_methods/JohnsonTbiLongitudinalSnodPreproc_job.m +69 -0
  21. data/lib/custom_methods/JohnsonTbiLongitudinalSnodStats.m +76 -0
  22. data/lib/custom_methods/JohnsonTbiLongitudinalSnodStats.rb +67 -0
  23. data/lib/custom_methods/JohnsonTbiLongitudinalSnodStats_job.m +59 -0
  24. data/lib/custom_methods/ReconWithHello.rb +7 -0
  25. data/lib/default_logger.rb +13 -0
  26. data/lib/default_methods/default_preproc.rb +76 -0
  27. data/lib/default_methods/default_recon.rb +80 -0
  28. data/lib/default_methods/default_stats.rb +94 -0
  29. data/lib/default_methods/recon/physionoise_helper.rb +69 -0
  30. data/lib/default_methods/recon/raw_sequence.rb +109 -0
  31. data/lib/generators/job_generator.rb +36 -0
  32. data/lib/generators/preproc_job_generator.rb +31 -0
  33. data/lib/generators/recon_job_generator.rb +76 -0
  34. data/lib/generators/stats_job_generator.rb +70 -0
  35. data/lib/generators/workflow_generator.rb +128 -0
  36. data/lib/global_additions.rb +18 -0
  37. data/lib/logfile.rb +310 -0
  38. data/lib/matlab_helpers/CreateFunctionalVolumeStruct.m +6 -0
  39. data/lib/matlab_helpers/import_csv.m +32 -0
  40. data/lib/matlab_helpers/matlab_queue.rb +37 -0
  41. data/lib/matlab_helpers/prepare_onsets_xls.m +30 -0
  42. data/lib/rpipe.rb +254 -0
  43. data/rpipe.gemspec +177 -0
  44. data/spec/generators/preproc_job_generator_spec.rb +27 -0
  45. data/spec/generators/recon_job_generator_spec.rb +33 -0
  46. data/spec/generators/stats_job_generator_spec.rb +50 -0
  47. data/spec/generators/workflow_generator_spec.rb +97 -0
  48. data/spec/helper_spec.rb +40 -0
  49. data/spec/integration/johnson.merit220.visit1_spec.rb +47 -0
  50. data/spec/integration/johnson.tbi.longitudinal.snod_spec.rb +48 -0
  51. data/spec/logfile_spec.rb +96 -0
  52. data/spec/matlab_queue_spec.rb +40 -0
  53. data/spec/merit220_stats_spec.rb +81 -0
  54. data/spec/physio_spec.rb +98 -0
  55. data/test/drivers/merit220_workflow_sample.yml +15 -0
  56. data/test/drivers/mrt00000.yml +65 -0
  57. data/test/drivers/mrt00015.yml +62 -0
  58. data/test/drivers/mrt00015_hello.yml +41 -0
  59. data/test/drivers/mrt00015_withphys.yml +81 -0
  60. data/test/drivers/tbi000.yml +129 -0
  61. data/test/drivers/tbi000_separatevisits.yml +137 -0
  62. data/test/drivers/tmp.yml +58 -0
  63. data/test/fixtures/faces3_recognitionA.mat +0 -0
  64. data/test/fixtures/faces3_recognitionA.txt +86 -0
  65. data/test/fixtures/faces3_recognitionA_equal.csv +25 -0
  66. data/test/fixtures/faces3_recognitionA_unequal.csv +21 -0
  67. data/test/fixtures/faces3_recognitionB_incmisses.txt +86 -0
  68. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_CPd3R_40.txt +13360 -0
  69. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_CPd3_40.txt +13360 -0
  70. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_CPttl_40.txt +13360 -0
  71. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_CRTd3R_40.txt +13360 -0
  72. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_CRTd3_40.txt +13360 -0
  73. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_CRTttl_40.txt +13360 -0
  74. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_CRTd3R_40.txt +334 -0
  75. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_CRTd3_40.txt +334 -0
  76. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_CRTttl_40.txt +334 -0
  77. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_RRT_40.txt +334 -0
  78. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_RVT_40.txt +334 -0
  79. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_card_spline_40.txt +334 -0
  80. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_HalfTR_resp_spline_40.txt +334 -0
  81. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_RRT_40.txt +9106 -0
  82. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_RVT_40.txt +9106 -0
  83. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_CRTd3R_40.txt +167 -0
  84. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_CRTd3_40.txt +167 -0
  85. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_CRTttl_40.txt +167 -0
  86. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_RRT_40.txt +167 -0
  87. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_RVT_40.txt +167 -0
  88. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_card_spline_40.txt +167 -0
  89. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_TR_resp_spline_40.txt +167 -0
  90. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_card_spline_40.txt +13360 -0
  91. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_resp_spline_40.txt +9106 -0
  92. data/test/fixtures/physionoise_regressors/EPI__fMRI_Task1_resp_spline_downsampled_40.txt +9106 -0
  93. data/test/fixtures/ruport_summary.yml +123 -0
  94. data/test/fixtures/valid_scans.yaml +35 -0
  95. data/test/helper.rb +10 -0
  96. data/test/test_dynamic_method_inclusion.rb +10 -0
  97. data/test/test_includes.rb +11 -0
  98. data/test/test_integrative_johnson.merit220.visit1.rb +31 -0
  99. data/test/test_preproc.rb +11 -0
  100. data/test/test_recon.rb +11 -0
  101. data/test/test_rpipe.rb +19 -0
  102. data/vendor/output_catcher.rb +93 -0
  103. data/vendor/trollop.rb +781 -0
  104. metadata +260 -0
@@ -0,0 +1,63 @@
1
+ require 'logfile'
2
+ module JohnsonMerit220Visit1Stats
3
+
4
+ DEFAULT_CONDITIONS = [:new_correct_, :new_incorrect_, :old_correct_, :old_incorrect, {:misses => [:new_misses, :old_misses]} ]
5
+
6
+ # runs the complete set of tasks using data in a subject's "proc" directory and a preconfigured template spm job.
7
+ def run_first_level_stats
8
+ flash "Highway to the dangerzone..."
9
+ setup_directory(@statsdir, "STATS")
10
+
11
+ Dir.chdir(@statsdir) do
12
+ link_files_from_proc_directory(File.join(@procdir, "sw*.nii"), File.join(@procdir, "rp*.txt"))
13
+ setup_onsets
14
+ run_stats_spm_job
15
+ end
16
+ end
17
+
18
+ alias_method :perform, :run_first_level_stats
19
+
20
+ def setup_onsets
21
+ setup_conditions
22
+ create_or_link_onsets_files
23
+ end
24
+
25
+ def setup_conditions
26
+ @conditions = @conditions ? @conditions.collect! {|c| c.to_sym if c.respond_to? :to_sym } : DEFAULT_CONDITIONS
27
+ end
28
+
29
+ def create_or_link_onsets_files
30
+ if @onsetsfiles.nil?
31
+ if @responses.nil?
32
+ raise ScriptError, "Condition vectors cannot be created because neither log response files nor onsets mat files have been specified."
33
+ else
34
+ @onsetsfiles = create_onsets_files(@responses, conditions)
35
+ end
36
+ else
37
+ link_onsets_files
38
+ end
39
+ end
40
+
41
+ # Finally runs the stats job
42
+ def run_stats_spm_job
43
+ images = Dir.glob(File.join(@statsdir, "sw*#{@subid}*[Tt]ask*.nii"))
44
+ raise ScriptError, "Can't find any smoothed, warped images in #{@statsdir}" if images.empty?
45
+
46
+ queue = MatlabQueue.new
47
+ queue.paths << ['/Applications/spm/spm8/spm8_current',
48
+ '/apps/spm/spm8_current',
49
+ File.join(@libdir, 'custom_methods'),
50
+ File.join(@libdir, 'matlab_helpers') ]
51
+
52
+ queue << "#{@method}('#{@statsdir}/', \
53
+ { #{images.collect {|im| "'#{File.basename(im)}'"}.join(' ')} }, \
54
+ { #{@bold_reps.join(' ') } }, \
55
+ { #{@onsetsfiles.collect { |file| "'#{File.basename(file)}'"}.join(' ') } }, \
56
+ { #{@regressorsfiles.collect { |file| "'#{File.basename(file)}'"}.join(' ') } }, \
57
+ '#{@method}_job.m')"
58
+
59
+ # puts queue.to_s
60
+ queue.run!
61
+ end
62
+
63
+ end
@@ -0,0 +1,63 @@
1
+ %-----------------------------------------------------------------------
2
+ % Job configuration created by cfg_util (rev $Rev: 3599 $)
3
+ %-----------------------------------------------------------------------
4
+ %% Specification
5
+ matlabbatch{1}.spm.stats.fmri_spec.dir = '<UNDEFINED>';
6
+ matlabbatch{1}.spm.stats.fmri_spec.timing.units = 'scans';
7
+ matlabbatch{1}.spm.stats.fmri_spec.timing.RT = 2;
8
+ matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t = 16;
9
+ matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t0 = 1;
10
+ matlabbatch{1}.spm.stats.fmri_spec.sess(1).scans = '<UNDEFINED>';
11
+ matlabbatch{1}.spm.stats.fmri_spec.sess(1).cond = struct('name', {}, 'onset', {}, 'duration', {}, 'tmod', {}, 'pmod', {});
12
+ matlabbatch{1}.spm.stats.fmri_spec.sess(1).multi = '<UNDEFINED>';
13
+ matlabbatch{1}.spm.stats.fmri_spec.sess(1).regress = struct('name', {}, 'val', {});
14
+ matlabbatch{1}.spm.stats.fmri_spec.sess(1).multi_reg = '<UNDEFINED>';
15
+ matlabbatch{1}.spm.stats.fmri_spec.sess(1).hpf = 128;
16
+ matlabbatch{1}.spm.stats.fmri_spec.sess(2).scans = '<UNDEFINED>';
17
+ matlabbatch{1}.spm.stats.fmri_spec.sess(2).cond = struct('name', {}, 'onset', {}, 'duration', {}, 'tmod', {}, 'pmod', {});
18
+ matlabbatch{1}.spm.stats.fmri_spec.sess(2).multi = '<UNDEFINED>';
19
+ matlabbatch{1}.spm.stats.fmri_spec.sess(2).regress = struct('name', {}, 'val', {});
20
+ matlabbatch{1}.spm.stats.fmri_spec.sess(2).multi_reg = '<UNDEFINED>';
21
+ matlabbatch{1}.spm.stats.fmri_spec.sess(2).hpf = 128;
22
+ matlabbatch{1}.spm.stats.fmri_spec.fact = struct('name', {}, 'levels', {});
23
+ matlabbatch{1}.spm.stats.fmri_spec.bases.hrf.derivs = [0 0];
24
+ matlabbatch{1}.spm.stats.fmri_spec.volt = 1;
25
+ matlabbatch{1}.spm.stats.fmri_spec.global = 'None';
26
+ matlabbatch{1}.spm.stats.fmri_spec.mask = {''};
27
+ matlabbatch{1}.spm.stats.fmri_spec.cvi = 'AR(1)';
28
+ %% Estimation
29
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1) = cfg_dep;
30
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tname = 'Select SPM.mat';
31
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(1).name = 'filter';
32
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(1).value = 'mat';
33
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(2).name = 'strtype';
34
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(2).value = 'e';
35
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).sname = 'fMRI model specification: SPM.mat File';
36
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).src_exbranch = substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1});
37
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).src_output = substruct('.','spmmat');
38
+ matlabbatch{2}.spm.stats.fmri_est.method.Classical = 1;
39
+ %% Contrast Creation
40
+ matlabbatch{3}.spm.stats.con.spmmat(1) = cfg_dep;
41
+ matlabbatch{3}.spm.stats.con.spmmat(1).tname = 'Select SPM.mat';
42
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(1).name = 'filter';
43
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(1).value = 'mat';
44
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(2).name = 'strtype';
45
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(2).value = 'e';
46
+ matlabbatch{3}.spm.stats.con.spmmat(1).sname = 'Model estimation: SPM.mat File';
47
+ matlabbatch{3}.spm.stats.con.spmmat(1).src_exbranch = substruct('.','val', '{}',{2}, '.','val', '{}',{1}, '.','val', '{}',{1});
48
+ matlabbatch{3}.spm.stats.con.spmmat(1).src_output = substruct('.','spmmat');
49
+ matlabbatch{3}.spm.stats.con.consess{1}.fcon.name = '<UNDEFINED>';
50
+ matlabbatch{3}.spm.stats.con.consess{1}.fcon.convec{1} = '<UNDEFINED>';
51
+ matlabbatch{3}.spm.stats.con.consess{1}.fcon.sessrep = 'repl';
52
+ matlabbatch{3}.spm.stats.con.consess{2}.tcon.name = '<UNDEFINED>';
53
+ matlabbatch{3}.spm.stats.con.consess{2}.tcon.convec = '<UNDEFINED>';
54
+ matlabbatch{3}.spm.stats.con.consess{2}.tcon.sessrep = 'repl';
55
+ matlabbatch{3}.spm.stats.con.consess{3}.tcon.name = '<UNDEFINED>';
56
+ matlabbatch{3}.spm.stats.con.consess{3}.tcon.convec = '<UNDEFINED>';
57
+ matlabbatch{3}.spm.stats.con.consess{3}.tcon.sessrep = 'repl';
58
+ matlabbatch{3}.spm.stats.con.delete = 0;
59
+
60
+ % NOTE! F-contrasts expect cells containing contrast arrays, while
61
+ % T-contrasts expect the arrays themslves (hence the cell reference in
62
+ % F-contrast definition (convec{1} = [];) but not in T-contrast definition
63
+ % (convec = [];)
@@ -0,0 +1,26 @@
1
+ function [] = JohnsonTbiLongitudinalSnodPreproc(studypath, images, image_boldreps, job_mfile)
2
+ % [] = Calls and runs TBI Longitudinal preproc job.
3
+ % merit_preproc('/private/tmp/mrt00015_orig/', ...
4
+ % {'amrt00015_task1.nii' 'amrt00015_task2.nii' 'amrt00015_rest.nii'}, ...
5
+ % {164 164 164}, ...
6
+ % '/private/tmp/mrt00015_orig/mrt00015_preproc_job.m')
7
+ %
8
+
9
+ spm('defaults', 'FMRI');
10
+
11
+ % List of open inputs
12
+ % Realign: Estimate: Session - cfg_files
13
+ % Realign: Estimate: Session - cfg_files
14
+ % Realign: Estimate: Session - cfg_files
15
+ nrun = 1; % enter the number of runs here
16
+ jobfile = {job_mfile};
17
+ jobs = repmat(jobfile, 1, nrun);
18
+ inputs = cell(3, nrun);
19
+
20
+ for crun = 1:nrun
21
+ for index = 1:length(images)
22
+ inputs{index, crun} = CreateFunctionalVolumeStruct(studypath, images{index}, image_boldreps{index}); % Realign: Estimate: Session - cfg_files
23
+ end
24
+ end
25
+
26
+ spm_jobman('serial', jobs, '', inputs{:});
@@ -0,0 +1,41 @@
1
+ require 'matlab_helpers/matlab_queue'
2
+ module JohnsonTbiLongitudinalSnodPreproc
3
+
4
+ # Runs the preprocessing job, including spm job customization, run spm job, and handling motion issues.
5
+ # This function assumes a destination directory is set up; it will overwrite preexisting data. Careful!
6
+ def preproc_visit
7
+ flash "Spatial Preprocessing Subject: #{@subid}"
8
+ setup_directory(@procdir, "PREPROC")
9
+
10
+ Dir.chdir(@procdir) do
11
+ link_files_into_proc
12
+ run_preproc_mfile
13
+ deal_with_motion
14
+ end
15
+ end
16
+
17
+ alias_method :perform, :preproc_visit
18
+
19
+ private
20
+
21
+ def run_preproc_mfile
22
+ images = @scan_labels ? @scan_labels.collect! { |label| Dir.glob("a*#{label}*.nii").to_s } : Dir.glob(File.join(@origdir, "a*#{@subid}*.nii"))
23
+
24
+ raise ScriptError, "Can't find any slice-time corrected images in #{@origdir}" if images.empty?
25
+ queue = MatlabQueue.new
26
+ queue.paths << ['/Applications/spm/spm8/spm8_current',
27
+ '/apps/spm/spm8_current',
28
+ File.join(@libdir, 'custom_methods'),
29
+ File.join(@libdir, 'matlab_helpers')
30
+ ]
31
+
32
+ queue << "JohnsonTbiLongitudinalSnodPreproc('#{@procdir}/', \
33
+ { #{images.collect {|im| "'#{File.basename(im)}'"}.join(' ')} }, \
34
+ { #{@bold_reps.join(' ') } }, \
35
+ 'JohnsonTbiLongitudinalSnodPreproc_job.m')"
36
+
37
+ puts queue.to_s
38
+ queue.run!
39
+ end
40
+
41
+ end
@@ -0,0 +1,69 @@
1
+ %-----------------------------------------------------------------------------
2
+ % Unfortunately, there is no easy way to pass in the number of images to this
3
+ % template job, short of writing a function to build one on the fly. Number of
4
+ % sessions is hardcoded.
5
+ nsessions = 6;
6
+ %-----------------------------------------------------------------------------
7
+
8
+ %-----------------------------------------------------------------------
9
+ % Job configuration created by cfg_util (rev $Rev: 3599 $)
10
+ %-----------------------------------------------------------------------
11
+ for i = 1:nsessions
12
+ matlabbatch{1}.spm.spatial.realign.estimate.data{i} = '<UNDEFINED>';
13
+ end
14
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.quality = 0.9;
15
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.sep = 4;
16
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.fwhm = 5;
17
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.rtm = 1;
18
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.interp = 2;
19
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.wrap = [0 0 0];
20
+ matlabbatch{1}.spm.spatial.realign.estimate.eoptions.weight = {''};
21
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1) = cfg_dep;
22
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).tname = 'Source Image';
23
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).tgt_spec{1}(1).name = 'filter';
24
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).tgt_spec{1}(1).value = 'image';
25
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).tgt_spec{1}(2).name = 'strtype';
26
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).tgt_spec{1}(2).value = 'e';
27
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).sname = 'Realign: Estimate: Realigned Images (Sess 1)';
28
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).src_exbranch = substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1});
29
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.source(1).src_output = substruct('.','sess', '()',{1}, '.','cfiles');
30
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.wtsrc = '';
31
+ for i = 1:nsessions
32
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i) = cfg_dep;
33
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).tname = 'Images to Write';
34
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).tgt_spec{1}(1).name = 'filter';
35
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).tgt_spec{1}(1).value = 'image';
36
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).tgt_spec{1}(2).name = 'strtype';
37
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).tgt_spec{1}(2).value = 'e';
38
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).sname = ['Realign: Estimate: Realigned Images (Sess' i ')'];
39
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).src_exbranch = substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1});
40
+ matlabbatch{2}.spm.spatial.normalise.estwrite.subj.resample(i).src_output = substruct('.','sess', '()',{i}, '.','cfiles');
41
+ end
42
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.template = {[spm('Dir') '/templates/EPI.nii,1']};
43
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.weight = '';
44
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.smosrc = 8;
45
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.smoref = 0;
46
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.regtype = 'mni';
47
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.cutoff = 25;
48
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.nits = 16;
49
+ matlabbatch{2}.spm.spatial.normalise.estwrite.eoptions.reg = 1;
50
+ matlabbatch{2}.spm.spatial.normalise.estwrite.roptions.preserve = 0;
51
+ matlabbatch{2}.spm.spatial.normalise.estwrite.roptions.bb = [-78 -112 -50
52
+ 78 76 85];
53
+ matlabbatch{2}.spm.spatial.normalise.estwrite.roptions.vox = [2 2 2];
54
+ matlabbatch{2}.spm.spatial.normalise.estwrite.roptions.interp = 1;
55
+ matlabbatch{2}.spm.spatial.normalise.estwrite.roptions.wrap = [0 0 0];
56
+ matlabbatch{2}.spm.spatial.normalise.estwrite.roptions.prefix = 'w';
57
+ matlabbatch{3}.spm.spatial.smooth.data(1) = cfg_dep;
58
+ matlabbatch{3}.spm.spatial.smooth.data(1).tname = 'Images to Smooth';
59
+ matlabbatch{3}.spm.spatial.smooth.data(1).tgt_spec{1}(1).name = 'filter';
60
+ matlabbatch{3}.spm.spatial.smooth.data(1).tgt_spec{1}(1).value = 'image';
61
+ matlabbatch{3}.spm.spatial.smooth.data(1).tgt_spec{1}(2).name = 'strtype';
62
+ matlabbatch{3}.spm.spatial.smooth.data(1).tgt_spec{1}(2).value = 'e';
63
+ matlabbatch{3}.spm.spatial.smooth.data(1).sname = 'Normalise: Estimate & Write: Normalised Images (Subj 1)';
64
+ matlabbatch{3}.spm.spatial.smooth.data(1).src_exbranch = substruct('.','val', '{}',{2}, '.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1});
65
+ matlabbatch{3}.spm.spatial.smooth.data(1).src_output = substruct('()',{1}, '.','files');
66
+ matlabbatch{3}.spm.spatial.smooth.fwhm = [8 8 8];
67
+ matlabbatch{3}.spm.spatial.smooth.dtype = 0;
68
+ matlabbatch{3}.spm.spatial.smooth.im = 0;
69
+ matlabbatch{3}.spm.spatial.smooth.prefix = 's';
@@ -0,0 +1,76 @@
1
+ function [] = JohnsonTbiLongitudinalSnodStats(statsdir, images, image_boldreps, conditions, regressors, job_mfile)
2
+
3
+ % List of open inputs
4
+ % fMRI model specification: Directory - cfg_entry
5
+ % 6 Scans Each with:
6
+ % fMRI model specification: Scans
7
+ % fMRI model specification: Multiple Conditions File - cfg_entry
8
+ % fMRI model specification: Multiple Regressors File - cfg_entry
9
+
10
+ spm('defaults', 'FMRI');
11
+
12
+ % statsdir = '/private/tmp/mrt00015_stats/'
13
+ % images = { 'swamrt00015_task1.nii' 'swamrt00015_task2.nii' }
14
+ % image_boldreps = {164 164}
15
+ % regressors = { 'mrt00015_faces3_recognitionA.mat' 'mrt00015_faces3_recognitionB.mat' }
16
+ % job_mfile = '/private/tmp/mrt00015_stats/Merit220Stats_job.m'
17
+
18
+ nrun = 1; % enter the number of runs here
19
+ % jobfile = {'/private/tmp/mrt00015_proc/Merit220_Stats_job.m'};
20
+ jobfile = {job_mfile};
21
+ jobs = repmat(jobfile, 1, nrun);
22
+ inputs = cell(length(images) + 1, nrun);
23
+
24
+ % Load the first conditions file to get the length of the names.
25
+ eval(['load ' conditions{1}]);
26
+
27
+ switch numel(names)
28
+ case 2 % New, Old
29
+ fcontrast_vector = [1 0
30
+ 0 1];
31
+ tcontrast_1_vector = [-1 1];
32
+ tcontrast_2_vector = [1 -1];
33
+ % case 4 % New_correct, New_incorrect, Old_correct, Old_incorrect
34
+ % fcontrast_vector = [1 0 0 0
35
+ % 0 1 0 0
36
+ % 0 0 1 0
37
+ % 0 0 0 1 ];
38
+ % tcontrast_1_vector = [-1 0 1 0];
39
+ % tcontrast_2_vector = [ 1 0 -1 0];
40
+ % case 5 % New_correct, New_incorrect, Old_correct, Old_incorrect, Misses
41
+ % fcontrast_vector = [1 0 0 0
42
+ % 0 1 0 0
43
+ % 0 0 1 0
44
+ % 0 0 0 1 ];
45
+ % tcontrast_1_vector = [-1 0 1 0];
46
+ % tcontrast_2_vector = [ 1 0 -1 0];
47
+ otherwise
48
+ error('Incorrect number of conditions detected: %d', numel(conditions));
49
+ end
50
+
51
+ for crun = 1:nrun
52
+ inputs{1, crun} = { statsdir }; % fMRI model specification: Directory - cfg_entry
53
+
54
+ % fMRI model specification: Directory - cfg_entry
55
+ for index = 1:length(images)
56
+ offset = (index - 1) .* 3;
57
+ inputs{offset + 2, crun} = CreateFunctionalVolumeStruct(statsdir, images{index}, image_boldreps{index}); % fMRI model specification: Scans - cfg_entry
58
+ inputs{offset + 3, crun} = { strcat(statsdir, conditions{index} ) }; % fMRI model specification: Multiple Conditions File - cfg_entry
59
+ inputs{offset + 4, crun} = { strcat(statsdir, regressors{index} ) }; % fMRI model specification: Multiple Regressors File - cfg_entry
60
+ end
61
+
62
+ image_offset = (length(images) .* 3) + 1;
63
+ inputs{image_offset + 1, crun} = 'Omnibus F'; % fMRI model specification: Omnibus Title - cfg_entry
64
+ inputs{image_offset + 2, crun} = fcontrast_vector; % fMRI model specification: Omnibus Contrast - cfg_entry
65
+
66
+ inputs{image_offset + 3, crun} = 'NV > PV'; % fMRI model specification: T Contrast 1 Title - cfg_entry
67
+ inputs{image_offset + 4, crun} = tcontrast_1_vector; % fMRI model specification: T Contrast 1 Vector - cfg_entry
68
+
69
+ inputs{image_offset + 5, crun} = 'PV > NV'; % fMRI model specification: T Contrast 2 Title - cfg_entry
70
+ inputs{image_offset + 6, crun} = tcontrast_2_vector; % fMRI model specification: T Contrast 2 Vector - cfg_entry
71
+
72
+ end
73
+
74
+
75
+
76
+ spm_jobman('serial', jobs, '', inputs{:});
@@ -0,0 +1,67 @@
1
+ # require 'logfile'
2
+ module JohnsonTbiLongitudinalSnodStats
3
+
4
+ DEFAULT_CONDITIONS = [:new, :old]
5
+
6
+ # runs the complete set of tasks using data in a subject's "proc" directory and a preconfigured template spm job.
7
+ def run_first_level_stats
8
+ flash "Highway to the dangerzone..."
9
+ setup_directory(@statsdir, "STATS")
10
+
11
+ Dir.chdir(@statsdir) do
12
+ link_files_from_proc_directory(File.join(@procdir, "sw*.nii"), File.join(@procdir, "rp*.txt"))
13
+ setup_onsets
14
+ run_stats_spm_job
15
+ end
16
+ end
17
+
18
+ alias_method :perform, :run_first_level_stats
19
+
20
+ def setup_onsets
21
+ setup_conditions
22
+ create_or_link_onsets_files
23
+ end
24
+
25
+ def setup_conditions
26
+ @conditions = @conditions ? @conditions.collect {|c| c.to_sym if c.respond_to? :to_sym } : DEFAULT_CONDITIONS
27
+ end
28
+
29
+ def create_or_link_onsets_files
30
+ if @onsetsfiles.nil?
31
+ if @responses.nil?
32
+ raise ScriptError, "Condition vectors cannot be created because neither log response files nor onsets mat files have been specified."
33
+ else
34
+ @onsetsfiles = create_onsets_files(@responses, conditions)
35
+ end
36
+ else
37
+ link_onsets_files
38
+ end
39
+ end
40
+
41
+ # Finally runs the stats job
42
+ def run_stats_spm_job
43
+ images = @scan_labels ? @scan_labels.collect! { |label| Dir.glob("sw*#{label}*.nii").to_s } : Dir.glob(File.join(@origdir, "sw*#{@subid}*.nii"))
44
+ raise ScriptError, "Can't find any smoothed, warped images in #{@statsdir}" if images.empty?
45
+ unless @onsetsfiles.length == @regressorsfiles.length && @regressorsfiles.length == @bold_reps.length
46
+ raise ScriptError, "Mismatch between #{@bold_reps.length} reps, #{@onsetsfiles.length} onsets and #{@regressorsfiles.length} regressors files."
47
+ end
48
+
49
+ queue = MatlabQueue.new
50
+ queue.paths << [
51
+ '/Applications/spm/spm8/spm8_current',
52
+ '/apps/spm/spm8_current',
53
+ File.join(@libdir, 'custom_methods'),
54
+ File.join(@libdir, 'matlab_helpers') ]
55
+
56
+ queue << "JohnsonTbiLongitudinalSnodStats('#{@statsdir}/', \
57
+ { #{images.collect {|im| "'#{File.basename(im)}'"}.join(' ')} }, \
58
+ { #{@bold_reps.join(' ') } }, \
59
+ { #{@onsetsfiles.collect { |file| "'#{File.basename(file)}'"}.join(' ') } }, \
60
+ { #{@regressorsfiles.collect { |file| "'#{File.basename(file)}'"}.join(' ') } }, \
61
+ 'JohnsonTbiLongitudinalSnodStats_job.m')"
62
+
63
+ # puts queue.to_s
64
+ queue.run!
65
+ end
66
+
67
+ end
@@ -0,0 +1,59 @@
1
+ %-----------------------------------------------------------------------
2
+ % Job configuration created by cfg_util (rev $Rev: 3599 $)
3
+ %-----------------------------------------------------------------------
4
+ %% Specification
5
+ matlabbatch{1}.spm.stats.fmri_spec.dir = '<UNDEFINED>';
6
+ matlabbatch{1}.spm.stats.fmri_spec.timing.units = 'scans';
7
+ matlabbatch{1}.spm.stats.fmri_spec.timing.RT = 2;
8
+ matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t = 16;
9
+ matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t0 = 1;
10
+ for i = 1:2
11
+ matlabbatch{1}.spm.stats.fmri_spec.sess(i).scans = '<UNDEFINED>';
12
+ matlabbatch{1}.spm.stats.fmri_spec.sess(i).cond = struct('name', {}, 'onset', {}, 'duration', {}, 'tmod', {}, 'pmod', {});
13
+ matlabbatch{1}.spm.stats.fmri_spec.sess(i).multi = '<UNDEFINED>';
14
+ matlabbatch{1}.spm.stats.fmri_spec.sess(i).regress = struct('name', {}, 'val', {});
15
+ matlabbatch{1}.spm.stats.fmri_spec.sess(i).multi_reg = '<UNDEFINED>';
16
+ matlabbatch{1}.spm.stats.fmri_spec.sess(i).hpf = 128;
17
+ end
18
+ matlabbatch{1}.spm.stats.fmri_spec.fact = struct('name', {}, 'levels', {});
19
+ matlabbatch{1}.spm.stats.fmri_spec.bases.hrf.derivs = [0 0];
20
+ matlabbatch{1}.spm.stats.fmri_spec.volt = 1;
21
+ matlabbatch{1}.spm.stats.fmri_spec.global = 'None';
22
+ matlabbatch{1}.spm.stats.fmri_spec.mask = {''};
23
+ matlabbatch{1}.spm.stats.fmri_spec.cvi = 'AR(1)';
24
+ %% Estimation
25
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1) = cfg_dep;
26
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tname = 'Select SPM.mat';
27
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(1).name = 'filter';
28
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(1).value = 'mat';
29
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(2).name = 'strtype';
30
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(2).value = 'e';
31
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).sname = 'fMRI model specification: SPM.mat File';
32
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).src_exbranch = substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1});
33
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1).src_output = substruct('.','spmmat');
34
+ matlabbatch{2}.spm.stats.fmri_est.method.Classical = 1;
35
+ %% Contrast Creation
36
+ matlabbatch{3}.spm.stats.con.spmmat(1) = cfg_dep;
37
+ matlabbatch{3}.spm.stats.con.spmmat(1).tname = 'Select SPM.mat';
38
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(1).name = 'filter';
39
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(1).value = 'mat';
40
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(2).name = 'strtype';
41
+ matlabbatch{3}.spm.stats.con.spmmat(1).tgt_spec{1}(2).value = 'e';
42
+ matlabbatch{3}.spm.stats.con.spmmat(1).sname = 'Model estimation: SPM.mat File';
43
+ matlabbatch{3}.spm.stats.con.spmmat(1).src_exbranch = substruct('.','val', '{}',{2}, '.','val', '{}',{1}, '.','val', '{}',{1});
44
+ matlabbatch{3}.spm.stats.con.spmmat(1).src_output = substruct('.','spmmat');
45
+ matlabbatch{3}.spm.stats.con.consess{1}.fcon.name = '<UNDEFINED>';
46
+ matlabbatch{3}.spm.stats.con.consess{1}.fcon.convec{1} = '<UNDEFINED>';
47
+ matlabbatch{3}.spm.stats.con.consess{1}.fcon.sessrep = 'repl';
48
+ matlabbatch{3}.spm.stats.con.consess{2}.tcon.name = '<UNDEFINED>';
49
+ matlabbatch{3}.spm.stats.con.consess{2}.tcon.convec = '<UNDEFINED>';
50
+ matlabbatch{3}.spm.stats.con.consess{2}.tcon.sessrep = 'repl';
51
+ matlabbatch{3}.spm.stats.con.consess{3}.tcon.name = '<UNDEFINED>';
52
+ matlabbatch{3}.spm.stats.con.consess{3}.tcon.convec = '<UNDEFINED>';
53
+ matlabbatch{3}.spm.stats.con.consess{3}.tcon.sessrep = 'repl';
54
+ matlabbatch{3}.spm.stats.con.delete = 0;
55
+
56
+ % NOTE! F-contrasts expect cells containing contrast arrays, while
57
+ % T-contrasts expect the arrays themslves (hence the cell reference in
58
+ % F-contrast definition (convec{1} = [];) but not in T-contrast definition
59
+ % (convec = [];)
@@ -0,0 +1,7 @@
1
+ module ReconWithHello
2
+
3
+ def hello
4
+ "=== Hello World! ==="
5
+ end
6
+
7
+ end
@@ -0,0 +1,13 @@
1
+ require 'log4r'
2
+
3
+ # Setup Command and General Logs to point to STDOUT if not defined.
4
+ module DefaultLogger
5
+ def setup_logger
6
+ %w{$Log $CommandLog}.each do |log|
7
+ unless eval(log)
8
+ eval("#{log} = Log4r::Logger.new('output')")
9
+ eval("#{log}.add Log4r::StdoutOutputter.new(:stdout)")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,76 @@
1
+ module DefaultPreproc
2
+
3
+ # Runs the preprocessing job, including spm job customization, run spm job, and handling motion issues.
4
+ # This function assumes a destination directory is set up; it will overwrite preexisting data. Careful!
5
+ def preproc_visit
6
+ $Log.info "Spatial Preprocessing Subject: #{@subid}"
7
+
8
+ setup_directory(@procdir, "PREPROC")
9
+
10
+ Dir.chdir(@procdir) do
11
+ link_files_into_proc
12
+ customize_templates
13
+ run_spm_jobs
14
+ deal_with_motion
15
+ end
16
+ end
17
+
18
+ alias_method :perform, :preproc_visit
19
+
20
+ # Links all the slice timing corrected data from the source "orig" directory using a wildcard a${subid}*.nii,
21
+ # where subid is the subject id specified in the preproc_spec hash.
22
+ def link_files_into_proc
23
+ flash "Linking files from #{@origdir} into #{@procdir}"
24
+ wildcard = File.join(@origdir,"a*#{@subid}*.nii")
25
+ files = Dir.glob(wildcard)
26
+ unless files.empty?
27
+ system("ln -s #{wildcard} #{@procdir}")
28
+ else
29
+ raise(IOError, "No files matching #{wildcard} found.")
30
+ end
31
+ end
32
+
33
+ def check_permissions(files)
34
+ unwritable = []
35
+ files.collect { |file| unwritable << file unless File.writable?(file) }
36
+ raise IOError, "Cannot write to #{unwritable.join(", ")} files." unless unwritable.empty?
37
+ end
38
+
39
+ # Customizes the template job in preproc_spec to be specific for this particular preproc job.
40
+ # Performs to recursive string replacements inside the spm job:
41
+ # - path inside template is replaced with destination "proc" directory
42
+ # - subid inside template is replaced with the current subid
43
+ def customize_templates
44
+ flash "Customizing template SPM job: #{@tspec['job']}"
45
+ replacecmd = "spmjobStringReplace.sh"
46
+
47
+ templatejob = File.join(@spmdir, @tspec['job'])
48
+ thisjob = @subid + '_preproc.mat'
49
+
50
+ File.copy(templatejob, thisjob)
51
+ system("#{replacecmd} #{thisjob} #{@tspec['path']} #{@procdir} #{thisjob}")
52
+ system("#{replacecmd} #{thisjob} #{@tspec['subid']} #{@subid} #{thisjob}")
53
+ end
54
+
55
+ # Runs the customized spm job using the shell script runSpmJob.sh. Make sure this is available at your site.
56
+ def run_spm_jobs
57
+ thisjob = "#{@subid}_preproc.mat"
58
+ flash "Running spatial preprocessing SPM job: #{thisjob}"
59
+ system("runSpmJob.sh #{thisjob}")
60
+ end
61
+
62
+ # Calculates the realignment motion derivatives and checks that displacement
63
+ # in all directions was less than the MOTION_THRESHOLD. Operates on all files
64
+ # in the current working directory that match rp_a*txt (SPM convention). Uses
65
+ # two shell scripts that must both be available on the local machine:
66
+ # - calc_derivs.sh
67
+ # - fmri_motion_check.sh
68
+ def deal_with_motion
69
+ flash "Calculating motion derivatives and checking for excessive motion"
70
+ Dir.glob("rp_a*txt").each do |rp|
71
+ run("calc_derivs.sh #{rp}")
72
+ run("fmri_motion_check.sh #{rp} #{@motion_threshold}")
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,80 @@
1
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', '..', '..', 'physionoise/lib')
2
+
3
+ require 'metamri/core_additions'
4
+ require 'physionoise'
5
+ require 'pathname'
6
+ require 'default_methods/recon/physionoise_helper'
7
+ require 'default_methods/recon/raw_sequence'
8
+
9
+ module DefaultRecon
10
+
11
+ # Reconstructs, strips, and slice timing corrects all scans specified in the recon_spec.
12
+ # This function assumes a destination directory is set up in the filesystem and begins writing
13
+ # to it with no further checking. It will overwrite data if it already exists, be careful.
14
+ def recon_visit
15
+
16
+ setup_directory(@origdir, "RECON")
17
+
18
+ Dir.chdir(@origdir) do
19
+ @scans.each_with_index do |scan_spec, i|
20
+ outfile = "%s_%s.nii" % [@subid, scan_spec['label']]
21
+
22
+ reconstruct_scan(scan_spec, outfile)
23
+
24
+ if scan_spec['type'] == "func"
25
+ # if scan_spec['physio_files']
26
+ # create_physiosnoise_regressors(scan_spec)
27
+ # outfile = run_retroicor(scan_spec['physio_files'], outfile)
28
+ # end
29
+
30
+ slice_time_correct(outfile)
31
+ else
32
+ File.copy('tmp.nii', outfile)
33
+ end
34
+
35
+ File.delete('tmp.nii') if File.exist? 'tmp.nii'
36
+ end
37
+ end
38
+ end
39
+
40
+ alias_method :perform, :recon_visit
41
+
42
+ # Reconstructs a scan from dicoms or pfile to nifti, anatomical or functional.
43
+ # Uses a scan_spec hash to drive. Writes the result in current working
44
+ # directory. Raises an error if to3d system call fails. Conventions: I****.dcm
45
+ # filenaming, I0002.dcm is second file in series,
46
+ def reconstruct_scan(scan_spec, outfile)
47
+ if scan_spec['dir']
48
+ sequence = DicomRawSequence.new(scan_spec, @rawdir)
49
+ sequence.prepare('tmp.nii')
50
+ strip_leading_volumes('tmp.nii', outfile, @volume_skip, scan_spec['bold_reps'])
51
+ elsif scan_spec['pfile']
52
+ sequence = PfileRawSequence.new(scan_spec, @rawdir)
53
+ sequence.prepare(outfile)
54
+ else
55
+ raise ConfigError, "Scan must list either a pfile or a dicom directory."
56
+ end
57
+ end
58
+
59
+ # Removes the specified number of volumes from the beginning of a 4D functional nifti file.
60
+ # In most cases this will be 3 volumes. Writes result in current working directory.
61
+ def strip_leading_volumes(infile, outfile, volume_skip, bold_reps)
62
+ $Log.info "Stripping #{volume_skip.to_s} leading volumes: #{infile}"
63
+ cmd_fmt = "fslroi %s %s %s %s"
64
+ cmd_options = [infile, outfile, volume_skip.to_s, bold_reps.to_s]
65
+ cmd = cmd_fmt % cmd_options
66
+ unless run(cmd)
67
+ raise ScriptError, "Failed to strip volumes: #{cmd}"
68
+ end
69
+ end
70
+
71
+ # Uses to3d to slice time correct a 4D functional nifti file. Writes result in the current working directory.
72
+ def slice_time_correct(infile)
73
+ $Log.info "Slice Timing Correction: #{infile}"
74
+ cmd = "3dTshift -tzero 0 -tpattern alt+z -prefix a#{infile} #{infile}"
75
+ unless run(cmd)
76
+ raise ScriptError, "Failed to slice time correct: #{cmd}"
77
+ end
78
+ end
79
+
80
+ end