攝像頭採集到的影象進行映象變換,最簡單的方法是逐點掃描然後重新賦值給一個新的矩陣。但是一張普通質量的影象也有十萬級個畫素點,這種方式做映象變換,速度應該會很慢。

但是如果可以以矩陣的方式進行處理,訪問影象更快,程式執行的速度也就更快。

以矩陣的方式操作原理如下:
比如有矩陣A,

A=147258369

想要將A映象變換成B:
B=369258147

只需要在A右側乘以一個反對角矩陣T:
T=001010100
B=AT=147258369001010100

所以,在程式中先生成一張反對角矩陣,然後在每一幀影象右乘它,就可以了。但是在程式碼實現時,要注意矩陣相乘要保證資料型別一致(opencv要求CV_32FC1, CV_64FC1等等),可以用Mat::convertTo()來實現。而且影象採集進來是三通道的,我們要把三個通道分別處理以後,再重新組合成一張彩色圖。這就需要用到split()和merge()函式。

#include <opencv2/opencv.hpp>

#include <iostream>
using namespace std;
using namespace cv;

int main(){
    //生成右乘矩陣
    Mat inverse(300,300,CV_32F);
    for(int i=0;i<inverse.rows;i++){
        for(int j=0;j<inverse.cols;j++){
            if(i+j==inverse.rows-1) inverse.at<float>(i,j)=1;
            else inverse.at<float>(i,j)=0;
        }
    }

    VideoCapture capture(0);
    capture.set(CAP_PROP_FRAME_WIDTH,300);
    capture.set(CAP_PROP_FRAME_HEIGHT,180);
    Mat frame(180,300,CV_32FC3);
    Mat frameDisp(800,1440,CV_32FC3);
    vector<Mat> fms;

    cout<<"inverse matrix created!"<<endl;

    if(!capture.isOpened())
    {
        cout<<"fail to open"<<endl;
        return -1;
    }
    namedWindow("frames");
    //CV_WINDOW_NORMAL CV_WINDOW_AUTOSIZE
    //CV_WINDOW_FULLSCREEN
    //CV_WINDOW_FREERATIO CV_WINDOW_KEEPRATIO
    setWindowProperty("frames",CV_WND_PROP_FULLSCREEN,CV_WINDOW_FULLSCREEN);
    //CV_WND_PROP_FULLSCREEN CV_WND_PROP_AUTOSIZE CV_WND_PROP_ASPECTRATIO

    while(1){
        capture>>frame;
        frame.convertTo(frame,CV_32FC3,1/255.0); //轉換成可以相乘的矩陣資料型別
        split(frame,fms);
        fms.at(0)=fms.at(0)*inverse;
        fms.at(1)=fms.at(1)*inverse;
        fms.at(2)=fms.at(2)*inverse;
        merge(fms,frame);
        resize(frame,frameDisp,frameDisp.size());
        cout<<"frame channels : "<<frame.channels()<<endl;
        imshow("frames",frameDisp);
        if(waitKey(5)==' ') return 0;
    }
    return 0;
}

Tips:

  • Mat::convertTo()函式使用時,把彩色影象(CV_8UC3)轉換成(CV_32FC3)時應該注意,convertTo()的第三個引數應該設定轉換比例,此處是1/255.0(如果是反向轉換,則應是255.0)。這是因為,imshow()函式顯示(CV_8UC3)型別的影象時,資料範圍是0~255,但是顯示(CV_32FC3)時,資料範圍是0~1.0。convertTo()第三個引數如果不設定,會把0~255的(CV_8UC3)影象轉換成0~255.0(CV_32FC3),這樣顯示出來就是一片白色。
  • 右乘的那個矩陣資料型別要選(CV_32F或者CV_32FC1)
  • split()函式第二個引數,資料型別是vector<mat>
  • 顯示視窗我還沒有調出全屏效果,不知什麼原因,有待研究。

補充:
在opencv裡面,有一個flip()函式,可以進行影象的翻轉。
其描述如下:

C++: void flip(InputArray src, OutputArray dst, int flipCode)
Python: cv2.flip(src, flipCode[, dst]) → dst
C: void cvFlip(const CvArr* src, CvArr* dst=NULL, int flip_mode=0 )
Python: cv.Flip(src, dst=None, flipMode=0) → None
Parameters: 
    src – input array.
    dst – output array of the same size and type as src.
    flipCode – a flag to specify how to flip the array; 0 means flipping around the x-axis and positive value (for example, 1) means flipping around y-axis. Negative value (for example, -1) means flipping around both axes (see the discussion below for the formulas).