基于图像的口罩佩戴自动检测系统设计

基于图像的口罩佩戴自动检测系统设计

一、前言

学校的一门综合设计,在此仅作学习记录,在此仅作学习记录!在此仅作学习记录!再不记录一下自己都忘掉了,同时希望看到的友友指出不足,共同进步,如果有的话吼吼吼吼吼~。

二、背景

  1. 2020 年初,新型冠状肺炎疫情爆发,研究表明佩戴口罩相对于不佩戴口罩的感染风险大大降低。
  2. 设计实现一种可以基于视频图像的口罩佩戴正确与否的自动检测系统具有重要意义。
  3. 虽然现在似乎好像应该大概是有亿点过时,但是问题不大,浅记一下。

三、设计目标

  1. 进行人脸检测,将人脸进行框选
  2. 进行人脸物理特征识别,框选眼睛、鼻子、嘴巴等人脸特征信息
  3. 进行口罩佩戴判断,利用以上特征信息,通过是否检测到相关特征进行口罩佩戴情况的判断,佩戴情况分成三种:未佩戴,正确佩戴,错误佩戴。

四、实现方法

1. 基于特征检测的方法(本文方法)

  1. 输入视频图像,获取图像帧
  2. 背景减除法分离运动前景,即移动的人,目的是缩小检测氛围。
  3. 肤色提取分离人脸。
  4. Haar特征级联分类器进行人脸检测和特征提取,并框选人脸和相关特征。
  5. 根据是否检测到眼睛、鼻子、嘴巴等特征进行口罩佩戴情况分类,给出警示信息。

2. 深度学习的方法 (底子薄,没用这个)

咱就是一小小本科生,当时才开始接触深度学习,深度学习底子薄,用深度学习感觉难度大,就没选择这种方案。现在还是建议采用深度学习,准确率高,速度快,用yolo系列可以直接一步到位。

五、实现源代码

# Project: zonghesheji
# author:@hong
# Time: 2022/12

import numpy as np
import cv2 as cv
import random

#face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
#读取面部识别分类器
face_cascade = cv.CascadeClassifier('D:/opencv/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml')
#读取嘴巴识别分类器
mouth_cascade = cv.CascadeClassifier('D:/opencv/opencv/sources/data/haarcascades/haarcascade_mcs_mouth.xml')
#读取鼻子识别分类器
nose_cascade = cv.CascadeClassifier('D:/opencv/opencv/sources/data/haarcascades/haarcascade_mcs_nose.xml')
#读取眼睛识别分类器
eye_cascade = cv.CascadeClassifier('D:/opencv/opencv/sources/data/haarcascades/haarcascade_eye.xml')

#二值化最小阈值
black_white_threshold = 80

##初始化字体
font = cv.FONT_HERSHEY_SIMPLEX
#绘制字体框的大小
org = (30, 30)
#戴口罩字体颜色
weared_mask_font_color = (0, 255, 0)
#不戴口罩字体颜色
not_weared_mask_font_color = (0, 0, 255)
#错误佩戴口罩字体颜色
error_weared_mask_font_color = (30, 255, 255)

noface = (255, 255, 255)
thickness = 2
font_scale = 1
weared_mask = "Thank You for wearing MASK"
not_weared_mask = "Please wear MASK to defeat Corona"
error_weared_mask="Please wear MASK correctly"

def empty(a):
    pass

# 更新背景照片
def getbackground():
    cap = cv.VideoCapture(0)
    num = 0
    while True:
        ok, frame = cap.read()
        image = cv.GaussianBlur(frame, (3, 3), 0)  # 高斯滤波
        # cv2.imshow("gauss",image)
        gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)  # 将图片转化成灰度
        num = num + 1
        if num == 10:
            backgound = gray
            cv.imwrite("./backgound.jpg", backgound)
            cv.imshow("backgound", backgound)
            break


image = cv.imread("./withmask.jpg")  # 读取初始照片
cv.imshow('orignal_image', image)  # 展示
#cv.createTrackbar("Hmin", "orignal_image", 0, 90, empty)  # 创建bar
#cv.createTrackbar("Hmax", "orignal_image", 25, 90, empty)

#定义背景颜色
colour = ((0, 205, 205), (154, 250, 0), (34, 34, 178), (211, 0, 148), (255, 118, 72), (137, 137, 139))
#打开摄像头
cap = cv.VideoCapture(0)
history=200  #训练帧数
bs=cv.createBackgroundSubtractorKNN(detectShadows=False)  ##基于KNN的前景分割算法
#bs=cv.createBackgroundSubtractorMOG2(detectShadows=True)  ##基于MOG2的前景分割算法,速度比KNN快,但是质量没有LNN好,因此选择KNN算法
#bs=cv.createb(detectShadows=True)  ##基于KNN的前景分割算法    ##opencv库中没有GMG算法用于背景减除
bs.setHistory(history)
frames=0

#前景分割算法的前提是要先拍摄一张环境图作为背景
getbackground()#保存背景
backgound=cv.imread("./backgound.jpg")
backgound=cv.cvtColor(backgound, cv.COLOR_BGR2GRAY)#将图片转化成灰度
gray=backgound #k-1张


while True:
    gray_last=gray    #k-1张,方便后面考虑对比帧间差分法
    #cv.imshow("gray_last", gray)
    ret, img = cap.read()
    #镜像翻转
    img = cv.flip(img, 1)
    cv.imshow("orignal_image", img)      ##展示摄像头输入图像

    ###肤色区域检测  Ycrcb法
    ycrcb = cv.cvtColor(img, cv.COLOR_BGR2YCR_CB)       #将BGR色彩空间图片转化到YCrCb色彩空间
    (y, cr, cb) = cv.split(ycrcb)            #分别提取
    cr1 = cv.GaussianBlur(cr, (5, 5), 0)     #高斯模糊去除高斯噪声,降低细节层次
    _, skin = cv.threshold(cr1, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)  #大津法(大阈值图像分割法)二值化处理
    #cv.imshow("image raw", img)
    #cv.imshow("image CR", cr1)
    cv.imshow("Skin Cr+OTSU", skin)
    dst = cv.bitwise_and(img, img, mask=skin)
    cv.imshow("seperate", dst)      #分离提取肤色区间

    ####显示肤色区域  HSV法---效果很差
    """""""""
    hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)    #将图片转化成HSV格式
    H, S, V = cv.split(hsv)
    Hmin = cv.getTrackbarPos("Hmin", 'orignal_image')  # 获取bar
    Hmax = cv.getTrackbarPos("Hmax", 'orignal_image')
    if Hmin > Hmax:
        Hmax = Hmin
    thresh_h = cv.inRange(H, Hmin, Hmax)  # 提取人体肤色区域
    cv.imshow("thresh_h1", thresh_h)
    """
    #高斯模糊去除噪声
    #img = cv.GaussianBlur(dst, (3, 3), 0)   ##检测不到人脸
    img = cv.GaussianBlur(img, (3, 3), 0)
    #cv.imshow("Gaussionblur", img)

    #转为灰度图像
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    cv.imshow("gray_image", gray)

    """""""""
    ### 背景差分处理
    fgmask1 = cv.absdiff(gray, backgound)  # 背景差分
    cv.imshow("fgmask1", fgmask1)
    ### 帧间差分处理
    fgmask2 = cv.absdiff(gray, gray_last)  # 帧间差分法
    cv.imshow("fgmask2", fgmask2)
    ###按比例而调节的改进差分法
    fgmask = cv.addWeighted(fgmask1, 0.3, fgmask2, 0.7, 0)  # 按比例相加,相当于差分均值
    cv.imshow("weight", fgmask)
    """
    # 获取foreground mask 获取前景分割图
    fg_mask = bs.apply(img)         ##应用于前景掩模
    cv.imshow("foreground mask", fg_mask)
    if frames<history:
        frames+=1
        continue


    #图像二值化
    thresh, black_and_white= cv.threshold(fg_mask, 100, 255, cv.THRESH_BINARY)
    cv.imshow("threshold_image", black_and_white)

    """"""""""
    fgmask = cv.dilate(black_and_white, cv.getStructuringElement(cv.MORPH_ELLIPSE,(8,3)), iterations=8)  # 膨胀
    cv.imshow('dilate', fgmask)
    fgmask = cv.erode(fgmask, cv.getStructuringElement(cv.MORPH_ELLIPSE,(3,3)), iterations=5 )  # 腐蚀
    cv.imshow('erode', fgmask)
    """""
    # 腐蚀
    fgmask = cv.erode(black_and_white, cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5)), iterations=1 )
    #cv.imshow('erode', fgmask)
    # 膨胀
    fgmask = cv.dilate(fgmask, cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5)), iterations=20)
    #cv.imshow('dilate', fgmask)
    # 形态学去噪
    element = cv.getStructuringElement(cv.MORPH_CROSS, (25, 25))
    # 开运算去噪
    fgmask = cv.morphologyEx(fgmask, cv.MORPH_CLOSE, element)
    #cv.imshow('kaiyunsuan', fgmask)


    # 寻找运动前景勾画轮廓
    contours, hierarchy = cv.findContours(fgmask.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    cv.drawContours(img, contours, -1, (0, 0, 255), 3)
    rect_array = []
    count = 0
    for cont in contours:
        Area = cv.contourArea(cont)  # 计算轮廓面积
        if Area < 1500:  # 过滤面积小于10的形状
            continue
        count += 1  # 计数加一
        # print("{}-prospect:{}".format(count,Area),end="  ") #打印出每个前景的面积
        rect = cv.boundingRect(cont)  # 提取矩形坐标
        # rect_array.append(rect)
        # print("x:{} y:{}".format(rect[0],rect[1]))#打印坐标
        #cv.rectangle(img, (rect[0], rect[1]), (rect[0] + rect[2], rect[1] + rect[3]), colour[count % 6],1)  # 原图上绘制矩形
        cv.rectangle(fgmask, (rect[0], rect[1]), (rect[0] + rect[2], rect[1] + rect[3]), (0xff, 0xff, 0xff), 1)  # 黑白前景上绘制矩形
        y = 10 if rect[1] < 10 else rect[1]  # 防止编号到图片之外
        cv.putText(img, str(count), (rect[0], y), cv.FONT_HERSHEY_COMPLEX, 0.4, (0, 255, 0), 1)  # 在前景上写上编号
    cv.putText(img, "count:", (5, 20), cv.FONT_HERSHEY_COMPLEX, 0.6, (0, 255, 0), 1)  # 显示总数
    cv.putText(img, str(count), (75, 20), cv.FONT_HERSHEY_COMPLEX, 0.6, (0, 255, 0), 1)

    # 勾画前景后展示图像
    # cv2.imshow("bitwise_and",fgmask)
    cv.imshow("findcont", img)


    #脸部检测,缩放系数1.1,4轮确定
    faces = face_cascade.detectMultiScale(gray, 1.1, 10, 0, (100, 100))
    faces_bw = face_cascade.detectMultiScale(black_and_white, 1.1, 10, 0, (100, 100))
    if len(faces) == 0 and len(faces_bw) == 0:
        cv.putText(img, "No face found...", org, font, font_scale, noface, thickness, cv.LINE_AA)
    elif len(faces) == 0 and len(faces_bw) == 1:
        cv.putText(img, weared_mask, org, font, font_scale, weared_mask_font_color, thickness, cv.LINE_AA)
    else:
        for (x, y, w, h) in faces:
            cv.rectangle(img, (x, y), (x + w, y + h), (255, 255, 255), 2)
            roi_gray = gray[y:y + h, x:x + w]
            roi_color = img[y:y + h, x:x + w]
            ##检测到鼻子并标记
            nose_rects = nose_cascade.detectMultiScale(gray,1.05,4,0,(50,50),(100,100))
            for (nx,ny,nw,nh) in nose_rects:
                cv.line(img,(int(nx+nw/2),ny),(int(nx+nw/2),int(ny+nh/2)),(0,255,0),2)
            ##检测到眼睛并标记
            eyes_rects = eye_cascade.detectMultiScale(gray, 1.1, 3, 0, (50, 50),(100,100))
            for (ex,ey,ew,eh) in eyes_rects:
                cv.rectangle(img,(int(ex+ew/4),int(ey+eh/4)),(int(ex+ew*0.8),int(ey+eh*0.8)),(255,0,0),2)
            ##检测到嘴巴并标记
            mouth_rects = mouth_cascade.detectMultiScale(gray,1.1,5,0,(80,80),(100,120))
            for (mx,my,mw,mh) in mouth_rects:
                 cv.ellipse(img, (int(mx+mw/2), int(my+mh/2.5)), (int(mw/3),int(mh/3)),0,0,360,(0,0,255),1)
            ##没有检测到鼻子也没有检测到嘴巴,则是戴好了口罩
        if (len(nose_rects) == 0 and len(mouth_rects)==0):
            cv.putText(img, weared_mask, org, font, font_scale, weared_mask_font_color, thickness, cv.LINE_AA)
            ##只遮住了嘴巴或者只遮住了鼻子则是错误佩戴口罩
        elif ((len(nose_rects) == 1 and len(mouth_rects)==0)or(len(nose_rects) == 0 and len(mouth_rects)==1)):
            cv.putText(img, error_weared_mask, org, font, font_scale, error_weared_mask_font_color, thickness, cv.LINE_AA)
            ##其它情况认为没有佩戴口罩(检测到鼻子也检测到了嘴巴)
        else:
            for (mx, my, mw, mh) in nose_rects:
                ##对于鼻子在检测到人脸上,在多个人脸的情况
                if (y < my < y + h):
                    cv.putText(img, not_weared_mask, org, font, font_scale, not_weared_mask_font_color, thickness,
                                cv.LINE_AA)
                    break
    cv.imshow('Mask Detection', img)
    k = cv.waitKey(30) & 0xff
    if k == 27:
        break

cap.release()
cv.destroyAllWindows()

六、效果说明与总结

由于这个并不是采用深度学习的方法做的,在此强调!!!效果一般般!!!该方案适用于学习使用,对于现实中或者精度速度要求较高的,采用深度学习方法更好。演示图片设及本人,咱就不上传啦~
PS:后继有时间再选用其它视频检测后再更新展示图片~。
最后欢迎交流,good good study,day day up !

参考文献:

[1] https://blog.csdn.net/allway2/article/details/120831486
[2] https://blog.csdn.net/weixin_45224869/article/details/105275628
[3] https://blog.csdn.net/qq_42807924/article/details/104116361
[4] https://blog.csdn.net/weixin_44255592/article/details/89505450
[5] https://blog.csdn.net/drippingstone/article/details/116186462
[6] https://blog.csdn.net/qq_22527639/article/details/81501565
[7] https://blog.csdn.net/xss20072754/article/details/110147675
[8] https://blog.csdn.net(广大博友)

  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
我们很容易掌握AI深度学习。让机器能够模仿人脑的思考方式,从而摆脱原来的固有数据库比较的限制。深度学习的发展课件:链接:https://pan.baidu.com/s/1Ck4GN9N0OCzQgH0MxZOqeQ 提取码:b74k随着机器学习, 深度学习的发展,很多人眼很难去直接量化的特征, 深度学习可以搞定, 这就是深度学习带给我们的优点和前所未有的吸引力。很多特征我们通过传统算法无法量化,或者说很难去做到的, 深度学习可以搞定。特别是在图像分类, 目标检测这些问题上取得了显著的提升。下图是近几年来深度学习在图像分类问题上取得的成绩。●   机器视觉缺陷检测的痛点●   仍存在下面主要的问题和难点 1) 受环境、光照、生产工艺和噪声等多重因素影响,检测系统的信噪比一般较低,微弱信号难以检出或不能与噪声有效区分。如何构建稳定、可靠、鲁棒的检测系统,以适应光照变化、噪声以及其他外界不良环境的干扰,是要解决的问题之一。 2) 由于检测对象多样、表面缺陷种类繁多、形态多样、复杂背景,对于众多缺陷类型产生的机理以及其外在表现形式之间的关系尚不明确,致使对缺陷的描述不充分,缺陷的特征提取有效性不高,缺陷目标分割困难;同时,很难找到“标准”图像作为参照,这给缺陷的检测和分类带来困难,造成识别率尚有待提高。 3) 机器视觉表面缺陷检测,特别是在线检测,其特点是数据量庞大、冗余信息多、特征空间维度高,同时考虑到真正的机器视觉面对的对象和问题的多样性,从海量数据中提取有限缺陷信息的算法能力不足,实时性不高。 4) 与机器视觉表面检测密切相关的人工智能理论虽然得到了很大的发展,但如何模拟人类大脑的信息处理功能去构建智能机器视觉系统还需要理论上的进一步研究,如何更好的基于生物视觉认识、指导机器视觉得检测也是研究人员的难点之一。 5) 从机器视觉表面检测的准确性方面来看,尽管一系列优秀的算法不断出现,但在实际应用中准确率仍然与满足实际应用的需求尚有一定差距,如何解决准确识别与模糊特征之间、实时性与准确性之间的矛盾仍然是目前的难点。 传统算法在某些特定的应用中已经取得了较好的效果,但仍然存在许多不足。例如:图像预处理步骤繁多且具有强烈的针对性,鲁棒性差;多种算法计算量惊人且无法精确的检测缺陷的大小和形状。而深度学习可以直接通过学习数据更新参数,避免了人工设计复杂的算法流程,并且有着极高的鲁棒性和精度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值