首先是人脸检测:
OpenCV中自带的人脸检测类是一个分类器CascadeClassifier,利用滑动窗口机制+级联分类器的方式,我初步理解为在这个图像金字塔中的每一张图片都去做一次特征匹配。
首先包含头文件:(按照需求可能不止这些include)
#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/face.hpp"
using namespace std;
using namespace cv;
using namespace cv::face;
我在使用的时候将这个分类器定义为静态全局变量:
static CascadeClassifier cascadeClassifier; //人脸检测器
在初始化的时候对它进行初始化:
cascadeClassifier.load(faceCascadePath);
这个load中的文件路径是训练好的人脸检测分类器,提取的是Haar特征,描述这个特征的文件存放在了xml格式的的文件中。
(D:\OpenCV_3_2_0\opencv\sources\data\haarcascades 这里面还有好多表示其他特征的分类器参数)
Haar特征分为三类:边缘特征、线性特征、中心特征和对角线特征,组合成特征模板,Haar特征值反映了图像的灰度变化情况。如果后面有需要我们也可以利用Haar特征去训练自己的分类器,不过我还没有看。
初始化完成后就可以调用他来检测人脸了,检测的函数:
void cv::CascadeClassifier::detectMultiScale(
InputArray image, //原始图片 灰度图
std::vector< Rect > & objects, //人脸位置的矩形向量
double scaleFactor = 1.1, //图像金字塔中每一次缩放的倍数
int minNeighbors = 3, //匹配成功所需要的周围矩形框的数目,每一个特征匹配到的区域都是一个矩形框,只有多个矩形框同时存在的时候,才认为是匹配成功
int flags = 0,
Size minSize = Size(), //这两个是匹配物体的范围
Size maxSize = Size()
)
flags可能的值:
CASCADE_DO_CANNY_PRUNING=1, 利用canny边缘检测来排除一些边缘很少或者很多的图像区域
CASCADE_SCALE_IMAGE=2, 正常比例检测
CASCADE_FIND_BIGGEST_OBJECT=4, 只检测最大的物体
CASCADE_DO_ROUGH_SEARCH=8 初略的检测
上面输入图片一定要是灰度图,大小无所谓(越大执行的越慢罢了)。通过上面的两步就可以执行人脸检测的功能能,还是很方便的。
接下来就到了人脸识别的环节了。
这里利用了OpenCV自带的识别类FaceRecognizer,他支持的识别方式有 特征脸方法(Eigenface),Fisherface(LDA),LBP(局部二值模式)。
Eigenface:
基于PCA(主成分分析)的检测模式,在训练集中提取人脸的相对值并计算特征向量, 识别时通过提取人脸的特征向量并计算和每个训练集的欧氏距离来判断是谁的脸。
Fisherface:
这个我没怎么看懂(我还是太辣鸡了),不知道是不是将待识别的图像投影到不同人脸特征上然后分类?
LBPH:
在3*3的窗口内,以窗口中心像素为阈值,将相邻的8个像素的灰度值与其进行比较,若周围像素值大于中心像素值,则该像素点的位置被标记为1,否则为0。这样,3*3邻域内的8个点经比较可产生8位二进制数(通常转换为十进制数即LBP码,共256种),即得到该窗口中心像素点的LBP值,并用这个值来反映该区域的纹理信息。也有做圆形的改进。在图片的每个子区域采用LBP模式的统计直方图来表达图像的信息,再连接成为一个特征向量来表达整幅图片,然后用SVM来分类。
以上三种方法均要求用来训练的图片为灰度图片,前两种要求输入的图片大小相同,而LBP则不需要等大的图片。
在代码中创建这三种识别器的语句是:
Ptr faceRecognizer;
faceRecognizer = createEigenFaceRecognizer();
faceRecognizer = createFisherFaceRecognizer();
faceRecognizer = createLBPHFaceRecognizer();
不同的识别器用的是不同的识别方法。
由于一些原因,我选择了LBPH识别方式,因为只有这个可以动态添加人脸到已经存在的人脸空间(update),避免模型全部重新训练。
下面是训练用的代码:
Ptr<FaceRecognizer> faceLBPHClass ;
faceLBPHClass = createLBPHFaceRecognizer();
faceLBPHClass->update(vec_facesTraining, vec_faceLabels);
//update可以动态的添加进已存在的人脸xml中,
//不用再把新的所有人脸保存在调用train全训练一遍。
其中vec_facesTraining 是Mat向量,为人脸灰度图片的向量,vec_faceLabels为 int的向量,为对应的标签向量。
这里就训练结束了,我们可以把它保存成xml方便下次调用:
//保存xml文件
faceLBPHClass->save(faceTrainedXMLPath);
//加载xml
faceLBPHClass->load(faceTrainedXMLPath);
最后做预测:
//预测
int label = faceLBPHClass->predict(face);
调用faceLBPHClass中的predict函数,参数是需要被是别的脸,返回的是训练时的Label。
OpenCV自带的检测的实现很容易,但是里面的原理我还是没搞明白,太菜了- -。
参考资料:
CascadeClassifier:
https://blog.csdn.net/gdut2015go/article/details/48826159
https://blog.csdn.net/cooli7wa/article/details/53959195
Haar特征:
https://blog.csdn.net/lanxuecc/article/details/52222369
特征脸Eigenface:
https://blog.csdn.net/feirose/article/details/39552887
Fisherface(LDA):
https://blog.csdn.net/feirose/article/details/39552997
LBP:
https://blog.csdn.net/shenziheng1/article/details/72582197
上面算法的原理和差异:
https://www.cnblogs.com/little-monkey/p/8118938.html