• 欢迎访问新概念博客,研究javascript,css3,html5,nodejs,Ext js等技术研究,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入新概念博客

Python手势识别,手把手教你如何实现Python手势识别与控制

人工智能 新概念 3年前 (2021-08-17) 4861次浏览 0个评论 扫描二维码

这是借鉴了 github 上的一个源程序,参考源:github.com

运行效果:youku 

自己在这个基础上做了一点修改补充后,可以实现手指指尖的检测,并且可以在 windows 系统下通过判断手指数目,来模拟键盘操作。下面直接上源程序,并做了详细注释,方便理解。

环境:python3.6+opencv3.4.0

Python手势识别,手把手教你如何实现Python手势识别与控制

代码如下:

import cv2
import numpy as np
import copy
import math
import win32api
import win32con
 
# 参数
cap_region_x_begin = 0.5  # 起点/总宽度
cap_region_y_end = 0.8
threshold = 60  # 二值化阈值
blurValue = 41  # 高斯模糊参数
bgSubThreshold = 50
learningRate = 0
 
# 变量
isBgCaptured = 0  # 布尔类型, 背景是否被捕获
triggerSwitch = False  # 如果正确,键盘模拟器将工作
 
def printThreshold(thr):
    print("! Changed threshold to " + str(thr))
 
def removeBG(frame): #移除背景
    fgmask = bgModel.apply(frame, learningRate=learningRate) #计算前景掩膜
    kernel = np.ones((3, 3), np.uint8)
    fgmask = cv2.erode(fgmask, kernel, iterations=1) #使用特定的结构元素来侵蚀图像。
    res = cv2.bitwise_and(frame, frame, mask=fgmask) #使用掩膜移除静态背景
    return res
 
# 相机/摄像头
camera = cv2.VideoCapture(0)   #打开电脑自带摄像头,如果参数是 1 会打开外接摄像头
camera.set(10, 200)   #设置视频属性
cv2.namedWindow('trackbar') #设置窗口名字
cv2.resizeWindow("trackbar", 640, 200)  #重新设置窗口尺寸
cv2.createTrackbar('threshold', 'trackbar', threshold, 100, printThreshold)
#createTrackbar 是 Opencv 中的 API,其可在显示图像的窗口中快速创建一个滑动控件,用于手动调节阈值,具有非常直观的效果。
 
while camera.isOpened():
    ret, frame = camera.read()
    threshold = cv2.getTrackbarPos('threshold', 'trackbar') #返回滑动条上的位置的值(即实时更新阈值)
    # frame = cv2.cvtColor(frame,cv2.COLOR_RGB2YCrCb)
    frame = cv2.bilateralFilter(frame, 5, 50, 100)  # 双边滤波
    frame = cv2.flip(frame, 1)  # 翻转  0:沿 X 轴翻转(垂直翻转)   大于 0:沿 Y 轴翻转(水平翻转)   小于 0:先沿 X 轴翻转,再沿 Y 轴翻转,等价于旋转 180°
    cv2.rectangle(frame, (int(cap_region_x_begin * frame.shape[1]), 0),(frame.shape[1], int(cap_region_y_end * frame.shape[0])), (0, 0, 255), 2)
    #画矩形框  frame.shape[0]表示 frame 的高度    frame.shape[1]表示 frame 的宽度   注:opencv 的像素是 BGR 顺序
    cv2.imshow('original', frame)   #经过双边滤波后的初始化窗口
 
    #主要操作
    if isBgCaptured == 1:  # isBgCaptured == 1 表示已经捕获背景
        img = removeBG(frame)  #移除背景
        img = img[0:int(cap_region_y_end * frame.shape[0]),int(cap_region_x_begin * frame.shape[1]):frame.shape[1]]  # 剪切右上角矩形框区域
        cv2.imshow('mask', img)
 
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  #将移除背景后的图像转换为灰度图
        blur = cv2.GaussianBlur(gray, (blurValue, blurValue), 0)  #加高斯模糊
        cv2.imshow('blur', blur)
        ret, thresh = cv2.threshold(blur, threshold, 255, cv2.THRESH_BINARY)  #二值化处理
        cv2.imshow('binary', thresh)
 
        # get the coutours
        thresh1 = copy.deepcopy(thresh)
        _, contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
        #寻找轮廓   注:这里的'_'用作变量名称,_ 表示一个变量被指定了名称,但不打算使用。
        length = len(contours)
        maxArea = -1
        if length > 0:
            for i in range(length):  # 找到最大的轮廓(根据面积)
                temp = contours[i]
                area = cv2.contourArea(temp)  #计算轮廓区域面积
                if area > maxArea:
                    maxArea = area
                    ci = i
 
            res = contours[ci]  #得出最大的轮廓区域
            hull = cv2.convexHull(res)  #得出点集(组成轮廓的点)的凸包
            drawing = np.zeros(img.shape, np.uint8)
            cv2.drawContours(drawing, [res], 0, (0, 255, 0), 2)   #画出最大区域轮廓
            cv2.drawContours(drawing, [hull], 0, (0, 0, 255), 3)  #画出凸包轮廓
 
            moments = cv2.moments(res)  # 求最大区域轮廓的各阶矩
            center = (int(moments['m10'] / moments['m00']), int(moments['m01'] / moments['m00']))
            cv2.circle(drawing, center, 8, (0,0,255), -1)   #画出重心
 
            fingerRes = []   #寻找指尖
            max = 0; count = 0; notice = 0; cnt = 0
            for i in range(len(res)):
                temp = res[i]
                dist = (temp[0][0] -center[0])*(temp[0][0] -center[0]) + (temp[0][1] -center[1])*(temp[0][1] -center[1]) #计算重心到轮廓边缘的距离
                if dist > max:
                    max = dist
                    notice = i
                if dist != max:
                    count = count + 1
                    if count > 40:
                        count = 0
                        max = 0
                        flag = False   #布尔值
                        if center[1] < res[notice][0][1]:   #低于手心的点不算
                            continue
                        for j in range(len(fingerRes)):  #离得太近的不算
                            if abs(res[notice][0][0]-fingerRes[j][0]) < 20 :
                                flag = True
                                break
                        if flag :
                            continue
                        fingerRes.append(res[notice][0])
                        cv2.circle(drawing, tuple(res[notice][0]), 8 , (255, 0, 0), -1) #画出指尖
                        cv2.line(drawing, center, tuple(res[notice][0]), (255, 0, 0), 2)
                        cnt = cnt + 1
 
            cv2.imshow('output', drawing)
            print(cnt)
            if triggerSwitch is True:
                if cnt >= 3:
                    print(cnt)
                    # app('System Events').keystroke(' ')  # simulate pressing blank space
                    win32api.keybd_event(32, 0, 0, 0)  # 空格键位码是 32
                    win32api.keybd_event(32, 0, win32con.KEYEVENTF_KEYUP, 0)  # 释放空格键
 
    # 输入的键盘值
    k = cv2.waitKey(10)
    if k == 27:  # 按下 ESC 退出
        break
    elif k == ord('b'):  # 按下'b'会捕获背景
        bgModel = cv2.createBackgroundSubtractorMOG2(0, bgSubThreshold)
        #Opencv 集成了 BackgroundSubtractorMOG2 用于动态目标检测,用到的是基于自适应混合高斯背景建模的背景减除法。
        isBgCaptured = 1
        print('!!!Background Captured!!!')
    elif k == ord('r'):  # 按下'r'会重置背景
        bgModel = None
        triggerSwitch = False
        isBgCaptured = 0
        print('!!!Reset BackGround!!!')
    elif k == ord('n'):
        triggerSwitch = True
        print('!!!Trigger On!!!')

 

运行程序操作:运行程序后,按下键盘的 b 键就可以捕获背景了,按 R 就重新启动

运行结果:

Python手势识别,手把手教你如何实现Python手势识别与控制

注:模拟点击空格键部分并未展示出来,有兴趣的可以尝试一下(按下 n 键就可以模拟键盘操作了)

补:该程序受光线影响其实较大,只有在单调背景小效果很好。

——————-补充———————-

后期再运行该程序的时候发现有一个错误,如下:

Python手势识别,手把手教你如何实现Python手势识别与控制

原因:opencv 版本的原因,在 opencv 4.0.0 版本后,findContours 的返回值只有 contours, hierarchy 两个参数,不再有三个参数了!

解决办法:

法一:更换 opencv 的版本

法二:将代码 _,contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  改为 contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  即可!


新概念博客 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Python手势识别,手把手教你如何实现Python手势识别与控制
喜欢 (11)
[新概念]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址