// Declarations of cost-function classes used by topup // // topup_costfunctions.h // // Jesper Andersson, FMRIB Image Analysis Group // // Copyright (C) 2009 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. */ #ifndef topup_costfunctions_h #define topup_costfunctions_h #include #include #include #include "newmat.h" #include "miscmaths/bfmatrix.h" #include "miscmaths/nonlin.h" #include "basisfield/basisfield.h" #include "basisfield/splinefield.h" #include "basisfield/dctfield.h" namespace TOPUP { class TopupException: public std::exception { private: std::string m_msg; public: TopupException(const std::string& msg) throw(): m_msg(msg) {} virtual const char * what() const throw() { return string("Topup: msg=" + m_msg).c_str(); } ~TopupException() throw() {} }; enum SizeType {Original=0, Regridded=1, Subsampled=2, Target=3}; enum RegularisationType {MembraneEnergy, BendingEnergy}; enum TopupInterpolationType {LinearInterp, SplineInterp, UnknownInterp}; //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class TiledMatrix // // Helper class to organize the creation/calculation of the // movement part of the Hessian. // // {{{ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class TiledMatrix { public: TiledMatrix(const std::vector& tile_sizes) : _ts(tile_sizes) { unsigned int tots = _ts[0]; for (unsigned int i=1; i<_ts.size(); i++) tots += _ts[i]; _m.ReSize(tots,tots); _m = 0.0; } ~TiledMatrix() {} unsigned int NoOfTiles() const { return(_ts.size()); } unsigned int TileSizeRow(unsigned int r, unsigned int c) const { if (r>=_ts.size() || c>=_ts.size()) throw TopupException("TiledMatrix::TileSizeRow: Index out of range"); return(_ts[r]); } unsigned int TileSizeCol(unsigned int r, unsigned int c) const { if (r>=_ts.size() || c>=_ts.size()) throw TopupException("TiledMatrix::TileSizeCol: Index out of range"); return(_ts[c]); } // Read and read-write access functions. N.B. that the tiles are // indexed from 0 to no_tiles-1, i.e. C-convention rather than NEWMAT. NEWMAT::Matrix GetTile(unsigned int r, unsigned int c) const { if (r>=_ts.size() || c>=_ts.size()) throw TopupException("TiledMatrix::GetTile: Index out of range"); unsigned int rs = 0; for (unsigned i=0; i=_ts.size() || c>=_ts.size()) throw TopupException("TiledMatrix::SetTile: Index out of range"); if (tile.Nrows() != int(_ts[r]) || tile.Ncols() != int(_ts[c])) throw TopupException("TiledMatrix::SetTile: Wrong size matrix"); unsigned int rs = 0; for (unsigned i=0; i _ts; NEWMAT::Matrix _m; }; /* class TiledMatrix { public: TiledMatrix(unsigned int tile_size_rows, unsigned int tile_size_columns, unsigned int no_tiles_rows, unsigned int no_tiles_columns) : _tsr(tile_size_rows), _tsc(tile_size_columns), _ntr(no_tiles_rows), _ntc(no_tiles_columns), _m(_tsr*_ntr,_tsc*_ntc) { _m = 0.0; } ~TiledMatrix() {} unsigned int NoOfTilesRow() const { return(_ntr); } unsigned int NoOfTilesCol() const { return(_ntc); } unsigned int TileSizeRow() const { return(_tsr); } unsigned int TileSizeCol() const { return(_tsc); } // Read and read-write access functions. N.B. that the tiles are // indexed from 0 to no_tiles-1, i.e. C-convention rather than NEWMAT. NEWMAT::Matrix GetTile(unsigned int ri, unsigned int ci) const { if (ri>=_ntr || ci>=_ntc) throw TopupException("TiledMatrix::GetTile: Index out of range"); return(_m.SubMatrix(ri*_tsr+1,(ri+1)*_tsr,ci*_tsc+1,(ci+1)*_tsc)); } void SetTile(unsigned int ri, unsigned int ci, const NEWMAT::Matrix& tile) // Read-write { if (ri>=_ntr || ci>=_ntc) throw TopupException("TiledMatrix::SetTile: Index out of range"); if (tile.Nrows() != int(_tsr) || tile.Ncols() != int(_tsc)) throw TopupException("TiledMatrix::SetTile: Index out of range"); _m.SubMatrix(ri*_tsr+1,(ri+1)*_tsr,ci*_tsc+1,(ci+1)*_tsc) = tile; } const NEWMAT::Matrix& Untile() const { return(_m); } private: unsigned int _tsr; unsigned int _tsc; unsigned int _ntr; unsigned int _ntc; NEWMAT::Matrix _m; }; */ // }}} End of fold. //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class TopupScan // // Has the data, acquisition info and movement parameters for // one scan. Responsible for managing the scan, resampling the // scan (and serve it up) and keeping track of update status. // // {{{ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class TopupScan { public: TopupScan(const NEWIMAGE::volume& scan, const NEWMAT::ColumnVector& pevec, double rotime); ~TopupScan() {} std::vector ImageSize(SizeType type=Original) const; std::vector ImageVxs(SizeType type=Original) const; bool TracePrint() const { return(_tp); } void SetTracePrint(bool val=true) { _tp=val; } bool UpToDate() const { return(_uptodate); } bool HasBeta() const { if (_pevec(1)) return(true); else return(false); } bool HasGamma() const { if (_pevec(2)) return(true); else return(false); } NEWMAT::ColumnVector GetPhaseEncodeDirection() const { return(_pevec); } NEWIMAGE::volume GetResampled(const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetMask(const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetAlpha(const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetBeta(const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetGamma(const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetMovementDerivative(unsigned int i, const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetNumericalMovementDerivative(unsigned int i, const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetJacobian(const BASISFIELD::splinefield& field) const; NEWMAT::ColumnVector GetMovementParameters() const { return(_mp); } // Will always serve up six elements NEWMAT::Matrix GetRigidBodyMatrix() const { return(mp_to_matrix(_mp)); } void SetUpToDate(bool flag) const { _uptodate=flag; } void SetMovementParameters(const NEWMAT::ColumnVector& mp) const; // mp must contain six elements void SetInterpolationModel(TopupInterpolationType it) const; void ReGrid(int xsz, int ysz, int zsz); void ReGrid(const std::vector& sz) { ReGrid(int(sz[0]),int(sz[1]),int(sz[2])); } void Smooth(double fwhm); void SubSample(unsigned int ss); private: boost::shared_ptr > _orig; boost::shared_ptr > _regrid; // May be identical to _orig boost::shared_ptr > _subsamp; // May be identical to _regrid boost::shared_ptr > _smooth; // May be identical to _subsamp mutable NEWIMAGE::volume _resampled; mutable NEWIMAGE::volume4D _derivs; mutable NEWIMAGE::volume _jac; mutable NEWIMAGE::volume _mask; NEWMAT::ColumnVector _pevec; double _rotime; mutable bool _uptodate; mutable NEWMAT::ColumnVector _mp; // dx dy dz rx ry rz, N.B. different from MJ bool _tp; // Trace-print NEWMAT::Matrix mp_to_matrix(const NEWMAT::ColumnVector& mp) const; NEWIMAGE::interpolation translate_interp_type(TopupInterpolationType it) const { if (it==LinearInterp) return(NEWIMAGE::trilinear); else return(NEWIMAGE::spline); } TopupInterpolationType translate_interp_type(NEWIMAGE::interpolation it) const { if (it==NEWIMAGE::trilinear) return(LinearInterp); else if (it==NEWIMAGE::spline) return(SplineInterp); else return(UnknownInterp); } void update(const BASISFIELD::splinefield& field) const; // This is where all the work gets done }; // }}} End of fold. //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class TopupScanManager // // Responsible for managing the scans, updating the resampled ones // as neccessary, keep track of update-status and to serve up // resampled scans and a mask that is the intersection of the // individual masks. // // {{{ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class TopupScanManager { public: TopupScanManager(const NEWIMAGE::volume4D& scans, const NEWMAT::Matrix& pevecs, const NEWMAT::ColumnVector& rotimes); ~TopupScanManager(); // Routines that request information about the scans unsigned int NoOfScans() const { return(_scans.size()); } // unsigned int NoOfMovementParameters() const { return((NoOfScans()-1) * _mpi.size()); } unsigned int NoOfMovementParameters() const { unsigned int n=_mpi[0].size(); for (unsigned int i=1; i ImageSize(SizeType type=Original) const { return(_scans[0]->ImageSize(type)); } std::vector ImageVxs(SizeType type=Original) const { return(_scans[0]->ImageVxs(type)); } unsigned int SubSampling() const { return(_ss); } double FWHM() const { return(_fwhm); } TopupInterpolationType InterpolationModel() const { return(_it); } bool HasBeta() const { return(_hasx); } bool HasGamma() const { return(_hasy); } bool HasBeta(unsigned int i) const; bool HasGamma(unsigned int i) const; bool TracePrint() const { return(_tp); } void SetTracePrint(bool val=true) { _tp=val; for (unsigned int i=0; i<_scans.size(); i++) _scans[i]->SetTracePrint(val); } // Routines that return data from the scans // Get average of all warped and modulated scans const NEWIMAGE::volume& GetMean(const BASISFIELD::splinefield& field) const { update(field); return(_mean); } // Get area where data is present for all scans const NEWIMAGE::volume& GetMask(const BASISFIELD::splinefield& field) const { update(field); return(_mask); } // See paper/techreport for explanation of alpha, beta and gamma const NEWIMAGE::volume& GetMeanAlpha(const BASISFIELD::splinefield& field) const { update(field); return(_mean_alpha); } const NEWIMAGE::volume& GetMeanBeta(const BASISFIELD::splinefield& field) const { update(field); return(_mean_beta); } const NEWIMAGE::volume& GetMeanGamma(const BASISFIELD::splinefield& field) const { update(field); return(_mean_gamma); } NEWIMAGE::volume GetScan(unsigned int i, const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetAlpha(unsigned int i, const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetBeta(unsigned int i, const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetGamma(unsigned int i, const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetMovementDerivative(unsigned int scan, unsigned int deriv, const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetNumericalMovementDerivative(unsigned int scan, unsigned int deriv, const BASISFIELD::splinefield& field) const; NEWIMAGE::volume GetJacobian(unsigned int i, const BASISFIELD::splinefield& field) const; // Routines that set/change the state of the scans void FieldUpdated() const; void ReGrid(unsigned int xsz, unsigned int ysz, unsigned int zsz); void ReGrid(const std::vector& sz) { ReGrid(sz[0],sz[1],sz[2]); } void SubSample(unsigned int ss); void Smooth(double fwhm); void SetMovementParameters(const NEWMAT::ColumnVector& mp) const; // Sets the parameters that are being estimated void SetInterpolationModel(TopupInterpolationType it) const; private: std::vector _scans; // Vector of pointers mutable NEWIMAGE::volume _mask; mutable NEWIMAGE::volume _mean; mutable NEWIMAGE::volume _mean_alpha; mutable NEWIMAGE::volume _mean_beta; mutable NEWIMAGE::volume _mean_gamma; mutable bool _up_to_date; std::vector _regrid_sz; unsigned int _ss; mutable TopupInterpolationType _it; double _fwhm; std::vector > _mpi; // Indicies to movement parameters bool _hasx; bool _hasy; bool _tp; // Trace-print void update(const BASISFIELD::splinefield& field) const; bool have_different_signs(double f1, double f2) const { return((f1>0 && f2<0) || (f1<0 && f2>0)); } }; // }}} End of fold. //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class TopupCF // // The "main" class of the Topup application. Passed to the nonlin // library. // // {{{ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class TopupCF: public MISCMATHS::NonlinCF { public: TopupCF(const NEWIMAGE::volume4D& scans, const NEWMAT::Matrix& pevecs, const NEWMAT::ColumnVector& rotimes, double warpres, unsigned int sporder=3); // Routines to get information about the run // These will (probably) mostly be used internally unsigned int NPar() const { if (_mf) return(_field.CoefSz()); else return(_field.CoefSz()+NMovPar()); } NEWMAT::ReturnMatrix Par() const; unsigned int NDefPar() const { return(_field.CoefSz()); } unsigned int NMovPar() const { if (!_mf) return(_sm.NoOfMovementParameters()); else return(0); } double WarpResolution() const { return(_wr); } MISCMATHS::BFMatrixPrecisionType HessianPrecision() const { return(_hp); } bool TracePrint() const { return(_tp); } bool Verbose() const { return(_verb); } double Lambda() const { if (_ssql) return(_lssd * _lambda); else return(_lambda); } bool MovementsFixed() const { return(_mf); } TopupInterpolationType InterpolationModel() const { return(_sm.InterpolationModel()); } // Routines used to set parameters that determine how topup is run. // Most of these correspond to user settable parameters on the command line. void SetTracePrint(bool val=true) { _tp=val; _sm.SetTracePrint(val); } void SetRegridding(const std::vector& ims); void Smooth(double fwhm) { if (fwhm != _sm.FWHM()) _sm.Smooth(fwhm); } void SubSample(unsigned int ss); void SetWarpResolution(double wr); // N.B. wr should be in mm void SetVerbose(bool val=true) { _verb=val; } void SetMovementsFixed(bool val=true) { _mf=val; } void SetRegularisation(double lambda, RegularisationType type=BendingEnergy) { _lambda=lambda; _rt=type; } void SetSSQLambda(bool val=true) { _ssql=val; } void SetHessianPrecision(MISCMATHS::BFMatrixPrecisionType hp) { _hp = hp; } void SetInterpolationModel(TopupInterpolationType it) { _sm.SetInterpolationModel(it); } // Routines for writing results/information to file. void WriteCoefficients(const std::string& fname) const; void WriteUnwarped(const std::string& fname) const; void WriteJacobians(const std::string& fname) const; void WriteField(const std::string& fname) const; void WriteMask(const std::string& fname) const; void WriteMaskedDiff(const std::string& fname) const; void WriteMovementParameters(const std::string& fname) const; void WriteRigidBodyMatrices(const std::string& fname) const; // Routines for writing debug output to disc. void SetDebug(unsigned int level=1) { if (level>3) throw TopupException("TopupCF::SetDebug: Debug level out of range"); else _dl=level; } void SetLevel(unsigned int level) const { _level=level; _iter=0; _attempt=0; } // Returns relevent field energy or gradient/Hessian thereof. double FieldEnergy() const; NEWMAT::ReturnMatrix FieldEnergyGrad() const; boost::shared_ptr FieldEnergyHess() const; // cost-function, gradient and Hessian as specified in NonlinCF class virtual double cf(const NEWMAT::ColumnVector& p) const; virtual NEWMAT::ReturnMatrix grad(const NEWMAT::ColumnVector& p) const; virtual boost::shared_ptr hess(const NEWMAT::ColumnVector& p, boost::shared_ptr iptr=boost::shared_ptr()) const; private: TopupScanManager _sm; // All the scans mutable BASISFIELD::splinefield _field; // field double _wr; // Requested warp resolution (knot-spacing) in mm mutable double _lambda; // Regularisation weight bool _ssql; // Should regularisation be weighted with SSD? RegularisationType _rt; // Regularisation type bool _mf; // Movements kept fixed if set. MISCMATHS::BFMatrixPrecisionType _hp; // Precision of Hessian Matrix bool _verb; // Verbose bool _tp; // Trace-print unsigned int _dl; // Debug level mutable double _lssd; // Latest sum-of-squared differences mutable unsigned int _level; // Unwarping level mutable unsigned int _iter; // Unwarping iteration (within level) mutable unsigned int _attempt; // Unwarping attempt (within iter) void set_latest_ssd(double ssd) const { _lssd=ssd; } void set_field_params(const NEWMAT::ColumnVector& p) const; void set_movement_params(const NEWMAT::ColumnVector& p) const; double sum_of_prod(const NEWIMAGE::volume& i1, const NEWIMAGE::volume& i2, const NEWIMAGE::volume& mask) const; NEWMAT::ReturnMatrix movement_hessian(unsigned int s1, unsigned int s2, const NEWIMAGE::volume& mask, const BASISFIELD::splinefield& field) const; BASISFIELD::splinefield field_factory(const NEWIMAGE::volume4D& scans, double warpres, unsigned int sporder) const; // Routines for implementing debug write-outs. unsigned int debug_level() const { return(_dl); } void set_iter(unsigned int iter=0) const { _iter=iter; _attempt=0; } void set_attempt(unsigned int attempt) const { _attempt=attempt; } unsigned int level() const { return(_level); } unsigned int iter() const { return(_iter); } unsigned int attempt() const { return(_attempt); } std::string debug_string() const {char c_str[256]; sprintf(c_str,"level%02d_iter%02d_attempt%02d",level(),iter(),attempt()); return(std::string(c_str));} // Routines for numerical calculation of parts of // the gradient or the hessian NEWMAT::ColumnVector numerical_gradient(const NEWMAT::ColumnVector& p, unsigned int fi, unsigned int li, double delta=-1, bool reg=false) const; NEWMAT::Matrix numerical_hessian(const NEWMAT::ColumnVector& p, unsigned int fi1, unsigned int li1, unsigned int fi2, unsigned int li2, double delta1=-1, double delta2=-1, bool reg=false) const; NEWMAT::Matrix numerical_hessian(const NEWMAT::ColumnVector& p, unsigned int fi, unsigned int li, double delta=-1, bool reg=false) const { return(numerical_hessian(p,fi,li,fi,li,delta,delta,reg)); } }; // }}} End of fold. } // End namespace TOPUP #endif // End #ifndef topup_costfuctions_h