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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lsq2902101015/article/details/49717441

本文使用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);  
    }
}


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

代码运行的结果图就不贴上去了,见谅~



猜你喜欢

转载自blog.csdn.net/lsq2902101015/article/details/49717441