Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

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 = 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 python_reference_results

But when I try to apply my c++ code on the same image I got the folowing result: image description

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.

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 = 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 python_reference_results

But when I try to apply my c++ code on the same image I got the folowing result: image description

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.

Here is the evaluation code I use in both case:

In python:

if __name__ == '__main__':

    I = cv2.imread('/home/smile/Datasets/COCO/images/train2014/COCO_train2014_000000000009.jpg')

    Idcv = pfs(I.copy())



    plt.figure()
    plt.subplot(121)
    plt.imshow(cv2.cvtColor(I,cv2.COLOR_BGR2RGB))
    plt.subplot(122)
    plt.imshow(Idcv,cmap='gray')
    plt.show()

In C++

int main()
{

    cv::Mat img = 
    cv::imread("/home/smile/Datasets/COCO/images/train2014/COCO_train2014_000000000009.jpg");

    cv::imshow("original", img);
    cv::waitKey(-1);


    std::cout << "Hello World!" << std::endl;
    return 0;
}