/*! \file EddyHelperClasses.h \brief Contains declaration of classes that implements useful functionality for the eddy project. \author Jesper Andersson \version 1.0b, Sep., 2012. */ // Declarations of classes that implements useful // concepts for the eddy current project. // // EddyHelperClasses.h // // Jesper Andersson, FMRIB Image Analysis Group // // Copyright (C) 2011 University of Oxford // #ifndef EddyHelperClasses_h #define EddyHelperClasses_h #include #include #include #include #include #include "newmat.h" #include "newimage/newimageall.h" #include "miscmaths/miscmaths.h" namespace EDDY { enum Parameters { MOVEMENT, EC, ALL }; enum ECModel { Linear, Quadratic, Cubic, Unknown }; enum ScanType { DWI, B0 , ANY }; enum FinalResampling { JAC, LSR, UNKNOWN_RESAMPLING}; /****************************************************************//** * * \brief This is the exception that is being thrown by routines * in the core code of eddy. * * This is the exception that is being thrown by routines * in the core code of eddy. * ********************************************************************/ class EddyException: public std::exception { private: std::string m_msg; public: EddyException(const std::string& msg) throw(): m_msg(msg) { cout << what() << endl; } virtual const char * what() const throw() { return string("eddy: msg=" + m_msg).c_str(); } ~EddyException() throw() {} }; /****************************************************************//** * * \brief This class manages the diffusion parameters for one scan * * This class manages the diffusion parameters for one scan * ********************************************************************/ class DiffPara { public: /// Default constructor. Sets b-vector to [1 0 0] and b-value to zero. DiffPara() { _bvec.ReSize(3); _bvec(1)=1; _bvec(2)=0; _bvec(3)=0; _bval = 0; } /// Contructs a DiffPara object from a b-vector and a b-value. DiffPara(const NEWMAT::ColumnVector& bvec, double bval) : _bvec(bvec), _bval(bval) { if (_bvec.Nrows() != 3) throw EddyException("DiffPara::DiffPara: b-vector must be three elements long"); if (_bval < 0) throw EddyException("DiffPara::DiffPara: b-value must be non-negative"); if (_bval) _bvec /= _bvec.NormFrobenius(); } /// Prints out b-vector and b-value in formatted way friend ostream& operator<<(ostream& op, const DiffPara& dp) { op << "b-vector: " << dp._bvec.t() << endl << "b-value: " << dp._bval << endl; return(op); } /// Returns true if the b-value AND the direction are the same bool operator==(const DiffPara& rhs) const; /// Same as !(a==b) bool operator!=(const DiffPara& rhs) const { return(!(*this==rhs)); } /// Compares the b-values bool operator>(const DiffPara& rhs) const { return(this->bVal()>rhs.bVal()); } /// Compares the b-values bool operator<(const DiffPara& rhs) const { return(this->bVal()& mask) : _mask(mask) {} MaskManager(int xs, int ys, int zs) : _mask(xs,ys,zs) { _mask = 1.0; } void ResetMask() { _mask = 1.0; } void SetMask(const NEWIMAGE::volume& mask) { if (!NEWIMAGE::samesize(_mask,mask)) throw EddyException("EDDY::MaskManager::SetMask: Wrong dimension"); else _mask = mask;} void UpdateMask(const NEWIMAGE::volume& mask) { if (!NEWIMAGE::samesize(_mask,mask)) throw EddyException("EDDY::MaskManager::UpdateMask: Wrong dimension"); else _mask *= mask;} const NEWIMAGE::volume& GetMask() const { return(_mask); } private: NEWIMAGE::volume _mask; }; /****************************************************************//** * * \brief This class manages stats on slice wise differences. * * This class calculates and serves up information about slice-wise * (in observation space) statistics on the difference between * an observation (scan) and the prediction. * ********************************************************************/ class DiffStats { public: DiffStats() {} /// Constructs a Diffstats object given a difference volume and a mask. DiffStats(const NEWIMAGE::volume& diff, const NEWIMAGE::volume& mask); /// Returns the mean (across all valid voxels in the volume) difference. double MeanDifference() const { return(mean_stat(_md)); } /// Returns the mean (across all valid voxels in slice sl (zero-offset)) difference. double MeanDifference(int sl) const { if (index_ok(sl)) return(_md[sl]); else return(0.0); } /// Returns a vector with the mean difference for all slices NEWMAT::RowVector MeanDifferenceVector() const { return(get_vector(_md)); } /// Returns the mean (across all valid voxels in the volume) squared difference. double MeanSqrDiff() const { return(mean_stat(_msd)); } /// Returns the mean (across all valid voxels in slice sl (zero-offset)) squared difference. double MeanSqrDiff(int sl) const { if (index_ok(sl)) return(_msd[sl]); else return(0.0); } /// Returns a vector with the mean squared difference for all slices NEWMAT::RowVector MeanSqrDiffVector() const { return(get_vector(_msd)); } /// Number of valid voxels in the whole volume (as determined by the mask passed to the constructor) unsigned int NVox() const { unsigned int n=0; for (int i=0; i _md; // Slice wise mean difference std::vector _msd; // Slice wise mean squared difference std::vector _n; // Slice wise # of valid pixels bool index_ok(int sl) const { if (sl<0 || sl>=int(_n.size())) throw EddyException("DiffStats::index_ok: Index out of range"); return(true); } double mean_stat(const std::vector& stat) const { double ms=0; for (int i=0; i NEWMAT::RowVector get_vector(const std::vector& stat) const { NEWMAT::RowVector ov(stat.size()); for (unsigned int i=0; i > GetOutliers(double nstdev=4.0, unsigned int minn=100) const; /// Returns a vector of scan indicies for the outliers in slice sl (zero-offset). std::vector GetOutliersInSlice(unsigned int sl, double nstdev=4.0, unsigned int minn=100) const; /// Returns a vector (NScan() long) with the number of std for each scan for a slice given by sl. std::vector GetNoOfStdevInSlice(unsigned int sl, unsigned int minn=100) const; private: unsigned int _n; // Length of vector DiffStats *_ds; // Old fashioned C vector of DiffStats objects double slice_mean_mean_diff(unsigned int sl, unsigned int minn) const; double slice_stdev_mean_diff(unsigned int sl, unsigned int minn) const; void throw_if_oor(unsigned int i) const { if (i >= _n) throw EddyException("DiffStatsVector::throw_if_oor: Index out of range"); } }; /****************************************************************//** * * \brief This class decides and keeps track of which slices in which * scans should be replaced by their predictions * ********************************************************************/ class ReplacementManager { public: ReplacementManager(unsigned int nscan, unsigned int nsl, double nstdev=4.0, unsigned int minn=100) : _nstdev(nstdev), _minn(minn), _ovv(nsl), _nsv(nsl) { for (unsigned int sl=0; sl OutliersInScan(unsigned int scan) const; void WriteReport(const std::vector& i2i, const std::string& bfname) const; // For debugging void DumpOutlierMap(const std::string& fname) const; private: double _nstdev; // # of standard dev off to qualify as outlier unsigned int _minn; // Min # of voxels in slice to be considered reliable std::vector > _ovv; // _ovv[slice][scan] is an outlier if set std::vector > _nsv; // _nsv[slice][scan] tells how many stdev off that slice-scan is. void scan_oor(unsigned int scan) const { if (!_ovv.size() || scan >= _ovv[0].size()) throw EddyException("ReplacementManager::scan_oor: Scan index out of range"); } }; /****************************************************************//** * * \brief Helper class that manages a set of image coordinates in a way that * it enables calculation/implementation of partial derivatives of * images w.r.t. transformation parameters. * ********************************************************************/ class ImageCoordinates { public: ImageCoordinates(unsigned int xn, unsigned int yn, unsigned int zn, bool init=false) : _xn(xn), _yn(yn), _zn(zn) { _x = new float[_xn*_yn*_zn]; _y = new float[_xn*_yn*_zn]; _z = new float[_xn*_yn*_zn]; if (init) { for (unsigned int k=0, indx=0; k<_zn; k++) { for (unsigned int j=0; j<_yn; j++) { for (unsigned int i=0; i<_xn; i++) { _x[indx] = float(i); _y[indx] = float(j); _z[indx++] = float(k); } } } } } ImageCoordinates(const ImageCoordinates& inp) : _xn(inp._xn), _yn(inp._yn), _zn(inp._zn) { _x = new float[_xn*_yn*_zn]; std::memcpy(_x,inp._x,_xn*_yn*_zn*sizeof(float)); _y = new float[_xn*_yn*_zn]; std::memcpy(_y,inp._y,_xn*_yn*_zn*sizeof(float)); _z = new float[_xn*_yn*_zn]; std::memcpy(_z,inp._z,_xn*_yn*_zn*sizeof(float)); } ~ImageCoordinates() { delete[] _x; delete[] _y; delete[] _z; } ImageCoordinates& operator=(const ImageCoordinates& rhs) { if (this == &rhs) return(*this); delete[] _x; delete[] _y; delete[] _z; _xn = rhs._xn; _yn = rhs._yn; _zn = rhs._zn; _x = new float[_xn*_yn*_zn]; std::memcpy(_x,rhs._x,_xn*_yn*_zn*sizeof(float)); _y = new float[_xn*_yn*_zn]; std::memcpy(_y,rhs._y,_xn*_yn*_zn*sizeof(float)); _z = new float[_xn*_yn*_zn]; std::memcpy(_z,rhs._z,_xn*_yn*_zn*sizeof(float)); return(*this); } ImageCoordinates& operator-=(const ImageCoordinates& rhs) { if (_xn != rhs._xn || _yn != rhs._yn || _zn != rhs._zn) throw EddyException("ImageCoordinates::operator-= size mismatch"); for (unsigned int i=0; i<_xn*_yn*_zn; i++) { _x[i]-=rhs._x[i]; _y[i]-=rhs._y[i]; _z[i]-=rhs._z[i]; } return(*this); } ImageCoordinates operator-(const ImageCoordinates& rhs) const { return(ImageCoordinates(*this)-=rhs); } NEWIMAGE::volume operator*(const NEWIMAGE::volume4D& vol) { if (int(_xn) != vol.xsize() || int(_yn) != vol.ysize() || int(_zn) != vol.zsize() || vol.tsize() != 3) { throw EddyException("ImageCoordinates::operator* size mismatch"); } NEWIMAGE::volume ovol = vol[0]; for (unsigned int k=0, indx=0; k<_zn; k++) { for (unsigned int j=0; j<_yn; j++) { for (unsigned int i=0; i<_xn; i++) { ovol(i,j,k) = _x[indx]*vol(i,j,k,0) + _y[indx]*vol(i,j,k,1) + _z[indx]*vol(i,j,k,2); indx++; } } } return(ovol); } void Write(const std::string& fname) const { NEWMAT::Matrix omat(N(),3); for (unsigned int i=0; i