手指和手势识别算法原理和解析

时间: 2024-04-17 09:10:47 |   作者: 光电sensor

工作地点:首页 > 爱游戏最新官网 > 光电sensor

  我们将从视频序列中识别手势。为了从实时视频序列中识别这些手势,我们第一步需要单独取出手部区域,以去除视频序列中所有不需要的部分。在分割手部区域之后,我们对视频序列中显示的手指进行计数,以基于手指计数来指示机器人。因此,能够正常的使用2个简单的步骤解决整个问题

  手势识别的第一步显然是通过消除视频序列中所有其他不需要的部分来找到手部区域。起初这似乎令人恐惧。但是不需要过多的担心。使用Python和OpenCV会容易得多!

  首先,我们应该一种有效的方法来将前景与背景分开。为此,个人会使用移动平均值的概念。我们使我们的系统能查看特定场景的30帧。在此期间,我们计算当前帧和先前帧的运行平均值。通过这样做,我们实质上告诉我们的系统-

  弄清楚背景之后,我们举起手来,使系统了解我们的手是进入背景的新条目,这在某种程度上预示着它成为前景对象。但是,我们将如何单独看待这一前景呢?答案是背景减法。

  在使用移动平均值计算出背景模型之后,个人会使用当前框架以及背景来保存前景对象(在本例中为hand)。我们计算背景模型(随时间更新)与当前帧(有我们的手)之间的绝对差值,以获取包含新添加的前景对象(即我们的手)的差值图像。这就是背景减法的全部含义。

  为了从该差异图像中检测出手部区域,我们应该对差异图像进行阈值处理,以使只有我们的手部区域可见,而所有其他不需要的区域都被涂成黑色。这就是运动检测的全部意义。

  注意:阈值是基于特定阈值级别将像素强度分配为0和1,以便仅从图像中捕获我们感兴趣的对象。

  对差异图像进行阈值处理后,我们在结果图像中找到轮廓。假定面积最大的轮廓是我们的手。

  个人会使用函数来计算背景模型和当前帧之间的移动平均值。此函数接受两个参数- iamge(当前帧)和aWeight,这就像在图像上执行移动平均的阈值。如果背景模型为“ 无”(即,如果它是第一帧),则使用当前帧对其进行初始化。然后,使用cv2.accumulateWeighted()函数计算背景模型和当前帧的移动平均值。使用下面给出的公式计算移动平均值

  src(x,y)----源图像或输入图像(1或3通道,8位或32位浮点)

  dst(x,y)---目标图像或输出图像(与源图像相同的通道,32位或64位浮点)

  下一个功能用于从视频序列中分割手部区域。这个函数有两个参数- 当前帧和阈值用于阈值化的差分图像。

  首先,我们使用cv2.absdiff()函数找到背景模型和当前帧之间的绝对差异。

  接下来,我们对差异图像进行阈值处理以仅显示手部区域。最后,我们对阈值图像执行轮廓提取,并获取面积最大的轮廓(这就是我们的手)。

  我们将阈值图像和分割图像作为元组返回。阈值法背后的数学原理格外的简单。如果x(n)表示输入图像在特定像素坐标处的像素强度,那么threshold决定我们如何将图像分割/阈值为二值图像。

  上面的代码示例是我们程序的基本功能。我们将aWeight初始化为0.5。如移动平均值方程式中更早显示的那样,此阈值意味着如果为该变量设置较低的值,则将在较大数量的先前帧上执行移动平均值,反之亦然。个人会使用cv2.VideoCapture(0)引用了我们的网络摄像头,这在某种程度上预示着我们在计算机中获取了默认的网络摄像头实例。

  代替从整个视频序列中识别手势,我们将尝试最小化系统必须在其中寻找手部区域的识别区域(或区域)。为了突出显示该区域,个人会使用cv2.rectangle()函数,该函数需要顶部,右侧,底部和左侧像素坐标。

  为了跟踪帧数,我们初始化一个变量num_frames。然后,我们开始无限循环,并使用camera.read()函数从网络摄像头读取帧。然后,个人会使用imutils库将输入帧的大小调整为700像素的固定宽度,以保持宽高比,并翻转该帧以避免产生镜像。

  接下来,个人会使用简单的NumPy切片仅取出感兴趣的区域(即识别区域)。然后,我们将此ROI转换为灰度图像,并使用高斯模糊来最小化图像中的高频分量。直到超过30帧为止,我们继续将输入帧添加到run_avg函数并更新背景模型。请注意,在此步骤中,必须使相机保持不动。否则,整个算法将失败。

  更新背景模型后,将当前输入帧传递到分割函数中,并返回阈值图像和分割图像。所分割的轮廓绘制在使用帧cv2.drawContours() ,并使用被示出阈值化的输出cv2.imshow() 。

  从实时视频序列中分割出手部区域后,我们将使我们的系统对通过摄像头/网络摄像头显示的手指进行计数。咱们不可以使用任何模板(由OpenCV提供)来执行此操作,因为这确实是一个具有挑战性的问题。

  上一教程个人会使用了“背景减法”,“运动检测”和“阈值化”概念从实时视频序列中分割出手部区域。

  我们通过将分割的手区域假定为框架中的最大轮廓(即具有最大面积的轮廓)来获得该区域。如果您在此框架中引入了一个比您的手大的大物体,则此算法将失败。因此,您必须确保您的手占据框架中大部分区域。

  我们将使用在可变手中获得的分段手区域。请记住,此手形变量是具有阈值(阈值图像)和分段(分段的手区域)的元组。我们将利用这两个变量来计算所显示的手指。我们该怎么做?

  可以使用多种方法来数手指,但是在本教程中我们将看到一种这样的方法。如Malima等人提出的,这是一种执行手势识别的更快的方法。下图显示了计数手指的方法(由Malima等人提出)。

  找到分割的手部区域的凸包(轮廓),并计算凸包中的最极端点(极端顶部,极端底部,极端左侧,极端右侧)。

  使用手掌的中心,以最大欧几里德距离(手掌的中心与端点之间)为半径,构造一个圆。

  在带阈值的手形图像(帧)和圆形ROI(蒙版)之间执行按位与运算。这显示了手指切片,能更加进一步用于计算所示手指的数量。

  每个中间步骤都需要对图像处理基础知识知道,例如轮廓,按位与,欧氏距离和凸包。

  感兴趣对象的轮廓或边界。使用OpenCV的cv2.findContours()函数能轻松找到该轮廓。在解压缩此函数的返回值时要小心,因为在OpenCV 3.1.0-Contours中,我们应该三个变量来解压缩此元组。

  在两个对象之间执行按位逻辑与。您可以从视觉上将其想象为使用遮罩并提取图像中仅位于此遮罩下的区域。OpenCV提供cv2.bitwise_and()函数来执行此操作-按位与。

  这是此处所示方程式给出的两点之间的距离。Scikit-learn提供了一个名为pairwise.euclidean_distances ()的函数,用于计算单行代码“成对欧氏距离”中从一个点到多个点的欧氏距离。在那之后,我们采取了最大的使用与NumPy的所有这些距离argmax()函数。

  在要跟踪用户手部运动的应用程序中,肤色直方图将很有用。然后,此直方图用于从图像中减去背景,只保留图像中包含肤色的部分。

  一种检测皮肤的简单得多的方法是找到某个RGB或HSV范围内的像素。上述方法的问题就在于,改变光照条件和肤色会确实干扰皮肤检测。另一方面,直方图往往更准确,并考虑了当前的光照条件。

  绿色矩形绘制在框架上,用户将手放在这些矩形内。应用程序从用户的手中获取肤色样本,然后创建直方图。

  既然用户了解了他或她的手掌的放置位置,接下来的步骤是从这些矩形中提取像素并使用它们生成HSV直方图。

  此处功能将输入帧转换为HSV。使用Numpy,我们创建[90 * 10]具有3彩色通道大小的图像,并将其命名为ROI****(Intrest区域)。然后,它从绿色矩形中获取900像素的值,并将其放入ROI矩阵中。

  在cv2.calcHist创建使用肤色的ROI矩阵直方图和cv2.normalize标准化使用标准型这个矩阵cv2.NORM_MINMAX。现在我们有了一个直方图来检测帧中的皮肤区域。

  既然用户了解了他或她的手掌的放置位置,接下来的步骤是从这些矩形中提取像素并使用它们生成HSV直方图。

  现在我们拥有皮肤颜色直方图,我们大家可以使用它来查找包含皮肤的框架的组成部分。OpenCV为咱们提供了一种便捷的方法,cv2.calcBackProject该方法使用直方图来分离图像中的特征。我使用此功能将肤色直方图应用于帧。

  在前两行中,我将输入帧更改为HSV,然后应用cv2.calcBackProject肤色直方图hist。之后,我使用了“过滤和阈值”功能来平滑图像。最后,我使用该cv2.bitwise_and函数屏蔽了输入帧。最后一个框架应该是只包含肤域。

下一条:动态手势识别是如何实时工作的?