1. 程式人生 > >opencv學習(五)之畫素遍歷三種方式耗時分析

opencv學習(五)之畫素遍歷三種方式耗時分析

前面用兩篇介紹了畫素的顏色空間縮減、查詢表、遍歷畫素的三種方式、程式計時等,也說了一下每種方法的優缺點,現在用一個綜合型的程式進行對比。方式是用三種方式對lena影象(220x220)進行處理,使其顏色種類從256中變成64種。在顏色空間縮減方法中講過這種方式,即每個畫素值除以4向下取整然後再乘以4即可將其顏色種類縮減到64種。

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>

using namespace std;
using namespace cv;

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() { //載入lena影象 Mat srcImage = imread("lena.jpg"); //判斷影象是否載入成功 if
(srcImage.empty()) { cout << "影象載入失敗!" << endl << endl; return -1; } else cout << "影象載入成功!" << endl << endl; imshow("srcImage",srcImage); //宣告處理後圖像變數 Mat dstImageAt, dstImageIterator, dstImagePtr; dstImageAt = srcImage.clone(); dstImageIterator = srcImage.clone(); dstImagePtr = srcImage.clone(); int
div = 4; //宣告時間變數 double timeAt, timeIterator, timePtr; timeAt = static_cast<double>(getTickCount()); colorReduceAt(srcImage, dstImageAt, div); timeAt = ((double)getTickCount() - timeAt) / getTickFrequency(); imshow("dstImageAt",dstImageAt); cout << "使用at()動態地址計算耗時:" << timeAt << endl << endl; timeIterator = static_cast<double>(getTickCount()); colorReduceIterator(srcImage, dstImageIterator, div); timeIterator = ((double)getTickCount() - timeIterator) / getTickFrequency(); imshow("dstImageIterator",dstImageIterator); cout << "使用iterator迭代器耗時:" << timeIterator << endl << endl; timePtr = static_cast<double>(getTickCount()); colorReducePtr(srcImage, dstImagePtr, div); timePtr = ((double)getTickCount() - timePtr) / getTickFrequency(); imshow("dstImagePtr",dstImagePtr); cout << "使用ptr指標耗時:" << timePtr << endl; waitKey(0); 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; //Blue dstImageAt.at<Vec3b>(i,j)[1] = dstImageAt.at<Vec3b>(i,j)[1]/div*div; //Green dstImageAt.at<Vec3b>(i,j)[2] = dstImageAt.at<Vec3b>(i,j)[2]/div*div; //Red } } } //使用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; //Blue (*imageIt)[1] = (*imageIt)[1]/div*div; //Green (*imageIt)[2] = (*imageIt)[2]/div*div; //Red } } //使用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; } }

執行結果如下:
這裡寫圖片描述
三種方式在本程式中的耗時情況如下,值得注意的是程式耗時和電腦硬體和編譯器等都有關係,在此我用的是cmake 3.5.1
這裡寫圖片描述
從上述耗時分析來看使用指標方式是最快的處理方式,而迭代器的方式相對最慢。但是使用迭代器是較為安全的訪問方式。
從上面程式中仔細分析指標式訪問和at()動態地址分配訪問方式的不同。可以找更大的影象對三種畫素遍歷方式進行分析,其耗時會由明顯差別。
除了上面三種方式,其官方文件還提到了使用LUT()函式。在進行影象處理時將所給的所有影象值替換成其他的值,opencv中提供的LUT()函式可以批量實現這種功能。其用法如下:

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

//然後呼叫函式(I是輸入影象,J是輸出影象)
LUT(I, lookUpTable, J);

官方文件中通過對一幅(2560x1600)影象進行上百次的處理得出如下結論:
1. 如果可能的話儘可能使用opencv中提供的引數
2. 最快的方式是LUT()函式,因為opencv庫通過Intel Threaded Building Blocks實現其多執行緒。
3. 如果寫一個簡單影象的遍歷程式推薦使用指標方式。
4. 迭代器是相對來講比較安全的訪問方式,但其速度也相對較慢。
5. 在Debug模式下,動態地址計算方法是最慢的訪問方式,但是在Release模式下它有可能比iterator訪問方式快