1. 程式人生 > >Opencv中Mat矩陣相乘——點乘、dot、mul運算詳解

Opencv中Mat矩陣相乘——點乘、dot、mul運算詳解

Mat矩陣點乘——A*B


Opencv過載了運算子“*”,姑且稱之為Mat矩陣“點乘”,其中一個過載宣告為:


CV_EXPORTS MatExpr operator * (const Mat& a, const Mat& b);


點乘說明:


1.  A*B是以數學運算中矩陣相乘的方式實現的,即Mat矩陣A和B被當做純粹的矩陣做乘法運算,這就要求A的列數等       於B的行數時,才能定義兩個矩陣相乘。如A是m×n矩陣,B是n×p矩陣,它們的乘積AB是一個m×p矩陣。

如上圖所示,C=AB。C中第i行第j列所在元素C(i,j)等於A中第i行所有元素跟B中第j列所有元素一一對應的乘積之和。

更具有代表性的的:對於A、B都是2行2列矩陣的情況:

Opencv驗證:

定義兩個Mat矩陣A和B點乘,A為2行3列,B為3行2列:


#include "core/core.hpp"     
#include "iostream"  
 
using namespace std;   
using namespace cv;  
 
int main(int argc,char *argv[])    

    Mat A=Mat::ones(2,3,CV_32FC1);
    Mat B=Mat::ones(3,2,CV_32FC1);
    Mat AB;
 
    A.at<float>(0,0)=1;
    A.at<float>(0,1)=2;
    A.at<float>(0,2)=3;
    A.at<float>(1,0)=4;
    A.at<float>(1,1)=5;
    A.at<float>(1,2)=6;
 
    B.at<float>(0,0)=1;
    B.at<float>(0,1)=2;
    B.at<float>(1,0)=3;
    B.at<float>(1,1)=4;
    B.at<float>(2,0)=5;
    B.at<float>(2,1)=6;
 
    AB=A*B;
 
    cout<<"A=\n"<<A<<endl<<endl;
    cout<<"B=\n"<<B<<endl<<endl;
    cout<<"AB=\n"<<AB<<endl<<endl;
 
    system("pause");
}


輸出:


務必保證兩個Mat矩陣中第一個矩陣A的列數等於第二個矩陣B的行數。

2.  參與點乘的兩個Mat矩陣的資料型別(type)只能是 CV_32F、 CV_64FC1、 CV_32FC2、 CV_64FC2 這4種類        型中的一種。若選用其他型別,比如CV_8UC1,編譯器會報錯:

Mat矩陣dot——A.dot(B)


Opencv中.dot操作才算得上是真正的“點乘”,A.dot(B)操作相當於數學向量運算中的點乘,也叫向量的內積、數量積。

函式宣告:


  //! computes dot-product
    double dot(InputArray m) const;


dot說明:


1.  對兩個向量執行點乘運算,就是對這兩個向量對應位一一相乘之後求和的操作,點乘的結果是一個標量。 

  

對於向量a和向量b:


                                                           

a和b的點積公式為:

要求向量a和向量b的行列數相同。

Mat矩陣的dot方法擴充套件了一維向量的點乘操作,把整個Mat矩陣擴充套件成一個行(列)向量,之後執行向量的點乘運算,仍然要求參與dot運算的兩個Mat矩陣的行列數完全一致。

2.  dot方法宣告中顯示返回值是double,所以A.dot(B)結果是一個double型別資料,不是Mat矩陣,不能把A.dot(B)結       果賦值給Mat矩陣!

Opencv驗證:


#include "core/core.hpp"     
#include "iostream"  
 
using namespace std;   
using namespace cv;  
 
int main(int argc,char *argv[])    

    Mat A=Mat::ones(2,3,CV_8UC1);
    Mat B=Mat::ones(2,3,CV_8UC1);
 
    A.at<uchar>(0,0)=1;
    A.at<uchar>(0,1)=2;
    A.at<uchar>(0,2)=3;
    A.at<uchar>(1,0)=4;
    A.at<uchar>(1,1)=5;
    A.at<uchar>(1,2)=6;
 
    B.at<uchar>(0,0)=1;
    B.at<uchar>(0,1)=2;
    B.at<uchar>(0,2)=3;
    B.at<uchar>(1,0)=4;
    B.at<uchar>(1,1)=5;
    B.at<uchar>(1,2)=6;
 
    double AB=A.dot(B);
 
    cout<<"A=\n"<<A<<endl<<endl;
    cout<<"B=\n"<<B<<endl<<endl;
    cout<<"double型別的AB=\n"<<AB<<endl<<endl;
 
    system("pause");
}

執行結果:

若對AB宣告為Mat,則在編譯階段就會報錯。

3.  dot操作不對參與運算的矩陣A、B的資料型別做要求,CV_8UC1、CV_32FC1等,可以是任何Opencv定義的類         型,如在2中使用的就是CV_8UC1。

4.  若參與dot運算的兩個Mat矩陣是多通道的,則計算結果是所有通道單獨計算各自.dot之後,再累計的和,結果仍是一個double型別資料。

Mat矩陣mul——A.mul(B)


Opencv中mul會計算兩個Mat矩陣對應位的乘積,所以要求參與運算的矩陣A的行列和B的行列數一致。計算結果是跟A或B行列數一致的一個Mat矩陣。

Opencv中mul宣告:


//! per-element matrix multiplication by means of matrix expressions
    MatExpr mul(InputArray m, double scale=1) const;


以簡單的情況為例,對於2*2大小的Mat矩陣A和B:


對A和B執行mul運算:

mul說明:


1.  mul操作不對參與運算的兩個矩陣A、B有資料型別上的要求,但要求A,B型別一致,不然報錯;

2.  Mat AB=A.mul(B),若宣告AB時沒有定義AB的資料型別,則預設AB的資料型別跟A和B儲存一致;

3.  若AB精度不夠,可能產生溢位,溢位的值被置為當前精度下的最大值;

Opencv驗證:


#include "core/core.hpp"     
#include "iostream"  
 
using namespace std;   
using namespace cv;  
 
int main(int argc,char *argv[])    

    Mat A=Mat::ones(2,3,CV_8UC1);
    Mat B=Mat::ones(2,3,CV_8UC1);
 
    A.at<uchar>(0,0)=60;
    A.at<uchar>(0,1)=2;
    A.at<uchar>(0,2)=3;
    A.at<uchar>(1,0)=4;
    A.at<uchar>(1,1)=5;
    A.at<uchar>(1,2)=6;
 
    B.at<uchar>(0,0)=60;
    B.at<uchar>(0,1)=2;
    B.at<uchar>(0,2)=3;
    B.at<uchar>(1,0)=4;
    B.at<uchar>(1,1)=5;
    B.at<uchar>(1,2)=6;
 
    Mat AB=A.mul(B);
 
    cout<<"A=\n"<<A<<endl<<endl;
    cout<<"B=\n"<<B<<endl<<endl;
    cout<<"AB=\n"<<AB<<endl<<endl;
 
    system("pause");
}


輸出:


AB中第一個元素應該為60*60=360,但AB預設的型別為CV_8UC1,即最大值只能是255;所以執行mul運算一定要定義AB足夠的精度,防止溢位。