#! /bin/tcsh -f

#
# bbregister
#
# Original Author: Doug Greve
# CVS Revision Info:
#    $Author: greve $
#    $Date: 2009/03/20 20:16:27 $
#    $Revision: 1.14.2.5 $
#
# Copyright (C) 2002-2007,
# The General Hospital Corporation (Boston, MA).
# All rights reserved.
#
# Distribution, usage and copying of this software is covered under the
# terms found in the License Agreement file named 'COPYING' found in the
# FreeSurfer source code root directory, and duplicated here:
# https://surfer.nmr.mgh.harvard.edu/fswiki/FreeSurferOpenSourceLicense
#
# General inquiries: freesurfer@nmr.mgh.harvard.edu
# Bug reports: analysis-bugs@nmr.mgh.harvard.edu
#

#
set VERSION = '$Id: bbregister,v 1.14.2.5 2009/03/20 20:16:27 greve Exp $';
set inputargs = ($argv);

set subject = ();
set movvol = ();
set intvol = (); # intermediate volume
set frame = ();
set midframe = 0;
set outreg = ();
set InitFSL = 0;
set InitSPM = 0;
set InitHeader = 0;
set InitReg = ();
set VSM = ();
set Brute1Max = 4;
set Brute1Delta = 4;
set SubSamp1 = 100;

set LHOnly = 0;
set RHOnly = 0;
set Slope1 = 0.5;
set Slope2 = 0.5;
set Offset2 = 0;
set Contrast = ()
set Tol = 1e-8;

set GMProjFrac = 0.5;
set WMProjAbs = 2;
set nSubSamp = 1;
set Interp = trilinear
set UseEPIMask = 0;

set RMSFile = ();
set templateout = ();
set OutVol = ();
set fsvol  = brainmask;
set surfcost = ();
set surfcon  = ();

set RandInitMax = ();

set debug = 0;
set tmpdir = ();
set cleanup = 1;
set PrintHelp = 0;
set nolog = 0;

if($#argv == 0) goto usage_exit;
set n = `echo $argv | egrep -e -version | wc -l`
if($n != 0) then
  echo $VERSION
  exit 0;
endif
set n = `echo $argv | egrep -e -help | wc -l`
if($n != 0) then
  set PrintHelp = 1;
  goto usage_exit;
endif

goto parse_args;
parse_args_return:
goto check_params;
check_params_return:

set StartTime = `date`;

set outdir = `dirname $outreg`;
if($#tmpdir == 0) set tmpdir = $outdir/tmp.bbregister.$$
mkdir -p $tmpdir

if(! $nolog) then
  set LF = $outreg.log
  if(-e $LF) mv $LF $LF.old
else
  set LF = /dev/null
endif

echo "Log file is $LF"

echo "Logfile for bbregister" >> $LF
date |& tee -a $LF
echo "setenv SUBJECTS_DIR $SUBJECTS_DIR" |& tee -a $LF
echo "cd `pwd`" |& tee -a $LF
echo $0 |& tee -a $LF
echo $VERSION |& tee -a $LF
uname -a |& tee -a $LF
echo "FREESURFER_HOME $FREESURFER_HOME" |& tee -a $LF

# Create template
if($#templateout) then
  set template = $templateout
else
  set template = $tmpdir/template.nii
endif
set cmd = (mri_convert $movvol $template)
if($#frame != 0) set cmd = ($cmd --frame $cmd)
if($midframe) set cmd = ($cmd --mid-frame $cmd)
echo $cmd | tee -a $LF
$cmd |& tee -a $LF
if($status) exit 1;

if($#InitReg == 0) then
  set regvol = $template
  set InitReg = $tmpdir/reg.init.dat
  if($#intvol) then
    set regvol = $intvol
    set InitReg = $tmpdir/reg.intermediate.dat
  endif
  if($InitFSL) then
    set cmd = (fslregister --s $subject --mov $regvol --reg $InitReg \
      --niters 1 --maxangle 90 --nobetmov --tmp $tmpdir/fslregister)
    echo $cmd | tee -a $LF
    $cmd |& tee -a $LF
    if($status) exit 1;
  endif
  if($InitSPM) then
    set cmd = (spmregister --s $subject --mov $regvol --reg $InitReg \
      --tmp $tmpdir/fslregister)
    echo $cmd | tee -a $LF
    $cmd |& tee -a $LF
    if($status) exit 1;
  endif
  if($InitHeader) then
    set cmd = (tkregister2_cmdl --s $subject --mov $regvol \
      --regheader --reg $InitReg --noedit)
    echo $cmd | tee -a $LF
    $cmd |& tee -a $LF
    if($status) exit 1;
  endif
  if($#intvol) then
    set InitReg0 = $tmpdir/reg.init.dat
    set cmd = (tkregister2_cmdl --s $subject --mov $template \
      --int $intvol $InitReg  --noedit --reg $InitReg0)
    echo $cmd | tee -a $LF
    $cmd |& tee -a $LF
    if($status) exit 1;
    set InitReg = $InitReg0;
  endif
endif

# Pass 1
set Pass1Reg = $tmpdir/bbr.pass1.dat
set cmd = (mri_segreg --mov $template --init-reg $InitReg --out-reg $Pass1Reg\
  --subsamp-brute $SubSamp1 --subsamp $SubSamp1 --tol 1e-4 --tol1d 1e-4 \
  --brute -$Brute1Max $Brute1Max $Brute1Delta );
if("$Contrast" == "-1") set cmd = ($cmd --wm-gt-gm $Slope1);
if("$Contrast" == "+1") set cmd = ($cmd --gm-gt-wm $Slope1);
if($LHOnly) set cmd = ($cmd --lh-only);
if($RHOnly) set cmd = ($cmd --rh-only);
if($UseEPIMask) set cmd = ($cmd --mask);
if($#VSM) set cmd = ($cmd --mask --vsm $VSM);
if($#RandInitMax) then
  # Only for testing
  set cmd = ($cmd --trans-rand $RandInitMax --rot-rand $RandInitMax);
endif
echo $cmd | tee -a $LF
$cmd |& tee -a $LF
if($status) exit 1;

# Pass 2
set MinCostFile = $outreg.mincost; # This will be the final cost
set cmd = (mri_segreg --mov $template --init-reg $Pass1Reg \
  --out-reg $outreg --brute -0.1 0.1 0.1 --interp $Interp \
  --gm-proj-frac $GMProjFrac --wm-proj-abs $WMProjAbs \
  --c0 $Offset2 --mincost $MinCostFile)
if($#nSubSamp) set cmd = ($cmd --nsub $nSubSamp);
if($LHOnly) set cmd = ($cmd --lh-only);
if($RHOnly) set cmd = ($cmd --rh-only);
if($UseEPIMask) set cmd = ($cmd --mask);
if($#VSM) set cmd = ($cmd --mask --vsm $VSM);
if($#OutVol) set cmd = ($cmd --o $OutVol);
set cmd = ($cmd --tol $Tol --tol1d $Tol)
if("$Contrast" == "-1") set cmd = ($cmd --wm-gt-gm $Slope2);
if("$Contrast" == "+1") set cmd = ($cmd --gm-gt-wm $Slope2);
if($#surfcost)  set cmd = ($cmd --surf-cost $surfcost);
if($#surfcon)   set cmd = ($cmd --surf-con  $surfcon);

echo $cmd | tee -a $LF
$cmd |& tee -a $LF
if($status) exit 1;

# Compute RMS wrt the initial reg (however it was created)
if($#RMSFile) then
  set rmsdat = ();
  if(! $RHOnly) then
    set lhrms = $tmpdir/rh.rms
    set cmd = (mri_surf2surf --reg $InitReg --reg-diff $outreg \
     --sval-xyz white --rms $lhrms --s $subject --hemi lh)
    echo $cmd | tee -a $LF
    $cmd |& tee -a $LF
    if($status) exit 1;
    set rmsdat = (`cat $lhrms`);
  endif
  if(! $LHOnly) then
    set rhrms = $tmpdir/rh.rms
    set cmd = (mri_surf2surf --reg $InitReg --reg-diff $outreg \
     --sval-xyz white --rms $rhrms --s $subject --hemi rh)
    echo $cmd | tee -a $LF
    $cmd |& tee -a $LF
    if($status) exit 1;
    set rmsdat = ($rmsdat `cat $rhrms`);
  endif
  echo $rmsdat > $RMSFile
  echo Final RMS $rmsdat | tee -a $LF
endif

# Cleanup
if($cleanup) then
  echo "Cleaning up" |& tee -a $LF
  rm -r $tmpdir
endif

echo " " |& tee -a $LF
echo "Started at $StartTime " |& tee -a $LF
echo "Ended   at `date`" |& tee -a $LF
echo " " |& tee -a $LF
echo "bbregister Done" |& tee -a $LF
echo " "

echo "To check results, run:"
if($#templateout) then
  echo "tkregister2 --mov $templateout --reg $outreg --surf"
else
  echo "tkregister2 --mov $movvol --reg $outreg --surf"
endif
echo " "


exit 0;
###############################################

############--------------##################
parse_args:
set cmdline = ($argv);
while( $#argv != 0 )

  set flag = $argv[1]; shift;

  switch($flag)

    case "--s":
      if ( $#argv < 1) goto arg1err;
      set subject = $argv[1]; shift;
      breaksw

    case "--mov":
      if ( $#argv < 1) goto arg1err;
      set movvol = $argv[1]; shift;
      if(! -e $movvol) then
        echo "ERROR: cannot find $movvol"
        exit 1;
      endif
      breaksw

    case "--int":
      if ( $#argv < 1) goto arg1err;
      set intvol = $argv[1]; shift;
      if(! -e $intvol) then
        echo "ERROR: cannot find $intvol"
        exit 1;
      endif
      breaksw

    case "--vsm":
      if ( $#argv < 1) goto arg1err;
      set VSM = $argv[1]; shift;
      if(! -e $VSM) then
        echo "ERROR: cannot find $VSM"
        exit 1;
      endif
      breaksw

    case "--reg":
      if ( $#argv < 1) goto arg1err;
      set outreg = $argv[1]; shift;
      breaksw

    case "--init-fsl":
      set InitFSL = 1;
      set InitSPM = 0;
      set InitHeader = 0;
      set InitReg = ();
      breaksw
 
    case "--init-spm":
      set InitFSL = 0;
      set InitSPM = 1;
      set InitHeader = 0;
      set InitReg = ();
      breaksw
 
    case "--regheader":
    case "--reg-header":
    case "--init-header":
      set InitFSL = 0;
      set InitSPM = 0;
      set InitHeader = 1;
      set InitReg = ();
      breaksw
 
    case "--init-reg":
      if ( $#argv < 1) goto arg1err;
      set InitReg = $argv[1]; shift;
      if(! -e $InitReg) then
        echo "ERROR: cannot find $InitReg"
        exit 1;
      endif
      set subject = `head -n 1 $InitReg`
      set InitFSL = 0;
      set InitSPM = 0;
      set InitHeader = 0;
      breaksw

    case "--slope1":
      if ( $#argv < 1) goto arg1err;
      set Slope1 = $argv[1]; shift;
      breaksw

    case "--slope2":
      if ( $#argv < 1) goto arg1err;
      set Slope2 = $argv[1]; shift;
      breaksw

    case "--offset2":
      if ( $#argv < 1) goto arg1err;
      set Offset2 = $argv[1]; shift;
      breaksw

    case "--bold":
    case "--dti":
    case "--T2":
    case "--t2":
      set Contrast = +1;
      breaksw

    case "--T1":
    case "--t1":
      set Contrast = -1;
      breaksw

    case "--s-from-reg":
      if ( $#argv < 1) goto arg1err;
      set tmpfile = $argv[1]; shift;
      set subject = `head -n 1 $tmpfile`;
      if($status) then
        echo "$subject"
        exit 1;
      endif
      breaksw

    case "--fsvol":
      if ( $#argv < 1) goto arg1err;
      set fsvol = $argv[1]; shift;
      breaksw

    case "--template-out":
      if ( $#argv < 1) goto arg1err;
      set templateout = $argv[1]; shift;
      breaksw

    case "--frame":
      if ( $#argv < 1) goto arg1err;
      set frame = $argv[1]; shift;
      breaksw

    case "--mid-frame":
      set midframe = 1;
      breaksw

    case "--lh-only":
      set LHOnly = 1;
      set RHOnly = 0;
      breaksw

    case "--rh-only":
      set LHOnly = 0;
      set RHOnly = 1;
      breaksw

    case "--o":
      if ( $#argv < 1) goto arg1err;
      set OutVol = $argv[1]; shift;
      breaksw

    case "--brute1max":
      if ( $#argv < 1) goto arg1err;
      set Brute1Max = $argv[1]; shift;
      breaksw

    case "--brute1delta":
      if ( $#argv < 1) goto arg1err;
      set Brute1Delta = $argv[1]; shift;
      breaksw

    case "--subsamp1":
      if ( $#argv < 1) goto arg1err;
      set SubSamp1 = $argv[1]; shift;
      breaksw

    case "--surf-cost":
      if ( $#argv < 1) goto arg1err;
      set surfcost = $argv[1]; shift;
      breaksw

    case "--surf-con":
      if ( $#argv < 1) goto arg1err;
      set surfcon = $argv[1]; shift;
      breaksw

    case "--tol":
      if ( $#argv < 1) goto arg1err;
      set Tol = $argv[1]; shift;
      breaksw

    case "--gm-proj-frac":
      if($#argv < 1) goto arg1err;
      set GMProjFrac = $argv[1]; shift;
      breaksw

    case "--wm-proj-abs":
      if($#argv < 1) goto arg1err;
      set WMProjAbs = $argv[1]; shift;
      breaksw

    case "--nsub":
    case "--subsamp":
      if($#argv < 1) goto arg1err;
      set nSubSamp = $argv[1]; shift;
      breaksw

    case "--rms":
      if($#argv < 1) goto arg1err;
      set RMSFile = $argv[1]; shift;
      breaksw

    case "--rand-init":
      if($#argv < 1) goto arg1err;
      set RandInitMax = $argv[1]; shift;
      breaksw

    case "--nearest":
      set Interp = nearest
      breaksw

    case "--trilin":
    case "--trilinear":
      set Interp = trilinear
      breaksw

    case "--epi-mask":
      set UseEPIMask = 1;
      breaksw

    case "--tmp":
    case "--tmpdir":
      if ( $#argv < 1) goto arg1err;
      set tmpdir = $argv[1]; shift;
      set cleanup = 0;
      breaksw

    case "--nocleanup":
      set cleanup = 0;
      breaksw

    case "--debug":
      set verbose = 1;
      set echo = 1;
      breaksw

    case "--nolog":
      set nolog = 1;
      breaksw

    default:
      echo ERROR: Flag $flag unrecognized.
      echo $cmdline
      exit 1
      breaksw
  endsw

end

goto parse_args_return;
############--------------##################

############--------------##################
check_params:

  if($#subject == 0) then
    echo "ERROR: must spec a subject id"
    exit 1;
  endif
  if(! -e $SUBJECTS_DIR/$subject) then
    echo "ERROR: cannot find $subject in $SUBJECTS_DIR"
    exit 1;
  endif

  if($#movvol == 0) then
    echo "ERROR: must spec an movput vol"
    exit 1;
  endif

  if($#Contrast == 0) then
    echo "ERROR: you must specify a contrast."
    echo " use --bold, --dti, --t2, or --t1,"
    echo " which ever is most appropriate"
    exit 1;
  endif

  if($#outreg == 0) then
    echo "ERROR: must spec an output reg file"
    exit 1;
  endif

  if($#frame && $midframe) then
    echo "ERROR: cannot --frame AND --mid-frame"
    exit 1;
  endif

  if(! $InitFSL && ! $InitSPM && ! $InitHeader && ! $#InitReg) then
    echo "ERROR: must spec an init method"
    exit 1;
  endif

goto check_params_return;
############--------------##################

############--------------##################
arg1err:
  echo "ERROR: flag $flag requires one argument"
  exit 1
############--------------##################

############--------------##################
arg2err:
  echo "ERROR: flag $flag requires two arguments"
  exit 1
############--------------##################

############--------------##################
usage_exit:
  echo ""
  echo "USAGE: bbregister"
  echo ""
  echo "Required Arguments:";
  echo "   --s subject  : FreeSurfer subject name"
  echo "   --mov volid  : input/movable volume"
  echo "   --reg register.dat : output registration file"
  echo ""
  echo "Initialization Arguments (one required)"
  echo ""
  echo "   --init-fsl : initialize the registration with FSL"
  echo "   --init-spm : initialize the registration with SPM"
  echo "   --init-header : initialize the registration based on header goemetry"
  echo "   --init-reg initregfile : explicitly pass registration"
  echo ""
  echo "Contrast Arguments (one required)"
  echo ""
  echo "   --t1 : assume t1 contrast, ie, WM brighter than GM"
  echo "   --t2 : assume t2 contrast, ie, GM brighter than WM (default)"
  echo "   --bold : same as --t2"
  echo "   --dti  : same as --t2"
  echo ""
  echo "Optional Arguments"
  echo ""
  echo "   --int intvol : intermediate volume"
  echo "   --mid-frame : reg to middle frame (not with --frame)"
  echo "   --frame frameno : reg to frameno (default 0=1st)"
  echo "   --template-out template : save template (good with --frame)"
  echo "   --o outvol : resample mov and save as outvol"
  echo "   --s-from-reg reg : get subject name from regfile"
  echo "   --rms rmsfile : RMS change in cortical surface position"
  echo ""
  echo "   --lh-only : only use left hemi"
  echo "   --rh-only : only use right hemi"
  echo "   --slope1 slope1 : cost slope for 1st stage (default is $Slope1)"
  echo "   --slope2  slope2 : cost slope for 2nd stage (default is $Slope2)"
  echo "   --offset2 offset2 : cost offset for 2nd stage (default is $Offset2)"
  echo "   --tol tol : 2nd stage tolerance (default is $Tol)"
  echo "   --rand-init randmax : randomly change input to 1st stage reg"
  echo "   --gm-proj-frac frac : 2nd stage, default is 0.5"
  echo "   --wm-proj-abs  dist : 2nd stage, default is 2mm"
  echo "   --subsamp nsub : 2nd stage vertex subsampling, default is 1"
  echo "   --nearest  : 2nd stage, use nearest neighbor interp (defalt is trilinear)"
  echo "   --epi-mask : mask out brain edge and B0 regions (1st and 2nd stages)"
  echo "   --brute1max max     : pass 1 search -max to +max (default $Brute1Max)"
  echo "   --brute1delta delta : pass 1 search -max to +max step delta (default $Brute1Delta)" 
  echo "   --subsamp1 nsubsamp  : pass 1 vertex subsampling (default $nSubSamp)"
  echo ""
  echo "   --surf-cost basename : saves final cost as basename.?h.mgh"
  echo "   --surf-con  basename : saves final contrast as basename.?h.mgh"
  echo ""
  echo "   --tmp tmpdir  : temporary dir (implies --nocleanup)"
  echo "   --nocleanup  : do not delete temporary files"
  echo "   --version : print version and exit"
  echo "   --help    : print help and exit"
  echo ""

  if($PrintHelp) \
  cat $0 | awk 'BEGIN{prt=0}{if(prt) print $0; if($1 == "BEGINHELP") prt = 1 }'

exit 1;

#---- Everything below here is printed out as part of help -----#
BEGINHELP

This program performs within-subject, cross-modal registration using a
boundary-based cost function. The registration is constrained to be 6
DOF (rigid). It is required that you have an anatomical scan of the
subject that has been analyzed in freesurfer.

INPUTS (all required)

--s subject 

Subject name as found in $SUBJECTS_DIR.

--mov movvol

"Moveable" volume. This is the template for the cross-modal volume. Eg,
for fMRI, it is the volume used for motion correction. 

--reg register.dat

Output FreeSurfer (tkregister-style) registration file (simple text).

INITIALIZATION METHODS (Choose One)

--init-fsl

Initialize using FSL FLIRT (requires that FSL be installed)

--init-spm

Initialize using SPM spm_coreg (requires that SPM and matlab be installed)

--init-header

Assume that the geometry information in the cross-modal and anatomical
are sufficient to get a close voxel-to-voxel registration. This
usually is only the case if they were acquired in the same session.

--init-reg register.init.dat

Supply an initial registration matrix.

CONTRAST (Choose One)

There are only two types of contrast that bbregister understands:
  1. Gray matter brighter than white matter (--t2, --bold, --dti)
  2. White matter brighter than gray matter (--t1)

OTHER ARGUMENTS

--int intvol

Supply a volume to use an an intermediate volume when performing
registration. This is useful for when the cross-modal is volume is a
partial field-of-view (FoV). If you acquire in the same session a
whole-head FoV, then pass the whole-head as the intermediate and the
partial as the moveable.

EXAMPLES:

1. Intialize with FLIRT, view result with tkregister2:

     bbregister --s bert --mov func.nii --init-fsl --reg register.dat
     tkregister2 --mov func.nii --reg register.dat --surf

2. Intialize with SPM, view result with tkregister2:

     bbregister --s bert --mov func.nii --init-spm --reg register.dat
     tkregister2 --mov func.nii --reg register.dat --surf

3. Register a partial FoV using whole FoV as intermdediate:

     bbregister --s bert --mov partial.nii --init-fsl \
       --int whole.nii --reg register.partial.dat
     tkregister2 --mov partial.nii --reg register.partial.dat --surf

