zoukankan      html  css  js  c++  java
  • OpenCV学习(35) OpenCV中的PCA算法

    PCA算法的基本原理可以参考:http://www.cnblogs.com/mikewolf2002/p/3429711.html

        对一副宽p、高q的二维灰度图,要完整表示该图像,需要m = p*q维的向量空间,比如100*100的灰度图像,它的向量空间为100*100=10000。下图是一个3*3的灰度图和表示它的向量表示:

    imageimage

    该向量为行向量,共9维,用变量表示就是[v0, v1, v2, v3, v4, v5, v6, v7, v8],其中v0...v8,的范围都是0-255。

          现在的问题是假如我们用1*10000向量,表示100*100的灰度图,是否向量中的10000维对我们同样重要?肯定不是这样的,有些维的值可能对图像更有用,有些维相对来说作用小些。为了节省存储空间,我们需要对10000维的数据进行降维操作,这时就用到了PCA算法,该算法主要就是用来处理降维的,降维后会尽量保留更有意义的维数,它的思想就是对于高维的数据集来说,一部分维数表示大部分有意义的数据

    算法的基本原理:

    假设 image  表示一个特征向量,其中 image【注:xi可能也是一个列向量】

    1.计算均值向量 image

    image

    2.计算协方差矩阵 S

    image

    3.计算S的特征值image   和对应的特征向量image,根据线性代数知识我们知道有公式:image

    4. 对特征值按照大小进行递减排序,特征向量的顺序和特征值是一致的。假设我们只需要保留K个维数(K<n),则我们会选取特征值最大的前K个特征向量,用这K个特征向量,来表示图像,这K个向量就是图像K个主成分分量。

    对于被观测的向量image,它的K个主成分量可以通过下面公式计算得到:

    image其中image

    因为W是正交矩阵,所有有image

    下面我们在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副图像为:

    image_thumbimage_thumb2image_thumb3image_thumb4image_thumb5image_thumb6image_thumb7image_thumb8image_thumb9image_thumb1

    得到的5副特征脸为:

    imageimageimage

    imageimage

    均值脸为:

    image

     

    程序代码:参照工程FirstOpenCV32

  • 相关阅读:
    德信创业系2014版
    [学习笔记]矩形面积并
    第六章 深入分析ClassLoader工作机制
    第五章 深入class文件结构
    设计模式
    第四章 Javac编译原理
    第三章 深入分析Java Web中的中文编码问题
    第2章 深入分析java I/O的工作机制(下)
    linnx常用命令学习
    jenkins学习(1)
  • 原文地址:https://www.cnblogs.com/mikewolf2002/p/3432243.html
Copyright © 2011-2022 走看看