官方文档:https://docs.opencv.org/3.4.3/dd/d81/facerec_8hpp.html

源代码在 .\opencv_contrib-3.4.3\modules\face\src中

Opencv实现

从OpenCV2.4开始,加入新的类FaceRecognizer,可以用它方便的进行人脸识别实验。

人脸识别的任务也就是两大部分,训练和预测,分别对应着train函数和predict函数,还有对应的数据加载保存函数save和load。

     先来说说训练的过程,train函数的两个参数也很简单,训练的图像组vector<Mat>和对应的标签组vector<int>,这个label标签只需保证同一个人的标签相同即可,不需要保证图像的按标签顺序输入。
    对于预测,有两种调用,其中的参数有测试图像、返回的标签值和测试样本和标签样本的相似性。返回的标签值为-1,说明测试样本在训练集中无对应或距离较远。

目前支持的算法有:

  1.        Eigenface特征脸   createEigenFaceRecognizer()
  2.        Fisherface             createFisherFaceRecognizer()
  3.       LBP局部二值直方图    createLBPHFaceRecognizer()

不过这些模块放在contri库中:

    CV_EXPORTS_W Ptr<FaceRecognizer> **createEigenFaceRecognizer**(int num_components = 0, double threshold = DBL_MAX);
    CV_EXPORTS_W Ptr<FaceRecognizer> **createFisherFaceRecognizer**(int num_components = 0, double threshold = DBL_MAX);
    CV_EXPORTS_W Ptr<FaceRecognizer> **createLBPHFaceRecognizer**(int radius=1, int neighbors=8,
                                                            int grid_x=8, int grid_y=8, double threshold = DBL_MAX);
  1. num_components为PCA主成分的维数;threshold:预测时的阈值。主成分这里有一个选取的准则,要根据输入数据的大小而决定,通常认为80维主成分是足够的。除了这两个输入参数外,还有eigenvalues和eigenvectors分别代表特征值和特征向量,mean参数为训练样本的平均值,projections为训练数据的预测值,labels为预测时的阈值。
  2. FisherFace,也有num_components和threshold两个参数和其他5个参数,FisherFace的降维是LDA得到的。特别需要强调的是,EigenFace和FisherFace的训练图像和测试图像都必须是灰度图,而且是经过归一化裁剪过的。
  3. 对于LBPHFace,参数包括半径radius,邻域大小即采样点个数neighbors,x和y方向的单元格数目grid_x,grid_y,还有两个参数histograms为训练数据得到的直方图,labels为直方图对应的标签。这个方法也要求训练和测试的图像是灰度图

from:https://blog.csdn.net/yang_xian521/article/details/7735224


Eigenface

1、PCA类是OpenCV实现主要成分分析的类,在人脸识别等机器学习的项目中大量应用,使用前需要先实例化对象。

函数原型:
PCA(InputArray data, InputArray mean, int flags, int maxComponents = 0);
PCA(InputArray data, InputArray mean, int flags, double retainedVariance);

参数说明:
data:需要PCA的数据,每一行(列)表示一个样本;
mean:平均值;如果矩阵是空的(noArray()),则从数据中计算; 
flags:操作标志,具体参数如下:
                            DATA_AS_ROW :每一行表示一个样本;
                            DATA_AS_COL :每一列表示一个样本;
maxComponents :PCA应保留的最大组件数;默认情况下,所有组件都保留;
retainedVariance:PCA应保留的方差百分比。使用这个参数将让PCA决定保留多少组件,但它将始终保持至少2。 

2、PCA::project函数
    该函数的作用是将输入数据vec(该数据是用来提取PCA特征的原始数据)投影到PCA主成分空间中去,返回每一个样本主成分特征组成的矩阵。因为经过PCA处理后,原始数据的维数降低了,因此原始数据集中的每一个样本的维数都变了,由改变后的样本集就组成了本函数的返回值。

函数原型:
Mat project(InputArray vec) const;
参数说明:vec:参与投影(降维)的数据

eigenvalues:特征值

eigenvectors:特征向量

train函数的部分代码:

    // get labels
    Mat labels = _local_labels.getMat();
    // observations in row
    Mat data = asRowMatrix(_src, CV_64FC1);

    // number of samples
   int n = data.rows;
    // assert there are as much samples as labels
    if(static_cast<int>(labels.total()) != n) {
        String error_message = format("The number of samples (src) must equal the number of labels (labels)! len(src)=%d, len(labels)=%d.", n, labels.total());
        CV_Error(Error::StsBadArg, error_message);
    }
    // clear existing model data
    _labels.release();
    _projections.clear();
    // clip number of components to be valid
    if((_num_components <= 0) || (_num_components > n))
        _num_components = n;

    // perform the PCA
    PCA pca(data, Mat(), PCA::DATA_AS_ROW, _num_components);
    // copy the PCA results
    _mean = pca.mean.reshape(1,1); // store the mean vector
    _eigenvalues = pca.eigenvalues.clone(); // eigenvalues by row
    transpose(pca.eigenvectors, _eigenvectors); // eigenvectors by column
    // store labels for prediction
    _labels = labels.clone();
    // save projections
    for(int sampleIdx = 0; sampleIdx < data.rows; sampleIdx++) {
        Mat p = LDA::subspaceProject(_eigenvectors, _mean, data.row(sampleIdx));
        _projections.push_back(p);
    }

这里讲一下,从上面代码我们看到使用了LDA(它不是用在FisherFace中的吗,通过看下面FisherFace中LDA的用法,发现两者还是有区别的,其实这里只是用了LDA类中的subspaceProject函数,用于计算投影,实际上,投影的特征向量_eigenvectors不还是PCA求解的主成分向量嘛。这与LDA算法原理无关!!!)

  Mat p = LDA::subspaceProject(_eigenvectors, _mean, data.row(sampleIdx));// 作用:映射输入图像到特征空间

      你想啊,对输入m个图像矩阵 m*n,进行PCA,求出前K个最大特征值对应的特征向量,然后用输入的图像矩阵乘以K个特征向量,相当于把图像投影到K个向量上,得到一个特征图。参数_projections存放M个矩阵。

test:

void Eigenfaces::predict(InputArray _src, Ptr<PredictCollector> collector) const {
    // get data
    Mat src = _src.getMat();
    // make sure the user is passing correct data
    if(_projections.empty()) {
        // throw error if no data (or simply return -1?)
        String error_message = "This Eigenfaces model is not computed yet. Did you call Eigenfaces::train?";
        CV_Error(Error::StsError, error_message);
    } else if(_eigenvectors.rows != static_cast<int>(src.total())) {
        // check data alignment just for clearer exception messages
        String error_message = format("Wrong input image size. Reason: Training and Test images must be of equal size! Expected an image with %d elements, but got %d.", _eigenvectors.rows, src.total());
        CV_Error(Error::StsBadArg, error_message);
    }
    // project into PCA subspace 将测试图像投影到特征向量上,得到P
    Mat q = LDA::subspaceProject(_eigenvectors, _mean, src.reshape(1, 1));
    collector->init(_projections.size());
    for (size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) {
     //把测试图像的特征图,与之前训练得到的所有图的特征图进行比较
        double dist = norm(_projections[sampleIdx], q, NORM_L2);//范数,即每个对应像素的差异
        int label = _labels.at<int>((int)sampleIdx);
        if (!collector->collect(label, dist))return;//不知道是干嘛的??
    }
}

2、Fisher face方法(PCA+LDA)

LDA(线性判别分析或称Fisher线性判别),PCA(主成份分析)代码及表情识别中的应用

第八章 采用PCA(主成分分析)或LDA(线性判别分析)的人脸识别(二)

LDA算法原理可参见:人脸识别算法二:Fisherface(LDA)

      为了提高识别效率,在对特征向量进行降维的同时还需要寻求更有利用分类的向量。 
      Fisher Fface方法是主成分分析(PCA)与Fisher线性判别分析(FLD Fisher Linear Discriminant Analysis)相结合的算法,算法首先对高维特征样本进行PCA降维,投影到低维特征空间,再采用LDA方法得到最优判别向量。

Fisher线性判别分析: 
     基本思想是计算出使Fisher准则函数达到极值的向量,并将此向量作为最佳投影方向,样本在该方向上进行投影,投影后的特征向量具有类间离散度最大,类内离散度最小特点。将特征向量(直接将图像转换成1*mn)映射到K个低维的向量上(这K个低维的向量就是判别向量),然后判断离哪个类别最近,就属于哪个人的人脸。

train函数的部分源码:

   // get data
    Mat labels = _lbls.getMat();
    Mat data = asRowMatrix(src, CV_64FC1);
    // number of samples
    int N = data.rows;
    // make sure labels are passed in correct shape
    if(labels.total() != (size_t) N) {
        String error_message = format("The number of samples (src) must equal the number of labels (labels)! len(src)=%d, len(labels)=%d.", N, labels.total());
        CV_Error(Error::StsBadArg, error_message);
    } else if(labels.rows != 1 && labels.cols != 1) {
        String error_message = format("Expected the labels in a matrix with one row or column! Given dimensions are rows=%s, cols=%d.", labels.rows, labels.cols);
       CV_Error(Error::StsBadArg, error_message);
    }
    // clear existing model data
    _labels.release();
    _projections.clear();
    // safely copy from cv::Mat to std::vector
    std::vector<int> ll;
    for(unsigned int i = 0; i < labels.total(); i++) {
        ll.push_back(labels.at<int>(i));
    }
    // get the number of unique classes
    int C = (int) remove_dups(ll).size();
    // clip number of components to be a valid number
    if((_num_components <= 0) || (_num_components > (C-1)))
        _num_components = (C-1);
    // perform a PCA and keep (N-C) components
    PCA pca(data, Mat(), PCA::DATA_AS_ROW, (N-C));
    // 将样本投影到主成分的特征图最为输入,计算它的LDA向量,构造 LDA on it,注意这里输入的不是原图
    LDA lda(pca.project(data),labels, _num_components);
    // 保存total mean vector
    _mean = pca.mean.reshape(1,1);
    // store labels
    _labels = labels.clone();
    // lda的特征向量
    lda.eigenvalues().convertTo(_eigenvalues, CV_64FC1);
    // 计算投影矩阵:pca.eigenvectors * lda.eigenvectors.存放到 _eigenvectors中
    // Note: OpenCV stores the eigenvectors by row,要先转制
    gemm(pca.eigenvectors, lda.eigenvectors(), 1.0, Mat(), 0.0, _eigenvectors, GEMM_1_T);
    // store the projections of the original data
    for(int sampleIdx = 0; sampleIdx < data.rows; sampleIdx++) {
     //把样本图像头像到特征空间_eigenvectors中
        Mat p = LDA::subspaceProject(_eigenvectors, _mean, data.row(sampleIdx));
        _projections.push_back(p);
    }
}

1、LDA   lda(const Mat& src, vector<int> labels,int num_components = 0) :

  1. 第一个为数据集(行排列)
  2. 第二个为int类型的标签数组和第一个数据集相对应
  3. 第三个为默认降维的维度为c-1,c是类别的数量

2、void gemm(InputArray src1, InputArray src2, double alpha, InputArray src3, double gamma, OutputArray dst, int flags=0 )

src1 – 第一个矩阵
src2 – 第二个矩阵 of the same type as src1.
alpha – weight of the matrix product.
src3 – third optional delta matrix added to the matrix product; it should have the same type
as src1 and src2.
beta – weight of src3.
dst – output matrix; it has the proper size and the same type as input matrices.
flags – operation flags:
– GEMM_1_T transposes src1.
– GEMM_2_T transposes src2.
– GEMM_3_T transposes src3

公式    :      alpha*op(src1 )*op(src12) + beta*src13,

现在以为没弄明白的是:Ptr<PredictCollector> collector?????    if (!collector->collect(label, dist))return;是啥意思

from:https://blog.csdn.net/cshilin/article/details/52200627

调用:predicted返回预测的label:

	Ptr<FaceRecognizer> model = createFisherFaceRecognizer();
	model->train(images, labels);
 
	Mat img = imread("person1/2.jpg", CV_LOAD_IMAGE_GRAYSCALE);
	int predicted = model->predict(img);

LBP

LBP算子也可以用于人脸检测,原理详情参见:https://blog.csdn.net/qq_30815237/article/details/88541546

基于LBP算子的人脸识别算法:

LBP被运用于计算机人脸识别领域时,提取出来的人脸特征通常是以LBP直方图向量进行表达的。 
1. 对预处理后的人脸图像进行分块 
2. 对分块后的各小块图像区域进行LBP特征提取变换 
3. 使用LBP直返图向量作为人脸特征的描述。

     一般分块数越多,人脸表达的效果就会越好,但是分块数越多,会直接导致特征向量维数的增加,会增加计算的复杂度。对每个分块计算LBP值的直方图,然后将所有分块直方图进行连接得到最终的直方图特征向量,这个特征向量代表原来的人脸图像,可以用来描述整体图像。 

 OpenCV人脸识别类FaceRecognizer

     对于这个融合的直方图,我们进行特征分类。  如果训练样本数量越大,分类的效果也会越好,

LBPH的预测进程

预测进程就比较简单了,首先将待查询点图象进行lbp编码并生成空间直方图,然后线性暴力的计算直方图的距离,采用基于直方图的相似性度量的最近邻分类方法来分类,终究输出距离最小的预测种别。部分源码:

   // find 1-nearest neighbor
    minDist = DBL_MAX;
    minClass = -1;
    for(size_t sampleIdx = 0; sampleIdx < _histograms.size(); sampleIdx++) {
        double dist = compareHist(_histograms[sampleIdx], query, CV_COMP_CHISQR);
        if((dist < minDist) && (dist < _threshold)) {
            minDist = dist;
            minClass = _labels.at<int>((int) sampleIdx);
        }
    }

 

相关文章:

  • 2021-09-20
  • 2021-10-28
  • 2022-12-23
  • 2021-12-06
  • 2021-05-15
  • 2021-10-06
  • 2021-12-25
  • 2021-12-22
猜你喜欢
  • 2021-11-30
  • 2021-12-10
  • 2021-06-26
相关资源
相似解决方案