``第一步: 读取图片或摄像头,对图像或摄像头进行处理(形态学操作,肤色检测等等)提取手势二值图像.
ret, frame = capture.read() # 读取摄像头
# frame = cv.flip(frame, 1)
fgbg = cv.createBackgroundSubtractorMOG2() # 利用BackgroundSubtractorMOG2算法消除背景
# fgmask = bgModel.apply(frame)
fgmask = fgbg.apply(frame)
# kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
# res = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
kernel = np.ones((5, 5), np.uint8)
fgmask = cv.erode(fgmask, kernel, iterations=1) # 膨胀
res = cv.bitwise_and(frame, frame, mask=fgmask)
ycrcb = cv.cvtColor(res, cv.COLOR_BGR2YCrCb) # 分解为YUV图像,得到CR分量
(_, cr, _) = cv.split(ycrcb)
cr1 = cv.GaussianBlur(cr, (5, 5), 0) # 高斯滤波
_, skin = cv.threshold(cr1, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU) # OTSU图像二值化
其中,skin代表了手势的二值图像 可以imshow 一下看一下, 我的大致如下方:
第二步: 获取手势轮廓`,绘制手势轮廓的矩形框
for i, contour in enumerate(contours): # 获取轮廓
cv.drawContours(frame[0:350, 380:700], contours, i, (255, 0, 0), 1) # 绘制轮廓
x, y, w, h = cv.boundingRect(contour)
# center = (int(x), int(y))
cv.rectangle(frame[0:350, 380:700], (x, y), (x + w, y + h), (100, 100, 0), 1)
第三步: 计算手势凹凸包,并绘制轮廓(凸包连线)
hull = cv.convexHull(contour, True, returnPoints=False) # 获得凸包点 x, y坐标
defects = cv.convexityDefects(contour, hull) # 计算轮廓的凹点
if defects is not None: # 重要!
for i in range(defects.shape[0]):
s, e, f, d = defects[i, 0]
# float(s)
# float(e)
# float(f)
# float(d)
start = tuple(contour[s][0]) # 起点
end = tuple(contour[e][0]) # 终点
far = tuple(contour[f][0]) # 最远点
a = _get_eucledian_distance(start, end)
b = _get_eucledian_distance(start, far)
c = _get_eucledian_distance(end, far)
angle = math.acos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c))
cv.line(frame[0:350, 380:700], start, end, [255, 255, 0], 2)
cv.circle(frame[0:350, 380:700], far, 5, [0, 0, 255], -1)
!!!绘制凹凸包那有一步 注释重要的那一步 真的很重要 是个坑表情包没有那一步可能会出现凹包函数返回值为None的情况 导致错误!!!
第四步: 再 imshow 就完成了 大致效果图(用图片的效果明显)
最后要实现手势的识别可以在凹凸包的点上做文章(方法有很多但是检测识别率就不同了这里就不写出来本小白测试的方法了…)
说明:本文有些简单步骤省略了 大体主架完整。需要完整代码的请点击完整代码
有什么问题欢迎留言讨论噢! 谢谢!