点击上方“小白学视觉”,选择加"星标"或“置顶”

重磅干货,第一时间送达9df7a31d1fbaf9bd019cc7491df15e69.png

昨天在GitHub上看到了一个开源的项目,是利用深度学习来检测是否有佩戴口罩的,感觉还挺好玩的,于是就去下载了训练好的模型,打算用OpenCV的dnn模块来跑一跑。然而,在经过前向传播后,得到的推理矩阵prob是一个1x5972x2 的Mat矩阵,和之前遇到过的推理结果都不太一样,在经过多种解码方式的尝试后,还是没能够对这个推理结果正确得解码。并且在网上搜索也没有找到相关的内容,几乎没有网友使用OpenCV来运行这个模型,基本都是使用深度学习的框架来运行。这就很无奈了,现在只能暂时把这个模型放一边,等其他时候再来研究一下该怎么对它的推理结果进行解码。

然而,我还是想尝试一下做有无佩戴口罩的检测,因为被勾起了好奇心哈哈哈哈哈哈哈哈哈哈,然后又因为使用开源项目的预训练模型解码失败,一气之下,我就想要不自己试一试搞一个。说搞就搞,由于本人对深度学习的涉及面并不深入,所以我的思路是:使用OpenCV的dnn模块来进行人脸检测及定位,然后将检测出的人脸利用OpenCV的ml模块进行识别是否佩戴口罩。

那么要做的第一步,就是训练出我们需要的分类器,我选用OpenCV中ml模块的SVM分类器来训练口罩识别分类器。训练部分的代码如下:

string positive_path = "D:\\opencv_c++\\opencv_tutorial\\data\\test\\positive\\";
  string negative_path = "D:\\opencv_c++\\opencv_tutorial\\data\\test\\negative\\";
  
  vector<string> positive_images_str, negative_images_str;
  glob(positive_path, positive_images_str);
  glob(negative_path, negative_images_str);
  
  vector<Mat>positive_images, negative_images;
  for (int i = 0; i < positive_images_str.size(); i++)
  {
    Mat positive_image = imread(positive_images_str[i]);
    
    positive_images.push_back(positive_image);
  }
  for (int j = 0; j < negative_images_str.size(); j++)
  {
    Mat negative_image = imread(negative_images_str[j]);
    
    negative_images.push_back(negative_image);
  }
  string savePath = "face_mask_detection.xml";
  trainSVM(positive_images, negative_images, savePath);

首先读取所有的训练图像,包含正样本(戴口罩)图像和负样本(不戴口罩)图像,然后分别将正负样本集打包成vector<Mat>类型,传入训练函数trainSVM()中,这个函数定义在头文件 “face_mask.h” 中。

在训练过程中,我们不是把图像完全展开进行训练,而是通过特征提取,得到每个样本图像的HOG特征,再计算每个HOG特征的特征描述子,通过特征描述子来训练SVM分类器。

要注意的是,我们并不是对完整的样本图像进行HOG特征的提取与描述,而是对样本图像先进行人脸区域的提取,将提取出来的人脸区域图像再进行HOG特征提取与描述并进行训练。

同时,还需要对正负样本集进行标注,正样本标记为1,负样本标记为-1。

代码如下:

for (int i = 0; i < positive_num; i++)
  {
    Mat positive_face;
    Rect positive_faceBox;
    if (faceDetected(positive_images[i], positive_face, positive_faceBox))
    {
      resize(positive_face, positive_face, Size(64, 128));
      Mat gray;
      cvtColor(positive_face, gray, COLOR_BGR2GRAY);      
      vector<float> descriptor;
      hog_train->compute(gray, descriptor);
      train_descriptors.push_back(descriptor);
      labels.push_back(1);
    }
  }
  for (int j = 0; j < negative_num; j++)
  {
    Mat negative_face;
    Rect negative_faceBox;
    if (faceDetected(negative_images[j], negative_face, negative_faceBox))
    {
      resize(negative_face, negative_face, Size(64, 128));
      Mat gray;
      cvtColor(negative_face, gray, COLOR_BGR2GRAY);
      vector<float> descriptor;
      hog_train->compute(gray, descriptor);
      train_descriptors.push_back(descriptor);
      labels.push_back(-1);
    }
  }
  
  int width = train_descriptors[0].size();
  int height = train_descriptors.size();
  Mat train_data = Mat::zeros(Size(width, height), CV_32F);
  for (int r = 0; r < height; r++)
  {
    for (int c = 0; c < width; c++)
    {
      train_data.at<float>(r, c) = train_descriptors[r][c];
    }
  }
  auto train_svm = ml::SVM::create();
  train_svm->trainAuto(train_data, ml::ROW_SAMPLE, labels);
  train_svm->save(path);
  hog_train->~HOGDescriptor();
  train_svm->clear();

其中进行人脸提取的函数faceDetected()定义在头文件 “face.h” 中。在这里我们使用opencv_face_detector_uint8.pb人脸检测模型。

那么到这一步,就实现了检测是否佩戴口罩的SVM分类器的训练工作,训练得到的模型文件如下:
3422ab45df47fb6e008a6dfe99a1f58b.png

接下来,我们就要加载这个xml文件并且对输入的图像进行检测啦。其中,检测用的的函数是FaceMaskDetect(),这个函数定义在 “face_mask.h” 头文件中。

auto detecModel = ml::SVM::load("face_mask_detection.xml");
Mat test_image = imread("D:/BaiduNetdiskDownload/人脸口罩检测数据集/val/test_00004577.jpg");
FaceMaskDetect(test_image, detecModel);
  
imshow("test_image", test_image);

到这里,我们就实现了从训练,到运行检测的过程,下面来看一下运行的效果怎样:

先看下没带口罩的图像,如果检测到没佩戴口罩,那么人脸就用红色框框出,而且标记红色的 “ Not Face Mask ” 字样:
2eee2ca0f0b6ad24c9eb5902202ed1b8.pngbd9c10257abd5ec6ffea3fb6ed368e07.png1138c5d910d21186b47f8e292f2704c0.png7ddafb13e17effa9791b31e479ff77d1.png

39f131b7481485e5f2fedbef38a0d969.png

如果是有佩戴口罩,那么就用绿色框框出人脸,并且标记 “ Face Mask ” :
6ef3a58cb0c9c197ac6953535e577745.pngcaec59ac9840f309fe484266a7dcf92a.png43e32ea4862fb744f5fbdbe2af496acf.pngdd7c448d45d55a283453d70b7c71036e.png

从效果上来看,所采用的测试图像都不在训练集之内,对单个人脸的照片识别成功率还是可以的,但是肯定没有开源项目里神经网络模型的识别正确率高。而且我这里训练的时候,正负样本比例大约是1:2,总样本集是四百多张训练图像,相比起开源项目里八千多张图像的训练集来说简直是不值一提。

不过由于人脸检测那一部分中,并没有对同一幅图像中出现多个人脸这种情况进行处理,以至于当一副图像中出现多个人脸时,只会对其中人脸置信度最高的那个人进行佩戴口罩检测,所以这个部分还需要进一步优化。

当然了,只对一张图像进行检测就没啥意思了,我们同样可以联合摄像头来实现实时检测,演示代码如下:

VideoCapture capture;
  capture.open(0);
  if (!capture.isOpened())
  {
    cout << "can't open camera" << endl;
    exit(-1);
  }
  Mat frame;
  while (capture.read(frame))
  {
    FaceMaskDetect(frame, detecModel);
    
    imshow("test_image", frame);


    char ch = waitKey(1);
    if (ch == 27)
    {
      break;
    }
  }

那么本次笔记就到此结束啦,谢谢阅读~

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

下载1:OpenCV-Contrib扩展模块中文版教程

在「小白学视觉」公众号后台回复:扩展模块中文教程即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。

下载2:Python视觉实战项目52讲

在「小白学视觉」公众号后台回复:Python视觉实战项目即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。

下载3:OpenCV实战项目20讲

在「小白学视觉」公众号后台回复:OpenCV实战项目20讲即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。

交流群

欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~

f3cdafbfe808b85c2b04df7c029b1296.png

dbe94eb8cfce1516a4279cc9f89ae538.png

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐