Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
qualicheck/qualicheck.cpp
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
288 lines (241 sloc)
8.46 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "qualicheck.h" | |
#include "ui_qualicheck.h" | |
#include <algorithm> | |
#include <isis/data/io_application.hpp> | |
#include <isis/adapter/qt5/simpleimageview.hpp> | |
#include <isis/data/io_factory.hpp> | |
#include <isis/math/histogram.hpp> | |
#include <gsl/gsl_linalg.h> | |
#include <numeric> | |
using namespace isis; | |
// std::array<size_t,256> mk_histogram(data::TypedChunk<uint8_t> image){ | |
// std::array<size_t,256> ret; | |
// ret.fill(0); | |
// for(uint8_t v:image) | |
// ret[v]++; | |
// return ret; | |
// } | |
// | |
// std::array<double,256> mk_normalized_histogram(data::TypedChunk<uint8_t> image){ | |
// std::array<double,256> ret; | |
// const std::array<size_t,256> hist=mk_histogram(image); | |
// const uint64_t sum=std::accumulate(hist.begin(),hist.end(),0); | |
// | |
// | |
// std::transform(hist.begin(),hist.end(),ret.begin(),[sum](size_t v){return double(v)/sum;}); | |
// | |
// return ret; | |
// } | |
uint8_t otsu_thres(data::TypedChunk<uint8_t> image){ | |
std::array<double,256> p=math::normalized_histogram(image); | |
//M: omega = cumsum(p); | |
std::array<double,256> omega; | |
std::partial_sum(p.begin(),p.end(),omega.begin()); | |
//M: mu = cumsum(p .* (1:num_bins)'); | |
std::array<double,256> range,prod,mu; | |
std::iota(range.begin(),range.end(),1); | |
prod = p * range; | |
std::partial_sum(prod.begin(),prod.end(),mu.begin()); | |
double mu_t = mu.back(); | |
//M:sigma_b_squared = (mu_t * omega - mu).^2 ./ (omega .* (1 - omega)); | |
std::array<double,256> one;one.fill(1); | |
const auto v1 = (omega*mu_t - mu) * (omega*mu_t - mu); | |
const auto v2 = omega * (one - omega); | |
std::array<double,256> sigma_b_squared = v1 / v2; | |
double max = *std::max_element(sigma_b_squared.begin(),sigma_b_squared.end()); | |
if(std::isfinite(max)){ | |
//M: idx = mean(find(sigma_b_squared == maxval)); | |
const size_t idx1 = std::distance(sigma_b_squared.begin(), std::find(sigma_b_squared.begin(),sigma_b_squared.end(),max)); | |
const size_t idx2 = std::distance(std::find(sigma_b_squared.rbegin(),sigma_b_squared.rend(),max), sigma_b_squared.rend()); | |
const size_t idx = idx1+ (idx2-idx1)/2; | |
return idx; | |
} else | |
return 0; | |
} | |
data::TypedChunk<bool> binarise(data::TypedChunk<uint8_t> ch){ | |
uint8_t thres=otsu_thres(ch); | |
data::scaling_pair scale(util::Value<int>(1),util::Value<int>(-thres)); | |
return ch.copyByID(data::ValueArray<bool>::staticID(),scale); | |
} | |
std::list<std::pair<size_t,size_t>> traceboundary(data::TypedChunk<bool> ch, std::pair<size_t,size_t> start){ | |
//http://www.imageprocessingplace.com/downloads_V3/root_downloads/tutorials/contour_tracing_Abeer_George_Ghuneim/square.html | |
std::list<std::pair<size_t,size_t>> ret; | |
struct tracer:std::pair<size_t,size_t>{ | |
tracer(const std::pair<size_t,size_t> &p):std::pair<size_t,size_t>(p),direction(north){} | |
enum D{north=0,east,south,west}direction; | |
void move(){ | |
switch(direction){ | |
case north:second++;break; | |
case east:first++;break; | |
case south:second--;break; | |
case west:first--;break; | |
} | |
} | |
void go_left(){ | |
direction = D((uint(direction)+1)%4); | |
move(); | |
} | |
void go_right(){ | |
direction = D((uint(direction)-1)%4); | |
move(); | |
} | |
}p(start); | |
do{ | |
if(ch.voxel<bool>(p.first,p.second)){//turn left if black | |
ret.push_back(p); | |
p.go_left(); | |
} else { // right otherwise | |
p.go_right(); | |
} | |
}while(p!=start); | |
return ret; | |
} | |
std::tuple<double,double,double> solve_gsl(std::list<std::pair<size_t,size_t>> boundary){ | |
gsl_matrix *m = gsl_matrix_alloc(boundary.size(), 3); | |
gsl_vector *b = gsl_vector_alloc(boundary.size()); | |
gsl_vector *x = gsl_vector_alloc (3); | |
size_t i=0; | |
for(auto p:boundary){ | |
p.first++; | |
p.second++; | |
//M:[x y ones(length(x),1)] | |
gsl_matrix_set(m,i,0,p.first); | |
gsl_matrix_set(m,i,1,p.second); | |
gsl_matrix_set(m,i,2,1); | |
//M:[-(x.^2+y.^2)] | |
gsl_vector_set(b,i,-double(p.first*p.first+p.second*p.second)); | |
i++; | |
} | |
gsl_vector *residual = gsl_vector_alloc (boundary.size()); | |
gsl_vector *tau = gsl_vector_alloc (3); | |
gsl_linalg_QR_decomp (m, tau); // | |
gsl_linalg_QR_lssolve(m, tau, b, x, residual); | |
printf ("x = \n"); | |
gsl_vector_fprintf (stdout, x, "%g"); | |
return std::make_tuple(gsl_vector_get(x,0),gsl_vector_get(x,1),gsl_vector_get(x,2)); | |
} | |
template<typename T1, typename T2> data::TypedChunk<T1>& operator +=(data::TypedChunk<T1> &ch1, data::TypedChunk<T2> ch2){ | |
std::transform( | |
ch1.begin(),ch1.end(), | |
ch2.begin(), | |
ch1.begin(), | |
[](const T1 &v,const T2 &vodd)->T1{return v+vodd;} | |
); | |
return ch1; | |
} | |
template<typename T1, typename T2> data::TypedChunk<T1>& operator +=(data::TypedChunk<T1> &ch1, T2 scalar){ | |
for(T1 &v:ch1) | |
v+=scalar; | |
return ch1; | |
} | |
template<typename T1, typename T2> data::TypedChunk<decltype(T1()+T2())> operator +(data::TypedChunk<T1> ch1, data::TypedChunk<T2> ch2){ | |
data::MemChunk<decltype(T1()+T2())> ret(ch1); | |
return ret+=ch2; | |
} | |
template<typename T1, typename T2> data::TypedChunk<decltype(T1()*T2())> operator *(data::TypedChunk<T1> ch1, data::TypedChunk<T2> ch2){ | |
const auto size = ch1.getSizeAsVector(); | |
data::MemChunk<decltype(T1()*T2())> ret(size[0],size[1],size[2],size[3]); | |
static_cast<util::PropertyMap&>(ret)=ch1; | |
std::transform( | |
ch1.begin(),ch1.end(), | |
ch2.begin(), | |
ret.begin(), | |
[](const T1 &v,const T2 &vodd)->decltype(T1()*T2()){return v*vodd;} | |
); | |
return ret; | |
} | |
template<typename T1, typename T2> data::TypedChunk<decltype(T1()-T2())> operator -(data::TypedChunk<T1> ch1, data::TypedChunk<T2> ch2){ | |
const auto size = ch1.getSizeAsVector(); | |
data::MemChunk<decltype(T1()-T2())> ret(size[0],size[1],size[2],size[3]); | |
static_cast<util::PropertyMap&>(ret)=ch1; | |
std::transform( | |
ch1.begin(),ch1.end(), | |
ch2.begin(), | |
ret.begin(), | |
[](const T1 &v1,const T2 &v2)->decltype(T1()-T2()){return v1-v2;} | |
); | |
return ret; | |
} | |
template<typename T1, typename T2> data::TypedChunk<decltype(T1()*T2())> operator *(data::TypedChunk<T1> ch1, T2 scalar){ | |
data::MemChunk<decltype(T1()*T2())> ret(ch1); | |
for(auto &v:ret) | |
v*=scalar; | |
return ret; | |
} | |
qualicheck::qualicheck(std::list<data::Image> images, QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) | |
{ | |
ui->setupUi(this); | |
data::Image src=images.front(); | |
ui->image_frame->layout()->addWidget(new isis::qt5::SimpleImageView(src,"source")); | |
src.spliceDownTo(data::sliceDim); | |
size_t slices=src.getNrOfSlices(); | |
const double R = src.getNrOfColumns()==128?30:15; | |
const std::array<size_t, 4> slize_size=src.getChunkAt(0).getSizeAsVector(); | |
for(size_t s=0;s<slices;s++){ | |
data::Chunk ch=binarise(src.getChunk(0,0,s,0)); | |
size_t col = 79, row = 0; | |
for(;ch.voxel<bool>(col,row,0)==false && row<slize_size[0];row++){} | |
int connectivity = 8; | |
size_t num_points = 180; | |
auto contour=traceboundary(ch,{col,row}); | |
std::cout << contour << std::endl; | |
double a,b,c; | |
std::tie(a, b, c)=solve_gsl(contour); | |
//M: calculate the location of the center and the radius | |
size_t xc = std::round(-a/2),yc = std::round(-b/2); | |
double radius = sqrt((xc*xc+yc*yc)-c); | |
//M: ROI Koordinaten | |
size_t X1 = xc - std::floor(R/2); | |
size_t Y1 = yc - std::floor(R/2); | |
size_t S0=0,St=0,Stt=0; | |
std::vector<double> roi; | |
data::MemChunk<int> Iodd(slize_size[0],slize_size[1],1,1,true),Ieven(slize_size[0],slize_size[1],1,1,true); | |
data::MemChunk<double> Syt(slize_size[0],slize_size[1]),Syy(slize_size[0],slize_size[1]); | |
std::vector<std::vector<double>> roir(src.getNrOfTimesteps()); | |
for(size_t j=0;j<src.getNrOfTimesteps();j++){ | |
data::TypedChunk<int> I=src.getChunk(0,0,s,j); | |
if(j%2){ // odd | |
Iodd+=I; | |
} else { | |
Ieven+=I; | |
} | |
Syt+= I*j; | |
Syy+= I*I; | |
Stt+= j*j; | |
S0++; | |
St+=j; | |
Stt+=j*j; | |
//M:sub = I(X1:X2,Y1:Y2); | |
data::MemChunk<int> sub(R,R); | |
I.copyTileTo(sub,{X1,Y1}); | |
//M:roi(S0) = sum(sum(sub))/npx; | |
roi.push_back(std::accumulate(sub.begin(),sub.end(),0.0)/(R*R)); | |
for(size_t r=0;r<R;r++){ | |
size_t x1 = xc - r/2; | |
size_t y1 = yc - r/2; | |
data::MemChunk<int> sub(r+1,r+1); | |
I.copyTileTo(sub,{x1,y1}); | |
roir[j].push_back(std::accumulate(sub.begin(),sub.end(),0.0)/(r*r)); | |
} | |
} | |
data::MemChunk<int> sub(R,R); | |
(Iodd-Ieven).copyTileTo(sub,{X1,Y1}); | |
data::IOFactory::write(Iodd,"/tmp/odd.nii"); | |
data::IOFactory::write(Ieven,"/tmp/even.nii"); | |
data::IOFactory::write(Iodd-Ieven,"/tmp/diff.nii"); | |
//M:varI = var(sub(:)); | |
double mean=std::accumulate(sub.begin(),sub.end(),0.0)/(R*R); | |
double varI=0; | |
for(auto v:sub) | |
varI+= (v-mean)*(v-mean)/(R*R); | |
auto Iave = (Iodd+Ieven)* (1./src.getNrOfTimesteps()); | |
} | |
// ui->image_frame->layout()->addWidget(new isis::qt5::SimpleImageView(src)); | |
// | |
// auto bin=binarise(src); | |
// ui->bin_frame->layout()->addWidget(new isis::qt5::SimpleImageView(bin)); | |
} | |
qualicheck::~qualicheck() | |
{ | |
delete ui; | |
} |