opencv学习(五)之像素遍历三种方式耗时分析

前面用两篇介绍了像素的颜色空间缩减、查找表、遍历像素的三种方式、程序计时等,也说了一下每种方法的优缺点,现在用一个综合型的程序进行对比。方式是用三种方式对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;
    }
}

运行结果如下:
opencv学习(五)之像素遍历三种方式耗时分析_第1张图片
三种方式在本程序中的耗时情况如下,值得注意的是程序耗时和电脑硬件和编译器等都有关系,在此我用的是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访问方式快

你可能感兴趣的:(OpenCV基础,opencv2/3基础教程)