C++ OpenCV-用于边贴图(非等高线)的approxPolyDP

C++ OpenCV-用于边贴图(非等高线)的approxPolyDP,c++,opencv,image-processing,gesture-recognition,C++,Opencv,Image Processing,Gesture Recognition,我已经成功地在等高线(cv::findContours)上应用了cv::approxPolyDP方法,以便用更简单的多边形表示等高线,并隐式地进行一些去噪 我想在从RGBD相机获取的边缘贴图上做同样的事情(通常非常嘈杂),但到目前为止没有太多成功,我无法在网上找到相关示例。我之所以需要这样做,是因为通过边缘贴图,还可以使用手指之间的边、手指遮挡创建的边或手掌中创建的边 此方法是否适用于除等高线以外的常规边缘贴图 有人能给我举个例子吗 附上一些图片: 等高线的成功示例: 边贴图有问题的情况: 很

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

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

此方法是否适用于除等高线以外的常规边缘贴图

有人能给我举个例子吗

附上一些图片:

等高线的成功示例:

边贴图有问题的情况:

很可能我用错误的方式画东西,但是只画方法返回的像素表明,最终结果中可能没有表示大面积(根据epsilon参数,这不会有太大变化)

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

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


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

您的
approxPolyDP
问题是由于
approxPolyDP
中输入的格式问题

解释
approxPolyDP
期望其输入是点的向量。这些点定义了将由
approxPolyDP
处理的多边形曲线。曲线可以是开放的或闭合的,这可以由标志控制

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

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

中的插图说明了此问题。引用自:“Canny不会将像素连接成链或段。”


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


approxPolyDP


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

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

建议的解决办法 (1)使用二进制
阈值
而不是边缘检测作为
查找对象的输入

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

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

这与“连接组件”算法类似,不同的是,您不需要查找blob(您只需要知道每个像素的成员资格),而是尝试查找像素链,这样您就可以知道链上的上一个和下一个边缘像素

(3)使用其他边缘检测算法,如边缘绘制。

详情请浏览

不幸的是,OpenCV并没有提供开箱即用的功能,因此您可能需要在其他地方找到一个实现


选项#1的示例代码。
#包括
#包括
#包括
#包括
使用名称空间std;
使用名称空间cv;
int main()
{
Mat matInput=imread(“~/Data/mA9EE.png”,false);
//----深度图的预处理(可选)----
GaussianBlur(matInput,matInput,cv::Size(9,9),4.0);
//----这里,我们使用cv::threshold而不是如上所述的cv::Canny----
马特奇;
//Canny(matInput,matEdge,0.1,1.0);
阈值(matInput、matEdge、192.0、255.0、THRESH_BINARY_INV);
//----使用FindContentours查找连续边缘像素链----
矢量等值线;
findContours(等高线、等高线、等高线、等高线、等高线链、等高线近似值);
//----下面的代码仅用于可视化结果----
Mat matContour(matEdge.size(),CV_8UC1);
对于(大小k=0;k我的怀疑是,
cv::findContours
发现的边缘图中有缺口。你能发布一张RGBD相机的原始图像吗?(可能是深度图像)。如果需要去噪,则需要在轮廓/边缘查找之前应用。您好,在这种情况下,我不使用cv::FindOntours,我在边缘图像上应用Canny边缘检测。目前,我没有事先去噪边缘图像,因为我第一次想看到采集的真实数据的质量,但您是对的,我们也应该去噪在边缘检测之前一点。但是我必须注意,作为第一步,我使用合成生成的数据(OpenGL的深度缓冲区),因此在我发布的示例中不需要去噪。
#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;
}