【问题标题】:OpenCV - approxPolyDP for edge maps (not contours)OpenCV - 用于边缘图(不是轮廓)的 approxPolyDP
【发布时间】:2014-03-03 22:15:44
【问题描述】:

我已成功将方法 cv::approxPolyDP 应用于轮廓 (cv::findContours),以便用更简单的多边形表示轮廓并隐式进行一些去噪。

我想在从 RGBD 相机(通常非常嘈杂)获取的边缘图上做同样的事情,但到目前为止没有太大的成功,我在网上找不到相关的例子。我需要这个的原因是,通过边缘图,人们还可以使用手指之间的边缘、手指遮挡产生的边缘或手掌中产生的边缘。

此方法是否适用于除等高线以外的一般边缘图?

谁能给我举个例子?

附上一些图片:

等高线的成功例子:

边缘图的问题案例:

很可能我以错误的方式绘制东西,但仅绘制该方法返回的像素表明最终结果中可能没有表示大区域(根据 epsilon 参数,这并没有太大变化)。

我还附上了一个深度图像,类似于我在上面描述的实验管道中使用的那些。这个深度图不是深度相机获取的,而是通过OpenGL读取gpu的深度缓冲区综合生成的。

仅供参考,这也是直接从深度相机获取的深度图像的边缘图(使用原始图像,未应用平滑等)

(从深度相机看到的手,手掌朝上,手指向手掌“合拢”)

【问题讨论】:

  • 我怀疑是cv::findContours发现的边缘图有缝隙。您能发布一张来自 RGBD 相机的原始图像吗? (可能是深度图像)。如果需要去噪,则需要在轮廓/边缘查找之前应用。
  • 嗨 rwong,在这种情况下,我不使用 cv::findContours,我在边缘图像上应用 Canny 边缘检测。目前我没有预先对边缘图像进行去噪,因为我首先想看看获取的真实数据的质量,但你是对的,在边缘检测之前也应该去噪一点。但是我必须注意,作为第一步,我使用综合生成的数据(OpenGL 的深度缓冲区),因此在我发布的示例中不需要去噪。我将编辑问题以添加合成深度图像。
  • 对于 Canny 边缘检测,降低较低阈值(同时保持较高阈值相同)将导致更多边缘像素被标记,从而减少边缘链中出现间隙的机会。我之前没有使用过approxPolyDP,所以我无法对此发表评论。教训是,如果某个步骤存在缺陷,通常很难在后续步骤中修复。
  • 这是对的,但是图像显示canny边缘检测已经足够了,提供了有意义的边缘图(另外,降低阈值会产生更多的内边缘,对外边缘没有显着影响)。所以我想在这个阶段没有神器,要么是我以错误的方式使用了 approxPpolyDP,要么它无法处理非闭合轮廓图(我认为不是这种情况,也是基于方法的输入参数)。

标签: c++ opencv image-processing gesture-recognition


【解决方案1】:

approxPolyDP 的问题是由于 approxPolyDP 的输入格式。

说明

approxPolyDP 期望其输入是Points 的向量。这些点定义了将由approxPolyDP 处理的多边形曲线。曲线可以是开放的,也可以是封闭的,可以通过一个标志来控制。

列表中点的顺序很重要。就像手动绘制多边形一样,向量中的每个后续点都必须是多边形的下一个顶点,顺时针或逆时针。

如果点列表以光栅顺序存储(按 Y 排序,然后按 X 排序),则 point[k]point[k+1] 不一定属于同一条曲线。这就是问题的原因。

OpenCV - How to extract edges form result of Canny Function? 中的插图解释了此问题。引用Mikhail:“Canny 不会将像素连接成链或段。”


Canny 生成的“光栅顺序”示意图。


approxPolyDP 所期望的“轮廓顺序”图示


需要什么

您需要的是“边缘像素链”列表。每个链必须包含彼此相邻的边缘像素,就像有人用铅笔勾勒出物体的轮廓,而铅笔尖不会离开纸。

这不是边缘检测方法返回的内容,例如Canny。需要进一步处理以将边缘图转换为相邻(连续)边缘像素链。

建议的解决方案

(1)使用二进制threshold代替边缘检测作为findContours的输入

如果存在将手与背景分开的阈值,并且该值适用于整只手(不仅仅是手的一部分),这将适用。

(2) 扫描边缘图,通过检查每个边缘像素的邻居,构建相邻像素列表。

这类似于连通分量算法,不同之处在于不是寻找一个 blob(您只需要知道每个像素的成员资格),而是尝试找到像素链,以便您可以分辨出前一个和下一个边缘像素沿着链条。

(3) 使用替代边缘检测算法,例如边缘绘制。

详情请见http://ceng.anadolu.edu.tr/cv/EdgeDrawing/

不幸的是,OpenCV 没有提供开箱即用的功能,因此您可能必须在其他地方找到实现。


选项 #1 的示例代码。

#include <stdint.h>
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
    Mat matInput = imread("~/Data/mA9EE.png", false);

    // ---- Preprocessing of depth map. (Optional.) ----

    GaussianBlur(matInput, matInput, cv::Size(9, 9), 4.0);

    // ---- Here, we use cv::threshold instead of cv::Canny as explained above ----

    Mat matEdge;

    //Canny(matInput, matEdge, 0.1, 1.0);

    threshold(matInput, matEdge, 192.0, 255.0, THRESH_BINARY_INV);

    // ---- Use findContours to find chains of consecutive edge pixels ----

    vector<vector<Point> > contours;
    findContours(matEdge, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    // ---- Code below is only used for visualizing the result. ----

    Mat matContour(matEdge.size(), CV_8UC1);

    for (size_t k = 0; k < contours.size(); ++k)
    {
        const vector<Point>& contour = contours[k];
        for (size_t k2 = 0; k2 < contour.size(); ++k2)
        {
            const Point& p = contour[k2];
            matContour.at<uint8_t>(p) = 255;
        }
    }

    imwrite("~/Data/output.png", matContour);
    cout << "Done!" << endl;
    return 0;
}

【讨论】:

  • 嗨 rwong,哇,这是一个很好的答案!我认为我对输出做错了,而输入错误正是由于您描述的原因。感谢您列出 3 个可能的选项作为解决方案!关于第一个解决方案,我的第一篇文章中的“轮廓示例”图像正是关于这种情况,使用一个简单的阈值来分割整只手并提供 findcontours 方法。这给出了轮廓作为输出,并且(描绘的)结果非常好。在边缘的情况下,你不能使用方法阈值,所以在实践中你可以做什么......
  • 快速(hacky!)测试是使用精明的边缘检测器,反转它,然后使用阈值方法并将结果提供给 findcontours 方法。但是,每个边缘都会出现双轮廓的问题,根据您的应用程序,这可能会也可能不会。最后,我决定不使用任何平滑/过滤或轮廓近似,因为它对当前项目没有帮助,但答案很好而且非常具有分析性。为了将来参考,有类似问题的人应该考虑第二个或第三个选项作为最佳解决方案。
  • 土耳其大学网站是一个 PITA - CompVis 研究团队的子网站(URL 的 /cv/ 位)在几个小时内给出了 503。我也不想花 35 美元买一份 Elsevier 的论文……这里有一篇精确的论文 -> researchgate.net/publication/… 看起来很有希望:我有大约 200 万张图片我正在尝试实现一些特征检测,所以在这里和那里节省的几毫秒加起来是有意义的时间(手指交叉,如果我使用 GPU 则更多)
  • 嗨。我对上面的解释有点困惑。 Canny 和 threshold 都返回一个二维数组(图像),不是吗?那里的像素顺序在哪里?
  • @Marumba 问题在于approxPolyDP 预期的输入。
猜你喜欢
  • 2015-09-26
  • 2017-07-21
  • 1970-01-01
  • 2013-03-23
  • 2013-04-02
  • 2013-06-16
  • 2021-06-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多