opencv支持3种人脸识别的算法,分别是:
1. Eigen Faces PCA(特征脸方法)
2. Fisher Faces LDA(线性判别分析)
3. Local Binary Pattern Histograms(LBP 局部二值模式直方图)
详细介绍可以参照以下链接
https://blog.csdn.net/wanghz999/article/details/78817265
https://blog.csdn.net/u010006643/article/details/46417127
以下是部分摘抄:
Eigenfaces就是特征脸的意思,是一种从主成分分析(Principal Component Analysis,PCA)中导出的人脸识别和描述技术。特征脸方法的主要思路就是将输入的人脸图像看作一个个矩阵,通过在人脸空间中一组正交向量,并选择最重要的正交向量,作为“主成分”来描述原来的人脸空间。为了更好地理解特征脸方法,需要先了解PCA的主要过程。
在很多应用中,需要对大量数据进行分析计算并寻找其内在的规律,但是数据量巨大造成了问题分析的复杂性,因此我们需要一些合理的方法来减少分析的数据和变量同时尽量不破坏数据之间的关联性。于是这就有了主成分分析方法,PCA作用:
PCA的主要过程:
特征脸方法就是将PCA方法应用到人脸识别中,将人脸图像看成是原始数据集,使用PCA方法对其进行处理和降维,得到“主成分”——即特征脸,然后每个人脸都可以用特征脸的组合进行表示。这种方法的核心思路是认为同一类事物必然存在相同特性(主成分),通过将同一目标(人脸图像)的特性寻在出来,就可以用来区分不同的事物了。人脸识别嘛,就是一个分类的问题,将不同的人脸区分开来。特征脸方法的过程(先计算特征脸,然后识别人脸):
reshape(1,1)
),然后组合在一起形成一个大矩阵A。若人脸图像大小为m * m,则矩阵A的维度是m * m * N;在opencv中void EigenFaceRecognizer::train();
就是对训练集进行处理最终得到特征向量和阈值的过程,其中的阈值用于后面对测试图像的识别。int EigenFaceRecognizer:: predict(InputArray src) const;
是对测试图像进行识别。
要让系统准确识别需要保证人脸图像满足:
若不满足此条件,识别错误率很高。从PCA方法的过程可以看出,特征脸识别的方法是以每张人脸的一个维度(可以看出是矩阵的一列)为单位进行处理的,求得的特征向量(特征脸)中包含训练集每个纬度的绝大部分信息。但是若测试集中人脸尺寸不同,那么与特征脸中维度的也就没法对应起来。
在人脸识别之前需要获取人脸的训练集,这里利用的是The ORL Database of Faces’数据库,也可以自己采集人脸图像进行检测然后调整大小,具体的过程可以参照:http://blog.csdn.net/wanghz999/article/details/78751406
人脸识别的过程就是对人脸图像进行训练然后读取人脸并进行识别。在训练时,计算机会对每张人脸的多张图像进行特征提取,然后根据特征值来区分不同的人脸。然而计算机并不知道两种不同的人脸彼此是哪张,因此需要告诉预先告诉计算机一个标签——区分不同的人脸。cvs文件就是包含人脸图像路径和标签的文件,格式如下:
D:/workspace/VS/opencv/face_recg/face_recg/face/s1/1.pgm;0
D:/workspace/VS/opencv/face_recg/face_recg/face/s1/10.pgm;0
D:/workspace/VS/opencv/face_recg/face_recg/face/s1/2.pgm;0
D:/workspace/VS/opencv/face_recg/face_recg/face/s1/3.pgm;0
D:/workspace/VS/opencv/face_recg/face_recg/face/s1/4.pgm;0
...
;之前的表示人脸图像的路径,;
之后的0
表示标签。opencv官方提供了create_cvs.py,该文件位于源码目录(OPENCV_SOURCE_DIR
)的opencv_contrib模块目录下:
OPENCV_SOURCE_DIR/opencv_contrib/modules/face/samples/etc
也可以直接从github上获取:https://github.com/opencv/opencv_contrib/tree/master/modules/face/samples/etc
这里给出我使用的(python3.*适用):
#!/usr/bin/env python
import sys
import os.path
# This is a tiny script to help you creating a CSV file from a face
# database with a similar hierarchie:
#
# philipp@mango:~/facerec/data/at$ tree
# .
# |-- README
# |-- s1
# | |-- 1.pgm
# | |-- ...
# | |-- 10.pgm
# |-- s2
# | |-- 1.pgm
# | |-- ...
# | |-- 10.pgm
# ...
# |-- s40
# | |-- 1.pgm
# | |-- ...
# | |-- 10.pgm
#
if __name__ == "__main__":
#if len(sys.argv) != 2:
# print "usage: create_csv "
# sys.exit(1)
#BASE_PATH=sys.argv[1]
BASE_PATH="D:/workspace/VS\opencv/face_recg/face_recg/face/"
SEPARATOR=";"
fh = open("at.txt",'w')
label = 0
for dirname, dirnames, filenames in os.walk(BASE_PATH):
for subdirname in dirnames:
subject_path = os.path.join(dirname, subdirname)
for filename in os.listdir(subject_path):
abs_path = "%s/%s" % (subject_path, filename)
print ("%s%s%d" % (abs_path, SEPARATOR, label))
fh.write(abs_path)
fh.write(SEPARATOR)
fh.write(str(label))
fh.write("\n")
label = label + 1
fh.close()
其中,BASE_PATH
表示存放人脸图像的文件夹,在该文件夹下,每个文件夹代表一个人脸,一个人脸应该有多张大小一样的图。该文件夹如下:
opencv支持3种人脸识别的算法,分别是:
自opencv3.0开始,人脸识别的模块也有许多变动,实现跟以前一些很多不一样。上述的3种算法在opencv3.*中对应三个类:EigenFaceRecognizer
、FisherFaceRecognizer
和LBPHFaceRecognizer
,它们都是从BasicFaceRecognizer
继承过来的子类。这三个类有相似的API函数(以EigenFaceRecognizer
为例):
/*创建人脸识别的模型,若该两个参数为空,会以默认值创建
@param num_components:进行训练的人脸图片数
@param threshold: 阈值*/
Ptr EigenFaceRecognizer::create(int num_components,
double threshold);
/*对模型进行训练
@param src:进行训练的人脸图像
@param labels:标签值,每张相同的人脸都有标签值来区分*/
void train(InputArrayOfArrays src, InputArray labels);
/*利用训练好的模型进行人脸识别,返回标签值
@param src:需要进行识别的图像/
CV_WRAP_AS(predict_label) int predict(InputArray src) const;
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
using namespace cv::face;
//使用CVS文件读取图片和标签
void read_cvs(const string& filename, vector& img, vector& lable, char separator = ';')
{
Mat tmp_img;
ifstream cvs_file(filename.c_str());
assert(cvs_file);
string line, path, tag;
while (getline(cvs_file, line))
{
stringstream lines(line);
getline(lines, path, separator);
getline(lines, tag);
if (!path.empty() && !tag.empty())
{
tmp_img = imread(path, IMREAD_GRAYSCALE); //读入图像时转成灰度图
assert(!tmp_img.empty());
img.push_back(tmp_img);
lable.push_back(atoi(tag.c_str()));
}
}
tmp_img.release();
}
int main()
{
vector faces;
vector labels;
string cvs_path = ".\\face_recg\\face\\at.txt";
try
{
read_cvs(cvs_path, faces, labels); //读取人脸图像和标签
}
catch (cv::Exception e)
{
printf("Error open file, reason: %s\n", e.msg);
return -1;
}
if (faces.size() <= 1)
{
printf("Too few face images\n");
return -1;
}
/* 从训练集中读取一张图片作为测试图 */
Mat test_face;
int sample_num = 0;
int test_label;
sample_num = (int)faces.size();
test_face = faces[sample_num - 1];
test_label = labels[sample_num - 1];
faces.pop_back();
labels.pop_back();
double start1, start2, start3;
double end1, end2, end3;
/* 创建人脸识别的模型并进行训练,之后保存训练结果 */
//PCA特征脸算法
start1 = (double)getTickCount();
Ptr eigen_model = EigenFaceRecognizer::create();
eigen_model->train(faces, labels);
eigen_model->save("my_eigen_face_model.xml");
end1 = (getTickCount() - start1) / getTickFrequency();
//LDA线性判别分析
start2 = (double)getTickCount();
Ptr fisher_model = FisherFaceRecognizer::create();
fisher_model->train(faces, labels);
fisher_model->save("my_fisher_face_model.xml");
end2 = (getTickCount() - start2) / getTickFrequency();
//LBP局部二值模式直方图
start3 = (double)getTickCount();
Ptr lbph_model = LBPHFaceRecognizer::create();
lbph_model->train(faces, labels);
lbph_model->save("my_lbph_face_model.xml");
end3 = (getTickCount() - start3) / getTickFrequency();
printf("PAC算法训练所用时间: %f s\n", end1);
printf("LDA算法训练所用时间: %f s\n", end2);
printf("LBP算法训练所用时间: %f s\n", end3);
//对人脸进行测试,查看是否能够识别
int p_label;
p_label = eigen_model->predict(test_face);
printf("(PCA)Test label is: %d, predict label is: %d\n", test_label, p_label);
p_label = fisher_model->predict(test_face);
printf("(LDA)Test label is: %d, predict label is: %d\n", test_label, p_label);
p_label = lbph_model->predict(test_face);
printf("(LBP)Test label is: %d, predict label is: %d\n", test_label, p_label);
waitKey(0);
return 0;
}
训练和测试使用灰度图。EigenFaceRecognizer
、 FisherFaceRecognizer
两个类在调用train
训练时可以使用BGR图像,但是训练结果中保存的是单通道的灰度图结果。可以查看生成的训练结果文件my_eigen_face_model.xml
:
可以看到在训练结果中保存的训练集是1*10304大小的图片,而我训练集图片大小为92 * 112(92 * 112 = 10304)。在对测试图进行人脸识别时,必须确保测试图大小与保存的训练集大小一致,否则会报错。而LBPHFaceRecognizer
类必须使用灰度图进行训练,不然也会报错。
可以看到程序可以对人脸进行识别,正确给出标签值。
从算法耗时来看,PAC最耗时,LDA次之,LBP最省时。
从生成的训练结果文件来看,eigen算法最大,Fisher算法最小。