opencv學習(五)之畫素遍歷三種方式耗時分析
阿新 • • 發佈:2019-01-06
前面用兩篇介紹了畫素的顏色空間縮減、查詢表、遍歷畫素的三種方式、程式計時等,也說了一下每種方法的優缺點,現在用一個綜合型的程式進行對比。方式是用三種方式對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訪問方式快