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結果資料結構,目標參數或代號)



2008年9月11日 星期四

OpenCV線性代數-正交投影向量計算

向量空間,為一個可被多個向量基底線性組合的向量集合,也就是有許多的向量基底它可以用任意數據的線性組合產生另外一個向量,存在的那個向量就被稱為是向量空間的一部分,在這邊,要用一個向量空間來計算另外一個向量的正交投影,正交投影表示的是存在向量空間的一個向量它是另外一個向量的投影向量,而且該向量減去它的投影向量會跟向量空間正交(垂直),存在於向量空間的那個向量就稱為正交投影向量(orthogonal projection vector)



由這張圖可以看出,W為一個向量空間,v為一個向量,而proj_v為v的正交投影向量,v-proj_v則會與W向量空間正交,正交投影向量的計算方式如下


A為W空間向量基底的集合,為一個行向量(column vector)的矩陣,v則是要計算正交投影的目標向量,而它的程式計算,則是下面的方式


正交投影向量計算
#include <cv.h>
#include <stdio.h>


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

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

int main()
{
    CvMat *A=cvCreateMat(4,2,CV_32FC1);
    CvMat *v=cvCreateMat(4,1,CV_32FC1);
    CvMat *Transpose_A=cvCreateMat(2,4,CV_32FC1);
    CvMat *MulTransposed_A=cvCreateMat(2,2,CV_32FC1);
    CvMat *tempMatrix1=cvCreateMat(4,2,CV_32FC1);
    CvMat *tempMatrix2=cvCreateMat(2,1,CV_32FC1);
    CvMat *proj_v=cvCreateMat(4,1,CV_32FC1);
    cvSetData(A,array1,A->step);
    cvSetData(v,array2,v->step);

    cvTranspose(A,Transpose_A);
    cvMulTransposed(A,MulTransposed_A,1);
    cvInvert(MulTransposed_A,MulTransposed_A);
    cvmMul(A,MulTransposed_A,tempMatrix1);
    cvmMul(Transpose_A,v,tempMatrix2);
    cvmMul(tempMatrix1,tempMatrix2,proj_v);

    PrintMatrix(proj_v,4,1,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");
    }
}

執行結果:


這裡用到了前面許多運算的函數,cvTranspose(),cvInvert(),cvmMul()以及cvMulTransposed(),cvMulTransposed()轉置相乘的乘積第一個引數為輸入CvMat矩陣向量,第二個引數為輸出CvMat矩陣向量,第三個引數為計算方式,分別為原矩陣乘以原矩陣轉置或是原矩陣轉置在乘以原矩陣的選擇,0為原矩陣與原矩陣轉置相乘,1為原矩陣轉置乘以原矩陣.正交投影的計算方法如下.


cvMulTransposed()
計算原矩陣與原矩陣轉置相乘,或是原矩陣轉置與原矩陣的乘積,因為原矩陣轉置相乘為對稱矩陣,因此可以用一些特殊算法來加快矩陣相乘的乘積,cvMulTransposed()第一個引數為輸入CvMat原矩陣資料結構,第二個引數為輸出CvMat資料結構,第三個引數為cvMulTransposed()這函式的參數,0為原矩陣與原矩陣轉置相乘,1為原矩陣轉置與原矩陣的乘積,第四個引數為輸入CvMat資料結構,將原矩陣與第四個引數的矩陣做相減,再做原矩陣轉置相乘原矩陣或是原矩陣相乘原矩陣轉置的運算
cvMulTransposed(輸入CvMat原矩陣資料結構,輸出CvMat資料結構,0或1的參數,輸入CvMat矩陣相減資料結構)



2008年9月6日 星期六

OpenCV線性代數-向量的表達,內積與外積運算

接下來就簡單的介紹向量,以及向量的計算了,在OpenCV內,向量的表達有很多種方式,向量是以CvMat資料結構為基準,實際上,矩陣本身就可以看成是一群的向量,它可以切割成行向量,列向量,在OpenCV內,甚至可以以通道為單位做為一個向量的表達,在一般的圖形裡,<R,G,B>格式的三通道本身就可以當做是一種向量的形式.



在下面就簡單的做內積跟外積的運算,並且用不同的方式來表達向量的運作.下面這個是用列向量的形式來做表達


列向量內積與外積的計算
#include <cv.h>
#include <stdio.h>

float Array1[]={1,2,3};
float Array2[]={4,5,6};
void PrintMatrix(CvMat *Matrix,int Rows,int Cols,int Channels);

int main()
{
    CvMat *Matrix1=cvCreateMat(1,3,CV_32FC1);
    CvMat *Matrix2=cvCreateMat(1,3,CV_32FC1);
    CvMat *ResultMatrix=cvCreateMat(1,3,CV_32FC1);

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

    printf("The DotProduct is : %.f\n\n",cvDotProduct(Matrix1,Matrix2));

    printf("The CrossProduct is :\n");
    cvCrossProduct(Matrix1,Matrix2,ResultMatrix);
    PrintMatrix(ResultMatrix,1,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");
    }
}

執行結果:


再來就是以行向量的方式來表達,並做內積與外積的運算.


行向量內積與外積的計算
#include <cv.h>
#include <stdio.h>


float Array1[]={1,2,3};
float Array2[]={4,5,6};
void PrintMatrix(CvMat *Matrix,int Rows,int Cols,int Channels);

int main()
{
    CvMat *Matrix1=cvCreateMat(3,1,CV_32FC1);
    CvMat *Matrix2=cvCreateMat(3,1,CV_32FC1);
    CvMat *ResultMatrix=cvCreateMat(3,1,CV_32FC1);

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

    printf("The DotProduct is : %.f\n\n",cvDotProduct(Matrix1,Matrix2));

    printf("The CrossProduct is :\n");
    cvCrossProduct(Matrix1,Matrix2,ResultMatrix);
    PrintMatrix(ResultMatrix,3,1,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");
    }
}

執行結果:


接著是通道的方法,並且將通道的向量來做內外積的運算.


以通道為單位的計算方式
#include <cv.h>
#include <stdio.h>


float Array1[]={1,2,3};
float Array2[]={4,5,6};
void PrintMatrix(CvMat *Matrix,int Rows,int Cols,int Channels);

int main()
{
    CvMat *Matrix1=cvCreateMat(1,1,CV_32FC3);
    CvMat *Matrix2=cvCreateMat(1,1,CV_32FC3);
    CvMat *ResultMatrix=cvCreateMat(1,1,CV_32FC3);

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

    printf("The DotProduct is : %.f\n\n",cvDotProduct(Matrix1,Matrix2));

    printf("The CrossProduct is :\n");
    cvCrossProduct(Matrix1,Matrix2,ResultMatrix);
    PrintMatrix(ResultMatrix,1,1,3);

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

執行結果:


內積與外積的計算方式


從上面可以看的出來,向量的計算方式只有一些些微的數據有變化,在cvCreateMat()上要改變格式,在PrintMatrix()上要改變列印方式,其他的地方都是一樣,而輸出的結果也影響不大,在OpenCV上,向量的實作是實現在矩陣上,而它使用的方式也是很自由的,用這三種方式表達向量都是可行的.

cvDotProduct()
向量內積的計算,第一個引數為輸入CvMat資料結構的向量,第二個引數為輸入CvMat資料結構的向量,輸入的向量每個維度的個數必須要相等,且必須具備為向量輸入的格式,輸出為一個double型別的數據為內積計算的結果.
double cvDotProduct(輸入CvMat資料結構向量,輸入CvMat資料結構向量)

cvCrossProduct()
向量外積的計算,必須要是三維向量,且必須要為向量具備的輸入格式,第一個引數為輸入CvMat三維向量資料結構,第二個引數為輸入CvMat三維向量資料結構第三個引數為輸出CvMat三維向量外積計算結果.
cvCrossProduct(輸入CvMat三維向量資料結構,輸入CvMat三維向量資料結構,輸出CvMat三維向量資料結構)



2008年9月3日 星期三

OpenCV線性代數-cvNorm向量長度,距離計算

cvNorm()這個函數,可以用來做為特徵值比對的公式,也就是在資訊檢索這個領域內,將兩張圖形的特徵值擷取出來,利用紋理值擷取,統計直方圖,小波轉換,或是輪廓資訊等方法,在用裡面cvNorm()內建的CV_L1,CV_L2,CV_C參數來做圖形特徵值比對,cvNorm()的參數定義如下

#define CV_C 1
#define CV_L1 2
#define CV_L2 4
#define CV_DIFF_C (CV_DIFF CV_C)
#define CV_DIFF_L1 (CV_DIFF CV_L1)
#define CV_DIFF_L2 (CV_DIFF CV_L2)
#define CV_RELATIVE_C (CV_RELATIVE | CV_C)
#define CV_RELATIVE_L1 (CV_RELATIVE | CV_L1)
#define CV_RELATIVE_L2 (CV_RELATIVE | CV_L2)

CV_L1是一般的長度計算,CV_L2則是論文常用的歐幾里德距離,CV_C則是拿絕對值後的最大值,在圖形檢索比對這方面cvNorm()還算是不錯用的函式,cvNorm()還提供了遮罩的功能,可以將不必要的特徵資訊做掩蓋的功能,遮罩的部份就要參考前面"圖形的Mask遮罩實作"這地方,與前面圖形Mask使用原理相同.

在線性代數的部分Norm,為向量長度的意思,最常見的長度計算方式為1-Norm將所有向量數據取絕對值的總和,2-Norm為歐機里德距離,將所有數據平方在開根號,∞-Norm為將所有向量數據絕對值後取對大的那個,當然,這分別代表著CV_L1,CV_L2,CV_C的使用,在下面則用簡單的方式做cvNorm()的長度計算.

cvNorm()長度的計算
#include <cv.h>
#include <stdio.h>


float Array2[]={6,-1,2};

int main()
{
    CvMat *Matrix2=cvCreateMat(1,3,CV_32FC1);
    cvSetData(Matrix2,Array2,Matrix2->step);

    printf("1-norm is : %.1f\n",cvNorm(Matrix2,NULL,CV_L1));
    printf("2-norm is : %.1f\n",cvNorm(Matrix2,NULL,CV_L2));
    printf("Infininte norm is : %.1f\n",cvNorm(Matrix2,NULL,CV_C));

    system("pause");
}

執行結果:


cvNorm()的輸出為一個double型別的數據,上面的程式可以簡單的看出來cvNorm()對長度的計算,要計算Matrix2的長度,則是要把第二個引數設置為空,而第二個引數的輸入則是代表著距離,在下面的程式碼範例會介紹到,對於長度的計算公式則是如下所示.


再來是長度計算Mask使用的範例,利用一個遮罩將不想被計算的數據給取消掉,0代表不想使用的數據,1代表要被計算的數據.

cvNorm()遮罩與長度的計算
#include <cv.h>
#include <stdio.h>


float Array2[]={6,-1,2};
uchar MaskArray[]={1,1,0};

int main()
{
    CvMat *Matrix2=cvCreateMat(1,3,CV_32FC1);
    CvMat *MaskMatrix=cvCreateMat(1,3,CV_8UC1);

    cvSetData(Matrix2,Array2,Matrix2->step);
    cvSetData(MaskMatrix,MaskArray,MaskMatrix->step);

    printf("1-norm is : %.1f\n",cvNorm(Matrix2,NULL,CV_L1,MaskMatrix));
    printf("2-norm is : %.1f\n",cvNorm(Matrix2,NULL,CV_L2,MaskMatrix));
    printf("Infininte norm is : %.1f\n",cvNorm(Matrix2,NULL,CV_C,MaskMatrix));

    system("pause");
}

執行結果:


因此,實際上做長度計算的只有6跟-1這兩個數據,在這邊,遮罩的使用CvMat資料結構必須要用單通道非浮點數的數據型別,如CV_32FC1及CV_64FC1都是不可用來作遮罩矩陣的參數宣告.而矩陣也不可以用float及double的型別.遮罩長度的計算方式如下


再來,就是距離的計算了,它可以比較兩個向量的距離大小,也可以用許多向量的距離來做分數(Score)的參考目標,也就是說,在做圖形比對的時候,特徵值距離越小的兩張圖片某些特殊的特徵就越接近(看是用什麼方法擷取特徵值),因此,在一個龐大的圖形特徵資料庫中,將目標圖形的特徵值對每一筆資料做比對,可以藉由取得最小距離來找出最相似的圖形,距離的簡單例子就如下

cvNorm()距離的計算
#include <cv.h>
#include <stdio.h>


float Array1[]={6,-1,2};
float Array2[]={5,3,1};
int main()
{
    CvMat *Matrix1=cvCreateMat(1,3,CV_32FC1);
    CvMat *Matrix2=cvCreateMat(1,3,CV_32FC1);
    cvSetData(Matrix1,Array1,Matrix1->step);
    cvSetData(Matrix2,Array2,Matrix2->step);

    printf("1-norm distance is : %.1f\n",cvNorm(Matrix1,Matrix2,CV_DIFF_L1));
    printf("2-norm distance is : %.1f\n",cvNorm(Matrix1,Matrix2,CV_DIFF_L2));
    printf("Infininte norm distance is : %.1f\n\n",cvNorm(Matrix1,Matrix2,CV_DIFF_C));

    printf("Relative 1-norm distance is : %.1f\n",cvNorm(Matrix1,Matrix2,CV_RELATIVE_L1));
    printf("Relative 2-norm distance is : %.1f\n",cvNorm(Matrix1,Matrix2,CV_RELATIVE_L2));
    printf("Relative infininte norm distance is : %.1f\n",cvNorm(Matrix1,Matrix2,CV_RELATIVE_C));

    system("pause");
}

執行結果:


同樣的,它跟長度的計算有點像,比較不同的地方是將兩個向量個別值做相減在對它做1-Norm,2-Norm或是Infininte-Norm的處理,而2-Norm則是論文常用的歐幾里德距離,CV_RELATIVE_L1,CV_RELATIVE_L2,CV_RELATIVE_C則是取它們相對差異的距離.以下則是它們的計算方式.



再來就是遮罩的部份了,實作方式也跟長度的做法差不多.

cvNorm()遮罩與距離的計算
#include <cv.h>
#include <stdio.h>


float Array1[]={5,3,1};
float Array2[]={6,-1,2};
uchar MaskArray[]={1,1,0};

int main()
{
    CvMat *Matrix1=cvCreateMat(1,3,CV_32FC1);
    CvMat *Matrix2=cvCreateMat(1,3,CV_32FC1);
    CvMat *MaskMatrix=cvCreateMat(1,3,CV_8UC1);

    cvSetData(Matrix1,Array1,Matrix1->step);
    cvSetData(Matrix2,Array2,Matrix2->step);
    cvSetData(MaskMatrix,MaskArray,MaskMatrix->step);

    printf("1-norm distance is : %.1f\n",cvNorm(Matrix1,Matrix2,CV_DIFF_L1,MaskMatrix));
    printf("2-norm distance is : %.1f\n",cvNorm(Matrix1,Matrix2,CV_DIFF_L2,MaskMatrix));
    printf("Infininte norm distance is : %.1f\n\n",cvNorm(Matrix1,Matrix2,CV_DIFF_C,MaskMatrix));

    printf("Relative 1-norm distance is : %.1f\n",cvNorm(Matrix1,Matrix2,CV_RELATIVE_L1,MaskMatrix));
    printf("Relative 2-norm distance is : %.1f\n",cvNorm(Matrix1,Matrix2,CV_RELATIVE_L2,MaskMatrix));
    printf("Relative infininte norm distance is : %.1f\n",cvNorm(Matrix1,Matrix2,CV_RELATIVE_C,MaskMatrix));

    system("pause");
}

執行結果:


由上面看,有用到的Matrix1向量為<5,3>,Matrix2的向量為<6,-1>,因此Mask為0的向量數據都被忽略了,而Mask為1的被保留,計算的方式如下所示.



cvNorm()
計算向量的長度或距離,分為1-Norm(CV_L1),2-Norm(CV_L2),∞-Norm(CV_C)等三種方法,也可以計算相對距離,分別為CV_RELATIVE_C,CV_RELATIVE_L1,CV_RELATIVE_L2,在做資訊檢索比對上有很大的用處.第一個跟第二個引數為輸入向量的CvMat資料結構,如果要計算長度的話第二個引數要為空,第三個引數為cvNorm()的參數,第四個引數為單通道CvMat資料結構的CV_8UC1參數(非浮點數型別)的遮罩矩陣,cvNorm()的輸出為一個double型別的數據,代表著向量的長度或距離.
cvNorm(輸入CvMat資料結構,輸入CvMat資料結構或為空,參數或代號,輸入CvMat遮罩)



Copyright 2008-2009,yester