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

46
我在处理数字信号时遇到问题。我正在尝试检测手指,类似于这里提供的解决方案:使用JavaCV进行手部和手指检测 。但是,我没有使用JavaCV而是使用了略有不同的OpenCV for android。我已经完成了教程中介绍的所有步骤,但无法过滤凸包和凸性缺陷。这是我的图像外观:
此处省略图片
正如您可以清楚地看到的那样,有太多的黄色点(凸包)和红色点(凸性缺陷)。有时在两个黄点之间没有红点,这相当奇怪(凸包是如何计算的?)我需要创建类似于链接提供的过滤函数,但要使用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));
        }
    }

结果(白点-经过筛选后的指尖):

enter image description here

你能帮我编写适当的筛选函数吗?

更新于2013年8月14日

我使用标准的openCV函数进行轮廓近似。我必须根据分辨率变化和手到摄像头的距离来改变近似值,这非常困难。如果分辨率较小,则手指由更少的像素组成,因此近似值应该更低。同样,距离也是如此。保持高度将导致完全失去手指。因此,我认为近似并不是解决问题的好方法,但小的值可能有助于加快计算速度:

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

如果我使用高值,则结果如下图所示,只有当距离和分辨率不变时才会很好。 此外,我相当惊讶默认的凸壳点和缺陷点方法没有有用的参数可传递(最小角度,距离等)... 下面的图像展示了我希望始终实现的效果,独立于分辨率或手到相机的距离。而且,当我握紧拳头时,我不想看到任何黄色点...
总之,我想知道:
- 如何过滤这些点 - 如何进行分辨率和距离无关的逼近,以便始终有效 - 如果有人知道或有一些关于OpenCV中使用的这些数据结构(Mat、MatOfInt、MatOfPoint、MatOfPoint2、MatOfPoint4等)的材料(图形表示、说明),我将很高兴阅读它。 enter image description here

我真的非常需要对这个问题的帮助,你能帮帮我吗?谢谢。 https://dev59.com/q1IH5IYBdhLWcg3wGZC5 - Carlos Diego
2个回答

1
低分辨率下的凸包可用于识别手的整体位置,但对于手指而言并不实用,但提供了感兴趣区域和适当的比例尺。接着应该对近似轮廓进行更高分辨率的分析,可以轻松跳过未通过“长度和角度”标准的任何点,但您可能希望“平均”而不是“完全跳过”。您的代码示例只计算了一次凸性缺陷,然后将其删除..这是一个逻辑错误..您需要在进行时删除点..(a)一次性完成所有操作更快且更简单(b)它避免了第一次删除点并稍后必须添加它们的情况,因为任何删除都会改变之前的计算。这种基本技术非常简单,因此适用于基本的张开手掌,但它并不内在地理解手或手势,因此调整比例尺、角度和长度参数只能带来有限的效果。

关于技术的参考: 滤波器长度和角度“凸缺陷” Simen Andresen博客 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) 尼科斯•帕帕马尔科斯教授 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 页面已经无法访问。这是仓库链接:https://github.com/simenandresen/handDetectionCV - xxx

0

我认为你错过了这一点:

利用轮廓的低多边形逼近而不是原始轮廓,可以加快船体创建和缺陷分析。


我进行了近似处理(更新的帖子),但即使如此,结果仍然不可接受(有时在指尖上仍然会得到多个点。而且,近似大小应该取决于分辨率和手到相机的距离,这是非常难以实现的。 - Marek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接