OpenCV人脸检测
OpenCV提供了多种人脸检测方法,包括基于Haar级联的传统方法和基于深度学习的现代方法。Haar级联是一种经典的机器学习算法,适用于实时应用,因为它可以快速处理图像。
-
级联(Cascade)结构:
- Cascade分类器由多个简单的分类器组成,这些分类器按照一定的顺序级联起来。
- 每个分类器都是一个弱分类器,只能对图像中的一部分区域进行简单的判断。
- 当图像或图像中的某个区域通过所有分类器的判断时,才认为它是一个目标。
深度学习方法,如MTCNN(Multi-Task Cascade Convolutional Networks)和YOLOv3,提供了更高的检测准确度。
本文使用的是Haar特征的Cascade分类器(haarcascade_frontalface_default.xml
)进行人脸检测。这是一种基于机器学习的方法,可以快速检测图像中的人脸。
detectMultiScale
方法用于在灰度图像上检测多尺度的人脸,返回检测到的人脸矩形框坐标。人流
# 加载人脸检测器(这里使用的是Haar特征的Cascade分类器)
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
运动检测
通过比较连续两帧图像的灰度差异来检测运动。这通过计算两帧图像之间的绝对差(cv2.absdiff
)实现,对差异图像进行阈值处理(cv2.threshold
),将差异较大的区域(即运动区域)转换为白色,其他区域为黑色。使用形态学操作(如膨胀cv2.dilate
)来增强运动区域的特征,以便更容易地通过轮廓检测找到运动物体。cv2.findContours
方法用于检测并提取图像中的轮廓,这些轮廓对应于运动物体。
# 如果没有背景图像就将当前帧当作背景图片
if pre_frame is None:
pre_frame = gray_pic
else:
# absdiff把两幅图的差的绝对值输出到另一幅图上面来
img_delta = cv2.absdiff(pre_frame, gray_pic)
# threshold阈值函数(原图像应该是灰度图,对像素值进行分类的阈值,当像素值高于(有时是小于)阈值时应该被赋予的新的像素值,阈值方法)
thresh = cv2.threshold(img_delta, 30, 255, cv2.THRESH_BINARY)[1]
# 用一下腐蚀与膨胀
thresh = cv2.dilate(thresh, None, iterations=2)
# findContours检测物体轮廓(寻找轮廓的图像,轮廓的检索模式,轮廓的近似办法)
contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
人流监测
代码能根据人脸检测结果进行相应的处理,若检测到人脸则记录,若当前未检测到人脸但之前检测到过,则认为有人脸移出,并进行计数和显示提示信息。总的来说,可以实现通过摄像头进行实时视频处理,包括运动检测和人脸检测,并对人脸的进入和移出进行计数和提示。
该代码可实现以下功能:
- 结合人脸检测和运动检测结果,当检测到人脸且之前未检测到人脸时,认为有人进入。
- 通过维护一个计数器
J
来跟踪进入的人数。 - 如果之前检测到人脸但当前未检测到,且之前进入的人数大于0,则认为有人离开,但在这个实现中只统计了进入的人数。
# 保存图像 TI = time.strftime('%Y-%m-%d', time.localtime(time.time())) cv2.imwrite("D:/aopencv/" + TI + '.jpg', frame) # 读取刚才保存的图像进行人脸检测 saved_image = cv2.imread("D:/aopencv/" + TI + '.jpg') gray_saved = cv2.cvtColor(saved_image, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray_saved, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) # 检测到人脸 if len(faces) > 0: print("检测到人脸!") last_detected_faces +=1 else: # 如果上次检测到的人脸数量大于0,且当前没有检测到人脸,则认为有人脸移出 if last_detected_faces > 0: cv2.putText(frame, f"enter {J} people", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) J += 1 print(f"进入{J}个人") last_detected_faces = 0 # 重置检测到的人脸数量
完整代码,该代码可完美运行:
-
import cv2 import time # 加载人脸检测器(这里使用的是Haar特征的Cascade分类器) face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') # 定义摄像头对象,其参数0表示第一个摄像头 camera = cv2.VideoCapture(0) # 测试用,查看视频size width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT)) size = width, height # 打印一下分辨率 print(repr(size)) # 设置一下帧数和前背景 fps = 5 pre_frame = None J=0 C=0 last_detected_faces = 0 while (1): start = time.time() # 读取视频流 ret, frame = camera.read() # 转灰度图 gray_pic = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) if not ret: print("打开摄像头失败") break end = time.time() cv2.imshow("capture", frame) # 运动检测部分 seconds = end - start if seconds < 1.0 / fps: time.sleep(1.0 / fps - seconds) gray_pic = cv2.resize(gray_pic, (480, 480)) # 用高斯滤波进行模糊处理 gray_pic = cv2.GaussianBlur(gray_pic, (21, 21), 0) # 如果没有背景图像就将当前帧当作背景图片 if pre_frame is None: pre_frame = gray_pic else: # absdiff把两幅图的差的绝对值输出到另一幅图上面来 img_delta = cv2.absdiff(pre_frame, gray_pic) # threshold阈值函数(原图像应该是灰度图,对像素值进行分类的阈值,当像素值高于(有时是小于)阈值时应该被赋予的新的像素值,阈值方法) thresh = cv2.threshold(img_delta, 30, 255, cv2.THRESH_BINARY)[1] # 用一下腐蚀与膨胀 thresh = cv2.dilate(thresh, None, iterations=2) # findContours检测物体轮廓(寻找轮廓的图像,轮廓的检索模式,轮廓的近似办法) contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for c in contours: # 设置敏感度 # contourArea计算轮廓面积 if cv2.contourArea(c) < 1000: continue else: # 保存图像 TI = time.strftime('%Y-%m-%d', time.localtime(time.time())) cv2.imwrite("D:/aopencv/" + TI + '.jpg', frame) # 读取刚才保存的图像进行人脸检测 saved_image = cv2.imread("D:/aopencv/" + TI + '.jpg') gray_saved = cv2.cvtColor(saved_image, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray_saved, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) # 检测到人脸 if len(faces) > 0: print("检测到人脸!") last_detected_faces +=1 else: # 如果上次检测到的人脸数量大于0,且当前没有检测到人脸,则认为有人脸移出 if last_detected_faces > 0: cv2.putText(frame, f"enter {J} people", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) J += 1 print(f"进入{J}个人") last_detected_faces = 0 # 重置检测到的人脸数量 cv2.imshow("capture", frame) pre_frame = gray_pic cv2.putText(frame, f"enter {J} people", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.imshow("capture", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break # release()释放摄像头 camera.release() # destroyAllWindows()关闭所有图像窗口 cv2.destroyAllWindows()