// Definitions for class splinefield // // splinefield.cpp // // Jesper Andersson, FMRIB Image Analysis Group // // Copyright (C) 2007 University of Oxford // /* Part of FSL - FMRIB's Software Library http://www.fmrib.ox.ac.uk/fsl fsl@fmrib.ox.ac.uk Developed at FMRIB (Oxford Centre for Functional Magnetic Resonance Imaging of the Brain), Department of Clinical Neurology, Oxford University, Oxford, UK LICENCE FMRIB Software Library, Release 5.0 (c) 2012, The University of Oxford (the "Software") The Software remains the property of the University of Oxford ("the University"). The Software is distributed "AS IS" under this Licence solely for non-commercial use in the hope that it will be useful, but in order that the University as a charitable foundation protects its assets for the benefit of its educational and research purposes, the University makes clear that no condition is made or to be implied, nor is any warranty given or to be implied, as to the accuracy of the Software, or that it will be suitable for any particular purpose or for use under any specific conditions. Furthermore, the University disclaims all responsibility for the use which is made of the Software. It further disclaims any liability for the outcomes arising from using the Software. The Licensee agrees to indemnify the University and hold the University harmless from and against any and all claims, damages and liabilities asserted by third parties (including claims for negligence) which arise directly or indirectly from the use of the Software or the sale of any products based on the Software. No part of the Software may be reproduced, modified, transmitted or transferred in any form or by any means, electronic or mechanical, without the express permission of the University. The permission of the University is not required if the said reproduction, modification, transmission or transference is done without financial return, the conditions of this Licence are imposed upon the receiver of the product, and all original and amended source code is included in any transmitted product. You may be held legally responsible for any copyright infringement that is caused or encouraged by your failure to abide by these terms and conditions. You are not permitted under this Licence to use this Software commercially. Use for which any financial return is received shall be defined as commercial use, and includes (1) integration of all or part of the source code or the Software into a product for sale or license by or on behalf of Licensee to third parties or (2) use of the Software or any derivative of it for research with the final aim of developing software products for sale or license to a third party or (3) use of the Software or any derivative of it for research with the final aim of developing non-software products for sale or license to a third party, or (4) use of the Software to provide any service to an external organisation for which payment is received. If you are interested in using the Software commercially, please contact Isis Innovation Limited ("Isis"), the technology transfer company of the University, to negotiate a licence. Contact details are: innovation@isis.ox.ac.uk quoting reference DE/9564. */ // #include #include #include #include #include #include #include "newmat.h" #include "miscmaths/bfmatrix.h" #include "splines.h" #include "fsl_splines.h" #include "splinefield.h" using namespace std; using namespace NEWMAT; namespace BASISFIELD { // Constructor, assignement and destructor // Plain vanilla constructors splinefield::splinefield(const std::vector& psz, const std::vector& pvxs, const std::vector& ksp, int order) : basisfield(psz,pvxs), _sp(order,ksp), _dsp(3) { if (order < 2 || order > 3) {throw BasisfieldException("splinefield::splinefield: Only quadratic and cubic splines implemented yet");} if (ksp.size() != NDim()) {throw BasisfieldException("splinefield::splinefield: Dimensionality mismatch");} /* if (pksp[0]<0 || pksp[0]>FieldSz_x() || (NDim()>1 && (pksp[1]<0 || pksp[1]>FieldSz_y())) || (NDim()>2 && (pksp[2]<0 || pksp[2]>FieldSz_z()))) { throw BasisfieldException("splinefield::splinefield: Invalid knot-spacing"); } */ if (ksp[0]<0 || (NDim()>1 && ksp[1]<0) || (NDim()>2 && ksp[2]<0)) { throw BasisfieldException("splinefield::splinefield: Invalid knot-spacing"); } for (unsigned int i=0; i<3; i++) { std::vector deriv(3,0); deriv[i] = 1; _dsp[i] = boost::shared_ptr >(new Spline3D(order,ksp,deriv)); } boost::shared_ptr lcoef(new NEWMAT::ColumnVector(CoefSz())); *lcoef = 0.0; set_coef_ptr(lcoef); } // Copy constructor splinefield::splinefield(const splinefield& inf) : basisfield(inf), _sp(inf._sp) { // basisfield::assign(inf); // splinefield::assign(inf); } void splinefield::assign(const splinefield& inf) { _sp = inf._sp; for (unsigned int i=0; i<3; i++) { _dsp[i] = boost::shared_ptr >(new Spline3D(*(inf._dsp[i]))); } } splinefield& splinefield::operator=(const splinefield& inf) { if (&inf == this) {return(*this);} // Detect self basisfield::assign(inf); // Assign common part splinefield::assign(inf); // Assign splinefield specific bits return(*this); } // General utility functions // Functions that actually do some work double splinefield::Peek(double x, double y, double z, FieldIndex fi) const { const Spline3D *sp_ptr = 0; if (fi == FIELD) sp_ptr = &_sp; else { std::vector deriv(3,0); switch (fi) { case DFDX: deriv[0] = 1; break; case DFDY: deriv[1] = 1; break; case DFDZ: deriv[2] = 1; break; default: throw BasisfieldException("Peek: Invalid FieldIndex value"); } sp_ptr = new Spline3D(_sp.Order(),_sp.KnotSpacing(),deriv); } std::vector vox(3); vox[0]=x; vox[1]=y; vox[2]=z; std::vector coefsz(3); coefsz[0] = CoefSz_x(); coefsz[1] = CoefSz_y(); coefsz[2] = CoefSz_z(); std::vector first_cindx(3); std::vector last_cindx(3); sp_ptr->RangeOfSplines(vox,coefsz,first_cindx,last_cindx); double rval = 0.0; std::vector cindx(3,0); for (cindx[2]=first_cindx[2]; cindx[2]SplineValueAtVoxel(vox,cindx); } } } if (fi != FIELD) delete sp_ptr; return(rval); } void splinefield::Update(FieldIndex fi) { if (fi>int(NDim())) {throw BasisfieldException("splinefield::Update: Cannot take derivative in singleton direction");} if (UpToDate(fi)) {return;} // Field already fine. const boost::shared_ptr lcoef = GetCoef(); if (!lcoef) {throw BasisfieldException("splinefield::Update: No coefficients set yet");} // Get spline kernel std::vector deriv(3,0); if (fi) deriv[fi-1] = 1; Spline3D spline(_sp.Order(),_sp.KnotSpacing(),deriv); std::vector coefsz(3,0); coefsz[0] = CoefSz_x(); coefsz[1] = CoefSz_y(); coefsz[2] = CoefSz_z(); std::vector fieldsz(3,0); fieldsz[0] = FieldSz_x(); fieldsz[1] = FieldSz_y(); fieldsz[2] = FieldSz_z(); boost::shared_ptr fptr = get_ptr(fi); get_field(spline,static_cast(lcoef->Store()),coefsz,fieldsz,static_cast(fptr->Store())); set_update_flag(true,fi); } NEWMAT::ReturnMatrix splinefield::Jte(const NEWIMAGE::volume& ima1, const NEWIMAGE::volume& ima2, const NEWIMAGE::volume *mask) const { std::vector deriv(NDim(),0); NEWMAT::ColumnVector tmp = Jte(deriv,ima1,ima2,mask); tmp.Release(); return(tmp); } NEWMAT::ReturnMatrix splinefield::Jte(const std::vector& deriv, const NEWIMAGE::volume& ima1, const NEWIMAGE::volume& ima2, const NEWIMAGE::volume *mask) const { if (deriv.size() != 3) throw BasisfieldException("splinefield::Jte: Wrong size deriv vector"); if (!samesize(ima1,ima2) || (mask && !samesize(ima1,*mask))) { throw BasisfieldException("splinefield::Jte: Image dimensionality mismatch"); } if (static_cast(ima1.xsize()) != FieldSz_x() || static_cast(ima1.ysize()) != FieldSz_y() || static_cast(ima1.zsize()) != FieldSz_z()) { throw BasisfieldException("splinefield::Jte: Image-Field dimensionality mismatch"); } float *prodima = new float[FieldSz()]; hadamard(ima1,ima2,mask,prodima); Spline3D spline(_sp.Order(),_sp.KnotSpacing(),deriv); std::vector coefsz(3,0); coefsz[0] = CoefSz_x(); coefsz[1] = CoefSz_y(); coefsz[2] = CoefSz_z(); std::vector imasz(3,0); imasz[0] = FieldSz_x(); imasz[1] = FieldSz_y(); imasz[2] = FieldSz_z(); NEWMAT::ColumnVector ovec(CoefSz()); get_jte(spline,coefsz,prodima,imasz,static_cast(ovec.Store())); delete[] prodima; ovec.Release(); return(ovec); } NEWMAT::ReturnMatrix splinefield::Jte(const NEWIMAGE::volume& ima, const NEWIMAGE::volume *mask) const { std::vector deriv(NDim(),0); NEWMAT::ColumnVector tmp = Jte(deriv,ima,mask); return(tmp); } NEWMAT::ReturnMatrix splinefield::Jte(const std::vector& deriv, const NEWIMAGE::volume& ima, const NEWIMAGE::volume *mask) const { if (deriv.size() != 3) throw BasisfieldException("splinefield::Jte: Wrong size if deriv vector"); if (mask && !samesize(ima,*mask)) { throw BasisfieldException("splinefield::Jte: Image-Mask dimensionality mismatch"); } if (static_cast(ima.xsize()) != FieldSz_x() || static_cast(ima.ysize()) != FieldSz_y() || static_cast(ima.zsize()) != FieldSz_z()) { throw BasisfieldException("splinefield::Jte: Image-Field dimensionality mismatch"); } float *fima = new float[FieldSz()]; float *fiptr = fima; if (mask) { NEWIMAGE::volume::fast_const_iterator itm = mask->fbegin(); for (NEWIMAGE::volume::fast_const_iterator it=ima.fbegin(), it_end=ima.fend(); it!=it_end; ++it, ++itm, ++fiptr) { if (*itm) *fiptr = *it; else *fiptr = 0.0; } } else { for (NEWIMAGE::volume::fast_const_iterator it=ima.fbegin(), it_end=ima.fend(); it!=it_end; ++it, ++fiptr) *fiptr = *it; } Spline3D spline(_sp.Order(),_sp.KnotSpacing(),deriv); std::vector coefsz(3,0); coefsz[0] = CoefSz_x(); coefsz[1] = CoefSz_y(); coefsz[2] = CoefSz_z(); std::vector imasz(3,0); imasz[0] = FieldSz_x(); imasz[1] = FieldSz_y(); imasz[2] = FieldSz_z(); NEWMAT::ColumnVector ovec(CoefSz()); get_jte(spline,coefsz,fima,imasz,static_cast(ovec.Store())); delete[] fima; ovec.Release(); return(ovec); } boost::shared_ptr splinefield::JtJ(const NEWIMAGE::volume& ima, const NEWIMAGE::volume *mask, MISCMATHS::BFMatrixPrecisionType prec) const { std::vector deriv(3,0); boost::shared_ptr tmp = JtJ(deriv,ima,ima,mask,prec); return(tmp); } boost::shared_ptr splinefield::JtJ(const NEWIMAGE::volume& ima1, const NEWIMAGE::volume& ima2, const NEWIMAGE::volume *mask, MISCMATHS::BFMatrixPrecisionType prec) const { std::vector deriv(3,0); boost::shared_ptr tmp = JtJ(deriv,ima1,ima2,mask,prec); return(tmp); } boost::shared_ptr splinefield::JtJ(const std::vector& deriv, const NEWIMAGE::volume& ima, const NEWIMAGE::volume *mask, MISCMATHS::BFMatrixPrecisionType prec) const { boost::shared_ptr tmp = JtJ(deriv,ima,ima,mask,prec); return(tmp); } boost::shared_ptr splinefield::JtJ(const std::vector& deriv, const NEWIMAGE::volume& ima1, const NEWIMAGE::volume& ima2, const NEWIMAGE::volume *mask, MISCMATHS::BFMatrixPrecisionType prec) const { if (deriv.size() != 3) throw BasisfieldException("splinefield::JtJ: Wrong size derivative vector"); if (!samesize(ima1,ima2)) throw BasisfieldException("splinefield::JtJ: Image dimension mismatch"); if (mask && !samesize(ima1,*mask)) throw BasisfieldException("splinefield::JtJ: Mismatch between image and mask"); if (FieldSz_x() != static_cast(ima1.xsize()) || FieldSz_y() != static_cast(ima1.ysize()) || FieldSz_z() != static_cast(ima1.zsize())) throw BasisfieldException("splinefield::JtJ: Mismatch between image and field"); float *prodima = new float[FieldSz()]; hadamard(ima1,ima2,mask,prodima); std::vector isz(3,0); isz[0] = FieldSz_x(); isz[1] = FieldSz_y(); isz[2] = FieldSz_z(); std::vector csz(3,0); csz[0] = CoefSz_x(); csz[1] = CoefSz_y(); csz[2] = CoefSz_z(); boost::shared_ptr tmp; if (deriv[0]==0 && deriv[1]==0 && deriv[2]==0) { tmp = make_fully_symmetric_jtj(_sp,csz,prodima,isz,prec); } else { Spline3D sp(_sp.Order(),_sp.KnotSpacing(),deriv); tmp = make_fully_symmetric_jtj(sp,csz,prodima,isz,prec); } delete[] prodima; return(tmp); } boost::shared_ptr splinefield::JtJ(const std::vector& deriv1, const NEWIMAGE::volume& ima1, const std::vector& deriv2, const NEWIMAGE::volume& ima2, const NEWIMAGE::volume *mask, MISCMATHS::BFMatrixPrecisionType prec) const { if (deriv1.size() != 3 || deriv2.size() != 3) throw BasisfieldException("splinefield::JtJ: Wrong size derivative vector"); boost::shared_ptr tmp; if (deriv1 == deriv2) tmp = JtJ(deriv1,ima1,ima2,mask,prec); else { if (!samesize(ima1,ima2,true)) throw BasisfieldException("splinefield::JtJ: Image dimension mismatch"); if (mask && !samesize(ima1,*mask)) throw BasisfieldException("splinefield::JtJ: Mismatch between image and mask"); if (FieldSz_x() != static_cast(ima1.xsize()) || FieldSz_y() != static_cast(ima1.ysize()) || FieldSz_z() != static_cast(ima1.zsize())) throw BasisfieldException("splinefield::JtJ: Mismatch between image and field"); float *prodima = new float[FieldSz()]; hadamard(ima1,ima2,mask,prodima); std::vector isz(3,0); isz[0] = FieldSz_x(); isz[1] = FieldSz_y(); isz[2] = FieldSz_z(); std::vector csz(3,0); csz[0] = CoefSz_x(); csz[1] = CoefSz_y(); csz[2] = CoefSz_z(); Spline3D sp1(_sp.Order(),_sp.KnotSpacing(),deriv1); Spline3D sp2(_sp.Order(),_sp.KnotSpacing(),deriv2); tmp = make_asymmetric_jtj(sp1,csz,sp2,csz,prodima,isz,prec); delete[] prodima; } return(tmp); } boost::shared_ptr splinefield::JtJ(const NEWIMAGE::volume& ima1, const basisfield& bf2, // Spline that determines column in JtJ const NEWIMAGE::volume& ima2, const NEWIMAGE::volume *mask, MISCMATHS::BFMatrixPrecisionType prec) const { if (!samesize(ima1,ima2,true)) throw BasisfieldException("splinefield::JtJ: Image dimension mismatch"); if (mask && !samesize(ima1,*mask)) throw BasisfieldException("splinefield::JtJ: Mismatch between image and mask"); if (FieldSz_x() != static_cast(ima1.xsize()) || FieldSz_y() != static_cast(ima1.ysize()) || FieldSz_z() != static_cast(ima1.zsize())) throw BasisfieldException("splinefield::JtJ: Mismatch between image and field"); if (FieldSz_x() != bf2.FieldSz_x() || FieldSz_y() != bf2.FieldSz_y() || FieldSz_z() != FieldSz_z()) { throw BasisfieldException("splinefield::JtJ: Mismatch between fields"); } boost::shared_ptr tmp; try { const splinefield& tbf2 = dynamic_cast(bf2); float *prodima = new float[FieldSz()]; hadamard(ima1,ima2,mask,prodima); std::vector isz(3,0); isz[0] = FieldSz_x(); isz[1] = FieldSz_y(); isz[2] = FieldSz_z(); std::vector csz1(3,0); csz1[0] = CoefSz_x(); csz1[1] = CoefSz_y(); csz1[2] = CoefSz_z(); std::vector csz2(3,0); csz2[0] = tbf2.CoefSz_x(); csz2[1] = tbf2.CoefSz_y(); csz2[2] = tbf2.CoefSz_z(); tmp = make_asymmetric_jtj(_sp,csz1,tbf2._sp,csz2,prodima,isz,prec); delete[] prodima; } catch (bad_cast) { throw BasisfieldException("splinefield::JtJ: Must pass like to like field"); } return(tmp); } double splinefield::MemEnergy() const // Membrane energy of field { const boost::shared_ptr lcoef = GetCoef(); if (!lcoef) {throw BasisfieldException("splinefield::MemEnergy: No coefficients set yet");} NEWMAT::ColumnVector AtAb = 0.5 * MemEnergyGrad(); double me = DotProduct(*lcoef,AtAb); return(me); } double splinefield::BendEnergy() const // Bending energy of field { const boost::shared_ptr lcoef = GetCoef(); if (!lcoef) {throw BasisfieldException("splinefield::MemEnergy: No coefficients set yet");} NEWMAT::ColumnVector AtAb = 0.5 * BendEnergyGrad(); double be = DotProduct(*lcoef,AtAb); return(be); } NEWMAT::ReturnMatrix splinefield::MemEnergyGrad() const // Gradient of bending energy of field { const boost::shared_ptr lcoef = GetCoef(); if (!lcoef) {throw BasisfieldException("splinefield::MemEnergyGrad: No coefficients set yet");} std::vector csz(3,0); csz[0] = CoefSz_x(); csz[1] = CoefSz_y(); csz[2] = CoefSz_z(); std::vector isz(3,0); isz[0] = FieldSz_x(); isz[1] = FieldSz_y(); isz[2] = FieldSz_z(); std::vector ksp(3,0); ksp[0] = Ksp_x(); ksp[1] = Ksp_y(); ksp[2] = Ksp_z(); std::vector vxs(3,0); vxs[0] = Vxs_x(); vxs[1] = Vxs_y(); vxs[2] = Vxs_z(); NEWMAT::ColumnVector grad(CoefSz()); calculate_memen_AtAb(*lcoef,ksp,isz,vxs,csz,Order(),grad); grad *= 2.0; grad.Release(); return(grad); } NEWMAT::ReturnMatrix splinefield::BendEnergyGrad() const // Gradient of bending energy of field { const boost::shared_ptr lcoef = GetCoef(); if (!lcoef) {throw BasisfieldException("splinefield::BendEnergyGrad: No coefficients set yet");} std::vector csz(3,0); csz[0] = CoefSz_x(); csz[1] = CoefSz_y(); csz[2] = CoefSz_z(); std::vector isz(3,0); isz[0] = FieldSz_x(); isz[1] = FieldSz_y(); isz[2] = FieldSz_z(); std::vector ksp(3,0); ksp[0] = Ksp_x(); ksp[1] = Ksp_y(); ksp[2] = Ksp_z(); std::vector vxs(3,0); vxs[0] = Vxs_x(); vxs[1] = Vxs_y(); vxs[2] = Vxs_z(); NEWMAT::ColumnVector grad(CoefSz()); calculate_bender_AtAb(*lcoef,ksp,isz,vxs,csz,Order(),grad); grad *= 2.0; grad.Release(); return(grad); } boost::shared_ptr splinefield::MemEnergyHess(MISCMATHS::BFMatrixPrecisionType prec) const // Hessian of membrane energy { std::vector lksp(3,0); lksp[0] = Ksp_x(); lksp[1] = Ksp_y(); lksp[2] = Ksp_z(); std::vector csz(3,0); csz[0] = CoefSz_x(); csz[1] = CoefSz_y(); csz[2] = CoefSz_z(); std::vector isz(3,0); isz[0] = FieldSz_x(); isz[1] = FieldSz_y(); isz[2] = FieldSz_z(); boost::shared_ptr H = calculate_memen_bender_H(lksp,csz,isz,MemEn,prec); return(H); } boost::shared_ptr splinefield::BendEnergyHess(MISCMATHS::BFMatrixPrecisionType prec) const // Hessian of bending energy { std::vector lksp(3,0); lksp[0] = Ksp_x(); lksp[1] = Ksp_y(); lksp[2] = Ksp_z(); std::vector csz(3,0); csz[0] = CoefSz_x(); csz[1] = CoefSz_y(); csz[2] = CoefSz_z(); std::vector isz(3,0); isz[0] = FieldSz_x(); isz[1] = FieldSz_y(); isz[2] = FieldSz_z(); boost::shared_ptr H = calculate_memen_bender_H(lksp,csz,isz,BendEn,prec); return(H); } void splinefield::get_field(const Spline3D& sp, const double *coef, const std::vector& csz, const std::vector& fsz, double *fld) const { std::vector ff(3,0); // First index into field std::vector lf(3,0); // Last index into field std::vector os(3,0); // Offset into spline std::vector ci(3,0); // Coefficient/spline index std::vector ks(3,0); // Kernel size for (int i=0; i<3; i++) ks[i]=sp.KernelSize(i); memset(fld,0,fsz[0]*fsz[1]*fsz[2]*sizeof(double)); for (ci[2]=0; ci[2] void splinefield::get_jte(const Spline3D& sp, const std::vector& csz, const T *ima, const std::vector& isz, double *jte) const { std::vector fi(3,0); // First index into images std::vector li(3,0); // Last index into images std::vector os(3,0); // Offset into spline std::vector ci(3,0); // Coefficient/spline index std::vector ks(3,0); // Kernel size for (int i=0; i<3; i++) ks[i]=sp.KernelSize(i); memset(jte,0,csz[0]*csz[1]*csz[2]*sizeof(double)); for (ci[2]=0; ci[2](ima[ibi+ii]); } } } } } } } ///////////////////////////////////////////////////////////////////// // // This routines calculates A'*B where A is an nxm matrix where n is the // number of voxels in ima and m is the number of splines of one kind // and B is an nxl matrix where l is the number of splines of a different // kind. // // The splines may e.g. be a spline of some ksp in A and the same // ksp spline differentiated in one direction in B. This can then be // used for modelling effects of Jacobian modulation in distortion // correction. In this first case jtj is still square, though not // symmetrical. // // The other possibility is that A has splines of ksp1 modelling e.g. // displacements and B has splines of ksp2 modelling e.g. an intensity // bias field. In this case jtj is no longer square. // // This routine does not utilise symmetries/repeated values at any // level. For the second case above there is nothing to utilise (as // far as I can tell). For the former case there are complicated // patterns of repeated values that it should be possible to utilise // in order to speed things up. Future improvments. // ///////////////////////////////////////////////////////////////////// template boost::shared_ptr splinefield::make_asymmetric_jtj(const Spline3D& rsp, // Spline that determines row in JtJ const std::vector& cszr, // Coefficient matrix size for rsp const Spline3D& sp2, // Spline that determines column in JtJ const std::vector& cszc, // Coefficient matrix size for csp/sp2 const T *ima, // Image (typically product of two images) const std::vector& isz, // Matrix size of image MISCMATHS::BFMatrixPrecisionType prec) // Precision (float/double) of resulting matrix const { Spline3D csp(sp2); // Read-write copy of spline that determines column std::vector cindx(3,0); // Index of spline that determines column in JtJ std::vector rindx(3,0); // Index of spline that determines row in JtJ std::vector fo(3,0); // First index of overlapping spline in x-, y- and z-direction std::vector lo(3,0); // Last index of overlapping spline in x-, y- and z-direction unsigned int m = cszr[2]*cszr[1]*cszr[0]; // # of rows in JtJ unsigned int n = cszc[2]*cszc[1]*cszc[0]; // # of columns in JtJ unsigned int nnz = rsp.NzMax(isz,csp); // Max # of non-zero elements unsigned int *irp = new unsigned int[nnz]; // Row indices unsigned int *jcp = new unsigned int[n+1]; // Indicies into irp indicating start/stop of columns double *valp = new double[nnz]; // The values of the matrix unsigned int vali = 0; // Index of present non-zero value (linear indexing) unsigned int ci = 0; // Column index ZeroSplineMap r_zeromap(rsp,cszr,ima,isz); ZeroSplineMap c_zeromap(csp,cszc,ima,isz); // Same Kernel Size? bool sks = (rsp.KernelSize(0) == csp.KernelSize(0) && rsp.KernelSize(1) == csp.KernelSize(1) && rsp.KernelSize(2) == csp.KernelSize(2)); for (cindx[2]=0; cindx[2] jtj; if (prec==BFMatrixFloatPrecision) jtj = boost::shared_ptr(new SparseBFMatrix(m,n,irp,jcp,valp)); else jtj = boost::shared_ptr(new SparseBFMatrix(m,n,irp,jcp,valp)); delete [] irp; delete [] jcp; delete [] valp; return(jtj); } ///////////////////////////////////////////////////////////////////// // // This routines calculates A'*A where A is an nxm matrix where n is the // number of voxels in ima and m is the number of splines. Each column // of A is a spline elementwise multiplied by by the image. A is // typically too large to be represented, each column being the size // of the image volume and there typicall being tens of thousands of // columns. Therefore only A'*A is explicitly represented, and even that // as a sparse matrix. // The routine looks a little complicated. If not using symmetry it is // actually quite straightforward. However, only ~1/8 of the elements // are unique due to there being three levels of symmetry. In order to // maximise efficiency I have utilised this symmetry, which sadly leads // to lots of book keeping. // ///////////////////////////////////////////////////////////////////// template boost::shared_ptr splinefield::make_fully_symmetric_jtj(const Spline3D& sp2, const std::vector& csz, const T *ima, const std::vector& isz, MISCMATHS::BFMatrixPrecisionType prec) const { Spline3D sp1(sp2); // Another copy of spline std::vector indx1(3,0); // Index of spline that determines column in JtJ std::vector indx2(3,0); // Index of spline that determines row in JtJ std::vector fo(3,0); // First index of overlapping spline in x-, y- and z-direction std::vector lo(3,0); // Last index of overlapping spline in x-, y- and z-direction unsigned int ncoef = csz[2]*csz[1]*csz[0]; // Size of JtJ unsigned int nnz = sp1.NzMax(isz); // Max # of non-zero elements unsigned int *irp = new unsigned int[nnz]; // Row indicies unsigned int *jcp = new unsigned int[ncoef+1]; // Indicies into irp indicating start/stop of columns double *valp = new double[nnz]; // The values of the matrix unsigned int vali = 0; // Index of present non-zero value (linear indexing) unsigned int ci = 0; // Column index ZeroSplineMap zeromap(sp1,csz,ima,isz); for (indx1[2]=0; indx1[2] jtj; if (prec==BFMatrixFloatPrecision) jtj = boost::shared_ptr(new SparseBFMatrix(ncoef,ncoef,irp,jcp,valp)); else jtj = boost::shared_ptr(new SparseBFMatrix(ncoef,ncoef,irp,jcp,valp)); delete [] irp; delete [] jcp; delete [] valp; return(jtj); } ///////////////////////////////////////////////////////////////////// // // Helper routine to find the value for a given row-index in a // (possibly unfinished) compressed column storage format. Uses // bisection. // ///////////////////////////////////////////////////////////////////// double splinefield::get_val(unsigned int row, // The row we want to find the value of unsigned int col, // The column we want to find the value of const unsigned int *irp, // Array of row-indicies const unsigned int *jcp, // Array of indicies into irp const double *val) // Array of values sorted as irp const { const unsigned int *a = &(irp[jcp[col]]); const double *v = &(val[jcp[col]]); int n = jcp[col+1]-jcp[col]; int j = 0; int jlo = -1; int jup = n; if (row < a[0] || row > a[n-1]) {return(0.0);} while (jup-jlo > 1) { j = (jlo+jup) >> 1; if (row >= a[j]) {jlo = j;} else {jup = j;} } if (a[jlo] == row) {return(v[jlo]);} else return(0.0); } // // Calculates 0.5 times the gradient of membrane energy // void splinefield::calculate_memen_AtAb(const NEWMAT::ColumnVector& b, const std::vector& lksp, const std::vector& isz, const std::vector& vxs, const std::vector& csz, unsigned int sp_ord, NEWMAT::ColumnVector& grad) const { // Get helper that is the sum of all the component // helpers (dd/dx + dd/dy + dd/dy) std::vector deriv(3,0); deriv[0] = 1; Spline3D sp1(sp_ord,lksp,deriv); Memen_H_Helper sum_hlpr(sp1); for (unsigned int d=1; d<3; d++) { deriv[d-1]=0; deriv[d]=1; Spline3D sp2(sp_ord,lksp,deriv); Memen_H_Helper hlpr(sp2); sum_hlpr += hlpr; } calculate_AtAb(b,isz,csz,sp1,sum_hlpr,grad); } // // Calculates 0.5 times the gradient of bending energy // void splinefield::calculate_bender_AtAb(const NEWMAT::ColumnVector& b, const std::vector& lksp, const std::vector& isz, const std::vector& vxs, const std::vector& csz, unsigned int sp_ord, NEWMAT::ColumnVector& grad) const { // Get helper that is the sum of all the component // helpers (d2d/dx2 + d2d/dy2 + d2d/dz2 + 2*d2d/dxdy + ... Spline3D sp(sp_ord,lksp); Memen_H_Helper sum_hlpr(sp); sum_hlpr *= 0.0; for (unsigned int d1=0; d1<3; d1++) { for (unsigned int d2=d1; d2<3; d2++) { std::vector deriv(3,0); deriv[d1]++; deriv[d2]++; Spline3D spd(sp_ord,lksp,deriv); // Spline differentiated in 2 directions spd /= (vxs[d1]*vxs[d2]); // Derivative in mm^{-1} Memen_H_Helper hlpr(spd); if (d1 != d2) hlpr *= 2.0; sum_hlpr += hlpr; } } // Use the helper to calculate (A_1^T*A_1 + A_2^T*A_2 ...)*b // where A_1 is a matrix with translated d2d/dx2 kernels etc calculate_AtAb(b,isz,csz,sp,sum_hlpr,grad); } // // Heart of the calculation of the // gradient of membrane/bending-energy // void splinefield::calculate_AtAb(const NEWMAT::ColumnVector& b, const std::vector& isz, const std::vector& csz, const Spline3D& sp, const Memen_H_Helper& hlpr, NEWMAT::ColumnVector& grad) const { grad = 0; double *db=static_cast(b.Store()); // double[] representation of coefficients double *dg=static_cast(grad.Store()); // double[] representation of gradient std::vector ci(3,0); // Index of coefficient std::vector first(3,0); // index of first overlapping coefficient std::vector last(3,0); // Index of last overlapping coefficient unsigned int li=0; // Linear index of coefficient for (ci[2]=0; ci[2] splinefield::calculate_memen_bender_H(const std::vector& lksp, const std::vector& csz, const std::vector& isz, EnergyType et, MISCMATHS::BFMatrixPrecisionType prec) const { // Get Helpers with values for all possible overlaps. // For Membrane energy we need 3 helpers, and for // bending energy we need 6. double vxs[] = {Vxs_x(), Vxs_y(), Vxs_z()}; boost::shared_ptr helpers[6]; // Always room for 6 helpers unsigned int nh = 0; // Number of helpers if (et == MemEn) { for (unsigned int d=0; d<3; d++) { std::vector deriv(3,0); deriv[d] = 1; Spline3D spd(_sp.Order(),lksp,deriv); // Spline differentiated in one direction spd /= vxs[d]; // Derivative in mm^{-1} helpers[nh] = boost::shared_ptr(new Memen_H_Helper(spd)); *(helpers[nh++]) *= 2.0; // To get factor of 2 of entire matrix. } } else if (et == BendEn) { for (unsigned int d1=0; d1<3; d1++) { for (unsigned int d2=d1; d2<3; d2++) { std::vector deriv(3,0); deriv[d1]++; deriv[d2]++; Spline3D spd(_sp.Order(),lksp,deriv); // Spline twice differentiated spd /= (vxs[d1]*vxs[d2]); // Derivative in mm^{-1} helpers[nh] = boost::shared_ptr(new Memen_H_Helper(spd)); if (d1 == d2) *(helpers[nh++]) *= 2.0; // To get factor of 2 of entire matrix else *(helpers[nh++]) *= 4.0; // Count cross-terms twice } } } // Build compressed column storage representation of H std::vector cindx(3,0); // Index of spline that determines column in JtJ std::vector indx2(3,0); // Index of spline that determines row in JtJ std::vector fo(3,0); // First index of overlapping spline in x-, y- and z-direction std::vector lo(3,0); // Last index of overlapping spline in x-, y- and z-direction Spline3D sp(_sp.Order(),lksp); unsigned int ncoef = csz[2]*csz[1]*csz[0]; // Size of JtJ unsigned int nnz = sp.NzMax(isz); // Max # of non-zero elements unsigned int *irp = new unsigned int[nnz]; // Row indicies unsigned int *jcp = new unsigned int[ncoef+1]; // Indicies into irp indicating start/stop of columns double *valp = new double[nnz]; // The values of the matrix unsigned int vali = 0; // Index of present non-zero value (linear indexing) unsigned int ci = 0; // Column index for (cindx[2]=0; cindx[2]Peek(i-cindx[0],j-cindx[1],k-cindx[2]); } vali++; } } } } } } jcp[ci+1] = vali; boost::shared_ptr H; if (prec==BFMatrixFloatPrecision) H = boost::shared_ptr(new SparseBFMatrix(ncoef,ncoef,irp,jcp,valp)); else H = boost::shared_ptr(new SparseBFMatrix(ncoef,ncoef,irp,jcp,valp)); delete [] irp; delete [] jcp; delete [] valp; return(H); } void splinefield::hadamard(const NEWIMAGE::volume& ima1, const NEWIMAGE::volume& ima2, float *prod) const { if (!samesize(ima1,ima2,true)) throw BasisfieldException("hadamard: Image dimension mismatch"); for (NEWIMAGE::volume::fast_const_iterator it1=ima1.fbegin(), it2=ima2.fbegin(), it1_end=ima1.fend(); it1 != it1_end; ++it1, ++it2, ++prod) { *prod = (*it1) * (*it2); } } void splinefield::hadamard(const NEWIMAGE::volume& ima1, const NEWIMAGE::volume& ima2, const NEWIMAGE::volume& mask, float *prod) const { if (!samesize(ima1,ima2,true) || !samesize(ima1,mask)) throw BasisfieldException("hadamard: Image dimension mismatch"); NEWIMAGE::volume::fast_const_iterator itm = mask.fbegin(); for (NEWIMAGE::volume::fast_const_iterator it1=ima1.fbegin(), it2=ima2.fbegin(), it1_end=ima1.fend(); it1 != it1_end; ++it1, ++it2, ++itm, ++prod) { *prod = static_cast(*itm) * (*it1) * (*it2); } } void splinefield::hadamard(const NEWIMAGE::volume& ima1, const NEWIMAGE::volume& ima2, const NEWIMAGE::volume *mask, float *prod) const { if (mask) hadamard(ima1,ima2,*mask,prod); else hadamard(ima1,ima2,prod); } void splinefield::Set(const NEWIMAGE::volume& pfield) { if (int(FieldSz_x()) != pfield.xsize() || int(FieldSz_y()) != pfield.ysize() || int(FieldSz_z()) != pfield.zsize()) { throw BasisfieldException("basisfield::Set:: Matrix size mismatch beween basisfield class and supplied field"); } if (Vxs_x() != pfield.xdim() || Vxs_y() != pfield.ydim() || Vxs_z() != pfield.zdim()) { throw BasisfieldException("basisfield::Set:: Voxel size mismatch beween basisfield class and supplied field"); } helper_vol orig(pfield); helper_vol conv = get_coefs_one_dim(orig,0,CoefSz_x(),Order(),Ksp_x()); if (NDim() > 1) conv = get_coefs_one_dim(conv,1,CoefSz_y(),Order(),Ksp_y()); if (NDim() > 2) conv = get_coefs_one_dim(conv,2,CoefSz_z(),Order(),Ksp_z()); NEWMAT::ColumnVector lcoef = conv.AsNewmatVector(); SetCoef(lcoef); return; } //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // The following is a set of routines that are used for zooming // fields, and for deciding which zooms are valid and which are // not. These will be almost excessively commented. The reason // for that is that I have struggled so badly to get things clear // in my own head, and I don't want to return in 6 months not // understanding the code. // //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ///////////////////////////////////////////////////////////////////// // // The following is the "main" zooming routine. It will return a // completely new field, with new matrix size and/or voxel size // and/or knot-spacing. The new field will have coefficients set // so that the field is identical to the initial field (in the // case of upsampling) or the "best field in a least squares sense" // (in the case of downsampling). // ///////////////////////////////////////////////////////////////////// boost::shared_ptr splinefield::ZoomField(const std::vector& nms, const std::vector& nvxs, std::vector nksp) const { std::vector oms(3), oksp(3); std::vector ovxs(3); oms[0] = FieldSz_x(); oms[1] = FieldSz_y(); oms[2] = FieldSz_z(); oksp[0] = Ksp_x(); oksp[1] = Ksp_y(); oksp[2] = Ksp_z(); ovxs[0] = Vxs_x(); ovxs[1] = Vxs_y(); ovxs[2] = Vxs_z(); if (!nksp.size()) nksp = oksp; // cout << "Old matrix size: " << oms[0] << " " << oms[1] << " " << oms[2] << endl; // cout << "new matrix size: " << nms[0] << " " << nms[1] << " " << nms[2] << endl; // cout << "Old voxel size: " << ovxs[0] << " " << ovxs[1] << " " << ovxs[2] << endl; // cout << "New voxel size: " << nvxs[0] << " " << nvxs[1] << " " << nvxs[2] << endl; // cout << "Old knot-spacing: " << oksp[0] << " " << oksp[1] << " " << oksp[2] << endl; // cout << "New knot-spacing: " << nksp[0] << " " << nksp[1] << " " << nksp[2] << endl; // Check that new field is kosher // Make sure that we are not asked to change both voxel-size and knot-spacing if (nksp != oksp && nvxs != ovxs) throw BasisfieldException("ZoomField: Cannot change both voxel-size and knot-spacing"); // If voxel-size changed, make sure that the new voxel-size allows us to estimate the new coefficients if (nvxs != ovxs && !new_vxs_is_ok(nvxs)) throw BasisfieldException("ZoomField: The requested voxel-size is invalid"); // If voxel size changed, fake an old knot-spacing for the purpose of estimating new coefficients // This will always work when going from low->higher resolution (in which case the fake knot-spacing // will be some integer multiple of the old knot-spacing). It will not always work when going from // high->lower resolution since we may then get a fake knot-spacing that should really be a // non-integer #, which or present implementation cannot handle. This is really indicative of a // flawed implementation, but for the time being I will just work around it. std::vector fksp = oksp; if (nvxs != ovxs) { if (faking_works(nvxs,nksp,ovxs)) { fksp = fake_old_ksp(nvxs,nksp,ovxs); } else { return(zoom_field_in_stupid_way(nms,nvxs,nksp)); } } // cout << "fksp[0] = " << fksp[0] << ", fksp[1] = " << fksp[1] << ", fksp[2] = " << fksp[2] << endl; // Create the new field boost::shared_ptr tptr(new BASISFIELD::splinefield(nms,nvxs,nksp,this->Order())); // Get original set of coefficients const boost::shared_ptr ocoef = GetCoef(); // If not all coefficients zero, create the coefficients for the // new field from those of the old field NEWMAT::ColumnVector zerovec(ocoef->Nrows()); zerovec = 0.0; if (*ocoef != zerovec) { // Repack coefficient sizes of new and old field (for convenience) std::vector ocs(3); ocs[0]=CoefSz_x(); ocs[1]=CoefSz_y(); ocs[2]=CoefSz_z(); std::vector ncs(3); ncs[0]=tptr->CoefSz_x(); ncs[1]=tptr->CoefSz_y(); ncs[2]=tptr->CoefSz_z(); // Resample x-direction BASISFIELD::Spline1D osp(3,fksp[0]); // Spline object for old spline BASISFIELD::Spline1D nsp(3,nksp[0]); // Spline object for new spline NEWMAT::Matrix M = nsp.GetMMatrix(osp,nms[0],ncs[0],ocs[0]); // Resampling matrix double *tmp_coef_x = new double[ncs[0]*ocs[1]*ocs[2]]; // Temporary coefficient matrix NEWMAT::ColumnVector iv(ocs[0]); // Vector holding one "column" running in x-direction NEWMAT::ColumnVector ov(ncs[0]); // Dito, after resampling for (unsigned int k=0; kelement(k*ocs[0]*ocs[1]+j*ocs[0]+i); // Collect old column } ov = M*iv; // Calculate new column for (unsigned int i=0; i(3,fksp[1]); // Spline object for old spline nsp = BASISFIELD::Spline1D(3,nksp[1]); // Spline object for new spline M = nsp.GetMMatrix(osp,nms[1],ncs[1],ocs[1]); // Resampling matrix double *tmp_coef_y = new double[ncs[0]*ncs[1]*ocs[2]]; // Temporary coefficient matrix iv.ReSize(ocs[1]); // Vector holding one "column" running in y-direction ov.ReSize(ncs[1]); // Dito, after resampling for (unsigned int k=0; k(3,fksp[2]); // Spline object for old spline nsp = BASISFIELD::Spline1D(3,nksp[2]); // Spline object for new spline M = nsp.GetMMatrix(osp,nms[2],ncs[2],ocs[2]); // Resampling matrix NEWMAT::ColumnVector ncoef(ncs[0]*ncs[1]*ncs[2]); // Temporary coefficient matrix, now as ColumnVector iv.ReSize(ocs[2]); // Vector holding one "column" running in z-direction ov.ReSize(ncs[2]); // Dito, after resampling for (unsigned int j=0; jSetCoef(ncoef); } return(tptr); } /* void splinefield::SetToConstant(double fv) { NEWMAT::ColumnVector lcoef(CoefSz()); lcoef = fv; SetCoef(lcoef); } */ void splinefield::SetToConstant(double fv) { vector sz(3,0); sz[0] = FieldSz_x(); sz[1] = FieldSz_y(); sz[2] = FieldSz_z(); vector vxs(3,0.0); vxs[0] = Vxs_x(); vxs[1] = Vxs_y(); vxs[2] = Vxs_z(); NEWIMAGE::volume vol(sz[0],sz[1],sz[2]); vol.setdims(vxs[0],vxs[1],vxs[2]); vol = fv; Set(vol); } ///////////////////////////////////////////////////////////////////// // // Returns the new matrix size for a given level of subsampling // provided that this is a power of two. It is done by calling // "next_size_down" recursively so that at each subsequent upsampling // step the previous field should have a slightly larger FOV than // the new one. This guarantees that as we go to higher resolutions // we will not be in a position of having to extrapolate from a // previous resolution. // // The "power of 2 constraint" is not strictly necessary, and the // routines for doing the actual zooming has less severe constraints. // However, I have decided my life is a bit easier if I enforce this // constraint at this level. // ///////////////////////////////////////////////////////////////////// std::vector splinefield::SubsampledMatrixSize(const std::vector& ss, // Subsampling factor std::vector oms) const // Old Matrix Size { std::vector nms; // New matrix size if (!oms.size()) {nms.resize(NDim()); nms[0]=FieldSz_x(); if (NDim()>1) nms[1]=FieldSz_y(); if (NDim()>2) nms[2]=FieldSz_z();} else nms = oms; if (nms.size() != ss.size()) throw BasisfieldException("splinefield::SubsampledMatrixSize: Size mismatch between ss and oms"); for (unsigned int i=0; i 1) { oms = next_size_down(oms); ss /= 2; } return(oms); } ///////////////////////////////////////////////////////////////////// // // Returns the new voxel-size for a given level of subsampling. // For splinefields this is simply the old voxel-size divided by // the subsampling factor, guaranteeing that every voxel centre // in the original (low res) field is represented by a voxel centre // in the new field. // For DCT-fields it is a little less straightforward, which is // the reason we have declared the routine in the way it is. // ///////////////////////////////////////////////////////////////////// std::vector splinefield::SubsampledVoxelSize(const std::vector& ss, // Subsampling factor std::vector ovxs, // Old voxel size std::vector ms) const // Matrix size { std::vector nvxs; if (!ovxs.size()) {nvxs.resize(NDim()); nvxs[0]=Vxs_x(); if (NDim()>1) nvxs[1]=Vxs_y(); if (NDim()>2) nvxs[2]=Vxs_z();} else nvxs = ovxs; if (ovxs.size() != ss.size()) throw BasisfieldException("splinefield::SubsampledVoxelSize: Size mismatch between ss and ovxs"); for (unsigned int i=0; i splinefield::next_size_down(const std::vector& isize) const { std::vector osize(isize.size(),0); for (int i=0; i= 1) return(is_a_power_of_2(double(fac))); else return(false); } bool splinefield::are_a_power_of_2(const std::vector& facs) const { bool retval = true; for (unsigned int i=0; i& facs) const { bool retval = true; for (int unsigned i=0; i& nvxs, std::vector ovxs) const { if (!ovxs.size()) {ovxs.resize(3); ovxs[0]=Vxs_x(); ovxs[1]=Vxs_y(); ovxs[2]=Vxs_z();} if (ovxs.size() != nvxs.size()) throw BasisfieldException("splinefield::new_vxs_is_ok: size mismatch between nvxs and ovxs"); for (unsigned int i=0; i1; i--) if (fabs((1.0/double(i))-(nvxs/ovxs)) < eps) return(true); } else if (fabs((nvxs/ovxs)-1.0) < eps) return(true); else { for (int i=2; i<33; i++) if (fabs(double(i)-(nvxs/ovxs)) < eps) return(true); } return(false); } ///////////////////////////////////////////////////////////////////// // // These routines will provide a fake knot-spacing. When talking // about "zooming" fields we may consider going from one voxel-size // to another, or we may think of changing our parametrisation from // one knot-spacing to another. In our main routine we treat these // cases in an equivalent way by "transforming" the case where we // go from one voxel size to another to a case where we change // knot-spacing. For example if we have a knot-spacing of 3voxels // and a voxel-size of 4mm and want to go to a knot-spacing of // 3voxels for a 2mm we will "pretend" that we are in fact going // from a knot-spacing of 6voxels to 3voxels in a 2mm voxel matrix. // ///////////////////////////////////////////////////////////////////// std::vector splinefield::fake_old_ksp(const std::vector& nvxs, const std::vector& nksp, std::vector ovxs) const { if (!ovxs.size()) {ovxs.resize(NDim()); ovxs[0]=Vxs_x(); if (NDim()>1) ovxs[1]=Vxs_y(); if (NDim()>2) ovxs[2]=Vxs_z();} if (ovxs.size()!=nvxs.size() || ovxs.size()!=nksp.size()) throw BasisfieldException("splinefield::fake_old_ksp: size mismatch between nvxs, ovxs and nksp"); std::vector fksp(ovxs.size()); for (unsigned int i=0; i(MISCMATHS::round((ovxs/nvxs)*double(nksp)))); } bool splinefield::faking_works(const std::vector& nvxs, const std::vector& nksp, std::vector ovxs) const { if (!ovxs.size()) {ovxs.resize(NDim()); ovxs[0]=Vxs_x(); if (NDim()>1) ovxs[1]=Vxs_y(); if (NDim()>2) ovxs[2]=Vxs_z();} for (unsigned int i=0; i(roundl(dres)); if (fabs(double(ires)-dres) > 1e-6) return(false); return(true); } boost::shared_ptr splinefield::zoom_field_in_stupid_way(const std::vector& nms, const std::vector& nvxs, const std::vector& nksp) const { // Create new field boost::shared_ptr tptr(new BASISFIELD::splinefield(nms,nvxs,nksp,this->Order())); // See if we have non-zero field that we need to resample. const boost::shared_ptr ocoef = GetCoef(); NEWMAT::ColumnVector zerovec(ocoef->Nrows()); zerovec = 0.0; if (*ocoef == zerovec) return(tptr); // Get image representation of current field in resolution of new field NEWIMAGE::volume nvol(nms[0],nms[1],nms[2]); nvol.setdims(nvxs[0],nvxs[1],nvxs[2]); double z=0.0; for (unsigned int k=0; kSet(nvol); return(tptr); } helper_vol splinefield::get_coefs_one_dim(const helper_vol& in, unsigned int dim, unsigned int csz, unsigned int order, unsigned int ksp) const { std::vector nsz(3,0); for (unsigned int i=0; i<3; i++) {if (i==dim) nsz[i]=csz; else nsz[i]=in.Size(i); } helper_vol out(nsz); unsigned int ii, jj; switch (dim) { case 0: ii=in.Size(1); jj=in.Size(2); break; case 1: ii=in.Size(0); jj=in.Size(2); break; case 2: ii=in.Size(0); jj=in.Size(1); break; default: throw ; } Spline1D sp(order,ksp); NEWMAT::Matrix A = sp.GetAMatrix(in.Size(dim),csz); NEWMAT::Matrix S = get_s_matrix(in.Size(dim),csz); NEWMAT::Matrix AS = A & 0.005*S; NEWMAT::Matrix M = (AS.t()*AS).i()*A.t(); NEWMAT::ColumnVector y(in.Size(dim)); for (unsigned int i=0; i(y.Store())); NEWMAT::ColumnVector b = M*y; out.SetColumn(i,j,dim,static_cast(b.Store())); } } return(out); } NEWMAT::ReturnMatrix splinefield::get_s_matrix(unsigned int isz, unsigned int csz) const { NEWMAT::Matrix S(isz,csz); S = 0.0; S(1,1) = 2.0; S(1,2) = -1.0; S(1,csz) = -1.0; S(isz,1) = -1.0; S(isz,csz-1) = -1.0; S(isz,csz) = 2.0; S.Release(); return(S); } // This routine will return a value for the field (or a derivative of the field) // from outside the "valid" FOV. Since the splines actually extend a bit outside // the FOV there will be a gradual taper off to zero. double splinefield::peek_outside_fov(int i, int j, int k, FieldIndex fi) const { std::vector vox(3,0.0); vox[0] = static_cast(i); vox[1] = static_cast(j); vox[2] = static_cast(k); std::vector csz(3,0); csz[0] = CoefSz_x(); csz[1] = CoefSz_y(); csz[2] = CoefSz_z(); std::vector cindx(3,0); std::vector first(3,0), last(3,0); double rval = 0.0; if (fi == FIELD) { _sp.RangeOfSplines(vox,csz,first,last); // cout << "vox = " << vox[0] << ", " << vox[1] << ", " << vox[2] << endl; // cout << "csz = " << csz[0] << ", " << csz[1] << ", " << csz[2] << endl; // cout << "first = " << first[0] << ", " << first[1] << ", " << first[2] << endl; // cout << "last = " << last[0] << ", " << last[1] << ", " << last[2] << endl; for (cindx[2]=first[2]; cindx[2]RangeOfSplines(vox,csz,first,last); for (cindx[2]=first[2]; cindx[2]SplineValueAtVoxel(vox,cindx); } } } } return(rval); } ///////////////////////////////////////////////////////////////////// // // Member-functions for the ZeroSplineMap class. The class will // keep track of splines for which the/an image is zero for all // of their support, thereby avoiding unneccessary calculations. // ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// // // Member-functions for the Memen_H_Helper class. The idea behind // the class is that each column of H contains the same values // spaced out in a particular patter (though some values might be // "shifted out" of the volume and may be missing for a given // column). A Memen_H_Helper object will calculate all unique values // on construction and then one can use one of the access function // (operator()(i,j,k) or Peek(i,j,k)) to populate H with this values. // ///////////////////////////////////////////////////////////////////// Memen_H_Helper::Memen_H_Helper(const Spline3D& sp) : _sz(3,0), _cntr(3,0), _data(NULL) { // Fake a "really large" FOV std::vector isz(3,0); for (unsigned int i=0; i<3; i++) isz[i] = 1000*sp.KnotSpacing(i); // Pick an index somewhere in the centre std::vector cindx(3,0); for (unsigned int i=0; i<3; i++) cindx[i] = sp.NCoef(i,isz[i]) / 2; // Get indices of overlapping splines std::vector first(3,0), last(3,0); if (!sp.RangeOfOverlappingSplines(cindx,isz,first,last)) throw BasisfieldException("Memen_H_Helper::Memen_H_Helper: No overlapping splines"); _sz[0] = last[0]-first[0]; _sz[1] = last[1]-first[1]; _sz[2] = last[2]-first[2]; _cntr[0] = cindx[0]-first[0]; _cntr[1] = cindx[1]-first[1]; _cntr[2] = cindx[2]-first[2]; _data = new double[_sz[0]*_sz[1]*_sz[2]]; // Get values for all "overlaps" in "all positive" 1/8 std::vector cindx2(3,0); for (unsigned int ck=first[2]; ck= _sz[0] || j+_cntr[1] < 0 || j+_cntr[1] >= _sz[1] || k+_cntr[2] < 0 || k+_cntr[2] >= _sz[2]) { throw BasisfieldException("Memen_H_Helper::operator(): Index out of range"); } return(Peek(i,j,k)); } ///////////////////////////////////////////////////////////////////// // // Member-functions for the helper_vol class. It is a little helper // class that is used when deconvolving a field to obtain the spline // coefficients. // ///////////////////////////////////////////////////////////////////// helper_vol::helper_vol(const std::vector& sz) { if (sz.size()!=3) throw BasisfieldException("helper_vol::helper_vol: s must have 3 elements"); else { _sz[0]=sz[0]; _sz[1]=sz[1]; _sz[2]=sz[2]; _data = new double[_sz[0]*_sz[1]*_sz[2]]; } } helper_vol::helper_vol(const std::vector& sz, const NEWMAT::ColumnVector& vec) { if (sz.size()!=3) throw BasisfieldException("helper_vol::helper_vol: s must have 3 elements"); else { _sz[0]=sz[0]; _sz[1]=sz[1]; _sz[2]=sz[2]; _data = new double[_sz[0]*_sz[1]*_sz[2]]; double *dp = static_cast(vec.Store()); memcpy(_data,dp,_sz[0]*_sz[1]*_sz[2]*sizeof(double)); } } helper_vol::helper_vol(const NEWIMAGE::volume& vol) { _sz[0]=vol.xsize(); _sz[1]=vol.ysize(); _sz[2]=vol.zsize(); _data = new double[_sz[0]*_sz[1]*_sz[2]]; double *trgt = _data; for (NEWIMAGE::volume::fast_const_iterator it=vol.fbegin(), it_end=vol.fend(); it!=it_end; it++, trgt++) { *trgt = static_cast(*it); } } helper_vol::helper_vol(const helper_vol& in) { _sz[0]=in._sz[0]; _sz[1]=in._sz[1]; _sz[2]=in._sz[2]; _data = new double[_sz[0]*_sz[1]*_sz[2]]; memcpy(_data,in._data,_sz[0]*_sz[1]*_sz[2]*sizeof(double)); } helper_vol& helper_vol::operator=(const helper_vol& rhs) { if (this == &rhs) return(*this); _sz[0]=rhs._sz[0]; _sz[1]=rhs._sz[1]; _sz[2]=rhs._sz[2]; if (_data) delete [] _data; _data = new double[_sz[0]*_sz[1]*_sz[2]]; memcpy(_data,rhs._data,_sz[0]*_sz[1]*_sz[2]*sizeof(double)); return(*this); } NEWMAT::ReturnMatrix helper_vol::AsNewmatVector() const { NEWMAT::ColumnVector ovec(_sz[0]*_sz[1]*_sz[2]); double *dp = ovec.Store(); memcpy(dp,_data,_sz[0]*_sz[1]*_sz[2]*sizeof(double)); ovec.Release(); return(ovec); } void helper_vol::GetColumn(unsigned int i, unsigned int j, unsigned int dim, double *col) const { const double *endptr = end(i,j,dim); unsigned int stp = step(dim); for (const double *ptr=start(i,j,dim); ptr& lksp, const std::vector& csz) const; double calculate_memen(const NEWMAT::ColumnVector& b, const std::vector& lksp, const std::vector& csz) const; void calculate_bender_grad(const NEWMAT::ColumnVector& b, const std::vector& lksp, const std::vector& csz, NEWMAT::ColumnVector& grad) const; void calculate_memen_grad(const NEWMAT::ColumnVector& b, const std::vector& lksp, const std::vector& csz, NEWMAT::ColumnVector& grad) const; NEWMAT::ReturnMatrix memen_HtHb_helper(const Spline3D& spd, const std::vector& csz, const NEWMAT::ColumnVector& b, HtHbType what) const; double splinefield::MemEnergy() const // Membrane energy of field { const boost::shared_ptr lcoef = GetCoef(); if (!lcoef) {throw BasisfieldException("splinefield::MemEnergy: No coefficients set yet");} std::vector csz(3,0); csz[0] = CoefSz_x(); csz[1] = CoefSz_y(); csz[2] = CoefSz_z(); return(calculate_memen(*lcoef,_sp.KnotSpacing(),csz)); } double splinefield::BendEnergy() const // Bending energy of field { const boost::shared_ptr lcoef = GetCoef(); if (!lcoef) {throw BasisfieldException("splinefield::BendEnergy: No coefficients set yet");} std::vector csz(3,0); csz[0] = CoefSz_x(); csz[1] = CoefSz_y(); csz[2] = CoefSz_z(); return(calculate_bender(*lcoef,_sp.KnotSpacing(),csz)); } NEWMAT::ReturnMatrix splinefield::MemEnergyGrad() const // Gradient of membrane energy of field { const boost::shared_ptr lcoef = GetCoef(); if (!lcoef) {throw BasisfieldException("splinefield::MemEnergyGrad: No coefficients set yet");} std::vector csz(3,0); csz[0] = CoefSz_x(); csz[1] = CoefSz_y(); csz[2] = CoefSz_z(); NEWMAT::ColumnVector grad(CoefSz()); calculate_memen_grad(*lcoef,_sp.KnotSpacing(),csz,grad); grad.Release(); return(grad); } NEWMAT::ReturnMatrix splinefield::BendEnergyGrad() const // Gradient of bending energy of field { const boost::shared_ptr lcoef = GetCoef(); if (!lcoef) {throw BasisfieldException("splinefield::BendEnergyGrad: No coefficients set yet");} std::vector csz(3,0); csz[0] = CoefSz_x(); csz[1] = CoefSz_y(); csz[2] = CoefSz_z(); NEWMAT::ColumnVector grad(CoefSz()); calculate_bender_grad(*lcoef,_sp.KnotSpacing(),csz,grad); grad.Release(); return(grad); } // Calculates bending energy double splinefield::calculate_bender(const NEWMAT::ColumnVector& b, const std::vector& lksp, const std::vector& csz) const { double memen = 0.0; double vxs[] = {Vxs_x(), Vxs_y(), Vxs_z()}; // Sum over directions for (unsigned int d1=0; d1<3; d1++) { for (unsigned int d2=d1; d2<3; d2++) { std::vector deriv(3,0); deriv[d1]++; deriv[d2]++; Spline3D spd(_sp.Order(),lksp,deriv); // Spline twice differentiated spd /= (vxs[d1]*vxs[d2]); // Derivative in mm^{-1} NEWMAT::ColumnVector hb = memen_HtHb_helper(spd,csz,b,Hb); if (d1==d2) memen += DotProduct(hb,hb); else memen += 2.0*DotProduct(hb,hb); } } return(memen); } // Calculates membrane energy double splinefield::calculate_memen(const NEWMAT::ColumnVector& b, const std::vector& lksp, const std::vector& csz) const { double memen = 0.0; double vxs[] = {Vxs_x(), Vxs_y(), Vxs_z()}; // Sum over directions for (unsigned int d=0; d<3; d++) { std::vector deriv(3,0); deriv[d] = 1; Spline3D spd(_sp.Order(),lksp,deriv); // Spline differentiated in one direction spd /= vxs[d]; // Derivative in mm^{-1} NEWMAT::ColumnVector hb = memen_HtHb_helper(spd,csz,b,Hb); memen += DotProduct(hb,hb); } return(memen); } void splinefield::calculate_bender_grad(const NEWMAT::ColumnVector& b, const std::vector& lksp, const std::vector& csz, NEWMAT::ColumnVector& grad) const { if (static_cast(grad.Nrows()) != csz[2]*csz[1]*csz[0]) grad.ReSize(csz[2]*csz[1]*csz[0]); grad = 0.0; double vxs[] = {Vxs_x(), Vxs_y(), Vxs_z()}; // Sum over directions for (unsigned int d1=0; d1<3; d1++) { for (unsigned int d2=d1; d2<3; d2++) { std::vector deriv(3,0); deriv[d1]++; deriv[d2]++; Spline3D spd(_sp.Order(),lksp,deriv); // Spline twice differentiated spd /= (vxs[d1]*vxs[d2]); // Derivative in mm^{-1} if (d1==d2) grad += memen_HtHb_helper(spd,csz,b,HtHb); else grad += 2.0*memen_HtHb_helper(spd,csz,b,HtHb); } } grad *= 2.0; } void splinefield::calculate_memen_grad(const NEWMAT::ColumnVector& b, const std::vector& lksp, const std::vector& csz, NEWMAT::ColumnVector& grad) const { if (static_cast(grad.Nrows()) != csz[2]*csz[1]*csz[0]) grad.ReSize(csz[2]*csz[1]*csz[0]); grad = 0.0; double vxs[] = {Vxs_x(), Vxs_y(), Vxs_z()}; // Sum over directions for (unsigned int d=0; d<3; d++) { std::vector deriv(3,0); deriv[d] = 1; Spline3D spd(_sp.Order(),lksp,deriv); // Spline differentiated in one direction spd /= vxs[d]; // Derivative in mm^{-1} grad += memen_HtHb_helper(spd,csz,b,HtHb); } grad *= 2.0; } ///////////////////////////////////////////////////////////////////// // // This is a helper-routine that calculates H*b or H'*H*b depending // on the switch "what". H in this case is the matrix such that // b'*(H'*H)*b is the membrane energy for a field given by the // coefficients b. It does so without explicitly representing H, // which is good because it can be very large. It is a helper both // for calculating the memebrane energy (as (H*b)'*(H*b)) and the // gradient of the membrane energy (as H'*H*b). // N.B. that we want to assess the energy for the "entire field", // i.e. also the parts that extends beyond the FOV of the image. // That means that (H*b).Nrows() > FieldSz(). // ///////////////////////////////////////////////////////////////////// NEWMAT::ReturnMatrix splinefield::memen_HtHb_helper(const Spline3D& spd, const std::vector& csz, const NEWMAT::ColumnVector& b, HtHbType what) const { NEWMAT::ColumnVector hb(spd.TotalFullFOV(csz)); // H*b hb = 0.0; double *hbp = hb.Store(); const double *bp = b.Store(); // Generate indicies of first spline into Hb unsigned int *indx = new unsigned int[spd.TotalKernelSize()]; for (unsigned int k=0, li=0; k