difference in translation from python to C++
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 ...
idx where?
here?
I am not sure to understand your question. Idx is a list in python a cv::Mat1i in C++ it store the index of the boolean vector (hd after have been threshold) that represents a transition either positive (from true to false) or negative (from false to true).
You said
Do you check idx here :
During the test the main difference is after lines you refer in one case there is 32 elements in idx in python and only one in C++.
Set thresh=cv::mean(hd)(0) instead of thresh = (cv::mean(hd)(0) + utils::iqr(hd)(0) ); and python and c++ results are equal
Are you sure that c++ utils version is same than scipy version ? note in scipy doc :
How do you read data ? Are you sure that c++ and python data are equals?
I'll update later the code I use in both case. There is no version of C++ utils the utils::iqr it is my IQR code that I wrote in a namespace utils. Yes there is possible slight variation between my implementation and the scipy one but it is very slight from the experiment I made. By that when I wrote it there was no variations among the few cases I evaluated it.
Are you agree that if you set thresh=cv::mean(hd)(0) instead of thresh = (cv::mean(hd)(0) + utils::iqr(hd)(0) ); and python and c++ results are equal ?
I am not yet at my office, I'll check as soon as I'll be there. It would not very surprise me however I remember in one case the threshold was at 313 for one of the example and 315 for the other. I would be surprise if such a few difference had such huge impact on the thresholding results. It is mandatory for what I want to do that the IQR (and not standard deviation, nor the variance, nor the median) be there.
Hello I restart the evaluation without using the IQR and I still do not have the same output... But I have identically the same threshold what add something to my issue.
I also edited the post in with the test codes.