在人脸识别之前需要获取人脸的训练集,这里利用的是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
#include
#include
using namespace cv::face;
using namespace std;
using namespace cv;
vector faces;
vector labels;
//使用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()
{
string cvs_path = "./face/at.txt";
Mat test_face;
int test_label,p_label;
int sample_num = 0;
try
{
read_cvs(cvs_path, faces, labels); //读取人脸图像和标签
}
catch (cv::Exception& e)
{
cerr << "Error opening file.Reason:" << e.msg << endl;
exit(1);
}
if (faces.size() <= 1)
{
cout << "Too few face images" << endl;
return -1;
}
/*从训练集中取出一张图片作为测试图*/
sample_num = faces.size();
test_face = faces[sample_num - 1];
test_label = labels[sample_num - 1];
faces.pop_back();
labels.pop_back();
/* 创建人脸识别的模型,并进行训练,之后保存训练结果*/
Ptr eigen_model = EigenFaceRecognizer::create();
eigen_model->train(faces, labels);
eigen_model->save("my_eigen_face_model.xml");
Ptr fisher_model = FisherFaceRecognizer::create();
fisher_model->train(faces, labels);
fisher_model->save("my_fisher_face_model.xml");
Ptr lbph_model = LBPHFaceRecognizer::create();
lbph_model->train(faces, labels);
lbph_model->save("my_lbph_face_model.xml");
/*对人脸进行测试,查看是否能够识别*/
p_label = eigen_model->predict(test_face);
cout << "Test label is:" << test_label << ",predict label is:"<< p_label << endl;
p_label = fisher_model->predict(test_face);
cout << "Test label is:" << test_label << ",predict label is:" << p_label << endl;
p_label = lbph_model->predict(test_face);
cout << "Test label is:" << test_label << ",predict label is:" << p_label << endl;
return 0;
}
训练和测试使用灰度图。EigenFaceRecognizer
、 FisherFaceRecognizer
两个类在调用train
训练时可以使用BGR图像,但是训练结果中保存的是单通道的灰度图结果。可以查看生成的训练结果文件my_eigen_face_model.xml
:
可以看到在训练结果中保存的训练集是1*10304大小的图片,而我训练集图片大小为92 * 112(92 * 112 = 10304)。在对测试图进行人脸识别时,必须确保测试图大小与保存的训练集大小一致,否则会报错。而LBPHFaceRecognizer
类必须使用灰度图进行训练,不然也会报错。
可以看到程序可以对人脸进行识别,正确给出标签值。