OpenCV - 用于边缘图(不是轮廓)的approxPolyDP

时间:2014-03-02 19:46:31

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

我已成功在等高线(cv :: findContours)上应用方法cv :: approxPolyDP,以便用更简单的多边形表示轮廓并隐式做一些去噪。

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

此方法是否适用于轮廓以外的一般边缘图?

有人能指出我的例子吗?

附上一些图片:

轮廓的成功示例: enter image description here

边缘地图的问题案例:

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

enter image description here

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

enter image description here

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

enter image description here

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

1 个答案:

答案 0 :(得分:34)

approxPolyDP的问题归因于approxPolyDP输入的格式。

说明

approxPolyDP期望其输入为Point s的向量。这些点定义了由approxPolyDP处理的多边形曲线。曲线可以是打开或关闭的,可以通过标志来控制。

列表中点的排序很重要。就像用手追踪多边形一样,矢量中的每个后续点必须是多边形的下一个顶点,顺时针或逆时针。

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

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


Canny生成的“栅格顺序”的插图。

Raster order


approxPolyDP

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

Contour order


需要什么

您需要的是“边缘像素链”列表。每条链必须包含彼此相邻的边缘像素,就像有人用铅笔描绘出物体的轮廓一样,没有铅笔的尖端离开纸张。

这不是边缘检测方法返回的内容,例如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;
}