/*! \file LSResampler.cpp \brief Contains definition of CPU implementation of a class for least-squares resampling of pairs of images \author Jesper Andersson \version 1.0b, August, 2013. */ // // LSResampler.cpp // // Jesper Andersson, FMRIB Image Analysis Group // // Copyright (C) 2011 University of Oxford // #include #include #include #include #include #include "armawrap/newmat.h" #ifndef EXPOSE_TREACHEROUS #define EXPOSE_TREACHEROUS // To allow us to use .set_sform etc #endif #include "newimage/newimageall.h" #include "miscmaths/miscmaths.h" #include "warpfns/warpfns.h" #include "topup/topup_file_io.h" #include "topup/displacement_vector.h" #include "EddyHelperClasses.h" #include "EddyUtils.h" #include "ECScanClasses.h" #include "LSResampler.h" namespace EDDY { class LSResamplerImpl { public: LSResamplerImpl(const EDDY::ECScan& s1, const EDDY::ECScan& s2, std::shared_ptr > hzfield, double lambda, const Utilities::NoOfThreads& nthr); const NEWIMAGE::volume& GetResampledVolume() const EddyTry { return(_rvol); } EddyCatch const NEWIMAGE::volume& GetMask() const EddyTry { return(_omask); } EddyCatch private: NEWIMAGE::volume _rvol; // Resampled volume NEWIMAGE::volume _omask; // Mask indicating valid voxels in _rvol // Convenience routine that resamples the slices given by 'slices'. // It greatly facilitates a parallel implementation, which however // is not currently used. void resample_slices(// Input const NEWIMAGE::volume& ima1, const NEWIMAGE::volume& field1, const NEWIMAGE::volume& ima2, const NEWIMAGE::volume& field2, const NEWIMAGE::volume& mask, double lambda, int offset, int stride, bool pex, // Output NEWIMAGE::volume& ovol, NEWIMAGE::volume& omask); template T sqr(const T& v) const { return(v*v); } }; LSResampler::LSResampler(const EDDY::ECScan& s1, const EDDY::ECScan& s2, std::shared_ptr > hzfield, double lambda, const Utilities::NoOfThreads& nthr) EddyTry { _pimpl = new LSResamplerImpl(s1,s2,hzfield,lambda,nthr); } EddyCatch LSResampler::~LSResampler() { delete _pimpl; } const NEWIMAGE::volume& LSResampler::GetResampledVolume() const EddyTry { return(_pimpl->GetResampledVolume()); } EddyCatch const NEWIMAGE::volume& LSResampler::GetMask() const EddyTry { return(_pimpl->GetMask()); } EddyCatch /****************************************************************//** * * Constructs an LSResamplerImpl object. * All the work for resampling a pair of scans into a single volume * is carried out inside this constructor. After the object has been * constructed one can immediately obtain the resampled volume through * a call to GetResampledVolume. * \param s1 One of a pair of scans with parallel phase-encodings * \param s2 The second of a pair of scans with parallel phase-encodings * \param hzfield Field in Hz in model space * ********************************************************************/ LSResamplerImpl::LSResamplerImpl(const EDDY::ECScan& s1, const EDDY::ECScan& s2, std::shared_ptr > hzfield, double lambda, const Utilities::NoOfThreads& nthr) EddyTry { // cout << "I'm in CPU version of LSResampler" << endl; if (!EddyUtils::AreMatchingPair(s1,s2)) throw EddyException("LSResampler::LSResampler:: Mismatched pair"); // Resample both images using rigid body parameters NEWIMAGE::volume mask1, mask2; NEWIMAGE::volume ima1 = s1.GetMotionCorrectedIma(mask1); NEWIMAGE::volume ima2 = s2.GetMotionCorrectedIma(mask2); NEWIMAGE::volume mask = mask1*mask2; _omask.reinitialize(mask.xsize(),mask.ysize(),mask.zsize()); _omask = 1.0; _rvol.reinitialize(ima1.xsize(),ima1.ysize(),ima1.zsize()); NEWIMAGE::copybasicproperties(ima1,_rvol); // Get fields NEWIMAGE::volume4D field1_4D = s1.FieldForScanToModelTransform(hzfield); // In mm NEWIMAGE::volume4D field2_4D = s2.FieldForScanToModelTransform(hzfield); // In mm // Check what direction phase-encode is in bool pex = (s1.GetAcqPara().PhaseEncodeVector()(1)) ? true : false; // Get relevant parts of fields NEWIMAGE::volume field1, field2; if (pex) { field1 = field1_4D[0]; field2 = field2_4D[0]; } else { field1 = field1_4D[1]; field2 = field2_4D[1]; } // Pre-calculate some stuff for convenience if (nthr._n == 1) { // Single thread std::vector slices(ima1.zsize()); std::iota (slices.begin(),slices.end(),0); // Fill with 0, 1, ..., ima1.zsize()-1 this->resample_slices(ima1,field1,ima2,field2,mask,lambda,0,1,pex,_rvol,_omask); } else if (nthr._n > 1) { std::vector threads(nthr._n-1); for (int t=0; tresample_slices(ima1,field1,ima2,field2,mask,lambda,nthr._n-1,nthr._n,pex,_rvol,_omask); std::for_each(threads.begin(),threads.end(),std::mem_fn(&std::thread::join)); } return; } EddyCatch void LSResamplerImpl::resample_slices(// Input const NEWIMAGE::volume& ima1, const NEWIMAGE::volume& field1, const NEWIMAGE::volume& ima2, const NEWIMAGE::volume& field2, const NEWIMAGE::volume& mask, double lambda, int offset, int stride, bool pex, // Output NEWIMAGE::volume& ovol, NEWIMAGE::volume& omask) { // Display vector class that does much of the work unsigned int sz = (pex) ? field1.xsize() : field1.ysize(); TOPUP::DispVec dv1(sz), dv2(sz); // Pre-calculate some stuff for convenience NEWMAT::Matrix StS = dv1.GetS_Matrix(false); StS = lambda*StS.t()*StS; NEWMAT::ColumnVector zeros; if (pex) zeros.ReSize(ima1.xsize()); else zeros.ReSize(ima1.ysize()); zeros = 0.0; double sf1, sf2; // Scale factors mm->voxels if (pex) { sf1 = 1.0/ima1.xdim(); sf2 = 1.0/ima2.xdim(); } else { sf1 = 1.0/ima1.ydim(); sf2 = 1.0/ima2.ydim(); } // Loop over all slices we are supposed to do for (unsigned int k=offset; k