计算机视觉 - 使用 OpenCV 过滤凸包和凸面缺陷

Posted

技术标签:

【中文标题】计算机视觉 - 使用 OpenCV 过滤凸包和凸面缺陷【英文标题】:Computer Vision - filtering convex hulls and convexity defects with OpenCV 【发布时间】:2013-08-11 04:08:21 【问题描述】:

我在处理数字信号时遇到问题。我正在尝试检测指尖,类似于此处介绍的解决方案:Hand and finger detection using JavaCV。

但是,我没有使用 JavaCV,而是使用 OpenCV for android,这略有不同。 我已经完成了教程中介绍的所有步骤,但过滤了凸包和凸面缺陷。这就是我的图像的样子:

这是另一种分辨率的图像:

如您所见,有很多黄点(凸包)和很多红点(凸包)。有时2个黄点之间没有红点,这很奇怪(凸包是如何计算的?)

我需要像之前提供的链接一样创建类似的过滤功能,但使用 OpenCV 的数据结构。

凸包是 MatOfInt 的类型 ... 凸缺陷是 MatOfInt4 的类型 ...

我还创建了一些额外的数据结构,因为愚蠢的 OpenCV 以不同的方法使用包含相同数据的不同类型的数据...

convexHullMatOfInt = new MatOfInt();
convexHullPointArrayList = new ArrayList<Point>();
convexHullMatOfPoint = new MatOfPoint();
convexHullMatOfPointArrayList = new ArrayList<MatOfPoint>();

这是我到目前为止所做的,但效果不佳。问题可能在于以错误的方式转换数据:

创建凸包和凸缺陷:

public void calculateConvexHulls()

    convexHullMatOfInt = new MatOfInt();
    convexHullPointArrayList = new ArrayList<Point>();
    convexHullMatOfPoint = new MatOfPoint();
    convexHullMatOfPointArrayList = new ArrayList<MatOfPoint>();

    try 
        //Calculate convex hulls
        if(aproximatedContours.size() > 0)
        
            Imgproc.convexHull( aproximatedContours.get(0), convexHullMatOfInt, false);

            for(int j=0; j < convexHullMatOfInt.toList().size(); j++)
                convexHullPointArrayList.add(aproximatedContours.get(0).toList().get(convexHullMatOfInt.toList().get(j)));
            convexHullMatOfPoint.fromList(convexHullPointArrayList);
            convexHullMatOfPointArrayList.add(convexHullMatOfPoint);    
        
     catch (Exception e) 
        // TODO Auto-generated catch block
        Log.e("Calculate convex hulls failed.", "Details below");
        e.printStackTrace();
    


public void calculateConvexityDefects()

    mConvexityDefectsMatOfInt4 = new MatOfInt4();

    try 
        Imgproc.convexityDefects(aproximatedContours.get(0), convexHullMatOfInt, mConvexityDefectsMatOfInt4);

        if(!mConvexityDefectsMatOfInt4.empty())
        
            mConvexityDefectsIntArrayList = new int[mConvexityDefectsMatOfInt4.toArray().length];
            mConvexityDefectsIntArrayList = mConvexityDefectsMatOfInt4.toArray();
        
     catch (Exception e) 
        Log.e("Calculate convex hulls failed.", "Details below");
        e.printStackTrace();
    

过滤:

public void filterCalculatedPoints()
    
        ArrayList<Point> tipPts = new ArrayList<Point>();
        ArrayList<Point> foldPts = new ArrayList<Point>();
        ArrayList<Integer> depths = new ArrayList<Integer>();

        fingerTips = new ArrayList<Point>();

        for (int i = 0; i < mConvexityDefectsIntArrayList.length/4; i++)
        
            tipPts.add(contours.get(0).toList().get(mConvexityDefectsIntArrayList[4*i]));
            tipPts.add(contours.get(0).toList().get(mConvexityDefectsIntArrayList[4*i+1]));
            foldPts.add(contours.get(0).toList().get(mConvexityDefectsIntArrayList[4*i+2]));
            depths.add(mConvexityDefectsIntArrayList[4*i+3]);
        

        int numPoints = foldPts.size();
        for (int i=0; i < numPoints; i++) 
            if ((depths.get(i).intValue()) < MIN_FINGER_DEPTH)
                continue;

            // look at fold points on either side of a tip
            int pdx = (i == 0) ? (numPoints-1) : (i - 1);
            int sdx = (i == numPoints-1) ? 0 : (i + 1);

            int angle = angleBetween(tipPts.get(i), foldPts.get(pdx), foldPts.get(sdx));
            if (angle >= MAX_FINGER_ANGLE)   // angle between finger and folds too wide
                continue; 

            // this point is probably a fingertip, so add to list
            fingerTips.add(tipPts.get(i));
        
    

结果(白点-过滤后的指尖):

你能帮我写一个合适的过滤函数吗?

2013 年 8 月 14 日更新

我使用标准的 openCV 函数进行轮廓逼近。我必须随着分辨率的变化和手到相机的距离来改变近似值,这很难做到。如果分辨率较小,则手指包含的像素较少,因此近似值应该是情人。距离也一样。保持高将导致完全失去手指。所以我认为近似不是解决问题的好方法,但是小值可能有助于加快计算:

Imgproc.approxPolyDP(frame, frame, 2 , true); 

如果我使用高值,那么结果就像下图一样,只有在距离和分辨率不变的情况下才会好。 另外,我很惊讶船体点和缺陷点的默认方法没有有用的参数来传递(最小角度、距离等)......

下图展示了我希望始终实现的效果,与分辨率或手到相机的距离无关。我也不想在合上手掌的时候看到任何黄点...

总结一下,我想知道:

如何过滤点 如何使分辨率和距离无关的近似值始终有效 如果有人知道或有一些关于 OpenCV 中使用的数据结构的资料(图形表示、解释),我很乐意阅读。 (Mat、MatOfInt、MatOfPoint、MatOfPoint2、MatOfPoint4 等)

【问题讨论】:

我真的需要帮助解决这个问题,你能帮帮我吗? ***.com/questions/61216402/… 【参考方案1】:

低分辨率的凸包可用于识别整个手的位置,它对手指没有用,但确实提供了感兴趣的区域和适当的比例。

然后应该将更高分辨率的分析应用于您的近似轮廓,很容易跳过最后两个不通过“长度和角度”标准的任何点,尽管您可能希望“平均”而不是“完全跳过”。

您的代码示例是计算凸性缺陷然后删除它们的单程..这是一个逻辑错误..您需要随时删除点.. (a) 一次完成所有事情会更快更简单-pass (b) 它避免了在第一次通过时删除点并在以后添加它们,因为任何删除都会改变以前的计算。

这项基本技术非常简单,因此适用于基本张开手掌。但它本质上并不理解手或手势,因此调整比例、角度和长度参数只会让你“走这么远”。

技术参考: 过滤器长度和角度“凸面缺陷” 西蒙安德森博客http://simena86.github.io/blog/2013/08/12/hand-tracking-and-recognition-with-opencv/

基于 Kinect SDK 的 C# 库,添加了手指方向检测 http://candescentnui.codeplex.com/ http://blog.candescent.ch/2011/11/improving-finger-detection.html

“自我生长和有组织的神经气体”(SGONG) Nikos Papamarkos 教授http://www.papamarkos.gr/uploaded-files/Hand%20gesture%20recognition%20using%20a%20neural%20network%20shape%20fitting%20technique.pdf

商业产品 David Holz 和 Michael Buckwald 的“Leap Motion”创始人http://www.engadget.com/2013/03/11/leap-motion-michael-buckwald-interview/

【讨论】:

第一个github页面挂了。这是存储库:github.com/simenandresen/handDetectionCV【参考方案2】:

我认为你错过了这一点:

通过使用轮廓的低多边形近似而不是原始轮廓来加速船体创建和缺陷分析。

【讨论】:

我做近似值(更新后的帖子),但即使那样结果也不可接受(有时我的指尖仍然不止一个点。近似值大小应该是分辨率和手到相机的距离依赖这很难实现。

以上是关于计算机视觉 - 使用 OpenCV 过滤凸包和凸面缺陷的主要内容,如果未能解决你的问题,请参考以下文章

Opencv vs Opengl用于图像过滤器

OpenCV - 如何消除凸轮扫描仪中的凸面缺陷?

《计算机视觉和图像处理简介 - 中英双语版》:使用 OpenCV对图像进行空间滤波

25opencv入门轮廓查找与绘制——凸包

计算机视觉案例:应用opencv+keras完成视频物体检测

使用 OpenCV 凸包和凸缺陷函数的手指跟踪/计数