OpenCv 2.4.9 (一) Mat基礎結構&如何遍歷圖片


前言


 

  因為對圖像方面感興趣,所以有空學學OpenCV的使用,並且希望以此為引子,帶領自己入門圖像領域。

  先post上幾個參考網站,上面有完整源碼:

  因為這么多資源,所以就不貼完整代碼,這重點講解某部分,方便自己以后回來查詢。

 

Mat - 基本的圖像容器


Mat

  在以前,opencv使用的是C結構,IplImage。但是使用這個結構有一個缺點就是你需要注意內存的申請和銷毀。幸運的是,在C++我們可以使用一種更智能的結構,Mat。Mat會自動申請內存和銷毀。

  Mat由基本的兩部分組成:矩陣頭(包含圖片信息,例如矩陣大小,存儲方法等)和一個指向包含像素點信息的指針。矩陣頭部大小是常數,但是矩陣的大小卻各不相同。

1 Mat A, C;  // 只建立頭部
2 A = imread(fname, CV_LOAD_IMAGE_COLOR); 3 4 Mat B(A); // 調用copy構造函數 5 C = A; // 調用assign函數

 

  上面的所有對象都指向同一個矩陣,只是頭部不一樣而已。如果使用其中一個對象改變圖像內容,所有指向這個矩陣的對象都會受影響。copy和assign只是復制頭部的一些信息。

  我們可以調用其它方法實現深復制:

1 Mat F = A.clone();
2 Mat G;
3 A.copyTo(G);

 

 

顯式創建Mat

  我們可以使用 imwrite() 函數來把一個矩陣寫入到圖片文件。但是為了調試方便,我們還可以使用<<輸出(僅適用於二維矩陣)。

  下面是創建Mat對象的各種方法:

  • Mat()構造器
    1 Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255)); // CV_[多少位][有符號or無符號][類型前綴]C[通道數]
    2 cout << M <<endl;
    3 // [0, 0, 255, 0, 0, 255;
    4 //  0, 0, 255, 0, 0, 255
  • 使用C\C++數組構造
    1 int sz[3] = {2, 2,2};
    2 Mat L(3, sz, CV_8UC(1), Scalar::all(0)); // 3維的[2, 2, 2]的圖像
  • 為已存在IplImage指針構建頭部
    1 IplImage* img = cvLoadImage(fname);
    2 Mat mtx(img);
  • Create() 函數
    1 M.create(4, 4, CV_8UC(2)); // 這種方法不能賦初值,只在中心分配內存時使用
    2 cout<< M << endl
  • Matlab風格的初始化
    1 Mat E = Mat::eye(4, 4, CV_64F);
    2 Mat O = Mat::ones(2, 2, CV_32F);
    3 Mat Z = Mat::zeros(3, 3, CV_8UC1);
  • 逗號分隔的初始化小矩陣
    1 Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
  • 使用clone或copyTo。
    1 Mat RowClone = C.row(1).clone(); // randu(RowClone, Scalar::all(0), Scalar::all(255))可以在low和high之間隨機

    

怎樣遍歷圖片


 

  首先,我們可以使用一段代碼計算程序執行的時間:

1 double t = (double)getTickCount();
2 // do something
3 t = ((double)getTickCount() - t) / getTickFrequency();

 

  

圖像的存儲

  在RGB系統中,圖像是這樣存儲的:(注意是BGR的形式,可以使用 isContinunous() 函數查看是否連續存放)

(下面以顏色空間縮減為例子說明)

C風格的讀法

先用指針p指向一行,然后再p[j]形式讀取

 1 int channels = I.channels();
 2 int nRows = I.rows;
 3 int nCols = I.cols * channels;
 4 
 5 if (I.isContinuous()) {
 6     nCols = nCols * nRows;
 7     nRows = 1;
 8 }
 9 
10 int i, j;
11 uchar* p;
12 for (i=0; i<nRows; ++i) {
13     for (j=0; j<nCols; ++j) {
14         p = I.ptr<uchar>(i);
15         p[j] = table[p[j]]; //查表替換
16     }
17 }

 

迭代(安全)方法

迭代器從begin到end,使用(*it)[0]形式讀取

const int channels = I.channels();
switch(channels)
{
case 1: {
    MatIterator_<uchar> it, end;
    for (it = I.begin<uchar>(), end=I.end<uchar>(); it != end; ++it)
        *it = table[*it];
    break;
    }
case 3: {
    MatIterator_<Vec3b> it, end;
    for (it = I.begin<Vec3b>(), end=I.end<Vec3b>(); it != end; ++it)
        (*it)[0] = table[(*it)[0]];
        (*it)[1] = table[(*it)[1]];
        (*it)[2] = table[(*it)[2]];
    }
}

 

 通過相關返回值的On-the-fly地址計算

先把矩陣轉換成Mat_,再用_I(i, j)[0]形式讀取

const int channels = I.channels();
switch(channels)
{
case 1:
    {
        for (int i=0; i<I.rows; ++i)
            for (int j=0; j<I.cols; ++j)
                I.at<uchar>(i, j) = table[I.at<uchar>(i, j)];
        break;
    }
case 3:
    {
        Mat_<Vec3b> _I = I;
        for (int i=0; i<I.rows; ++i)
            for (int j=0; j<I.cols; ++j) {
                _I(i, j)[0] = table[_I(i, j)[0]];
                _I(i, j)[1] = table[_I(i, j)[1]];
                _I(i, j)[2] = table[_I(i, j)[2]];
            }
        I = _I;
        break;
    }
}

 

快速實現表替換

Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.data;
for (int i=0; i < 256; ++i)
    p[i] = table[i];
LUT(I, lookUpTable, I);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM