2008年9月14日 星期日

OpenCV線性代數-cvGEMM通用矩陣乘法

OpenCV提供了一個通用矩陣乘法的函式,cvGEMM(),代表的是GEneralized Matrix Multiplication,cvGEMM()可以處理線性代數方面許多的乘法運算,cvmMul()矩陣乘法這個函式就是從這裡來的,cvmMul()在"cvcompat.h"及"cxcore.h"這兩個函式庫被定義為

#define cvmMul(src1,src2,dst) cvMatMulAdd(src1,src2,0,dst)
#define cvMatMul(src1,src2,dst) cvMatMulAdd((src1),(src2),NULL,(dst))
#define cvMatMulAdd(src1,src2,src3,dst) cvGEMM((src1),(src2),1.,(src3),1.,(dst),0)

先簡單的介紹cvmMul()跟cvMatMul()的用法


cvmMul()及cvMatMul()的實作
#include <cv.h>
#include <stdio.h>

float array1[]={3,1,2,0,1,5};
float array2[]={4,3,1,1,6,0};

void PrintMatrix(CvMat *Matrix,int Rows,int Cols,int Channels);
int main()
{
    CvMat *A=cvCreateMat(2,3,CV_32FC1);
    CvMat *B=cvCreateMat(3,2,CV_32FC1);
    CvMat *ResultMatrix=cvCreateMat(2,2,CV_32FC1);

    cvSetData(A,array1,A->step);
    cvSetData(B,array2,B->step);

    printf("cvmMul():\n");
    cvmMul(A,B,ResultMatrix);
    PrintMatrix(ResultMatrix,2,2,1);

    printf("\ncvMatMul():\n");
    cvMatMul(A,B,ResultMatrix);
    PrintMatrix(ResultMatrix,2,2,1);

    system("pause");
}
void PrintMatrix(CvMat *Matrix,int Rows,int Cols,int Channels)
{
    for(int i=0;i<Rows;i++)
    {
        for(int j=0;j<Cols;j++)
        {
            for(int k=0;k<Channels;k++)
            {
                printf("%.2f ",cvGet2D(Matrix,i,j).val[k]);
            }
        }
        printf("\n");
    }
}

執行結果:


所以說cvmMul()跟cvMatMul()是相同的,而他們都源自於cvMatMulAdd()這個函式,cvMatMulAdd()這個函式的前身而是cvGEMM()定義而來的,因此cvGEMM()包含了許多乘法運算的應用,下面這個為cvmMul()跟cvMatMul()的計算方式


因此,cvmMul()跟cvMatMul()是簡單的乘法運算,而cvMatMulAdd()則是如何呢?下面就是cvMatMulAdd()的使用方法


cvMatMulAdd()實作
#include <cv.h>
#include <stdio.h>

float array1[]={3,1,2,0,1,5};
float array2[]={4,3,1,1,6,0};
float array3[]={-10,-2,-20,10};

void PrintMatrix(CvMat *Matrix,int Rows,int Cols,int Channels);
int main()
{
    CvMat *A=cvCreateMat(2,3,CV_32FC1);
    CvMat *B=cvCreateMat(3,2,CV_32FC1);
    CvMat *C=cvCreateMat(2,2,CV_32FC1);
    CvMat *ResultMatrix=cvCreateMat(2,2,CV_32FC1);

    cvSetData(A,array1,A->step);
    cvSetData(B,array2,B->step);
    cvSetData(C,array3,C->step);

    printf("cvMatMulAdd():\n");
    cvMatMulAdd(A,B,C,ResultMatrix);
    PrintMatrix(ResultMatrix,2,2,1);

    system("pause");
}
void PrintMatrix(CvMat *Matrix,int Rows,int Cols,int Channels)
{
    for(int i=0;i<Rows;i++)
    {
        for(int j=0;j<Cols;j++)
        {
            for(int k=0;k<Channels;k++)
            {
                printf("%.2f ",cvGet2D(Matrix,i,j).val[k]);
            }
        }
        printf("\n");
    }
}

執行結果:


cvMatMulAdd()是簡單的相乘在相加的函式,只要符合矩陣大小的規格,它就可以對三個矩陣做相乘在相加的動作,對於上面的程式碼,它的計算方式如下


這是將矩陣相乘後的結果在加上另外一個矩陣,這跟cvGEMM()什麼關係呢?cvGEMM()函式裡面包含了很多種計算方式,而cvmMul(),cvMatMul(),cvMatMulAdd()則是用#define來擷取它的算法,而cvGEMM()的使用方式如下


簡單cvGEMM()函式實作
#include <cv.h>
#include <stdio.h>

float array1[]={3,1,2,0,1,5};
float array2[]={4,3,1,1,6,0};
float array3[]={-10,-2,-20,10};

void PrintMatrix(CvMat *Matrix,int Rows,int Cols,int Channels);
int main()
{
    CvMat *A=cvCreateMat(2,3,CV_32FC1);
    CvMat *B=cvCreateMat(3,2,CV_32FC1);
    CvMat *C=cvCreateMat(2,2,CV_32FC1);
    CvMat *ResultMatrix=cvCreateMat(2,2,CV_32FC1);
    double alpha =0.5;
    double beta =0.1;

    cvSetData(A,array1,A->step);
    cvSetData(B,array2,B->step);
    cvSetData(C,array3,C->step);

    printf("cvGEMM():\n");
    cvGEMM(A,B,alpha,C,beta,ResultMatrix);
    PrintMatrix(ResultMatrix,2,2,1);

    system("pause");
}
void PrintMatrix(CvMat *Matrix,int Rows,int Cols,int Channels)
{
    for(int i=0;i<Rows;i++)
    {
        for(int j=0;j<Cols;j++)
        {
            for(int k=0;k<Channels;k++)
            {
                printf("%.2f ",cvGet2D(Matrix,i,j).val[k]);
            }
        }
    printf("\n");
    }
}

執行結果:


因此,cvGEMM()利用#define的方式,將不必要的引數歸零或設為空或是將純量積設為1,所以才有cvmMul()及cvMulAdd()的變化,cvGEMM()的程式碼,它的數學運算方式如下所示


cvGEMM()也可以做矩陣轉置的運算,它將矩陣轉置的定義如下

#define CV_GEMM_A_T 1
#define CV_GEMM_B_T 2
#define CV_GEMM_C_T 4

如果要轉置A矩陣的話就用CV_GEMM_A_T,如果要轉置A跟B矩陣的話就用CV_GEMM_A_T+CV_GEMM_B_T(1+2=3),如果要轉置A,B,C矩陣的話就要用CV_GEMM_A_T+CV_GEMM_B_T+CV_GEMM_C_T(1+2+4=7),它可以用參數來表示,也可以用參數代號表示,只要將參數代號的數值累加即可,參數代號用1,2,4這種方式是用類似linux權限控制規劃的方法,在數學上的意義來講就是2n的加法可以表達出所有數字的組合,n=0,1,2...,在離散數學裡面可以找到這種計算規則,以下就是這個轉置運算的程式碼.


cvGEMM()轉置實作
#include <cv.h>
#include <stdio.h>


float array1[]={3,1,2,0,1,5};
float array2[]={4,3,1,1,6,0};
float array3[]={-70,-4,15,5,15,5,35,-7,20};

void PrintMatrix(CvMat *Matrix,int Rows,int Cols,int Channels);
int main()
{
    CvMat *A=cvCreateMat(2,3,CV_32FC1);
    CvMat *B=cvCreateMat(3,2,CV_32FC1);
    CvMat *C=cvCreateMat(3,3,CV_32FC1);
    CvMat *ResultMatrix=cvCreateMat(3,3,CV_32FC1);
    double alpha =0.5;
    double beta =0.1;

    cvSetData(A,array1,A->step);
    cvSetData(B,array2,B->step);
    cvSetData(C,array3,C->step);

    printf("cvMatMulAdd():\n");
    cvGEMM(A,B,alpha,C,beta,ResultMatrix,CV_GEMM_A_T+CV_GEMM_B_T+CV_GEMM_C_T);
    PrintMatrix(ResultMatrix,3,3,1);

    system("pause");
}
void PrintMatrix(CvMat *Matrix,int Rows,int Cols,int Channels)
{
    for(int i=0;i<Rows;i++)
    {
        for(int j=0;j<Cols;j++)
        {
            for(int k=0;k<Channels;k++)
            {
                printf("%.2f ",cvGet2D(Matrix,i,j).val[k]);
            }
        }
    printf("\n");
    }
}

執行結果:


這邊CV_GEMM_A_T+CV_GEMM_B_T+CV_GEMM_C_T的參數可以直接用7來代替,上面的程式碼將A,B,C三個矩陣都轉置了,而他的計算方式如下


cvMatMul()
與cvmMul()相同,做簡單的矩陣乘法運算,輸入與輸出的矩陣大小要符合矩陣運算的規格,第一個引數為輸入CvMat資料結構,第二個引數為輸入CvMat資料結構,第三個引數為輸出CvMat資料結構
cvMatMul(輸入CvMat資料結構,輸入CvMat資料結構,輸出CvMat資料結構)

cvMulAdd()
先相乘後相加的矩陣運算,輸入相乘與相加的矩陣及輸出的矩陣必須要符合矩陣運算規格,第一個引數為輸入CvMat相乘矩陣資料結構,第二個引數為輸入CvMat相乘矩陣資料結構,第三的引數為輸入CvMat相加矩陣資料結構,第四個引數為CvMat輸出資料結構
cvMulAdd(輸入CvMat資料結構,輸入CvMat資料結構,輸入CvMat資料結構,輸出CvMat資料結構)

cvGEMM()
矩陣乘法通用運算,具有純量積,矩陣相乘以及矩陣相加,矩陣轉置的功能,包含所有的矩陣運算,並且有一些函式是由cvGEMM()縮減而來如cvmMul(),cvMatMul(),cvMulAdd()等,可以藉由將一些數值設為空值縮減運算的式子,並且可以指定目標矩陣做轉置運算,轉置的規則則由CV_GEMM_A_T(1),CV_GEMM_B_T(2),CV_GEMM_C_T(4)的參數而來,如果要轉置B跟C矩陣則要用CV_GEMM_B_T+CV_GEMM_C_T參數代號則為6(2+4),其他轉置規則依此類推.第一個引數為輸入相乘CvMat資料結構,第二個引數為輸入相乘CvMat資料結構,第三個引數為輸入double型別相乘結構的純量積,第四個引數為輸入相加CvMat資料結構純量積,第五個引數為輸入double型別相加結構的純量積,第六個引數為輸出CvMat資料結構,第七個引數為輸入cvGEMM()的轉置矩陣參數
cvGEMM(輸入CvMat相乘資料結構,輸入CvMat相乘資料結構,輸入double型別相乘純量積,輸入CvMat相加資料結構,輸入double型別相加純量積,輸出CvMat結果資料結構,目標參數或代號)



0 意見:

Copyright 2008-2009,yester