/* newimage.cc Mark Jenkinson, FMRIB Image Analysis Group Copyright (C) 2000 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 "newmatio.h" #include "newimage.h" #include "fslio/fslio.h" using namespace NEWMAT; using namespace LAZY; using namespace MISCMATHS; namespace NEWIMAGE { void imthrow(const string& msg, int nierrnum) { cerr << "Image Exception : #" << nierrnum << " :: " << msg << endl; if (USEASSERT) { bool no_error=false; assert(no_error); } else { throw Exception(msg.data()); } } // interfaces for lazy evaluated calculations template minmaxstuff calc_minmax(const volume& vol); template minmaxstuff calc_minmax(const volume& vol, const volume& mask); template std::vector calc_sums(const volume& vol); template std::vector calc_sums(const volume& vol, const volume& mask); template T calc_backgroundval(const volume& vol); template ColumnVector calc_cog(const volume& vol); template std::vector calc_robustlimits(const volume& vol); template Matrix calc_principleaxes(const volume& vol); template std::vector calc_percentiles(const volume& vol); template std::vector calc_percentiles(const volume& vol, const volume& mask, const std::vector& percentilepvals); template ColumnVector calc_histogram(const volume& vol); template ColumnVector calc_histogram(const volume& vol, const volume& mask); template ColumnVector calc_histogram(const volume4D& vol); template ColumnVector calc_histogram(const volume4D& vol, const volume4D& mask); template ColumnVector calc_histogram(const volume4D& vol, const volume& mask); template SPLINTERPOLATOR::Splinterpolator calc_spline_coefs(const volume& vol); // Declaration of helper functions. SPLINTERPOLATOR::ExtrapolationType translate_extrapolation_type(extrapolation ep); // CONSTRUCTORS (not including copy constructor - see under copying) template int volume::initialize(int xsize, int ysize, int zsize, T *d, bool d_owner) { // set up storage this->destroy(); SlicesZ = zsize; RowsY = ysize; ColumnsX = xsize; SizeBound = SlicesZ * RowsY * ColumnsX; SliceOffset = RowsY * ColumnsX; if (SizeBound > 0) { if (d != 0) { Data = d; data_owner = d_owner; } else { try { Data = new T[SizeBound]; } catch(...) { Data=0; } if (Data==0) { imthrow("Out of memory",99); } data_owner = true; } } else { Data = 0; data_owner = false; } setdefaultproperties(); return 0; } template void volume::setdefaultproperties() { Xdim = 1.0; Ydim = 1.0; Zdim = 1.0; StandardSpaceCoordMat = IdentityMatrix(4); RigidBodyCoordMat = IdentityMatrix(4); StandardSpaceTypeCode = NIFTI_XFORM_UNKNOWN; RigidBodyTypeCode = NIFTI_XFORM_UNKNOWN; RadiologicalFile = true; IntentCode = NIFTI_INTENT_NONE; IntentParam1 = 0.0; IntentParam2 = 0.0; IntentParam3 = 0.0; SliceOrderingCode = NIFTI_SLICE_UNKNOWN; Limits.resize(6,0); setdefaultlimits(); // Default ROI is whole volume ROIbox = Limits; activeROI = false; calc_no_voxels(); minmax.init(this,calc_minmax); sums.init(this,calc_sums); backgroundval.init(this,calc_backgroundval); lazycog.init(this,calc_cog); robustlimits.init(this,calc_robustlimits); principleaxes.init(this,calc_principleaxes); percentiles.init(this,calc_percentiles); l_histogram.init(this,calc_histogram); splint.init(this,calc_spline_coefs); HISTbins=256; HISTmin=(T) 0; HISTmax=(T) 0; // Initial percentile pvals to store when calculating percentiles percentilepvals.erase(percentilepvals.begin(),percentilepvals.end()); percentilepvals.push_back(0.0); percentilepvals.push_back(0.001); percentilepvals.push_back(0.005); for (int probval=1; probval<=99; probval++) { percentilepvals.push_back(((float) probval)/100.0); } percentilepvals.push_back(0.995); percentilepvals.push_back(0.999); percentilepvals.push_back(1.0); p_interpmethod = trilinear; p_extrapmethod = zeropad; splineorder = 3; padvalue = (T) 0; extrapval = padvalue; p_userinterp = 0; p_userextrap = 0; ep_valid.resize(3); ep_valid[0] = false; ep_valid[1] = false; ep_valid[2] = false; displayMaximum=0; displayMinimum=0; strncpy(auxFile,string("").c_str(),24); set_whole_cache_validity(false); } template volume::volume() : Data(0), data_owner(false) { this->initialize(0,0,0,0,false); } template volume::volume(int xsize, int ysize, int zsize) : Data(0), data_owner(false) { this->initialize(xsize,ysize,zsize,0,true); } template volume::volume(int xsize, int ysize, int zsize, T *d, bool d_owner) : Data(d), data_owner(false) { this->initialize(xsize,ysize,zsize,d,d_owner); } template int volume::reinitialize(int xsize, int ysize, int zsize) { return this->initialize(xsize,ysize,zsize,0,true); } template int volume::reinitialize(int xsize, int ysize, int zsize, T *d, bool d_owner) { return this->initialize(xsize,ysize,zsize,d,d_owner); } template void volume::destroy() { if (data_owner) delete [] Data; Data = 0; } template volume::~volume() { this->destroy(); } template void volume::calc_no_voxels() const { no_voxels = ((unsigned long int)(maxx()-minx()+1)) *((unsigned long int) (maxy()-miny()+1)) *((unsigned long int) (maxz()-minz()+1)); } template void volume::setupsizeproperties() const { calc_no_voxels(); } template void volume::setdefaultlimits() const { Limits[0]=0; Limits[1]=0; Limits[2]=0; Limits[3]=this->xsize()-1; Limits[4]=this->ysize()-1; Limits[5]=this->zsize()-1; } template void volume::enforcelimits(std::vector& lims) const { // clamp all elements within valid bounds // lims[0]=Max(0,lims[0]); // lims[1]=Max(0,lims[1]); // lims[2]=Max(0,lims[2]); // lims[3]=Min(this->xsize() - 1,lims[3]); // lims[4]=Min(this->ysize() - 1,lims[4]); // lims[5]=Min(this->zsize() - 1,lims[5]); } // COPYING AND CONVERSION FUNCTIONS template int volume::reinitialize(const volume& source) { this->initialize(source.xsize(),source.ysize(),source.zsize(),0,false); this->copydata(source); return this->copyproperties(source); } template volume::volume(const volume& source) : Data(0), data_owner(false) { this->reinitialize(source); } template const volume& volume::operator=(const volume& source) { this->reinitialize(source); return *this; } template int volume::copydata(const volume& source) { if (SizeBound != source.SizeBound) { imthrow("Attempted to copydata with non-matching sizes",2); //return -1; } copy(source.Data, source.Data + SizeBound, Data); // use the STL data_owner = true; return 0; } template volume volume::ROI() const { volume roivol; roivol.reinitialize(this->maxx() - this->minx()+1, this->maxy() - this->miny()+1, this->maxz() - this->minz()+1,0,false); // now copy only the appropriate data for (int z=this->minz(); z<=this->maxz(); z++) { for (int y=this->miny(); y<=this->maxy(); y++) { for (int x=this->minx(); x<=this->maxx(); x++) { roivol(x - this->minx(),y - this->miny(),z - this->minz()) = (*this)(x,y,z); } } } roivol.copyproperties(*this); roivol.deactivateROI(); // set sform and qform matrices appropriately (if set) Matrix roi2vol= IdentityMatrix(4); roi2vol(1,4) = this->minx(); roi2vol(2,4) = this->miny(); roi2vol(3,4) = this->minz(); if (this->sform_code()!=NIFTI_XFORM_UNKNOWN) { roivol.set_sform(this->sform_code(),this->sform_mat() * roi2vol); } if (this->qform_code()!=NIFTI_XFORM_UNKNOWN) { roivol.set_qform(this->qform_code(),this->qform_mat() * roi2vol); } roivol.set_whole_cache_validity(false); return roivol; } template int volume::copyROIonly(const volume& source) { if (!samesize(*this,source)) { imthrow("Attempted to copy ROIs when different sizes",3); } // now copy only the appropriate data int xoff=source.minx()-minx(), yoff=source.miny()-miny(), zoff=source.minz()-minz(); for (int z=source.minz(); z<=source.maxz(); z++) { for (int y=source.miny(); y<=source.maxy(); y++) { for (int x=source.minx(); x<=source.maxx(); x++) { (*this)(x - xoff,y - yoff,z - zoff) = source(x,y,z); } } } set_whole_cache_validity(false); return 0; } template int volume::copyproperties(const volume& source) { // sets all properties copybasicproperties(source,*this); this->copylazymanager(source); minmax.copy(source.minmax,this); sums.copy(source.sums,this); backgroundval.copy(source.backgroundval,this); lazycog.copy(source.lazycog,this); robustlimits.copy(source.robustlimits,this); principleaxes.copy(source.principleaxes,this); percentiles.copy(source.percentiles,this); l_histogram.copy(source.l_histogram,this); HISTbins = source.HISTbins; HISTmin = source.HISTmin; HISTmax = source.HISTmax; percentilepvals = source.percentilepvals; p_userextrap = source.p_userextrap; p_userinterp = source.p_userinterp; return 0; } // Volume->ColumnVector template ReturnMatrix volume::vec(const volume& mask) const { if (!samesize(mask,*this)) {imthrow("volume::vec: Mask and volume of different size",3);} ColumnVector ovec(xsize()*ysize()*zsize()); for (int vindx=0,k=0; k0) ? (*this)(i,j,k) : 0.0; vindx++; } } } ovec.Release(); return ovec; } // Code multiplication to avoid allocating mask volume template ReturnMatrix volume::vec() const { ColumnVector ovec(xsize()*ysize()*zsize()); for (int vindx=0, k=0; k void volume::insert_vec(const ColumnVector& pvec, const volume& mask) { set_whole_cache_validity(false); if (pvec.Nrows() != xsize()*ysize()*zsize()) { cout << "pvec.Nrows() = " << pvec.Nrows() << endl; cout << "xsize() = " << xsize() << ", ysize() = " << ysize() << ", zsize() = " << zsize() << endl; imthrow("volume::insert_vec: Size mismatch between ColumnVector and image volume",3); } if (!samesize(mask,*this)) { imthrow("volume::insert_vec: Size mismatch between mask and image volume",3); } for (int vindx=0, k=0; k 0) ? ((T) pvec.element(vindx)) : ((T) 0); vindx++; } } } } template void volume::insert_vec(const ColumnVector& pvec) { set_whole_cache_validity(false); if (pvec.Nrows() != xsize()*ysize()*zsize()) { cout << "pvec.Nrows() = " << pvec.Nrows() << endl; cout << "xsize() = " << xsize() << ", ysize() = " << ysize() << ", zsize() = " << zsize() << endl; imthrow("volume::insert_vec: Size mismatch between ColumnVector and image volume",3); } for (int vindx=0, k=0; k vector volume::labelToCoord(const long label) const { vector coordinates; coordinates.push_back(label%this->xsize()); coordinates.push_back( (floor) ( ( label%( this->xsize()*this->ysize() ) ) / this->xsize() )); coordinates.push_back( (floor) ( label / ( this->xsize()*this->ysize() ) ) ); return coordinates; } // ROI functions template void volume::setROIlimits(int x0, int y0, int z0, int x1, int y1, int z1) const { // Enforce ordering ROIbox[0]=Min(x0,x1); ROIbox[1]=Min(y0,y1); ROIbox[2]=Min(z0,z1); ROIbox[3]=Max(x0,x1); ROIbox[4]=Max(y0,y1); ROIbox[5]=Max(z0,z1); enforcelimits(ROIbox); if (activeROI) activateROI(); } template void volume::setROIlimits(const std::vector& lims) const { if (lims.size()!=6) imthrow("ROI limits the wrong size (not 6) in volume::setROIlimits",13); setROIlimits(lims[0],lims[1],lims[2],lims[3],lims[4],lims[5]); } template void volume::activateROI() const { activeROI=true; enforcelimits(ROIbox); Limits = ROIbox; set_whole_cache_validity(false); setupsizeproperties(); } template void volume::deactivateROI() const { activeROI=false; setdefaultlimits(); set_whole_cache_validity(false); setupsizeproperties(); } int mirrorclamp(int x, int x1, int x2) { if (x2 x2) { nx = 2*x2 + 1 - nx; } return nx; } // EXTRAPOLATION AND INTERPOLATION template void volume::setinterpolationmethod(interpolation interpmethod) const { p_interpmethod = interpmethod; // define a default sinc kernel if no kernel has previously been defined if ( (interpmethod == sinc) && (interpkernel.kernelvals()==0) ) { string sincwindowtype = "blackman"; this->definesincinterpolation(sincwindowtype,7); } } template void volume::setsplineorder(unsigned int order) const { if (order > 7) imthrow("setsplineorder: Only splines of order up to 7 allowed",10); splineorder = order; } template bool in_neigh_bounds(const volume& vol, int x, int y, int z) { return ( (x>=0) && (y>=0) && (z>=0) && (x<(vol.xsize()-1)) && (y<(vol.ysize()-1)) && (z<(vol.zsize()-1)) ); } inline float q_tri_interpolation(float v000, float v001, float v010, float v011, float v100, float v101, float v110, float v111, float dx, float dy, float dz) { float temp1, temp2, temp3, temp4, temp5, temp6; temp1 = (v100 - v000)*dx + v000; temp2 = (v101 - v001)*dx + v001; temp3 = (v110 - v010)*dx + v010; temp4 = (v111 - v011)*dx + v011; // second order terms temp5 = (temp3 - temp1)*dy + temp1; temp6 = (temp4 - temp2)*dy + temp2; // final third order term return (temp6 - temp5)*dz + temp5; } //////// Kernel Interpolation Call ///////// template float volume::kernelinterpolation(const float x, const float y, const float z) const { const kernelstorage* storedkernel = interpkernel.kernelvals(); // sanity check on kernel if (storedkernel==0) { cerr << "ERROR: Must set kernel parameters before using interpolation!" << endl; return (float) extrapolate(0,0,0); } // kernel half-width (i.e. range is +/- w) int wx=storedkernel->widthx(); int wy=storedkernel->widthy(); int wz=storedkernel->widthz(); ColumnVector kernelx = storedkernel->kernelx(); ColumnVector kernely = storedkernel->kernely(); ColumnVector kernelz = storedkernel->kernelz(); float *storex = storedkernel->storex; float *storey = storedkernel->storey; float *storez = storedkernel->storez; int ix0, iy0, iz0; ix0 = (int) floor(x); iy0 = (int) floor(y); iz0 = (int) floor(z); float convsum=0.0, interpval=0.0, kersum=0.0; for (int d=-wz; d<=wz; d++) { storez[d+wz] = kernelval((z-iz0+d),wz,kernelz); } for (int d=-wy; d<=wy; d++) { storey[d+wy] = kernelval((y-iy0+d),wy,kernely); } for (int d=-wx; d<=wx; d++) { storex[d+wx] = kernelval((x-ix0+d),wx,kernelx); } int xj, yj, zj; for (int z1=iz0-wz; z1<=iz0+wz; z1++) { zj=iz0-z1+wz; for (int y1=iy0-wy; y1<=iy0+wy; y1++) { yj=iy0-y1+wy; for (int x1=ix0-wx; x1<=ix0+wx; x1++) { if (in_bounds(x1,y1,z1)) { xj=ix0-x1+wx; float kerfac = storex[xj] * storey[yj] * storez[zj]; convsum += this->operator()(x1,y1,z1) * kerfac; kersum += kerfac; } } } } if ( (fabs(kersum)>1e-9) ) { interpval = convsum / kersum; } else { interpval = (float) extrapolate(ix0,iy0,iz0); } return interpval; } // The following routines are used to obtain an interpolated intensity value and // either a selected partial derivative (dx, dy or dz) or all partial derivatives // at the same location. The routine returning all derivatives is useful for // non-linear registration of one subject to another (or to an atlas) and the // routines returning a single derivative are useful e.g. for distortion correction. // Puss J template float volume::interp1partial(// Input float x, float y, float z, // Co-ordinates to get value for int dir, // Direction for partial, 0->x, 1->y, 2->z // Output float *pderiv) // Derivative returned here const { if (getinterpolationmethod() != trilinear && getinterpolationmethod() != spline) { imthrow("Derivatives only implemented for tri-linear and spline interpolation",10); } if (dir < 0 || dir > 2) { imthrow("Ivalid derivative direction",11); } if (getinterpolationmethod() == trilinear) { int ix = ((int) floor(x)); int iy = ((int) floor(y)); int iz = ((int) floor(z)); float dx = x - ((float) ix); float dy = y - ((float) iy); float dz = z - ((float) iz); float v000, v001, v010, v011, v100, v101, v110, v111; if (!in_neigh_bounds(*this,ix,iy,iz)) { // We'll have to do some extrapolation v000 = (float) this->operator()(ix,iy,iz); v001 = (float) this->operator()(ix,iy,iz+1); v010 = (float) this->operator()(ix,iy+1,iz); v011 = (float) this->operator()(ix,iy+1,iz+1); v100 = (float) this->operator()(ix+1,iy,iz); v101 = (float) this->operator()(ix+1,iy,iz+1); v110 = (float) this->operator()(ix+1,iy+1,iz); v111 = (float) this->operator()(ix+1,iy+1,iz+1); } else { T t000, t001, t010, t011, t100, t101, t110, t111; this->getneighbours(ix,iy,iz,t000,t001,t010,t011,t100,t101,t110,t111); v000 = ((float) t000); v001 = ((float) t001); v010 = ((float) t010); v011 = ((float) t011); v100 = ((float) t100); v101 = ((float) t101); v110 = ((float) t110); v111 = ((float) t111); } // The (seemingly silly) code multiplication below is to // ensure that in no case does calculating one of the partials // neccessitate any calculation over and above just calculating // the interpolated value. float tmp11, tmp12, tmp13, tmp14; float tmp21, tmp22; if (dir == 0) { // df/dx float onemdz = 1.0-dz; tmp11 = onemdz*v000 + dz*v001; tmp12 = onemdz*v010 + dz*v011; tmp13 = onemdz*v100 + dz*v101; tmp14 = onemdz*v110 + dz*v111; tmp21 = (1.0-dy)*tmp11 + dy*tmp12; tmp22 = (1.0-dy)*tmp13 + dy*tmp14; *pderiv = tmp22 - tmp21; return((1.0-dx)*tmp21 + dx*tmp22); } else if (dir == 1) { // df/dy float onemdz = 1.0-dz; tmp11 = onemdz*v000 + dz*v001; tmp12 = onemdz*v010 + dz*v011; tmp13 = onemdz*v100 + dz*v101; tmp14 = onemdz*v110 + dz*v111; tmp21 = (1.0-dx)*tmp11 + dx*tmp13; tmp22 = (1.0-dx)*tmp12 + dx*tmp14; *pderiv = tmp22 - tmp21; return((1.0-dy)*tmp21 + dy*tmp22); } else if (dir == 2) { // df/dz float onemdy = 1.0-dy; tmp11 = onemdy*v000 + dy*v010; tmp12 = onemdy*v001 + dy*v011; tmp13 = onemdy*v100 + dy*v110; tmp14 = onemdy*v101 + dy*v111; tmp21 = (1.0-dx)*tmp11 + dx*tmp13; tmp22 = (1.0-dx)*tmp12 + dx*tmp14; *pderiv = tmp22 - tmp21; return((1.0-dz)*tmp21 + dz*tmp22); } } else if (getinterpolationmethod() == spline) { return(spline_interp1partial(x,y,z,dir,pderiv)); } return(-1.0); // Should not be reached. Just to stop compiler from complaining. } template float volume::interp3partial(// Input float x, float y, float z, // Co-ordinates to get value for // Output float *dfdx, float *dfdy, float *dfdz) // Partials const { if (getinterpolationmethod() != trilinear && getinterpolationmethod() != spline) { imthrow("interp3partial: Derivatives only implemented for tri-linear and spline interpolation",10); } if (getinterpolationmethod() == trilinear) { int ix = ((int) floor(x)); int iy = ((int) floor(y)); int iz = ((int) floor(z)); float dx = x - ((float) ix); float dy = y - ((float) iy); float dz = z - ((float) iz); float v000, v001, v010, v011, v100, v101, v110, v111; if (!in_neigh_bounds(*this,ix,iy,iz)) { // We'll have to do some extrapolation v000 = (float) this->operator()(ix,iy,iz); v001 = (float) this->operator()(ix,iy,iz+1); v010 = (float) this->operator()(ix,iy+1,iz); v011 = (float) this->operator()(ix,iy+1,iz+1); v100 = (float) this->operator()(ix+1,iy,iz); v101 = (float) this->operator()(ix+1,iy,iz+1); v110 = (float) this->operator()(ix+1,iy+1,iz); v111 = (float) this->operator()(ix+1,iy+1,iz+1); } else { T t000, t001, t010, t011, t100, t101, t110, t111; this->getneighbours(ix,iy,iz,t000,t001,t010,t011,t100,t101,t110,t111); v000 = ((float) t000); v001 = ((float) t001); v010 = ((float) t010); v011 = ((float) t011); v100 = ((float) t100); v101 = ((float) t101); v110 = ((float) t110); v111 = ((float) t111); } // // And do linear interpolation with calculation of all partials // float onemdz = 1.0-dz; float onemdy = 1.0-dy; float tmp11 = onemdz*v000 + dz*v001; float tmp12 = onemdz*v010 + dz*v011; float tmp13 = onemdz*v100 + dz*v101; float tmp14 = onemdz*v110 + dz*v111; *dfdx = onemdy*(tmp13-tmp11) + dy*(tmp14-tmp12); *dfdy = (1.0-dx)*(tmp12-tmp11) + dx*(tmp14-tmp13); tmp11 = onemdy*v000 + dy*v010; tmp12 = onemdy*v001 + dy*v011; tmp13 = onemdy*v100 + dy*v110; tmp14 = onemdy*v101 + dy*v111; float tmp21 = (1.0-dx)*tmp11 + dx*tmp13; float tmp22 = (1.0-dx)*tmp12 + dx*tmp14; *dfdz = tmp22 - tmp21; return(onemdz*tmp21 + dz*tmp22); } else if (getinterpolationmethod() == spline) { return(spline_interp3partial(x,y,z,dfdx,dfdy,dfdz)); } return(0.0); // To silence compiler. } template float volume::spline_interp1partial(// Input float x, float y, float z, // Co-ordinates to get value for int dir, // Direction for partial, 0->x, 1->y, 2->z // Output float *deriv) // Derivative returned here const { if (!in_bounds(x,y,z)) { extrapolation ep = getextrapolationmethod(); if (ep == boundsassert) { *deriv=0.0; assert(false); extrapval = padvalue; return(extrapval); } else if (ep == boundsexception) imthrow("splineinterpolate: Out of bounds",1); else if (ep == zeropad) { *deriv=0.0; extrapval = static_cast(0.0); return(extrapval); } else if (ep == constpad) { *deriv=0.0; extrapval = padvalue; return(extrapval); } } T partial = static_cast(0.0); float rval = 0.0; const SPLINTERPOLATOR::Splinterpolator& sp = splint(); if (getsplineorder() != sp.Order() || translate_extrapolation_type(getextrapolationmethod()) != sp.Extrapolation(0)) { const SPLINTERPOLATOR::Splinterpolator& spp = splint.force_recalculation(); rval = static_cast(spp(x,y,z,dir,&partial)); } else rval = static_cast(sp(x,y,z,dir,&partial)); *deriv = static_cast(partial); return(rval); } template float volume::spline_interp3partial(// Input float x, float y, float z, // Co-ordinates to get value for // Output float *dfdx, float *dfdy, float *dfdz) // Partials const { if (!in_bounds(x,y,z)) { extrapolation ep = getextrapolationmethod(); if (ep == boundsassert) { *dfdx=0.0; *dfdy=0.0; *dfdz=0.0; assert(false); extrapval = padvalue; return(extrapval); } else if (ep == boundsexception) imthrow("splineinterpolate: Out of bounds",1); else if (ep == zeropad) { *dfdx=0.0; *dfdy=0.0; *dfdz=0.0; extrapval = static_cast(0.0); return(extrapval); } else if (ep == constpad) { *dfdx=0.0; *dfdy=0.0; *dfdz=0.0; extrapval = padvalue; return(extrapval); } } static std::vector partials(3,0); float rval = 0.0; const SPLINTERPOLATOR::Splinterpolator& sp = splint(); if (getsplineorder() != sp.Order() || translate_extrapolation_type(getextrapolationmethod()) != sp.Extrapolation(0)) { const SPLINTERPOLATOR::Splinterpolator& spp = splint.force_recalculation(); rval = static_cast(spp.ValAndDerivs(x,y,z,partials)); } else rval = static_cast(sp.ValAndDerivs(x,y,z,partials)); *dfdx = static_cast(partials[0]); *dfdy = static_cast(partials[1]); *dfdz = static_cast(partials[2]); return(rval); } template float volume::splineinterpolate(float x, float y, float z) const { extrapolation ep = getextrapolationmethod(); if (!in_bounds(x,y,z)) { if (ep == boundsassert) { assert(false); extrapval = padvalue; return(extrapval); } else if (ep == boundsexception) imthrow("splineinterpolate: Out of bounds",1); else if (ep == zeropad) { extrapval = static_cast(0.0); return(extrapval); } else if (ep == constpad) { extrapval = padvalue; return(extrapval); } } if (ep == extraslice) if (!in_extraslice_bounds(x,y,z)) { extrapval = padvalue; return(extrapval); } const SPLINTERPOLATOR::Splinterpolator& sp = splint(); if (getsplineorder() != sp.Order() || translate_extrapolation_type(ep) != sp.Extrapolation(0)) { const SPLINTERPOLATOR::Splinterpolator& spp = splint.force_recalculation(); return(static_cast(spp(x,y,z))); } return(static_cast(sp(x,y,z))); } template float volume::interpolate(float x, float y, float z) const { int ix, iy, iz; switch (p_interpmethod) { case userinterpolation: if (p_userinterp == 0) { imthrow("No user interpolation method set",7); } else { return (*p_userinterp)(*this,x,y,z); } case nearestneighbour: ix=round(x); iy=round(y); iz=round(z); return this->operator()(ix,iy,iz); case trilinear: { ix=(int) floor(x); iy=(int) floor(y); iz=(int) floor(z); if (in_neigh_bounds(*this,ix,iy,iz)) return interpolatevalue(x,y,z); float dx=x-ix, dy=y-iy, dz=z-iz; float v000=0, v001=0, v010=0, v011=0, v100=0, v101=0, v110=0, v111=0; v000 = (float) this->operator()(ix,iy,iz); v001 = (float) this->operator()(ix,iy,iz+1); v010 = (float) this->operator()(ix,iy+1,iz); v011 = (float) this->operator()(ix,iy+1,iz+1); v100 = (float) this->operator()(ix+1,iy,iz); v101 = (float) this->operator()(ix+1,iy,iz+1); v110 = (float) this->operator()(ix+1,iy+1,iz); v111 = (float) this->operator()(ix+1,iy+1,iz+1); return q_tri_interpolation(v000,v001,v010,v011,v100,v101,v110,v111, dx,dy,dz); } case sinc: case userkernel: { return kernelinterpolation(x,y,z); } case spline: { return(splineinterpolate(x,y,z)); } default: imthrow("Invalid interpolation method",6); } return 0.0; // Should never get to here } template float volume::interpolatevalue(float x, float y, float z) const { int ix, iy, iz; switch (p_interpmethod) { case userinterpolation: if (p_userinterp == 0) { imthrow("No user interpolation method set",7); } else { return (*p_userinterp)(*this,x,y,z); } case nearestneighbour: ix=round(x); iy=round(y); iz=round(z); return value(ix,iy,iz); case trilinear: { ix=(int) floor(x); iy=(int) floor(y); iz=(int) floor(z); float dx=x-ix, dy=y-iy, dz=z-iz; T t000=0, t001=0, t010=0, t011=0, t100=0, t101=0, t110=0, t111=0; float v000, v001, v010, v011, v100, v101, v110, v111; this->getneighbours(ix,iy,iz,t000,t001,t010,t011,t100,t101,t110,t111); v000=(float) t000; v001=(float) t001; v010=(float) t010; v011=(float) t011; v100=(float) t100; v101=(float) t101; v110=(float) t110; v111=(float) t111; return q_tri_interpolation(v000,v001,v010,v011,v100,v101,v110,v111, dx,dy,dz); } case sinc: case userkernel: { return kernelinterpolation(x,y,z); } case spline: { return(splineinterpolate(x,y,z)); } default: imthrow("Invalid interpolation method",6); } return 0.0; // Should never get to here } template ColumnVector volume::ExtractRow(int j, int k) const { if (j<0 || j>ysize()-1 || k<0 || k>zsize()-1) imthrow("ExtractRow: index out of range",3); ColumnVector rval(xsize()); for (int i=0; i ColumnVector volume::ExtractColumn(int i, int k) const { if (i<0 || i>xsize()-1 || k<0 || k>zsize()-1) imthrow("ExtractColumn: index out of range",3); ColumnVector rval(ysize()); for (int j=0; j void volume::SetRow(int j, int k, const ColumnVector& row) { if (j<0 || j>ysize()-1 || k<0 || k>zsize()-1) imthrow("SetRow: index out of range",3); if (row.Nrows() != xsize()) imthrow("SetRow: mismatched row vector",3); for (int i=0; i void volume::SetColumn(int i, int k, const ColumnVector& col) { if (i<0 || i>xsize()-1 || k<0 || k>zsize()-1) imthrow("SetColumn: index out of range",3); if (col.Nrows() != ysize()) imthrow("SetRow: mismatched row vector",3); for (int j=0; j const T& volume::extrapolate(int x, int y, int z) const { switch (getextrapolationmethod()) { case userextrapolation: if (p_userextrap == 0) { imthrow("No user extrapolation method set",7); } else { extrapval = (*p_userextrap)(*this,x,y,z); return extrapval; } case zeropad: extrapval = (T) 0; return extrapval; case constpad: extrapval = padvalue; return extrapval; default: ; // do nothing } int nx=x, ny=y, nz=z; switch (getextrapolationmethod()) { case periodic: nx = periodicclamp(x,Limits[0],Limits[3]); ny = periodicclamp(y,Limits[1],Limits[4]); nz = periodicclamp(z,Limits[2],Limits[5]); return value(nx,ny,nz); case mirror: nx = mirrorclamp(x,Limits[0],Limits[3]); ny = mirrorclamp(y,Limits[1],Limits[4]); nz = mirrorclamp(z,Limits[2],Limits[5]); return value(nx,ny,nz); case extraslice: if (nx==Limits[0]-1) { nx=Limits[0]; } else { if (nx==Limits[3]+1) nx=Limits[3]; } if (ny==Limits[1]-1) { ny=Limits[1]; } else { if (ny==Limits[4]+1) ny=Limits[4]; } if (nz==Limits[2]-1) { nz=Limits[2]; } else { if (nz==Limits[5]+1) nz=Limits[5]; } if (in_bounds(nx,ny,nz)) { return value(nx,ny,nz); } else { extrapval = padvalue; return extrapval; } case boundsexception: if (!in_bounds(x,y,z)) { ostringstream msg; msg << "Out of Bounds at ("< void volume::defineuserinterpolation(float (*interp)( const volume& , float, float, float)) const { p_userinterp = interp; } template void volume::defineuserextrapolation(T (*extrap)( const volume& , int, int, int)) const { p_userextrap = extrap; } template void volume::definekernelinterpolation(const ColumnVector& kx, const ColumnVector& ky, const ColumnVector& kz, int wx, int wy, int wz) const { // takes full-widths and converts all to half-widths int hwx = (wx-1)/2; int hwy = (wy-1)/2; int hwz = (wz-1)/2; interpkernel.setkernel(kx,ky,kz,hwx,hwy,hwz); } template void volume::definekernelinterpolation(const volume& vol) const { // copying like this is safe interpkernel = vol.interpkernel; } // Support Functions template void volume::definesincinterpolation(const string& sincwindowtype, int w, int nstore) const { // full width this->definesincinterpolation(sincwindowtype,w,w,w,nstore); } template void volume::definesincinterpolation(const string& sincwindowtype, int wx, int wy, int wz, int nstore) const { // full widths if (nstore<1) nstore=1; ColumnVector kx, ky, kz; // calculate kernels kx = sinckernel1D(sincwindowtype,wx,nstore); ky = sinckernel1D(sincwindowtype,wy,nstore); kz = sinckernel1D(sincwindowtype,wz,nstore); this->definekernelinterpolation(kx,ky,kz,wx,wy,wz); } // PROPERTIES template Matrix volume::sampling_mat() const { Matrix samp=IdentityMatrix(4); samp(1,1) = xdim(); samp(2,2) = ydim(); samp(3,3) = zdim(); // NOTE: no origin information is contained in this matrix! return samp; } template Matrix volume4D::sampling_mat() const { return this->operator[](0).sampling_mat(); } template void volume::set_sform(int sform_code, const Matrix& snewmat) const { StandardSpaceTypeCode = sform_code; StandardSpaceCoordMat = snewmat; } template void volume::set_qform(int qform_code, const Matrix& qnewmat) const { RigidBodyTypeCode = qform_code; RigidBodyCoordMat = qnewmat; } template Matrix volume4D::sform_mat() const { return this->operator[](0).sform_mat(); } template int volume4D::sform_code() const { return this->operator[](0).sform_code(); } template Matrix volume4D::qform_mat() const { return this->operator[](0).qform_mat(); } template int volume4D::qform_code() const { return this->operator[](0).qform_code(); } template void volume4D::set_sform(int sform_code, const Matrix& snewmat) const { for (int t=0; ttsize(); t++) { vols[t].set_sform(sform_code,snewmat); } } template void volume4D::set_qform(int qform_code, const Matrix& qnewmat) const { for (int t=0; ttsize(); t++) { vols[t].set_qform(qform_code,qnewmat); } } template float volume::intent_param(int n) const { float retval=0; if (n==1) { retval = IntentParam1; } if (n==2) { retval = IntentParam2; } if (n==3) { retval = IntentParam3; } return retval; } template void volume::set_intent(int intent_code, float p1, float p2, float p3) const { IntentCode = intent_code; IntentParam1 = p1; IntentParam2 = p2; IntentParam3 = p3; } template int volume4D::intent_code() const { return this->operator[](0).intent_code(); } template float volume4D::intent_param(int n) const { return this->operator[](0).intent_param(n); } template void volume4D::set_intent(int intent_code, float p1, float p2, float p3) const { for (int t=0; ttsize(); t++) { vols[t].set_intent(intent_code,p1,p2,p3); } } template ColumnVector volume::principleaxis(int n) const { Matrix tmp = principleaxes(); ColumnVector res = tmp.SubMatrix(1,3,n,n); return res; } template Matrix volume::principleaxes_mat() const { return principleaxes(); } int pval_index_end() { return -1; } template int get_pval_index(const std::vector& pvals, float p) { int idx=0; while (idx < (int) pvals.size()) { // success if p is near pvals[idx] by a relative factor of 0.001 or less if ( fabs((p-pvals[idx])/Max(1e-5,Min(pvals[idx],1-pvals[idx]))) < 0.001 ) return idx; else idx++; } return pval_index_end(); } template T volume::percentile(float pvalue, const volume& mask) const { if ((pvalue>1.0) || (pvalue<0.0)) { imthrow("Percentiles must be in the range [0.0,1.0]",4); } std::vector pvaluevec; std::vector retval; pvaluevec.push_back(pvalue); retval = calc_percentiles(*this,mask,pvaluevec); return retval[0]; } template T volume::percentile(float pvalue) const { if ((pvalue>1.0) || (pvalue<0.0)) { imthrow("Percentiles must be in the range [0.0,1.0]",4); } int idx = get_pval_index(percentilepvals,pvalue); if (idx==pval_index_end()) { percentilepvals.push_back(pvalue); idx = percentilepvals.size() - 1; percentiles.force_recalculation(); } assert((idx>=0) && (idx < (int) percentilepvals.size())); return percentiles()[idx]; } template std::vector percentile_vec(std::vector& hist, const std::vector& percentilepvals) { unsigned int numbins = hist.size(); if (numbins==0) { //cerr << "ERROR:: Empty image" << endl; // why call this an error? when the image is empty this should still work..... hist.push_back((T) 0); return hist; } sort(hist.begin(),hist.end()); std::vector outputvals(percentilepvals.size()); for (unsigned int n=0; n=numbins) percentile=numbins-1; outputvals[n] = hist[percentile]; } return outputvals; } template std::vector calc_percentiles(const volume& vol, const volume& mask, const std::vector& percentilepvals) { if (!samesize(vol,mask)) { imthrow("mask and vol have different sizes in calc_percentiles",3); } std::vector hist; for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { if (mask(x,y,z)>0.5) hist.push_back(vol(x,y,z)); } } } return percentile_vec(hist,percentilepvals); } template std::vector calc_percentiles(const volume& vol) { unsigned int numbins = (unsigned int) vol.nvoxels(); unsigned int hindx = 0; std::vector hist(numbins); for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { hist[hindx++] = vol(x,y,z); } } } return percentile_vec(hist,vol.percentilepvalues()); } template ColumnVector volume::histogram(int nbins, T minval, T maxval) const { bool sameparams = true; if (HISTbins != nbins) { HISTbins = nbins; sameparams = false; } if (HISTmin != minval) { HISTmin = minval; sameparams = false; } if (HISTmax != maxval) { HISTmax = maxval; sameparams = false; } if (!sameparams) { l_histogram.force_recalculation(); } return l_histogram(); } template ColumnVector volume::histogram(int nbins) const { return histogram(nbins,robustmin(),robustmax()); } template ColumnVector volume::histogram(int nbins, T minval, T maxval, const volume& mask) const { ColumnVector hist; calc_histogram(*this,nbins,minval,maxval,hist,mask); return hist; } template ColumnVector volume::histogram(int nbins, const volume& mask) const { return histogram(nbins,robustmin(),robustmax(),mask); } template T volume::min(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.min; } template T volume::max(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.max; } template int volume::mincoordx(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.minx; } template int volume::mincoordy(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.miny; } template int volume::mincoordz(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.minz; } template int volume::maxcoordx(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.maxx; } template int volume::maxcoordy(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.maxy; } template int volume::maxcoordz(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.maxz; } template double volume::mean(const volume& mask) const { return sum(mask)/(Max((double) no_mask_voxels(mask),1.0)); } template double volume::variance(const volume& mask) const { if (no_mask_voxels(mask)>0) { double n=(double) no_mask_voxels(mask); return (n/Max(1.0,n-1))*(sumsquares(mask)/n - mean(mask)*mean(mask)); } else { cerr << "ERROR:: Empty mask image" << endl; return 0; } } template minmaxstuff calc_minmax(const volume& vol) { T newmin, newmax; int newminx=vol.minx(), newminy=vol.miny(), newminz=vol.minz(), newmaxx=vol.minx(), newmaxy=vol.miny(), newmaxz=vol.minz(); newmin = newmax = vol(vol.minx(),vol.miny(),vol.minz()); for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { T val = vol(x,y,z); if (newmin>val) { newmin=val; newminx=x; newminy=y; newminz=z; } else if (val>newmax) { newmax=val; newmaxx=x; newmaxy=y; newmaxz=z;} } } } minmaxstuff newminmax; newminmax.min = newmin; newminmax.max = newmax; newminmax.minx = newminx; newminmax.miny = newminy; newminmax.minz = newminz; newminmax.mint = 0; newminmax.maxx = newmaxx; newminmax.maxy = newmaxy; newminmax.maxz = newmaxz; newminmax.maxt = 0; return newminmax; } template minmaxstuff calc_minmax(const volume& vol, const volume& mask) { if (!samesize(vol,mask)) { imthrow("calc_minmax:: mask and volume must be the same size",4); } bool valid=false; T newmin, newmax; int newminx=vol.minx(), newminy=vol.miny(), newminz=vol.minz(), newmaxx=vol.minx(), newmaxy=vol.miny(), newmaxz=vol.minz(); newmin = newmax = vol(vol.minx(),vol.miny(),vol.minz()); for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { if (mask.value(x,y,z)>(T) 0.5) { T val = vol.value(x,y,z); if (!valid || (newmin>val)) { newmin=val; newminx=x; newminy=y; newminz=z; } if (!valid || (val>newmax)) { newmax=val; newmaxx=x; newmaxy=y; newmaxz=z;} valid=true; } } } } minmaxstuff newminmax; if (valid) { newminmax.min = newmin; newminmax.max = newmax; newminmax.minx = newminx; newminmax.miny = newminy; newminmax.minz = newminz; newminmax.mint = 0; newminmax.maxx = newmaxx; newminmax.maxy = newmaxy; newminmax.maxz = newmaxz; newminmax.maxt = 0; } else { // invalid return type cerr << "ERROR:: Empty mask image" << endl; newminmax.min = 0; newminmax.max = 0; newminmax.minx = -1; newminmax.miny = -1; newminmax.minz = -1; newminmax.mint = -1; newminmax.maxx = -1; newminmax.maxy = -1; newminmax.maxz = -1; newminmax.maxt = -1; } return newminmax; } template std::vector calc_sums(const volume& vol) { double sum=0, sum2=0, totsum=0, totsum2=0; long int n=0, nlim; nlim = (long int) sqrt((double) vol.nvoxels()); if (nlim<100000) nlim=100000; if (vol.usingROI()) { for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { T val = vol.value(x,y,z); sum += val; sum2 += val*val; n++; if (n>nlim) { n=0; totsum+=sum; totsum2+=sum2; sum=0; sum2=0; } } } } totsum+=sum; totsum2+=sum2; } else { for (typename volume::fast_const_iterator it=vol.fbegin(), itend = vol.fend(); it!=itend; ++it) { T val = *it; sum += val; sum2 += val*val; n++; if (n>nlim) { n=0; totsum+=sum; totsum2+=sum2; sum=0; sum2=0; } } totsum+=sum; totsum2+=sum2; } std::vector newsums(2); newsums[0] = totsum; newsums[1] = totsum2; return newsums; } template double volume::sum(const volume& mask) const { std::vector retval; retval = calc_sums(*this,mask); return retval[0]; } template double volume::sumsquares(const volume& mask) const { std::vector retval; retval = calc_sums(*this,mask); return retval[1]; } template std::vector calc_sums(const volume& vol, const volume& mask) { if (!samesize(vol,mask)) { imthrow("calc_sums:: mask and volume must be the same size",4); } double sum=0, sum2=0, totsum=0, totsum2=0; long int n=0, nlim, nn=0; nlim = (long int) sqrt((double) vol.nvoxels()); if (nlim<100000) nlim=100000; for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { if (mask.value(x,y,z)>(T) 0.5) { T val = vol.value(x,y,z); sum += val; sum2 += val*val; n++; if (n>nlim) { nn++; n=0; totsum+=sum; totsum2+=sum2; sum=0; sum2=0; } } } } } totsum+=sum; totsum2+=sum2; std::vector newsums(2); newsums[0] = totsum; newsums[1] = totsum2; if (n + nn == 0) { cerr << "ERROR:: Empty mask image" << endl; } return newsums; } template ColumnVector volume::cog(const string& coordtype) const { // for coordtype="scaled_mm" return the old style, otherwise // return newimage voxel coordinates ColumnVector retcog; retcog = this->lazycog(); if (coordtype=="scaled_mm") { ColumnVector v(4); v << retcog(1) << retcog(2) << retcog(3) << 1.0; v = this->sampling_mat() * v; retcog(1) = v(1); retcog(2) = v(2); retcog(3) = v(3); } return retcog; } // the following calculates a robust background by taking the 10th percentile // of the edge voxels only // Note: it does NOT use the ROI even if it is active template T calc_bval(const volume& vol, unsigned int edgewidth) { unsigned int zb = vol.zsize(), yb = vol.ysize(), xb = vol.xsize(); unsigned int ewx, ewy, ewz, numbins; ewx = edgewidth; ewy = edgewidth; ewz = edgewidth; if (ewx >= xb) ewx=xb-1; if (ewy >= yb) ewy=yb-1; if (ewz >= zb) ewz=zb-1; numbins = 2*(xb-2*ewx)*(yb-2*ewy)*ewz + 2*(xb-2*ewx)*zb*ewy + 2*yb*zb*ewx; std::vector hist(numbins); // put the edge voxel values into the histogram unsigned int hindx = 0; // put in the faces // xy faces (small lids of the box) for (unsigned int e=0; e T calc_backgroundval(const volume& vol) { return calc_bval(vol,2); } template ColumnVector calc_cog(const volume& vol) { ColumnVector v_cog(3); v_cog(1)=0.0; v_cog(2)=0.0; v_cog(3)=0.0; double val=0, total=0, vx=0, vy=0, vz=0, tot=0; T vmin=vol.min(); long int n=0, nlim; nlim = (long int) sqrt((double) vol.nvoxels()); if (nlim<1000) nlim=1000; for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { val = (double) (vol(x,y,z) - vmin); vx += val*x; vy += val*y; vz += val*z; tot += val; n++; if (n>nlim) { n=0; total+=tot; v_cog(1)+=vx; v_cog(2)+=vy; v_cog(3)+=vz; tot=0; vx=0; vy=0; vz=0; } } } } total+=tot; v_cog(1)+=vx; v_cog(2)+=vy; v_cog(3)+=vz; if (fabs(total) < 1e-5) { cerr << "WARNING::in calculating COG, total = 0.0" << endl; total = 1.0; } v_cog(1) /= total; v_cog(2) /= total; v_cog(3) /= total; // Leave these values in (newimage) voxel coordinates return v_cog; } template Matrix calc_principleaxes(const volume& vol) { SymmetricMatrix m2(3); m2 = 0; double val=0, total=0, tot=0; double mxx=0, mxy=0, mxz=0, myy=0, myz=0, mzz=0, mx=0, my=0, mz=0; ColumnVector mean(3); mean = 0; T vmin=vol.min(); long int n=0, nlim; nlim = (long int) sqrt((double) vol.nvoxels()); if (nlim<1000) nlim=1000; for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { val = (double) (vol(x,y,z) - vmin); mxx += val*x*x; mxy += val*x*y; mxz += val*x*z; myy += val*y*y; myz += val*y*z; mzz += val*z*z; mx += val*x; my += val*y; mz += val*z; tot += val; n++; if (n>nlim) { n=0; total+=tot; m2(1,1)+=mxx; m2(1,2)+=mxy; m2(1,3)+=mxz; m2(2,2)+=myy; m2(2,3)+=myz; m2(3,3)+=mzz; mean(1)+=mx; mean(2)+=my; mean(3)+=mz; tot=0; mxx=0; mxy=0; mxz=0; myy=0; myz=0; mzz=0; mx=0; my=0; mz=0; } } } } total+=tot; m2(1,1)+=mxx; m2(1,2)+=mxy; m2(1,3)+=mxz; m2(2,2)+=myy; m2(2,3)+=myz; m2(3,3)+=mzz; mean(1)+=mx; mean(2)+=my; mean(3)+=mz; if (fabs(total) < 1e-5) { cerr << "WARNING::in calculating Principle Axes, total = 0.0" << endl; total = 1.0; } m2 /= total; mean /= total; // Now adjust for voxel dimensions Matrix samp(3,3); samp = vol.sampling_mat().SubMatrix(1,3,1,3); m2 << samp * m2 * samp; mean = samp*mean; // Now make it central (taking off the cog) Matrix meanprod(3,3); for (int k1=1; k1<=3; k1++) { for (int k2=1; k2<=3; k2++) { meanprod(k1,k2) = mean(k1)*mean(k2); } } m2 << m2 - meanprod; Matrix paxes; DiagonalMatrix evals; Jacobi(m2,evals,paxes); // Force the eigenvalues (and vectors) to be in descending order ColumnVector ptemp; float etemp; // brute force sort the eigen values and vectors // find inedx of least e-value int kmin=1; for (int k=2; k<=3; k++) { if (evals(k,k) < evals(kmin,kmin)) kmin = k; } // put the least in position 1 etemp = evals(1,1); ptemp = paxes.SubMatrix(1,3,1,1); evals(1,1) = evals(kmin,kmin); paxes.SubMatrix(1,3,1,1) = paxes.SubMatrix(1,3,kmin,kmin); evals(kmin,kmin) = etemp; paxes.SubMatrix(1,3,kmin,kmin) = ptemp; // check if remaining ones require swapping if (evals(3,3) < evals(2,2)) { etemp = evals(2,2); ptemp = paxes.SubMatrix(1,3,2,2); evals(2,2) = evals(3,3); paxes.SubMatrix(1,3,2,2) = paxes.SubMatrix(1,3,3,3); evals(3,3) = etemp; paxes.SubMatrix(1,3,3,3) = ptemp; } return paxes; } template int calc_histogram(const volume& vol, int nbins, double minval, double maxval, ColumnVector& hist, const volume& mask, bool use_mask=true) { // MJ NOTE: Concerned about the behaviour for integer volumes // as rounding could cause values to go into the wrong bins if (hist.Nrows()!=nbins) hist.ReSize(nbins); hist = 0.0; if (maxval < minval) return -1; double a=((double) nbins)/(maxval - minval); double b= - ((double) nbins)*minval/(maxval - minval); int binno = 0; for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { if ( (!use_mask) || (mask(x,y,z)>(T) 0.5) ) { binno = (int) (a*((double) vol(x,y,z)) + b); if (binno > nbins-1) binno=nbins-1; if (binno < 0) binno=0; hist(binno+1)++; } } } } return 0; } template int calc_histogram(const volume& vol, int nbins, double minval, double maxval, ColumnVector& hist) { return calc_histogram(vol,nbins,minval,maxval,hist,vol,false); } template int calc_histogram(const volume4D& vol, int nbins, double minval, double maxval, ColumnVector& hist, const volume4D& mask, bool use_mask=true) { // MJ NOTE: Concerned about the behaviour for integer volumes // as rounding could cause values to go into the wrong bins if (!samesize(vol[0],mask[0])) { imthrow("calc_histogram:: mask and volume must be the same size",4); } if (hist.Nrows()!=nbins) hist.ReSize(nbins); hist = 0.0; if (maxval < minval) return -1; double a=((double) nbins)/(maxval - minval); double b= - ((double) nbins)*minval/(maxval - minval); int binno = 0; for (int t=vol.mint(); t<=vol.maxt(); t++) { for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { if ( (!use_mask) || (mask(x,y,z,Min(t,mask.maxt()))>(T) 0.5) ) { binno = (int) (a*((double) vol(x,y,z,t)) + b); if (binno > nbins-1) binno=nbins-1; if (binno < 0) binno=0; hist(binno+1)++; } } } } } return 0; } template int calc_histogram(const volume4D& vol, int nbins, double minval, double maxval, ColumnVector& hist, const volume& mask, bool use_mask=true) { // MJ NOTE: Concerned about the behaviour for integer volumes // as rounding could cause values to go into the wrong bins if (!samesize(vol[0],mask)) { imthrow("calc_histogram:: mask and volume must be the same size",4); } if (hist.Nrows()!=nbins) hist.ReSize(nbins); hist = 0.0; if (maxval < minval) return -1; double a=((double) nbins)/(maxval - minval); double b= - ((double) nbins)*minval/(maxval - minval); int binno = 0; for (int t=vol.mint(); t<=vol.maxt(); t++) { for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { if ( (!use_mask) || (mask(x,y,z)>(T) 0.5) ) { binno = (int) (a*((double) vol(x,y,z,t)) + b); if (binno > nbins-1) binno=nbins-1; if (binno < 0) binno=0; hist(binno+1)++; } } } } } return 0; } template int calc_histogram(const volume4D& vol, int nbins, double minval, double maxval, ColumnVector& hist) { return calc_histogram(vol,nbins,minval,maxval,hist,vol,false); } template int find_histogram(const volume& vol, ColumnVector& hist, int bins, T& min, T& max) { // STEVE SMITH'S CODE - ADAPTED FOR NEWIMAGE BY MARK JENKINSON int validsize=0; /* zero histogram */ hist=0; if (min==max) return -1; /* create histogram; the MIN is so that the maximum value falls in the last valid bin, not the (last+1) bin */ double fA = ((double)bins)/(max-min); double fB = ( ((double)bins) * ((double)(-min)) ) / (max-min); for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { hist(Max(0, Min( (int)(fA*(vol(x,y,z)) + fB), bins-1) ) + 1)++; validsize++; } } } return validsize; } template int find_histogram(const volume& vol, ColumnVector& hist, int bins, T& min, T& max, const volume& mask) { // STEVE SMITH'S CODE - ADAPTED FOR NEWIMAGE BY MARK JENKINSON if (!samesize(vol,mask)) { imthrow("find_histogram:: mask and volume must be the same size",4); } if (no_mask_voxels(mask)==0) { cerr << "ERROR:: Empty mask image" << endl; return 0; } int validsize=0; /* zero histogram */ hist=0; if (min==max) return -1; /* create histogram; the MIN is so that the maximum value falls in the last valid bin, not the (last+1) bin */ double fA = ((double)bins)/(max-min); double fB = ( ((double)bins) * ((double)(-min)) ) / (max-min); for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { if ( (mask(x,y,z)>(T) 0.5) ) { hist(Max(0, Min( (int)(fA*(vol(x,y,z)) + fB), bins-1) ) + 1)++; validsize++; } } } } return validsize; } template int find_histogram(const volume4D& vol, ColumnVector& hist, int bins, T& min, T& max, const volume4D& mask) { // STEVE SMITH'S CODE - ADAPTED FOR NEWIMAGE BY MARK JENKINSON if (!samesize(vol[0],mask[0])) { imthrow("find_histogram:: mask and volume must be the same size",4); } if (no_mask_voxels(mask)==0) { cerr << "ERROR:: Empty mask image" << endl; return 0; } int validsize=0; /* zero histogram */ hist=0; if (min==max) return -1; /* create histogram; the MIN is so that the maximum value falls in the last valid bin, not the (last+1) bin */ double fA = ((double)bins)/(max-min); double fB = ( ((double)bins) * ((double)(-min)) ) / (max-min); for (int t=vol.mint(); t<=vol.maxt(); t++) { for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { if ( (mask(x,y,z,Min(t,mask.maxt()))>(T) 0.5) ) { hist(Max(0, Min( (int)(fA*(vol(x,y,z,t)) + fB), bins-1) ) + 1)++; validsize++; } } } } } return validsize; } template int find_histogram(const volume4D& vol, ColumnVector& hist, int bins, T& min, T& max, const volume& mask) { // STEVE SMITH'S CODE - ADAPTED FOR NEWIMAGE BY MARK JENKINSON if (!samesize(vol[0],mask)) { imthrow("find_histogram:: mask and volume must be the same size",4); } if (no_mask_voxels(mask)==0) { cerr << "ERROR:: Empty mask image" << endl; return 0; } int validsize=0; /* zero histogram */ hist=0; if (min==max) return -1; /* create histogram; the MIN is so that the maximum value falls in the last valid bin, not the (last+1) bin */ double fA = ((double)bins)/(max-min); double fB = ( ((double)bins) * ((double)(-min)) ) / (max-min); for (int t=vol.mint(); t<=vol.maxt(); t++) { for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { if ( (mask(x,y,z)>(T) 0.5) ) { hist(Max(0, Min( (int)(fA*(vol(x,y,z,t)) + fB), bins-1) ) + 1)++; validsize++; } } } } } return validsize; } template int find_histogram(const volume4D& vol, ColumnVector& hist, int bins, T& min, T& max) { // STEVE SMITH'S CODE - ADAPTED FOR NEWIMAGE BY MARK JENKINSON int validsize=0; /* zero histogram */ hist=0; if (min==max) return -1; /* create histogram; the MIN is so that the maximum value falls in the last valid bin, not the (last+1) bin */ double fA = ((double)bins)/(max-min); double fB = ( ((double)bins) * ((double)(-min)) ) / (max-min); for (int t=vol.mint(); t<=vol.maxt(); t++) { for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { hist(Max(0, Min( (int)(fA*(vol(x,y,z,t)) + fB), bins-1) ) + 1)++; validsize++; } } } } return validsize; } template void find_thresholds(const S& vol, T& minval, T& maxval, const R& mask, bool use_mask=true) { // STEVE SMITH'S CODE - ADAPTED FOR NEWIMAGE BY MARK JENKINSON int HISTOGRAM_BINS=1000; ColumnVector hist(HISTOGRAM_BINS); int MAX_PASSES=10; int top_bin=0, bottom_bin=0, count, pass=1, lowest_bin=0, highest_bin=HISTOGRAM_BINS-1, validsize; T thresh98=0, thresh2=0, min, max; if (use_mask) { min=vol.min(mask), max=vol.max(mask); } else { min=vol.min(); max=vol.max(); } if (hist.Nrows()!=HISTOGRAM_BINS) { hist.ReSize(HISTOGRAM_BINS); } while ( (pass==1) || ( (double) (thresh98 - thresh2) < (((double) (max - min)) / 10.0) ) ) // test for very long tails // find histogram and thresholds { if (pass>1) // redo histogram with new min and max { // increase range slightly from the 2-98% range found bottom_bin=Max(bottom_bin-1,0); top_bin=Min(top_bin+1,HISTOGRAM_BINS-1); // now set new min and max on the basis of this new range T tmpmin = (T)( min + ((double)bottom_bin/(double)(HISTOGRAM_BINS))*(max-min) ); max = (T)( min + ((double)(top_bin+1)/(double)(HISTOGRAM_BINS))*(max-min) ); min=tmpmin; } if (pass==MAX_PASSES || min==max) // give up and revert to full range ... { if (use_mask) { min=vol.min(mask); max=vol.max(mask); } else { min=vol.min(); max=vol.max(); } } if (use_mask) validsize = find_histogram(vol,hist,HISTOGRAM_BINS,min,max,mask); else validsize = find_histogram(vol,hist,HISTOGRAM_BINS,min,max); if (validsize<1) { minval=thresh2=min; maxval=thresh98=max; return; } if (pass==MAX_PASSES) /* ... _but_ ignore end bins */ { validsize-= MISCMATHS::round(hist(lowest_bin+1)) + MISCMATHS::round(hist(highest_bin+1)); lowest_bin++; highest_bin--; } if (validsize<0) /* ie zero range */ { thresh2=thresh98=min; break; } double fA = (max-min)/(double)(HISTOGRAM_BINS); for(count=0, bottom_bin=lowest_bin; count void find_thresholds(const S& vol, T& minval, T& maxval) { return find_thresholds(vol,minval,maxval,vol,false); } template std::vector calc_robustlimits(const volume& vol) { std::vector rlimits(2); T minval=0, maxval=0; find_thresholds(vol,minval,maxval); //find_robust_limits(vol,1000,hist,minval,maxval); // MJ version rlimits[0] = minval; rlimits[1] = maxval; return rlimits; } template std::vector calc_robustlimits(const volume& vol, const volume& mask) { std::vector rlimits(2); if (no_mask_voxels(mask)==0) { cerr << "ERROR:: Empty mask image" << endl; rlimits[0]=0; rlimits[1]=0; return rlimits; } T minval=0, maxval=0; find_thresholds(vol,minval,maxval,mask); rlimits[0] = minval; rlimits[1] = maxval; return rlimits; } template std::vector calc_robustlimits(const volume4D& vol) { std::vector rlimits(2); T minval=0, maxval=0; find_thresholds(vol,minval,maxval); rlimits[0] = minval; rlimits[1] = maxval; return rlimits; } template std::vector calc_robustlimits(const volume4D& vol, const volume4D& mask) { std::vector rlimits(2); if (no_mask_voxels(mask)==0) { cerr << "ERROR:: Empty mask image" << endl; rlimits[0]=0; rlimits[1]=0; return rlimits; } T minval=0, maxval=0; find_thresholds(vol,minval,maxval,mask); rlimits[0] = minval; rlimits[1] = maxval; return rlimits; } template std::vector calc_robustlimits(const volume4D& vol, const volume& mask) { std::vector rlimits(2); if (no_mask_voxels(mask)==0) { cerr << "ERROR:: Empty mask image" << endl; rlimits[0]=0; rlimits[1]=0; return rlimits; } T minval=0, maxval=0; find_thresholds(vol,minval,maxval,mask); rlimits[0] = minval; rlimits[1] = maxval; return rlimits; } template T volume::robustmin(const volume& mask) const { std::vector rlim; rlim = calc_robustlimits(*this,mask); return rlim[0]; } template T volume::robustmax(const volume& mask) const { std::vector rlim; rlim = calc_robustlimits(*this,mask); return rlim[1]; } template T volume4D::robustmin(const volume& mask) const { std::vector rlim; rlim = calc_robustlimits(*this,mask); return rlim[0]; } template T volume4D::robustmax(const volume& mask) const { std::vector rlim; rlim = calc_robustlimits(*this,mask); return rlim[1]; } template T volume4D::robustmin(const volume4D& mask) const { std::vector rlim; rlim = calc_robustlimits(*this,mask); return rlim[0]; } template T volume4D::robustmax(const volume4D& mask) const { std::vector rlim; rlim = calc_robustlimits(*this,mask); return rlim[1]; } template ColumnVector calc_histogram(const volume& vol) { ColumnVector hist; calc_histogram(vol,vol.histbins(),vol.histmin(),vol.histmax(),hist); return hist; } template ColumnVector calc_histogram(const volume& vol, const volume& mask) { ColumnVector hist; calc_histogram(vol,vol.histbins(),vol.histmin(),vol.histmax(),hist,mask); return hist; } template SPLINTERPOLATOR::Splinterpolator calc_spline_coefs(const volume& vol) { std::vector dim(3,0); dim[0] = vol.xsize(); dim[1] = vol.ysize(); dim[2] = vol.zsize(); std::vector ep(3,SPLINTERPOLATOR::Mirror); for (unsigned int i=0; i<3; i++) ep[i] = translate_extrapolation_type(vol.getextrapolationmethod()); SPLINTERPOLATOR::Splinterpolator rval(vol.fbegin(),dim,ep,vol.getsplineorder(),false); return(rval); } SPLINTERPOLATOR::ExtrapolationType translate_extrapolation_type(extrapolation ep) { switch (ep) { case zeropad: return(SPLINTERPOLATOR::Zeros); break; case extraslice: return(SPLINTERPOLATOR::Constant); // It is constant for a given column, hence name. break; case mirror: return(SPLINTERPOLATOR::Mirror); break; case periodic: return(SPLINTERPOLATOR::Periodic); break; case boundsassert: case boundsexception: // We deal with this at the actual interpolation, and for now just return something return(SPLINTERPOLATOR::Zeros); break; case constpad: // Not implemented in splinterpolator, so I'll deal with this too at the actual interpolation. return(SPLINTERPOLATOR::Zeros); break; case userextrapolation: imthrow("translate_extrapolation_type: userextrapolation not implemented for spline interpolation",10); break; default: imthrow("translate_extrapolation_type: I am lost",10); break; } return(SPLINTERPOLATOR::Zeros); } // GENERAL MANIPULATION template void volume::threshold(T lowerth, T upperth, threshtype tt) { if (activeROI) { for (int z=minz(); z<=maxz(); z++) { for (int y=miny(); y<=maxy(); y++) { for (int x=minx(); x<=maxx(); x++) { if ( ((tt==inclusive) && (value(x,y,z)>= lowerth) && (value(x,y,z)<= upperth)) || ((tt==exclusive) && (value(x,y,z)>lowerth) && (value(x,y,z)= lowerth) && ((*it)<= upperth)) || ((tt==exclusive) && ((*it)> lowerth) && ((*it)< upperth)) ) { //*it = 1; } else { *it = 0; } } } } template void volume::binarise(T lowerth, T upperth, threshtype tt) { if (activeROI) { for (int z=minz(); z<=maxz(); z++) { for (int y=miny(); y<=maxy(); y++) { for (int x=minx(); x<=maxx(); x++) { if ( ((tt==inclusive) && (value(x,y,z)>= lowerth) && (value(x,y,z)<= upperth)) || ((tt==exclusive) && (value(x,y,z)>lowerth) && (value(x,y,z)= lowerth) && ((*it)<= upperth)) || ((tt==exclusive) && ((*it)> lowerth) && ((*it)< upperth)) ) { *it = 1; } else { *it = 0; } } } } template inline S swapval(S xval, S yval, S zval, int dim) { switch (dim) { case 1: return xval; case -1: return -xval; case 2: return yval; case -2: return -yval; case 3: return zval; case -3: return -zval; } return (S) 0; // should never get here } template inline int coordval(const volume& vol, int x, int y, int z, int dim) { switch (dim) { case 1: return x; case -1: return vol.xsize() - x - 1; case 2: return y; case -2: return vol.ysize() - y - 1; case 3: return z; case -3: return vol.zsize() - z - 1; } return 0; // should never get here } int dimarg(const string& val) { if (val=="x") { return 1; } else if (val=="x-" || val=="-x") { return -1; } else if (val=="y") { return 2; } else if (val=="y-" || val=="-y") { return -2; } else if (val=="z") { return 3; } else if (val=="z-" || val=="-z") { return -3; } else { return 0; } } template void setrow(Matrix& affmat, int rownum, int dimnum, const volume& invol) { if (dimnum==1 || dimnum==-1) { affmat(rownum,1)=1*sign(dimnum); affmat(rownum,2)=0; affmat(rownum,3)=0; } if (dimnum==2 || dimnum==-2) { affmat(rownum,1)=0; affmat(rownum,2)=1*sign(dimnum); affmat(rownum,3)=0; } if (dimnum==3 || dimnum==-3) { affmat(rownum,1)=0; affmat(rownum,2)=0; affmat(rownum,3)=1*sign(dimnum); } if (dimnum>0) return; float fov=0.0; if (dimnum==-1) { fov = (invol.xsize() -1) * invol.xdim(); } if (dimnum==-2) { fov = (invol.ysize() -1) * invol.ydim(); } if (dimnum==-3) { fov = (invol.zsize() -1) * invol.zdim(); } affmat(rownum,4)=fov; } template Matrix volume::swapmat(int dim1, int dim2, int dim3) const { Matrix affmat(4,4); affmat = 0.0; affmat(4,4)=1.0; setrow(affmat,1,dim1,*this); setrow(affmat,2,dim2,*this); setrow(affmat,3,dim3,*this); return affmat; } template Matrix volume::swapmat(const string& newx, const string& newy, const string& newz) const { return this->swapmat(dimarg(newx),dimarg(newy),dimarg(newz)); } template void volume::swapdimensions(const string& newx, const string& newy, const string& newz) { this->swapdimensions(dimarg(newx),dimarg(newy),dimarg(newz)); } template void volume::swapdimensions(int dim1, int dim2, int dim3) { basic_swapdimensions(dim1,dim2,dim3,true); } template void volume::basic_swapdimensions(int dim1, int dim2, int dim3, bool keepLRorder) { // valid entries for dims are +/- 1, 2, 3 (corresponding to +/- x,y,z) if ( (dim1>3) || (dim1<-3) || (dim1==0) || (dim2<-3) || (dim2>3) || (dim2==0) || (dim3<-3) || (dim3>3) || (dim3==0) ) { imthrow("Invalid dimension numbers entered to swapdimensions",8); } if ( (std::abs(dim1)==std::abs(dim2)) || (std::abs(dim1)==std::abs(dim3)) || (std::abs(dim2)==std::abs(dim3)) ) { imthrow("Dimension numbers were not a permutation in swapdimensions",8); } int sx, sy, sz; sx = std::abs(swapval(this->xsize(),this->ysize(),this->zsize(),dim1)); sy = std::abs(swapval(this->xsize(),this->ysize(),this->zsize(),dim2)); sz = std::abs(swapval(this->xsize(),this->ysize(),this->zsize(),dim3)); volume swapvol(sx,sy,sz); // now copy only the appropriate data for (int z=0; zzsize(); z++) { for (int y=0; yysize(); y++) { for (int x=0; xxsize(); x++) { int nx = coordval(*this,x,y,z,dim1); int ny = coordval(*this,x,y,z,dim2); int nz = coordval(*this,x,y,z,dim3); swapvol.value(nx,ny,nz) = this->value(x,y,z); } } } swapvol.copyproperties(*this); // now fix up the spatial properties // if a LR flip has happened then for all properties retain the unflipped values // therefore the data really has flipped, as otherwise it views identically if (keepLRorder) { // arbitrarily choose x to flip (if necessary) if (this->swapmat(dim1,dim2,dim3).Determinant() < 0) { dim1*=-1; } } float dx = swapval(this->xdim(), this->ydim(), this->zdim(), dim1); float dy = swapval(this->xdim(), this->ydim(), this->zdim(), dim2); float dz = swapval(this->xdim(), this->ydim(), this->zdim(), dim3); swapvol.setdims(dx,dy,dz); // fix sform and qform matrices // NB: sform and qform are voxel->mm but swapmat is mm->mm (flirt mm), // hence sampling mats Matrix nmat; nmat = this->sform_mat() * this->sampling_mat().i() * this->swapmat(dim1,dim2,dim3).i() * swapvol.sampling_mat(); swapvol.set_sform(this->sform_code(), nmat); nmat = this->qform_mat() * this->sampling_mat().i() * this->swapmat(dim1,dim2,dim3).i() * swapvol.sampling_mat(); swapvol.set_qform(this->qform_code(), nmat); int nx, ny, nz, mx, my, mz; mx = coordval(*this,this->minx(),this->miny(),this->minz(),dim1); my = coordval(*this,this->minx(),this->miny(),this->minz(),dim2); mz = coordval(*this,this->minx(),this->miny(),this->minz(),dim3); nx = coordval(*this,this->maxx(),this->maxy(),this->maxz(),dim1); ny = coordval(*this,this->maxx(),this->maxy(),this->maxz(),dim2); nz = coordval(*this,this->maxx(),this->maxy(),this->maxz(),dim3); swapvol.setROIlimits(mx,my,mz,nx,ny,nz); swapvol.set_whole_cache_validity(false); // now force the ROI and limits to be rebuilt swapvol.deactivateROI(); if (this->usingROI()) swapvol.activateROI(); *this = swapvol; } template int volume::left_right_order() const { int order; // call the function in fslio order = FslGetLeftRightOrder2(this->sform_code(), newmat_to_mat44(this->sform_mat()), this->qform_code(), newmat_to_mat44(this->qform_mat())); return order; } template short vox2mm_all(const volume& vol, Matrix& vox2mm_mat, short& code) { mat44 vox2mm44; code = FslGetVox2mmMatrix2(&vox2mm44,vol.sform_code(), newmat_to_mat44(vol.sform_mat()), vol.qform_code(), newmat_to_mat44(vol.qform_mat()), vol.xdim(),vol.ydim(),vol.zdim()); vox2mm_mat = mat44_to_newmat(vox2mm44); return code; } template Matrix volume::newimagevox2mm_mat() const { Matrix vox2mm; short code; vox2mm_all(*this,vox2mm,code); return vox2mm; } template Matrix volume::niftivox2newimagevox_mat() const { Matrix vox2vox=IdentityMatrix(4); if ((!RadiologicalFile) && (this->left_right_order()==FSL_RADIOLOGICAL)) { vox2vox = (this->sampling_mat()).i() * this->swapmat(-1,2,3) * (this->sampling_mat()); } return vox2vox; } template void volume::swapLRorder() { basic_swapdimensions(-1,2,3,false); } template void volume::setLRorder(int LRorder) { if (LRorder != this->left_right_order()) { this->swapLRorder(); } } template void volume::makeradiological() { // use existing matrices to determine the order and if necessary swap // all data and matrices if (this->left_right_order()==FSL_NEUROLOGICAL) { this->swapLRorder(); } } template void volume::makeneurological() { // use existing matrices to determine the order and if necessary swap // all data and matrices if (this->left_right_order()==FSL_RADIOLOGICAL) { this->swapLRorder(); } } // ARITHMETIC OPERATIONS template T volume::operator=(T val) { if (activeROI) { for (int z=minz(); z<=maxz(); z++) { for (int y=miny(); y<=maxy(); y++) { for (int x=minx(); x<=maxx(); x++) { value(x,y,z) = val; } } } } else { fill(nsfbegin(),nsfend(),val); // use the STL } return val; } template const volume& volume::operator+=(T val) { if (activeROI) { for (int z=minz(); z<=maxz(); z++) { for (int y=miny(); y<=maxy(); y++) { for (int x=minx(); x<=maxx(); x++) { value(x,y,z) += val; } } } } else { for (nonsafe_fast_iterator it=nsfbegin(), itend=nsfend(); it!=itend; ++it) { *it += val; } } return *this; } template const volume& volume::operator-=(T val) { if (activeROI) { for (int z=minz(); z<=maxz(); z++) { for (int y=miny(); y<=maxy(); y++) { for (int x=minx(); x<=maxx(); x++) { value(x,y,z) -= val; } } } } else { for (nonsafe_fast_iterator it=nsfbegin(), itend=nsfend(); it!=itend; ++it) { *it -= val; } } return *this; } template const volume& volume::operator*=(T val) { if (activeROI) { for (int z=minz(); z<=maxz(); z++) { for (int y=miny(); y<=maxy(); y++) { for (int x=minx(); x<=maxx(); x++) { value(x,y,z) *= val; } } } } else { for (nonsafe_fast_iterator it=nsfbegin(), itend=nsfend(); it!=itend; ++it) { *it *= val; } } return *this; } template const volume& volume::operator/=(T val) { if (activeROI) { for (int z=minz(); z<=maxz(); z++) { for (int y=miny(); y<=maxy(); y++) { for (int x=minx(); x<=maxx(); x++) { value(x,y,z) /= val; } } } } else { for (nonsafe_fast_iterator it=nsfbegin(), itend=nsfend(); it!=itend; ++it) { *it /= val; } } return *this; } template const volume& volume::operator+=(const volume& source) { if (!samesize(*this,source)) { imthrow("Attempted to add images/ROIs of different sizes",3); } if (activeROI || source.activeROI) { int xoff=source.minx()-minx(), yoff=source.miny()-miny(), zoff=source.minz()-minz(); for (int z=minz(); z<=maxz(); z++) { for (int y=miny(); y<=maxy(); y++) { for (int x=minx(); x<=maxx(); x++) { value(x,y,z) += source(x+xoff,y+yoff,z+zoff); } } } } else { fast_const_iterator dit=source.fbegin(); for (nonsafe_fast_iterator it=nsfbegin(), itend=nsfend(); it!=itend; ++it, ++dit) { *it += *dit; } } return *this; } template const volume& volume::operator-=(const volume& source) { if (!samesize(*this,source)) { imthrow("Attempted to subtract images/ROIs of different sizes",3); } if (activeROI || source.activeROI) { int xoff=source.minx()-minx(), yoff=source.miny()-miny(), zoff=source.minz()-minz(); for (int z=minz(); z<=maxz(); z++) { for (int y=miny(); y<=maxy(); y++) { for (int x=minx(); x<=maxx(); x++) { value(x,y,z) -= source(x+xoff,y+yoff,z+zoff); } } } } else { fast_const_iterator dit=source.fbegin(); for (nonsafe_fast_iterator it=nsfbegin(), itend=nsfend(); it!=itend; ++it, ++dit) { *it -= *dit; } } return *this; } template const volume& volume::operator*=(const volume& source) { if (!samesize(*this,source)) { imthrow("Attempted to multiply images/ROIs of different sizes",3); } if (activeROI || source.activeROI) { int xoff=source.minx()-minx(), yoff=source.miny()-miny(), zoff=source.minz()-minz(); for (int z=minz(); z<=maxz(); z++) { for (int y=miny(); y<=maxy(); y++) { for (int x=minx(); x<=maxx(); x++) { value(x,y,z) *= source(x+xoff,y+yoff,z+zoff); } } } } else { fast_const_iterator dit=source.fbegin(); for (nonsafe_fast_iterator it=nsfbegin(), itend=nsfend(); it!=itend; ++it, ++dit) { *it *= *dit; } } return *this; } template const volume& volume::operator/=(const volume& source) { if (!samesize(*this,source)) { imthrow("Attempted to divide images/ROIs of different sizes",3); } if (activeROI || source.activeROI) { int xoff=source.minx()-minx(), yoff=source.miny()-miny(), zoff=source.minz()-minz(); for (int z=minz(); z<=maxz(); z++) { for (int y=miny(); y<=maxy(); y++) { for (int x=minx(); x<=maxx(); x++) { value(x,y,z) /= source(x+xoff,y+yoff,z+zoff); } } } } else { fast_const_iterator dit=source.fbegin(); for (nonsafe_fast_iterator it=nsfbegin(), itend=nsfend(); it!=itend; ++it, ++dit) { *it /= *dit; } } return *this; } template volume volume::operator+(T num) const { volume tmp = *this; tmp+=num; return tmp; } template volume volume::operator-(T num) const { volume tmp = *this; tmp-=num; return tmp; } template volume volume::operator*(T num) const { volume tmp = *this; tmp*=num; return tmp; } template volume volume::operator/(T num) const { volume tmp = *this; tmp/=num; return tmp; } template volume volume::operator+(const volume& vol2) const { volume tmp = *this; tmp+=vol2; return tmp; } template volume volume::operator-(const volume& vol2) const { volume tmp = *this; tmp-=vol2; return tmp; } template volume volume::operator*(const volume& vol2) const { volume tmp = *this; tmp*=vol2; return tmp; } template volume volume::operator/(const volume& vol2) const { volume tmp = *this; tmp/=vol2; return tmp; } /////////////////////////////////////////////////////////////////////// //////////////////////// VOLUME4D CLASS ///////////////////////////// /////////////////////////////////////////////////////////////////////// // interfaces for lazy evaluated calculations template minmaxstuff calc_minmax(const volume4D& source); template minmaxstuff calc_minmax(const volume4D& source, const volume& mask); template minmaxstuff calc_minmax(const volume4D& source, const volume4D& mask); template std::vector calc_sums(const volume4D& vol); template std::vector calc_sums(const volume4D& vol, const volume& mask); template std::vector calc_sums(const volume4D& vol, const volume4D& mask); template std::vector calc_percentiles(const volume4D& vol); template std::vector calc_percentiles(const volume4D& vol, const volume& mask, const std::vector& percentilepvals); template std::vector calc_percentiles(const volume4D& vol, const volume4D& mask, const std::vector& percentilepvals); // CONSTRUCTORS (not including copy constructor - see under copying) template int volume4D::initialize(int xsize, int ysize, int zsize, int tsize, T *d) { this->destroy(); long int vsize = xsize * ysize * zsize; volume dummyvol; vols.insert(vols.begin(),tsize,dummyvol); T* ptr = d; for (int t=0; t void volume4D::setdefaultproperties() { p_TR = 1.0; dim5 = 1; Limits.resize(8,0); setdefaultlimits(); // Default ROI is whole volume ROIbox = Limits; activeROI = false; p_extrapmethod = zeropad; p_interpmethod = trilinear; p_padval = (T) 0; tsminmax.init(this,calc_minmax); sums.init(this,calc_sums); percentiles.init(this,calc_percentiles); robustlimits.init(this,calc_robustlimits); l_histogram.init(this,calc_histogram); // Initial percentile pvals to store when calculating percentiles percentilepvals.erase(percentilepvals.begin(),percentilepvals.end()); percentilepvals.push_back(0.0); percentilepvals.push_back(0.001); percentilepvals.push_back(0.005); for (int probval=1; probval<=99; probval++) { percentilepvals.push_back(((float) probval)/100.0); } percentilepvals.push_back(0.995); percentilepvals.push_back(0.999); percentilepvals.push_back(1.0); set_whole_cache_validity(false); } template volume4D::volume4D() { this->initialize(0,0,0,0,0); } template volume4D::volume4D(int xsize, int ysize, int zsize, int tsize, T *d) { this->initialize(xsize,ysize,zsize,tsize,d); } template int volume4D::reinitialize(int xsize, int ysize, int zsize, int tsize, T *d) { return this->initialize(xsize,ysize,zsize,tsize,d); } template void volume4D::destroy() { for (int t=0; t0) vols.clear(); } template volume4D::~volume4D() { this->destroy(); } template void volume4D::setdefaultlimits() const { Limits[0]=0; Limits[1]=0; Limits[2]=0; Limits[3]=0; Limits[4]=this->xsize()-1; Limits[5]=this->ysize()-1; Limits[6]=this->zsize()-1; Limits[7]=this->tsize()-1; } template void volume4D::enforcelimits(std::vector& lims) const { // lims[0]=Max(0,lims[0]); // lims[1]=Max(0,lims[1]); // lims[2]=Max(0,lims[2]); lims[3]=Max(0,lims[3]); // lims[4]=Min(this->xsize() - 1,lims[4]); // lims[5]=Min(this->ysize() - 1,lims[5]); // lims[6]=Min(this->zsize() - 1,lims[6]); lims[7]=Min(this->tsize() - 1,lims[7]); } // COPYING AND CONVERSION FUNCTIONS template int volume4D::reinitialize(const volume4D& source) { this->initialize(source.xsize(),source.ysize(),source.zsize(), source.tsize(),0); this->copyvolumes(source); return this->copyproperties(source); } template volume4D::volume4D(const volume4D& source) { this->reinitialize(source); } template const volume4D& volume4D::operator=(const volume4D& source) { this->reinitialize(source); return *this; } template const volume4D& volume4D::operator=(const volume& source) { this->clear(); this->addvolume(source); return *this; } template int volume4D::copyvolumes(const volume4D& source) { if (tsize() != source.tsize()) { imthrow("Attempted to copy with non-matching tsizes",2); } for (int t=0; t int volume4D::copyproperties(const volume4D& source) { // sets all properties copybasicproperties(source,*this); // copy lazy properties tsminmax.copy(source.tsminmax,this); sums.copy(source.sums,this); percentiles.copy(source.percentiles,this); percentilepvals = source.percentilepvals; robustlimits.copy(source.robustlimits,this); l_histogram.copy(source.l_histogram,this); HISTbins = source.HISTbins; HISTmin = source.HISTmin; HISTmax = source.HISTmax; // now copy all individual volume properties if (sameabssize(source,*this)) { for (int t=0; tmint(); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t].copyproperties(source[Min(t + toffset,source.maxt())]); } } return 0; } template int volume4D::copyproperties(const volume& source) { // copy all individual volume properties for (int t=0; ttsize(); t++) { vols[t].copyproperties(source); } return 0; } // ROI FUNCTIONS template volume4D volume4D::ROI() const { volume4D roivol; roivol.reinitialize(this->maxx() - this->minx()+1, this->maxy() - this->miny()+1, this->maxz() - this->minz()+1, this->maxt() - this->mint()+1); // now copy only the appropriate data for (int t=this->mint(); t<=this->maxt(); t++) { roivol[t - this->mint()].copyROIonly(this->vols[t]); } roivol.copyproperties(*this); roivol.deactivateROI(); // set sform and qform matrices appropriately (if set) Matrix roi2vol= IdentityMatrix(4); roi2vol(1,4) = this->minx(); roi2vol(2,4) = this->miny(); roi2vol(3,4) = this->minz(); if (this->sform_code()!=NIFTI_XFORM_UNKNOWN) { roivol.set_sform(this->sform_code(),this->sform_mat() * roi2vol); } if (this->qform_code()!=NIFTI_XFORM_UNKNOWN) { roivol.set_qform(this->qform_code(),this->qform_mat() * roi2vol); } roivol.set_whole_cache_validity(false); return roivol; } template int volume4D::copyROIonly(const volume4D& source) { if (!samesize(*this,source)) { imthrow("Attempted to copy ROIs when different sizes",3); } int toff = mint() - source.mint(); for (int t=source.mint(); t<=source.maxt(); t++) { vols[t + toff].copyROIonly(source[t]); } set_whole_cache_validity(false); // is the default by reinitialize too return 0; } template void volume4D::setROIlimits(int x0, int y0, int z0, int t0, int x1, int y1, int z1, int t1) const { this->setROIlimits(x0,y0,z0,x1,y1,z1); this->setROIlimits(t0,t1); } template void volume4D::setROIlimits(int x0, int y0, int z0, int x1, int y1, int z1) const { // Enforce ordering ROIbox[0]=Min(x0,x1); ROIbox[1]=Min(y0,y1); ROIbox[2]=Min(z0,z1); ROIbox[4]=Max(x0,x1); ROIbox[5]=Max(y0,y1); ROIbox[6]=Max(z0,z1); enforcelimits(ROIbox); for (int t=0; ttsize(); t++) { vols[t].setROIlimits(x0,y0,z0,x1,y1,z1); } if (activeROI) this->activateROI(); } template void volume4D::setROIlimits(int t0, int t1) const { // Enforce ordering ROIbox[3]=Min(t0,t1); ROIbox[7]=Max(t0,t1); enforcelimits(ROIbox); if (activeROI) this->activateROI(); } template void volume4D::setROIlimits(const std::vector& lims) const { if (lims.size()==6) { setROIlimits(lims[0],lims[1],lims[2],this->mint(), lims[3],lims[4],lims[5],this->maxt()); return; } if (lims.size()!=8) imthrow("ROI limits the wrong size (not 6 or 8) in volume4D::setROIlimits",13); setROIlimits(lims[0],lims[1],lims[2],lims[3], lims[4],lims[5],lims[6],lims[7]); } template void volume4D::activateROI() const { activeROI=true; enforcelimits(ROIbox); Limits = ROIbox; set_whole_cache_validity(false); for (int t=0; ttsize(); t++) { vols[t].activateROI(); } } template void volume4D::deactivateROI() const { activeROI=false; setdefaultlimits(); set_whole_cache_validity(false); for (int t=0; ttsize(); t++) { vols[t].deactivateROI(); } } template void make_consistent_params(const volume4D& vols, int t) { vols[t].setextrapolationmethod(vols.getextrapolationmethod()); vols[t].setinterpolationmethod(vols.getinterpolationmethod()); if (vols.tsize()>0) vols[t].definekernelinterpolation(vols[0]); vols[t].setpadvalue(vols.getpadvalue()); vols[t].setROIlimits(vols.minx(),vols.miny(),vols.minz(), vols.maxx(),vols.maxy(),vols.maxz()); if ( (vols[t].usingROI()) && (!vols.usingROI()) ) { vols[t].deactivateROI(); } if ( (!vols[t].usingROI()) && (vols.usingROI()) ) { vols[t].activateROI(); } } // VOLUME ACCESS FUNCTIONS (INSERT AND DELETE) template void volume4D::insertvolume(const volume& source, int t) { if ((t<0) || (t>tsize())) t=tsize(); if (tsize()>0) { if (!sameabssize(source,vols[0])) { imthrow("Non-equal volume sizes in volume4D",3); } } vols.insert(vols.begin() + t,source); if (!activeROI) setdefaultlimits(); // fix up new time-length make_consistent_params(*this,t); set_whole_cache_validity(false); } template void volume4D::addvolume(const volume& source) { // add to the end of the series insertvolume(source,tsize()); } template void volume4D::addvolume(const volume4D& source) { // add to the end of the series for (int t=source.mint(); t<=source.maxt(); t++) { addvolume(source[t]); } } template void volume4D::deletevolume(int t) { if ((t<0) || (t>tsize())) t=tsize(); vols.erase(vols.begin() + t); if (!activeROI) setdefaultlimits(); set_whole_cache_validity(false); } template void volume4D::clear() { // inefficient but safe - should replace with a better method someday for (int t=tsize()-1; t>=0; t--) { this->deletevolume(t); } } template interpolation volume4D::getinterpolationmethod() const { return p_interpmethod; } template void volume4D::setinterpolationmethod(interpolation interpmethod) const { p_interpmethod = interpmethod; if (interpmethod == userinterpolation) { this->defineuserinterpolation(p_userinterp); } for (int t=0; t0) this->definekernelinterpolation(vols[0]); } } } template unsigned int volume4D::getsplineorder() const { if (!tsize()) imthrow("getsplineorder: No volumes defined yet",10); return(vols[0].getsplineorder()); } template void volume4D::setsplineorder(unsigned int order) const { for (int i=0; i void volume4D::setextrapolationvalidity(bool xv, bool yv, bool zv) const { for (int i=0; i std::vector volume4D::getextrapolationvalidity() const { if (!tsize()) imthrow("getextrapolationvalidity: No volumes defined yet",10); return(vols[0].getextrapolationvalidity()); } template extrapolation volume4D::getextrapolationmethod() const { return p_extrapmethod; } template void volume4D::setextrapolationmethod(extrapolation extrapmethod) const { p_extrapmethod = extrapmethod; for (int t=0; t void volume4D::setpadvalue(T padval) const { p_padval = padval; for (int t=0; t T volume4D::getpadvalue() const { return p_padval; } template void volume4D::defineuserinterpolation(float (*interp)( const volume& , float, float, float)) const { p_userinterp = interp; for (int t=0; t void volume4D::defineuserextrapolation(T (*extrap)( const volume& , int, int, int)) const { p_userextrap = extrap; for (int t=0; t void volume4D::definekernelinterpolation(const ColumnVector& kx, const ColumnVector& ky, const ColumnVector& kz, int wx, int wy, int wz) const { // full widths for (int t=0; t void volume4D::definekernelinterpolation(const volume4D& vol) const { if (vol.tsize()>0) { for (int t=0; t void volume4D::definekernelinterpolation(const volume& vol) const { for (int t=0; t void volume4D::definesincinterpolation(const string& sincwindowtype, int w, int nstore) const { // full width for (int t=0; t void volume4D::definesincinterpolation(const string& sincwindowtype, int wx, int wy, int wz, int nstore) const { // full widths for (int t=0; t VOLUME4D CONVERSIONS template ReturnMatrix volume4D::matrix(const volume& mask) const { Matrix matv; if (tsize()<=0) return matv; if (!samesize(mask,vols[0])) { imthrow("Mask of different size used in matrix()",3); } long cidx(1); matv.ReSize(this->maxt() - this->mint() + 1, no_mask_voxels(mask)); int xoff = vols[0].minx() - mask.minx(); int yoff = vols[0].miny() - mask.miny(); int zoff = vols[0].minz() - mask.minz(); int toff = 1 - this->mint(); for (int z=mask.minz(); z<=mask.maxz(); z++) { for (int y=mask.miny(); y<=mask.maxy(); y++) { for (int x=mask.minx(); x<=mask.maxx(); x++) { if (mask(x,y,z)>0) { for (int t=this->mint(); t<=this->maxt(); t++) { matv(t+toff,cidx) = vols[t](x+xoff,y+yoff,z+zoff); } cidx++; } } } } matv.Release(); return matv; } template ReturnMatrix volume4D::matrix(const volume& mask, vector& voxelLabels) const { voxelLabels.clear(); Matrix matv; if (tsize()<=0) return matv; if (!samesize(mask,vols[0])) { imthrow("Mask of different size used in matrix()",3); } long cidx (1); matv.ReSize(this->maxt() - this->mint() + 1, no_mask_voxels(mask) ); int xoff = vols[0].minx() - mask.minx(); int yoff = vols[0].miny() - mask.miny(); int zoff = vols[0].minz() - mask.minz(); int toff = 1 - this->mint(); for (int z=mask.minz(); z<=mask.maxz(); z++) { for (int y=mask.miny(); y<=mask.maxy(); y++) { for (int x=mask.minx(); x<=mask.maxx(); x++) { if (mask(x,y,z)>0) { voxelLabels.push_back(x+y*mask.xsize()+z*mask.xsize()*mask.ysize()); for (int t=this->mint(); t<=this->maxt(); t++) { matv(t+toff,cidx) = vols[t](x+xoff,y+yoff,z+zoff); } cidx++; } } } } matv.Release(); return matv; } template ReturnMatrix volume4D::matrix() const { volume mask(vols[0]); mask = 1; return matrix(mask); } template void volume4D::setmatrix(const Matrix& newmatrix, const volume& mask, const T pad) { int tsz = this->maxt() - this->mint() + 1; if ( (tsz==0) || (tsz!=newmatrix.Nrows()) || (!samesize(mask,vols[0])) ) { this->reinitialize(mask.xsize(),mask.ysize(),mask.zsize(), newmatrix.Nrows()); } this->copyproperties(mask); this->operator=(pad); if (newmatrix.Ncols()!=no_mask_voxels(mask)) { imthrow("Incompatible number of mask positions and matrix columns",4); } long cidx = 1; int xoff = mask.minx() - vols[0].minx(); int yoff = mask.miny() - vols[0].miny(); int zoff = mask.minz() - vols[0].minz(); for (int z=vols[0].minz(); z<=vols[0].maxz(); z++) { for (int y=vols[0].miny(); y<=vols[0].maxy(); y++) { for (int x=vols[0].minx(); x<=vols[0].maxx(); x++) { if (mask(x+xoff,y+yoff,z+zoff)>0) { for (int t=this->mint(); t<=this->maxt(); t++) { vols[t](x,y,z) = (T) newmatrix(t+1,cidx); } cidx++; } } } } set_whole_cache_validity(false); } template void volume4D::setmatrix(const Matrix& newmatrix) { volume dummymask(vols[0]); dummymask = 1; this->setmatrix(newmatrix,dummymask,0); } template volume volume4D::vol2matrixkey(const volume& mask) { int count=1; volume tmp(this->xsize(),this->ysize(),this->zsize()); for(int z=0;z< this->zsize();z++){ for(int y=0;yysize();y++){ for(int x=0;xxsize();x++){ if(mask(x,y,z)>0){ tmp(x,y,z)=count; count++; } else{ tmp(x,y,z)=0; } } } } return tmp; } template ReturnMatrix volume4D::matrix2volkey(volume& mask){ int count=0; for(int z=0;z< this->zsize();z++) for(int y=0;yysize();y++) for(int x=0;xxsize();x++) if(mask(x,y,z)>0) count++; Matrix key(count,3); count=1; for(int z=0;z< this->zsize();z++) for(int y=0;yysize();y++) for(int x=0;xxsize();x++) if(mask(x,y,z)>0){ key(count,1)=x; key(count,2)=y; key(count,3)=z; count++; } key.Release(); return key; } template ReturnMatrix volume4D::voxelts(int x, int y, int z) const { ColumnVector res; if (this->maxt()mint()) return res; res.ReSize(this->maxt() - this->mint() + 1); int toff = 1 - this->mint(); for (int t=this->mint(); t<=this->maxt(); t++) { res(t + toff) = (NEWMAT::Real) vols[t](x,y,z); } res.Release(); return res; } template void volume4D::setvoxelts(const ColumnVector& ts, int x, int y, int z) { if (ts.Nrows() != (this->maxt() - this->mint() + 1)) { imthrow("setvoxelts - incorrectly sized vector",3); } for (int t=this->mint(); t<=this->maxt(); t++) { vols[t](x,y,z) = (T) ts(t+1); } } // PROPERTIES template void volume4D::setxdim(float x) { for (int t=0; t void volume4D::setydim(float y) { for (int t=0; t void volume4D::setzdim(float z) { for (int t=0; t void volume4D::setDisplayMaximumMinimum(const float maximum, const float minimum) const { for (int t=0; t void volume4D::setAuxFile(const string fileName) { for (int t=0; t minmaxstuff calc_minmax(const volume4D& source) { minmaxstuff newminmax; newminmax.min=source(source.minx(),source.miny(),source.minz(),source.mint()); newminmax.max=newminmax.min; newminmax.minx=source.minx(); newminmax.miny=source.miny(); newminmax.minz=source.minz(); newminmax.maxx=source.minx(); newminmax.maxy=source.miny(); newminmax.maxz=source.minz(); newminmax.mint=source.mint(); newminmax.maxt=source.maxt(); if (source.maxt()>=source.mint()) { newminmax = calc_minmax(source[source.mint()]); newminmax.mint = source.mint(); newminmax.maxt = source.mint(); } for (int t=source.mint(); t<=source.maxt(); t++) { if (source[t].min() < newminmax.min) { newminmax.min = source[t].min(); newminmax.minx = source[t].mincoordx(); newminmax.miny = source[t].mincoordy(); newminmax.minz = source[t].mincoordz(); newminmax.mint = t; } if (source[t].max() > newminmax.max) { newminmax.max = source[t].max(); newminmax.maxx = source[t].maxcoordx(); newminmax.maxy = source[t].maxcoordy(); newminmax.maxz = source[t].maxcoordz(); newminmax.maxt = t; } } return newminmax; } template minmaxstuff calc_minmax(const volume4D& source, const volume& mask) { if (!samesize(source[0],mask)) { imthrow("Mask of different size used in calc_minmax",3); } minmaxstuff newminmax; newminmax.min=source(source.minx(),source.miny(),source.minz(),source.mint()); newminmax.max=newminmax.min; newminmax.minx=source.minx(); newminmax.miny=source.miny(); newminmax.minz=source.minz(); newminmax.maxx=source.minx(); newminmax.maxy=source.miny(); newminmax.maxz=source.minz(); newminmax.mint=source.mint(); newminmax.maxt=source.maxt(); if (source.maxt()>=source.mint()) { newminmax = calc_minmax(source[source.mint()],mask); newminmax.mint = source.mint(); newminmax.maxt = source.mint(); } for (int t=source.mint(); t<=source.maxt(); t++) { if (source[t].min(mask) < newminmax.min) { newminmax.min = source[t].min(mask); newminmax.minx = source[t].mincoordx(mask); newminmax.miny = source[t].mincoordy(mask); newminmax.minz = source[t].mincoordz(mask); newminmax.mint = t; } if (source[t].max(mask) > newminmax.max) { newminmax.max = source[t].max(mask); newminmax.maxx = source[t].maxcoordx(mask); newminmax.maxy = source[t].maxcoordy(mask); newminmax.maxz = source[t].maxcoordz(mask); newminmax.maxt = t; } } return newminmax; } template minmaxstuff calc_minmax(const volume4D& source, const volume4D& mask) { if (!samesize(source[0],mask[0])) { imthrow("Mask of different size used in calc_minmax",3); } minmaxstuff newminmax; newminmax.min=source(source.minx(),source.miny(),source.minz(),source.mint()); newminmax.max=newminmax.min; newminmax.minx=source.minx(); newminmax.miny=source.miny(); newminmax.minz=source.minz(); newminmax.maxx=source.minx(); newminmax.maxy=source.miny(); newminmax.maxz=source.minz(); newminmax.mint=source.mint(); newminmax.maxt=source.maxt(); if (source.maxt()>=source.mint()) { newminmax = calc_minmax(source[source.mint()],mask[Min(source.mint(),mask.maxt())]); newminmax.mint = source.mint(); newminmax.maxt = source.mint(); } for (int t=source.mint(); t<=source.maxt(); t++) { if (source[t].min(mask[Min(t,mask.maxt())]) < newminmax.min) { newminmax.min = source[t].min(mask[Min(t,mask.maxt())]); newminmax.minx = source[t].mincoordx(mask[Min(t,mask.maxt())]); newminmax.miny = source[t].mincoordy(mask[Min(t,mask.maxt())]); newminmax.minz = source[t].mincoordz(mask[Min(t,mask.maxt())]); newminmax.mint = t; } if (source[t].max(mask[Min(t,mask.maxt())]) > newminmax.max) { newminmax.max = source[t].max(mask[Min(t,mask.maxt())]); newminmax.maxx = source[t].maxcoordx(mask[Min(t,mask.maxt())]); newminmax.maxy = source[t].maxcoordy(mask[Min(t,mask.maxt())]); newminmax.maxz = source[t].maxcoordz(mask[Min(t,mask.maxt())]); newminmax.maxt = t; } } return newminmax; } template std::vector calc_sums(const volume4D& vol) { std::vector newsums(2), addterm(2); newsums[0]=0; newsums[1]=0; for (int t=vol.mint(); t<=vol.maxt(); t++) { addterm = calc_sums(vol[t]); newsums[0] += addterm[0]; newsums[1] += addterm[1]; } return newsums; } template std::vector calc_sums(const volume4D& vol, const volume& mask) { if (!samesize(vol[0],mask)) { imthrow("calc_sums:: mask and volume must be the same size",4); } std::vector newsums(2), addterm(2); newsums[0]=0; newsums[1]=0; for (int t=vol.mint(); t<=vol.maxt(); t++) { addterm = calc_sums(vol[t],mask); newsums[0] += addterm[0]; newsums[1] += addterm[1]; } return newsums; } template std::vector calc_sums(const volume4D& vol, const volume4D& mask) { if (!samesize(vol[0],mask[0])) { imthrow("calc_sums:: mask and volume must be the same size",4); } std::vector newsums(2), addterm(2); newsums[0]=0; newsums[1]=0; for (int t=vol.mint(); t<=vol.maxt(); t++) { addterm = calc_sums(vol[t],mask[Min(t,mask.maxt())]); newsums[0] += addterm[0]; newsums[1] += addterm[1]; } return newsums; } template T volume4D::min(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.min; } template T volume4D::min(const volume4D& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.min; } template T volume4D::max(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.max; } template T volume4D::max(const volume4D& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.max; } template int volume4D::mincoordx(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.minx; } template int volume4D::mincoordx(const volume4D& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.minx; } template int volume4D::mincoordy(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.miny; } template int volume4D::mincoordy(const volume4D& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.miny; } template int volume4D::mincoordz(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.minz; } template int volume4D::mincoordz(const volume4D& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.minz; } template int volume4D::maxcoordx(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.maxx; } template int volume4D::maxcoordx(const volume4D& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.maxx; } template int volume4D::maxcoordy(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.maxy; } template int volume4D::maxcoordy(const volume4D& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.maxy; } template int volume4D::maxcoordz(const volume& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.maxz; } template int volume4D::maxcoordz(const volume4D& mask) const { minmaxstuff retval; retval = calc_minmax(*this,mask); return retval.maxz; } template double volume4D::sum(const volume& mask) const { std::vector retval; retval = calc_sums(*this,mask); return retval[0]; } template double volume4D::sum(const volume4D& mask) const { std::vector retval; retval = calc_sums(*this,mask); return retval[0]; } template double volume4D::sumsquares(const volume& mask) const { std::vector retval; retval = calc_sums(*this,mask); return retval[1]; } template double volume4D::sumsquares(const volume4D& mask) const { std::vector retval; retval = calc_sums(*this,mask); return retval[1]; } template double volume4D::mean(const volume& mask) const { return sum(mask)/(Max((double) no_mask_voxels(mask),1.0)); } template double volume4D::mean(const volume4D& mask) const { return sum(mask)/(Max((double) no_mask_voxels(mask),1.0)); } template double volume4D::variance(const volume& mask) const { if (no_mask_voxels(mask)>0) { double n=(double) no_mask_voxels(mask); return (n/Max(1.0,n-1))*(sumsquares(mask)/n - mean(mask)*mean(mask)); } else { cerr << "ERROR:: Empty mask image" << endl; return 0; } } template double volume4D::variance(const volume4D& mask) const { if (no_mask_voxels(mask)>0) { double n=(double) no_mask_voxels(mask); return (n/Max(1.0,n-1))*(sumsquares(mask)/n - mean(mask)*mean(mask)); } else { cerr << "ERROR:: Empty mask image" << endl; return 0; } } template T volume4D::percentile(float pvalue) const { if ((pvalue>1.0) || (pvalue<0.0)) { imthrow("Percentiles must be in the range [0.0,1.0]",4); } int idx = get_pval_index(percentilepvals,pvalue); if (idx==pval_index_end()) { percentilepvals.push_back(pvalue); idx = percentilepvals.size() - 1; percentiles.force_recalculation(); } assert((idx>=0) && (idx < (int) percentilepvals.size())); return percentiles()[idx]; } template T volume4D::percentile(float pvalue, const volume& mask) const { if ((pvalue>1.0) || (pvalue<0.0)) { imthrow("Percentiles must be in the range [0.0,1.0]",4); } std::vector pvaluevec; std::vector retval; pvaluevec.push_back(pvalue); retval = calc_percentiles(*this,mask,pvaluevec); return retval[0]; } template T volume4D::percentile(float pvalue, const volume4D& mask) const { if ((pvalue>1.0) || (pvalue<0.0)) { imthrow("Percentiles must be in the range [0.0,1.0]",4); } std::vector pvaluevec; std::vector retval; pvaluevec.push_back(pvalue); retval = calc_percentiles(*this,mask,pvaluevec); return retval[0]; } template std::vector calc_percentiles(const volume4D& vol) { unsigned int numbins = (unsigned int) vol.nvoxels() * vol.ntimepoints(); unsigned int hindx = 0; std::vector hist(numbins); for (int t=vol.mint(); t<=vol.maxt(); t++) { for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { hist[hindx++] = vol(x,y,z,t); } } } } return percentile_vec(hist,vol.percentilepvalues()); } template std::vector calc_percentiles(const volume4D& vol, const volume& mask, const std::vector& percentilepvals) { if (!samesize(vol[0],mask)) { imthrow("mask and vol have different sizes in calc_percentiles",3); } std::vector hist; for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { if (mask(x,y,z)>0.5) { for (int t=vol.mint(); t<=vol.maxt(); t++) { hist.push_back(vol(x,y,z,t)); } } } } } return percentile_vec(hist,percentilepvals); } template std::vector calc_percentiles(const volume4D& vol, const volume4D& mask, const std::vector& percentilepvals) { if (!samesize(vol[0],mask[0])) { imthrow("mask and vol have different sizes in calc_percentiles",3); } std::vector hist; for (int t=vol.mint(); t<=vol.maxt(); t++) { for (int z=vol.minz(); z<=vol.maxz(); z++) { for (int y=vol.miny(); y<=vol.maxy(); y++) { for (int x=vol.minx(); x<=vol.maxx(); x++) { if (mask(x,y,z,Min(t,mask.maxt()))>0.5) hist.push_back(vol(x,y,z,t)); } } } } return percentile_vec(hist,percentilepvals); } template ColumnVector calc_histogram(const volume4D& vol) { ColumnVector hist; calc_histogram(vol,vol.histbins(),vol.histmin(),vol.histmax(),hist); return hist; } template ColumnVector calc_histogram(const volume4D& vol, const volume4D& mask) { ColumnVector hist; calc_histogram(vol,vol.histbins(),vol.histmin(),vol.histmax(),hist,mask); return hist; } template ColumnVector calc_histogram(const volume4D& vol, const volume& mask) { ColumnVector hist; calc_histogram(vol,vol.histbins(),vol.histmin(),vol.histmax(),hist,mask); return hist; } template ColumnVector volume4D::histogram(int nbins, T minval, T maxval) const { bool sameparams = true; if (HISTbins != nbins) { HISTbins = nbins; sameparams = false; } if (HISTmin != minval) { HISTmin = minval; sameparams = false; } if (HISTmax != maxval) { HISTmax = maxval; sameparams = false; } if (!sameparams) { l_histogram.force_recalculation(); } return l_histogram(); } template ColumnVector volume4D::histogram(int nbins) const { return histogram(nbins,robustmin(),robustmax()); } template ColumnVector volume4D::histogram(int nbins, T minval, T maxval, const volume4D& mask) const { ColumnVector hist; calc_histogram(*this,nbins,minval,maxval,hist,mask); return hist; } template ColumnVector volume4D::histogram(int nbins, const volume4D& mask) const { return histogram(nbins,robustmin(),robustmax(),mask); } template ColumnVector volume4D::histogram(int nbins, T minval, T maxval, const volume& mask) const { ColumnVector hist; calc_histogram(*this,nbins,minval,maxval,hist,mask); return hist; } template ColumnVector volume4D::histogram(int nbins, const volume& mask) const { return histogram(nbins,robustmin(),robustmax(),mask); } // GENERAL MANIPULATION template void volume4D::binarise(T lowerth, T upperth, threshtype tt) { set_whole_cache_validity(false); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t].binarise(lowerth,upperth,tt); } } template void volume4D::threshold(T lowerth, T upperth, threshtype tt) { set_whole_cache_validity(false); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t].threshold(lowerth,upperth,tt); } } template void volume4D::swapdimensions(int dim1, int dim2, int dim3) { for (int t=0; ttsize(); t++) { vols[t].swapdimensions(dim1,dim2,dim3); } } template void volume4D::swapdimensions(const string& newx, const string& newy, const string& newz) { this->swapdimensions(dimarg(newx),dimarg(newy),dimarg(newz)); } template Matrix volume4D::swapmat(int dim1, int dim2, int dim3) const { if (this->tsize()>0) { return vols[0].swapmat(dim1,dim2,dim3); } return IdentityMatrix(4); } template Matrix volume4D::swapmat(const string& newx, const string& newy, const string& newz) const { return this->swapmat(dimarg(newx),dimarg(newy),dimarg(newz)); } template Matrix volume4D::newimagevox2mm_mat() const { if (this->tsize()>0) return vols[0].newimagevox2mm_mat(); return IdentityMatrix(4); } template Matrix volume4D::niftivox2newimagevox_mat() const { if (this->tsize()>0) return vols[0].niftivox2newimagevox_mat(); return IdentityMatrix(4); } template int volume4D::left_right_order() const { if (this->tsize()>0) return vols[0].left_right_order(); return FSL_RADIOLOGICAL; } template void volume4D::swapLRorder() { for (int t=0; ttsize(); t++) { vols[t].swapLRorder(); } } template void volume4D::setLRorder(int LRorder) { if (LRorder != this->left_right_order()) { this->swapLRorder(); } } template void volume4D::makeradiological() { if (this->left_right_order()==FSL_NEUROLOGICAL) { this->swapLRorder(); } } template void volume4D::makeneurological() { if (this->left_right_order()==FSL_RADIOLOGICAL) { this->swapLRorder(); } } // ARITHMETIC OPERATIONS template T volume4D::operator=(T val) { set_whole_cache_validity(false); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] = val; } return val; } template const volume4D& volume4D::operator+=(T val) { set_whole_cache_validity(false); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] += val; } return *this; } template const volume4D& volume4D::operator-=(T val) { set_whole_cache_validity(false); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] -= val; } return *this; } template const volume4D& volume4D::operator*=(T val) { set_whole_cache_validity(false); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] *= val; } return *this; } template const volume4D& volume4D::operator/=(T val) { set_whole_cache_validity(false); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] /= val; } return *this; } template const volume4D& volume4D::operator+=(const volume& source) { set_whole_cache_validity(false); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] += source; } return *this; } template const volume4D& volume4D::operator-=(const volume& source) { set_whole_cache_validity(false); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] -= source; } return *this; } template const volume4D& volume4D::operator*=(const volume& source) { set_whole_cache_validity(false); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] *= source; } return *this; } template const volume4D& volume4D::operator/=(const volume& source) { set_whole_cache_validity(false); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] /= source; } return *this; } template const volume4D& volume4D::operator+=(const volume4D& source) { if (!samesize(*this,source)) { imthrow("Attempted to add images/ROIs of different sizes",3); } set_whole_cache_validity(false); int toff = source.mint() - this->mint(); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] += source[t + toff]; } return *this; } template const volume4D& volume4D::operator-=(const volume4D& source) { if (!samesize(*this,source)) { imthrow("Attempted to subtract images/ROIs of different sizes",3); } set_whole_cache_validity(false); int toff = source.mint() - this->mint(); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] -= source[t + toff]; } return *this; } template const volume4D& volume4D::operator*=(const volume4D& source) { if (!samesize(*this,source)) { imthrow("Attempted to multiply images/ROIs of different sizes",3); } set_whole_cache_validity(false); int toff = source.mint() - this->mint(); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] *= source[t + toff]; } return *this; } template const volume4D& volume4D::operator/=(const volume4D& source) { if (!samesize(*this,source)) { imthrow("Attempted to divide images/ROIs of different sizes",3); } set_whole_cache_validity(false); int toff = source.mint() - this->mint(); for (int t=this->mint(); t<=this->maxt(); t++) { vols[t] /= source[t + toff]; } return *this; } template volume4D volume4D::operator+(T num) const { set_whole_cache_validity(false); volume4D tmp = *this; tmp+=num; return tmp; } template volume4D volume4D::operator-(T num) const { set_whole_cache_validity(false); volume4D tmp = *this; tmp-=num; return tmp; } template volume4D volume4D::operator*(T num) const { volume4D tmp = *this; tmp*=num; return tmp; } template volume4D volume4D::operator/(T num) const { set_whole_cache_validity(false); volume4D tmp = *this; tmp/=num; return tmp; } template volume4D volume4D::operator+(const volume& vol2) const { volume4D tmp = *this; tmp+=vol2; return tmp; } template volume4D volume4D::operator-(const volume& vol2) const { set_whole_cache_validity(false); volume4D tmp = *this; tmp-=vol2; return tmp; } template volume4D volume4D::operator*(const volume& vol2) const { volume4D tmp = *this; tmp*=vol2; return tmp; } template volume4D volume4D::operator/(const volume& vol2) const { set_whole_cache_validity(false); volume4D tmp = *this; tmp/=vol2; return tmp; } template volume4D volume4D::operator+(const volume4D& vol2) const { set_whole_cache_validity(false); volume4D tmp = *this; tmp+=vol2; return tmp; } template volume4D volume4D::operator-(const volume4D& vol2) const { set_whole_cache_validity(false); volume4D tmp = *this; tmp-=vol2; return tmp; } template volume4D volume4D::operator*(const volume4D& vol2) const { set_whole_cache_validity(false); volume4D tmp = *this; tmp*=vol2; return tmp; } template volume4D volume4D::operator/(const volume4D& vol2) const { set_whole_cache_validity(false); volume4D tmp = *this; tmp/=vol2; return tmp; } /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// // SPECIFIC INSTANTIATIONS // provide only these instances of the class // NB: unsigned int is not included as it is not a valid AVW DT_TYPE template class volume; template class volume; template class volume; template class volume; template class volume; // template class volume; // does not define less than template class volume4D; template class volume4D; template class volume4D; template class volume4D; template class volume4D; // template class volume4D; // does not define less than } // end namespace