前面用两篇介绍了像素的颜色空间缩减、查找表、遍历像素的三种方式、程序计时等,也说了一下每种方法的优缺点,现在用一个综合型的程序进行对比。方式是用三种方式对lena图像(220x220)进行处理,使其颜色种类从256中变成64种。在颜色空间缩减方法中讲过这种方式,即每个像素值除以4向下取整然后再乘以4即可将其颜色种类缩减到64种。
#include
#include
#include
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(i,j)[0] = dstImageAt.at(i,j)[0]/div*div; //Blue
dstImageAt.at(i,j)[1] = dstImageAt.at(i,j)[1]/div*div; //Green
dstImageAt.at(i,j)[2] = dstImageAt.at(i,j)[2]/div*div; //Red
}
}
}
//使用iterator迭代器方式
void colorReduceIterator(Mat& srcImage, Mat& dstImageIterator, int div)
{
MatIterator_ imageIt = dstImageIterator.begin(); //获取迭代器初始位置
MatIterator_ imageEnd = dstImageIterator.end(); //获取迭代器结束位置
//对每个像素进行处理
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(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访问方式快