From c8fab05e0b32cb34d0c2abe3f4cbb15720224f0c Mon Sep 17 00:00:00 2001 From: Saad Jbabdi <saad@fmrib.ox.ac.uk> Date: Fri, 29 Feb 2008 12:56:35 +0000 Subject: [PATCH] kurtosis v.0.0 --- kurtosis.cc | 576 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 576 insertions(+) create mode 100644 kurtosis.cc diff --git a/kurtosis.cc b/kurtosis.cc new file mode 100644 index 0000000..474763a --- /dev/null +++ b/kurtosis.cc @@ -0,0 +1,576 @@ +/* Copyright (C) 2008 University of Oxford */ + +/* S.Jbabdi */ + +/* CCOPYRIGHT */ + +#include <iostream> +#include <cmath> +#include "miscmaths/miscmaths.h" +#include "miscmaths/minimize.h" +#include "newmat.h" +#include "newimage/newimageall.h" +#include "dtifitOptions.h" + +using namespace std; +using namespace NEWMAT; +using namespace MISCMATHS; +using namespace NEWIMAGE; +using namespace DTIFIT; + +const float maxfloat=1e10; +const float minfloat=1e-10; +const float maxlogfloat=23; +const float minlogfloat=-23; +const int maxint=1000000000; + + +ReturnMatrix form_Kmat(const Matrix& r){ + Matrix K(r.Ncols(),15); + + for(int j=1;j<=r.Ncols();j++){ + float x=r(1,j),y=r(2,j),z=r(3,j); + K(j,1) = MISCMATHS::pow(x,4); + K(j,2) = MISCMATHS::pow(y,4); + K(j,3) = MISCMATHS::pow(z,4); + K(j,4) = 4*MISCMATHS::pow(x,3)*y; + K(j,5) = 4*MISCMATHS::pow(x,3)*z; + K(j,6) = 4*MISCMATHS::pow(y,3)*x; + K(j,7) = 4*MISCMATHS::pow(y,3)*z; + K(j,8) = 4*MISCMATHS::pow(z,3)*x; + K(j,9) = 4*MISCMATHS::pow(z,3)*y; + K(j,10) = 6*MISCMATHS::pow(x,2)*MISCMATHS::pow(y,2); + K(j,11) = 6*MISCMATHS::pow(x,2)*MISCMATHS::pow(z,2); + K(j,12) = 6*MISCMATHS::pow(y,2)*MISCMATHS::pow(z,2); + K(j,13) = 12*MISCMATHS::pow(x,2)*y*z; + K(j,14) = 12*MISCMATHS::pow(y,2)*x*z; + K(j,15) = 12*MISCMATHS::pow(z,2)*x*y; + j+=1; + } + K.Release(); + return K; +} + + +// note the order of the variable parameters +// D11,D12,D13,D22,D23,D33,logS0 +// W1111,W2222,W333,W1112,W1113,W1222,W2223,W1333, +// W2333,W1122,W1133,W2233,W1123,W1223,W1233 +class KurtosisNonlinCF : public gEvalFunction +{ +protected: + ColumnVector m_A; + ColumnVector m_B; + Matrix m_C; + Matrix m_D; + int m_n; + +public: + KurtosisNonlinCF(const ColumnVector& data,const Matrix& bvals,const Matrix& bvecs):gEvalFunction() + { + + m_n = data.Nrows(); + + m_A.ReSize(m_n); + m_B.ReSize(m_n); + m_C.ReSize(m_n,6); + m_D.ReSize(m_n,15); + + Matrix K = form_Kmat(bvecs); + + for (int i=1;i<=m_n;i++){ + if(data(i)>0){ + m_A(i)=-log(data(i)); + } + else{ + m_A(i)=0; + } + m_B(i) = 1.0; + + m_C(i,1) = -bvals(1,i)*bvecs(1,i)*bvecs(1,i); + m_C(i,2) = -2*bvals(1,i)*bvecs(1,i)*bvecs(2,i); + m_C(i,3) = -2*bvals(1,i)*bvecs(1,i)*bvecs(3,i); + m_C(i,4) = -bvals(1,i)*bvecs(2,i)*bvecs(2,i); + m_C(i,5) = -2*bvals(1,i)*bvecs(2,i)*bvecs(3,i); + m_C(i,6) = -bvals(1,i)*bvecs(3,i)*bvecs(3,i); + + for(int j=1;j<=15;j++) + m_D(i,j) = (bvals(1,i)*bvals(1,i)/6) * K(i,j); + + } + + } + + virtual ~KurtosisNonlinCF(){}; + + float evaluate(const ColumnVector& x) const{ + float res=0; + res = ( m_A + m_B*x(7) + m_C*x.SubMatrix(1,6,1,1) + + m_D*x.SubMatrix(8,22,1,1)*(x(1)+x(4)+x(6))*(x(1)+x(4)+x(6))/9 ).SumSquare(); + + return res; + } + ReturnMatrix g_evaluate(const ColumnVector& x) const{ + ColumnVector sj_g(x.Nrows()); + // ColumnVector sj_gg; + + // sj_gg = MISCMATHS::gradient(x,*this,1e-4); + + ColumnVector sj_d(6); + ColumnVector sj_w(15); + sj_d = x.SubMatrix(1,6,1,1); + sj_w = x.SubMatrix(8,22,1,1); + double sj_t = x(1)+x(4)+x(6); + double sj_t2=sj_t*sj_t; + + ColumnVector sj_func(m_n); + sj_func = m_A + m_B*x(7) + m_C*sj_d + m_D*sj_w*sj_t*sj_t/9; + + sj_g(1) = 2*NEWMAT::SP(sj_func,m_C.SubMatrix(1,m_n,1,1)+2*sj_t/9*m_D*sj_w).Sum(); + sj_g(2) = 2*NEWMAT::SP(sj_func,m_C.SubMatrix(1,m_n,2,2)).Sum(); + sj_g(3) = 2*NEWMAT::SP(sj_func,m_C.SubMatrix(1,m_n,3,3)).Sum(); + sj_g(4) = 2*NEWMAT::SP(sj_func,m_C.SubMatrix(1,m_n,4,4)+2*sj_t/9*m_D*sj_w).Sum(); + sj_g(5) = 2*NEWMAT::SP(sj_func,m_C.SubMatrix(1,m_n,5,5)).Sum(); + sj_g(6) = 2*NEWMAT::SP(sj_func,m_C.SubMatrix(1,m_n,6,6)+2*sj_t/9*m_D*sj_w).Sum(); + + sj_g(7) = 2*NEWMAT::SP(sj_func,m_B).Sum(); + + for(int sj_i=1,sj_j=8;sj_j<=x.Nrows();sj_i++,sj_j++) + sj_g(sj_j) = 2*NEWMAT::SP(sj_func,sj_t2/9*m_D.SubMatrix(1,m_n,sj_i,sj_i)).Sum(); + + sj_g.Release(); + return sj_g; + + } + + const KurtosisNonlinCF& operator=(const KurtosisNonlinCF& par) + { + m_A = par.m_A; + m_B = par.m_B; + m_C = par.m_C; + m_D = par.m_D; + m_n = par.m_n; + + return *this; + + } + KurtosisNonlinCF(const KurtosisNonlinCF& rhs): + m_A(rhs.m_A),m_B(rhs.m_B),m_C(rhs.m_C),m_D(rhs.m_D),m_n(rhs.m_n){ + *this=rhs; + } + +}; + +inline float PI() { return 3.14159265358979;} +inline float min(float a,float b){ + return a<b ? a:b;} +inline float max(float a,float b){ + return a>b ? a:b;} +inline Matrix Anis() +{ + Matrix A(3,3); + A << 1 << 0 << 0 + << 0 << 0 << 0 + << 0 << 0 << 0; + return A; +} + +inline Matrix Is() +{ + Matrix I(3,3); + I << 1 << 0 << 0 + << 0 << 1 << 0 + << 0 << 0 << 1; + return I; +} + +inline ColumnVector Cross(const ColumnVector& A,const ColumnVector& B) +{ + ColumnVector res(3); + res << A(2)*B(3)-A(3)*B(2) + << A(3)*B(1)-A(1)*B(3) + << A(1)*B(2)-B(1)*A(2); + return res; +} + +inline Matrix Cross(const Matrix& A,const Matrix& B) +{ + Matrix res(3,1); + res << A(2,1)*B(3,1)-A(3,1)*B(2,1) + << A(3,1)*B(1,1)-A(1,1)*B(3,1) + << A(1,1)*B(2,1)-B(1,1)*A(2,1); + return res; +} + +float mod(float a, float b){ + while(a>b){a=a-b;} + while(a<0){a=a+b;} + return a; +} + + + +Matrix form_Amat(const Matrix& r,const Matrix& b) +{ + Matrix A(r.Ncols(),7); + Matrix tmpvec(3,1), tmpmat; + + for( int i = 1; i <= r.Ncols(); i++){ + tmpvec << r(1,i) << r(2,i) << r(3,i); + tmpmat = tmpvec*tmpvec.t()*b(1,i); + A(i,1) = tmpmat(1,1); + A(i,2) = 2*tmpmat(1,2); + A(i,3) = 2*tmpmat(1,3); + A(i,4) = tmpmat(2,2); + A(i,5) = 2*tmpmat(2,3); + A(i,6) = tmpmat(3,3); + A(i,7) = 1; + } + return A; +} +inline SymmetricMatrix vec2tens(ColumnVector& Vec){ + SymmetricMatrix tens(3); + tens(1,1)=Vec(1); + tens(2,1)=Vec(2); + tens(3,1)=Vec(3); + tens(2,2)=Vec(4); + tens(3,2)=Vec(5); + tens(3,3)=Vec(6); + return tens; +} + +void tensorfit(DiagonalMatrix& Dd,ColumnVector& evec1,ColumnVector& evec2,ColumnVector& evec3,float& f,float& s0,ColumnVector& Dvec, const Matrix& Amat,const ColumnVector& S) +{ + //Initialise the parameters using traditional DTI analysis + ColumnVector logS(S.Nrows()); + SymmetricMatrix tens; //Basser's Diffusion Tensor; + // DiagonalMatrix Dd; //eigenvalues + Matrix Vd; //eigenvectors + DiagonalMatrix Ddsorted(3); + float mDd, fsquared; + + for ( int i = 1; i <= S.Nrows(); i++) + { + if(S(i)>0){ + logS(i)=log(S(i)); + } + else{ + logS(i)=0; + } + // logS(i)=(S(i)/S0)>0.01 ? log(S(i))-log(S0):log(0.01); + } + + Dvec = -pinv(Amat)*logS; + if( Dvec(7) > -maxlogfloat ){ + s0=exp(-Dvec(7)); + } + else{ + s0=S.MaximumAbsoluteValue(); + } + for ( int i = 1; i <= S.Nrows(); i++) + { + if(s0<S.Sum()/S.Nrows()){ s0=S.MaximumAbsoluteValue(); } + logS(i)=(S(i)/s0)>0.01 ? log(S(i)):log(0.01*s0); + } + Dvec = -pinv(Amat)*logS; + s0=exp(-Dvec(7)); + if(s0<S.Sum()/S.Nrows()){ s0=S.Sum()/S.Nrows(); } + tens = vec2tens(Dvec); + EigenValues(tens,Dd,Vd); + mDd = Dd.Sum()/Dd.Nrows(); + int maxind = Dd(1) > Dd(2) ? 1:2; //finding max,mid and min eigenvalues + maxind = Dd(maxind) > Dd(3) ? maxind:3; + int midind; + if( (Dd(1)>=Dd(2) && Dd(2)>=Dd(3)) || (Dd(1)<=Dd(2) && Dd(2)<=Dd(3)) ){midind=2;} + else if( (Dd(2)>=Dd(1) && Dd(1)>=Dd(3)) || (Dd(2)<=Dd(1) && Dd(1)<=Dd(3)) ){midind=1;} + else {midind=3;} + int minind = Dd(1) < Dd(2) ? 1:2; //finding maximum eigenvalue + minind = Dd(minind) < Dd(3) ? minind:3; + Ddsorted << Dd(maxind) << Dd(midind) << Dd(minind); + Dd=Ddsorted; + evec1 << Vd(1,maxind) << Vd(2,maxind) << Vd(3,maxind); + evec2 << Vd(1,midind) << Vd(2,midind) << Vd(3,midind); + evec3 << Vd(1,minind) << Vd(2,minind) << Vd(3,minind); + + float numer=1.5*((Dd(1)-mDd)*(Dd(1)-mDd)+(Dd(2)-mDd)*(Dd(2)-mDd)+(Dd(3)-mDd)*(Dd(3)-mDd)); + float denom=(Dd(1)*Dd(1)+Dd(2)*Dd(2)+Dd(3)*Dd(3)); + + if(denom>0) fsquared=numer/denom; + else fsquared=0; + if(fsquared>0){f=sqrt(fsquared);} + else{f=0;} + +} + +void kurtosisfit(DiagonalMatrix& Dd,ColumnVector& evec1,ColumnVector& evec2, ColumnVector& evec3, + float& f,float& s0,ColumnVector& Dvec, float& mk, ColumnVector& tens4, + const Matrix& Amat,const Matrix& Kmat,const ColumnVector& S,const Matrix& bvals,const Matrix& bvecs){ + + // initialise second-order tensor with simple tensor fit + // tensorfit(Dd,evec1,evec2,evec3,f,s0,Dvec,Amat,S); + //SymmetricMatrix tens; + //tens = vec2tens(Dvec); + +// // initialise Kurtosis using Linear fit +// ColumnVector v(S.Nrows()); +// for(int i=1;i<=S.Nrows();i++){ +// float bDi = bvals(1,i)*(bvecs.Column(i).t()*tens*bvecs.Column(i)).AsScalar(); +// if(bDi>0) +// v(i) = 6*(log(S(i)/s0)+bDi)/(bDi*bDi); +// else +// v(i) = 0; +// } +// tens4 = pinv(Kmat) * v; + + // calculate DT and KT using non-linear fitting + KurtosisNonlinCF KNL(S,bvals,bvecs); + ColumnVector xmin(22); + KNL.minimize(xmin); + + + Dvec.SubMatrix(1,6,1,1) = xmin.SubMatrix(1,6,1,1); + tens4 = xmin.SubMatrix(8,22,1,1); + Dvec(7) = exp(xmin(7)); + s0 = Dvec(7); + + // Tensor Stuff + float mDd, fsquared; + SymmetricMatrix tens; + DiagonalMatrix Ddsorted(3); + Matrix Vd; + tens = vec2tens(Dvec); + EigenValues(tens,Dd,Vd); + mDd = Dd.Sum()/Dd.Nrows(); + int maxind = Dd(1) > Dd(2) ? 1:2; //finding max,mid and min eigenvalues + maxind = Dd(maxind) > Dd(3) ? maxind:3; + int midind; + if( (Dd(1)>=Dd(2) && Dd(2)>=Dd(3)) || (Dd(1)<=Dd(2) && Dd(2)<=Dd(3)) ){midind=2;} + else if( (Dd(2)>=Dd(1) && Dd(1)>=Dd(3)) || (Dd(2)<=Dd(1) && Dd(1)<=Dd(3)) ){midind=1;} + else {midind=3;} + int minind = Dd(1) < Dd(2) ? 1:2; //finding maximum eigenvalue + minind = Dd(minind) < Dd(3) ? minind:3; + Ddsorted << Dd(maxind) << Dd(midind) << Dd(minind); + Dd=Ddsorted; + evec1 << Vd(1,maxind) << Vd(2,maxind) << Vd(3,maxind); + evec2 << Vd(1,midind) << Vd(2,midind) << Vd(3,midind); + evec3 << Vd(1,minind) << Vd(2,minind) << Vd(3,minind); + float numer=1.5*((Dd(1)-mDd)*(Dd(1)-mDd)+(Dd(2)-mDd)*(Dd(2)-mDd)+(Dd(3)-mDd)*(Dd(3)-mDd)); + float denom=(Dd(1)*Dd(1)+Dd(2)*Dd(2)+Dd(3)*Dd(3)); + if(denom>0) fsquared=numer/denom; + else fsquared=0; + if(fsquared>0){f=sqrt(fsquared);} + else{f=0;} + + // Kurtosis Stuff + mk = 0; + ColumnVector vec(S.Nrows()); + vec = Kmat*tens4; + for(int i=1;i<=S.Nrows();i++){ + if(bvals(1,i)>0) + mk+=vec(i)/(bvecs.Column(i).t()*tens*bvecs.Column(i)).AsScalar(); + } + mk *= mDd*mDd; + //OUT(mk); +} + +int main(int argc, char** argv) +{ + //parse command line + dtifitOptions& opts = dtifitOptions::getInstance(); + int success=opts.parse_command_line(argc,argv); + if(!success) return 1; + + if(opts.verbose.value()){ + cout<<"data file "<<opts.dtidatafile.value()<<endl; + cout<<"mask file "<<opts.maskfile.value()<<endl; + cout<<"bvecs "<<opts.bvecsfile.value()<<endl; + cout<<"bvals "<<opts.bvalsfile.value()<<endl; + if(opts.littlebit.value()){ + cout<<"min z "<<opts.z_min.value()<<endl; + cout<<"max z "<<opts.z_max.value()<<endl; + cout<<"min y "<<opts.y_min.value()<<endl; + cout<<"max y "<<opts.y_max.value()<<endl; + cout<<"min x "<<opts.x_min.value()<<endl; + cout<<"max x "<<opts.x_max.value()<<endl; + } + } + ///////////////////////////////////////// + // read bvecs and bvals + // correct transpose and normalise bvecs + Matrix r = read_ascii_matrix(opts.bvecsfile.value()); + if(r.Nrows()>3) r=r.t(); + for(int i=1;i<=r.Ncols();i++){ + float tmpsum=sqrt(r(1,i)*r(1,i)+r(2,i)*r(2,i)+r(3,i)*r(3,i)); + if(tmpsum!=0){ + r(1,i)=r(1,i)/tmpsum; + r(2,i)=r(2,i)/tmpsum; + r(3,i)=r(3,i)/tmpsum; + } + } + Matrix b = read_ascii_matrix(opts.bvalsfile.value()); + if(b.Nrows()>1) b=b.t(); + ////////////////////////////////////////// + + volume4D<float> data; + volume<int> mask; + volumeinfo tempinfo; + if(opts.verbose.value()) cout<<"reading data"<<endl; + read_volume4D(data,opts.dtidatafile.value(),tempinfo); + if(opts.verbose.value()) cout<<"reading mask"<<endl; + read_volume(mask,opts.maskfile.value()); + if(opts.verbose.value()) cout<<"ok"<<endl; + int minx=opts.littlebit.value() ? opts.x_min.value():0; + int maxx=opts.littlebit.value() ? opts.x_max.value():mask.xsize(); + int miny=opts.littlebit.value() ? opts.y_min.value():0; + int maxy=opts.littlebit.value() ? opts.y_max.value():mask.ysize(); + int minz=opts.littlebit.value() ? opts.z_min.value():0; + int maxz=opts.littlebit.value() ? opts.z_max.value():mask.zsize(); + cout<<minx<<" "<<maxx<<" "<<miny<<" "<<maxy<<" "<<minz<<" "<<maxz<<endl; + if(opts.verbose.value()) cout<<"setting up vols"<<endl; + + volume<float> l1(maxx-minx,maxy-miny,maxz-minz); + volume<float> l2(maxx-minx,maxy-miny,maxz-minz); + volume<float> l3(maxx-minx,maxy-miny,maxz-minz); + volume<float> MD(maxx-minx,maxy-miny,maxz-minz); + volume<float> FA(maxx-minx,maxy-miny,maxz-minz); + volume<float> S0(maxx-minx,maxy-miny,maxz-minz); + volume4D<float> V1(maxx-minx,maxy-miny,maxz-minz,3); + volume4D<float> V2(maxx-minx,maxy-miny,maxz-minz,3); + volume4D<float> V3(maxx-minx,maxy-miny,maxz-minz,3); + volume4D<float> Delements(maxx-minx,maxy-miny,maxz-minz,6); + volume<float> MK(maxx-minx,maxy-miny,maxz-minz); + volume4D<float> KurtTens(maxx-minx,maxy-miny,maxz-minz,15); + + if(opts.verbose.value()) cout<<"copying input properties to output volumes"<<endl; + copybasicproperties(data[0],l1); + copybasicproperties(data[0],l2); + copybasicproperties(data[0],l3); + copybasicproperties(data[0],MD); + copybasicproperties(data[0],FA); + copybasicproperties(data[0],S0); + copybasicproperties(data[0],V1[0]); + copybasicproperties(data[0],V2[0]); + copybasicproperties(data[0],V3[0]); + copybasicproperties(data[0],Delements[0]); + copybasicproperties(data[0],MK); + copybasicproperties(data[0],KurtTens[0]); + + if(opts.verbose.value()) cout<<"zeroing output volumes"<<endl; + l1=0;l2=0;l3=0;MD=0;FA=0;S0=0;V1=0;V2=0;V3=0;Delements=0;MK=0;KurtTens=0; + if(opts.verbose.value()) cout<<"ok"<<endl; + + + DiagonalMatrix evals(3); + ColumnVector evec1(3),evec2(3),evec3(3); + ColumnVector tens4(15); + ColumnVector S(data.tsize()); + float fa,s0,mk; + if(opts.verbose.value()) cout<<"Forming A matrix"<<endl; + Matrix Amat = form_Amat(r,b); + Matrix Kmat = form_Kmat(r); + if(opts.verbose.value()) cout<<"starting the fits"<<endl; + ColumnVector Dvec(7); Dvec=0; + for(int k = minz; k < maxz; k++){ + cout<<k<<" slices processed"<<endl; + for(int j=miny; j < maxy; j++){ + for(int i =minx; i< maxx; i++){ + + if(mask(i,j,k)>0){ + + for(int t=0;t < data.tsize();t++){ + S(t+1)=data(i,j,k,t); + } + //tensorfit(evals,evec1,evec2,evec3,fa,s0,Dvec,Amat,S); + kurtosisfit(evals,evec1,evec2,evec3,fa,s0,Dvec,mk,tens4,Amat,Kmat,S,b,r); + + + l1(i-minx,j-miny,k-minz)=evals(1); + l2(i-minx,j-miny,k-minz)=evals(2); + l3(i-minx,j-miny,k-minz)=evals(3); + MD(i-minx,j-miny,k-minz)=(evals(1)+evals(2)+evals(3))/3; + FA(i-minx,j-miny,k-minz)=fa; + S0(i-minx,j-miny,k-minz)=s0; + V1(i-minx,j-miny,k-minz,0)=evec1(1); + V1(i-minx,j-miny,k-minz,1)=evec1(2); + V1(i-minx,j-miny,k-minz,2)=evec1(3); + V2(i-minx,j-miny,k-minz,0)=evec2(1); + V2(i-minx,j-miny,k-minz,1)=evec2(2); + V2(i-minx,j-miny,k-minz,2)=evec2(3); + V3(i-minx,j-miny,k-minz,0)=evec3(1); + V3(i-minx,j-miny,k-minz,1)=evec3(2); + V3(i-minx,j-miny,k-minz,2)=evec3(3); + Delements(i-minx,j-miny,k-minz,0)=Dvec(1); + Delements(i-minx,j-miny,k-minz,1)=Dvec(2); + Delements(i-minx,j-miny,k-minz,2)=Dvec(3); + Delements(i-minx,j-miny,k-minz,3)=Dvec(4); + Delements(i-minx,j-miny,k-minz,4)=Dvec(5); + Delements(i-minx,j-miny,k-minz,5)=Dvec(6); + + MK(i-minx,j-miny,k-minz)=mk; + for(int iii=0;iii<15;iii++) + KurtTens(i-minx,j-miny,k-minz,iii) = tens4(iii+1); + + } + } + } + } + + string fafile=opts.ofile.value()+"_FA"; + string s0file=opts.ofile.value()+"_S0"; + string l1file=opts.ofile.value()+"_L1"; + string l2file=opts.ofile.value()+"_L2"; + string l3file=opts.ofile.value()+"_L3"; + string v1file=opts.ofile.value()+"_V1"; + string v2file=opts.ofile.value()+"_V2"; + string v3file=opts.ofile.value()+"_V3"; + string MDfile=opts.ofile.value()+"_MD"; + string tensfile=opts.ofile.value()+"_tensor"; + string MKfile=opts.ofile.value()+"_MK"; + string kurtosisfile=opts.ofile.value()+"_kurtosis"; + if(opts.littlebit.value()){ + fafile+="littlebit"; + s0file+="littlebit"; + l1file+="littlebit"; + l2file+="littlebit"; + l3file+="littlebit"; + v1file+="littlebit"; + v2file+="littlebit"; + v3file+="littlebit"; + MDfile+="littlebit"; + tensfile+="littlebit"; + MKfile+="littlebit"; + kurtosisfile+="littlebit"; + } + + save_volume(FA,fafile,tempinfo); + save_volume(S0,s0file,tempinfo); + save_volume(l1,l1file,tempinfo); + save_volume(l2,l2file,tempinfo); + save_volume(l3,l3file,tempinfo); + save_volume(MD,MDfile,tempinfo); + save_volume4D(V1,v1file,tempinfo); + save_volume4D(V2,v2file,tempinfo); + save_volume4D(V3,v3file,tempinfo); + save_volume(MK,MKfile,tempinfo); + + if(opts.savetensor.value()){ + save_volume4D(Delements,tensfile,tempinfo); + save_volume4D(KurtTens,kurtosisfile,tempinfo); + } + + + return 0; +} + + + + + + + + + + + + + -- GitLab