前言
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.从网上下载带有人脸的图像和不带人脸的图像,图像大小没有什么要求,我这里是壁纸网站下载的美女壁纸,如下图:
2.找到自己安装的OpenCV的源码文件夹,在源码文件下有个Data的目录,打开目录下的haarcascades这个文件夹,可以看到里面有官方训练好的级联分类器。
二、代码实现
代码我这里封装了一个类,两个方法,一个用来检测人脸的,一个用来检测人脸和眼睛的,然后在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).有误检测了地方。
(2)完全成检测到人脸。
(3)检测不到,如果调下参数或用另一个分类能检测到。
2.人脸与眼睛的检测效果
(1)完全检测到人脸和眼睛。
(2)有漏检测和误检测的眼睛。
(3)有检测不到的眼睛的。
结语
1.检测结果并不理想,对检测准确率影响有环境的因素,和传给检测器之前的对图像的处理,设置检测的分类的参数等。
2.如果要在相对稳定的环境下使用,最好的方法是使用该环境下的人脸样品自己训练模型。
3.之后我会试用opencv和caffe的训练自己的数据模型,看是否能达到官方分类器的准确率。