// Definitions of classes that implements useful // concepts for the eddy current project. // // EddyHelperClasses.cpp // // Jesper Andersson, FMRIB Image Analysis Group // // Copyright (C) 2011 University of Oxford // #include #include #include #include #include #include #include "nlohmann/json.hpp" #include "armawrap/newmat.h" #include "newimage/newimageall.h" #include "miscmaths/miscmaths.h" #include "EddyHelperClasses.h" #include "EddyUtils.h" using namespace std; using namespace EDDY; //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class JsonReader // // This class manages reading .json files. // //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ JsonReader::~JsonReader() { delete _content; } NEWMAT::Matrix JsonReader::SliceOrdering() const EddyTry { // Check for existence of "SliceTiming" field auto SOObj = _content->find("SliceTiming"); if (SOObj == _content->end()) { // Field not found throw EddyException("JsonReader::SliceOrdering: Failed to find object \"SliceTiming\""); } std::vector slice_times = (*_content)["SliceTiming"]; // Sort slice indicies in order of increasing acquisition time std::vector > indextime(slice_times.size()); for (unsigned int i=0; i p1, std::pair p2) { return((p1.second == p2.second) ? (p1.first < p2.first) : (p1.second < p2.second)); }); // Do a little sanity check unsigned int mbf = 1; for (unsigned int i=1; i indextime[i-1].second) break; else mbf++; } double ctime = indextime[0].second; unsigned int cnt = 1; for (unsigned int i=1; ifind("MultibandAccelerationFactor"); if (MBFObj != _content->end()) { int MBFInt = (*_content)["MultibandAccelerationFactor"]; if (MBFInt != mbf) throw("JsonReader::SliceOrdering: Mismatch between \"SliceTiming\" and \"MultibandAccelerationFactor\""); } // Finally, repack it in slspec format NEWMAT::Matrix M(indextime.size()/mbf,mbf); unsigned int ii=0; for (int ri=0; ri(indextime[ii++].first)); } return(M); } EddyCatch NEWMAT::ColumnVector JsonReader::PEVector() const EddyTry { NEWMAT::ColumnVector pe(3); pe = 0; // Check for existence of "PhaseEncodingDirection" field auto PEObj = _content->find("PhaseEncodingDirection"); if (PEObj == _content->end()) { // Field not found throw EddyException("JsonReader::PEVector: Failed to find object \"PhaseEncodingDirection\""); } std::string PEString = (*_content)["PhaseEncodingDirection"]; if (PEString == std::string("i")) pe(1) = 1; else if (PEString == std::string("i-")) pe(1) = -1; else if (PEString == std::string("j")) pe(2) = 1; else if (PEString == std::string("j-")) pe(2) = -1; else throw EddyException("JsonReader::PEVector: Failed to decode \"PhaseEncodingDirection\""); return(pe); } EddyCatch double JsonReader::TotalReadoutTime() const EddyTry { auto TRTObj = _content->find("TotalReadoutTime"); if (TRTObj == _content->end()) { // Field not found throw EddyException("JsonReader::TotalReadoutTime: Failed to find object \"TotalReadoutTime\""); } return((*_content)["TotalReadoutTime"]); } EddyCatch double JsonReader::EchoTime() const EddyTry { auto ETObj = _content->find("EchoTime"); if (ETObj == _content->end()) { // Field not found throw EddyException("JsonReader::EchoTime: Failed to find object \"EchoTime\""); } return((*_content)["EchoTime"]); } EddyCatch double JsonReader::RepetitionTime() const EddyTry { auto RTObj = _content->find("RepetitionTime"); if (RTObj == _content->end()) { // Field not found throw EddyException("JsonReader::RepetitionTime: Failed to find object \"RepetitionTime\""); } return((*_content)["RepetitionTime"]); } EddyCatch void JsonReader::common_read() EddyTry { try { std::ifstream ifs(_fname); // nlohmann::json tmp = nlohmann::json::parse(ifs); _content = new nlohmann::json; (*_content) = nlohmann::json::parse(ifs); ifs.close(); } catch (std::exception& e) { throw EddyException("JsonReader::common_read(): exception thrown with e.what() = " + std::string(e.what())); } } EddyCatch //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class DiffPara (Diffusion Parameters) // // This class manages the diffusion parameters for a given // scan. // //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool DiffPara::operator==(const DiffPara& rhs) const EddyTry { return(EddyUtils::AreInSameShell(*this,rhs) && EddyUtils::HaveSameDirection(*this,rhs)); } EddyCatch //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class AcqPara (Acquisition Parameters) // // This class manages the acquisition parameters for a given // scan. // //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ AcqPara::AcqPara(const NEWMAT::ColumnVector& pevec, double rotime) EddyTry : _pevec(pevec), _rotime(rotime) { if (pevec.Nrows() != 3) throw EddyException("AcqPara::AcqPara: Wrong dimension pe-vector"); if (rotime < 0.01 || rotime > 0.2) throw EddyException("AcqPara::AcqPara: Unrealistic read-out time"); int cc = 0; //Component count for (int i=0; i<3; i++) { if (fabs(pevec(i+1)) > 1e-6) cc++; } if (!cc) throw EddyException("AcqPara::AcqPara: Zero Phase-encode vector"); if (cc > 1) throw EddyException("AcqPara::AcqPara: Oblique pe-vectors not yet implemented"); } EddyCatch bool AcqPara::operator==(const AcqPara& rh) const EddyTry { if (fabs(this->_rotime-rh._rotime) > 1e-6) return(false); for (int i=0; i<3; i++) { if (fabs(this->_pevec(i+1)-rh._pevec(i+1)) > 1e-6) return(false); } return(true); } EddyCatch std::vector AcqPara::BinarisedPhaseEncodeVector() const EddyTry { std::vector rval(3,0); for (unsigned int i=0; i<3; i++) rval[i] = (_pevec(i+1) == 0) ? 0 : 1; return(rval); } EddyCatch DiffStats::DiffStats(const NEWIMAGE::volume& diff, const NEWIMAGE::volume& mask) EddyTry : _md(diff.zsize(),0.0), _msd(diff.zsize(),0.0), _n(mask.zsize(),0) { #pragma omp parallel for // shared(diff,mask,_md,_msd,_n) private(j,k) for (int k=0; k 1) throw EddyException("MultiBandGroups::MultiBandGroups: offs out of range"); if (((int(_nsl)+std::abs(_offs)) % int(_mb)) || (_mb==1 && _offs!=0)) throw EddyException("MultiBandGroups::MultiBandGroups: Incompatible nsl, mb and offs"); unsigned int ngrp = (_nsl+static_cast(std::abs(_offs))) / _mb; _grps.resize(ngrp); for (unsigned int grp=0; grp= 0 && i < int(_nsl)) _grps[grp].push_back(static_cast(i)); } } _to.resize(ngrp); for (unsigned int grp=0; grp tmp_vec; // Empty vector of slice numbers unsigned int tmp_ui; std::stringstream ss(line); while (ss >> tmp_ui) tmp_vec.push_back(tmp_ui); _grps.push_back(tmp_vec); // Add vector of slice numbers to the end of vector of groups } if (ifs.eof()) ifs.close(); else throw EddyException("MultiBandGroups::MultiBandGroups: Problem reading file"); } else throw EddyException("MultiBandGroups::MultiBandGroups: Unable to open file"); assert_grps(); // Check that _grps is kosher } EddyCatch MultiBandGroups::MultiBandGroups(const NEWMAT::Matrix& slices) EddyTry { _grps.resize(slices.Nrows()); for (unsigned int i=0; i<_grps.size(); i++) { _grps[i].resize(slices.Ncols()); for (unsigned int j=0; j<_grps[i].size(); j++) _grps[i][j] = static_cast(std::round(slices(i+1,j+1))); } assert_grps(); // Check that _grps is kosher } EddyCatch void MultiBandGroups::assert_grps() EddyTry { // First deduce number of slices and MB factor. _nsl = 0; _mb = 0; for (unsigned int grp=0; grp<_grps.size(); grp++) { _mb = std::max(static_cast(_grps.size()),_mb); for (unsigned int sl=0; sl<_grps[grp].size(); sl++) { _nsl = std::max(_grps[grp][sl],_nsl); } } _nsl++; _offs = 0; // Arbitrary, not used. // Next make sure that each slices is specified once and only once. std::vector check_vec(_nsl,0); for (unsigned int grp=0; grp<_grps.size(); grp++) { for (unsigned int sl=0; sl<_grps[grp].size(); sl++) { check_vec[_grps[grp][sl]] += 1; } } for (unsigned int i=0; i >& shi, // Shell indicies const std::vector& bvs, // b-values of the shells unsigned int nsl, // # of slices const OutlierDefinition& old, // Class defining an outlier unsigned int etc, // =1->const (across slices) type 1 error, =2->const type 2 error OLType olt, // Slice-wise, group-wise or both. const MultiBandGroups& mbg) // multi-band structure EddyTry : _old(old), _etc(etc), _olt(olt), _mbg(mbg), _shi(shi), _bvs(bvs) { if (_etc != 1 && _etc != 2) throw EddyException("ReplacementManager::ReplacementManager: etc must be 1 or 2"); unsigned int nscan=0; for (unsigned int i=0; i<_shi.size(); i++) nscan += _shi[i].size(); _sws = ReplacementManager::StatsInfo(nsl,nscan); _gws = ReplacementManager::StatsInfo(_mbg.NGroups(),nscan); _swo = ReplacementManager::OutlierInfo(nsl,nscan); _gwo = ReplacementManager::OutlierInfo(_mbg.NGroups(),nscan); } EddyCatch void ReplacementManager::Update(const DiffStatsVector& dsv) EddyTry { if (dsv.NSlice() != this->NSlice() || dsv.NScan() != this->NScan()) { throw EddyException("ReplacementManager::Update: Mismatched DiffStatsVector object"); } // Populate slice-wise stats matrix for (unsigned int scan=0; scan sl_in_grp = _mbg.SlicesInGroup(grp); _gws._nvox[grp][scan] = 0; _gws._mdiff[grp][scan] = 0.0; _gws._msqrd[grp][scan] = 0.0; for (unsigned int i=0; i(_gws._nvox[grp][scan]); _gws._msqrd[grp][scan] /= static_cast(_gws._nvox[grp][scan]); } } // Calculate population statistics _sss = mean_and_std(_sws,_shi,_old.MinVoxels(),_etc,_swo._ovv); // Slice-wise summary stats _gss = mean_and_std(_gws,_shi,_old.MinVoxels(),_etc,_gwo._ovv); // Group-wise summary stats // First group-wise map for (unsigned int sh=0; sh<_shi.size(); sh++) { for (const unsigned int& scan : _shi[sh]) { for (unsigned int grp=0; grp= _old.MinVoxels()) { // Only consider groups with more than minimum valid voxels double sf = (_etc == 1) ? 1.0 / std::sqrt(double(_gws._nvox[grp][scan])) : 1.0; _gwo._nsv[grp][scan] = (_gws._mdiff[grp][scan] - _gss[sh]._diff_mean) / (sf * _gss[sh]._diff_std); _gwo._nsq[grp][scan] = (_gws._msqrd[grp][scan] - _gss[sh]._sqr_mean) / (sf * _gss[sh]._sqr_std); _gwo._ovv[grp][scan] = (-_gwo._nsv[grp][scan] > _old.NStdDev()) ? true : false; if (_old.ConsiderPosOL()) _gwo._ovv[grp][scan] = (_gwo._nsv[grp][scan] > _old.NStdDev()) ? true : _gwo._ovv[grp][scan]; if (_old.ConsiderSqrOL()) _gwo._ovv[grp][scan] = (_gwo._nsq[grp][scan] > _old.NStdDev()) ? true : _gwo._ovv[grp][scan]; } } } } // Then slice-wise map if (_olt==OLType::GroupWise || _olt==OLType::Both) { // If based (wholy or partially) on mb-groups // First pass to identify candidates for (unsigned int grp=0; grp sl_in_grp = _mbg.SlicesInGroup(grp); for (unsigned int scan=0; scan sl_in_grp = _mbg.SlicesInGroup(grp); for (unsigned int sh=0; sh<_shi.size(); sh++) { for (const unsigned int& scan : _shi[sh]) { bool group_kosher = true; for (unsigned int i=0; i= _old.MinVoxels()) { // Only check slices with more than minimum valid voxels double sf = (_etc == 1) ? 1.0 / std::sqrt(double(_sws._nvox[sl_in_grp[i]][scan])) : 1.0; double tmp_ns = (_sws._mdiff[sl_in_grp[i]][scan] - _sss[sh]._diff_mean) / (sf * _sss[sh]._diff_std); group_kosher = (-tmp_ns > (_old.NStdDev()-1.0)) ? true : false; if (_old.ConsiderPosOL()) group_kosher = (tmp_ns > (_old.NStdDev()-1.0) && _gwo._nsv[grp][scan] > 0.0) ? true : group_kosher; if (_old.ConsiderSqrOL()) { tmp_ns = (_sws._msqrd[sl_in_grp[i]][scan] - _sss[sh]._sqr_mean) / (sf * _sss[sh]._sqr_std); group_kosher = (tmp_ns > (_old.NStdDev()-1.0)) ? true : group_kosher; } } if (!group_kosher) break; } if (!group_kosher) { // Reset group for (unsigned int i=0; i= _old.MinVoxels()) { // Only consider slices with more than minimum valid voxels double sf = (_etc == 1) ? 1.0 / std::sqrt(double(_sws._nvox[sl][scan])) : 1.0; double tmp_ns = (_sws._mdiff[sl][scan] - _sss[sh]._diff_mean) / (sf * _sss[sh]._diff_std); _swo._nsv[sl][scan] = (std::abs(tmp_ns) > std::abs(_swo._nsv[sl][scan])) ? tmp_ns : _swo._nsv[sl][scan]; tmp_ns = (_sws._msqrd[sl][scan] - _sss[sh]._sqr_mean) / (sf * _sss[sh]._sqr_std); _swo._nsq[sl][scan] = (std::abs(tmp_ns) > std::abs(_swo._nsq[sl][scan])) ? tmp_ns : _swo._nsq[sl][scan]; _swo._ovv[sl][scan] = (-_swo._nsv[sl][scan] > _old.NStdDev()) ? true : false; if (_old.ConsiderPosOL()) _swo._ovv[sl][scan] = (_swo._nsv[sl][scan] > _old.NStdDev()) ? true : _swo._ovv[sl][scan]; if (_old.ConsiderSqrOL()) _swo._ovv[sl][scan] = (_swo._nsq[sl][scan] > _old.NStdDev()) ? true : _swo._ovv[sl][scan]; } } } } } } else { // If slice-based for (unsigned int sh=0; sh<_shi.size(); sh++) { for (const unsigned int& scan : _shi[sh]) { for (unsigned int sl=0; sl= _old.MinVoxels()) { // Only consider slices with more than minimum valid voxels double sf = (_etc == 1) ? 1.0 / std::sqrt(double(_sws._nvox[sl][scan])) : 1.0; _swo._nsv[sl][scan] = (_sws._mdiff[sl][scan] - _sss[sh]._diff_mean) / (sf * _sss[sh]._diff_std); _swo._nsq[sl][scan] = (_sws._msqrd[sl][scan] - _sss[sh]._sqr_mean) / (sf * _sss[sh]._sqr_std); _swo._ovv[sl][scan] = (-_swo._nsv[sl][scan] > _old.NStdDev()) ? true : false; if (_old.ConsiderPosOL()) _swo._ovv[sl][scan] = (_swo._nsv[sl][scan] > _old.NStdDev()) ? true : _swo._ovv[sl][scan]; if (_old.ConsiderSqrOL()) _swo._ovv[sl][scan] = (_swo._nsq[sl][scan] > _old.NStdDev()) ? true : _swo._ovv[sl][scan]; } } } } } } EddyCatch std::vector ReplacementManager::OutliersInScan(unsigned int scan) const EddyTry { throw_if_oor(scan); std::vector ol; for (unsigned int sl=0; sl& i2i, const std::string& fname, bool write_old_style_file) const EddyTry { if (write_old_style_file) { std::ofstream fout; fout.open(fname.c_str(),ios::out|ios::trunc); if (fout.fail()) throw EddyException("ReplacementManager::WriteReport:Failed to open outlier report file " + fname); for (unsigned int sl=0; sl ol_object_vector; for (unsigned int sl=0; sl ReplacementManager::WriteMatrixReport(const std::vector& i2i, unsigned int nscan, const std::string& om_fname, const std::string& nstdev_fname, const std::string& n_sqr_stdev_fname, bool write_old_style_file) const EddyTry { if (write_old_style_file) { std::ofstream fout; try { if (!om_fname.empty()) { fout.open(om_fname.c_str(),ios::out|ios::trunc); fout << "One row per scan, one column per slice. Outlier: 1, Non-outlier: 0" << endl; // Repack into matrix with b0's in place NEWMAT::Matrix rpovv(nscan,NSlice()); rpovv = 0.0; for (unsigned int scan=0; scan 0) fout << "1 "; else fout << "0 "; } fout << endl; } fout.close(); } } catch (...) { throw EddyException("ReplacementManager::WriteMatrixReport: Error when writing file " + om_fname); } // Now start on nstdev of mean file if (!nstdev_fname.empty()) { try { fout.open(nstdev_fname.c_str(),ios::out|ios::trunc); fout << "One row per scan, one column per slice. b0s set to zero" << endl; // Repack into matrix with b0's in place NEWMAT::Matrix rpnsv(nscan,NSlice()); rpnsv = 0.0; for (unsigned int scan=0; scan > ol_map(nscan,std::vector(NSlice(),false)); std::vector > stdev_map(nscan,std::vector(NSlice(),0.0)); std::vector > sqr_stdev_map(nscan,std::vector(NSlice(),0.0)); for (unsigned int scan=0; scan all_maps = {ol_map_json, stdev_map_json, sqr_stdev_map_json}; return(all_maps); } EddyCatch void ReplacementManager::WriteDebugInfo(const std::string& fname, const std::vector& i2i, unsigned int nscan) const EddyTry { nlohmann::json top; std::vector > outlier_map(nscan,std::vector(NSlice(),false)); std::vector > nstdev_map(nscan,std::vector(NSlice(),0.0)); std::vector > sqr_nstdev_map(nscan,std::vector(NSlice(),0.0)); top["NumberOfScans"] = nscan; top["NumberOfSlices"] = NSlice(); top["NumberOfMBGroups"] = NGroup(); top["OutlierDefinition"]["NoOfStdDev"] = _old.NStdDev(); top["OutlierDefinition"]["MinNoOfVoxels"] = _old.MinVoxels(); top["OutlierDefinition"]["ConsiderPositive"] = (_old.ConsiderPosOL()) ? "Yes" : "No"; top["OutlierDefinition"]["ConsiderSquaredDiff"] = (_old.ConsiderSqrOL()) ? "Yes" : "No"; top["OutlierDefinition"]["ConstantErrorType"] = _etc; top["OutlierDefinition"]["OutlierType"] = (_olt==OLType::SliceWise) ? "Slice-wise" : ((_olt==OLType::GroupWise) ? "Group-wise" : "Both"); for (unsigned int sh=0; sh<_shi.size(); sh++) { if (_bvs.size()) top["ShellInfo"][sh]["BValue"] = _bvs[sh]; else top["ShellInfo"][sh]["BValue"] = "Unknown b-value"; top["ShellInfo"][sh]["SliceWiseDiffMean"] = _sss[sh]._diff_mean; top["ShellInfo"][sh]["SliceWiseSqrMean"] = _sss[sh]._sqr_mean; top["ShellInfo"][sh]["GroupWiseDiffMean"] = _gss[sh]._diff_mean; top["ShellInfo"][sh]["GroupWiseSqrMean"] = _gss[sh]._sqr_mean; double sf = (_etc == 1) ? 1.0 / std::sqrt(_sws._nvox[static_cast(NSlice()/2)][static_cast(NScan()/2)]) : 1.0; // Arbitrarily mid-slice of mid-scan top["ShellInfo"][sh]["SliceWiseDiffStdDev"] = sf * _sss[sh]._diff_std; top["ShellInfo"][sh]["SliceWiseSqrStdDev"] = sf * _sss[sh]._sqr_std; sf = (_etc == 1) ? 1.0 / std::sqrt(_gws._nvox[static_cast(NGroup()/2)][static_cast(NScan()/2)]) : 1.0; // Arbitrarily mid-group of mid-scan top["ShellInfo"][sh]["GroupWiseDiffStdDev"] = sf * _gss[sh]._diff_std; top["ShellInfo"][sh]["GroupWiseSqrStdDev"] = sf * _gss[sh]._sqr_std; std::vector global_index(_shi[sh].size()); for (unsigned int i=0; i<_shi[sh].size(); i++) global_index[i] = i2i[_shi[sh][i]]; top["ShellInfo"][sh]["Index"] = global_index; } top["SliceWiseInfo"]["RawStats"]["NoOfVoxels"] = unpack(_sws._nvox,nscan,i2i); top["SliceWiseInfo"]["RawStats"]["DiffMean"] = unpack(_sws._mdiff,nscan,i2i); top["SliceWiseInfo"]["RawStats"]["SqrMean"] = unpack(_sws._msqrd,nscan,i2i); top["SliceWiseInfo"]["Transformed"]["OutlierMap"] = unpack(_swo._ovv,nscan,i2i); top["SliceWiseInfo"]["Transformed"]["NDiffStDev"] = unpack(_swo._nsv,nscan,i2i); top["SliceWiseInfo"]["Transformed"]["NSqrStDev"] = unpack(_swo._nsq,nscan,i2i); top["GroupWiseInfo"]["RawStats"]["NoOfVoxels"] = unpack(_gws._nvox,nscan,i2i); top["GroupWiseInfo"]["RawStats"]["DiffMean"] = unpack(_gws._mdiff,nscan,i2i); top["GroupWiseInfo"]["RawStats"]["SqrMean"] = unpack(_gws._msqrd,nscan,i2i); top["GroupWiseInfo"]["Transformed"]["OutlierMap"] = unpack(_gwo._ovv,nscan,i2i); top["GroupWiseInfo"]["Transformed"]["NDiffStDev"] = unpack(_gwo._nsv,nscan,i2i); top["GroupWiseInfo"]["Transformed"]["NSqrStDev"] = unpack(_gwo._nsq,nscan,i2i); try { std::ofstream ofile(fname); ofile << std::setw(4) << top << std::endl; ofile.close(); } catch (const std::exception& e) { throw EddyException("ReplacementManager::WriteDebugInfo: Exception thrown with message " + std::string(e.what())); } } EddyCatch template std::vector > ReplacementManager::unpack(const std::vector >& mat, unsigned int nscan, const std::vector& i2i) const EddyTry { unsigned int nsl = mat.size(); // Number of slices/MB-groups std::vector > rval(nscan,std::vector(nsl,static_cast(0))); for (unsigned int scan=0; scan ReplacementManager::mean_and_std(// Input const EDDY::ReplacementManager::StatsInfo& stats, // Slice/group-wise stats const std::vector >& shi, // Shell indicies unsigned int minvox,// Smallest allowed # of voxels in a slice unsigned int etc, // ErrorTypeControl (type 1 or 2) const std::vector >& ovv) const EddyTry // Slices/groups currently labeled as outliers { std::vector rval(shi.size()); // First pass to get means for (unsigned int sh=0; sh= minvox) { if (etc == 1) { rval[sh]._diff_mean += stats._nvox[sl_gr][s] * stats._mdiff[sl_gr][s]; rval[sh]._sqr_mean += stats._nvox[sl_gr][s] * stats._msqrd[sl_gr][s]; ntot += stats._nvox[sl_gr][s]; } else { rval[sh]._diff_mean += stats._mdiff[sl_gr][s]; rval[sh]._sqr_mean += stats._msqrd[sl_gr][s]; ntot += 1; } } } } rval[sh]._diff_mean /= double(ntot); rval[sh]._sqr_mean /= double(ntot); } // Second pass to get standard deviations // If etc==1, i.e. if we want to keep the false-positive // rate constant we, "guesstimate" the underlying // voxel-wise standard deviation. for (unsigned int sh=0; sh= minvox) { if (etc == 1) { rval[sh]._diff_std += stats._nvox[sl_gr][s] * this->sqr(stats._mdiff[sl_gr][s] - rval[sh]._diff_mean); rval[sh]._sqr_std += stats._nvox[sl_gr][s] * this->sqr(stats._msqrd[sl_gr][s] - rval[sh]._sqr_mean); } else { rval[sh]._diff_std += this->sqr(stats._mdiff[sl_gr][s] - rval[sh]._diff_mean); rval[sh]._sqr_std += this->sqr(stats._msqrd[sl_gr][s] - rval[sh]._sqr_mean); } ntot += 1; } } } rval[sh]._diff_std /= double(ntot - 1); rval[sh]._diff_std = std::sqrt(rval[sh]._diff_std); rval[sh]._sqr_std /= double(ntot - 1); rval[sh]._sqr_std = std::sqrt(rval[sh]._sqr_std); } return(rval); } EddyCatch double MutualInfoHelper::MI(const NEWIMAGE::volume& ima1, const NEWIMAGE::volume& ima2, const NEWIMAGE::volume& mask) const EddyTry { // Make joint histograms memset(_mhist1,0,_nbins*sizeof(double)); memset(_mhist2,0,_nbins*sizeof(double)); memset(_jhist,0,_nbins*_nbins*sizeof(double)); float min1, max1, min2, max2; if (!_lset) { min1 = ima1.min(); max1 = ima1.max(); min2 = ima2.min(); max2 = ima2.max(); } else { min1 = _min1; max1 = _max1; min2 = _min2; max2 = _max2; } unsigned int nvox=0; for (int z=0; zval_to_indx(ima1(x,y,z),min1,max1,_nbins); unsigned int i2 = this->val_to_indx(ima2(x,y,z),min2,max2,_nbins); _mhist1[i1] += 1.0; _mhist2[i2] += 1.0; _jhist[i2*_nbins + i1] += 1.0; nvox++; } } } } // Calculate entropies double je=0.0; double me1=0.0; double me2=0.0; for (unsigned int i1=0; i1<_nbins; i1++) { me1 += this->plogp(_mhist1[i1]/static_cast(nvox)); me2 += this->plogp(_mhist2[i1]/static_cast(nvox)); for (unsigned int i2=0; i2<_nbins; i2++) { je += this->plogp(_jhist[i2*_nbins + i1]/static_cast(nvox)); } } // return mutual information return(me1+me2-je); } EddyCatch double MutualInfoHelper::SoftMI(const NEWIMAGE::volume& ima1, const NEWIMAGE::volume& ima2, const NEWIMAGE::volume& mask) const EddyTry { // Make joint histograms memset(_mhist1,0,_nbins*sizeof(double)); memset(_mhist2,0,_nbins*sizeof(double)); memset(_jhist,0,_nbins*_nbins*sizeof(double)); float min1, max1, min2, max2; if (!_lset) { min1 = ima1.min(); max1 = ima1.max(); min2 = ima2.min(); max2 = ima2.max(); } else { min1 = _min1; max1 = _max1; min2 = _min2; max2 = _max2; } double nvox=0.0; for (int z=0; zval_to_floor_indx(ima1(x,y,z),min1,max1,_nbins,&r1); unsigned int i2 = this->val_to_floor_indx(ima2(x,y,z),min2,max2,_nbins,&r2); _mhist1[i1] += mv*(1.0 - r1); if (r1) _mhist1[i1+1] += mv*r1; _mhist2[i2] += mv*(1.0 - r2); if (r2) _mhist2[i2+1] += mv*r2; _jhist[i2*_nbins + i1] += mv*(1.0 - r1)*(1.0 - r2); if (r1) _jhist[i2*_nbins + i1+1] += mv*r1*(1.0 - r2); if (r2) _jhist[(i2+1)*_nbins + i1] += mv*(1.0 - r1)*r2; if (r1 && r2) _jhist[(i2+1)*_nbins + i1+1] += mv*r1*r2; nvox += mv; } } } } // Calculate entropies double je=0.0; double me1=0.0; double me2=0.0; for (unsigned int i1=0; i1<_nbins; i1++) { me1 += this->plogp(_mhist1[i1]/nvox); me2 += this->plogp(_mhist2[i1]/nvox); for (unsigned int i2=0; i2<_nbins; i2++) { je += this->plogp(_jhist[i2*_nbins + i1]/nvox); } } // return mutual information return(me1+me2-je); } EddyCatch void Stacks2YVecsAndWgts::MakeVectors(const NEWIMAGE::volume4D& stacks, const NEWIMAGE::volume4D& masks, const NEWIMAGE::volume4D& zcoord, unsigned int i, unsigned int j) EddyTry { int tsz = stacks.tsize(); int zsz = stacks.zsize(); std::fill(_n.begin(),_n.end(),0); for (int t=0; t-1.0 && zcoord(i,j,k,t) zsz-1) { // If beyond last element _wgt[zsz-1][_n[zsz-1]] = static_cast(zsz) - zcoord(i,j,k,t); _sqrtwgt[zsz-1][_n[zsz-1]] = std::sqrt(_wgt[zsz-1][_n[zsz-1]]); _y[zsz-1][_n[zsz-1]] = stacks(i,j,k,t); _bvi[zsz-1][_n[zsz-1]].first = t; _bvi[zsz-1][_n[zsz-1]].second = k; _n[zsz-1]++; } else { // If somewhere in the middle int li = static_cast(std::floor(zcoord(i,j,k,t))); _wgt[li][_n[li]] = 1.0 + static_cast(li) - zcoord(i,j,k,t); _sqrtwgt[li][_n[li]] = std::sqrt(_wgt[li][_n[li]]); _y[li][_n[li]] = stacks(i,j,k,t); _bvi[li][_n[li]].first = t; _bvi[li][_n[li]].second = k; _n[li]++; int ui = static_cast(std::ceil(zcoord(i,j,k,t))); _wgt[ui][_n[ui]] = 1.0 - static_cast(ui) + zcoord(i,j,k,t); _sqrtwgt[ui][_n[ui]] = std::sqrt(_wgt[ui][_n[ui]]); _y[ui][_n[ui]] = stacks(i,j,k,t); _bvi[ui][_n[ui]].first = t; _bvi[ui][_n[ui]].second = k; _n[ui]++; } } } } } EddyCatch NEWMAT::RowVector Indicies2KMatrix::GetkVector(const NEWMAT::ColumnVector& bvec, unsigned int grp) const EddyTry { NEWMAT::RowVector rval(_K.Nrows()); for (int i=0; ith) { rval(i+1) = _thpar[0] * (1.0 - 1.5*th/a + 0.5*(th*th*th)/(a*a*a)); if (_grpb.size() > 1 && _grpi[i] != grp) { double bvdiff = _log_grpb[_grpi[i]] - _log_grpb[grp]; rval(i+1) *= std::exp(-(bvdiff*bvdiff) / (2*_thpar[2]*_thpar[2])); } if (_wgt.size() != 0) rval(i+1) *= _wgt[i]; } else rval(i+1) = 0.0; } return(rval); } EddyCatch void Indicies2KMatrix::common_construction(const std::vector >& bvecs, const std::vector& grpi, const std::vector& grpb, const std::vector >& indx, unsigned int nval, const std::vector& hpar, const std::vector *wgt) EddyTry { // Set and transform hyper-parameters and group b-values _grpb = grpb; _log_grpb = _grpb; for (unsigned int i=0; i<_log_grpb.size(); i++) _log_grpb[i] = std::log(_grpb[i]); _hpar = hpar; _thpar = _hpar; for (unsigned int i=0; i<_thpar.size(); i++) _thpar[i] = std::exp(_hpar[i]); if (wgt != nullptr) _wgt = *wgt; // First extract relevant vectors of bvecs and bval-groups _bvecs.resize(nval); _grpi.resize(nval); for (unsigned int i=0; ith) _K(i+1,j+1) = sm * (1.0 - 1.5*th/a + 0.5*(th*th*th)/(a*a*a)); else _K(i+1,j+1) = 0.0; if (i!=j) _K(j+1,i+1) = _K(i+1,j+1); } } // Second pass for b-value covariance if (_grpb.size() > 1) { double l = _thpar[2]; for (int j=0; j<_K.Ncols(); j++) { for (int i=j+1; i<_K.Nrows(); i++) { if (_K(i+1,j+1) != 0.0) { double bvdiff = _log_grpb[_grpi[i]] - _log_grpb[_grpi[j]]; if (bvdiff) { _K(i+1,j+1) *= std::exp(-(bvdiff*bvdiff) / (2*l*l)); _K(j+1,i+1) = _K(i+1,j+1); } } } } } // (Optional) third pass for weights if (_wgt.size() != 0) { for (int j=0; j<_K.Ncols(); j++) { for (int i=j; i<_K.Nrows(); i++) { _K(i+1,j+1) *= _wgt[i]*_wgt[j]; if (i!=j) _K(j+1,i+1) = _K(i+1,j+1); } } } // Fourth pass for error variances const double *ev = nullptr; ev = (_grpb.size() > 1) ? &(_thpar[3]) : &(_thpar[2]); for (int i=0; i<_K.Ncols(); i++) { _K(i+1,i+1) += ev[_grpi[i]]; } } EddyCatch