2020 12-23 视觉组作业

opencv-python 关于识别手势剪刀石头布

1.步骤

  1. 导入用得到的库

    import cv2
    import numpy as np
    import math
    
  2. 打开摄像头 内置摄像头索引为0

    cap = cv2.VideoCapture(0)
    
  3. 读取照片 选择手势输入的位置

    while (cap.isOpened()):
        ret, frame = cap.read()  # 读取摄像头每帧图片
    
        frame = cv2.flip(frame, 1)
        kernel = np.ones((2, 2), np.uint8)
        roi = frame[100:300, 100:300]  # 选取图片中固定位置作为手势输入 选了左上角.
    
    cv2.rectangle(frame, (100, 100), (300, 300), (0, 0, 255), 0)  # 用红线画出手势识别框
    
  4. 基于hsv的肤色检测

    # hsv的肤色检测
    hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
    #定义三个通道颜色取值
    lower_skin = np.array([0, 28, 70], dtype=np.uint8)
    upper_skin = np.array([20, 255, 255], dtype=np.uint8)
    
  5. 高斯滤波

    # 进行高斯滤波
    #mask把HSV图片中在颜色范围内的区域变成白色,其他区域变成黑色
    mask = cv2.inRange(hsv, lower_skin, upper_skin) #设置阈值去除背景部分,得到想要的区域
    mask = cv2.dilate(mask, kernel, iterations=4)
    mask = cv2.GaussianBlur(mask, (5, 5), 100)
    
  6. 找轮廓

    contours, h = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    
  7. 定义、找出凹凸点

    cnt = max(contours, key=lambda x: cv2.contourArea(x))
    epsilon = 0.0005 * cv2.arcLength(cnt, True)
    approx = cv2.approxPolyDP(cnt, epsilon, True)
    hull = cv2.convexHull(cnt)
    areahull = cv2.contourArea(hull)
    areacnt = cv2.contourArea(cnt)
    arearatio = ((areahull - areacnt) / areacnt) * 100
    # 求出凹凸点
    hull = cv2.convexHull(approx, returnPoints=False)
    defects = cv2.convexityDefects(approx, hull)
    
    l = 0  # 定义凹凸点个数初始值为0
    for i in range(defects.shape[0]):
        s, e, f, d, = defects[i, 0]
        start = tuple(approx[s][0])
        end = tuple(approx[e][0])
        far = tuple(approx[f][0])
        pt = (100, 100)
    
        a = math.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)
        b = math.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2)
        c = math.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)
        s = (a + b + c) / 2
        ar = math.sqrt(s * (s - a) * (s - b) * (s - c))
        # 手指间角度求取
        angle = math.acos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c)) * 57
    
        if angle <= 90 and d > 20:
            l += 1
            cv2.circle(roi, far, 3, [255, 0, 0], -1)
        cv2.line(roi, start, end, [0, 255, 0], 2)  # 画出包络线
    l += 1
    font = cv2.FONT_HERSHEY_SIMPLEX
    
  8. 输出是什么手势

    # 条件判断,根据特征不同选取出剪刀、石头、布手势(凹凸点个数)
    
    if l==2:#没有手势的时候是none
            cv2.putText(frame,'none', (0, 50), font, 2, (0, 0, 255), 3, cv2.LINE_AA)
    elif l==3:#三个点是剪刀(虽然其他三个点的手势也会输出剪刀。。。)
        cv2.putText(frame, 'scissors', (0, 50), font, 2, (0, 0, 255), 3, cv2.LINE_AA)
    elif l == 5:#五个点的是布
        cv2.putText(frame, 'cloth', (0, 50), font, 2, (0, 0, 255), 3, cv2.LINE_AA)
    elif l==1:#一个点的是石头
        cv2.putText(frame, 'stone', (0, 50), font, 2, (0, 0, 255), 3, cv2.LINE_AA)
    
  9. 保存显示

    cv2.imshow('frame', frame)
    cv2.imshow('mask', mask)
    
  10. 按esc键退出 释放窗口

    k = cv2.waitKey(25) & 0xff
        if k == 27:  # 键盘Esc键退出
            break
    cv2.destroyAllWindows()
    cap.release()#释放窗口
    

2.遇到的问题

  • 受光线影响很大,而且经常受背景干扰。凹凸点也经常显示不准确(用手背测试效果好一些,可能因为颜色变化比较平滑)开始判断条件设置错了,导致没有手的时候也显示scissors。程序无法判断剪刀,因为剪刀和没有手势都是一个凹凸点,可能可以通过是否轮廓封闭来区分?但不会用代码进行区分
    中间的凹凸点的实现没有怎么理解。
  • 实际中凹凸点很不准确,并且由于手的晃动点的识别更加不准确

3.实现的结果

3.1 识别拳头手势
2020 12-23 视觉组作业_第1张图片

3.2 识别布手势

2020 12-23 视觉组作业_第2张图片

3.3 无手势识别

2020 12-23 视觉组作业_第3张图片

剪刀手识别还未实现,待开发中…

你可能感兴趣的:(opencv,图像识别)