1. 程式人生 > >《OpenCV3程式設計入門》——5.1.5 訪問影象中畫素的三類方法(指標訪問、迭代器iterator、動態地址計算配合at元素)

《OpenCV3程式設計入門》——5.1.5 訪問影象中畫素的三類方法(指標訪問、迭代器iterator、動態地址計算配合at元素)

目錄

1、指標訪問畫素

2、迭代器操作畫素

3、動態地址計算


OpenCV中,有三種方式訪問影象畫素: 

  1. 指標訪問:C操作符[];
  2. 迭代器iterator
  3. 動態地址計算

上述方法在訪問速度上略有差異。debug模式下,差異非常明顯,在release模式下,差異就不太明顯。

下邊通過一組例子來說明這三種方法的使用,程式的作用是減少顏色的數量,比如原來的影象是256種顏色,我們希望將它變成64中顏色,只需要將原來的顏色除以4(整除)以後再乘以4即可。

主程式程式碼如下:

//---------------------------------【標頭檔案、名稱空間包含部分】--------------------------
//		描述:包含程式所使用的標頭檔案和名稱空間
//---------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>   
#include <iostream>  

using namespace std;
using namespace cv;

//-----------------------------------【全域性函式宣告部分】-----------------------------------
//          描述:全域性函式宣告
//------------------------------------------------------------------------------------------
//減少顏色
void colorReduce(Mat& inputImage, Mat& outputImage, int div);


//--------------------------------------【main( )函式】---------------------------------------
//          描述:控制檯應用程式的入口函式,我們的程式從這裡開始執行
//-----------------------------------------------------------------------------------------------
int main()
{
    //【1】建立原始圖並顯示
    Mat srcImage = imread("1.jpg");
    imshow("原始影象", srcImage);

    //【2】按原始圖的引數規格來建立建立效果圖
    Mat dstImage;
    dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());//效果圖的大小、型別與原圖片相同 

    //【3】記錄起始時間
    double time0 = static_cast<double>(getTickCount());

    //【4】呼叫顏色空間縮減函式
    colorReduce(srcImage, dstImage, 32);

    //【5】計算執行時間並輸出
    time0 = ((double)getTickCount() - time0) / getTickFrequency();
    cout << "\t此方法執行時間為: " << time0 << "秒" << endl;  //輸出執行時間

    //【6】顯示效果圖
    imshow("效果圖", dstImage);
    waitKey(0);
}

1、指標訪問畫素

指標訪問畫素利用的是C語言中的操作符[]。這種方法最快,但是略有點抽象。實驗條件下單詞執行時間為0.00370487秒,程式碼如下:

//---------------------------------【colorReduce( )函式】---------------------------------
//          描述:使用【指標訪問:C操作符[ ]】方法版的顏色空間縮減函式
//----------------------------------------------------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
    //引數準備
    outputImage = inputImage.clone();  //拷貝實參到臨時變數
    int rowNumber = outputImage.rows;  //行數
    int colNumber = outputImage.cols*outputImage.channels();  //列數 x 通道數=每一行元素的個數

    //雙重迴圈,遍歷所有的畫素值
    for (int i = 0; i < rowNumber; i++)  //行迴圈
    {
        uchar* data = outputImage.ptr<uchar>(i);  //獲取第i行的首地址
        for (int j = 0; j < colNumber; j++)   //列迴圈
        {
            // ---------【開始處理每個畫素】-------------     
            data[j] = data[j] / div*div;
            // ----------【處理結束】---------------------
        }  //行處理結束
    }
}

分析講解上述程式碼:

Mat類屬性:

  • 公有變數cols和rows給出影象的寬和高
  • channels():返回影象的通道數。灰度圖通道數為1,彩色通道數為3
  • ptr()函式:得到任意行的首地址。ptr是一個模板函式,它返回第i行的首地址:uchar* data = outputImage.ptr<uchar>(i);  //獲取第i行的首地址

每行的畫素值個數以下語句得到:

int colNumber = outputImage.cols*outputImage.channels();  //列數 x 通道數=每一行元素的個數

執行結果:

                                            原始圖

                                           效果圖

2、迭代器操作畫素

迭代器需要做的僅僅是獲得影象矩陣的begin和end,然後增加迭代直至從begin到end。將 * 操作符新增在迭代指標前,即可訪問當前指向的內容。

相比使用指標可能越界問題,迭代器絕對是非常安全的方法。

//-------------------------------------【colorReduce( )函式】-----------------------------
//		描述:使用【迭代器】方法版的顏色空間縮減函式
//----------------------------------------------------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
    //引數準備
    outputImage = inputImage.clone();  //拷貝實參到臨時變數
    //獲取迭代器
    Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();  //初始位置的迭代器
    Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();  //終止位置的迭代器

    //存取彩色影象畫素
    for (; it != itend; ++it)
    {
        // ------------------------【開始處理每個畫素】--------------------
        (*it)[0] = (*it)[0] / div*div;  //藍色通道
        (*it)[1] = (*it)[1] / div*div;  //綠色通道
        (*it)[2] = (*it)[2] / div*div;  //紅色通道
        // ------------------------【處理結束】----------------------------
    }
}

執行結果:  

                                            原始圖

                                           效果圖

3、動態地址計算

動態地址運算配合at方法。這種方法簡單明瞭,符合我們對畫素的認識。

//----------------------------------【colorReduce( )函式】-------------------------------
//          描述:使用【動態地址運算配合at】方法版本的顏色空間縮減函式
//----------------------------------------------------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
    //引數準備
    outputImage = inputImage.clone();  //拷貝實參到臨時變數
    int rowNumber = outputImage.rows;  //行數
    int colNumber = outputImage.cols;  //列數

    //存取彩色影象畫素
    for (int i = 0; i < rowNumber; i++)
    {
        for (int j = 0; j < colNumber; j++)
        {
            // ------------------------【開始處理每個畫素】--------------------
            outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0] / div*div;  //藍色通道
            outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[1] / div*div;  //綠色通道
            outputImage.at<Vec3b>(i, j)[2] = outputImage.at<Vec3b>(i, j)[2] / div*div;  //紅是通道
            // -------------------------【處理結束】----------------------------
        }  // 行處理結束     
    }
}

分析講解程式碼:

Mat:

  • 成員函式at(int y, int x):用來儲存影象元素,但是必須在編譯期知道影象的資料型別。需要注意的是,一定要確保指定的資料型別和矩陣中的資料型別符合,因為at方法本身不會對任何資料型別進行轉換。

Vec3b:

  • 一個影象的Mat會返回一個由三個8位陣列成的向量,,將其定義為Vec3b,即由三個unsigned char組成的向量,所以存取彩色影象畫素的程式碼可以寫出如下形式:
image.at<Vec3b>(i, j)[chanel] = value;  //索引值channel標明瞭顏色通道號

執行結果:

                                            原始圖

                                           效果圖