zoukankan      html  css  js  c++  java
  • OpenCV 中使用 PCA

    对于PCA,一直都是有个概念,没有实际使用过,今天终于实际使用了一把,发现PCA还是挺神奇的。


    在OPENCV中使用PCA非常简单,只要几条语句就可以了。


    1、初始化数据


    //每一行表示一个样本


    CvMat* pData = cvCreateMat( 总的样本数, 每个样本的维数, CV_32FC1 );


    CvMat* pMean = cvCreateMat(1, 样本的维数, CV_32FC1);


    //pEigVals中的每个数表示一个特征值


    CvMat* pEigVals = cvCreateMat(1, min(总的样本数,样本的维数), CV_32FC1);


    //每一行表示一个特征向量


    CvMat* pEigVecs = cvCreateMat( min(总的样本数,样本的维数), 样本的维数, CV_32FC1);


    2、PCA处理,计算出平均向量pMean,特征值pEigVals和特征向量pEigVecs


    cvCalcPCA( pData, pMean, pEigVals, pEigVecs, CV_PCA_DATA_AS_ROW );


    3、选出前P个特征向量(主成份),然后投影,结果保存在pResult中,pResult中包含了P个系数


    CvMat* pResult = cvCreateMat( 总的样本数, PCA变换后的样本维数(即主成份的数目), CV_32FC1 );


    cvProjectPCA( pData, pMean, pEigVecs, pResult );


    4、 重构,结果保存在pRecon中


    CvMat* pRecon = cvCreateMat( 总的样本数, 每个样本的维数, CV_32FC1 );


    cvBackProjectPCA( pResult, pMean, pEigVecs, pRecon );


    5、重构误差的计算


    计算pRecon和pData的"差"就可以了.


    使用时如果是想用PCA判断“是非”问题,则可以先用正样本计算主成分,判断时,对需要判断得数据进行投影,然后重构,计算重构出的数据与原数据的差异,如果差异在给定范围内,可以认为“是”。


    如果相用PCA进行分类,例如对数字进行分类,则先用所有数据(0-9的所有样本)计算主成分,然后对每一类数据进行投影,计算投影的系数,可简单得求平均。即对每一类求出平均系数。分类时,将需要分类得数据进行投影,得到系数,与先前计算出得每一类得平均系数进行比较,可判为最接近得一类。当然这只是最简单得使用方法


    *********************************************************************************************




    *********************************************************************************************


    PCA是主成分分析,主要用于数据降维,对于一系列sample的feature组成的多维向量,多维向量里的某些元素本身没有区分性,比如某个元素在所有的sample中都为1,或者与1差距不大,那么这个元素本身就没有区分性,用它做特征来区分,贡献会非常小。所以我们的目的是找那些变化大的元素,即方差大的那些维,而去除掉那些变化不大的维,从而使feature留下的都是“精品”,而且计算量也变小了。


    对于一个k维的feature来说,相当于它的每一维feature与其他维都是正交的(相当于在多维坐标系中,坐标轴都是垂直的),那么我们可以变化这些维的坐标系,从而使这个feature在某些维上方差大,而在某些维上方差很小。例如,一个45度倾斜的椭圆,在第一坐标系,如果按照x,y坐标来投影,这些点的x和y的属性很难用于区分他们,因为他们在x,y轴上坐标变化的方差都差不多,我们无法根据这个点的某个x属性来判断这个点是哪个,而如果将坐标轴旋转,以椭圆长轴为x轴,则椭圆在长轴上的分布比较长,方差大,而在短轴上的分布短,方差小,所以可以考虑只保留这些点的长轴属性,来区分椭圆上的点,这样,区分性比x,y轴的方法要好!


    所以我们的做法就是求得一个k维特征的投影矩阵,这个投影矩阵可以将feature从高维降到低维。投影矩阵也可以叫做变换矩阵。新的低维特征必须每个维都正交,特征向量都是正交的。通过求样本矩阵的协方差矩阵,然后求出协方差矩阵的特征向量,这些特征向量就可以构成这个投影矩阵了。特征向量的选择取决于协方差矩阵的特征值的大小。


    举一个例子:


    对于一个训练集,100个sample,特征是10维,那么它可以建立一个100*10的矩阵,作为样本。求这个样本的协方差矩阵,得到一个10*10的协方差矩阵,然后求出这个协方差矩阵的特征值和特征向量,应该有10个特征值和特征向量,我们根据特征值的大小,取前四个特征值所对应的特征向量,构成一个10*4的矩阵,这个矩阵就是我们要求的特征矩阵,100*10的样本矩阵乘以这个10*4的特征矩阵,就得到了一个100*4的新的降维之后的样本矩阵,每个sample的维数下降了。


    当给定一个测试的特征集之后,比如1*10维的特征,乘以上面得到的10*4的特征矩阵,便可以得到一个1*4的特征,用这个特征去分类。


    所以做PCA实际上是求得这个投影矩阵,用高维的特征乘以这个投影矩阵,便可以将高维特征的维数下降到指定的维数。


    在opencv里面有专门的函数,可以得到这个这个投影矩阵(特征矩阵)。


    void cvCalcPCA( const CvArr* data, CvArr* avg, CvArr* eigenvalues, CvArr* eigenvectors, int flags );




    *********************************************************************************************
    *********************************************************************************************


    float* features=new float[lenOfFeatures];


    ...


    CvMat* vector_feature=cvCreateMat(m_pm.numOfSamples,m_pm.dim,CV_32FC1);


    cvSetData(vector_feature,features,vector_feature->step);


    CvMat *AvgVector;


    CvMat *EigenVector;


    CvMat *EigenValue_Row;


    CvMat* vector_pca=cvCreateMat(m_pm.numOfSamples,PCA_DIM,CV_32FC1);


    AvgVector=cvCreateMat(1,m_pm.dim,CV_32FC1);


    EigenValue_Row=cvCreateMat(1,min(m_pm.dim,m_pm.numOfSamples),CV_32FC1);


    EigenVector=cvCreateMat(min(m_pm.dim,m_pm.numOfSamples),m_pm.dim,CV_32FC1);


    // 计算特征值,对原始数据进行变换,得到主成分


    cvCalcPCA(vector_feature,AvgVector,EigenValue_Row,EigenVector,CV_PCA_DATA_AS_ROW);


    cvProjectPCA(vector_feature,AvgVector,EigenVector,vector_pca);


    delete[] features;

  • 相关阅读:
    Unsafe(转载) 规格严格
    MySQL 中文 规格严格
    2007“奥普迪杯”开放式实时在线辞典系统设计大赛
    2007“奥普迪杯”开放式实时在线辞典系统设计大赛
    2007年个人回忆与总结
    蔡学镛:2008编程语言走势解盘
    用scanf实现gets的功能
    2007年个人回忆与总结
    用scanf实现gets的功能
    初学入门:如何有效编写软件的75条建议
  • 原文地址:https://www.cnblogs.com/lingtianyulong/p/4198522.html
Copyright © 2011-2022 走看看