From 6c3a54027e9e8edde1efaa534371b7bac4519cec Mon Sep 17 00:00:00 2001
From: Saad Jbabdi <saad@fmrib.ox.ac.uk>
Date: Tue, 24 Nov 2009 18:37:42 +0000
Subject: [PATCH] added rotmat and rotwarp to deal with highres warpfields

---
 vecreg.cc | 175 +++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 113 insertions(+), 62 deletions(-)

diff --git a/vecreg.cc b/vecreg.cc
index 0ea16f7..4b4f8d5 100755
--- a/vecreg.cc
+++ b/vecreg.cc
@@ -13,7 +13,7 @@
 
 using namespace Utilities;
 
-string title="vecreg (Version 1.0)\nVector Affine/NonLinear Tranformation with Orientation Preservation";
+string title="vecreg (Version 1.1)\nVector Affine/NonLinear Tranformation with Orientation Preservation";
 string examples="vecreg -i <input4D> -o <output4D> -r <refvol> -t <transform>";
 
 Option<bool> verbose(string("-v,--verbose"),false,
@@ -37,8 +37,11 @@ Option<string> matrix(string("-t,--affine"),string(""),
 Option<string> warp(string("-w,--warpfield"),string(""),
 		       string("filename for 4D warp field for nonlinear registration"),
 		       false,requires_argument);
-Option<string> matrix2(string("--postmat"),string(""),
-		       string("filename for post-transform affine matrix \n\t\t\tif set, this matrix will be used for the rotation of the vector/tensor field "),
+Option<string> matrix2(string("--rotmat"),string(""),
+		       string("filename for secondary affine matrix \n\t\t\tif set, this will be used for the rotation of the vector/tensor field "),
+		       false,requires_argument);
+Option<string> warp2(string("--rotwarp"),string(""),
+		       string("filename for secondary warp field \n\t\t\tif set, this will be used for the rotation of the vector/tensor field "),
 		       false,requires_argument);
 Option<string> interpmethod(string("--interp"),"",
 		       string("interpolation method : nearestneighbour, trilinear (default), sinc or spline"),
@@ -46,6 +49,9 @@ Option<string> interpmethod(string("--interp"),"",
 Option<string> maskfile(string("-m,--mask"),string(""),
 		       string("brain mask in input space"),
 		       false,requires_argument);
+Option<string> omaskfile(string("--refmask"),string(""),
+			 string("brain mask in output space (useful for speed up of nonlinear reg)"),
+			 false,requires_argument);
 ////////////////////////////////////////////////////////
 
 ReturnMatrix rodrigues(const float& angle,ColumnVector& w){
@@ -137,6 +143,33 @@ ReturnMatrix ppd(const Matrix& F,ColumnVector& e1){
   return R;
 }
 
+void sjgradient(const volume<float>& im,volume4D<float>& grad){
+  
+  grad.reinitialize(im.xsize(),im.ysize(),im.zsize(),3);
+  copybasicproperties(im,grad[0]);
+
+  int fx,fy,fz,bx,by,bz;
+  float dx,dy,dz; 
+  for (int z=0; z<grad.zsize(); z++){
+    fz = z ==(grad.zsize()-1) ? 0 :  1;
+    bz = z == 0              ? 0 : -1;
+    dz = (fz==0 || bz==0)    ? 1.0 :  2.0;
+    for (int y=0; y<grad.ysize(); y++){
+      fy = y ==(grad.ysize()-1) ? 0 :  1;
+      by = y == 0              ? 0 : -1;
+      dy = (fy==0 || by==0)    ? 1.0 :  2.0;
+      for (int x=0; x<grad.xsize(); x++){
+	fx = x ==(grad.xsize()-1) ? 0 :  1;
+	bx = x == 0              ? 0 : -1;
+	dx = (fx==0 || bx==0)    ? 1.0 :  2.0;
+	grad[0](x,y,z) = (im(x+fx,y,z) - im(x+bx,y,z))/dx;
+	grad[1](x,y,z) = (im(x,y+fy,z) - im(x,y+by,z))/dy;
+	grad[2](x,y,z) = (im(x,y,z+fz) - im(x,y,z+bz))/dz;
+      }
+    }
+  }
+
+}
 
 void vecreg_aff(const volume4D<float>& tens,
 		volume4D<float>& oV1,
@@ -147,12 +180,26 @@ void vecreg_aff(const volume4D<float>& tens,
   Matrix iM(4,4);
   iM=M.i();
 
-  //////////////////////////////////////////////////
-  // Do we only need a FLIRT matrix for reorienting?
+  /////////////////////////////////////////////////////////////////////////
+  // Where we define a potential affine transfo for the rotation
   Matrix M2;
   if(matrix2.value()!="")
     M2 = read_ascii_matrix(matrix2.value());
-  //////////////////////////////////////////////////
+  // Where we define a potential warp field transfo for the rotation
+  volume4D<float> warpvol2;
+  volume4D<float> jx,jy,jz;
+  Matrix Jw(3,3),I(3,3);I<<1<<0<<0<<0<<1<<0<<0<<0<<1;
+  if(warp2.value()!=""){
+    FnirtFileReader ffr2(warp2.value());
+    warpvol2=ffr2.FieldAsNewimageVolume4D(true);
+    sjgradient(warpvol2[0],jx);
+    sjgradient(warpvol2[1],jy);
+    sjgradient(warpvol2[2],jz);
+  }
+  /////////////////////////////////////////////////////////////////////////
+  volume<float> omask;
+  if(omaskfile.value()!="")
+    read_volume(omask,omaskfile.value());
 
 
   // extract rotation matrix from M
@@ -174,14 +221,17 @@ void vecreg_aff(const volume4D<float>& tens,
   for(int z=0;z<oV1.zsize();z++)
     for(int y=0;y<oV1.ysize();y++)
       for(int x=0;x<oV1.xsize();x++){
+	if(omaskfile.value()!="")
+	  if(omask(x,y,z)==0)
+	    continue;
 
 	// compute seed coordinates
 	X_target << x << y << z;
 	X_seed=vox_to_vox(X_target,targetdim,seeddim,iM);
 	
-	if(mask((int)X_seed(1),(int)X_seed(2),(int)X_seed(3))==0){
+	if(mask((int)round(float(X_seed(1))),(int)round(float(X_seed(2))),(int)round(float(X_seed(3))))==0)
 	  continue;
-	}
+	
 
 	 // compute interpolated tensor
 	 Tens.Row(1) << tens[0].interpolate(X_seed(1),X_seed(2),X_seed(3));
@@ -192,11 +242,21 @@ void vecreg_aff(const volume4D<float>& tens,
 		     << tens[5].interpolate(X_seed(1),X_seed(2),X_seed(3));
 
 
+
+	 if(warp2.value()!=""){
+	   // Local Jacobian of the backward warpfield
+	   Jw <<   jx(x,y,z,0) <<  jx(x,y,z,1) << jx(x,y,z,2)
+	      <<   jy(x,y,z,0) <<  jy(x,y,z,1) << jy(x,y,z,2)
+	      <<   jz(x,y,z,0) <<  jz(x,y,z,1) << jz(x,y,z,2); 
+	   // compute local forward affine transformation	
+	   F = (I + Jw).i();
+	 }
+	 
 	 if(oV1.tsize()==3){ // case where input is a vector
 	   // compute first eigenvector
 	   EigenValues(Tens,d,v);
 	   V_seed = v.Column(3);
-	  
+	   
 	   // rotate vector
 	   V_target=F*V_seed;
 	   if(V_target.MaximumAbsoluteValue()>0)
@@ -209,7 +269,14 @@ void vecreg_aff(const volume4D<float>& tens,
 	
 	 // create tensor
 	 if(oV1.tsize()==6){
-	   Tens << R*Tens*R.t();
+	   if(warp2.value()!=""){
+	     EigenValues(Tens,d,v);
+	     R=ppd(F,v.Column(3),v.Column(2));
+	     Tens << R*Tens*R.t();
+	   }
+	   else{
+	     Tens << R*Tens*R.t();
+	   }
 	   
 	   oV1(x,y,z,0)=Tens(1,1);
 	   oV1(x,y,z,1)=Tens(2,1);
@@ -223,33 +290,6 @@ void vecreg_aff(const volume4D<float>& tens,
       }
   
 }
-void sjgradient(const volume<float>& im,volume4D<float>& grad){
-  
-  grad.reinitialize(im.xsize(),im.ysize(),im.zsize(),3);
-  copybasicproperties(im,grad[0]);
-
-  int fx,fy,fz,bx,by,bz;
-  float dx,dy,dz; 
-  for (int z=0; z<grad.zsize(); z++){
-    fz = z ==(grad.zsize()-1) ? 0 :  1;
-    bz = z == 0              ? 0 : -1;
-    dz = (fz==0 || bz==0)    ? 1.0 :  2.0;
-    for (int y=0; y<grad.ysize(); y++){
-      fy = y ==(grad.ysize()-1) ? 0 :  1;
-      by = y == 0              ? 0 : -1;
-      dy = (fy==0 || by==0)    ? 1.0 :  2.0;
-      for (int x=0; x<grad.xsize(); x++){
-	fx = x ==(grad.xsize()-1) ? 0 :  1;
-	bx = x == 0              ? 0 : -1;
-	dx = (fx==0 || bx==0)    ? 1.0 :  2.0;
-	grad[0](x,y,z) = (im(x+fx,y,z) - im(x+bx,y,z))/dx;
-	grad[1](x,y,z) = (im(x,y+fy,z) - im(x,y+by,z))/dy;
-	grad[2](x,y,z) = (im(x,y,z+fz) - im(x,y,z+bz))/dz;
-      }
-    }
-  }
-
-}
 
 
 void vecreg_nonlin(const volume4D<float>& tens,volume4D<float>& oV1,
@@ -258,11 +298,6 @@ void vecreg_nonlin(const volume4D<float>& tens,volume4D<float>& oV1,
 
   ColumnVector X_seed(3),X_target(3);
   
-  //float dxx=tens.xdim(),dyy=tens.ydim(),dzz=tens.zdim();
-  //float dx=oV1.xdim(),dy=oV1.ydim(),dz=oV1.zdim();
-  //float nxx=(float)tens.xsize()/2.0,nyy=(float)tens.ysize()/2.0,nzz=(float)tens.zsize()/2.0;
-  //float nx=(float)oV1.xsize()/2.0,ny=(float)oV1.ysize()/2.0,nz=(float)oV1.zsize()/2.0;
-
   
   // read warp field created by Jesper
   FnirtFileReader ffr(warp.value());
@@ -271,8 +306,8 @@ void vecreg_nonlin(const volume4D<float>& tens,volume4D<float>& oV1,
   Matrix F(3,3),u(3,3),v(3,3);
   DiagonalMatrix d(3);
     
-  //////////////////////////////////////////////////
-  // Do we only need a FLIRT matrix for reorienting?
+  /////////////////////////////////////////////////////////////////////////
+  // Where we define a potential affine transfo for the rotation
   Matrix M2;
   if(matrix2.value()!=""){
     M2 = read_ascii_matrix(matrix2.value());
@@ -281,16 +316,25 @@ void vecreg_nonlin(const volume4D<float>& tens,volume4D<float>& oV1,
     SVD(F*F.t(),d,u,v);
     F=(u*sqrt(d)*v.t()).i()*F;
   }
-  //////////////////////////////////////////////////
-
-  // compute transformation jacobian
-  volume4D<float> jx(mask.xsize(),mask.ysize(),mask.zsize(),3);
-  volume4D<float> jy(mask.xsize(),mask.ysize(),mask.zsize(),3);
-  volume4D<float> jz(mask.xsize(),mask.ysize(),mask.zsize(),3);
-  sjgradient(warpvol[0],jx);
-  sjgradient(warpvol[1],jy);
-  sjgradient(warpvol[2],jz);
-
+  // Where we define a potential warp field transfo for the rotation
+  volume4D<float> warpvol2;
+  volume4D<float> jx,jy,jz;
+  if(warp2.value()!=""){
+    FnirtFileReader ffr2(warp2.value());
+    warpvol2=ffr2.FieldAsNewimageVolume4D(true);
+    sjgradient(warpvol2[0],jx);
+    sjgradient(warpvol2[1],jy);
+    sjgradient(warpvol2[2],jz);
+  }
+  else{
+    sjgradient(warpvol[0],jx);
+    sjgradient(warpvol[1],jy);
+    sjgradient(warpvol[2],jz);
+  }
+  /////////////////////////////////////////////////////////////////////////
+  volume<float> omask;
+  if(omaskfile.value()!="")
+    read_volume(omask,omaskfile.value());
 
   ColumnVector V_seed(3),V_target(3);
   ColumnVector V1_seed(3),V2_seed(3);
@@ -301,16 +345,16 @@ void vecreg_nonlin(const volume4D<float>& tens,volume4D<float>& oV1,
   for(int z=0;z<oV1.zsize();z++)
     for(int y=0;y<oV1.ysize();y++)
        for(int x=0;x<oV1.xsize();x++){
-	 
-
+	 if(omaskfile.value()!="")
+	   if(omask(x,y,z)==0)
+	     continue;
 
 	 X_target << x << y << z;
 	 X_seed = NewimageCoord2NewimageCoord(warpvol,false,oV1[0],mask,X_target);
 
 
-	 if(mask((int)X_seed(1),(int)X_seed(2),(int)X_seed(3))==0){
-	   continue;
-	 }
+	if(mask((int)round(float(X_seed(1))),(int)round(float(X_seed(2))),(int)round(float(X_seed(3))))==0)
+	  continue;
 	
 	 // compute interpolated tensor
 	 Tens.Row(1) << tens[0].interpolate(X_seed(1),X_seed(2),X_seed(3));
@@ -448,6 +492,7 @@ int do_vecreg(){
   }
   //cout<<"elapsed time:"<<time(NULL)-_time<<" sec"<<endl;
 
+  ovol.setDisplayMaximumMinimum(ivol.max(),ivol.min());
   save_volume4D(ovol,outfilename.value());
 
   return 0;
@@ -467,10 +512,12 @@ int main(int argc,char *argv[]){
     options.add(outfilename);
     options.add(ref);
     options.add(matrix);
-    options.add(matrix2);
     options.add(warp);
+    options.add(matrix2);
+    options.add(warp2);
     options.add(interpmethod);
     options.add(maskfile);
+    options.add(omaskfile);
 
     options.parse_command_line(argc,argv);
 
@@ -480,19 +527,23 @@ int main(int argc,char *argv[]){
       exit(EXIT_FAILURE);
     }
     if( (matrix.set()) && (warp.set()) ){
-      options.usage();
       cerr << endl
 	   << "Cannot specify both --affine AND --warpfield"
 	   << endl << endl;
       exit(EXIT_FAILURE);
     }
     if( (matrix.unset()) && (warp.unset()) ){
-      options.usage();
       cerr << endl
 	   << "Please Specify either --affine OR --warpfield"
 	   << endl << endl;
       exit(EXIT_FAILURE);
     }
+    if( (warp2.set()) && (matrix2.set()) ){
+      cerr << endl
+	   << "Cannot Specify both --rotaff AND --rotwarp"
+	   << endl << endl;
+      exit(EXIT_FAILURE);
+    }
     
   }
   catch(X_OptionError& e) {
-- 
GitLab