由于是使用Opencv完成手势识别,所以首先利用颜色特征是对手的颜色进行提取,获得HSV的最小值与最大值。
HSV的提取
HSV颜色空间阈值主要是靠Trackbar
调节阈值和cv2.inRange
来生成掩膜来提取。
这是我写的HSV阈值获取的代码:
import cv2
import numpy as np
def MouseBack(value):
pass
cv2.namedWindow('frams')
cv2.resizeWindow('frams',[400,400])
Video=cv2.VideoCapture(0)
cv2.createTrackbar('minH','frams',0,179,MouseBack)
cv2.createTrackbar('maxH','frams',179,179,MouseBack)
cv2.createTrackbar('minS','frams',0,255,MouseBack)
cv2.createTrackbar('maxS','frams',255,255,MouseBack)
cv2.createTrackbar('minV','frams',0,255,MouseBack)
cv2.createTrackbar('maxV','frams',255,255,MouseBack)
while True:
res,img=Video.read()
hsvimg=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
minh=cv2.getTrackbarPos('minH','frams')
maxh=cv2.getTrackbarPos('maxH','frams')
mins=cv2.getTrackbarPos('minS','frams')
maxs=cv2.getTrackbarPos('maxS','frams')
minv=cv2.getTrackbarPos('minV','frams')
maxv=cv2.getTrackbarPos('maxV','frams')
lowHSV=np.array((minh,mins,minv),np.uint8)
highHSV=np.array((maxh,maxs,maxv),np.uint8)
newimg=cv2.inRange(hsvimg,lowHSV,highHSV)
newimg=cv2.bitwise_and(img,img,mask=newimg)
cv2.imshow('frams',newimg)
if cv2.waitKey(1) == ord('q'):
print(lowHSV)
print(highHSV)
break
Video.release()
cv2.destroyAllWindows()
使用方法:
运行代码之后,从第一个依次调节滑块,使画面中只有手显示出来。然后按Q退出界面打印阈值。
效果如下:
特征提取
准备工作做完,就可以开始进行图像处理了。可以先对单张图片进行处理。
- 首先是进行高斯滤波去噪
- 然后将图像从BGR转换到HSV
- 使用
cv2.inRange
获得掩膜 - 进行形态学操作进一步提取特征
代码如下:
#读取图片
img=cv2.imread(img_path)
#高斯滤波
Gaussimg=cv2.GaussianBlur(img,[5,5],0)
#HSV转换
hsvimg=cv2.cvtColor(Gaussimg,cv2.COLOR_BGR2HSV)
#获得HSV掩膜
maskimg=cv2.inRange(hsvimg,lowHSV,highHSV)
#开运算
kernel=np.ones([3,3],dtype=np.uint8)
closeimg=cv2.morphologyEx(maskimg,cv2.MORPH_CLOSE,kernel,iterations=3)
#腐蚀
kernel=np.ones([5,5],dtype=np.uint8)
dilateimg=cv2.morphologyEx(closeimg,cv2.MORPH_DILATE,kernel,iterations=3)
效果如下:
轮廓绘制
轮廓绘制首先需要寻找轮廓,然后对轮廓特征,比如面积周长进行进一步过滤,最后对轮廓进行逼近,对轮廓进行逼近有两种。
- 轮廓近似
cv2.approxPolyDP
- 轮廓凸包
cv2.convexHull
#寻找轮廓
contours,num=cv2.findContours(newimg,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
#获得轮廓面积
area=cv2.contourArea(contour)
#获得轮廓周长
lenth=cv2.arcLength(contour,True)
#阈值判断
if area>20000 and lenth>1000:
epsilon = 0.02*cv2.arcLength(contour,True)
#轮廓近似
approx1 = cv2.approxPolyDP(contour,epsilon,True)
#轮廓凸包
approx2=cv2.convexHull(contour)
approx1=approx1.reshape(len(approx1),2)
approx2 = approx2.reshape(len(approx2), 2)
approx1=np.array(approx1,dtype=np.int32)
approx2 = np.array(approx2, dtype=np.int32)
cv2.polylines(faimg, [approx1], True, [255, 125, 100], 4, 16)
cv2.polylines(f, [approx2], True, [255, 125, 100], 4, 16)
效果如下:
完整代码
import cv2
import numpy as np
highHSV=np.array([ 15 ,255,255])
lowHSV=np.array([ 0 ,50 ,50])
def img_hand(img):
# if img.shape[0]>1000 and img.shape[1]>1000:
# img=cv2.resize(img,None,fx=0.2,fy=0.2)
faimg=np.copy(img)
img=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
cv2.GaussianBlur(img,[5,5],0)
img=cv2.inRange(img,lowHSV,highHSV)
kernel=np.ones([3,3],dtype=np.uint8)
img=cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel,iterations=1)
kernel=np.ones([5,5],dtype=np.uint8)
newimg=cv2.morphologyEx(img,cv2.MORPH_DILATE,kernel,iterations=1)
contours,num=cv2.findContours(newimg,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
area=cv2.contourArea(contour)
lenth=cv2.arcLength(contour,True)
if area>20000 and lenth>1000:
epsilon = 0.02*cv2.arcLength(contour,True)
approx = cv2.approxPolyDP(contour,epsilon,True)
approx=approx.reshape(len(approx),2)
approx=np.array(approx,dtype=np.int32)
cv2.polylines(faimg, [approx], True, [255, 125, 100], 4, 16)
return faimg
video=cv2.VideoCapture(0)
while video.isOpened():
res,img=video.read()
if res== True:
newimg=img_hand(img)
cv2.imshow('frams',newimg)
if cv2.waitKey(1)==ord('q'):
break
cv2.destroyAllWindows()
video.release()
由于不使用机器学习方法,缺点就是使用时手往前靠一点,头往后靠一点,防止脸的肤色与手混淆,下一章讲解如何识别不同手势。
Python使用Opencv图像处理方法完成手势识别(二)