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;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM