2008年8月11日 星期一

OpenCV線性代數-cvSVD奇異值分解(1)

奇異值分解(Singular Value Decomposition,SVD),可以說是對角化應用的特例,它具有與Eigen Value,Eigen Vector相同的特性,分解後向量的矩陣乘積可以還原為原矩陣,而他可以對奇異矩陣(Singular Matrix)做分解,亦可以對非奇異矩陣做分解.奇異矩陣的定義就是不能計算為反矩陣的矩陣,包括方陣,以及非方陣的長方形矩陣.而奇異值分解的計算它的數學定義為



它將奇異矩陣轉置後相乘,藉由這個方法計算出對稱矩陣,在對對稱矩陣做SVD分解,由上面可知,原矩陣大小為m*n,而W(Sigma)矩陣的舉陣大小必須為m*n,U的矩陣大小必須為m*m,而V的矩陣大小為n*n,它可以藉由矩陣矩陣相乘的方式算回原矩陣.

下面這個是拿2*3的奇異矩陣做奇異值分解


奇異矩陣SVD分解
#include <cv.h>
#include <highgui.h>
#include <stdio.h>


void PrintMatrix(CvMat *Matrix,int Rows,int Cols);

double Array1[]={3,2,2,2,3,-2};
int main()
{
    CvMat *Matrix1=cvCreateMat(2,3,CV_64FC1);
    CvMat *W=cvCreateMat(2,3,CV_64FC1);
    CvMat *V=cvCreateMat(3,3,CV_64FC1);
    CvMat *U=cvCreateMat(2,2,CV_64FC1);
    CvMat *V_T=cvCreateMat(3,3,CV_64FC1);
    CvMat *ResultMatrix=cvCreateMat(2,3,CV_64FC1);

    cvSetData(Matrix1,Array1,Matrix1->step);

    cvSVD(Matrix1,W,U,V);

    printf("\nW\n");
    PrintMatrix(W,W->rows,W->cols);
    printf("\nU\n");
    PrintMatrix(U,U->rows,U->cols);
    printf("\nV\n");
    PrintMatrix(V,V->rows,V->cols);

    printf("\nValid\n");
    cvmMul(U,W,ResultMatrix);
    cvTranspose(V,V_T);
    cvmMul(ResultMatrix,V_T,ResultMatrix);
    PrintMatrix(ResultMatrix,ResultMatrix->rows,ResultMatrix->cols);

    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");
    }
}

執行結果:


上面的程式,利用了前面奇異值分解公式說明的方式,cvSVD()的內部運算,先將矩陣做轉置在相乘,求得對稱矩陣,在將該矩陣做奇異值分解,而它的W,U,V矩陣的大小皆不相同,W將會是原矩陣大小2*3,V則是2*2,U則會是3*3,因此利用公式倒回運算,可以計算出原矩陣,計算以及驗證的公式如下.





而對稱矩陣的奇異值分解,則是不需要再做轉置後相乘,直接做奇異值分解.


對稱矩陣奇異值分解
#include <cv.h>
#include <highgui.h>
#include <stdio.h>


void PrintMatrix(CvMat *Matrix,int Rows,int Cols);

double Array1[]={2,-2,4,-2,2,-4,4,-4,8};
int main()
{
    CvMat *Matrix1=cvCreateMat(3,3,CV_64FC1);
    CvMat *W=cvCreateMat(3,3,CV_64FC1);
    CvMat *V=cvCreateMat(3,3,CV_64FC1);
    CvMat *U=cvCreateMat(3,3,CV_64FC1);
    CvMat *V_T=cvCreateMat(3,3,CV_64FC1);
    CvMat *ResultMatrix=cvCreateMat(3,3,CV_64FC1);

    cvSetData(Matrix1,Array1,Matrix1->step);

    cvSVD(Matrix1,W,U,V);

    printf("\nW\n");
    PrintMatrix(W,W->rows,W->cols);
    printf("\nU\n");
    PrintMatrix(U,U->rows,U->cols);
    printf("\nV\n");
    PrintMatrix(V,V->rows,V->cols);

    printf("\nValid\n");
    cvmMul(U,W,ResultMatrix);
    cvTranspose(V,V_T);
    cvmMul(ResultMatrix,V_T,ResultMatrix);
    PrintMatrix(ResultMatrix,ResultMatrix->rows,ResultMatrix->cols);

    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");
    }
}

執行結果:


這是一個直接用對稱矩陣做奇異值分解的程式,奇異值分解很特別的地方,它對對稱矩陣做奇異值分解求得的結果為特徵值(Eigen Value)及特徵向量(Eigen Vector),但是相較於cvEigenVV()這兩個使用的是不同的演算法,而一般的矩陣則無法用cvSVD()求特徵值及特徵向量,上面的程式用cvSVD()分別求出它的W,U,V,在代回原公式做驗證,驗證的方法如下





驗證對稱矩陣奇異值分解




2 意見:

匿名 提到...

Good! thank you!
a little error for first question exist!

匿名 提到...

http://2.bp.blogspot.com/_HcQD5f5sLPc/SKNtnK8MvII/AAAAAAAABB0/v-qYwW6HRoE/s1600-h/mat_number_73-2.JPG

V^t 好像有問題

Copyright 2008-2009,yester