人工智能之口罩检测算法

白昼怎懂夜的黑 提交于 2020-02-27 19:55:51

由于疫情的影响,口罩检测已经成为各个程序员竞相开发的一种算法。
百度的人脸检测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;

}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!