diff --git a/vecreg.cc b/vecreg.cc new file mode 100755 index 0000000000000000000000000000000000000000..5dd4ae6b20a3947ab703a93cc6a38f889d89083a --- /dev/null +++ b/vecreg.cc @@ -0,0 +1,334 @@ +/* vector_flirt.cc + + Saad Jbabdi, FMRIB Image Analysis Group + + Copyright (C) 2007 University of Oxford */ + +/* CCOPYRIGHT */ + +#include "vecreg.h" +#include "utils/options.h" + +using namespace Utilities; + +string title="vector_flirt (Version 1.0)\nVector Affine/non linear Tranformation with Orientation Preservation"; +string examples="vector_flirt -i <input4Dvector> -o <output4D> -t <transformation>"; + +Option<bool> verbose(string("-v,--verbose"),false, + string("switch on diagnostic messages"), + false,no_argument); +Option<bool> help(string("-h,--help"),false, + string("display this message"), + false,no_argument); +Option<string> ivector(string("-i,--input"),string(""), + string("filename of input vector"), + true,requires_argument); +Option<string> ovector(string("-o,--output"),string(""), + string("filename of output registered vector"), + true,requires_argument); +Option<string> ref(string("-r,--ref"),string(""), + string("filename of reference (target) volume"), + true,requires_argument); +Option<string> matrix(string("-t,--affine"),string(""), + string("filename of affine transformation matrix"), + false,requires_argument); +Option<string> warp(string("-w,--warpfield"),string(""), + string("filename of 4D warp field for nonlinear registration"), + false,requires_argument); +Option<string> interpmethod(string("--interp"),string("nearestneighbour"), + string("interpolation method : nearestneighbour (default) or trilinear or sinc"), + false,requires_argument); +Option<string> maskfile(string("-m,--mask"),string(""), + string("brain mask in input space"), + false,requires_argument); +//////////////////////////////////////////////////////// + + +void vecreg_aff(const volume4D<float>& tens, + volume4D<float>& oV1, + const volume<float>& refvol, + const Matrix& M, + const volume<float>& mask){ + + Matrix iM(4,4),R(3,3); + iM=M.i(); + // extract rotation matrix from M + Matrix F(3,3),u(3,3),v(3,3); + DiagonalMatrix d(3); + F=M.SubMatrix(1,3,1,3); + SVD(F*F.t(),d,u,v); + R=(u*sqrt(d)*v.t()).i()*F; + + ColumnVector seeddim(3),targetdim(3); + seeddim << tens.xdim() << tens.ydim() << tens.zdim(); + targetdim << refvol.xdim() << refvol.ydim() << refvol.zdim(); + SymmetricMatrix Tens(3); + ColumnVector X_seed(3),X_target(3); + ColumnVector V_seed(3),V_target(3); + for(int z=0;z<oV1.zsize();z++) + for(int y=0;y<oV1.ysize();y++) + for(int x=0;x<oV1.xsize();x++){ + + // 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){ + continue; + } + + // compute interpolated tensor + Tens << tens[0].interpolate(X_seed(1),X_seed(2),X_seed(3)) + << tens[1].interpolate(X_seed(1),X_seed(2),X_seed(3)) + << tens[2].interpolate(X_seed(1),X_seed(2),X_seed(3)) + << tens[3].interpolate(X_seed(1),X_seed(2),X_seed(3)) + << tens[4].interpolate(X_seed(1),X_seed(2),X_seed(3)) + << tens[5].interpolate(X_seed(1),X_seed(2),X_seed(3)); + + // compute first eigenvector + EigenValues(Tens,d,v); + V_seed = v.Column(3); + + // rotate vector + V_target=R*V_seed; + V_target/=sqrt(V_target.SumSquare()); + oV1(x,y,z,0)=V_target(1); + oV1(x,y,z,1)=V_target(2); + oV1(x,y,z,2)=V_target(3); + } + +} +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, + const volume<float>& refvol,volume4D<float>& warp, + const volume<float>& mask){ + + 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; + + // transform mm warp to voxel warp + // the warpfield here has been transfomed by MJ's script + for(int z=0;z<warp[0].zsize();z++) + for(int y=0;y<warp[0].ysize();y++) + for(int x=0;x<warp[0].xsize();x++){ + warp[0](x,y,z) /= dx; + warp[1](x,y,z) /= dy; + warp[2](x,y,z) /= dz; + } + + + // 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(warp[0],jx); + sjgradient(warp[1],jy); + sjgradient(warp[2],jz); + + + ColumnVector V_seed(3),V_target(3); + Matrix R(3,3),I(3,3);I<<1<<0<<0<<0<<1<<0<<0<<0<<1; + Matrix F(3,3),Jw(3,3),u(3,3),v(3,3); + DiagonalMatrix d(3); + SymmetricMatrix Tens(3); + for(int z=0;z<oV1.zsize();z++) + for(int y=0;y<oV1.ysize();y++) + for(int x=0;x<oV1.xsize();x++){ + + X_seed << round(x+warp[0](x,y,z)) + << round(y+warp[1](x,y,z)) + << round(z+warp[2](x,y,z)); + if(mask((int)X_seed(1),(int)X_seed(2),(int)X_seed(3))==0){ + continue; + } + + // compute interpolated tensor + Tens << tens[0].interpolate(X_seed(1),X_seed(2),X_seed(3)) + << tens[1].interpolate(X_seed(1),X_seed(2),X_seed(3)) + << tens[2].interpolate(X_seed(1),X_seed(2),X_seed(3)) + << tens[3].interpolate(X_seed(1),X_seed(2),X_seed(3)) + << tens[4].interpolate(X_seed(1),X_seed(2),X_seed(3)) + << tens[5].interpolate(X_seed(1),X_seed(2),X_seed(3)); + // compute first eigenvector + EigenValues(Tens,d,v); + V_seed = v.Column(3); + + // 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(); + + // reorient according to affine reorientation scheme + SVD(F*F.t(),d,u,v); + R=(u*sqrt(d)*v.t()).i()*F; + + //R=ppd(F,V_seed); + + V_target=R*V_seed; + V_target/=sqrt(V_target.SumSquare()); + + oV1(x,y,z,0)=V_target(1); + oV1(x,y,z,1)=V_target(2); + oV1(x,y,z,2)=V_target(3); + } + + +} + + + +int do_vecreg(){ + volume4D<float> ivol,warpvol; + volume<float> refvol,mask; + Matrix Aff(4,4); + volumeinfo vinfo; + + if((matrix.set())){ + read_ascii_matrix(Aff,matrix.value()); + } + if((warp.set())){ + if(verbose.value()) cerr << "Loading warpfield" << endl; + read_volume4D(warpvol,warp.value()); + } + if(verbose.value()) cerr << "Loading volumes" << endl; + read_volume4D(ivol,ivector.value()); + read_volume(refvol,ref.value(),vinfo); + + + volume4D<float> ovol(refvol.xsize(),refvol.ysize(),refvol.zsize(),3); + copybasicproperties(refvol,ovol); + + // set interpolation method + if(interpmethod.value()=="nearestneighbour") + ivol.setinterpolationmethod(nearestneighbour); + else if(interpmethod.value()=="sinc") + ivol.setinterpolationmethod(sinc); + else + ivol.setinterpolationmethod(trilinear); + + if(maskfile.value()!="") + read_volume(mask,maskfile.value()); + else{ + copybasicproperties(ivol,mask); + mask=1; + } + + /////////////////////// + // tensor for interpolation + volume4D<float> tens(ivol.xsize(),ivol.ysize(),ivol.zsize(),6); + copybasicproperties(ivol,tens); + for(int z=0;z<ivol.zsize();z++) + for(int y=0;y<ivol.ysize();y++) + for(int x=0;x<ivol.xsize();x++){ + tens(x,y,z,0)=ivol(x,y,z,0)*ivol(x,y,z,0); + tens(x,y,z,1)=ivol(x,y,z,1)*ivol(x,y,z,0); + tens(x,y,z,2)=ivol(x,y,z,1)*ivol(x,y,z,1); + tens(x,y,z,3)=ivol(x,y,z,2)*ivol(x,y,z,0); + tens(x,y,z,4)=ivol(x,y,z,2)*ivol(x,y,z,1); + tens(x,y,z,5)=ivol(x,y,z,2)*ivol(x,y,z,2); + } + + //time_t _time=time(NULL); + if(matrix.set()){ + if(verbose.value()) cerr << "Affine registration" << endl; + vecreg_aff(tens,ovol,refvol,Aff,mask); + } + else{ + if(verbose.value()) cerr << "Nonlinear registration" << endl; + vecreg_nonlin(tens,ovol,refvol,warpvol,mask); + } + //cout<<"elapsed time:"<<time(NULL)-_time<<" sec"<<endl; + + save_volume4D(ovol,ovector.value()); + + return 0; + +} + + +int main(int argc,char *argv[]){ + + Tracer tr("main"); + OptionParser options(title,examples); + + try{ + options.add(verbose); + options.add(help); + options.add(ivector); + options.add(ovector); + options.add(ref); + options.add(matrix); + options.add(warp); + options.add(interpmethod); + options.add(maskfile); + + options.parse_command_line(argc,argv); + + + if ( (help.value()) || (!options.check_compulsory_arguments(true)) ){ + options.usage(); + 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); + } + } + catch(X_OptionError& e) { + options.usage(); + cerr << endl << e.what() << endl; + exit(EXIT_FAILURE); + } + catch(std::exception &e) { + cerr << e.what() << endl; + } + + return do_vecreg(); + + +} diff --git a/vecreg.h b/vecreg.h new file mode 100755 index 0000000000000000000000000000000000000000..afdb8a6cc1129e5064dacd538b7a2a93b8bef843 --- /dev/null +++ b/vecreg.h @@ -0,0 +1,23 @@ +#include <cmath> +#include <stdlib.h> +#include "newimage/newimageall.h" +#include "miscmaths/miscmaths.h" + +using namespace NEWIMAGE; +using namespace NEWMAT; +using namespace std; + + +ReturnMatrix rodrigues(const float&,ColumnVector&); + +ReturnMatrix rodrigues(const float&,const float&,ColumnVector&); + +ReturnMatrix rodrigues(const ColumnVector&,const ColumnVector&); + +ReturnMatrix ppd(const Matrix&,const ColumnVector&, const ColumnVector&); + +void vecreg_aff(const volume4D<float>&,volume4D<float>&,const volume<float>&,const Matrix&,const volume<float>&); + +void vecreg_nonlin(const volume4D<float>&,volume4D<float>&,const volume<float>&,volume4D<float>&,const volume<float>&); + +void sjgradient(const volume<float>&,volume4D<float>&);