#! /usr/bin/perl -w # ------------------------------ MNI Header ---------------------------------- #@NAME : mritoself #@INPUT : #@OUTPUT : #@RETURNS : #@DESCRIPTION: register MRI data within modality, within subject #@METHOD : #@GLOBALS : #@CALLS : #@CREATED : originally: April 1995, Greg Ward # rewrite #1: July 1995, GW # rewrite #2: December 1995-February 1996, GW #@MODIFIED : April-May 1996, Louis Collins: modifications to make an # official mritoself for ICBM and AI data #@VERSION : $Id: mritoself.in,v 1.4 2004/02/12 05:55:18 rotor Exp $ #@COMMENTS : # possible additions: # figure out the thresholding stuff # add the dilation code back in (add to findprogram list, and # change default from 0 to 1 or 2. # figure out how to have automatically defined step sizes that # do not align with either volume voxel sizes. #----------------------------------------------------------------------------- # usage: mritoself [options] source_vol target_vol transform use FindBin; use lib "$FindBin::Bin/../lib/mni_autoreg"; use Startup; use JobControl qw(AddProgramOptions Spawn); use Getopt::Tabular; # import only GetOptions require "file_utilities.pl"; require "path_utilities.pl"; require "minc_utilities.pl"; require "numeric_utilities.pl"; require "volume_heuristics.pl"; &Startup; &Initialize; # variables local to the main program my ($source_cropped, $source_crop, $source_subsample); my ($target_cropped, $target_crop, $target_subsample); my ($source_mask, $target_mask); my ($source_blur, $target_blur, $fit_source, $fit_target); my ($source_threshold, $target_threshold); # Quit now if final xfm already exists (and -clobber wasn't set) if (-e $FinalXfm && !$Clobber) { warn "$FinalXfm already exists (use -clobber to overwrite it)\n"; exit 0; } &SelfAnnounce () unless -t "STDOUT"; # Get the working filenames (uncompressed), and their base names ($Source, $Target) = &uncompress ($TmpDir, $Source, $Target); $SourceBase = (&split_path ($Source, "last"))[1]; $TargetBase = (&split_path ($Target, "last"))[1]; # Possibly crop/subsample both volumes ($source_cropped, $source_crop, $source_subsample)= &Crop ($SourceBase, $Source, "crop", $GuessCrop, $GuessSubsample, \@Subsample, \@Crop); ($target_cropped, $target_crop, $target_subsample) = &Crop ($TargetBase, $Target, "crop", $GuessCrop, $GuessSubsample, \@Subsample, \@Crop); # Possible create masks for each volume (possibly different, depending # on the files named in @TalXfm) ($source_mask, $target_mask) = &MakeMasks ($source_cropped, $target_cropped, \@TalXfm, $Mask, $DilateMask); # Possibly create blurred versions of each ($source_blur, $target_blur) = $Blur ? &Blur ($source_cropped, $target_cropped, $Blur) : ($source_cropped, $target_cropped); ($fit_source, $fit_target) = ($source_blur, $target_blur); # Compute threshold values (if necessary). These are computed as real # voxel values here, just the way minctracc likes 'em if ($Threshold) { ($source_threshold, $target_threshold) = &GetThresholds ($fit_source, $fit_target); &verbose ("threshold on source volume = $source_threshold\n"); &verbose ("threshold on target volume = $target_threshold\n"); } # Perform the fit. &Fit ($fit_source, $fit_target, $source_threshold, $target_threshold, $source_mask, $target_mask, $source_subsample, $target_subsample, $InputXfm, $FinalXfm); &Resample ($Source, $Resample, $FinalXfm, $target_cropped) if ($Resample); &Shutdown (1); sub verbose { print @_ if $Verbose; } sub set_fit_params { my ($arg, $arglist) = @_; if ($arg eq "-close") { $Simplex = 3; $InitialGuess = "-identity -est_center"; } elsif ($arg eq "-far") { $Simplex = 10; $InitialGuess = "-est_center -est_translations"; } else { die "Bad fit param option: $arg\n"; } } sub Initialize { $Verbose = 1; $Execute = 1; $Clobber = 0; $Debug = 0; $KeepTmp = 0; &JobControl::SetOptions ("ErrorAction", "fatal"); # $Fitter = "tracc"; # N.B. this is ignored... $GuessSubsample = 0; # don't subsample @Subsample = (); $GuessCrop = 1; @Crop = (0); # so heuristic cropping is the default $Blur = 0; # don't blur $Mask = "none"; # don't mask $MaskLabel = "mask"; # which mask volume to use $DilateMask = 0; # number of dilation steps $UseGradient = 1; $Threshold = 1; # $AutoThreshold = 1; # only has meaning if $Threshold == 1 @Thresholds = (); # only has meaning if $Threshold == 1 and # !$AutoThreshold $Model = "average_305"; $ModelDir = "$FindBin::Bin/../share/mni_autoreg"; $Simplex = 3; $Groups = 256; $InvertOutputTransform = 0; $InvertInputTransform = 0; &set_fit_params ('-close'); my $usage = <= 1 || $Thresholds[1] < 0 || $Thresholds[1] >= 1)) { warn "$ProgramName: warning: " . "thresholds should be between 0 and 1 (fraction of volume range)\n"; } # Turn off GuessSubsample/GuessCrop if any subsample/crop # array other than (0) is specified -- this is what lets # us override guessing by simply specifying "-subsample ..." # on the command line. $GuessSubsample = 0 unless (@Subsample == 1 && $Subsample[0] == 0); $GuessCrop = 0 unless (@Crop == 1 && $Crop[0] == 0); # Look for required programs my @programs = qw(autocrop xfminvert mincinfo volume_cog mincresample mincblur minctracc); push (@programs, 'dilate_volume') if $DilateMask; push (@programs, 'mincstats') if $AutoThreshold; JobControl::FindPrograms (\@programs) || exit 1; # Add options for various programs according to $Debug, $Verbose, # $Clobber flags AddProgramOptions ([qw(mincblur minctracc mincresample autocrop)], ["-quiet"]) unless $Verbose; AddProgramOptions ([qw(mincblur minctracc mincresample autocrop)], ["-clobber"]) if $Clobber; AddProgramOptions ([qw(mincblur minctracc)], ["-debug"]) if $Debug; ($Source, $Target, $FinalXfm) = @argv; &check_output_dirs ($TmpDir) || exit 1; } sub Blur { my ($source, $target, $fwhm) = @_; my (@opts, $source_blur, $target_blur); my ($source_output, $target_output); $source_blur = "${TmpDir}/${SourceBase}_${fwhm}"; $target_blur = "${TmpDir}/${TargetBase}_${fwhm}"; if ($UseGradient) { @opts = ('-gradient', '-fwhm', $fwhm); $source_output = "${source_blur}_dxyz.mnc"; $target_output = "${target_blur}_dxyz.mnc"; unlink ("${source_blur}_blur.mnc", "${target_blur}_blur.mnc"); } else { @opts = ('-fwhm', $fwhm); $source_output = "${source_blur}_blur.mnc"; $target_output = "${target_blur}_blur.mnc"; } &Spawn (['mincblur', $source, $source_blur, @opts]) unless (-e $source_output && !$Clobber); &Spawn (['mincblur', $target, $target_blur, @opts]) unless (-e $target_output && !$Clobber); ($source_output, $target_output); } sub Crop { my ($base, $input, $label, $guess_crop, $guess_subsample, $subsample, $crop) = @_; my ($cropped, @opts); my (@start, @step, @length); my (@crop, @subsample); &volume_params ($input, \@start, \@step, \@length); @crop = $guess_crop ? &GuessCrop (\@step, \@length) : @$crop; @subsample = $guess_subsample ? &GuessSubsample (\@step) : @$subsample; @subsample = @step unless @subsample; $cropped = "${TmpDir}/${base}_${label}.mnc"; return ($cropped, \@crop, \@subsample) if (-e $cropped); @opts = (); push (@opts, '-step', @subsample) if @subsample; push (@opts, '-extend', @crop) if @crop; push (@opts, '-byte') if $Objective eq "-mi"; &Spawn (['autocrop', $input, $cropped, @opts]) unless (-e $cropped && !$Clobber); ($cropped, \@crop, \@subsample); } sub make_mask { my ($modelmask, $modelxfm, $target, $mask, $dilation) = @_; my ($tempmask); $tempmask = ($dilation) ? ($mask . ".undilated") : ($mask); # $tempxfm = "${TmpDir}/" . (&SplitPath($modelxfm))[1] . "_inv.xfm"; # &Spawn (['xfminvert', $modelxfm, $tempxfm]) # unless -e $tempxfm && !$Clobber; &Spawn (['mincresample', $modelmask, $tempmask, '-transform', $modelxfm, '-invert_transform', '-like', $target, '-nearest_neighbour']) unless -e $tempmask && !$Clobber; if ($dilation) { &Spawn (['dilate_volume', $tempmask, $mask, 255, 26, $dilation]) unless -e $mask && !$Clobber; } } sub MakeMasks { my ($source, $target, $tal_xfm, $which, $dilation) = @_; my ($model_mask, $mask); my ($source_mask, $target_mask) = ("", ""); $model_mask = "${ModelDir}/${Model}_${MaskLabel}.mnc"; if ($which eq "both" || $which eq "source") { $source_mask = "${TmpDir}/${SourceBase}_${MaskLabel}.mnc"; &make_mask($model_mask, $tal_xfm->[0], $source, $source_mask, $dilation); } if ($which eq "both" || $which eq "target") { $target_mask = "${TmpDir}/${TargetBase}_${MaskLabel}.mnc"; if (defined $tal_xfm->[0] && $tal_xfm->[0] eq $tal_xfm->[1] && -e $source_mask) { link ($source_mask, $target_mask); } else { &make_mask ($model_mask, $tal_xfm->[1], $target, $target_mask, $dilation); } } ($source_mask, $target_mask); } sub apply_mask { my ($input, $mask, $output) = @_; &Spawn (['mincmask', $input, $mask, $output]) unless (-e $output && !$Clobber); } sub ApplyMasks { my ($source, $target, $source_mask, $target_mask) = @_; my ($source_masked, $target_masked) = ($source, $target); if ($source_mask) { $source_masked = "${TmpDir}/${SourceBase}_masked.mnc"; &apply_mask ($source, $source_mask, $source_masked); } if ($target_mask) { $target_masked = "${TmpDir}/${TargetBase}_masked.mnc"; &apply_mask ($target, $target_mask, $target_masked); } ($source_masked, $target_masked); } sub GetThresholds { my ($source, $target) = @_; my ($source_threshold, $target_threshold); my ($source_min, $source_max, $target_min, $target_max); if ($Objective eq "-mi") { warn "warning: mutual information used as objective function -- " . "target threshold forced to volume min\n"; $target_threshold = &volume_min ($target); } if ($AutoThreshold) { $source_threshold = round (&auto_threshold ($source)); if ($Objective ne "-mi") { $target_threshold = round (&auto_threshold ($target)); } } elsif (@Thresholds) { Fatal ("\@Thresholds must have exactly two elements") unless (@Thresholds == 2); ($source_min, $source_max) = &volume_minmax ($source); $source_threshold = round (&volume_threshold ($source_min, $source_max, $Thresholds[0])); if ($Objective ne "-mi") { ($target_min, $target_max) = &volume_minmax ($target); $target_threshold = round (&volume_threshold ($target_min, $target_max, $Thresholds[1])); } } ($source_threshold, $target_threshold); } sub Fit { my ($source, $target, $source_threshold, $target_threshold, $source_mask, $target_mask, $source_steps, $target_steps, $in_xfm, $out_xfm) = @_; my (@inxfm_opts, @threshold_opts, @mask_opts); # my ($steps, $mask); # my ($source_min, $source_max, $target_min, $target_max); # my ($threshold); # Determine what to do about the initial guess. We either estimate # translations only, *or* just use the user-supplied xfm (after tacking # on "-est_center -transformation"). if ($in_xfm) { if ($InvertInputTransform) { my $temp_in_xfm = $TmpDir . "/" . (&split_path ($in_xfm))[1] . "_inv.xfm"; &Spawn (['xfminvert', $in_xfm, $temp_in_xfm]); @inxfm_opts = ('-est_center', '-transformation', $temp_in_xfm); } else { @inxfm_opts = ('-est_center', '-transformation', $in_xfm); } } else { @inxfm_opts = split (" ", $InitialGuess); } # Use the volume thresholds to determine some more command-line args. if (defined $source_threshold && defined $target_threshold) { @threshold_opts = ('-threshold', $source_threshold, $target_threshold); } else { @threshold_opts = (); } # Add the mask volumes to the command line if necessary or applicable @mask_opts = (); push (@mask_opts, '-source_mask', $source_mask) if $source_mask; push (@mask_opts, '-model_mask', $target_mask) if $target_mask; # Now the real fit, using minctracc (xcorr, vr, or mi) my $outbase = $TmpDir . "/" . (&split_path ($out_xfm))[1]; my $temp1_xfm = $outbase . "_tmp1.xfm"; if ($VeryClose) # skip first fit -- use user-supplied { # input transformation $temp1_xfm = $in_xfm; } else # do first fit as usual { unless (-e $temp1_xfm && !$Clobber) { &verbose ("Starting first fit...\n") if $verbose; my @fitsteps = (7.3, 7.3, 7.3); @step_opts = ('-step', @fitsteps); @objective_opts = ($Objective); push (@objective_opts, '-groups', $Groups) if ($Objective =~ /^-(vr|mi)$/); @simplex_opts = ('-simplex', $Simplex); &Spawn (['minctracc', $source, $target, $temp1_xfm, @inxfm_opts, $FitType, @objective_opts, @threshold_opts, @mask_opts, @step_opts, @simplex_opts]) } } $temp2_xfm = ($InvertOutputTransform) ? ($outbase . "_tmp2.xfm") : ($out_xfm); unless (-e $temp2_xfm && !$Clobber) { &verbose ("Starting second fit...\n") if $verbose; my @inxfm_tmp1_opts = ('-est_center', '-transformation', $temp1_xfm); @fitsteps = (4.3, 4.3, 4.3); @step_opts = ('-step', @fitsteps); @objective_opts = ($Objective); push (@objective_opts, '-groups', $Groups) if ($Objective =~ /^-(vr|mi)$/); @simplex_opts = ('-simplex', 1.5); &Spawn (['minctracc', $source, $target, $temp2_xfm, @inxfm_tmp1_opts, $FitType, @objective_opts, @threshold_opts, @mask_opts, @step_opts, @simplex_opts]) unless (-e $temp2_xfm && !$Clobber); } if ($InvertOutputTransform) { &Spawn (['xfminvert', $temp2_xfm, $out_xfm]) unless (-e $out_xfm && !$Clobber); } } sub Resample { my ($input, $output, $xfm, $like) = @_; if (-e $output && !$Clobber) { &verbose ("resampled volume \"$output\" already exists"); } else { &Spawn(['mincresample', $input, $output, '-byte', '-transform', $xfm, '-like', $like]); } }