opencv人脸检测
最近有空对学习下opencv的东西,本篇主要记录对人脸检测实现,而人脸检测是为人脸识别做准备。
opencv版本:3.3.0
环境:VS2015
关键函数介绍
void CascadeClassifier::detectMultiScale(InputArray image,
vector<Rect>& objects,
double scaleFactor=1.1,
int minNeighbors=3,
int flags=0,
Size minSize=Size(),
Size maxSize=Size());
- image:输入的图像,注意是CV_8U类型的,也就是灰度图;
- objects:矩形的vector对象用来存放检测到的人脸;
- scaleFactor:指定图像减少了多少,即每次扫描中搜索窗口的增大比例,默认为1.1;
- minNeighbors:构成检测目标相邻矩阵的最小个数,默认为3,如果该参数为0,函数不做任何操作就返回所有的候选矩形框;
- flags:对于新的分类器无用
- CV_HAAR_DO_CANNY_PRUNING:利用Canny边缘检测时会排除一些边缘很少或者很多的图像区域;
- CV_HAAR_SCALE_IMAGE:按正常比例监测;
- CV_HAAR_FIND_BIGGEST_OBJECT:只检测最大的物体;
- CV_HAAR_DO_ROUGH_SEARCH:只做粗略检测;
- minSize:检测窗口的最小值
- maxSize:监测窗口的最大值
人脸检测的过程其实也就是一个分类的过程,CascadeClassifier是opencv中人脸检测的一个级联分类器。opencv中目标检测是使用Haar分类器来实现的(后面也使用了LBP特征),这是一种监督学习分类器,使用的是AdaBoost算法。AdaBoost算法的基本过程是对给定的训练样本进行训练得到弱分类器,然后通过多个弱分类器进行加权叠加得到强分类器。弱分类器和强分类器与若学习和强学习对应,弱学习是指学习算法的识别率只是比随机识别稍好;强学习是指学习算法识别概率很高。
人脸检测
opencv为我们提供了方便API,我们只要调用就可以快速实现人脸检测,过程主要是:
读取图片 -> 转化成灰度图 -> 对灰度图进行直方图均衡化 -> 调用detectMultiScale进行人脸检测 -> 将结果转化成人脸识别需要的训练集格式
在这之前需要获取人脸的照片,这个很好办,打开手机疯狂自拍或者从网上下载一些包含人脸的照片即可。由于我还想用这些数据集来做识别,因此我这里用的是自己的人脸。
#include<opencv2/objdetect/objdetect.hpp>
#include<iostream>
#include<opencv2\core\core.hpp>
#include<opencv2\opencv.hpp>
#include<opencv2\highgui.hpp>
#include<cv.h>
using namespace std;
using namespace cv;
String face_config_name = "D:/whz_tools/opencv/opencv/vc14x64/etc/haarcascades/haarcascade_frontalface_default.xml";
CascadeClassifier face_cascade;
vector<Rect_<int> > faces;
int main()
{
//存放照片
string file_path_in = "D:/workspace/VS/opencv/face_detect/face_detect/data/photo/";
//存放结果的人脸
string file_path_out = "D:/workspace/VS/opencv/face_detect/face_detect/data/face/";
vector<string> files;
get_files(file_path_in, files); //获取目录下的文件名
Mat img,img_gray,faceROI;
string tmp_file_name;
assert(face_cascade.load(face_config_name));//导入分类器
for (int i = 0; i < files.size(); ++i)
{
tmp_file_name = file_path_in + files[i];
img = imread(tmp_file_name);
assert(!img.empty());
cvtColor(img, img_gray, CV_BGR2GRAY);//转化成灰度图来加快人脸识别的速度
equalizeHist(img_gray, img_gray); //直方图均衡化
face_cascade.detectMultiScale(img_gray, faces, 1.1, 2, CV_HAAR_SCALE_IMAGE, Size(30, 30));
if (faces.size() > 0)
{
for (int j = 0; j < faces.size(); ++j)
{
faceROI = img(faces[j]); //将img中人脸区域赋值给faceROI
if (faceROI.cols > 100)
{
resize(faceROI, faceROI, Size(92, 112));//调整大小
tmp_file_name = file_path_out + files[i];
imwrite(tmp_file_name, faceROI);
}
/*
也可以不调整大小,直接在原图中画出矩形
Point p1 = Point(faces[i].x, faces[i].y);
Point p2 = Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height);
rectangle(img, p1, p2, Scalar(255, 0, 0), 2, 8, 0);
*/
}
}
}
return 0;
}
对于上面的代码进行如下说明:
- haarcascade_frontalface_default.xml是opencv提供的Haar分类器的xml文件,其中存放了描述物体的Haar特征值,直接导入该文件就可以使用Haar分类器,也可以使用其它自带xml文件(如haarcascade_frontalface_alt2.xml)。该文件最好使用绝对路径导入,若导入失败就不能坚信人脸检测;
vector<Rect_<int> > faces;
用来存放识别后的人脸矩形框,最好定义成全局变量,有网友说局部变量在Debug时会出现问题;resize(faceROI, faceROI, Size(92, 112));
这里之所以需要调整成92 * 112的大小是为了后续人脸识别做准备,人脸识别选择的是AT&T的The ORL Database of Faces数据库,其中包含了40个不同人的脸,每个人有10张照片,而该数据集的图片大小就是92 * 112;
获取目录下的文件名
这里希望能够自动化处理采集的图像,因此需要自动获取目录下的文件名,然后根据文件名自动处理每个图像。也就是上面代码中get_files
函数:
#include<iostream>
#include<io.h>
#include<string>
using namespace std;
/*获取目录下的文件名*/
void get_files(string file_path, vector<string>& files)
{
long file_handler = 0; //文件句柄
_finddata_t fileinfo;
string p;
if ((file_handler = _findfirst(file_path.append("\\*").c_str(), &fileinfo)) == -1)
{
cout << file_path<<" not found"<< endl;
}
else
{
while (_findnext(file_handler, &fileinfo) == 0)
{
if (strcmp(fileinfo.name, ".") == 0 || strcmp(fileinfo.name, "..") == 0)
continue;
files.push_back(fileinfo.name);
}
}
_findclose(file_handler);
}
获取目录下的文件中的关键的结构是_finddata_t
,定义在io.h头文件中:
struct _finddata_t
{
unsigned attrib; //文件的存储属性
time_t time_create; //文件创建时间
time_t time_access; //最后一次被访问的时间
time_t time_write; //最后一次被修改的时间
_fsize_t size; //文件大小
char name[_MAX_FNAME]; //文件名
};
该结构用来存储文件信息,而查找就需要靠_findfirst
,_findnext
,_fineclose
:
/*@return 若查找成功,将返回long型的文件句柄,该句柄在_findnext中使用;若失败则返回-1;
@param filespec:文件名,支持通配符;
@param fileinfo:存放文件信息的结构体指针;*/
long _findfirst( char *filespec, struct _finddata_t *fileinfo );
/*@return 若成功返回0,否则返回-1;
@param handle:_findfirst返回的文件句柄
@param fileinfo:存放文件信息的结构体指针,找到文件后放入此结构;*/
int _findnext( long handle, struct _finddata_t *fileinfo );
/*@return 结束查找,若成功返回0,否则返回-1;
@param handle:_findfirst返回的文件句柄*/
int _findclose( long handle );
结果
当程序运行结束时就可以得到92 * 112的人脸图像,可以进行后续的人脸识别了!