├── README.md └── shitoujiandaobu.py /README.md: -------------------------------------------------------------------------------- 1 | ## opencv手势识别 ## 2 | 基于opencv实现的石头剪刀布的人机交互小游戏。 3 | 4 | > 虽说原理很简单,但是实现起来,还是有一些复杂的,首先声明代码并非原创,所以有些部分我也不太明了,毕竟自己学习Python+opencv才一个月左右 5 | > 6 | > 上图来看看吧。 7 | > ![石头剪刀布](http://img.blog.csdn.net/20180321100843712?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3dpdGNoX2xvdmVfY2FzZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 8 | 9 | ---------- 10 | 11 | > 能力有限,但也是把这位大神的代码移植到了3.0上,而且是可以运形的,我发现了几个问题: 12 | > 13 | 14 | 1. 他的算法,他区别手势的算法,应该是利用手势遮挡的屏幕的大小来区分,因为在调试过程中,往往遇到识别错误的情况,就是把石头识别成了布。 15 | 2. 他的局限性,就是他的项目对于摄像头背景要求比较高,比如我昨天在宿舍测试,基本都是不准的,因为我看到他膨胀之后的图基本都是乱的,因为宿舍的背景太乱,物体太多,所以调试的时候尽量使背景整洁,最好是一面纯色的墙。 16 | 3. 拓展:我在想可不可以对接Face++api的手势识别做一些小游戏呢,比如手势翻页,手势开关音乐,感觉还是很简单的,估计几十行代码就可以实现了。 17 | 18 | > 总之还是有收获的。 19 | > 我的个人博客--[个人站](http://wenzheng.club) 20 | -------------------------------------------------------------------------------- /shitoujiandaobu.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # -*- coding: cp936 -*- 3 | ###资源网址:http://blog.csdn.net/u013480370/article/details/38389089 这位大哥的时python2.7,不适用于现在的3.0+ 4 | # 并不能用,经过我以下的修改,可以了。本人博客:wenzheng.club 5 | import cv2 6 | import numpy 7 | import time 8 | import random 9 | import os 10 | def judge(): 11 | # 构造一个3×3的结构元素 12 | # return 0 stone ,1 jiandao, 2 bu 13 | img = cv2.imread("wif.jpg", 0) 14 | element = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 11)) 15 | dilate = cv2.dilate(img, element) 16 | erode = cv2.erode(img, element) 17 | # 将两幅图像相减获得边,第一个参数是膨胀后的图像,第二个参数是腐蚀后的图像 18 | result = cv2.absdiff(dilate, erode); 19 | # 上面得到的结果是灰度图,将其二值化以便更清楚的观察结果 20 | retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY); 21 | # 反色,即对二值图每个像素取反 22 | result = cv2.bitwise_not(result); 23 | """ 24 | python-opencv图像的位操作:图像与运算cv2.bitwise_and,图像或运算cv2.bitwise_or,图像非运算cv2.bitwise_not与图像异或运算cv2.bitwise_xor。 25 | 26 | 1)图像与运算-cv2.bitwise_and(src1, src2, dst=None, mask = None) 27 | 28 | 2)图像或运算-cv2.bitwise_or(src1, src2, dst=None, mask = None) 29 | 30 | 3)图像非运算-cv2.bitwise_not(src1, src2, dst=None, mask = None) 31 | 32 | 图像非运算的效果:一个二值图,将黑色转为白色,白色转为黑色。 33 | """ 34 | result = cv2.medianBlur(result, 23) 35 | #cv2.imshow("test",result) 36 | """ 37 | 这里介绍非线性过滤器——中值滤波器。 38 | 由于中值滤波器对消除椒盐现象特别有用。所以我们使用第二篇教程中椒盐函数先对图像进行处理,将处理结果作为示例图片。 39 | 调用中值滤波器的方法与调用其他滤波器的方法类似,如下: 40 | result = cv2.medianBlur(image,5) 41 | 函数返回处理结果,第一个参数是待处理图像, 42 | 第二个参数是孔径的尺寸,一个大于1的奇数。 43 | 比如这里是5,中值滤波器就会使用5×5的范围来计算。即对像素的中心值及其5×5邻域组成了一个数值集,对其进行处理计算,当前像素被其中值替换掉。 44 | """ 45 | a = [] 46 | posi = [] 47 | width = [] 48 | count = 0 49 | area = 0 50 | #这个大神的计算方法,我一时也解析不出来,应该是依靠手势面积来计算的 51 | for i in range(result.shape[1]): 52 | for j in range(result.shape[0]): 53 | if (result[j][i] == 0): 54 | area += 1 55 | for i in range(result.shape[1]): 56 | if (result[5 * result.shape[0] // 16][i] == 0 and result[5 * result.shape[0] // 16][i - 1] != 0): 57 | count += 1 58 | width.append(0) 59 | posi.append(i) 60 | if (result[5 * result.shape[0] // 16][i] == 0): 61 | width[count-1]+= 1 # 如果在这里报错,是因为背景问题,请让手的背景尽量整洁 62 | #这里是调试用的代码可以注释掉 63 | print ('the pic width is ',result.shape[1],'\n') 64 | for i in range(count): 65 | print ('the ',i,'th',' ','is') 66 | print ('width ',width[i] ) 67 | print ('posi ',posi[i],'\n') 68 | print (count,'\n') 69 | print ('area is ',area,'\n') 70 | cv2.line(result,(0,5*result.shape[0]//16),(214,5*result.shape[0]//16),(0,0,0)) 71 | cv2.namedWindow("fcuk") 72 | cv2.imshow("fcuk",result) 73 | cv2.waitKey(0) 74 | #这里是调试用的代码可以注释掉 75 | # 判定时间 76 | width_length = 0 77 | width_jiandao = True 78 | for i in range(count): 79 | if width[i] > 45: 80 | # print 'bu1'; 81 | return 2; 82 | if width[i] <= 20 or width[i] >= 40: 83 | width_jiandao = False 84 | width_length += width[i] 85 | if width_jiandao == True and count == 2: 86 | return 1; 87 | if (area < 8500): 88 | print ('shi tou') 89 | return 0; 90 | print("width_leng", width_length) 91 | if (width_length < 35): 92 | # 这个时候说明照片是偏下的,所以需要重新测定。 93 | a = [] 94 | posi = [] 95 | width = [] 96 | count = 0 97 | for i in range(result.shape[1]): 98 | if (result[11 * result.shape[0] // 16][i] == 0 and result[11 * result.shape[0] // 16][i - 1] != 0): 99 | count += 1 100 | width.append(0) 101 | posi.append(i) 102 | if (result[11 * result.shape[0] // 16][i] == 0): 103 | width[count - 1] += 1 104 | """ 105 | print 'the pic width is ',result.shape[1],'\n' 106 | for i in range(count): 107 | print 'the ',i,'th',' ','is'; 108 | print 'width ',width[i] 109 | print 'posi ',posi[i],'\n' 110 | print count,'\n' 111 | print 'area is ',area,'\n' 112 | """ 113 | width_length = 0 114 | width_jiandao = True 115 | for i in range(count): 116 | if width[i] > 45: 117 | print ('bu1') 118 | return 2; 119 | if width[i] <= 20 or width[i] >= 40: 120 | width_jiandao = False 121 | width_length += width[i] 122 | if width_jiandao == True and count == 2: 123 | return 1; 124 | if (area > 14000 or count >= 3): 125 | print ('bu2') 126 | return 2; 127 | if (width_length < 110): 128 | print ('jian dao') 129 | return 1; 130 | else: 131 | print('bu3') 132 | return 2; 133 | def game(): 134 | fuck = [] 135 | fuck.append("石头") 136 | fuck.append("剪刀") 137 | fuck.append("布") 138 | capture = cv2.VideoCapture(0) 139 | cv2.namedWindow("camera", 1) 140 | start_time = time.time() 141 | print("给你5秒的时间把手放到方框的位置\n") 142 | while (1): 143 | ha, img = capture.read() 144 | end_time = time.time() 145 | cv2.rectangle(img, (426, 0), (640, 250), (170, 170, 0)) 146 | cv2.putText(img, str(int((5 - (end_time - start_time)))), (100, 100), cv2.FONT_HERSHEY_SIMPLEX, 2, 255) 147 | cv2.imshow("camera", img) 148 | if (end_time - start_time > 5): 149 | break 150 | if (cv2.waitKey(30) >= 0): 151 | break 152 | ha, img = capture.read() 153 | capture.release() 154 | cv2.imshow("camera", img) 155 | img = img[0:210, 426:640] 156 | cv2.imwrite("wif.jpg", img) 157 | p1 = judge() 158 | pc = random.randint(0, 2) 159 | # print p1,' ',pc,'\n' 160 | print("你出的是", fuck[p1], " 电脑出的是", fuck[pc], "\n") 161 | cv2.destroyAllWindows() 162 | if (p1 == pc): 163 | print("平局\n") 164 | return 0 165 | if ((p1 == 0 and pc == 1) or (p1 == 1 and pc == 2) or (p1 == 2 and pc == 0)): 166 | print('你赢了\n') 167 | return 1 168 | else: 169 | print('你输了\n') 170 | return -1 171 | def main(): 172 | you_win = 0 173 | pc_win = 0 174 | print("这是通过摄像头来玩的剪刀石头布的游戏,请输入回车开始游戏\n") 175 | #s = raw_input() 176 | while (1): 177 | print("比分(玩家:电脑) ", you_win, ":", pc_win, '\n') 178 | #s = raw_input() 179 | os.system('cls') 180 | ans = game() 181 | if (ans == 1): 182 | you_win += 1 183 | elif (ans == -1): 184 | pc_win += 1 185 | print("为了减少误判,请尽可能将手占据尽可能大的框框") 186 | 187 | main() 188 | #judge() 189 | # ############################################################################# 190 | --------------------------------------------------------------------------------