由于疫情的影响,口罩检测已经成为各个程序员竞相开发的一种算法。
百度的人脸检测SDK使用的还不错,他们还把口罩检测也给开源了
我这里使用基于OPENCV的检测
一般的思路可能就是手机带有口罩和没有戴口罩的数据集进行训练,但是我暂时没有找到这些数据集,我就采用使用opencv原来带有的训练集先检测出人脸,然后再对人脸检测鼻子和嘴巴。但是由于opencv的检测鼻子和嘴巴的算法准确性不高,需要经过附加条件检测是不是真正的嘴巴和鼻子,如果在人脸中检测出了嘴巴和鼻子的话,那么没有戴口罩puttext no mask,否则就进行人脸识别
那么要进行人脸识别的话,需要采集本人的数据,然后在获取ORL的数据集一同训练。我这里获取了ORL提供的40个样本,每个样本里面有10个bmp格式的图像。
现在我们开始获取数据集,思路很简单,就是打开摄像头,对每一帧图像进行处理。对这每一帧图像识别出人脸,如果人脸的size为1,那么表示这就是你的人脸,然后把处理后的人脸保存起来。
为了拍摄多角度图像,需要每处理一次都需要等待,设置一个计数器,当经过十次的拍摄后,就退出程序
int makepicture()
{
CascadeClassifier cascada;
cascada.load("E:/OPENCV/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml");
VideoCapture cap(0);
Mat frame, myFace;
int pic_num = 1;
while (1) {
cap >> frame;
vector<Rect> faces;//vector容器存检测到的faces
Mat frame_gray;
cvtColor(frame, frame_gray, COLOR_BGR2GRAY);//转灰度化,减少运算
cascada.detectMultiScale(frame_gray, faces, 1.1, 4, CV_HAAR_DO_ROUGH_SEARCH, Size(70, 70), Size(1000, 1000));
for (int i = 0; i < faces.size(); i++)
{
rectangle(frame, faces[i], Scalar(255, 0, 0), 2, 8, 0);
}
//当只有一个人脸时,开始拍照
if (faces.size() == 1)
{
Mat faceROI = frame_gray(faces[0]);//在灰度图中将圈出的脸所在区域裁剪出
//cout << faces[0].x << endl;//测试下face[0].x
resize(faceROI, myFace, Size(92, 112));//将兴趣域size为92*112
putText(frame, to_string(pic_num), faces[0].tl(), 3, 1.2, (0, 0, 225), 2, 0);//在 faces[0].tl()的左上角上面写序号
string filename = "样本/s41/"+to_string(pic_num)+".bmp"; //存放在当前项目文件夹以1-10.jpg 命名,format就是转为字符串
imwrite(filename, myFace);//存在当前目录下
imshow(filename, myFace);//显示下size后的脸
waitKey(500);//等待500us
destroyWindow(filename);//:销毁指定的窗口
pic_num++;//序号加1
if (pic_num == 11)
{
return 0;//当序号为11时退出循环
}
}
int c = waitKey(10);
if ((char)c == 27) { break; } //10us内输入esc则退出循环
imshow("frame", frame);//显示视频流
waitKey(100);//等待100us
}
return 0;
}
然后需要对自己的样本进行处理
void initdata()
{
/*
对于训练样本:
Ptr<FaceRecognizer> model = createLBPHFaceRecognizer();
model->train(img, labels);train函数的两个参数也很简单,训练的图像组vector<Mat>和对应的标签组vector<int>,这个label标签只需保证同一个人的标签相同即可,不需要保证图像的按标签顺序输入。
*/
vector<Mat> img;
vector<int> labels;//定义标签还有图片
for (int i = 1; i <= 41; i++)
{
for (int j = 1; j < 11; j++)
{
string path = "样本/s"+to_string(i)+"/" + to_string(j) + ".bmp";
Mat img_gray = imread(path);
cvtColor(img_gray, img_gray, COLOR_BGR2GRAY);
img.push_back(img_gray);
labels.push_back(i);
}
}
Ptr<FaceRecognizer> model = createFisherFaceRecognizer();//训练
model->train(img, labels);
model->save("model.xml");
}
ok,处理完自己的样本之后,就可以直接打开摄像头,对每一帧识别出人脸,如果人脸有嘴巴或者鼻子的时候,就把他视作没有带口罩;如果没有的话,加载数据集,然后预测人脸,显示预测结果
核心代码如下
void fun(Mat& img, CascadeClassifier& face_cascade, CascadeClassifier& eyes_cascade, CascadeClassifier& nose_cascade, CascadeClassifier& mouse_cascade, Ptr<FaceRecognizer> model, Vector<string> name)
{
vector<Rect> faces;//脸的存储
vector<Rect> noses;
vector<Rect> mouses;
Mat frame;//需要使用灰度图
Mat predect;
cvtColor(img, frame, COLOR_BGR2GRAY);
face_cascade.detectMultiScale(frame, faces, 1.2, 7, 0, Size(80, 80));//分类器检测人脸
//输入照片,检测到的人脸序列,图像尺寸减小比例,检测5次,最小尺寸 最大尺寸
for (int i = 0; i < faces.size(); i++)
{
frame(faces[i]).copyTo(predect);
resize(predect,predect,Size(92,112));
mouse_cascade.detectMultiScale(predect, mouses, 1.2, 8, 0, Size(5, 5));
nose_cascade.detectMultiScale(predect, noses, 1.2, 8, 0, Size(5, 5));
//imshow("",predect);
int label=-1;
double predectnumber=0;
model->predict(predect,label, predectnumber);
rectangle(img, Point(faces[i].x, faces[i].y), Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height),
Scalar(0, 255, 0), 1, 8); //框出人脸位置左上和右下
if(mouses.size()>0||noses.size()>0)
putText(img, "no mask", Point(faces[i].x, faces[i].y), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 4, 8);
else
{
if (predectnumber>70)
putText(img, name[label-1], Point(faces[i].x, faces[i].y), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 4, 8);
else
putText(img, "I dont know", Point(faces[i].x, faces[i].y), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255),4,8);
}
}
imshow("FacesOfPrettyGirl", img);
}
测试的结果如下图
整个程序如下
#include <opencv2\contrib\contrib.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include "opencv2\opencv.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
using namespace cv;
String face_cascade_name = "E:/OPENCV/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml";
String eyes_cascade_name = "E:\\OPENCV\\opencv\\sources\\data\\haarcascades\\haarcascade_eye_tree_eyeglasses.xml";
String mouse_cascade_name = "E:/OPENCV/opencv/sources/data/haarcascades/haarcascade_mcs_mouth.xml";
String nose_cascade_name = "E:/OPENCV/opencv/sources/data/haarcascades/haarcascade_mcs_nose.xml";
CascadeClassifier face_cascade; //定义人脸分类器
CascadeClassifier eyes_cascade; //定义人眼分类器
CascadeClassifier nose_cascade; //定义鼻子分类器
CascadeClassifier mouse_cascade; //定义嘴巴分类器
String window_name = "Capture - Face detection";
void fun(Mat& img, CascadeClassifier& face_cascade, CascadeClassifier& eyes_cascade, CascadeClassifier& nose_cascade, CascadeClassifier& mouse_cascade, Ptr<FaceRecognizer> model, Vector<string> name);
void initdata();
void initdataname();
int makepicture();
int main() {
if (!face_cascade.load(face_cascade_name)) { printf("--(!)Error loading face cascade\n"); return -1; };
if (!eyes_cascade.load(eyes_cascade_name)) { printf("--(!)Error loading eyes cascade\n"); return -1; };
if (!nose_cascade.load(nose_cascade_name)) { printf("--(!)Error loading nose cascade\n"); return -1; };
if (!mouse_cascade.load(mouse_cascade_name)) { printf("--(!)Error loading mouse cascade\n"); return -1; };
//initdataname();
//initdata();
//makepicture();
Vector<string>name;
ifstream infile("dataname.csv", ios::in);
string line, field;
for (int i = 0; i < 41; i++)
{
getline(infile, line);
stringstream sin(line);
getline(sin, field);
name.push_back( field);
}
infile.close();
VideoCapture capture(0);
Ptr<FaceRecognizer> model = createFisherFaceRecognizer();
model->load("model.xml");
while (true) {
Mat frame;
capture >> frame;
fun(frame, face_cascade, eyes_cascade,nose_cascade,mouse_cascade ,model,name);
waitKey(1);
}
system("pause");
return 0;
}
void fun(Mat& img, CascadeClassifier& face_cascade, CascadeClassifier& eyes_cascade, CascadeClassifier& nose_cascade, CascadeClassifier& mouse_cascade, Ptr<FaceRecognizer> model, Vector<string> name)
{
vector<Rect> faces;//脸的存储
vector<Rect> noses;
vector<Rect> mouses;
Mat frame;//需要使用灰度图
Mat predect;
cvtColor(img, frame, COLOR_BGR2GRAY);
face_cascade.detectMultiScale(frame, faces, 1.2, 7, 0, Size(80, 80));//分类器检测人脸
//输入照片,检测到的人脸序列,图像尺寸减小比例,检测5次,最小尺寸 最大尺寸
for (int i = 0; i < faces.size(); i++)
{
frame(faces[i]).copyTo(predect);
resize(predect,predect,Size(92,112));
mouse_cascade.detectMultiScale(predect, mouses, 1.2, 8, 0, Size(5, 5));
nose_cascade.detectMultiScale(predect, noses, 1.2, 8, 0, Size(5, 5));
//imshow("",predect);
int label=-1;
double predectnumber=0;
model->predict(predect,label, predectnumber);
rectangle(img, Point(faces[i].x, faces[i].y), Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height),
Scalar(0, 255, 0), 1, 8); //框出人脸位置左上和右下
if(mouses.size()>0||noses.size()>0)
putText(img, "no mask", Point(faces[i].x, faces[i].y), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 4, 8);
else
{
if (predectnumber>70)
putText(img, name[label-1], Point(faces[i].x, faces[i].y), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 4, 8);
else
putText(img, "I dont know", Point(faces[i].x, faces[i].y), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255),4,8);
}
}
imshow("FacesOfPrettyGirl", img);
}
void initdata()
{
/*
对于训练样本:
Ptr<FaceRecognizer> model = createLBPHFaceRecognizer();
model->train(img, labels);train函数的两个参数也很简单,训练的图像组vector<Mat>和对应的标签组vector<int>,这个label标签只需保证同一个人的标签相同即可,不需要保证图像的按标签顺序输入。
*/
vector<Mat> img;
vector<int> labels;//定义标签还有图片
for (int i = 1; i <= 41; i++)
{
for (int j = 1; j < 11; j++)
{
string path = "样本/s"+to_string(i)+"/" + to_string(j) + ".bmp";
Mat img_gray = imread(path);
cvtColor(img_gray, img_gray, COLOR_BGR2GRAY);
img.push_back(img_gray);
labels.push_back(i);
}
}
Ptr<FaceRecognizer> model = createFisherFaceRecognizer();//训练
model->train(img, labels);
model->save("model.xml");
}
void initdataname()
{
ofstream file1("dataname.csv", ios::out);
for (int i = 1; i <= 40; i++)
{
file1 << "s" << i << endl;
}
file1 << "qianpli" << endl;
file1.close();
}
int makepicture()
{
CascadeClassifier cascada;
cascada.load("E:/OPENCV/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml");
VideoCapture cap(0);
Mat frame, myFace;
int pic_num = 1;
while (1) {
cap >> frame;
vector<Rect> faces;//vector容器存检测到的faces
Mat frame_gray;
cvtColor(frame, frame_gray, COLOR_BGR2GRAY);//转灰度化,减少运算
cascada.detectMultiScale(frame_gray, faces, 1.1, 4, CV_HAAR_DO_ROUGH_SEARCH, Size(70, 70), Size(1000, 1000));
for (int i = 0; i < faces.size(); i++)
{
rectangle(frame, faces[i], Scalar(255, 0, 0), 2, 8, 0);
}
//当只有一个人脸时,开始拍照
if (faces.size() == 1)
{
Mat faceROI = frame_gray(faces[0]);//在灰度图中将圈出的脸所在区域裁剪出
//cout << faces[0].x << endl;//测试下face[0].x
resize(faceROI, myFace, Size(92, 112));//将兴趣域size为92*112
putText(frame, to_string(pic_num), faces[0].tl(), 3, 1.2, (0, 0, 225), 2, 0);//在 faces[0].tl()的左上角上面写序号
string filename = "样本/s41/"+to_string(pic_num)+".bmp"; //存放在当前项目文件夹以1-10.jpg 命名,format就是转为字符串
imwrite(filename, myFace);//存在当前目录下
imshow(filename, myFace);//显示下size后的脸
waitKey(500);//等待500us
destroyWindow(filename);//:销毁指定的窗口
pic_num++;//序号加1
if (pic_num == 11)
{
return 0;//当序号为11时退出循环
}
}
int c = waitKey(10);
if ((char)c == 27) { break; } //10us内输入esc则退出循环
imshow("frame", frame);//显示视频流
waitKey(100);//等待100us
}
return 0;
}
来源:CSDN
作者:鹏鹏诶
链接:https://blog.csdn.net/qq_43294951/article/details/104541794