機器學習方面的降維講解(PCA原理,奇異值分解):http://blog.csdn.net/abcjennifer/article/details/8002329
在圖形識別方面,主成分分析(Principal Comonents Analysis,PCA)算是比較快速而且又準確的方式之一,它可以對抗圖形平移旋轉的事件發生,並且藉由主要特徵(主成分)投影過後的數據做數據的比對,在多個特徵信息裏面,取最主要的K個,做爲它的特徵依據,在這邊拿前面共變量矩陣的數據來做沿用,主成分分析使用的方法爲計算共變量矩陣,在加上計算共變量矩陣的特徵值及特徵向量,將特徵值以及所對應的特徵向量排序之後,取前面主要K個特徵向量當做主要特徵,而OpenCV也可以對高維度的向量進行主成分分析的計算
數據原始的分佈情況,紅點代表着它的平均數
將座標系位移,以紅點爲主要的原點
計算共變量矩陣以及共變量的特徵值以及特徵向量,將特徵向量排序後投影回原始數據的結果的結果,也就是說,對照上面的圖片,EigenVector的作用是找到主軸後,將原本的座標系做旋轉了
再來就是對它做投影,也就是降低維度的動作,將Y軸的數據全部歸零,投影在X軸上
投影完之後,在將它轉回原本的座標系
PCA主成分分析,與線性迴歸有異曲同工之妙,也就是說,這條投影過後的直線,可以稱做迴歸線,當它在做主軸旋轉的時候,所投影的結果爲最小均方誤,在將它轉置回來的時候,就形成了一條迴歸直線了
OpenCV的PCA輸入必須要是單信道32位浮點數格式或是單信道64位浮點數格式的,參數爲CV_32FC1或是CV_64FC1,程序寫法如下
PCA程序實作
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>
float Coordinates[20]={1.5,2.3,
3.0,1.7,
1.2,2.9,
2.1,2.2,
3.1,3.1,
1.3,2.7,
2.0,1.7,
1.0,2.0,
0.5,0.6,
1.0,0.9};
void PrintMatrix(CvMat *Matrix,int Rows,int Cols);
int main()
{
CvMat *Vector1;
CvMat *AvgVector;
CvMat *EigenValue_Row;
CvMat *EigenVector;
Vector1=cvCreateMat(10,2,CV_32FC1);
cvSetData(Vector1,Coordinates,Vector1->step);
AvgVector=cvCreateMat(1,2,CV_32FC1);
EigenValue_Row=cvCreateMat(2,1,CV_32FC1);
EigenVector=cvCreateMat(2,2,CV_32FC1);
cvCalcPCA(Vector1,AvgVector,EigenValue_Row,EigenVector,CV_PCA_DATA_AS_ROW);
printf("Original Data:\n");
PrintMatrix(Vector1,10,2);
printf("==========\n");
PrintMatrix(AvgVector,1,2);
printf("\nEigne Value:\n");
PrintMatrix(EigenValue_Row,2,1);
printf("\nEigne Vector:\n");
PrintMatrix(EigenVector,2,2);
system("pause");
}
void PrintMatrix(CvMat *Matrix,int Rows,int Cols)
{
for(int i=0;i<Rows;i++)
{
for(int j=0;j<Cols;j++)
{
printf("%.2f ",cvGet2D(Matrix,i,j).val[0]);
}
printf("\n");
}
}
執行結果:
這部份是把平均數,共變量矩陣,以及特徵值及特徵向量都計算出來了,全部都包在cvCalcPCA()的函式裏面,因此可以不必特地的用cvCalcCovarMatrix()求得共變量矩陣,也不需要再由共變量矩陣套用cvEigenVV()求得它的EigenValue以及EigenVector了,所以說,cvCalcPCA()=cvCalcCovarMatrix()+cvEigenVV(),不僅如此,cvCalcPCA()使用上更是靈活,當向量的維度數目比輸入的數據那的時候(例如Eigenface),它的共變量矩陣就會自動轉成CV_COVAR_SCRAMBLED,而當輸入數據量比向量維度大的時候,它亦會自動轉成CV_COVAR_NORMAL的形態,而OpenCV也提供了計算投影量cvProjectPCA(),以及反向投影的函式cvBackProjectPCA(),cvCalcPCA()的計算結果如下
詳細計算方法可以參考"OpenCV統計應用-共變量矩陣"以及"OpenCV線性代數-cvEigenVV實作"這兩篇,cvCalcPCA()第一個自變量爲輸入目標要計算的向量,整合在CvMat數據結構裏,第二個自變量爲空的平均數向量,第三個自變量爲輸出排序後的EigenValue,以列(Rows)爲主的數值,第四個自變量爲排序後的EigenVector,第五個自變量爲cvCalcPCA()的參數,它的參數公式如下
#define CV_PCA_DATA_AS_ROW 0
#define CV_PCA_DATA_AS_COL 1
#define CV_PCA_USE_AVG 2
分別代表以列爲主,以欄爲主的參數設定,以及使用自己定義的平均數,CV_PCA_DATA_AS_ROW與CV_PCA_DATA_AS_COL的參數不可以同時使用,而對於主成分分析的EigenVector對原始數據投影的程序範例如下
PCA特徵向量投影
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>
float Coordinates[20]={1.5,2.3,
3.0,1.7,
1.2,2.9,
2.1,2.2,
3.1,3.1,
1.3,2.7,
2.0,1.7,
1.0,2.0,
0.5,0.6,
1.0,0.9};
void PrintMatrix(CvMat *Matrix,int Rows,int Cols);
int main()
{
CvMat *Vector1;
CvMat *AvgVector;
CvMat *EigenValue_Row;
CvMat *EigenVector;
Vector1=cvCreateMat(10,2,CV_32FC1);
cvSetData(Vector1,Coordinates,Vector1->step);
AvgVector=cvCreateMat(1,2,CV_32FC1);
EigenValue_Row=cvCreateMat(2,1,CV_32FC1);
EigenVector=cvCreateMat(2,2,CV_32FC1);
cvCalcPCA(Vector1,AvgVector,EigenValue_Row,EigenVector,CV_PCA_DATA_AS_ROW);
cvProjectPCA(Vector1,AvgVector,EigenVector,Vector1);
printf("Project Original Data:\n");
PrintMatrix(Vector1,10,2);
system("pause");
}
void PrintMatrix(CvMat *Matrix,int Rows,int Cols)
{
for(int i=0;i<Rows;i++)
{
for(int j=0;j<Cols;j++)
{
printf("%.2f ",cvGet2D(Matrix,i,j).val[0]);
}
printf("\n");
}
}
執行結果:
cvProjectPCA(),它的公式定義如下
因此,它所呈現的結果就是將座標旋轉後的特徵向量投影,cvProjectPCA()的地一個自變量爲輸入原始向量數據,第二個自變量爲輸入空的(或是以設定好的)平均數向量,第三個自變量爲輸入以排序好的特徵向量EigenVector,第四個自變量爲輸出目標投影向量,而投影之外,OpenCV還可以給他做反向投影回原始的數據
PCA反向投影
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>
float Coordinates[20]={1.5,2.3,
3.0,1.7,
1.2,2.9,
2.1,2.2,
3.1,3.1,
1.3,2.7,
2.0,1.7,
1.0,2.0,
0.5,0.6,
1.0,0.9};
int main()
{
CvMat *Vector1;
CvMat *AvgVector;
IplImage *Image1=cvCreateImage(cvSize(450,450),IPL_DEPTH_8U,3);
Image1->origin=1;
Vector1=cvCreateMat(10,2,CV_32FC1);
cvSetData(Vector1,Coordinates,Vector1->step);
AvgVector=cvCreateMat(1,2,CV_32FC1);
CvMat *EigenValue_Row=cvCreateMat(2,1,CV_32FC1);
CvMat *EigenVector=cvCreateMat(2,2,CV_32FC1);
cvCalcPCA(Vector1,AvgVector,EigenValue_Row,EigenVector,CV_PCA_DATA_AS_ROW);
cvProjectPCA(Vector1,AvgVector,EigenVector,Vector1);
cvBackProjectPCA(Vector1,AvgVector,EigenVector,Vector1);
printf("Back Project Original Data:\n");
for(int i=0;i<10;i++)
{
printf("%.2f ",cvGetReal2D(Vector1,i,0));
printf("%.2f ",cvGetReal2D(Vector1,i,1));
cvCircle(Image1,cvPoint((int)(cvGetReal2D(Vector1,i,0)*100),(int)(cvGetReal2D(Vector1,i,1)*100)),0,CV_RGB(0,0,255),10,CV_AA,0);
printf("\n");
}
cvCircle(Image1,cvPoint((int)((cvGetReal2D(AvgVector,0,0))*100),(int)((cvGetReal2D(AvgVector,0,1))*100)),0,CV_RGB(255,0,0),10,CV_AA,0);
printf("==========\n");
printf("%.2f ",cvGetReal2D(AvgVector,0,0));
printf("%.2f ",cvGetReal2D(AvgVector,0,1));
cvNamedWindow("Coordinates",1);
cvShowImage("Coordinates",Image1);
cvWaitKey(0);
}
執行結果:
而反向投影,只不過是將投影向量還原成原始數據罷了,可以用來做爲新進數據反向投影后用來比對的步驟,以下是它的計算公式推導
cvBackProjectPCA()的自變量輸入與cvProjectPCA()是一樣的,只不過是裏面的計算公式不同,其實也只是cvProjectPCA()的反運算
cvCalcPCA()
計算多筆高維度數據的主要特徵值及特徵向量,先自動判斷維度大於數據量或維度小於數據量來選擇共變量矩陣的樣式,在由共變量矩陣求得已排序後的特徵值及特徵向量,cvCalcPCA()函式的參數爲CV_PCA_DATA_AS_ROW以列爲主的資料排列,CV_PCA_DATA_AS_COL以行爲主的數據排列,CV_PCA_USE_AVG自行定義平均數數據,CV_PCA_DATA_AS_ROW以及CV_PCA_DATA_AS_COL不可以同時使用,cvCalcPCA()的第一個自變量爲輸入CvMat維度向量數據結構,第二個自變量爲輸入空的CvMat平均數向量數據結構(或是自行定義平均數向量),第三個自變量爲輸入以列爲主的特徵值CvMat數據結構,第四個自變量爲輸入特徵向量CvMat數據結構,第四個自變量爲輸入cvCalcPCA()函式的參數
cvCalcPCA(輸入目標向量數據CvMat數據結構,輸入或輸出向量平均數CvMat數據結構,輸入以列爲主EigenValue的CvMat數據結構,輸入EigenVector的CvMat數據結構,目標參數或代號)
cvProjectPCA()
將計算主成分分析的結果做投影的運算,主要作用是將最具意義的k個EgienVector與位移後的原始資料做矩陣乘法,投影過後的結果將會降低維度,cvProjectPCA()第一個自變量爲輸入CvMat數據結構原始向量數據數據,第二個自變量爲輸入CvMat數據結構平均數向量,第三個自變量爲輸入要降維的特徵向量,第四個自變量爲輸出CvMat數據結構原始數據投影的結果
cvProjectPCA(輸入CvMat原始向量數據數據結構,輸入CvMat原始向量平均數數據結構,輸入CvMat降低維度的特徵向量數據結構,輸出CvMat投影向量數據結構)
cvBackProjectPCA()
將投影向量轉回原始向量維度座標系,cvProjectPCA()第一個自變量爲輸入CvMat數據結構投影向量數據,第二個自變量爲輸入CvMat數據結構平均數向量,第三個自變量爲輸入特徵向量,第四個自變量爲輸出CvMat數據結構反向投影的結果
cvProjectPCA(輸入CvMat投影向量數據結構,輸入CvMat原始向量平均數數據結構,輸入CvMat特徵向量數據結構,輸出CvMat反向投影數據結構)
另附:http://blog.csdn.net/yang_xian521/article/details/7445536