前言

1.OpenCV官方训练好的人脸和眼睛的级联分类器,3.30的版本都放在opencv\sources\data这个文件夹下,在OpenCV这个文件夹中,主要有 Haar特征 和 LBP特征进行人脸检测,其中“lbpcascades”,“haarcascades”,“hogcascades”,这三个文件夹,分别放表示通过“haar”、“hog”和“lbp”三种不同的特征而训练出的分类器。"haar"特征主要用于人脸检测,“hog”特征主要用于行人检测,“lbp”特征主要用于人脸识别。
2.人脸检测用的“haarcascades”文件夹里有检测正脸的,检测眼睛的,检测嘴巴的,检测笑脸的等几种分类器,这里我只试了常用的人脸检测器和眼睛的检测器。
3.我用的是OpenCV3.30这个,IDE是vs2015,Win7 64位,boost库,其中boost是用来操作文件的,为了方便测试整个文件夹的图像,实现语言是C++。

一、资源准备

1.从网上下载带有人脸的图像和不带人脸的图像,图像大小没有什么要求,我这里是壁纸网站下载的美女壁纸,如下图:

opencv人脸居中裁剪 opencv3人脸识别_计算机视觉


2.找到自己安装的OpenCV的源码文件夹,在源码文件下有个Data的目录,打开目录下的haarcascades这个文件夹,可以看到里面有官方训练好的级联分类器。

opencv人脸居中裁剪 opencv3人脸识别_opencv人脸居中裁剪_02

二、代码实现

代码我这里封装了一个类,两个方法,一个用来检测人脸的,一个用来检测人脸和眼睛的,然后在main里调用。
1.类声明部分

#pragma once
#include <opencv2/opencv.hpp>
#include <opencv2\core\core.hpp>
#include <opencv\highgui.h>
#include <string>
//boost 库(读取文件)
#include<boost/filesystem.hpp>
//定义一个boost库的命名空间
namespace fs = boost::filesystem;
using namespace std;
using namespace cv;

class FaceDetection
{
public:
	~FaceDetection();
	//人脸和眼睛的构造函数
	FaceDetection(string _image_path, string _face_path, string _eye_path);
	//检测人脸的方法
	void frontalFaceDetection();
	//检测人脸和眼睛的方法
	void faceAndeye();
	Mat eyeDetection(Mat image);
	Mat detection(Mat image);
private:
	//人脸检测的分类器路径
	string face_path;
	//图像路径或存放图像的路径
	string image_path;
	//眼睛的分类器路径
	string eye_path;

	CascadeClassifier face_classifier;
	CascadeClassifier eye_cascader;
};

2.类的实现文件

#include "FaceDetection.h"

FaceDetection::FaceDetection(string _image_path, string _face_path, string _eye_path)
{
	face_path = _face_path;
	//判断模型路径
	fs::path face_xml(face_path);
	if (!fs::exists(face_xml))
	{
		std::cout << "请转入正确的模型文件路径!" << std::endl;
		exit(0);
	}
	eye_path = _eye_path;
	//判断模型路径
	fs::path eye_xml(eye_path);
	if (!fs::exists(eye_xml))
	{
		std::cout << "请转入正确的模型文件路径!" << std::endl;
		exit(0);
	}
	image_path = _image_path;
	//判断图像路径
	fs::path src(image_path);
	if (!fs::exists(src))
	{
		std::cout << "传入的图像路径错误!" << std::endl;
		exit(0);
	}
}


FaceDetection::~FaceDetection()
{
}

void FaceDetection::frontalFaceDetection()
{
	//判断模型是否能打开
	if (!face_classifier.load(face_path))
	{
		std::cout << "模型文件无法读取!" << std::endl;
		exit(0);
	}
	//判断传入的是否是文件夹
	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_path = begin_iter->path().string();
			if (!fs::is_directory(file_path))
			{
				string image_path = begin_iter->path().string();
				Mat src = imread(image_path);
				if (!src.empty())
				{
					Mat src_image = imread(image_path);
					Mat image = detection(src_image);
					
					imshow("detect faces", image);
					waitKey(30);
				}
			}
		}
	}
	else
	{
		Mat src_image = imread(image_path);
		if(!src_image.empty())
		{
			Mat image = detection(src_image);
			imshow("detect faces", image);
			waitKey(30);
		}
	}
}

void FaceDetection::faceAndeye()
{
	namedWindow("face_eye", CV_WINDOW_AUTOSIZE);
	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_path = begin_iter->path().string();
			if (!fs::is_directory(file_path))
			{
				string image_path = begin_iter->path().string();
				Mat src = imread(image_path);
				if (!src.empty())
				{
					Mat src_image = imread(image_path);
					Mat image = eyeDetection(src_image);
					imshow("face_eye", image);
					waitKey(30);
				}
			}
		}
	}
	else
	{
		Mat src_image = imread(image_path);
		if (!src_image.empty())
		{
			Mat image = eyeDetection(src_image);
			imshow("face_eye", image);
			waitKey(30);
		}
	}
}

Mat FaceDetection::eyeDetection(Mat frame)
{
	if (!face_classifier.load(face_path)) 
	{
		std::cout << "无法读取人脸的分类文件!" << std::endl;
		exit(0);
	}
	if (!eye_cascader.load(eye_path))
	{
		std::cout << "无法读取眼睛的分类文件!" << std::endl;
		exit(0);
	}
	Mat gray;
	vector<Rect> faces;
	vector<Rect> eyes;
	cvtColor(frame, gray, COLOR_BGR2GRAY);
	equalizeHist(gray, gray);
	face_classifier.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);
		eye_cascader.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);
		}
		rectangle(frame, faces[static_cast<int>(t)], Scalar(0, 255, 0), 2, 8, 0);
	}	
	return frame;
}

Mat FaceDetection::detection(Mat image)
{
	if (image.empty())
	{
		std::cout << "无法打开传入的图像文件!" << std::endl;
		exit(0);
	}

	Mat gray;
	//灰度化
	cvtColor(image, gray, COLOR_BGR2GRAY);
	//直方图均衡化,用于提高图像的质量
	/*有时提高了图像质量之后,检测的人脸有漏检的现象*/
	//equalizeHist(gray, gray);

	//存放检测到人脸的矩形
	vector<Rect> faces;

	//开始检测
	face_classifier.detectMultiScale(gray, faces, 1.2, 3, 0, Size(24, 24));

	//画出检测到的矩形的位置
	for (size_t t = 0; t < faces.size(); t++)
	{
		rectangle(image, faces[static_cast<int>(t)], Scalar(0, 0, 255), 2, 8, 0);
	}

	return image;
}

3.main文件

#include "FaceDetection.h"
//人脸检测的分类器
string face_file = "E:/LIB/opencv330/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
//眼睛检测的分类器
string eye_file = "E:/LIB/opencv330/opencv/sources/data/haarcascades/haarcascade_eye.xml";
//图像路径或目录
string image_path = "C:/Users/matt/Desktop/face/";

int main()
{
	
	FaceDetection face_eye(image_path,face_file,eye_file);
	//检测人脸
	//face_eye.frontalFaceDetection();
	//检测人脸与眼睛的
	face_eye.faceAndeye();

	system("pause");
	return 0;
}

三、测试结果

1.人脸检测,有漏检的,也有误检的。

(1).有误检测了地方。

opencv人脸居中裁剪 opencv3人脸识别_人脸检测_03


(2)完全成检测到人脸。

opencv人脸居中裁剪 opencv3人脸识别_opencv_04


(3)检测不到,如果调下参数或用另一个分类能检测到。

opencv人脸居中裁剪 opencv3人脸识别_opencv人脸居中裁剪_05


2.人脸与眼睛的检测效果

(1)完全检测到人脸和眼睛。

opencv人脸居中裁剪 opencv3人脸识别_人脸检测_06


(2)有漏检测和误检测的眼睛。

opencv人脸居中裁剪 opencv3人脸识别_opencv人脸居中裁剪_07


(3)有检测不到的眼睛的。

opencv人脸居中裁剪 opencv3人脸识别_人脸检测_08

结语

1.检测结果并不理想,对检测准确率影响有环境的因素,和传给检测器之前的对图像的处理,设置检测的分类的参数等。
2.如果要在相对稳定的环境下使用,最好的方法是使用该环境下的人脸样品自己训练模型。
3.之后我会试用opencv和caffe的训练自己的数据模型,看是否能达到官方分类器的准确率。