背景
假设要实现人的面部或手部的运动检测。
方法:先对摄像头输入的图像进行处理,只保留符合人体肤色的像素,再计算视频中此帧和上一帧图像数据的差异。差异超过一定阈值即认为画面中的人体产生了运动。
代码
from cv2 import cv2
import matplotlib.pyplot as plt
def cvt(img): # 肤色提取
YCrCb = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB) # 转换至YCrCb空间
# 人体肤色在YCrCb空间的粗略映射
skin = cv2.inRange(YCrCb, np.array([0, 133, 77]), np.array([255, 173, 127]))
res = cv2.bitwise_and(YCrCb, YCrCb, mask=skin)
return res
cap = cv2.VideoCapture(0)
cap.set(3, 640)
cap.set(4, 480)
firstlaunch = True # 首次运行的标志
prev_frame = None # 用于保存上一帧图像
plt.ion() # 交互模式,使绘制的直方图可以不断变化
while True:
ret, frame_read = cap.read()
frame_read = cv2.flip(frame_read, 1)
frame_read = cv2.medianBlur(frame_read, 5)
frame_read = cv2.GaussianBlur(frame_read, (5, 5), 0)
# 提取肤色,然后再转化为灰度图
present_frame = cvt(frame_read)
present_frame = cv2.cvtColor(present_frame, cv2.COLOR_YCrCb2BGR)
present_frame = cv2.cvtColor(present_frame, cv2.COLOR_BGR2GRAY)
if not firstlaunch:
sub = cv2.subtract(present_frame, prev_frame)
hist = cv2.calcHist([sub], [0], None, [256], [0, 255])
# 只统计灰度值10-100的部分,这只是随意取的
# 可以自己实验调整找到适合的范围
diff = np.sum(hist[10:100]) # 统计产生变化的像素点个数
print(diff)
plt.plot(hist[6:100], color='red') # 绘制直方图
plt.ylim([0, 2000])
plt.show()
cv2.imshow('preview', sub)
else:
firstlaunch = False # 首次运行的标志
cv2.waitKey(10)
if not firstlaunch:
plt.clf() # 清除直方图图像
prev_frame = present_frame.copy() # 存储此帧
plt.close('all')
cap.release()
效果
当人头部基本保持静止时,统计数值基本不超过10000
头部静止时的直方图,此时统计值为1653
当人的头部或手部产生运动时,统计数值上升为一万至数万。
头部运动时的直方图,此时统计值为13353