当前位置:首页 » 《随便一记》 » 正文

基于opencv的手势识别

18 人参与  2023年05月06日 12:49  分类 : 《随便一记》  评论

点击全文阅读


大家好,我是一名本科生,我的主要学习方向是计算机视觉以及人工智能。按照目前的学习进度来说,我就是一小白,在这里写下自己编写的程序,与大家分享,记录一下自己的成长。
在这里插入图片描述

今天与大家分享的是基于OpenCv的手势识别。

思路分析

获取图片,在图片中找到手,然后进行一系列的闭运算,降噪平滑处理,轮廓查找,凸缺陷检测。(如果你不太理解这些操作,别着急在下面的源码中我会尝试着解释)
然后根据凸包缺陷的个数来判断手指的个数。

遇到的困难

在上述的过程借助opencv库很容易就可以实现,但实现的过程中令人头疼的地方。

在图像中找出手部是很麻烦的,一开始按照书上的方法是将读入的图片由BGR转换为HSV模式,由HSV来确定手的肤色范围,将手从图像中分离出来,代码如下:

//设置肤色范围,该范围的数值是百度出来的人体肤色范围lower_skin = np.array([0,28,70],dtype = np.uint8)upper_skin = np.array([20,255,255],dtype = np.uint8)//根据肤色范围进行手的查找,//cv2.inRange函数会将图像内不在该范围区域设置为黑色mask = cv2.inRange(img_hsv,lower_skin,upper_skin)//但是该方法的效果不理想,找出的手部不准确

我又在网上查找一番,找到了比HSV好的方法:椭圆肤色检测

//这里我写成了一个类class check_skin():    def __init__(self):        // 创建椭圆模型        self.ellipse_mode = np.zeros((256, 256), dtype=np.uint8)        /// 在图像上绘制白色椭圆        cv2.ellipse(self.ellipse_mode, (113, 155), (23, 15), 43, 0, 360, (255, 255, 255), -1)    def check_finger(self,path):        img = cv2.imread(path, cv2.IMREAD_COLOR)        // 图像皮肤掩膜创建        skin_mask = np.zeros(img.shape[:2], dtype=np.uint8)        // 将图像转换为YCBCR        img_ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB)        for i in range(img.shape[0]):            for j in range(img.shape[1]):                cr = img_ycrcb[i, j][1]                cb = img_ycrcb[i, j][2]                if self.ellipse_mode[cr, cb] > 0:                    skin_mask[i, j] = 255        img = cv2.bitwise_and(img,img,mask = skin_mask)        return img

这个方法要比HSV检测出来的手部效果好的多,效果图就不展示了,有兴趣的可以自己实验一下。
更多的肤色检测方法:请点击这里

手部识别的问题解决后,我来讲一下手势识别的过程
首先,了解一下凸包与凸缺陷

红色为凸包,蓝色点为凸缺陷的最深点(即边缘点到凸包距离最大点),绿色是轮廓。红色与绿色之间的区域即为凸缺陷。在这里插入图片描述
接着我们使用函数:cv2.convexityDefects,convexityDefects:输出参数,检测到的最终结果,返回一个数组,其中每一行包含的值是[起点,终点,最远的点,到最远点的近似距离]。前三个点都是轮廓索引。前三个值得含义分别为:凸缺陷的起始点,凸缺陷的终点,凸缺陷的最深点(即边缘点到凸包距离最大点)
就是上图的蓝色小点,然后根据convexityDefects返回数组的每一行的前三个值,构成的三角形由于人手指伸开的时候所构成的三角形角度总是小于90度,来去除不属于手指的凸缺陷,然后统计小于90度的凸缺陷个数再加1就是手指的个数。
在伸出的手指的个数为一和零的时候统计凸缺陷是不可行的,如图:
在这里插入图片描述
在这里插入图片描述
这时候应该统计轮廓以及凸包的面积,设置比例关系来判断手指为1和0的情况。

代码

import numpy as npimport cv2import timeimport math#将肤色检测的椭圆模型设计为类class check_skin():    def __init__(self):        # 创建椭圆模型        self.ellipse_mode = np.zeros((256, 256), dtype=np.uint8)        # 在图像上绘制白色椭圆        cv2.ellipse(self.ellipse_mode, (113, 155), (23, 15), 43, 0, 360, (255, 255, 255), -1)    def check_finger(self,path):        img = cv2.imread(path, cv2.IMREAD_COLOR)        # 图像皮肤掩膜创建        skin_mask = np.zeros(img.shape[:2], dtype=np.uint8)        # 将图像转换为YCBCR        img_ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB)        for i in range(img.shape[0]):            for j in range(img.shape[1]):                cr = img_ycrcb[i, j][1]                cb = img_ycrcb[i, j][2]                if self.ellipse_mode[cr, cb] > 0:                    skin_mask[i, j] = 255        img = cv2.bitwise_and(img,img,mask = skin_mask)        return img #将类初始化为对象check_finger = check_skin()#这个列表储存图片名称img_path = ['zero','one','two','three','four','five']for path in img_path:#图片地址合成以及椭圆模型检测    finger_img = check_finger.check_finger(path+'.JPG')    #转换为灰度图像    finger_img_gray = cv2.cvtColor(finger_img,cv2.COLOR_BGR2GRAY)    #阈值函数转化为二值图像    _,finger_img_binary = cv2.threshold(finger_img_gray,50,255,cv2.THRESH_BINARY)    #进行闭运算    kernel = np.ones((4,4),dtype = np.uint8)    finger_img_binary = cv2.morphologyEx(finger_img_binary,cv2.MORPH_CLOSE,kernel,iterations = 2)    #高斯滤波进行去噪平滑    finger_img_binary = cv2.GaussianBlur(finger_img_binary,(3,3),50)    #轮廓查找    contours, hierarchy = cv2.findContours(finger_img_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)    area_cont = cv2.contourArea(contours[0])    #凸缺陷检测    hull1 = cv2.convexHull(contours[0])    area_hull = cv2.contourArea(hull1)    #根据轮廓面积和轮廓凸包面积的比例来确定zero和one    percentage = (area_hull-area_cont)/area_cont    print(percentage)    hull = cv2.convexHull(contours[0], returnPoints=False)    convex = cv2.convexityDefects(contours[0],hull)    #用来统计小于90的角的个数    count = 0    for i in range(convex.shape[0]):        s,e,f,d = convex[i][0]        start = contours[0][s][0]        end = contours[0][e][0]        far = contours[0][f][0]        #计算三角形的三边的长度        a = math.sqrt((start[0]-far[0])**2+(start[1]-far[1])**2)        b = math.sqrt((end[0]-far[0])**2+(end[1]-far[1])**2)        c = math.sqrt((start[0]-end[0])**2+(start[1]-end[1])**2)        #计算角度        angle = math.acos((a**2+b**2-c**2)/(2*a*b))*57        #画出角度        cv2.line(finger_img,start,far,(0,255,0),2)        cv2.line(finger_img,end,far,(0,255,0),2)        # #在角上放上角的度数        # cv2.putText(finger_img,str(angle),far,cv2.FONT_HERSHEY_COMPLEX,1,(0,255,0),1)        #判断角度小于九十度的角的个数        if angle<90:            count += 1    if count == 0 :        if percentage < 0.2 :            cv2.putText(finger_img,str(count),(50,20),cv2.FONT_HERSHEY_COMPLEX,1,(0,255,0),1)        else:            cv2.putText(finger_img, str(count+1), (50,20), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 1)    else:        cv2.putText(finger_img, str(count+1), (50,20), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 1)    cv2.imshow(path,finger_img)    cv2.waitKey(0)cv2.destroyAllWindows()

结尾

后来在网上无意间发现了mediapipe这个手势检测库,然后我使用这个库重新又写了一个手势识别的程序,如果大家想要了解,请看我的下一个文章。

最后,创作不易,请各位支持一下。我也是小白一名,欢迎大家的指导,交流。
在这里插入图片描述


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/61112.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1