本
用的开发平台是
![]()
以下是对做去年视觉作业的一些笔记分享:
主要做的是 基于OpenCV的动态手势跟踪识别,根据分类识别手势,并依据“剪刀石头布”规则瞬间做出赢人类玩家的反应的程序。
就是一个机器人跟你玩剪刀石头布,当你快出来的时候,机器识别你的手势,迅速给出反应。
以下是实现的效果:
https://www.zhihu.com/video/1027966682396049408
以下是实现的原理:
1. 滤波去噪
由于边缘检测容易受到噪音影响,所以第一步是使用高斯滤波器去除噪声。
2. 去除背景,提取手部的轮廓
2.1方法一:肤色检测手部区域
利用肤色来检测手部是手部检测最直接的方法。皮肤颜色稳定,不轻易受到缩放、平移和旋转的影响,且对图像的尺寸、拍摄方向和角度的依赖性较小
颜色空间主要有两种,分别是YCbCr颜色空间和HSV颜色空间,根据Enamin D. Zarit等人对肤色在这些彩色空间分布的研究,以及在检测中性能的分析,本文通过对手部皮肤颜色进行特征分析(选取黄种人的肤色),选用HSV颜色空间进行手部区域检测。
RGB颜色空间和YCbCr颜色空间的混合肤色检测器。像素值满足如下条件:
然而,利用python编程的结果很卡顿。
结论分析:有点卡顿,这与python计算能力有关,若用c++,则没有此现象,故使用方法二。
此方法也有一个技术难点就是生成新的二值图像时,需要注意图像格式的问题。后来我使用了深复制的思想,完整地实现了。
2.2方法二:灰度图像,阈值分割:
图像阈值化分割是一种传统的最常用的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和应用最广泛的分割技术。它特别适用于目标和背景占据不同灰度级范围的图像。难点在于如何选择一个合适的阈值实现较好的分割。
我们将RGB图像转变为灰度图像,便于后续处理。
2.3 方法三:k-means分割
Kmeans是最简单的聚类算法之一,应用十分广泛,Kmeans以距离作为相似性的评价指标,其基本思想是按照距离将样本聚成不同的簇,两个点的距离越近,其相似度就越大,以得到紧凑且独立的簇作为聚类目标。
缺点:易受光源影响,参数需根据光源修改。
3. 找出轮廓:
利用Opencv中通过使用findContours函数,简单几个的步骤就可以检测出物体的轮廓。
4. 凸包
凸包(凸壳)是指如果在集合A内连接任意两个点的直线段都在A的内部,则称集合A是凸形的。简单点理解,就是一个多边型,没有凹的地方。凸包(凸壳)能包含点集中所有的点
5. 求moment,后求质心(用mean shift的方法求质心)
计算手指的个数,来识别手势特征并进行跟踪。首先要提取手掌轮廓,计算轮廓形状特征有:轮廓的质心、轮廓的最短最长径长、轮廓的外接圆(圆心和半径)、轮廓的周长和面积、轮廓在图像中的矩形框位置、轮廓的外包络点集合、轮廓的点集合、轮廓的各阶矩、轮廓的有效的特征向量的提取、手指指尖的定位。手的位置特征是指手掌的质心位置,针对二维图像,质心位置是可以通过计算零阶距和X、Y的一阶距得到的。假设二值化之后的图像为 I ( X , Y ) ,质心 ( x c , y c ) 计算公式如下:
6. 标出手指和手掌
质心处即为手掌,手指求法如下:


7. 判断手势和形状
7.1方法一:特征点
把提取的特征点和手势字典进行对比,然后判断手势和形状
7.2方法二:手指的个数
根据图像中凹凸点中的 (开始点, 结束点, 远点)的坐标, 利用余弦定理计算两根手指之间的夹角, 其必为锐角, 根据锐角的个数判别手势。
7.3方法三:轮廓的匹配程度
函数 cv2.matchShape() 可以帮我们比较两个形状或轮廓的相似度。如果返回值越小,匹配越好。它是根据 Hu 矩来计算的。
8. 输出结果:
动态输出克制当前输出的手势(依据角刀石头布规则)
以下是实现的代码
import cv2 import numpy as np import math ##输入结果库 pic_1 = cv2.imread('v1.png') pic_2 = cv2.imread('v2.png') pic_3 = cv2.imread('v3.png') ##摄像机输入 cap = cv2.VideoCapture(0) while( cap.isOpened() ) : ret,img = cap.read() gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray,(5,5),0) ##阈值分割 ret,thresh1 = cv2.threshold(blur,70,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) aa,contours, hierarchy = cv2.findContours(thresh1,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) ##深复制 drawing = np.zeros(img.shape,np.uint8) max_area=0 ##找轮廓 for i in range(len(contours)): cnt=contours[i] area = cv2.contourArea(cnt) if(area>max_area): max_area=area ci=i cnt=contours[ci] hull = cv2.convexHull(cnt)#0621 ##meanshift求质心 moments = cv2.moments(cnt) #print len(cnt) #print hull ##求质心公式 if moments['m00']!=0: cx = int(moments['m10']/moments['m00']) # cx = M10/M00 cy = int(moments['m01']/moments['m00']) # cy = M01/M00 centr=(cx,cy) cv2.circle(img,centr,5,[0,0,255],2) #cv2.circle(img,centr,5,[0,255,255],2)#0621 #cv2.rectangle(original, p1, p2, (77, 255, 9), 1, 1)#0621 cv2.drawContours(drawing,[cnt],0,(255,255,0),2) #cv2.drawContours(drawing,[hull],0,(0,0,255),2) cnt = cv2.approxPolyDP(cnt,0.01*cv2.arcLength(cnt,True),True) hull = cv2.convexHull(cnt,returnPoints = False) ndefects = 0 #0622 for counting finger_number ###根据图像中凹凸点中的 (开始点, 结束点, 远点)的坐标, 可利用余弦定理计算两根手指之间的夹角, if(1): defects = cv2.convexityDefects(cnt,hull) #mind=0 #maxd=0 for i in range(defects.shape[0]): s,e,f,d = defects[i,0] start = tuple(cnt[s][0]) end = tuple(cnt[e][0]) far = tuple(cnt[f][0]) #dist = cv2.pointPolygonTest(cnt,centr,True) a = np.sqrt(np.square(start[0]-end[0])+np.square(start[1]-end[1]))#0622 b = np.sqrt(np.square(start[0]-far[0])+np.square(start[1]-far[1]))#0622 c = np.sqrt(np.square(end[0]-far[0])+np.square(end[1]-far[1]))#0622 angle = math.acos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c)) # * 57#0622 ##其必为锐角, 根据锐角的个数判别手势 if angle <= math.pi/2 :#90:#0622 ndefects = ndefects + 1#0622 #cv2.line(img,start,end,[0,255,255],2) cv2.line(img,start,centr,[0,255,255],2) cv2.circle(img,start,20,[0,255,255],4) #cv2.circle(img,end,20,[0,255,0],4) cv2.circle(img,far,5,[0,0,255],-1) #print(i) print ndefects i=0 if ndefects == 0: print 07 cv2.imshow("RESULT",pic_3) else: if ndefects == 1: print 27 cv2.imshow("RESULT",pic_2) else: if ndefects == 4: print 57 cv2.imshow("RESULT",pic_1) else: print 0000 cv2.imshow('output',drawing) cv2.imshow('input',img) k = cv2.waitKey(10) #Esc if k == 27: break
以下这份是加了肤色检测的程序
import cv2 import numpy as np cap = cv2.VideoCapture(0) while( cap.isOpened() ) : ret,img = cap.read() # load an original image #img = cv2.imread(imgFile) rows,cols,channels = img.shape # convert color space from rgb to ycbcr imgYcc = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB) # convert color space from bgr to rgb img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # prepare an empty image space imgSkin = np.zeros(img.shape, np.uint8) # copy original image/深复制 imgSkin = img.copy() for r in range(rows): for c in range(cols): # non-skin area if skin equals 0, skin area otherwise skin = 0 # get values from rgb color space R = img.item(r,c,0) G = img.item(r,c,1) B = img.item(r,c,2) # get values from ycbcr color space Y = imgYcc.item(r,c,0) Cr = imgYcc.item(r,c,1) Cb = imgYcc.item(r,c,2) # skin color detection if R > G and R > B: if (133 <= Cr and Cr <= 173) and (77 <= Cb and Cb <= 127): skin = 1 # print 'Skin detected!' if 0 == skin: imgSkin.itemset((r,c,0),0) imgSkin.itemset((r,c,1),0) imgSkin.itemset((r,c,2),0) if 1 == skin: imgSkin.itemset((r,c,0),255) imgSkin.itemset((r,c,1),255) imgSkin.itemset((r,c,2),255) cv2.imshow("RESULT",imgSkin) k = cv2.waitKey(10) if k == 27: break
以下是一些学习心得和笔记:
import cv2 #opencv函數大多數都在cv2模塊內,並不是針對2.x.x版本的
这些程序的文件格式都是.py,是用python语言编写的
只需要在文件所在路径>打开Terminal>输入:python 文件名.py即可运行
结束运行只需要Ctrl+C或者Ctrl+Z
#查询 库函数
https://docs.opencv.org/3.4.1/d2/d96/tutorial_py_table_of_contents_imgproc.html
#官方教程:
我一般看3.4.1版本/python的
https://docs.opencv.org/3.4.1/d6/d00/tutorial_py_root.html
numpy:
还挺重要和挺常用的
MJ: https://www.yiibai.com/numpy/numpy_array_from_numerical_ranges.html net: https://blog.csdn.net/scorpion_zs/article/details/52982872 numpy.zeros(np.zeros)使用方法--python学习笔记31 https://blog.csdn.net/qq_26948675/article/details/54318917 ndarray :n维数组 py文件名字不能叫numpy.py x = np.arange(5) print x result(in terminal): [0 1 2 3 4] ... numpy.ndarray.item np.random.randint产生一个范围内的数据 owners = np.random.randint(15000, high=73001, size=398, dtype='l') 官方文档中给出的用法是:numpy.random.randint(low,high=None,size=None,dtype) 生成在半开半闭区间[low,high)上离散均匀分布的整数值;若high=None,则取值区间变为[0,low) eg: x = np.random.randint(9, size=(3, 3)) print x print x.item(3) print x.item(7) print x.item((0, 1)) 默认high=none 取值区间[0,9),生成3*3的数组 比如随机生成了: [[5 7 3] [6 6 7] [4 5 5]] print x.item(3)即从0位开始数,0;1;2;;3;4;5;;6;7;8; “3”对应的是6 print x.item(7)对应是5 print x.item((0, 1))即从第0行第0列开始,则0行1列是7 Z = img.reshape((-1,3)) numpy.reshape https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.reshape.html https://blog.csdn.net/qq_28618765/article/details/78083895 # convert to np.float32 Z = np.float32(Z) Numpy 中文用户指南 3.1 数据类型: https://segmentfault.com/a/1190000004681332
#數組
b=[[1,1],[1,1]]#定义了一个2*2的,且初始为0的二维数组? from Python数组定义方法_python_脚本之家 c = [[0]*10]*10 #定义10*10初始为0的二维数组 src = np.zeros((100, 100)) #使用np.zeros可以构造一个相应大小的全部由0组成的矩阵 100行100列 這用了numpy src[49][49] = 255 #第49行49列的元素爲255
#讀取攝像頭
看了很多教程,最后发现只要下面几句是必要的:
just need one order: cap = cv2.VideoCapture(1) while( cap.isOpened() ) : ret,img = cap.read() then use the img
#改变inshow的位置
这个找了好久才找到这个解决方案,很不错。
https://www.2cto.com/kf/201611/568821.html中的 cv2.imshow('draw_result',result) cv2.moveWindow('draw_result',1000,500)
#关于plot的一点知识
solution:https://blog.csdn.net/u011333059/article/details/38079833 代码如下: [python] view plain copy import matplotlib.pyplot as plt input_image = root/to/your/image plt.imshow(input_image) 运行后发现图片无法显示, 解决方法是: 首先import pylab 在需要显示图片的代码下一行加上 pylab.show() 这种方法可以显示图片,但是不关掉这个图片窗口,代码就不会继续运行下去,暂时还没有找到更合适的方法。 希望有知道解决方法的可以告诉我,谢谢! 问题来了,为什么显示的颜色与原图不同呢? 后来网上搜索后才知道,对于opencv的像素是BGR顺序,然而matplotlib所遵循的是RGB顺序。 opencv的一个像素为:[B,G,R] ,matplotlib的一个像素为:[R,G,B]。这就是为什么本来发红的区域变得有些发蓝了。 from 博客 :https://www.cnblogs.com/visionfeng/p/6094423.html
#关于wait.Key的一点笔记
from https://stackoverflow.com/questions/14494101/using-other-keys-for-the-waitkey-function-of-opencv You can use ord() function in Python for that. For example, if you want to trigger 'a' key press, do as follows : if cv2.waitKey(33) == ord('a'): print "pressed a" To find the key value for any key is to print the key value using a simple script as follows : import cv2 img = cv2.imread('sof.jpg') # load a dummy image while(1): cv2.imshow('img',img) k = cv2.waitKey(33) if k==27: # Esc key to stop break elif k==-1: # normally -1 returned,so don't print it continue else: print k # else print its value With this code, I got following values : Upkey : 2490368 DownKey : 2621440 LeftKey : 2424832 RightKey: 2555904 Space : 32 Delete : 3014656
#补充一个自己和喜欢的小功能
将两个不同角度拍摄的同一个场景的照片拼接为一张,以下是拼接效果,非常好玩。可以用于照片合成,将两个不同时间的自己合成到同一张照片上。

代码如下:
import cv2 stitcher = cv2.createStitcher(False) foo = cv2.imread("v1.jpg") bar = cv2.imread("v2.jpg") result = stitcher.stitch((foo,bar)) cv2.imwrite("v3.jpg", result[1]) cv2.imshow("v3.jpg", result[1]) cv2.waitKey(0)
原文始发于:OpenCV on ubuntu using Python
主题测试文章,只做测试使用。发布者:熱鬧獨處,转转请注明出处:http://www.cxybcw.com/11780.html