OpenCV3实现人脸识别(二)——收集要识别的人脸数据集

版权声明:转载请注明出版 https://blog.csdn.net/matt45m/article/details/88836461

前言

1、前面博文有演示过如何使用OpenCV自带的人脸与眼睛的级联分类器检测到图像中的人脸,这里将演示如何打开连接电脑的摄像头并检测人脸,然后拍照保存下来,用来做人脸识别的训练数据。
2.我的编程环境是Windows 7 64位,IDE是VS2015,配置了OpenCV3.3与OpenCV_Contrib,Boost 1.66,其中Boost是用来操作文件和目录用的,是于如果配置以上的环境,可以看我之前写的博文。

一、处理官方数据

1.下载OpenCV官方的人脸数据,官方人脸数据总共给了40个人的正脸,分好类放在一个文件目录下,每个人的人脸数据是10张,格式是pgm的,pgm这个格式是在PC机是无法用看图软件打开的,但可以使用OpenCV写个小程序查看里面的内容。csdn下载地址是:https://download.csdn.net/download/matt45m/11090734 。下载之后打开是这样的:
在这里插入图片描述
每个文件夹下包含10张人脸数据,格式是pgm的,尺寸为92X112。
在这里插入图片描述
2.写个函数查看每个分类的人脸数据,这里使用到boost库的文件操作类递归遍历每个子目录下的图像数据并显示。
(1)函数代码

/显示目标路径下的所有图像
void showImage(string image_path)
{
	//判断是事为文件夹
	if (!fs::is_directory(image_path))
	{
		Mat image = imread(image_path);
		if (!image.empty())
		{
			imshow(image_path, image);
			waitKey(0);
		}
	}
	else if(fs::is_directory(image_path))
	{
		fs::recursive_directory_iterator begin_iter(image_path);
		fs::recursive_directory_iterator end_iter;
		for (; begin_iter != end_iter; begin_iter++)
		{
			string file_name = begin_iter->path().string();
			if (!fs::is_directory(file_name))
			{
				Mat image = imread(file_name);
				if (!image.empty())
				{
					imshow(file_name, image);
					waitKey(30);
				}
			}
		}
	}
}

(2)打开其中的一组数据,可以看到其中的人脸,有各种视角的正脸,这样我们在录入要识别的人脸尽量可借鉴官方的视角来收集。
在这里插入图片描述

二、收集要识别的人脸数据

收集自己要识别的人脸数据,可以从图像集里面检测出人脸,也可以从USB摄像头或笔记本自带的摄像头检测出人脸,这里给出的代码是从摄像头检测到人脸,然后截取,改成官方给的人脸图像大小一样的数据,保存。
1.使用OpenCV人脸检测的级联分类器检测到人脸,如果检测到当前视频中只有一张人脸时,然后再用眼睛分类器检测这张脸是否能完全检测两个眼睛,如果能同时检测到两个眼睛,按p键拍照并保存。保存10张以上的人脸,退出。

void photograph(string save_path,int _cap)
{
	int image_number = 0;
	if (!face_detector.load(face_path))
	{
		std::cout << "无法打开人脸检测的级联分类器!" << endl;
		exit(0);
	}
	if (!eye_detector.load(eye_path))
	{
		std::cout << "无法打开左眼检测的级联分类器!" << endl;
		exit(0);
	}
	cap.open(_cap);
	if (!cap.isOpened())
	{
		std::cout << "无法打开摄像头!" << std::endl;
		exit(0);
	}
	Mat frame;
	Mat gray;
	Mat use_face;
	vector<Rect> faces;
	vector<Rect> eyes;
	while (cap.read(frame))
	{
		cvtColor(frame, gray, COLOR_BGR2GRAY);
		equalizeHist(gray, gray);
		//flip(gray, gray, 1);
		vector<Rect> faces;

		face_detector.detectMultiScale(gray, faces, 1.2, 3, 0, Size(30, 30));
		for (size_t t = 0; t < faces.size(); t++)
		{
			Rect roi;
			roi.x = faces[static_cast<int>(t)].x;
			roi.y = faces[static_cast<int>(t)].y;
			roi.width = faces[static_cast<int>(t)].width;
			roi.height = faces[static_cast<int>(t)].height / 2;
			Mat faceROI = frame(roi);
			//当检测到只有一张脸时,检测眼睛
			if (faces.size() == 1)
			{
				eye_detector.detectMultiScale(faceROI, eyes, 1.2, 3, 0, Size(20, 20));
				for (size_t k = 0; k < eyes.size(); k++)
				{
					Rect rect;
					rect.x = faces[static_cast<int>(t)].x + eyes[k].x;
					rect.y = faces[static_cast<int>(t)].y + eyes[k].y;
					rect.width = eyes[k].width;
					rect.height = eyes[k].height;
					rectangle(frame, rect, Scalar(0, 0, 255), 2, 8, 0);

					//只有都检测到两只眼睛时开始拍照
					if (eyes.size() == 2)
					{
						char key = waitKey(100);
						switch (key)
						{
							//按下p键时开始拍照
							case'p':
							{
								Mat face_roi = gray(faces[0]);
								//图像序号加1
								image_number++;
								//将检测到的人脸大小改为92*112与官司数据一样
								resize(face_roi, use_face, Size(92, 112));
	
								string filename = save_path + format("%d.jpg", image_number);
								//存放到传入的目录
								imwrite(filename, use_face);
								imshow(filename, use_face);
								waitKey(1000);
								//销毁指定的窗口
								destroyWindow(filename);
								break;
							}
						}

						//按ese键退出拍照
						if (key == 27)
						{
							cap.release();
							exit(0);
						}
					}
				}
				rectangle(frame, faces[t], Scalar(255, 0, 0), 2, 8, 0);
			}
		}
		imshow("camera", frame);
		waitKey(33);
	}
}

2.我这里跟下载的官方数据保存在一起,文件夹名为41。
在这里插入图片描述
3.我多拍了十几张人脸照片,保存的格式还是跟官方一样为pgm,然后用showImage()函数去读取显示,把太相似的图像删除,只保存10张图像数据就可以了。

三、生成图像列表文件

当训练人脸模型的时候,需要读取人脸和人脸对应的类名(标签)。官方给的教程是生成csv,本质上是用来让训练程序读取当前数据与对应的标签,opencv官方教程里面提供了自动生成csv文件的python脚本,文件路径与文件名是opencv_contrib-3.3.0\modules\face\samples\etc\create_csv.py。复制一份改成自己存放图像的路径运行就可以了。但我这里使用Boost库和C++的读写文件,生成了一个faceList.txt也是可以用的。
1.生成faceList.txt的函数代码:

void buildList(string face_iamge_path)
{
	string bow_path = face_iamge_path + string("faceList.txt");
	ifstream read_file(bow_path);
	ofstream ous(bow_path);
	fs::path face_path(face_iamge_path);
	if (!fs::exists(face_path))
	{
		std::cout << "当前目录没有要训练的文件!" << std::endl;
		exit(0);
	}
	fs::directory_iterator begin_iter(face_iamge_path);
	fs::directory_iterator end_iter;
	int i = 0;
	//递归迭代rescursive 直接定义两个迭代器:i为迭代起点(有参数),end_iter迭代终点
	for (; begin_iter != end_iter; ++begin_iter)
	{
		i++;
		if (fs::is_directory(begin_iter->path()))
		{
			fs::directory_iterator begin(begin_iter->path());
			fs::directory_iterator end;
			for (; begin != end; ++begin)
			{
				string face_name = begin->path().string() + ";" + to_string(i);
				//cout << face_name << endl;
				ous << face_name << endl;
			}
		}
	}
	ous.close();
}

2.调用函数,传入数据路径,在当前目录下生成一个faceList.txt的文件,文件格式如下,标签名对应目录类名。
在这里插入图片描述

结语

1.到这里所有数据都准备完成,之后就是训练和识别,影响识别的精准度有很多,训练数据收集应该是最重要的部分,所以尽量收集各种视角不用光线的正脸。
2.关于整个工程的源码,运行程序时的bug,或者有如何优化的想法都可以加这个群(487350510)互相讨论学习

猜你喜欢

转载自blog.csdn.net/matt45m/article/details/88836461