【问题标题】:Skin Detection and Background Subtraction in Python cv2Python cv2 中的皮肤检测和背景减法
【发布时间】:2016-09-18 12:13:26
【问题描述】:

在过去的几天里,我一直在使用 OpenCV Python 开发一个手势识别程序。理想情况下,我想要一个像this video 中所示的系统。我已经通读了视频描述中描述的算法,并且我几乎了解了所有内容。但是,我在复制他在背景减除和肤色检测方面的成功时遇到了麻烦。我所有的尝试都以非常嘈杂和/或非常依赖照明而告终,这两种情况都会使我的代码暂时无用。

我尝试了以下方法:

  • BackgroundSubtractorMOG 和 BackgroundSubtractorMOG2 应用于彩色框架
  • 将图像拆分为 Y Cr Cb,对每个图像应用 MOG 或 MOG2,然后重新组合成单​​个图像
  • 过滤范围以去除不是肤色的颜色(应用于彩色图像)

到目前为止,我还没有成功。我已经能够挑选出一些轮廓,但是这些轮廓嘈杂且不稳定(根据框架来来去去)。他们是我应该使用的另一种方法吗?

【问题讨论】:

标签: python opencv gesture-recognition


【解决方案1】:

我编写了一个fingerspelling 解释器应用程序,因此我遇到了许多您面临的健壮性问题。经过几周的实验,我确定了一个包含几种不同方法的解决方案,但最重要的方法是基于颜色的分割。

基于颜色(肤色)的分割可能非常强大,但过于简单的实现必然会缺乏您所寻求的稳健性。首先,每个人的肤色都不一样。因此,通过其他一些机制来识别基线肤色非常重要。一种方法是使用人脸检测器(例如级联分类器)来查找对象的人脸,然后相应地“调整”过滤器范围。

我个人使用级联分类器首先找到闭合的拳头形状,然后在 1) 仅包括拳头的紧密边界框和 2) 整个图像之间对 HSV 直方图进行归一化和差分。然后我建立了一个查找表 (LUT),将每个通道的每个值映射到 0 到 255 之间的值,表示像素代表皮肤的概率。

根据我的经验,提高我的手部跟踪逻辑性能的最重要的因素是当我想到不丢弃信息的时候。您会很想使用一些最佳范围简单地设置阈值以生成二值图像,但知道一个像素有 40% 的机会是皮肤,而另一个像素有 60% 的机会是有价值的。一旦你设置了阈值,你就只有一个 1 和一个 0。

当然,如果您打算使用基于轮廓的姿势分类,可能需要在某个点设置阈值。但如果您真的想构建强大的手势识别软件,您可能需要使用Convolutional Neural Net (CNN) 进行调查以执行分类。预测可以很快完成,并且对背景噪音、翻译等非常鲁棒......希望这会有所帮助!

编辑:让我澄清一下关于“标准化和区分 HSV 直方图”的 cmets。首先,这样做的基本原理是能够利用 OpenCV 的LUT (lookup table),而不是使用基于范围的阈值。 LUT 非常高效,并且比基于范围的阈值更灵活。例如,假设您想要的色调包含两个不同范围内的值(例如:0-30 和 150-180)。基于 LUT 的方法很容易解决这个问题,因为每个单独的值都可以独立映射。

所以一旦你建立了 LUT,你只需要在 HSV 图像上为每一帧运行LUT。这是一个非常有效的解决方案。

就我而言,为了构建 LUT,我采取了以下步骤:

  • 转换为 HSV 颜色空间。
  • 获取拳头周围紧密边界框的 ROI。
  • 为 ROI 和整个图像的每个通道构建直方图。
  • 缩小(标准化)全图像通道直方图,使比例与 ROI 直方图匹配(即:将每个全图像直方图乘以 ROI_area/full_image_area)。
  • 从 ROI 直方图中减去(差异)整个图像直方图。 (具有较大正值的条目将对应于 ROI 中常见的通道值,而不是完整图像。)
  • 然后我对差异直方图进行平滑处理以减少噪声和“过度拟合”。
  • 最后,我使用 OpenCV 的 normalize 函数和 NORM_MINMAX 将差异直方图标准化为 0.0 和 1.0 之间的值。
  • 然后可以将差异直方图合并在一起以生成 3 通道 LUT。

我个人不会在使用 LUT 后设置阈值。相反,我只是使用结果数据来计算我用来保持 ROI 集中在手上的各种“颜色质量中心”。然后,我可以将相同的基于 LUT 的值发送到 CNN 进行分类。

请注意,虽然这种方法对我的目的很有效,但如果您执行阈值处理,它仍然不会是完美的;会有一些前景被检测为背景,反之亦然。我结合了一些基于边缘检测的逻辑来帮助减少由米色墙壁(我家中常见的故障模式)导致的误报,但现实情况是,如果有一种真正强大的方法可以从背景中清晰地分割出一只手,这对改变背景和照明条件,我还没有找到它。所以我的建议是从视觉皮层中获取线索,并允许来自更高抽象层次的信息来帮助过滤掉噪音。 (换句话说,研究一下 CNN——它们真的很了不起。)

【讨论】:

  • 到目前为止,我已经选择了一个测试区域(根据您的建议,现在是一个封闭的拳头)并根据 H 和 S 通道生成了一个 HSV 直方图。但是,我不太确定你的意思是规范化和区分两个图像之间的直方图。我试图均衡第一个直方图并将其反向投影到原始图像,但我仍然看到一些皮肤遗漏和一些其他物体被检测为皮肤的问题。你的技术能避免这种情况吗?
  • 我已经添加了一些进一步的说明 - 请查看我的编辑。
  • 好的,我已经成功地生成、规范化和区分直方图,但是我在创建查找表时遇到了麻烦。我环顾四周,但似乎找不到一个很好的解释来说明如何创建我需要的 HSV 查找表。我尝试了这段代码LUT = cv2.merge((difference_hists[0], difference_hists[1], difference_hists[2])),其中differ_hists 将H、S 和V 通道的差异直方图分别存储在索引0、1 和2 中。但是,每当我运行它时,这都会给我一个错误:
  • LUT = cv2.merge((difference_hists[0], difference_hists[1], difference_hists[2])) error: ..\..\..\..\opencv\modules\core\src\convert.cpp:296: error: (-215) mv[i].size == mv[0].size && mv[i].depth() == depth in function cv::merge
  • 查看merge 文档here。输入矩阵数组作为一个参数传递。所以你可能想要更像:LUT = cv2.merge(difference_hists)。希望对您有所帮助。
【解决方案2】:

背景减法往往会留下很多鬼影。您应该从基于 HSV 的皮肤检测开始,以排除非皮肤区域。这将消除对背景扣除的需要。

然后您可以应用面部检测来掩盖您的面部,然后,当您只剩下手部轮廓时,您可以使用 approxPolyDP() 来获得简化的多边形。计算这个多边形的矩和凸度将为您提供手势识别的基础

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-06
    • 2012-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多