bestlinreg_s2 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. #! /usr/bin/env perl
  2. #
  3. # linear fitting using parameters optimised by Claude Lepage,
  4. # using a brain mask for the source and the target. This was
  5. # greatly inspired by best1stepnlreg.pl by Steve Robbins.
  6. #
  7. # Claude Lepage - claude@bic.mni.mcgill.ca
  8. # Andrew Janke - rotor@cmr.uq.edu.au
  9. # Center for Magnetic Resonance
  10. # The University of Queensland
  11. # http://www.cmr.uq.edu.au/~rotor
  12. #
  13. # Copyright Andrew Janke, The University of Queensland.
  14. # Permission to use, copy, modify, and distribute this software and its
  15. # documentation for any purpose and without fee is hereby granted,
  16. # provided that the above copyright notice appear in all copies. The
  17. # author and the University of Queensland make no representations about the
  18. # suitability of this software for any purpose. It is provided "as is"
  19. # without express or implied warranty.
  20. use strict;
  21. use warnings "all";
  22. use Getopt::Tabular;
  23. use File::Basename;
  24. use File::Temp qw/ tempdir /;
  25. my @conf = (
  26. { type => "blur",
  27. trans => [qw/-est_translations/],
  28. blur_fwhm => 16,
  29. steps => [qw/8 8 8/],
  30. tolerance => 0.01,
  31. simplex => 32 },
  32. { type => "blur",
  33. trans => undef,
  34. blur_fwhm => 8,
  35. steps => [qw/4 4 4/],
  36. tolerance => 0.004,
  37. simplex => 16 },
  38. { type => "blur",
  39. trans => undef,
  40. blur_fwhm => 4,
  41. steps => [qw/4 4 4/],
  42. tolerance => 0.004,
  43. simplex => 8 },
  44. { type => "dxyz",
  45. trans => undef,
  46. blur_fwhm => 8,
  47. steps => [qw/4 4 4/],
  48. tolerance => 0.004,
  49. simplex => 4 },
  50. { type => "dxyz",
  51. trans => undef,
  52. blur_fwhm => 4,
  53. steps => [qw/4 4 4/],
  54. tolerance => 0.004,
  55. simplex => 2 }
  56. );
  57. my($Help, $Usage, $me);
  58. my(@opt_table, %opt, $source, $target, $outxfm, $outfile, @args, $tmpdir);
  59. $me = &basename($0);
  60. %opt = (
  61. 'verbose' => 1,
  62. 'clobber' => 0,
  63. 'fake' => 0,
  64. 'init_xfm' => undef,
  65. 'source_mask' => undef,
  66. 'target_mask' => undef,
  67. 'lsqtype' => "-lsq9",
  68. 'work_dir' => undef,
  69. 'sec_source' => undef,
  70. 'sec_target' => undef,
  71. 'cost_function' => '-xcorr',
  72. 'noshear' => 0,
  73. 'noscale' => 0,
  74. 'norot' => 0,
  75. 'noshift' => 0,
  76. 'close' => 0,
  77. );
  78. $Help = <<HELP;
  79. | $me does hierachial linear fitting between two files.
  80. | you will have to edit the script itself to modify the
  81. | fitting levels themselves
  82. |
  83. | Problems or comments should be sent to: claude\@bic.mni.mcgill.ca
  84. HELP
  85. $Usage = "Usage: $me [options] source.mnc target.mnc output.xfm [output.mnc]\n".
  86. " $me -help to list options\n\n";
  87. @opt_table = (
  88. ["-verbose", "boolean", 0, \$opt{verbose},
  89. "be verbose" ],
  90. ["-clobber", "boolean", 0, \$opt{clobber},
  91. "clobber existing check files" ],
  92. ["-fake", "boolean", 0, \$opt{fake},
  93. "do a dry run, (echo cmds only)" ],
  94. ["-init_xfm", "string", 1, \$opt{init_xfm},
  95. "initial transformation (default identity)" ],
  96. ["-source_mask", "string", 1, \$opt{source_mask},
  97. "source mask to use during fitting" ],
  98. ["-target_mask", "string", 1, \$opt{target_mask},
  99. "target mask to use during fitting" ],
  100. ["-lsq9", "const", "-lsq9", \$opt{lsqtype},
  101. "use 9-parameter transformation (default)" ],
  102. ["-lsq12", "const", "-lsq12", \$opt{lsqtype},
  103. "use 12-parameter transformation (default -lsq9)" ],
  104. ["-lsq6", "const", "-lsq6", \$opt{lsqtype},
  105. "use 6-parameter transformation" ],
  106. ["-lsq7", "const", "-lsq7", \$opt{lsqtype},
  107. "use 7-parameter transformation" ],
  108. ["-quaternions","const",'-quaternions', \$opt{lsqtype},
  109. "use quaternion transformation" ],
  110. ["-mi","const",'-mi', \$opt{cost_function},
  111. "Mutual information cost function" ],
  112. ["-xcorr","const",'-xcorr', \$opt{cost_function},
  113. "Cross-correlation cost function" ],
  114. ["-work_dir", "string", 1, \$opt{work_dir},
  115. "Directory to keep blurred files" ],
  116. ["-sec_source", "string", 1, \$opt{sec_source},
  117. "secondaty source" ],
  118. ["-sec_target", "string", 1, \$opt{sec_target},
  119. "secondary target" ],
  120. ["-noshear", "boolean", 1, \$opt{noshear},
  121. "lsq12 w/o shear" ],
  122. ["-noscale", "boolean", 1, \$opt{noscale},
  123. "lsq12 w/o scale" ],
  124. ["-norot", "boolean", 1, \$opt{norot},
  125. "lsq12 w/o rotations" ],
  126. ["-noshift", "boolean", 1, \$opt{noshift},
  127. "lsq12 w/o shifts" ],
  128. ["-close", "boolean", 1, \$opt{close},
  129. "Samples are close" ],
  130. );
  131. delete $ENV{MINC_COMPRESS} if $ENV{MINC_COMPRESS};
  132. # Check arguments
  133. &Getopt::Tabular::SetHelp($Help, $Usage);
  134. &GetOptions (\@opt_table, \@ARGV) || exit 1;
  135. die $Usage if(! ($#ARGV == 2 || $#ARGV == 3));
  136. $source = shift(@ARGV);
  137. $target = shift(@ARGV);
  138. $outxfm = shift(@ARGV);
  139. $outfile = (defined($ARGV[0])) ? shift(@ARGV) : undef;
  140. # check for files
  141. die "$me: Couldn't find input file: $source\n\n" if (!-e $source);
  142. die "$me: Couldn't find input file: $target\n\n" if (!-e $target);
  143. if(-e $outxfm && !$opt{clobber}){
  144. die "$me: $outxfm exists, -clobber to overwrite\n\n";
  145. }
  146. if(defined($outfile) && -e $outfile && !$opt{clobber}){
  147. die "$me: $outfile exists, -clobber to overwrite\n\n";
  148. }
  149. # make tmpdir
  150. $tmpdir = &tempdir( "$me-XXXXXXXX", TMPDIR => 1, CLEANUP => 1 );
  151. $opt{work_dir}=$tmpdir unless $opt{work_dir};
  152. # set up filename base
  153. my($i, $s_base, $t_base, $tmp_xfm, $tmp_source, $tmp_target, $prev_xfm,
  154. $tmp_sec_source, $tmp_sec_target);
  155. $s_base = &basename($source);
  156. $s_base =~ s/\.mnc(.gz)?$//;
  157. $s_base = 's_'.$s_base;
  158. $t_base = &basename($target);
  159. $t_base =~ s/\.mnc(.gz)?$//;
  160. $t_base = 't_'.$t_base;
  161. # Mask the source and target once before blurring. Both masks must exist.
  162. my $source_masked = $source;
  163. my $target_masked = $target;
  164. # initial transformation supplied by the user, applied to both the
  165. # source image and its mask.
  166. #if( defined $opt{init_xfm} ) {
  167. # my $source_resampled = "${tmpdir}/${s_base}_resampled.mnc";
  168. # &do_cmd( 'mincresample',
  169. # '-clobber',
  170. # '-like', $target_masked,
  171. # '-transform', $opt{init_xfm},
  172. # $source_masked, $source_resampled );
  173. # $source_masked = $source_resampled;
  174. # #apply it to the mask, if it's defined
  175. # if(defined($opt{source_mask}))
  176. # {
  177. # my $mask_resampled = "${tmpdir}/${s_base}_mask_resampled.mnc";
  178. # &do_cmd( 'mincresample', '-clobber', '-like', $target_masked,
  179. # '-nearest_neighbour', '-transform', $opt{init_xfm},
  180. # $opt{source_mask}, $mask_resampled );
  181. # $opt{source_mask} = $mask_resampled;
  182. # }
  183. #}
  184. $prev_xfm = undef;
  185. # a fitting we shall go...
  186. for ($i=0; $i<=$#conf; $i++){
  187. # set up intermediate files
  188. $tmp_xfm = "$tmpdir/$s_base\_$i.xfm";
  189. $tmp_source = "$opt{work_dir}/$s_base\_$conf[$i]{blur_fwhm}";
  190. $tmp_target = "$opt{work_dir}/$t_base\_$conf[$i]{blur_fwhm}";
  191. $tmp_source = "$opt{work_dir}/$s_base\_$conf[$i]{blur_fwhm}";
  192. $tmp_target = "$opt{work_dir}/$t_base\_$conf[$i]{blur_fwhm}";
  193. if($opt{close})
  194. {
  195. next if($conf[$i]{blur_fwhm}>8);
  196. $conf[$i]{simplex}=1;
  197. $conf[$i]{steps}=[qw/2 2 2/];
  198. }
  199. print STDOUT "-+-------------------------[$i]-------------------------\n".
  200. " | steps: @{$conf[$i]{steps}}\n".
  201. " | blur_fwhm: $conf[$i]{blur_fwhm}\n".
  202. " | simplex: $conf[$i]{simplex}\n".
  203. " | source: $tmp_source\_$conf[$i]{type}.mnc\n".
  204. " | target: $tmp_target\_$conf[$i]{type}.mnc\n".
  205. " | xfm: $tmp_xfm\n".
  206. "-+-----------------------------------------------------\n".
  207. "\n";
  208. # blur the masked source and target images
  209. my @grad_opt = ();
  210. push( @grad_opt, '-gradient' ) if( $conf[$i]{type} eq "dxyz" );
  211. if($conf[$i]{blur_fwhm}>0) # actually this should be 1
  212. {
  213. if(!-e "$tmp_source\_$conf[$i]{type}.mnc") {
  214. &do_cmd('mincblur', '-clobber', '-no_apodize', '-fwhm', $conf[$i]{blur_fwhm},
  215. @grad_opt, $source_masked, $tmp_source);
  216. }
  217. if(!-e "$tmp_target\_$conf[$i]{type}.mnc") {
  218. &do_cmd('mincblur', '-clobber', '-no_apodize', '-fwhm', $conf[$i]{blur_fwhm},
  219. @grad_opt, $target_masked, $tmp_target);
  220. }
  221. if(defined($opt{sec_source}) && defined($opt{sec_target}) )
  222. {
  223. $tmp_sec_source = "$opt{work_dir}/sec_$s_base\_$conf[$i]{blur_fwhm}";
  224. $tmp_sec_target = "$opt{work_dir}/sec_$t_base\_$conf[$i]{blur_fwhm}";
  225. if(! -e "$tmp_sec_source\_$conf[$i]{type}.mnc")
  226. {
  227. &do_cmd('mincblur', '-clobber', '-no_apodize', '-fwhm',
  228. $conf[$i]{blur_fwhm},
  229. @grad_opt, $opt{sec_source}, $tmp_sec_source);
  230. }
  231. if(! -e "$tmp_sec_target\_$conf[$i]{type}.mnc")
  232. {
  233. &do_cmd('mincblur', '-clobber', '-no_apodize', '-fwhm',
  234. $conf[$i]{blur_fwhm},
  235. @grad_opt, $opt{sec_target}, $tmp_sec_target);
  236. }
  237. $tmp_sec_source="$tmp_sec_source\_$conf[$i]{type}.mnc";
  238. $tmp_sec_target="$tmp_sec_target\_$conf[$i]{type}.mnc";
  239. }
  240. } else { # noop
  241. if(!-e "$tmp_source\_$conf[$i]{type}.mnc") {
  242. do_cmd('ln','-s',$source_masked,"$tmp_source\_$conf[$i]{type}.mnc");
  243. }
  244. if(!-e "$tmp_target\_$conf[$i]{type}.mnc") {
  245. &do_cmd('ln', '-s', $target_masked, "$tmp_target\_$conf[$i]{type}.mnc");
  246. }
  247. if(defined($opt{sec_source}) && defined($opt{sec_target}) )
  248. {
  249. $tmp_sec_source =$opt{sec_source};
  250. $tmp_sec_target =$opt{sec_target};
  251. }
  252. }
  253. # set up registration
  254. @args = ('minctracc', '-clobber', $opt{lsqtype},
  255. '-step', @{$conf[$i]{steps}}, '-simplex', $conf[$i]{simplex},
  256. '-tol', $conf[$i]{tolerance});
  257. # Initial transformation will be computed from the from Principal axis
  258. # transformation (PAT).
  259. push(@args, @{$conf[$i]{trans}}) if( defined $conf[$i]{trans} && !$opt{init_xfm} );
  260. # Current transformation at this step
  261. if( defined $prev_xfm )
  262. {
  263. push(@args, '-transformation', $prev_xfm ) ;
  264. } elsif( defined $opt{init_xfm} ) {
  265. push(@args, '-transformation', $opt{init_xfm} ) ;
  266. #push(@args, '-est_center' ) ;
  267. } elsif($opt{close}) {
  268. push(@args, '-identity') ;
  269. }
  270. # masks (even if the blurred image is masked, it's still preferable
  271. # to use the mask in minctracc)
  272. push(@args, '-source_mask', $opt{source_mask} ) if defined($opt{source_mask});
  273. push(@args, '-model_mask', $opt{target_mask}) if defined($opt{target_mask});
  274. if($tmp_sec_source && $tmp_sec_target )
  275. {
  276. push(@args, $opt{cost_function},
  277. "$tmp_source\_$conf[$i]{type}.mnc",
  278. "$tmp_target\_$conf[$i]{type}.mnc");
  279. push(@args, '-feature_vol',$tmp_sec_source,$tmp_sec_target,'xcorr',1.0);
  280. } else {
  281. push(@args, $opt{cost_function},"$tmp_source\_$conf[$i]{type}.mnc",
  282. "$tmp_target\_$conf[$i]{type}.mnc");
  283. }
  284. push @args,'-w_shear',0,0,0 if $opt{noshear};
  285. push @args,'-w_scales',0,0,0 if $opt{noscale};
  286. push @args,'-w_translations',0,0,0 if $opt{noshift};
  287. push @args,'-w_rotations',0,0,0 if $opt{norot};
  288. # add files and run registration
  289. push(@args, $tmp_xfm);
  290. &do_cmd(@args);
  291. $prev_xfm = $tmp_xfm;
  292. }
  293. # Concatenate transformations if an initial transformation was given.
  294. #if( defined $opt{init_xfm} ) {
  295. # &do_cmd( 'xfmconcat', $prev_xfm, $opt{init_xfm}, $outxfm );
  296. #} else {
  297. do_cmd( 'mv', '-f', $prev_xfm, $outxfm );
  298. #}
  299. # resample if required
  300. if(defined($outfile)){
  301. print STDOUT "-+- creating $outfile using $outxfm\n".
  302. &do_cmd( 'mincresample', '-clobber', '-like', $target,
  303. '-transformation', $outxfm, $source, $outfile );
  304. }
  305. sub do_cmd {
  306. print STDOUT "@_\n" if $opt{verbose};
  307. if(!$opt{fake}){
  308. system(@_) == 0 or die;
  309. }
  310. }