c++ opencv人臉識別 PCA實現(六)

這是opencv中的一個sample:
上圖先:

在這裏插入圖片描述

PCA的數學原理
PCA(Principal Component Analysis)是一種常用的數據分析方法。PCA通過線性變換將原始數據變換爲一組各維度線性無關的表示,可用於提取數據的主要特徵分量,常用於高維數據的降維。下面我將以opencv里人臉識別爲例。
對一副寬p、高q的二維灰度圖,要完整表示該圖像,需要m = pq維的向量空間,比如100100的灰度圖像,它的向量空間爲100100=10000。下圖是一個33的灰度圖和表示它的向量表示:

在這裏插入圖片描述在這裏插入圖片描述

該向量爲行向量,共9維,用變量表示就是[v0, v1, v2, v3, v4, v5, v6, v7, v8],其中v0…v8,的範圍都是0-255。
現在的問題是假如我們用110000向量,表示100100的灰度圖,是否向量中的10000維對我們同樣重要?肯定不是這樣的,有些維的值可能對圖像更有用,有些維相對來說作用小些。爲了節省存儲空間,我們需要對10000維的數據進行降維操作,這時就用到了PCA算法,該算法主要就是用來處理降維的,降維後會儘量保留更有意義的維數,它的思想就是對於高維的數據集來說,一部分維數表示大部分有意義的數據。

算法的基本原理:

假設 image 表示一個特徵向量,其中
在這裏插入圖片描述
【注:xi可能也是一個列向量】

1.計算均值向量 image

在這裏插入圖片描述

2.計算協方差矩陣 S

在這裏插入圖片描述
3.計算S的特徵值和對應的特徵向量,根據線性代數知識我們知道有公式:
在這裏插入圖片描述

  1. 對特徵值按照大小進行遞減排序,特徵向量的順序和特徵值是一致的。假設我們只需要保留K個維數(K<n),則我們會選取特徵值最大的前K個特徵向量,用這K個特徵向量,來表示圖像,這K個向量就是圖像K個主成分分量。

對於被觀測的向量image,它的K個主成分量可以通過下面公式計算得到:
在這裏插入圖片描述

,其中
在這裏插入圖片描述

因爲W是正交矩陣,所有有
在這裏插入圖片描述

下面我們在OpenCV中看一個計算PCA的例子:

1.首先讀入10副人臉圖像,這些圖像大小相等,是一個人的各種表情圖片。

2.把圖片轉爲1*pq的一維形式,p是圖像寬,q是圖像高。這時我們的S矩陣就是10行,每行是pq維的向量。

3.然後我們在S上執行PCA算法,設置K=5,求得5個特徵向量,這5個特徵向量就是我們求得的特徵臉,用這5個特徵臉圖像,可以近似表示之前的十副圖像。

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/contrib/contrib.hpp"

#include <iostream>
#include <fstream>
#include <sstream>

using namespace cv;
using namespace std;
//把圖像歸一化爲0-255,便於顯示
Mat norm_0_255(const Mat& src)
    {
    Mat dst;
    switch(src.channels())
        {
    case 1:
        cv::normalize(src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
        break;
    case 3:
        cv::normalize(src, dst, 0, 255, NORM_MINMAX, CV_8UC3);
        break;
    default:
        src.copyTo(dst);
        break;
        }
    return dst;
    }

//轉化給定的圖像爲行矩陣
Mat asRowMatrix(const vector<Mat>& src, int rtype, double alpha = 1, double beta = 0)
    {
    //樣本數量
    size_t n = src.size();
    //如果沒有樣本,返回空矩陣
    if(n == 0)
        return Mat();
    //樣本的維數
    size_t d = src[0].total();

    Mat data(n, d, rtype);
    //拷貝數據
    for(int i = 0; i < n; i++)
        {

        if(src[i].empty()) 
            {
            string error_message = format("Image number %d was empty, please check your input data.", i);
            CV_Error(CV_StsBadArg, error_message);
            }
        // 確保數據能被reshape
        if(src[i].total() != d) 
            {
            string error_message = format("Wrong number of elements in matrix #%d! Expected %d was %d.", i, d, src[i].total());
            CV_Error(CV_StsBadArg, error_message);
            }
        Mat xi = data.row(i);
        //轉化爲1行,n列的格式
        if(src[i].isContinuous())
            {
            src[i].reshape(1, 1).convertTo(xi, rtype, alpha, beta);
            } 
        else {
            src[i].clone().reshape(1, 1).convertTo(xi, rtype, alpha, beta);
            }
        }
    return data;
    }

int main(int argc, const char *argv[])
    {

    vector<Mat> db;

    string prefix = "../att_faces/";

    db.push_back(imread(prefix + "s1/1.pgm", IMREAD_GRAYSCALE));
    db.push_back(imread(prefix + "s1/2.pgm", IMREAD_GRAYSCALE));
    db.push_back(imread(prefix + "s1/3.pgm", IMREAD_GRAYSCALE));
    db.push_back(imread(prefix + "s1/4.pgm", IMREAD_GRAYSCALE));
    db.push_back(imread(prefix + "s1/5.pgm", IMREAD_GRAYSCALE));
    db.push_back(imread(prefix + "s1/6.pgm", IMREAD_GRAYSCALE));
    db.push_back(imread(prefix + "s1/7.pgm", IMREAD_GRAYSCALE));
    db.push_back(imread(prefix + "s1/8.pgm", IMREAD_GRAYSCALE));
    db.push_back(imread(prefix + "s1/9.pgm", IMREAD_GRAYSCALE));
    db.push_back(imread(prefix + "s1/10.pgm", IMREAD_GRAYSCALE));

    // Build a matrix with the observations in row:
    Mat data = asRowMatrix(db, CV_32FC1);

    // PCA算法保持5主成分分量
    int num_components = 5;

    //執行pca算法
    PCA pca(data, Mat(), CV_PCA_DATA_AS_ROW, num_components);

    //copy  pca算法結果
    Mat mean = pca.mean.clone();
    Mat eigenvalues = pca.eigenvalues.clone();
    Mat eigenvectors = pca.eigenvectors.clone();

    //均值臉
    imshow("avg", norm_0_255(mean.reshape(1, db[0].rows)));

    //五個特徵臉
    imshow("pc1", norm_0_255(pca.eigenvectors.row(0)).reshape(1, db[0].rows));
    imshow("pc2", norm_0_255(pca.eigenvectors.row(1)).reshape(1, db[0].rows));
    imshow("pc3", norm_0_255(pca.eigenvectors.row(2)).reshape(1, db[0].rows));
    imshow("pc4", norm_0_255(pca.eigenvectors.row(3)).reshape(1, db[0].rows));
    imshow("pc5", norm_0_255(pca.eigenvectors.row(4)).reshape(1, db[0].rows));

    while(1)
        waitKey(0);

    // Success!
    return 0;
    }


我們輸入的10副圖像爲:
在這裏插入圖片描述
得到的5副特徵臉爲:
在這裏插入圖片描述
均值臉爲:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章