导读
本期将介绍使用OpenCV实现人脸口罩佩戴检测的详细步骤,手把手教你做一个效果还可以的口罩佩戴检测系统。
口罩检测思路与常用方法
在动手实现人脸口罩佩戴检测系统之前,我们常常会思考如何检测一个人是否佩戴口罩?方法很多,这里列举几个比较典型的方法:
① 用深度学习目标检测的方法,标注佩戴口罩与未佩戴口罩两类,然后选择合适的网络去训练检测,比如YoloV5等;
② 先检测人脸,然后将人脸ROI图像做分类,分为佩戴口罩和未佩戴口罩两类,结合人脸检测和分类网络实现;
③ 用OpenCV实现,先检测人脸,然后判断人脸ROI是否有佩戴口罩的特征;
④ 其他更好的方法。
本文使用第③种方法,选择合适的人脸检测方法与口罩佩戴判断方法即可。
效果展示
演示从视频中检测人脸是否佩戴口罩,未佩戴口罩将语音提示,避免露脸,测试视频素材来源于网络,实测使用笔记本自带摄像头检测效果更佳:
实现步骤与对应代码
程序实现步骤:
(1) 使用OpenCV DNN网络检测人脸;
(2) 通过HSV阈值提取肤色;
(3) 通过肤色轮廓面积与人脸ROI面积比值判断是否佩戴口罩。
人脸检测方法对比:
(1) 使用OpenCV Haar分类器检测人脸,例如使用自带的haarcascade_frontalface_alt.xml不能适应光线暗的情况和人脸遮挡情况,佩戴了口罩无法检测到人脸,无法继续进行后面步骤;
(2) 使用Dlib检测人脸,例如使用自带的shape_predictor_5_face_landmarks.dat不能适应人脸遮挡情况,佩戴了口罩无法检测到人脸,无法继续进行后面步骤;
(3) 使用OpenCV DNN网络检测人脸,使用自带的opencv_face_detector_uint8.pb能适应部分人脸遮挡和侧脸情况,能继续进行后续检测。
人脸检测步骤与代码:
(1) 下载OpenCV DNN人脸检测模型:
链接:https://pan.baidu.com/s/1AaLYDjyMn17ZTxOv5f-m5A
提取码:9396
(2) 加载模型检测人脸,实例代码与效果如下;
代码语言:javascript复制import sys
import numpy as np
import cv2
modelFile = "opencv_face_detector_uint8.pb"
configFile = "opencv_face_detector.pbtxt"
net = cv2.dnn.readNetFromTensorflow(modelFile, configFile)
conf_threshold = 0.7
def detectFaceOpenCVDnn(net, frame):
blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), [104, 117, 123], False, False)
frameHeight = frame.shape[0]
frameWidth = frame.shape[1]
net.setInput(blob)
detections = net.forward()
bboxes = []
ret = 0
for i in range(detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence > conf_threshold:
x1 = int(detections[0, 0, i, 3] * frameWidth)
y1 = int(detections[0, 0, i, 4] * frameHeight)
x2 = int(detections[0, 0, i, 5] * frameWidth)
y2 = int(detections[0, 0, i, 6] * frameHeight)
ROI = frame[y1:y2,x1:x2].copy()
cv2.rectangle(frame,(x1,y1),(x2,y2),(0,255,0),2)#框出人脸区域
return ret, frame
if __name__ == '__main__':
img = cv2.imread("./2.jpg")
_, result = detectFaceOpenCVDnn(net,img)
cv2.imshow("face_detection", result)
cv2.waitKey()
cv2.destroyAllWindows()
(3) 通过HSV阈值提取肤色轮廓:
代码语言:javascript复制ROI = frame[y1:y2,x1:x2].copy()
hsv_img=cv2.cvtColor(ROI,cv2.COLOR_BGR2HSV)
lower_hsv_1 = np.array([0,30,30])#颜色范围低阈值
upper_hsv_1 = np.array([40,255,255])#颜色范围高阈值
lower_hsv_2 = np.array([140,30,30])#颜色范围低阈值
upper_hsv_2 = np.array([180,255,255])#颜色范围高阈值
mask1 = cv2.inRange(hsv_img,lower_hsv_1,upper_hsv_1)
mask2 = cv2.inRange(hsv_img,lower_hsv_2,upper_hsv_2)
mask = mask1 mask2
mask = cv2.blur(mask,(3,3))
mask_color = cv2.cvtColor(mask,cv2.COLOR_GRAY2BGR)
cv2.imshow("mask", mask)
(4) 通过肤色轮廓面积与ROI面积比值判断是否有佩戴口罩,这里设置比值为0.65,上面三幅图的比例分别如下:
代码语言:javascript复制def cnt_area(cnt):
area = cv2.contourArea(cnt)
return area
def If_Have_Mask(img):
hsv_img=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
lower_hsv_1 = np.array([0,30,30])#颜色范围低阈值
upper_hsv_1 = np.array([40,255,255])#颜色范围高阈值
lower_hsv_2 = np.array([140,30,30])#颜色范围低阈值
upper_hsv_2 = np.array([180,255,255])#颜色范围高阈值
mask1 = cv2.inRange(hsv_img,lower_hsv_1,upper_hsv_1)
mask2 = cv2.inRange(hsv_img,lower_hsv_2,upper_hsv_2)
mask = mask1 mask2
mask = cv2.blur(mask,(3,3))
mask_color = cv2.cvtColor(mask,cv2.COLOR_GRAY2BGR)
cv2.imshow("mask", mask)
contours,hierarchy = cv2.findContours(mask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
if len(contours) < 1:
return "No Mask"
contours.sort(key = cnt_area, reverse=True)
#print(cv2.contourArea(contours[0]))
area = cv2.contourArea(contours[0])
mask_rate = area / (img.shape[0]*img.shape[1])
print(mask_rate)
if mask_rate < 0.65:
return "Have Mask"
else:
return "No Mask"
结尾语:
文章实现的步骤并非最优,但基本检测效果还OK,大家跟着上述步骤和代码完全可以自己做出来。当然如果你懒得动手,那就加入知识星球获取吧,那里为你准备了完整源码和测试素材。
更多实用文章请移步至--精华文章--专题文章分类。