OpenCV,如何使用点数组来平滑和采样轮廓? [英] OpenCV, how to use arrays of points for smoothing and sampling contours?

查看:3022
本文介绍了OpenCV,如何使用点数组来平滑和采样轮廓?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个问题,让我围绕平滑和抽样轮廓在OpenCV(C ++ API)。
假设我有从 cv :: findContours 中检索的点序列(例如应用于此图像:



>



最终,我想要


  1. 使用不同的内核来平滑点序列。



  2. 平滑后,我希望有一个结果:



    >



    我也考虑在 cv :: Mat 中绘制我的轮廓,过滤Mat(使用模糊或形态运算)和重新找到轮廓,但是慢和次优。所以,理想情况下,我可以使用排序的点序列。



    我读了几个帖子,天真地认为,我可以简单地转换 std :: vector cv :: Point )复制到 cv :: Mat 然后OpenCV的功能,像模糊/调整大小会为我做的工作,但他们没有。



    这是我尝试:

      int main(int argc,char ** argv){

    cv :: Mat conv,ori;
    ori = cv :: imread(argv [1]);
    ori.copyTo(conv);
    cv :: cvtColor(ori,ori,CV_BGR2GRAY);

    std :: vector< std :: vector< cv :: Point> >轮廓;
    std :: vector< cv :: Vec4i>层次;

    cv :: findContours(ori,contour,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_NONE);

    for(int k = 0; k <100; k + = 2){
    cv :: Mat smoothCont;

    smoothCont = cv :: Mat(contoururs [0]);
    std :: cout<< smoothCont.rows<<\t<< smoothCont.cols<< std :: endl;
    / *尝试平滑:不修改数组* /
    // cv :: GaussianBlur(smoothCont,smoothCont,cv :: Size(k + 1,1),k);
    / *尝试抽样:调整失败(func!= 0)in resize* /
    // cv :: resize(smoothCont,smoothCont,cv :: Size(0,0),1, 1);
    std :: vector< std :: vector< cv :: Point> > v(1);
    smoothCont.copyTo(v [0]);
    cv :: drawContours(conv,v,0,cv :: Scalar(255,0,0),2,CV_AA);
    std :: cout<< k<< std :: endl;
    cv :: imshow(conv,conv);
    cv :: waitKey();
    }
    return 1;
    }

    任何人都可以解释怎么做?



    此外,由于我可能使用更小的轮廓,我想知道这种方法如何处理边界效果(例如,当平滑时,由于轮廓是圆形的,因此序列的最后一个元素必须是用于计算第一个元素的新值...)



    非常感谢您的建议,



    修改:



    我也尝试过 cv :: approxPolyDP()正如你所看到的,它倾向于保存极值点(我要删除):



    Epsilon = 0





    Epsilon = 6



    >



    Epsilon = 12





    Epsilon = 24





    编辑2:
    正如Ben所建议的,看起来不支持 cv :: GaussianBlur(),但是 cv :: blur()是。它看起来非常接近我的期望。以下是我使用它的结果:



    k = 13





    k = 53





    k = 103



    >



    为了避开边框效果,我做了:

      cv :: copyMakeBorder smoothCont,smoothCont,(k-1)/ 2,(k-1)/ 2,0,0,cv :: BORDER_WRAP); 
    cv :: blur(smoothCont,result,cv :: Size(1,k),cv :: Point(-1,-1));
    result.rowRange(cv :: Range((k-1)/2,1+result.rows-(k-1)/ 2))。copyTo(v [0]);

    我还在寻找解决方案来插值/抽样我的轮廓。

    解决方案

    您的高斯模糊不工作,因为您在列方向模糊,但只有一列。使用 GaussianBlur()导致OpenCV中的功能未实现错误,当尝试将向量复制到 cv :: Mat (这可能是为什么你有这个奇怪的 resize()在你的代码),但一切工作正常使用 cv :: blur ,无需 resize()。尝试大小(0,41)例如。使用 cv :: BORDER_WRAP 似乎也不工作,但这里是另一个人的线程,发现了一个解决方法。



    更多的事情:你说你的轮廓可能会小得多。这种方式平滑你的轮廓会收缩它。极端情况是 k = size_of_contour ,这产生一个点。所以不要选择你的k太大。


    I have a problem to get my head around smoothing and sampling contours in OpenCV (C++ API). Lets say I have got sequence of points retrieved from cv::findContours (for instance applied on this this image:

    Ultimately, I want

    1. To smooth a sequence of points using different kernels.
    2. To resize the sequence using different types of interpolations.

    After smoothing, I hope to have a result like :

    I also considered drawing my contour in a cv::Mat, filtering the Mat (using blur or morphological operations) and re-finding the contours, but is slow and suboptimal. So, ideally, I could do the job using exclusively the point sequence.

    I read a few posts on it and naively thought that I could simply convert a std::vector(of cv::Point) to a cv::Mat and then OpenCV functions like blur/resize would do the job for me... but they did not.

    Here is what I tried:

    int main( int argc, char** argv ){
    
        cv::Mat conv,ori;
        ori=cv::imread(argv[1]);
        ori.copyTo(conv);
        cv::cvtColor(ori,ori,CV_BGR2GRAY);
    
        std::vector<std::vector<cv::Point> > contours;
        std::vector<cv::Vec4i > hierarchy;
    
        cv::findContours(ori, contours,hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
    
        for(int k=0;k<100;k += 2){
            cv::Mat smoothCont;
    
            smoothCont = cv::Mat(contours[0]);
            std::cout<<smoothCont.rows<<"\t"<<smoothCont.cols<<std::endl;
            /* Try smoothing: no modification of the array*/
    //        cv::GaussianBlur(smoothCont, smoothCont, cv::Size(k+1,1),k);
            /* Try sampling: "Assertion failed (func != 0) in resize"*/
    //        cv::resize(smoothCont,smoothCont,cv::Size(0,0),1,1);
            std::vector<std::vector<cv::Point> > v(1);
            smoothCont.copyTo(v[0]);
            cv::drawContours(conv,v,0,cv::Scalar(255,0,0),2,CV_AA);
            std::cout<<k<<std::endl;
            cv::imshow("conv", conv);
            cv::waitKey();
        }
        return 1;
    }
    

    Could anyone explain how to do this ?

    In addition, since I am likely to work with much smaller contours, I was wondering how this approach would deal with border effect (e.g. when smoothing, since contours are circular, the last elements of a sequence must be used to calculate the new value of the first elements...)

    Thank you very much for your advices,

    Edit:

    I also tried cv::approxPolyDP() but, as you can see, it tends to preserve extremal points (which I want to remove):

    Epsilon=0

    Epsilon=6

    Epsilon=12

    Epsilon=24

    Edit 2: As suggested by Ben, it seems that cv::GaussianBlur() is not supported but cv::blur() is. It looks very much closer to my expectation. Here are my results using it:

    k=13

    k=53

    k=103

    To get around the border effect, I did:

        cv::copyMakeBorder(smoothCont,smoothCont, (k-1)/2,(k-1)/2 ,0, 0, cv::BORDER_WRAP);
        cv::blur(smoothCont, result, cv::Size(1,k),cv::Point(-1,-1));
        result.rowRange(cv::Range((k-1)/2,1+result.rows-(k-1)/2)).copyTo(v[0]);
    

    I am still looking for solutions to interpolate/sample my contour.

    解决方案

    Your Gaussian blurring doesn't work because you're blurring in column direction, but there is only one column. Using GaussianBlur() leads to a "feature not implemented" error in OpenCV when trying to copy the vector back to a cv::Mat (that's probably why you have this strange resize() in your code), but everything works fine using cv::blur(), no need to resize(). Try Size(0,41) for example. Using cv::BORDER_WRAP for the border issue doesn't seem to work either, but here is another thread of someone who found a workaround for that.

    Oh... one more thing: you said that your contours are likely to be much smaller. Smoothing your contour that way will shrink it. The extreme case is k = size_of_contour, which results in a single point. So don't choose your k too big.

    这篇关于OpenCV,如何使用点数组来平滑和采样轮廓?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆