您的位置:首页 > 运维架构

【OpenCV人脸识别入门教程之四】LBP人脸识别

2015-11-10 20:33 453 查看
本文使用OpenCV实现摄像头实时LBP人脸识别,这里不讲解LBP人脸识别的原理,小路孩会另外专门写特征提取的系列博客,到时会详细讲解LBP特征,敬请关注。才疏学浅,有错误在所难免,欢迎指正。

系统:Windows7;OpenCV版本:2.4.10.

一、准备工作

1、人脸识别的简单流程

人脸识别的简单流程一般包括:人脸检测、图像预处理、特征提取和人脸识别。下面分别进行介绍。

人脸检测,使用Haar+adaboost算法,通过OpenCV人脸检测实现,在人脸识别系列博客第二篇已给出代码。

图像预处理,一般对检测到的人脸图像作直方图或者滤波处理,以可以更好的提取特征。

特征提取,通过提取人脸特征,然后对这些特征进行分类进而实现人脸识别,用于描述人脸的特征一般有LBP、Gabor、HOG和SIFT等,目前十分火热的卷积神经网络也是在提取特征。

人脸识别,换句话说就是人脸分类器,将提取出的特征进行分类,一般的分类器包括欧式距离、马氏距离、SVM、贝叶斯分类器等等。

2、OpenCV-LBP人脸识别相关类和函数

首先,还是说一下LBP特征的原理,方便后面的阐述。

原始的LBP算法的基本思想是在3*3的窗口内,以窗口中心像素为阈值,将相邻的8个像素的灰度值与其进行比较,若周围像素值大于中心像素值,则该像素点的位置被标记为1,否则为0。这样,3*3邻域内的8个点经过比较可产生8位二进制数,如图1中00010011(通常转换为十进制数即LBP码,共256种),即得到该窗口中心像素点的LBP值,并用这个值来反映该区域的纹理信息。如下图所示:



通过对全局图像进行LBP特征提取得到LBP图,LBP特征图是不能直接来作人脸识别的,需要对LBP特征图进行分块并计算每个分块的直方图,通过直方图的统计信息进行识别,最后将各块的直方图首尾相连就得到一张图片最终的LBP特征描述向量。计算两张图片的LBP特征向量的相似度即可实现人脸识别。

OpenCV中LBPH人脸识别类如下进行创建

Ptr<FaceRecognizer> LBPHRecog = createLBPHFaceRecognizer(1, 8 ,3, 3, 50);         //构造LBPH人脸识别类的对象并初始化


下面看一下构造函数的原型,进而解释一下参数的含义

CV_EXPORTS_W Ptr<FaceRecognizer> createLBPHFaceRecognizer(int radius=1, int neighbors=8,
int grid_x=8, int grid_y=8, double threshold = DBL_MAX);


可以看到,参数是有默认值得,各个参数的含义如下:

int radius = 1 :中心像素点到周围像素点的距离,相邻像素距离为1,以此类推
int neighbors = 8 :选取的周围像素点的个数
int grid_x= 8 :将一张图片在x方向分成8块
int grid_y = 8 :将一张图片在y方向分成8块
double threshold = DBL_MAX :LBP特征向量相似度的阈值,只有两张图片的相似度小于阈值才可认为识别有效,大于阈值则返回-1

构造好LBPH人脸识别的对象,下面分别来看看训练和识别的函数:

LBPHRecog->train(trainPic, labels);         //LBP人脸识别训练函数


训练函数参数中的trainPic是一个vector,所有需要训练的图片都被包含在内,labels也是vector,存储与trainPic中对应训练图片的名字,这样从trainPic中得到的与识别的图片最相似的图片的名字即可在labels中查找到。

result = LBPHRecog->predict(recogPic);//进行识别,并返回识别结果


返回的识别结果即是对应的labels中的名字。

二、代码实现

下面直接来看代码,程序会打开笔记本的摄像头,实现实时人脸识别,并将识别结果显示在画面上。

//头文件
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/contrib/contrib.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/objdetect/objdetect.hpp>
#include<iostream>
#include<io.h>

//命名空间
using namespace std;
using namespace cv;

CascadeClassifier faceDetect;   //人脸检测对象

//函数声明
vector<Rect> FaceDetector(Mat img);//检测人脸,并返回人脸区域
void DrawFace(Mat img, vector<Rect> faces);//将人脸标注出来
void GetFilesName(string path, vector<string>& filesName);//获取文件夹里的所有文件名称

int main()
{
//变量
Mat frame;
Mat src;
Mat facePic;
Mat recogPic;

vector<Rect> faces;
vector<string> filesName;
string path = "..\\TrainPic";
string pic;

vector<Mat> trainPic;//训练图片
vector<int> labels;//与训练图片一一对应的标签
int result;
map<int, string> labelsInfo;
faceDetect.load("../models/haarcascade_frontalface_alt2.xml");

Ptr<FaceRecognizer> LBPHRecog = createLBPHFaceRecognizer(1, 8 ,3, 3, 50);//构造LBPH人脸识别类的对象并初始化
//加载人脸库
GetFilesName(path, filesName);
for(int i = 0; i<filesName.size(); i++)
{
pic = filesName[i];
src = imread(pic);
cvtColor(src, src, CV_RGB2GRAY);
trainPic.push_back(src);
labelsInfo.insert(make_pair(i, filesName[i]));
labels.push_back(i);
}
//LBPHRecog->setLabelsInfo(labelsInfo);
LBPHRecog->train(trainPic, labels);         //LBP人脸识别训练函数

VideoCapture cap(0);//打开摄像头
int c=0;
if(!cap.isOpened())
{
cerr<<"摄像头无法打开"<<endl;
}

while(c!= 27)
{
cap>>frame;
flip(frame, frame, 1);
recogPic = Mat::zeros(200, 200, frame.type());
double t = (double)getTickCount();//检测的时间
faces = FaceDetector(frame);
t = ((double)getTickCount() - t)/getTickFrequency();
cout<<t<<endl;
DrawFace(frame, faces);

for(size_t i = 0; i<faces.size(); i++)
{
facePic = frame(Rect(faces[i]));
resize(facePic, recogPic, recogPic.size());
cvtColor(recogPic, recogPic, CV_RGB2GRAY);
//equalizeHist(recogPic, recogPic);
result = LBPHRecog->predict(recogPic);//进行识别
//将识别结果显示在实时画面上
if(result ==-1)
putText(frame, "unknow", Point(faces[i].x, faces[i].y), 3, 0.5, Scalar(0, 255, 255), 1);
else
putText(frame, filesName[result], Point(faces[i].x, faces[i].y), 3, 0.5, Scalar(0, 255, 255), 1);
}

imshow("1", frame);

c =waitKey(1);
}

return 0;
}

vector<Rect> FaceDetector(Mat img)
{
Mat src = Mat::zeros(240, 320, img.type());
vector<Rect> faces;
cvtColor(img, img, CV_RGB2GRAY);
resize(img, src, src.size());
faceDetect.detectMultiScale(src, faces, 1.2, 6, 0,Size(30,30));
for(int i=0; i<faces.size(); i++)
{
faces[i].x=faces[i].x*2;
faces[i].y=faces[i].y*2;
faces[i].width=faces[i].width*2;
faces[i].height=faces[i].height*2;
}
return faces;
}

void DrawFace(Mat img, vector<Rect> faces)
{
for(size_t num = 0; num<faces.size(); num++)
{
rectangle(img, Point(faces[num].x, faces[num].y),
Point(faces[num].x + faces[num].width, faces[num].y + faces[num].height), Scalar(0, 255, 0), 1, 8);
}
}

void GetFilesName(string path, vector<string>& filesName)
{
//文件句柄
long   hFile   =   0;
//文件信息
struct _finddata_t fileinfo;
string p;
if((hFile = _findfirst(p.assign(path).append("\\*").c_str(),&fileinfo)) !=  -1)
{
do
{
//如果是目录,迭代之
//如果不是,加入列表
if((fileinfo.attrib &  _A_SUBDIR))
{
if(strcmp(fileinfo.name,".") != 0  &&  strcmp(fileinfo.name,"..") != 0)
GetFilesName( p.assign(path).append("\\").append(fileinfo.name), filesName );
}
else
{
filesName.push_back(p.assign(path).append("\\").append(fileinfo.name));
}
}while(_findnext(hFile, &fileinfo)  == 0);
_findclose(hFile);
}
}


注意:上述代码中人脸图片的训练部分并不具有通用性,需要根据实际运用进行修改,这部分就由读者自己来完成吧 :)

代码运行的结果图就不贴上去了,见谅~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: