OpenCV之人脸识别(三) PCA降维原理与应用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huanghuangjin/article/details/81627618
PCA原理:
    通过对高维数据分析发现他们的相同与不同表达为一个低维数据模式,
    主成分不变
    细微损失
    高维数据到低维数据

这里写图片描述
这里写图片描述

PCA可逆,可以用于数据压缩,但是有损耗

代码

#include "../common/common.hpp"

static void calcPCAorientation(vector<Point>& pts, Mat &image); // 计算PCA算出的特征向量的夹角

void main(int argc, char ** argv)
{
    Mat src = imread(getCVImagesPath("images/pca_test1.jpg"));
    imshow("src8-4", src);

    Mat gray, binary;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);

    vector<Vec4i> hireachy;
    vector <vector<Point>> contours;
    findContours(binary, contours, hireachy, RETR_LIST, CHAIN_APPROX_NONE);
    Mat ret = src.clone();
    for (size_t i = 0; i < contours.size(); i++)
    {
        double area = contourArea(contours[i]);
        if (area > 1e5 || area < 1e2) continue; // 面积太大或太小的过滤,1e5 表示10的5次方
        drawContours(ret, contours, i, Scalar(0, 0, 255), 2, 8);
        calcPCAorientation(contours[i], ret); // 计算PCA算出的特征向量的夹角
    }
    imshow("ret", ret);

    waitKey(0);
}

void calcPCAorientation(vector<Point>& pts, Mat &image) // 计算PCA算出的特征向量的夹角
{
    int size = static_cast<int>(pts.size());
    Mat data_pts = Mat(size, 2, CV_64FC1);
    for (int i = 0; i < size; i++)
    {
        data_pts.at<double>(i, 0) = pts[i].x;
        data_pts.at<double>(i, 1) = pts[i].y;
    }

    /*
        PCA( // PCA类
            InputArray data, // 原始矩阵数据
            InputArray mean, // 原始矩阵数据均值,输入空函数会自己计算
            int flags, // 每行(CV_PCA_DATA_AS_ROW) 列(CV_PCA_DATA_AS_COL)代表一个样本
            double retainedVariance // 保留多少特征值,百分比,默认全保留
        );
    */
    PCA pca_analysis(data_pts, Mat(), CV_PCA_DATA_AS_ROW); // pca分析
    // 因为维度为2,所以均值 pca_analysis.mean 为2行1列的矩阵,本例中的均值为轮廓几何矩的中心位置
    Point center = Point(static_cast<int>(pca_analysis.mean.at<double>(0, 0)), static_cast<int>(pca_analysis.mean.at<double>(0, 1)));
    circle(image, center, 2, Scalar(0, 255, 0), 2, 8); // 绘制几何矩的中心位置

    vector<double> vals(2); // 特征值,2个
    vector<Point2d> vecs(2); // 特征向量,2*2的矩阵
    for (int i = 0; i < 2; i++)
    {
        vals[i] = pca_analysis.eigenvalues.at<double>(i, 0); // pca_analysis.eigenvalues 表示PCA计算出来的特征值
        // pca_analysis.eigenvectors 表示PCA计算出来的特征向量
        vecs[i] = Point2d(pca_analysis.eigenvectors.at<double>(i, 0), pca_analysis.eigenvectors.at<double>(i, 1));
        cout << i << ", vals=" << vals[i] << ", vecs.x=" << vecs[i].x << ", vecs.y=" << vecs[i].y << endl;
    }
    Point p1 = center + 0.02*Point(static_cast<int>(vecs[0].x*vals[0]), static_cast<int>(vecs[0].y*vals[0])); // 为什么是乘以0.02   0.05 ?
    Point p2 = center - 0.05*Point(static_cast<int>(vecs[1].x*vals[1]), static_cast<int>(vecs[1].y*vals[1]));

    line(image, center, p1, Scalar(255, 0, 0), 2, 8); // 画线,这两条线是正交的,因为一个是x方向一个是y方向
    line(image, center, p2, Scalar(255, 0, 255), 2, 8);

    double angle = atan2(vecs[0].y, vecs[0].x); // 瞎算的?
    cout << "angle=" << 180 * angle / CV_PI << endl;
    /* 日志
        0, vals=13507.4, vecs.x=0.966421, vecs.y=-0.256964
        1, vals=499.892, vecs.x=0.256964, vecs.y=0.966421
        angle=-14.89
        0, vals=13275.5, vecs.x=0.972491, vecs.y=-0.23294
        1, vals=486.813, vecs.x=0.23294, vecs.y=0.972491
        angle=-13.4702
        0, vals=12837.3, vecs.x=0.975379, vecs.y=-0.220535
        1, vals=463.17, vecs.x=0.220535, vecs.y=0.975379
        angle=-12.7405
        0, vals=12497, vecs.x=0.98585, vecs.y=-0.167631
        1, vals=430.128, vecs.x=0.167631, vecs.y=0.98585
        angle=-9.65011
        0, vals=11958.1, vecs.x=0.0609259, vecs.y=0.998142
        1, vals=463.379, vecs.x=0.998142, vecs.y=-0.0609259
        angle=86.507
        0, vals=12096.5, vecs.x=0.985447, vecs.y=-0.169983
        1, vals=414.236, vecs.x=0.169983, vecs.y=0.985447
        angle=-9.78686
    */
}

效果图

这里写图片描述

猜你喜欢

转载自blog.csdn.net/huanghuangjin/article/details/81627618