闲的无聊做的一个小玩意,可以调用你的计算机相机,识别框内的手势(剪刀、石头和布),提供一个判决平台,感兴趣的可以继续完善。
 用到的参考小文献:
 
具体实现结果如下

 
 并且我另写了一个框架平台,可以进行下一步的功能拓展,发在我的资源界面了;
 
手势识别与控制系统
简介
  我们系统的手势识别与控制功能主要采用 OpenCV库实现 , OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库, 可以运行在Linux, Windows, Android和Mac-OS操作系统上. 它轻量级而且高效—由一系列 C 函数和少量 C++类构成, 同时提供了Python, Ruby, MATLAB等语言的接口, 实现了图像处理和计算机视觉方面的很多通用算法。
   本系统主要使用了OpenCV的视频采集, 图像色域转换, 颜色通道分割, 高斯滤波, OSTU自动阈值, 凸点检测, 边缘检测, 余弦定理计算手势等功能。
手势检测:#pic_center
水平翻转
设置ROI
ROI是region of interest首字母的简写,翻译为感性趣的区域。ROI是在一定图片中取一小部分进行重点处理的区域。图片就是一个二维数组,把数组分块求取就可以得到你想要的ROI区域。
定义肤色范围
人为定义RGB颜色范围,截取红黄蓝三原色上下限选出符合人肤色的区域。
提取肤色
  在ROI选区范围内提取出肤色转化为灰度图像
   在进行颜色空间转换时,RGD各个通道的范围应当根据实际  需要来进行归一化,如RGB转LUV通道时,需要将RGB归一化为32位浮点数,即各通道的值的变化范围为0到1,转化为HSV时也是这样。
   RGB空间下标(R、G、B)代表着相应的颜色空间,且R、G、B∈(0,1).OpenCV中从RGB颜色空间转换为HSV颜色空间按照下式进行:
  
      
       
        
        
          V 
         
        
          = 
         
        
          m 
         
        
          a 
         
        
          x 
         
        
          ( 
         
        
          R 
         
        
          , 
         
        
          G 
         
        
          , 
         
        
          B 
         
        
          ) 
         
        
       
         V=max(R,G,B) 
        
       
     V=max(R,G,B)
  
      
       
        
        
          f 
         
        
          ( 
         
        
          x 
         
        
          ) 
         
        
          = 
         
         
         
           { 
          
          
           
            
             
              
               
                
                 
                 
                   V 
                  
                 
                   − 
                  
                 
                   min 
                  
                 
                    
                  
                 
                   ( 
                  
                 
                   R 
                  
                 
                   , 
                  
                 
                   G 
                  
                 
                   , 
                  
                 
                   B 
                  
                 
                   ) 
                  
                 
                
                  V 
                 
                
               
              
                , 
               
              
             
            
            
             
              
               
              
                i 
               
              
                f 
               
              
                V 
               
              
                ≠ 
               
              
                0 
               
              
             
            
           
           
            
             
              
              
                0 
               
              
                , 
               
              
             
            
            
             
              
               
              
                i 
               
              
                f 
               
              
                otherwise 
               
              
             
            
           
          
         
        
       
         f(x)=\begin{cases}\dfrac{V-\min(R,G,B)}{V},&\quad if V\neq0\\ 0,&\quad if \textit{otherwise}\end{cases} 
        
       
     f(x)=⎩ 
              ⎨ 
              ⎧VV−min(R,G,B),0,ifV=0ifotherwise
  
      
       
        
        
          H 
         
        
          = 
         
         
         
           { 
          
          
           
            
             
              
               
                
                 
                 
                   60 
                  
                 
                   ( 
                  
                 
                   G 
                  
                 
                   − 
                  
                 
                   B 
                  
                 
                   ) 
                  
                 
                 
                 
                   V 
                  
                 
                   − 
                  
                 
                   min 
                  
                 
                   ( 
                  
                 
                   R 
                  
                 
                   , 
                  
                 
                   G 
                  
                 
                   , 
                  
                 
                   B 
                  
                 
                   ) 
                  
                 
                
               
              
                , 
               
              
                i 
               
              
                f 
               
              
                V 
               
              
                = 
               
              
                R 
               
              
             
            
           
           
            
             
              
              
                120 
               
              
                + 
               
               
                
                 
                 
                   60 
                  
                 
                   ( 
                  
                 
                   B 
                  
                 
                   − 
                  
                 
                   R 
                  
                 
                   ) 
                  
                 
                 
                 
                   V 
                  
                 
                   − 
                  
                 
                   min 
                  
                 
                   ( 
                  
                 
                   R 
                  
                 
                   , 
                  
                 
                   G 
                  
                 
                   , 
                  
                 
                   B 
                  
                 
                   ) 
                  
                 
                
               
              
                , 
               
              
                i 
               
              
                f 
               
              
                V 
               
              
                = 
               
              
                G 
               
              
             
            
           
           
            
             
              
              
                250 
               
              
                + 
               
               
                
                 
                 
                   60 
                  
                 
                   ( 
                  
                 
                   R 
                  
                 
                   − 
                  
                 
                   G 
                  
                 
                   ) 
                  
                 
                 
                 
                   V 
                  
                 
                   − 
                  
                 
                   min 
                  
                 
                   ( 
                  
                 
                   R 
                  
                 
                   , 
                  
                 
                   G 
                  
                 
                   , 
                  
                 
                   B 
                  
                 
                   ) 
                  
                 
                
               
              
                , 
               
              
                i 
               
              
                f 
               
              
                V 
               
              
                = 
               
              
                B 
               
              
             
            
           
          
         
        
       
         H=\begin{cases}\dfrac{60(G-B)}{V-\text{min}(R,G,B)},ifV=R\\ 120+\dfrac{60(B-R)}{V-\text{min}(R,G,B)},ifV=G\\ 250+\dfrac{60(R-G)}{V-\text{min}(R,G,B)},ifV=B\end{cases} 
        
       
     H=⎩ 
              ⎨ 
              ⎧V−min(R,G,B)60(G−B),ifV=R120+V−min(R,G,B)60(B−R),ifV=G250+V−min(R,G,B)60(R−G),ifV=B
 HSV色表如下:
 
图形经过膨胀与腐蚀后进行高斯滤波模糊图像
实现原理
   图像的膨胀(Dilation)和腐蚀(Erosion)是两种基本的形态学运算,主要用来寻找图像中的极大区域和极小区域。其中膨胀类似于“领域扩张”,将图像中的高亮区域或白色部分进行扩张,其运行结果图比原图的高亮区域更大;腐蚀类似于“领域被蚕食”,将图像中的高亮区域或白色部分进行缩减细化,其运行结果图比原图的高亮区域更小。
   膨胀的运算符是”⊕”,其定义如下:
  
      
       
        
        
          A 
         
        
          ⊕ 
         
        
          B 
         
        
          = 
         
         
         
           { 
          
         
           x 
          
          
          
            ∣ 
           
           
            
            
              ( 
             
            
              B 
             
            
              ) 
             
            
           
             x 
            
           
          
            ∩ 
           
          
            A 
           
          
            ≠ 
           
          
            Θ 
           
          
            } 
           
          
         
        
       
         \mathrm{A}\oplus\mathrm{B}=\left\{\mathrm{x}\left|\left(\mathrm{B}\right)_x\cap\mathrm{A}\neq\Theta\right\}\right. 
        
       
     A⊕B={x∣(B)x∩A=Θ}
   该公式表示用B来对图像A进行膨胀处理,其中B是一个卷积模板或卷积核,其形状可以为正方形或圆形,通过模板B与图像A进行卷积计算,扫描图像中的每一个像素点,用模板元素与二值图像元素做“与”运算,如果都为0,那么目标像素点为0,否则为1。从而计算B覆盖区域的像素点最大值,并用该值替换参考点的像素值实现膨胀。
 图像腐蚀
   该公式表示图像A用卷积模板B来进行腐蚀处理,通过模板B与图像A进行卷积计算,得出B覆盖区域的像素点最小值,并用这个最小值来替代参考点的像素值。
   a. 图像二值化处理,将图像的灰度值根据阈值进行0,1处理得到的图像;
   b. 卷积核,对应信号处理中的高低频滤波器。常用numpy函数去设置,np.ones((m,n), np.uint8) 表示指定m*n的卷积核;
   c. 图像的膨胀或腐蚀,cv2.dilate(二值化图像, 卷积核, 迭代次数),完成图片的高斯滤波模糊。
轮廓检测和查找最大面积轮廓
轮廓处理的话主要用到函数cv2.findContours和这两个函数的使用使用方法很容易搜到就不说了,这部分主要的问题是提取到的轮廓有很多个,但是我们只需要手的轮廓,所以我们要用sorted函数找到最大的轮廓。
估算轮廓然后绘制轮廓曲线
查找手指凸包
  凸包(Convex Hull)是一个计算几何(图形学)中的概念,它的严格的数学定义为:在一个向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。   
   在图像处理过程中,我们常常需要寻找图像中包围某个物体的凸包。凸包跟多边形逼近很像,只不过它是包围物体最外层的一个凸集,这个凸集是所有能包围这个物体的凸集的交集。
   在opencv中,通过函数convexHulll能很容易的得到一系列点的凸包,比如由点组成的轮廓,通过convexHull函数,我们就能得到轮廓的凸包。寻找图像的凸包,就能够实现手势识别的功能。
 对如下手势进行凸包查找:
 
手指凹陷查找{求三角形三边长度、点和曲线凸处的距离、余弦定理、去噪、手指轮廓描述曲线}
  利用opencv提供的 convexityDefects 凹点检测函数检测图像凹陷的点, 然后利用, 然后根据凹陷点中的 (开始点, 结束点, 远点)的坐标, 利用余弦定理计算两根手指之间的夹角, 其必为锐角, 根据锐角的个数判别手势.
   其中,锐角个数为0 ,表示 手势是 拳头 或 一,
     锐角个数为0 ,表示 手势是 拳头 或 一,
     锐角个数为1 ,表示 手势是 剪刀
     锐角个数为2 ,表示 手势是 三,
     锐角个数为3 ,表示 手势是 四,
     锐角个数为4 ,表示 手势是 布
   具体原理如下:利用findContours检测图像中的轮廓, 其中返回值contours包含了图像中所有轮廓的坐标点,它们的值分别为起点,终点,最远的点,到最远点的近似距离。根据图像中凹凸点中的 (开始点, 结束点, 远点)的坐标, 利用余弦定理计算两根手指之间的夹角, 其必为锐角, 根据锐角的个数判别手势。
输出手势
划拳(三次训练):
- 构造3*3的结构元素,得到ROI分析块
- 将膨胀后的图像和腐蚀相减得到轮廓边(灰度图)
- 将上面结果二进制化并进行反色
- 对反色结果进行中值滤波
- 计算手势面积
- 设定一定的判定时间,等到倒计时结束的时候将取得的图片进行处理和判定,经过训练三次得到判定的结果。
代码实现过程
import cv2
import time
import os
def judge():
    # 构造一个3×3的结构元素
    # return 0 stone ,1 jiandao, 2 bu
    img = cv2.imread("wif.jpg", 0)
    element = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 11))  # 矩形:MORPH_RECT  交叉形:MORPH_CROSS  椭圆形:MORPH_ELLIPSE
    dilate = cv2.dilate(img, element)
    erode = cv2.erode(img, element)
    # 将两幅图像相减获得边,第一个参数是膨胀后的图像,第二个参数是腐蚀后的图像
    result = cv2.absdiff(dilate, erode);
    # 上面得到的结果是灰度图,将其二值化以便更清楚的观察结果
    retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY);
    # 反色,即对二值图每个像素取反 图像非运算的效果:一个二值图,将黑色转为白色,白色转为黑色。
    result = cv2.bitwise_not(result);
    # 非线性过滤——中值滤波
    result = cv2.medianBlur(result, 23)
    #  计算手势面积
    a = []
    posi = []
    width = []
    count = 0
    area = 0
    for i in range(result.shape[1]):
        for j in range(result.shape[0]):
            if (result[j][i] == 0):
                area += 1
    for i in range(result.shape[1]):
        if (result[5 * result.shape[0] // 16][i] == 0 and result[5 * result.shape[0] // 16][i - 1] != 0):
            count += 1
            width.append(0)
            posi.append(i)
        if (result[5 * result.shape[0] // 16][i] == 0):
            width[count - 1] += 1  # 如果在这里报错,是因为背景问题,请让手的背景尽量整洁
    print('the pic width is ', result.shape[1], '\n')
    for i in range(count):
        print('the ', i, 'th', ' ', 'is')
        print('width ', width[i])
        print('posi ', posi[i], '\n')
    print(count, '\n')
    print('area is ', area, '\n')
    cv2.line(result, (0, 5 * result.shape[0] // 16), (214, 5 * result.shape[0] // 16), (0, 0, 0))
    cv2.namedWindow("fcuk")
    cv2.imshow("fcuk", result)
    cv2.waitKey(0)
    # 判定时间
    width_length = 0
    width_jiandao = True
    for i in range(count):
        if width[i] > 45:
            return 2;
        if width[i] <= 20 or width[i] >= 40:
            width_jiandao = False
        width_length += width[i]
    if width_jiandao == True and count == 2:
        return 1;
    if (area < 8500):
        print('shi tou')
        return 0;
    print("width_leng", width_length)
    if (width_length < 35):
        # 这个时候说明照片是偏下的,所以需要重新测定。
        a = []
        posi = []
        width = []
        count = 0
        for i in range(result.shape[1]):
            if (result[11 * result.shape[0] // 16][i] == 0 and result[11 * result.shape[0] // 16][i - 1] != 0):
                count += 1
                width.append(0)
                posi.append(i)
            if (result[11 * result.shape[0] // 16][i] == 0):
                width[count - 1] += 1
    width_length = 0
    width_jiandao = True
    for i in range(count):
        if width[i] > 45:
            print('bu1')
            return 2;
        if width[i] <= 20 or width[i] >= 40:
            width_jiandao = False
        width_length += width[i]
    if width_jiandao == True and count == 2:
        return 1;
    if (area > 14000 or count >= 3):
        print('bu2')
        return 2;
    if (width_length < 110):
        print('jian dao')
        return 1;
    else:
        print('bu3')
        return 2;
def show():
    box = []
    box.append("石头")
    box.append("剪刀")
    box.append("布")
    capture = cv2.VideoCapture(0)
    cv2.namedWindow("camera", 1)
    start_time = time.time()
    while (1):
        ha, img = capture.read()
        end_time = time.time()
        cv2.rectangle(img, (426, 0), (640, 250), (0, 255, 0))
        # (426, 0), (640, 250), 分别代表左上角和右下角的两个坐标点 (0, 255, 0)分别代指B G R
        cv2.putText(img, str(int(5 - (end_time - start_time))), (100, 100), cv2.FONT_HERSHEY_TRIPLEX, 2, 255, 0)
        # cv2.putText(图片, 添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细)
        # cv2.FONT_HERSHEY_SIMPLEX 字体效果 https://www.pianshen.com/article/2216678823/
        cv2.imshow("camera", img)
        if (end_time - start_time > 5):
            break
        if (cv2.waitKey(30) >= 0):
            break
    ha, img = capture.read()
    capture.release()
    cv2.imshow("camera", img)
    img = img[0:210, 426:640]
    cv2.imwrite("wif.jpg", img)
    p1 = judge()
    print('你出的是',box[p1],'\n')
    cv2.destroyAllWindows()
def main():
    i=0
    while (i<5):
        i=i+1
        #  清空屏幕
        os.system('cls')
        show()
main()
编写不易,求个点赞!!!!!!!
“你是谁?”
“一个看帖子的人。”
“看帖子不点赞啊?”
“你点赞吗?”
“当然点了。”
“我也会点。”
“谁会把经验写在帖子里。”
“写在帖子里的那能叫经验贴?”
“上流!”
cheer!!!




















