Hello
I am facing hard time to undestand the difference between two algorithm that I wrote. I following code in python:
import cv2 import numpy as np from scipy.stats import iqr
def pfs(I):
if I.ndim == 3:
I, _ = cv2.decolor(cv2.cvtColor(I, cv2.COLOR_BGR2Lab))
h = cv2.calcHist([I],[0],None,[256],[0,256]).astype(np.int32).reshape((-1,))
print(h.dtype, I.dtype, I.shape, h.min(), h.max(), I.min(), I.max())
hd = np.abs(np.diff(h))
thresh = np.mean(hd) + iqr(hd)
print(thresh, np.mean(hd), iqr(hd),flush=True)
hd = hd > thresh
print(hd)
idx = list()
for i in range(254):
if hd[i] and not hd[i+1]:
idx.append(i)
elif not hd[i] and hd[i+1]:
idx.append(i+1)
ret = np.zeros(I.shape,np.uint8)
for i in idx:
ret = cv2.bitwise_or(ret, cv2.compare(I,i,cv2.CMP_EQ))
return ret
I translated that algorithm in c++ as:
cv::Mat pfd(const cv::Mat& _I)
{
cv::Mat I = _I;
if(I.depth())
cv::normalize(I, I, 0., 255., cv::NORM_MINMAX, CV_8U);
if(I.channels() == 3)
{
cv::Mat Lab, tmp;
std::vector<cv::Mat> cns;
cv::cvtColor(I, Lab, cv::COLOR_BGR2Lab);
cv::decolor(Lab, I, tmp);
}
cv::Mat hist;
cv::calcHist(std::vector<cv::Mat>(1,I),{0},cv::noArray(), hist, {256}, {0.f,256.f});
cv::Mat hd;
{
cv::Mat curr = hist.rowRange(0,hist.rows-1);
cv::Mat next = hist.rowRange(1,hist.rows);
cv::absdiff(next,curr, hd);
}
hd.convertTo(hd,CV_32S);
double thresh = (cv::mean(hd)(0) + utils::iqr(hd)(0) );
hd = hd>thresh;
cv::Mat1i idx;
int i=0;
for(auto it_current = hd.begin<uchar>(), it_next = hd.begin<uchar>() + 1; it_next != hd.end<uchar>(); it_current++, it_next++, i++)
{
uchar current = *it_current;
uchar next = *it_next;
if(current && !next)
idx.push_back(i);
if(!current && next)
idx.push_back(i+1);
}
std::cout<<idx<<std::endl;
cv::Mat1b ret = cv::Mat1b::zeros(I.size());
for(const int& i : idx)
{
cv::Mat1b mask = I == i;
ret |= mask;
}
return ret;
}
For calculate the Inter Quantile Range I wrote the following code:
namespace
{
template<class type,class wtype,bool check>
struct calculate_iqr_t
{
calculate_iqr_t(const cv::Mat& src,
const float& , const float& frank25, const float& crank25,
const float& , const float& frank75, const float& crank75, cv::Scalar& iqr)
{
for(int i=0;i<src.rows;i++)
{
wtype v25 = ( cv::saturate_cast<wtype>(src.at<type>(i,cv::saturate_cast<int>(frank25)-1))+cv::saturate_cast<wtype>(src.at<type>(i,cv::saturate_cast<int>(crank25)-1)) )/2.f;
wtype v75 = ( cv::saturate_cast<wtype>(src.at<type>(i,cv::saturate_cast<int>(frank75)-1))+cv::saturate_cast<wtype>(src.at<type>(i,cv::saturate_cast<int>(crank75)-1)) )/2.f;
iqr(i) = cv::saturate_cast<double>(v75-v25);
}
}
};
template<class type, class wtype>
struct calculate_iqr_t<type,wtype,false>
{
calculate_iqr_t(const cv::Mat& src,
const float& rank25, const float& frank25, const float& crank25,
const float& rank75, const float& frank75, const float& crank75, cv::Scalar& iqr)
{
for(int i=0;i<src.rows;i++)
{
wtype v25 = cv::saturate_cast<wtype>(src.at<type>(i,frank25-1)) + (rank25-frank25)*(cv::saturate_cast<wtype>(src.at<type>(i,crank25))-cv::saturate_cast<wtype>(src.at<type>(i,frank25)));
wtype v75 = cv::saturate_cast<wtype>(src.at<type>(i,frank75-1)) + (rank75-frank75)*(cv::saturate_cast<wtype>(src.at<type>(i,crank75))-cv::saturate_cast<wtype>(src.at<type>(i,frank75)));
iqr(i) = cv::saturate_cast<double>(v75-v25);
}
}
};
template<class type,class wtype,bool check>
void calculate_iqr(const cv::Mat& src,
const float& r25,const float& fr25, const float& cr25,
const float& r75,const float& fr75, const float& cr75, cv::Scalar& iqr)
{
calculate_iqr_t<type,wtype,check> obj(src,r25,fr25,cr25,r75,fr75,cr75,iqr);
}
} // anonymous
Scalar iqr(InputArray _src)
{
typedef void(*function_type)(const Mat&, const float&, const float&, const float&, const float&, const float&, const float&, Scalar& );
static const function_type funcs[7][2] = {
{calculate_iqr<uchar,float,false>,calculate_iqr<uchar,float,true>},
{calculate_iqr<schar,float,false>,calculate_iqr<schar,float,true>},
{calculate_iqr<ushort,float,false>,calculate_iqr<ushort,float,true>},
{calculate_iqr<short,float,false>,calculate_iqr<short,float,true>},
{calculate_iqr<int,float,false>,calculate_iqr<int,float,true>},
{calculate_iqr<float,double,false>,calculate_iqr<float,double,true>},
{calculate_iqr<double,double,false>,calculate_iqr<double,double,true>}
};
Mat src = getMat(_src);
function_type fun = nullptr;
Scalar ret;
if(src.isContinuous())
src = src.reshape(1,1);
else
{
Mat tmp(src.channels(), src.total(), src.depth());
size_t esz = src.elemSize1();
cv::parallel_for_(cv::Range(0, src.rows),[&src, &tmp, esz](const int& r)->void
{
for(int c=0, idx = r * src.cols; c<src.cols; c++, idx++)
for(int cn=0;cn<src.channels();cn++)
std::memcpy(tmp.ptr(cn, idx), src.ptr(r,c)+idx, esz);
});
src = tmp;
}
sort(src, src, SORT_ASCENDING | SORT_EVERY_ROW);
const int length = src.cols;
#ifdef FP_FAST_FMA
float rank25 = std::fmaf(0.25f, (cv::saturate_cast<float>(length)-1.f), 1.f);
float rank75 = std::fmaf(0.75f, (cv::saturate_cast<float>(length)-1.f), 1.f);
#else
float rank25 = 0.25f * (cv::saturate_cast<float>(length)-1.f) + 1.f;
float rank75 = 0.75f * (cv::saturate_cast<float>(length)-1.f) + 1.f;
#endif
float frank25 = cvFloor(rank25);
float frank75 = cvFloor(rank75);
float crank25 = cvCeil(rank25);
float crank75 = cvCeil(rank75);
fun = funcs[src.depth()][length<20];
CV_Assert(fun);
fun(src,rank25,frank25,crank25,rank75,frank75,crank75,ret);
return ret;
}
In Python I run the following code :
if __name__ == '__main__':
for i in range(6):
plt.close()
I = cv2.imread('/home/smile/Datasets/COCO/images/train2014/COCO_train2014_000000000009.jpg')
points = pfs(I.copy())
plt.figure()
plt.subplot(121)
plt.imshow(cv2.cvtColor(I,cv2.COLOR_BGR2RGB))
plt.subplot(122)
plt.imshow(points,cmap='gray')
plt.show()
And I got the following results
But when I try to apply my c++ code on the same image I got the folowing result:
My question may look naive but I do not understand what can cause a so big difference considering that there not such difference between the two code ?
I almost check every line in two codes, the main difference in the variable idx that in the c++ code does contains one single value while in python it does contains 32 values in python... for almost the same threshold.