// // fsl_splines.h // // 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. */ // #ifndef fsl_splines_h #define fsl_splines_h #include #include #include #include "newmat.h" #include "miscmaths/miscmaths.h" namespace BASISFIELD { class FslSplinesException: public std::exception { private: std::string m_msg; public: FslSplinesException(const std::string& msg) throw(): m_msg(msg) {} virtual const char * what() const throw() { return string("FslSplines::" + m_msg).c_str(); } ~FslSplinesException() throw() {} }; // Declare ahead template class Spline1D; //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class Spline3D: // This class is used by class splinefield to calculate field, // derivative of field w.r.t. coefficients etc. It is implemented // through Spline1D, containing a vector of three of those. // // Its interface includes // Operator (i,j,k) : // Zero offset read access with range check. // Peek(i,j,k): // Zero offset read access without range check // Operator [i] : // Zero offset read access into vectorised spline without range check. // RangeInField: // Given a coef/spline index returns range of non-zero indicies in a 3D spline field // OffsetIntoKernel: // Given a coef/spline index returns first indicies in spline that falls within field // //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ template class Spline3D { public: // Constructors Spline3D(unsigned int order, unsigned int ksp, std::vector deriv = std::vector(3,0)) : _vsp1D(3), _premul(false) { std::vector vksp(3,ksp); common_construction(order,vksp,deriv); } Spline3D(unsigned int order, const std::vector& ksp, std::vector deriv = std::vector(3,0)) : _vsp1D(3), _premul(false) { common_construction(order,ksp,deriv); } Spline3D(const Spline3D& sp) : _vsp1D(3) { common_construction(sp.Order(),sp.KnotSpacing(),sp.DerivativeOrder()); if (sp.IsPremul()) { memcpy(_sp3D,sp._sp3D,sp.TotalKernelSize()*sizeof(T)); _premul = true; } } // Destructor ~Spline3D() {delete [] _osp3D; delete [] _sp3D;} Spline3D& operator=(const Spline3D& insp) { if (this != &insp) { delete[] _osp3D; delete[] _sp3D; common_construction(insp.Order(),insp.KnotSpacing(),insp.DerivativeOrder()); if (insp.IsPremul()) { memcpy(_sp3D,insp._sp3D,insp.TotalKernelSize()*sizeof(T)); _premul = true; } } return(*this); } // Functions for enquiring about properties of spline unsigned int Order() const {return(_vsp1D[0].Order());} unsigned int KnotSpacing(unsigned int dir) const {return(_vsp1D[dir].KnotSpacing());} std::vector KnotSpacing() const {std::vector vec(3,0); for (int i=0; i<3; i++) vec[i]=_vsp1D[i].KnotSpacing(); return(vec);} unsigned int DerivativeOrder(unsigned int dir) const {return(_vsp1D[dir].DerivativeOrder());} std::vector DerivativeOrder() const {std::vector vec(3,0); for (int i=0; i<3; i++) vec[i]=_vsp1D[i].DerivativeOrder(); return(vec);} unsigned int KernelSize(unsigned int dir) const {return(_vsp1D[dir].KernelSize());} unsigned int TotalKernelSize() const {return(_vsp1D[0].KernelSize()*_vsp1D[1].KernelSize()*_vsp1D[2].KernelSize());} unsigned int NCoef(unsigned int dir, unsigned int isz) const {return(_vsp1D[dir].NCoef(isz));} unsigned int FullFOV(unsigned int dir, unsigned int csz) const {return(_vsp1D[dir].FullFOV(csz));} unsigned int TotalFullFOV(const std::vector& csz) const {return(_vsp1D[0].FullFOV(csz[0])*_vsp1D[1].FullFOV(csz[1])*_vsp1D[2].FullFOV(csz[2]));} bool IsPremul() const {return(_premul);} // Functions for read access to individual element when sampled on // a regular grid corresponding to voxels where knot-spacing is an // integer number of voxels T operator()(unsigned int i, unsigned int j, unsigned int k) const; // Zero offset with range check T Peek(unsigned int i, unsigned int j, unsigned int k) const // Zero offset without range check { return(_sp3D[k*_vsp1D[1].KernelSize()*_vsp1D[0].KernelSize()+j*_vsp1D[0].KernelSize()+i]); } T operator[](unsigned int indx) const {return(_sp3D[indx]);} // Zero offset vectorised access vithout range check // Functions for read access to individual elements when sampled // on any (non-regular) grid. Index should be in units of knot-spacings. T operator()(double x, double y, double z) const {return(_vsp1D[0](x) * _vsp1D[1](y) * _vsp1D[2](z));} T operator()(float x, float y, float z) const {return(this->operator()(static_cast(x),static_cast(y),static_cast(z)));} // Function for read access to value of spline given by cindx // at voxel location given by vox. T SplineValueAtVoxel(const std::vector& vox, // Voxel index const std::vector& cindx) const // Spline index { return(_vsp1D[2].SplineValueAtVoxel(vox[2],cindx[2]) * _vsp1D[1].SplineValueAtVoxel(vox[1],cindx[1]) * _vsp1D[0].SplineValueAtVoxel(vox[0],cindx[0])); } T SplineValueAtVoxel(const std::vector& vox, // Voxel index const std::vector& cindx) const // Spline index { std::vector dvox(3,0); for (int i=0; i<3; i++) dvox[i] = static_cast(vox[i]); return(SplineValueAtVoxel(dvox,cindx)); } T SplineValueAtVoxel(const std::vector& vox, // Voxel index const std::vector& cindx) const // Spline index { std::vector dvox(3,0); for (int i=0; i<3; i++) dvox[i] = static_cast(vox[i]); return(SplineValueAtVoxel(dvox,cindx)); } // Functions for access to entire spline kernel NEWMAT::ReturnMatrix AsNewmat() const; // Arithmetic operations on splines. Spline3D& operator*=(double s); // Multiplication of self by scalar Spline3D& operator/=(double s) {return((*this)*=(1.0/s));} // Division of self by scalar // Functions returning the range of spline-kernels that has an overlap // with that given by cindx. The first form assumes that "this" spline // has the same knot-spacing as those it overlaps with. The second form // allows for passing a different spline by which "this" overlaps. // Note that I am using the C indexing convention here so that last // is one past. I.e. the range is first -- (last-1). bool RangeOfOverlappingSplines(const std::vector& cindx, const std::vector& isz, std::vector& first, std::vector& last) const; bool RangeOfOverlappingSplines(const std::vector& cindx, const std::vector& isz, const Spline3D& sp2, std::vector& first, std::vector& last) const; // The range functions will return the first and last index (zero-offset) // into a field in which the spline is the cindx'th (zero-offset) spline. // Note that the range is really first -- (last-1). void RangeInField(const std::vector& cindx, // Index of spline/coefficient const std::vector& isz, // Size of image/field std::vector& first, // First index in x-, y- and direction std::vector& last) const // Last index in x-, y- and direction { for (unsigned int i=0; i<3; i++) _vsp1D[i].Range(cindx[i],isz[i],first[i],last[i]); } void XRangeInField(unsigned int cindx, unsigned int isz, unsigned int& first, unsigned int& last) const { _vsp1D[0].Range(cindx,isz,first,last); } void YRangeInField(unsigned int cindx, unsigned int isz, unsigned int& first, unsigned int& last) const { _vsp1D[1].Range(cindx,isz,first,last); } void ZRangeInField(unsigned int cindx, unsigned int isz, unsigned int& first, unsigned int& last) const { _vsp1D[2].Range(cindx,isz,first,last); } // The RangeOfSplines functions will return the first and (one past) last indicies // of spline kernels that have a support (non-zero) value at a voxel location. void RangeOfSplines(const std::vector& vox, // Voxel index (does not need to be integer) const std::vector& csz, // Coefficient matrix size std::vector& first, // Index of first spline with support for vox std::vector& last) const // Index of last spline with support for vox { for (unsigned int i=0; i<3; i++) _vsp1D[i].RangeOfSplines(vox[i],csz[i],first[i],last[i]); } void RangeOfSplines(const std::vector& vox, // Voxel index (does not need to be integer) const std::vector& csz, // Coefficient matrix size std::vector& first, // Index of first spline with support for vox std::vector& last) const // Index of last spline with support for vox { std::vector dvox(3,0); for (int i=0; i<3; i++) dvox[i] = static_cast(vox[i]); RangeOfSplines(dvox,csz,first,last); } void RangeOfSplines(const std::vector& vox, // Voxel index const std::vector& csz, // Coefficient matrix size std::vector& first, // Index of first spline with support for vox std::vector& last) const // Index of last spline with support for vox { std::vector dvox(3,0); for (int i=0; i<3; i++) dvox[i] = static_cast(vox[i]); RangeOfSplines(dvox,csz,first,last); } // The offset functions returns the offset into the spline kernel to the first // index in the spline that falls inside the field. void OffsetIntoKernel(const std::vector& indx, // Index of spline/coefficient const std::vector& isz, // Size of image/field std::vector& offs) const // Offset in x-, y- and z-direction { for (unsigned int i=0; i<3; i++) offs[i] = _vsp1D[i].Offset(indx[i],isz[i]); } unsigned int XOffsetIntoKernel(unsigned int indx, unsigned int isz) const { return(_vsp1D[0].Offset(indx,isz)); } unsigned int YOffsetIntoKernel(unsigned int indx, unsigned int isz) const { return(_vsp1D[1].Offset(indx,isz)); } unsigned int ZOffsetIntoKernel(unsigned int indx, unsigned int isz) const { return(_vsp1D[2].Offset(indx,isz)); } // The premul function is useful for multiplying a spline with a field once and for all // before multiplying it with other splines. Note that all the read-access methods // returns the pre-multiplied values after a call to this function. template void Premul(const std::vector& indx, // Index of spline/coefficient into field of same size as ima const std::vector& isz, // Size of ima const S *ima); // Well. // The NzMax routines calculates the maximum # of non-zero elements of A'*B where // A and B are matrices with one column per spline and each column containing a // spline kernel. The first form assume identical splines in A and B. unsigned int NzMax(const std::vector& isz) const; unsigned int NzMax(const std::vector& isz, const Spline3D& sp2) const; T MulByOther(const std::vector& cindx1, const std::vector& cindx2, const std::vector& isz, const Spline3D& sp2) const; private: std::vector > _vsp1D; // Vector of 1D splines T *_osp3D; // 3D spline kernel T *_sp3D; // 3D spline kernel, possibly pre-multiplied bool _premul; // True if _sp3D has been pre-multiplied by image (or scalar) void common_construction(unsigned int order, const std::vector& ksp, const std::vector& deriv); }; //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class Spline1D: // This is for all purposes a helper class for Spline3D. // It implements some basic characteristics of 1D splines. // Its interface includes // // Operator () : // One offset read access with range check. // Peek: // One offset read access without range check // Operator [] : // Zero offset read access without range check. // Range: // Given a coef/spline index returns range of non-zero indicies in a 1D spline field // Offset: // Given a coef/spline index returns first index in spline that falls within field // GetAMatrix: // Returns A such that A*coef gives 1D field, where coef ColumnVector of coefficients // GetMMatrix: // Returns M that can be used to zoom up and down between knot-spacings. // //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ template class Spline1D { public: Spline1D(unsigned int order, unsigned int ksp, unsigned int deriv=0) : _order(order), _ksp(ksp), _deriv(deriv), _n(spline_size()), _sp(spline_kernel()) { if (_order < 2 || _order > 3) throw FslSplinesException("Spline1D::Spline1D: only orders 2 and 3 implemented"); if (_ksp < 1) throw FslSplinesException("Spline1D::Spline1D: knot-spacing has to be > 0"); if (_deriv > (_order)) throw FslSplinesException("Spline1D::Spline1D: only differentiable order times"); } Spline1D(const Spline1D& insp) : _order(insp._order), _ksp(insp._ksp), _deriv(insp._deriv), _n(insp._n), _sp(spline_kernel()) {} Spline1D() : _order(3), _ksp(1), _deriv(0), _n(spline_size()), _sp(spline_kernel()) {} ~Spline1D() {delete[] _sp;} Spline1D& operator=(const Spline1D& insp) { if (this != &insp) { _order = insp._order; _ksp = insp._ksp; _deriv = insp._deriv; _n = insp._n; delete[] _sp; _sp = spline_kernel(); } return(*this); } // Functions for enquring about properties of spline unsigned int Order() const {return(_order);} unsigned int KnotSpacing() const {return(_ksp);} unsigned int DerivativeOrder() const {return(_deriv);} unsigned int KernelSize() const {return(_n);} unsigned int FullFOV(unsigned int csz) const; unsigned int NCoef(unsigned int isz) const; // Returns the offset (in the field, zero-offset) for the center // of the specified spline (default is first spline). int OffsetIntoField(unsigned int cindx=0) const; // Returns the range of voxels for which cindx has // support (i.e. non-zero value). // Note that range is really first -- (last-1). void Range(// Input unsigned int cindx, // Index of coefficient unsigned int isz, // Size of image/field // Output unsigned int& first, // First index (into field) of spline unsigned int& last) const; // Last index (into field) of spline // Returns the range of splines that has support // (i.e. non-zero value) for vox. // Note that the range is really first -- (last-1). void RangeOfSplines(double vox, // Voxel index (can be non-integer) unsigned int csz, // Coefficient matrix size unsigned int& first, // Index of first spline with support for vox unsigned int& last) const; // Index of last spline with support for vox // Returns first index (into spline-kernel that falls within field/image unsigned int Offset(unsigned int cindx, // Index of coefficient unsigned int isz) const; // Image/field-size // Returns the rance of spline/coefficient indicies that overlap with *this // Note that range is really first -- (last-1) bool RangeOfOverlappingSplines(// Input unsigned int cindx, // Index of coefficient/spline unsigned int isz, // Size of image/field // Output unsigned int& first, // First coefficient/spline with overlap unsigned int& last) const; // Last coefficient/spline with overlap bool RangeOfOverlappingSplines(// Input unsigned int cindx, // Index of coefficient/spline unsigned int isz, // Size of image/field const Spline1D& sp2, // Type of spline that we want to know overlap with // Output unsigned int& first, // First coefficient/spline with overlap unsigned int& last) const; // Last coefficient/spline with overlap // Returns the value of spline given by cindx at voxel vox T SplineValueAtVoxel(double vox, // Voxel index unsigned int cindx) const // Spline coefficient index { return(kernel_value(vox2splindx(vox,cindx),_order,_ksp,_deriv)); } // Functions for read access to individual elements when sampled // on a regular grid corresponding to voxels where knot-spacing // is an integer number of voxels. T operator()(unsigned int indx) const // One offset with range check { if (indx>0 && indx<(_n+1)) return(_sp[indx-1]); else throw FslSplinesException("Spline1D::operator(): index out of range"); } T Peek(unsigned int indx) const {return(_sp[indx-1]);} // One offset without range check T operator[](unsigned int indx) const {return(_sp[indx]);} // Zero offset without range check // Functions for read access to individual elements when sampled // on any (non-regular) grid. Index should be in units of knot-spacings. T operator()(double x) const {return(this->kernel_value(x,_order,_ksp,_deriv));} T operator()(float x) const {return(this->operator()(static_cast(x)));} // Functions for obtaining reference to/copy of entire kernel const T* Kernel() const {return(_sp);} NEWMAT::ReturnMatrix AsNewmat() const; // Functions for generating/zooming 1D fields NEWMAT::ReturnMatrix GetAMatrix(unsigned int isz, // Image/field size unsigned int csz=0) const; // # of splines/coefficients NEWMAT::ReturnMatrix GetMMatrix(const Spline1D& s, // Spline for which we want coefficients unsigned int isz, // Size of image/field unsigned int csz1=0, // # of coefficients we know for this spline unsigned int csz2=0) const; // # of coefficients we want to know for s private: unsigned int _order; unsigned int _ksp; unsigned int _deriv; unsigned int _n; T *_sp; unsigned int spline_size(unsigned int order=0, unsigned int ksp=0) { if (!order) order = _order; if (!ksp) ksp = _ksp; if (!(order%2) && ksp%2) { // If even order (e.g. quadratic splines) and odd knot-spacing return((order+1)*ksp); } else { return((order+1)*ksp-1); } } double vox2splindx(double vox, unsigned int cindx) const { return((vox-OffsetIntoField(cindx))/static_cast(_ksp)); } T* spline_kernel(unsigned int order=0, unsigned int ksp=0, unsigned int deriv=0); T kernel_value(double x, unsigned int order, unsigned int ksp, unsigned int deriv) const; }; ///////////////////////////////////////////////////////////////////// // // Here starts memeber functions for Spline3D // ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// // // Multiplication by scalar // ///////////////////////////////////////////////////////////////////// template Spline3D& Spline3D::operator*=(double s) { if (_premul) throw FslSplinesException("Spline3D::operator*=: You should not combine multiplication by scalar and image"); for (unsigned int i=0; i Spline3D operator*(const Spline3D& sp, double s) { Spline3D spo(sp); spo *= s; return(spo); } template Spline3D operator*(double s, const Spline3D& sp) { return(sp*s); } template Spline3D operator/(const Spline3D& sp, double s) { Spline3D spo(sp); spo /= s; return(spo); } ///////////////////////////////////////////////////////////////////// // // Zero-offset read-only access with range check // ///////////////////////////////////////////////////////////////////// template inline T Spline3D::operator()(unsigned int i, unsigned int j, unsigned int k) const { if (i>=0 && i<_vsp1D[0].KernelSize() && j>=0 && j<_vsp1D[1].KernelSize() && k>=0 && k<_vsp1D[2].KernelSize()) { return(_sp3D[k*_vsp1D[1].KernelSize()*_vsp1D[0].KernelSize()+j*_vsp1D[0].KernelSize()+i]); } else { throw FslSplinesException("Spline3D::operator(): index out of range"); } } ///////////////////////////////////////////////////////////////////// // // Return entire 3D kernel in NEWMAT matrix format // ///////////////////////////////////////////////////////////////////// template NEWMAT::ReturnMatrix Spline3D::AsNewmat() const { NEWMAT::Matrix M(KernelSize(1)*KernelSize(2),KernelSize(0)); for (unsigned int k=0; k T Spline3D::MulByOther(const std::vector& cindx1, const std::vector& cindx2, const std::vector& isz, const Spline3D& sp2) const { std::vector f1(3,0); // First index into *this of overlapping part std::vector f2(3,0); // First index into sp2 of overlapping part std::vector l1(3,0); // Last index into *this of overlapping part unsigned int if1 = 0, if2 = 0; // First index given cindx1[i] and cindx2[i] in (some) ima of size isz[i] unsigned int il1 = 0, il2 = 0; // Last index given cindx1[i] and cindx2[i] in (some) ima of size isz[i] unsigned int o1 = 0, o2 = 0; // Offset into spline kernel for cindx1[i] and cindx2[i] respectively T prod = static_cast(0.0); // Product for (unsigned int d=0; d<3; d++) { _vsp1D[d].Range(cindx1[d],isz[d],if1,il1); o1 = _vsp1D[d].Offset(cindx1[d],isz[d]); sp2._vsp1D[d].Range(cindx2[d],isz[d],if2,il2); o2 = sp2._vsp1D[d].Offset(cindx2[d],isz[d]); f1[d] = max(static_cast(if2)-static_cast(if1),0) + o1; f2[d] = max(static_cast(if1)-static_cast(if2),0) + o2; l1[d] = min(il1,il2) - if1 + o1; } for (unsigned int k1=f1[2], k2=f2[2]; k1operator[](bi12+i1) * sp2[bi22+i2]; } } } return(prod); } ///////////////////////////////////////////////////////////////////// // // Get maximum # of non-zero elements in A'*B. // ///////////////////////////////////////////////////////////////////// template unsigned int Spline3D::NzMax(const std::vector& isz) const { unsigned int nzmax=0; for (unsigned int k=0; k<_vsp1D[2].NCoef(isz[2]); k++) { unsigned int first=0, last=0; _vsp1D[2].RangeOfOverlappingSplines(k,isz[2],first,last); unsigned int zn = last-first; for (unsigned int j=0; j<_vsp1D[1].NCoef(isz[1]); j++) { _vsp1D[1].RangeOfOverlappingSplines(j,isz[1],first,last); unsigned int yn = last-first; for (unsigned int i=0; i<_vsp1D[0].NCoef(isz[0]); i++) { _vsp1D[0].RangeOfOverlappingSplines(i,isz[0],first,last); nzmax += zn*yn*(last-first); } } } return(nzmax); } template unsigned int Spline3D::NzMax(const std::vector& isz, const Spline3D& sp2) const { unsigned int nzmax=0; for (unsigned int k=0; k<_vsp1D[2].NCoef(isz[2]); k++) { unsigned int first=0, last=0; _vsp1D[2].RangeOfOverlappingSplines(k,isz[2],sp2._vsp1D[2],first,last); unsigned int zn = last-first; for (unsigned int j=0; j<_vsp1D[1].NCoef(isz[1]); j++) { _vsp1D[1].RangeOfOverlappingSplines(j,isz[1],sp2._vsp1D[1],first,last); unsigned int yn = last-first; for (unsigned int i=0; i<_vsp1D[0].NCoef(isz[0]); i++) { _vsp1D[0].RangeOfOverlappingSplines(i,isz[0],sp2._vsp1D[0],first,last); nzmax += zn*yn*(last-first); } } } return(nzmax); } ///////////////////////////////////////////////////////////////////// // // Get range of indicies of splines that has an overlap with *this // ///////////////////////////////////////////////////////////////////// template bool Spline3D::RangeOfOverlappingSplines(const std::vector& cindx, const std::vector& isz, std::vector& first, std::vector& last) const { for (unsigned int i=0; i<3; i++) { if (!_vsp1D[i].RangeOfOverlappingSplines(cindx[i],isz[i],first[i],last[i])) return(false); } return(true); } template bool Spline3D::RangeOfOverlappingSplines(const std::vector& cindx, const std::vector& isz, const Spline3D& sp2, std::vector& first, std::vector& last) const { for (unsigned int i=0; i<3; i++) { if (!_vsp1D[i].RangeOfOverlappingSplines(cindx[i],isz[i],sp2._vsp1D[i],first[i],last[i])) return(false); } return(true); } ///////////////////////////////////////////////////////////////////// // // Pre-multiply spline with image. // ///////////////////////////////////////////////////////////////////// template template void Spline3D::Premul(const std::vector& cindx, // Index of spline/coefficient into field of same size as ima const std::vector& isz, // Size of ima const S *ima) // Well. { std::vector fi(3,0); // First indicies into ima std::vector li(3,0); // Last indicies into ima std::vector so(3,0); // Offset into spline RangeInField(cindx,isz,fi,li); OffsetIntoKernel(cindx,isz,so); memset(_sp3D,0,TotalKernelSize()*sizeof(T)); for (unsigned int ik=fi[2], sk=so[2]; ik(ima[ibi+ii]); } } } _premul = true; } ///////////////////////////////////////////////////////////////////// // // Private helper function for constructors of Spline3D // ///////////////////////////////////////////////////////////////////// template void Spline3D::common_construction(unsigned int order, const std::vector& ksp, const std::vector& deriv) { unsigned int size = 1; for (unsigned int i=0; i<3; i++) { _vsp1D[i] = Spline1D(order,ksp[i],deriv[i]); size *= _vsp1D[i].KernelSize(); } _osp3D = new T[size]; for (unsigned int k=0;k<_vsp1D[2].KernelSize(); k++) { for (unsigned int j=0; j<_vsp1D[1].KernelSize(); j++) { unsigned int bi = k*_vsp1D[1].KernelSize()*_vsp1D[0].KernelSize()+j*_vsp1D[0].KernelSize(); for (unsigned int i=0; i<_vsp1D[0].KernelSize(); i++) { _osp3D[bi+i] = _vsp1D[2][k]*_vsp1D[1][j]*_vsp1D[0][i]; } } } _sp3D = new T[size]; memcpy(_sp3D,_osp3D,TotalKernelSize()*sizeof(T)); } ///////////////////////////////////////////////////////////////////// // // Here starts memeber functions for Spline1D // ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// // // Returns the range of indicies of splines that overlap with self, // when the other splines are of same type as self. // ///////////////////////////////////////////////////////////////////// template inline bool Spline1D::RangeOfOverlappingSplines(// Input unsigned int cindx, // Index of coefficient/spline unsigned int isz, // Size of image/field // Output unsigned int& first, // First coefficient/spline with overlap unsigned int& last) const // One past last coefficient/spline with overlap { if (_ksp==1) { first = max(0,static_cast(cindx) - static_cast(2*(_order/2))); last = min(NCoef(isz),cindx+2*(_order/2)+1); } else { first = max(0,static_cast(cindx) - static_cast(_order)); last = min(NCoef(isz),cindx+_order+1); } if (last > first) return(true); else return(false); } template bool Spline1D::RangeOfOverlappingSplines(// Input unsigned int cindx, // Index of coefficient/spline unsigned int isz, // Size of image/field const Spline1D& sp2, // Type of spline that we want to know overlap with // Output unsigned int& first, // First coefficient/spline with overlap unsigned int& last) const // Last coefficient/spline with overlap { unsigned int fme, lme; // First and last index of *this into ima of size isz unsigned int fsp2, lsp2; // First and lasp index of sp2 into ima of size isz int ifirst; // Signed version of first Range(cindx,isz,fme,lme); // cout << "Range is " << fme << " -- " << lme << endl; ifirst = -1; for (unsigned int i=0; i= 0) { // If there is at least one common voxel if (ifirst == -1) ifirst = i; else last = i+1; } else { // If there is no overlap if (ifirst != -1) break; } } if (ifirst == -1) { // If there were no overlap at all first = 0; last = 0; // Return zero range return(false); } else { first = static_cast(ifirst); return(true); } } ///////////////////////////////////////////////////////////////////// // // Returns the first and (one past) last index into a zero-offset // field for the spline with index cindx. // ///////////////////////////////////////////////////////////////////// template inline void Spline1D::Range(// Input unsigned int cindx, // Index of coefficient unsigned int isz, // Size of image/field // Output unsigned int& first, // First index (into field) of spline unsigned int& last) const // One past last index (into field) of spline { switch(_order) { case 2: case 3: first = std::max(0,OffsetIntoField(cindx)-static_cast(KernelSize()/2)); last = std::min(static_cast(isz),OffsetIntoField(cindx)+static_cast(KernelSize()/2)+1); // cout << "New range = " << first << " -- " << last << endl; // first = std::max(0,(static_cast(cindx)-3)*static_cast(_ksp) + 1); // last = min(isz,(cindx+1)*_ksp - 1); // Scary change made 21/2-08. J.A. // last = std::min(isz,(cindx+1)*_ksp); // cout << "Old range = " << first << " -- " << last << endl; break; default: throw FslSplinesException("Spline1D::Range: Only orders 2 and 3 implemented"); break; } } ///////////////////////////////////////////////////////////////////// // // Returns the first and (one past) last coefficient/spline index // that has support for voxel vox. // ///////////////////////////////////////////////////////////////////// template inline void Spline1D::RangeOfSplines(// Input double vox, // Voxel index (can be non-integer) unsigned int csz, // Coefficient matrix size // Output unsigned int& first, // Index of first spline with support for vox unsigned int& last) const // Index of last spline with support for vox { first = std::max(0,static_cast(ceil((vox - static_cast(OffsetIntoField(0)) - (static_cast(KernelSize())+1.0)/2.0) / _ksp))); int tmp = std::min(static_cast(csz),static_cast(ceil((vox - static_cast(OffsetIntoField(0)) + (static_cast(KernelSize())+1.0)/2.0) / _ksp))); if (tmp < 0) last = 0; else last = static_cast(tmp); } ///////////////////////////////////////////////////////////////////// // // Returns the offset (in the field, zero-offset) for the center // (middle) element of the specified spline. // ///////////////////////////////////////////////////////////////////// template inline int Spline1D::OffsetIntoField(unsigned int cindx) const { int oif = 0; if (_ksp == 1) oif = cindx*_ksp; else oif = (cindx - _order/2)*_ksp; return(oif); } ///////////////////////////////////////////////////////////////////// // // Returns the offset (in the spline) for the first // element of the spline that falls within the field. // ///////////////////////////////////////////////////////////////////// template inline unsigned int Spline1D::Offset(unsigned int cindx, // Index of coefficient unsigned int isz) const // Image/field-size { unsigned int os=0; switch(_order) { case 2: case 3: os = -std::min(0,OffsetIntoField(cindx)-static_cast(KernelSize())/2); // cout << "New Offset = " << os << endl; // os = -std::min(0,(static_cast(cindx)-3)*static_cast(_ksp) + 1); // cout << "Old Offset = " << os << endl; break; default: throw FslSplinesException("Spline1D::Offset: Only orders 2 and 3 implemented"); break; } return(os); } ///////////////////////////////////////////////////////////////////// // // Returns a copy of the spline kernel as a NEWMAT ColumnVector. // ///////////////////////////////////////////////////////////////////// template NEWMAT::ReturnMatrix Spline1D::AsNewmat() const { NEWMAT::ColumnVector s(_n); for (unsigned int i=0; i<_n; i++) s.element(i) = static_cast(_sp[i]); s.Release(); return(s); } ///////////////////////////////////////////////////////////////////// // // Returns the "full" FOV (int # of voxels) for csz # of splines. // With "full" I mean all those voxels for which there is a non-zero // representation of any spline. // ///////////////////////////////////////////////////////////////////// template unsigned int Spline1D::FullFOV(unsigned int csz) const { if (KernelSize() % 2) { // If kernel size odd # of voxels return((csz-1) * KnotSpacing() + 1 + KernelSize() - 1); } else { // If kernel size even # of voxels return((csz-1) * KnotSpacing() + 1 + KernelSize()); } } ///////////////////////////////////////////////////////////////////// // // Returns the smallest number of coefficients/splines neccesary // to completely specify a 1D spline function over the range 1-isz // (or 0-(isz-1) if you prefer). // Quite infuriatingly I am using a value that is sometimes // non-optimal for _order=3. This is in order to ensure backwards // compatibilty of some applications. The "correct" value is that // which is given in the else branch. // ///////////////////////////////////////////////////////////////////// template unsigned int Spline1D::NCoef(unsigned int isz) const { unsigned int nc=0; if (_ksp==1) nc = isz; else if (_order==3) { nc = static_cast(ceil(double(isz+1)/double(_ksp))) + 2; } else { nc = isz / _ksp + _order/2; while (OffsetIntoField(nc)-KernelSize()/2 < isz-1) nc++; } return(nc); } ///////////////////////////////////////////////////////////////////// // // Returns matrix M that allows you to map the coefficients for a // 1D spline function from one knot-spacing to another. It is intended // for use in zooming (in and out) splinefields. Let us say we have // a spline sp1 and a set of coefficients c1 giving a 1D function // over a range 1-64 through f = sp1.A(64)*c1; Let us further say // that we have another spline (with a different knot-spacing) sp2 // and that we want to find the coefficients c2 such that // f = sp2.A(64)*c2; We can then find c2 from c2 = sp2.M(sp1,64)*c1; // If sp2.KnotSpacing()<=sp1.KnotSpacing() the result will be "exact", // i.e. f is identical. If sp2.KnotSpacing()>sp1.KnotSpacing() the // result is optimal in a least-squares sense. // ///////////////////////////////////////////////////////////////////// template NEWMAT::ReturnMatrix Spline1D::GetMMatrix(const Spline1D& s, unsigned int isz, unsigned int csz1, unsigned int csz2) const { if (!csz1) csz1 = NCoef(isz); if (!csz2) csz2 = s.NCoef(isz); NEWMAT::Matrix A1 = GetAMatrix(isz,csz1); NEWMAT::Matrix A2 = s.GetAMatrix(isz,csz2); // NEWMAT::Matrix M = (A1.t()*A1).i() * (A1.t()*A2); NEWMAT::Matrix M = MISCMATHS::pinv(A1.t()*A1) * (A1.t()*A2); M.Release(); return(M); } ///////////////////////////////////////////////////////////////////// // // Returns matrix A that allows you to calculate a 1D spline function // f over a range 1-isz given a set of coefficients c from f = A*c, // where f and c are NEWMAT::ColumnVector's. // ///////////////////////////////////////////////////////////////////// template NEWMAT::ReturnMatrix Spline1D::GetAMatrix(unsigned int isz, unsigned int csz) const { if (!csz) csz = NCoef(isz); NEWMAT::Matrix A(isz,csz); A = 0.0; unsigned int fr=0, lr=0; unsigned int so=0; for (unsigned int i=0; i NEWMAT::ReturnMatrix Spline1D::GetAMatrix(unsigned int isz, unsigned int csz) const { if (!csz) csz = NCoef(isz); NEWMAT::Matrix A(isz,csz); A = 0.0; for (unsigned int r=1; r<=isz; r++) { if ((r % _ksp) == 1) { for (int i=1; i<4; i++) { unsigned int c = (r / _ksp) + i; // Intentional integer division if (c>=1 && c<=csz) { A(r,c) = _sp[_n - i*_ksp]; } } } else { for (int i=0; i<4; i++) { unsigned int c = ((r-1) / _ksp) + i + 1; // Intentional integer division if (c>=1 && c<=csz) { A(r,c) = _sp[_n - (i+1)*_ksp + ((r-1)%_ksp)]; } } } } A.Release(); return(A); } */ ///////////////////////////////////////////////////////////////////// // // Internal (private) function that returns the value of a spline // for the abscissa x. x should be in units of knot-spacings from // the centre of the spline. // ///////////////////////////////////////////////////////////////////// template T Spline1D::kernel_value(double x, unsigned int order, unsigned int ksp, unsigned int deriv) const { double ax = fabs(x); switch (order) { case 0: case 1: switch (deriv) { case 0: if (ax < 1) return(static_cast(1 - ax)); else return(static_cast(0.0)); break; case 1: if (ax < 1e-6) return(static_cast(0.0)); // Smack on center discontinuity else if (fabs(ax-1.0) < 1e-6) return(static_cast(-(x/ax) * 0.5 / double(ksp))); // Smack on edge discontinuity else if (ax < 1.0) return(static_cast(-(x/ax) / double(ksp))); break; } break; case 2: switch (deriv) { case 0: if (ax <= 0.5) return(static_cast(0.75 - ax*ax)); else if (ax < 1.5) return(static_cast(0.5 * (1.5-ax) * (1.5-ax))); else return(static_cast(0.0)); break; case 1: if (ax < 1e-6) return(static_cast(0.0)); else if (ax <= 0.5) return(static_cast(- (x/ax) * 2.0 * (ax / double(ksp)))); else if (ax < 1.5) return(static_cast((x/ax) * 0.5 * ((2.0*ax - 3)/double(ksp)))); else return(static_cast(0.0)); break; case 2: if (fabs(ax-1.5) < 1e-6) return(static_cast(0.5 / double(ksp*ksp))); // Smack on edge discontinuity else if (fabs(ax-0.5) < 1e-6) return(static_cast(-0.5 / double(ksp*ksp))); // Smack on inner discontinuity else if (ax < 0.5) return(static_cast(-2.0 / double(ksp*ksp))); else if (ax < 1.5) return(static_cast(1.0 / double(ksp*ksp))); else return(static_cast(0.0)); break; default: break; } break; case 3: switch (deriv) { case 0: if (ax <= 1.0) return(static_cast((2.0/3.0) + (ax*ax)*(ax/2.0 - 1))); else if (ax < 2.0) return(static_cast((1.0/6.0) * (2.0-ax)*(2.0-ax)*(2.0-ax))); else return(static_cast(0.0)); break; case 1: if (ax < 1e-6) return(static_cast(0.0)); else if (ax <= 1.0) return(static_cast((x/ax) * (1.5*ax*ax - 2.0*ax) / double(ksp))); else if (ax < 2.0) return(static_cast((x/ax) * (-0.5*(2.0-ax)*(2.0-ax)) / double(ksp))); else return(static_cast(0.0)); break; case 2: if (ax <= 1.0) return(static_cast((3.0*ax - 2.0)/double(ksp*ksp))); else if (ax < 2.0) return(static_cast((2.0-ax)/double(ksp*ksp))); else return(static_cast(0.0)); break; default: break; } break; default: break; } return(0.0); } ///////////////////////////////////////////////////////////////////// // // Internal (private) function that is used to calculate and // store the spline function on a regular grid. // ///////////////////////////////////////////////////////////////////// template T* Spline1D::spline_kernel(unsigned int order, unsigned int ksp, unsigned int deriv) { if (!order) order = _order; if (!ksp) ksp = _ksp; if (!deriv) deriv = _deriv; unsigned int spsz = spline_size(order,ksp); T *tsp = new T[spsz]; double coa = static_cast(spsz-1) / 2.0; // Centre of array in voxels for (unsigned int i=0; i(i-coa)/static_cast(ksp); tsp[i] = kernel_value(x,order,ksp,deriv); } return(tsp); } ///////////////////////////////////////////////////////////////////// // // Obsolete Internal (private) function that used to calculate // and store the spline function on a regular grid. // ///////////////////////////////////////////////////////////////////// /* template T* Spline1D::spline_kernel(unsigned int order, unsigned int ksp, unsigned int deriv) { if (!order) order = _order; if (!ksp) ksp = _ksp; if (!deriv) deriv = _deriv; // cout << "order = " << order << ", ksp = " << ksp << ", spline_size(order,ksp) = " << spline_size(order,ksp) << endl; T *tsp = new T[spline_size(order,ksp)]; switch (order) { case 1: break; case 2: // cout << "I'm constructing 2nd order spline" << endl; { unsigned int coffs = spline_size(order,ksp) / 2; // Deliberate truncation for (unsigned int i=0; i