前言:在各类的智能识别中,手势识别是比较简单的一种了。本人大二,在大一下学期做了一个简单的树莓派摄像头手势识别的程序。当初选择opencv这个库是因为感觉它较skimage对新手比较友好,现在在学图像识别之前想把手势识别再看一遍,且思且记,以便日后复习。
好了废话不多说,上干货!
首先,我们要对手势识别的基本步骤做一个了解:打开摄像头 ——>截取图像手势——>图像处理——>手势模型匹配预测——>给出判断。再本篇博客中我重点介绍的是图像处理这个环节,因为这是最基本的,手势模型预测可能会涉及机器学习(还是有点难度的)。
第一步:去噪即滤波,因为我们截取的图像可能会包含很多无用的干扰信息,所以这一步是对图像进行预处理。代码块如下:
blur = cv2.bilateralFilter(img,9,75,75)
主要函数方法介绍:1.cv2的bilateral Filter方法含有四个参数,img是图像,9代表处理面积,75是空间高斯标准差和相似性高斯函数标准差(这边至于为啥是75应该是个数据我们不在深究)
效果图:
第二步:二值化黑白处理
def binaryMask(frame,x0,y0,width,height):
cv2.rectangle(frame,(x0,y0),(x0+width,y0+height),(0,255,0))
roi = frame[y0:y0+height,x0:x0+width]
cv2.imshow("roi",roi)#读取roi文件
res = skinMask(roi)
cv2.imshow("res",res)
这边定义一个函数,有四个参数分别是:frame要处理的图像,x0和y0要处理图片的左上角的坐标,width和heignt是你要处理图片的长和宽。该函数的作用就是绘制一个手势框图来获取最初的手势图像。
主要函数方法介绍:1.rectangle绘制一个矩形区域即绘制手势框图。
2.fream绘制手势框图。
3.imshow显示手势框图。
第三步:肤色检测,所谓肤色检测顾名思义就是把人皮肤的颜色的区域保留,不是人皮肤颜色的区域掩膜成黑色。
具体的代码如下:
def skinMask(roi):
rgb = cv2.cvtColor(roi, cv2.COLOR_BGR2RGB) #转换到RGB空间
(R,G,B) = cv2.split(rgb) #获取图像每个像素点的RGB的值,即将一个二维矩阵拆成三个二维矩阵
skin = np.zeros(R.shape, dtype = np.uint8) #掩膜
(x,y) = R.shape #获取图像的像素点的坐标范围
for i in range(0, x):
for j in range(0, y):
#判断条件,不在肤色范围内则将掩膜设为黑色,即255
if (abs(R[i][j] - G[i][j]) > 15) and (R[i][j] > G[i][j]) and (R[i][j] > B[i][j]):
if (R[i][j] > 95) and (G[i][j] > 40) and (B[i][j] > 20) \
and (max(R[i][j],G[i][j],B[i][j]) - min(R[i][j],G[i][j],B[i][j]) > 15):
skin[i][j] = 255
elif (R[i][j] > 220) and (G[i][j] > 210) and (B[i][j] > 170):
skin[i][j] = 255
res = cv2.bitwise_and(roi,roi, mask = skin) #图像与运算
return res
定义函数skinMask参数为roi,想用numpy中的zeros方法返回一个给定形态和类型的用0填充的数组,YCrCb则是返回的roi把它转换到RGB空间。因为imread读取的图片是以BGR的形式的。
这边我提一句奥,那边if里面的条件判断语句是在一般的光线下,所有的的RGB特定数据,也就是说在强光或者是很暗的环境下手势识别的效果可能没有那么好。
一般光照条件下的判断条件:R>95 AND G>40 B>20 AND MAX(R,G,B)-MIN(R,G,B)>15 AND ABS(R-G)>15 AND R>G AND R>B
在侧光拍摄环境下,R>220 AND G>210 AND B>170 AND ABS(R-G)<=15 AND R>B AND G>B
效果图:
第四步:轮廓提取
camera = cv2.VideoCapture(0)
camera.set(10, 150)
while camera.isOpened():
ret, frame = camera.read()
threshold = cv2.getTrackbarPos('trh1', 'trackbar')
frame = cv2.bilateralFilter(frame, 5, 50, 100) # smoothing filter
frame = cv2.flip(frame, 1) # flip the frame horizontally
cv2.rectangle(frame, (int(cap_region_x_begin * frame.shape[1]), 0),
(frame.shape[1], int(cap_region_y_end * frame.shape[0])), (255, 0, 0), 2)
cv2.imshow('original', frame)
# Main operation
if isBgCaptured == 1: # this part wont run until background captured
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]] # clip the ROI
# cv2.imshow('mask', img)
# convert the image into binary image
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('ori', 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): # find the biggest contour (according to area)
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)
cv2.imshow('output', drawing)
效果图:
总结:实现了简单的图像处理和轮廓绘制。为接下来的特征提取打下了基础