``第一步: 讀取圖檔或攝像頭,對圖像或攝像頭進行處理(形态學操作,膚色檢測等等)提取手勢二值圖像.
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 就完成了 大緻效果圖(用圖檔的效果明顯)
最後要實作手勢的識别可以在凹凸包的點上做文章(方法有很多但是檢測識别率就不同了這裡就不寫出來本小白測試的方法了…)
說明:本文有些簡單步驟省略了 大體主架完整。需要完整代碼的請點選完整代碼
有什麼問題歡迎留言讨論噢! 謝謝!