我有一个问题来解决 OpenCV (C++ API) 中的平滑和采样轮廓问题。
假设我有从 cv::findContours 检索到的点序列(例如应用于此图像:

c++ - OpenCV,如何使用点数组来平滑和采样轮廓?-LMLPHP

最终,我想要

  • 使用不同的内核平滑一系列点。
  • 使用不同类型的插值调整序列大小。

  • 平滑后,我希望得到如下结果:

    c++ - OpenCV,如何使用点数组来平滑和采样轮廓?-LMLPHP

    我还考虑在 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> > 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;
    }
    

    谁能解释一下如何做到这一点?

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

    非常感谢您的建议,

    编辑:

    我也试过 cv::approxPolyDP() 但是,正如你所看到的,它倾向于保留极值点(我想删除):

    厄普西隆=0

    c&#43;&#43; - OpenCV,如何使用点数组来平滑和采样轮廓?-LMLPHP

    厄普西隆=6

    c&#43;&#43; - OpenCV,如何使用点数组来平滑和采样轮廓?-LMLPHP

    厄普西隆=12

    c&#43;&#43; - OpenCV,如何使用点数组来平滑和采样轮廓?-LMLPHP

    厄普西隆=24

    c&#43;&#43; - OpenCV,如何使用点数组来平滑和采样轮廓?-LMLPHP

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

    k=13

    c&#43;&#43; - OpenCV,如何使用点数组来平滑和采样轮廓?-LMLPHP

    k=53

    c&#43;&#43; - OpenCV,如何使用点数组来平滑和采样轮廓?-LMLPHP

    k=103

    c&#43;&#43; - OpenCV,如何使用点数组来平滑和采样轮廓?-LMLPHP

    为了解决边界效应,我做了:
        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]);
    

    我仍在寻找对我的轮廓进行插值/采样的解决方案。

    最佳答案

    您的高斯模糊不起作用,因为您在列方向模糊,但只有一列。尝试将 vector 复制回 GaussianBlur() 时,使用 cv::Mat 会导致 OpenCV 中出现“未实现的功能”错误(这可能就是为什么您的代码中有这个奇怪的 resize()),但使用 423,1823,18231323132313231343132313133131313000 一切正常例如,试试 Size(0,41)。使用 cv::blur() 解决边界问题似乎也不起作用,但 here 是另一个找到解决方法的人的帖子。

    哦...还有一件事:你说你的轮廓可能要小得多。以这种方式平滑你的轮廓会缩小它。极端情况是 resize() ,这会导致一个点。所以不要选择你的k太大。

    关于c++ - OpenCV,如何使用点数组来平滑和采样轮廓?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11925777/

    10-16 19:01