手把手教你用OpenCV做人脸口罩佩戴检测(附详细步骤+代码)

2021-04-30 11:29:08 浏览数 (1)

导读

本期将介绍使用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,大家跟着上述步骤和代码完全可以自己做出来。当然如果你懒得动手,那就加入知识星球获取吧,那里为你准备了完整源码和测试素材。

更多实用文章请移步至--精华文章--专题文章分类。

0 人点赞