1. 程式人生 > >opencv影象畫素操作方法

opencv影象畫素操作方法

影象容器Mat

Mat和Matlab裡的陣列格式有點像,但一般是二維向量,如果是灰度圖,一般存放<uchar>型別;如果是RGB彩色圖,存放<Vec3b>型別。 單通道灰度圖資料存放格式:
多通道的影象中,每列並列存放通道數量的子列,如RGB三通道彩色圖:
有一點需要注意:影象的通道順序是:BGR。通常情況記憶體足夠大的話影象的每一行是連續存放的,也就是在記憶體上影象的所有資料存放成一行,這中情況在訪問時可以提供很大方便好了,下面總結一下常見的訪問影象畫素的三種方法:使用at動態地址計算方式,使用iterator迭代器方式,使用ptr指標。先附程式碼:
#include <iostream>  
#include<core/core.hpp>  
#include<highgui/highgui.hpp>   
using namespace cv;  
using namespace std;    
void colorReduceAt(Mat& srcImage, Mat& dstImageAt, int div);
void colorReduceIterator(Mat& srcImage, Mat& dstImageIterator, int div);
void colorReducePtr(Mat& srcImage, Mat& dstImagePtr, int div);
int main()  
{    
	Mat image=imread("e:\\kobe.jpg"); 
	Mat mv[3];
	split(image,mv); 
	if(!image.data)  
	{  
		cout<<"you idiot!where did you hide kobe!"<<endl;     
		system("pause");  
		return -1;  
	}    

//宣告處理後圖像變數
    Mat dstImageAt, dstImageIterator, dstImagePtr;
    dstImageAt = image.clone();
    dstImageIterator = image.clone();
    dstImagePtr = image.clone();
    int div = 4;
//宣告時間變數
    double timeAt, timeIterator, timePtr;
    timeAt = static_cast<double>(getTickCount());
    colorReduceAt(image, dstImageAt, div);
    timeAt = ((double)getTickCount() - timeAt) / getTickFrequency();
    namedWindow("dstImageAt",CV_WINDOW_NORMAL);
    imshow("dstImageAt",dstImageAt);
    cout << "使用at()動態地址計算耗時:" << timeAt << endl << endl;

    timeIterator = static_cast<double>(getTickCount());
    colorReduceIterator(image, dstImageIterator, div);
    timeIterator = ((double)getTickCount() - timeIterator) / getTickFrequency();
    namedWindow("dstImageIterator",CV_WINDOW_NORMAL);
    imshow("dstImageIterator",dstImageIterator);
    cout << "使用iterator迭代器耗時:" << timeIterator << endl << endl;

    timePtr = static_cast<double>(getTickCount());
    colorReducePtr(image, dstImagePtr, div);
    timePtr = ((double)getTickCount() - timePtr) / getTickFrequency();
    namedWindow("dstImagePtr",CV_WINDOW_NORMAL);
    imshow("dstImagePtr",dstImagePtr);
    cout << "使用ptr指標耗時:" << timePtr << endl;
//等待按鍵
    waitKey();  
    return 0;    
}  

//使用at動態地址計算方式
void colorReduceAt(Mat& srcImage, Mat& dstImageAt, int div)
{
    int rowNumber = dstImageAt.rows;      //獲取影象行數
    int colNumber = dstImageAt.cols;      //獲取影象列數

    //對每個畫素進行處理
    for(int i = 0; i < rowNumber; i++)
    {
        for(int j = 0; j < colNumber; j++)
        {
            dstImageAt.at<Vec3b>(i,j)[0] = dstImageAt.at<Vec3b>(i,j)[0]/div*div;    //B通道
            dstImageAt.at<Vec3b>(i,j)[1] = dstImageAt.at<Vec3b>(i,j)[1]/div*div;    //G通道
            dstImageAt.at<Vec3b>(i,j)[2] = dstImageAt.at<Vec3b>(i,j)[2]/div*div;    //R通道
        }
    }

}

//使用iterator迭代器方式
void colorReduceIterator(Mat& srcImage, Mat& dstImageIterator, int div)
{
    MatIterator_<Vec3b> imageIt = dstImageIterator.begin<Vec3b>();      //獲取迭代器初始位置
    MatIterator_<Vec3b> imageEnd = dstImageIterator.end<Vec3b>();       //獲取迭代器結束位置

    //對每個畫素進行處理
    for(;imageIt != imageEnd; imageIt++)
    {
        (*imageIt)[0] = (*imageIt)[0]/div*div;      //B通道
        (*imageIt)[1] = (*imageIt)[1]/div*div;      //G通道
        (*imageIt)[2] = (*imageIt)[2]/div*div;      //R通道
    }
}

//使用ptr指標
void colorReducePtr(Mat& srcImage, Mat& dstImagePtr, int div)
{
    int rowNumber = dstImagePtr.rows;                           //獲取影象矩陣行數
    int colNumber = dstImagePtr.cols*dstImagePtr.channels();    //三通道影象矩陣列樹=影象列數x通道數

    for(int i = 0; i < rowNumber; i++)
    {
        uchar* pixelPtr = dstImagePtr.ptr<uchar>(i);            //獲取矩陣每行首地址指標
        for(int j = 0; j < colNumber; j++)
        pixelPtr[j] = pixelPtr[j] / div * div;
    }
}
執行結果如下:

並且發現,使用ptr方式訪問畫素用時最少。

參考:

https://yq.aliyun.com/articles/9300

http://www.cnblogs.com/zjgtan/archive/2013/04/06/3002962.html

http://blog.csdn.net/keith_bb/article/details/53071133

後來新發現一篇比較全的博文介紹:http://blog.csdn.net/xiaowei_cqu/article/details/19839019