大数跨境
0
0

【深度好文】高效图像访问方式

【深度好文】高效图像访问方式 AI算法之道
2021-06-20
1
导读:目前多用opencv进行图像处理,一般使用opencv访问图像时有三种方式,at,iterator,以及ptr;本文以图像灰度化为例对比了上述三种方式的访问效率,并给出了高效访问的一般形式。

图像存储特点:

但凡谈及图像,一般指的是一个二维的数组,我们知道数组在计算机内存中的存放就是一个连续的地址空间,该地址空间可以由第一个像素和最后一个像素的存储地址决定;也可以由第一像素和总的像素个数所决定。二维数组的各元素在内存中的存放顺序是按行存储的,即在此连续地址的内存空间中先存放第一行的所有元素,再存放第二行的内存数据,依次存放直至图像最后一行。

使用opencv遍历图像方式:

一般来说在opencv中可以使用Mat.at、iterator以及ptr来访问图像中的元素,接下来我们以彩色图像灰度化为例来对比以上几种方式的访问效率。

彩色图像灰度化原理: 

公式:                       Gray(i,j)=0.299*R(i,j)+0.587*G(i,j)+0.144*B(i,j)

彩色图像灰度化代码实现:

1)采用at方式实现:

void rgbtogray_1(Mat& image, Mat &result){    int height = image.rows;    int width = image.cols;    for (int i = 0; i < height; i++){        for (int j = 0; j < width; j++) {            int B = image.at<cv::Vec3b>(i, j)[0];            int G = image.at<cv::Vec3b>(i, j)[1];            int R = image.at<cv::Vec3b>(i, j)[2];            result.at<uchar>(i, j)=(uchar)(0.114*B + 0.587*G + 0.2989*R);        }    }}

2)用iterator方式实现:

void rgbtogray_2(Mat& image, Mat &result){    cv::Mat_<Vec3b>::iterator it = image.begin<Vec3b>();    cv::Mat_<Vec3b>::iterator itend = image.end<Vec3b>();    cv::Mat_<uchar>::iterator itt = result.begin<uchar>();    for (; it != itend; ++it, ++itt)    {        int B = (*it)[0];        int G = (*it)[1];        int R = (*it)[2];        (*itt) =(uchar)(0.114*B + 0.587*G + 0.2989*R);    }}

3)用ptr方式实现:

void rgbtogray_3(Mat& image, Mat &result){    int n = image.rows * image.cols;    uchar * src = image.ptr<uchar>(0);    uchar * dest = result.ptr<uchar>(0);    for(int i=0;i<n;i++)    {        int B = *(src);        int G = *(src+1);        int R = *(src+2);        *dest = (uchar)(0.114*B + 0.587*G + 0.2989*R);        dest++;        src += 3;    }}

4)以opencv官方实现作为对比:

void rgbtogray_cv(Mat &image,Mat &result){    cvtColor(image, result, CV_RGB2GRAY);}

对比访问效率:

选取一副1920X1080的图像作为测试图像,上述函数执行100次,计算平均耗时。为了统一统计以上几种方式的效率,采用函数指针方式传参,编写统计耗时函数如下:

typedef void (*FunType)(Mat& image, Mat &result);
double get_time(FunType func ,Mat & image, Mat & result){ clock_t t1 = clock(); for(int i=0;i<100;i++) { func(image,result); } clock_t t2 = clock(); double contume = (t2 - t1) * 1.0 * 1000 /CLOCKS_PER_SEC ; double avg_time = contume / 100.0; return avg_time;}

【对比结果如下】

主函数如下:

int main(){    string img_name = "./sample/sample.jpg";    cv::Mat src_image = cv::imread(img_name);    std::cout<< src_image.size()<< std::endl;    cv::Mat dst_image = cv::Mat(src_image.size(),CV_8UC1);
double time_consume0 = get_time(rgbtogray_cv ,src_image, dst_image);    std::cout<<"rgbtogray_cv time consume "<< time_consume0 << "ms"<< std::endl; double time_consume1 = get_time(rgbtogray_1 ,src_image, dst_image);    std::cout<<"rgbtogray_1 time consume "<< time_consume1 << "ms"<< std::endl; double time_consume2 = get_time(rgbtogray_2 ,src_image, dst_image);    std::cout<<"rgbtogray_2 time consume "<< time_consume2 << "ms"<< std::endl; double time_consume3 = get_time(rgbtogray_3 ,src_image, dst_image);    std::cout<<"rgbtogray_3 time consume "<< time_consume3 << "ms"<< std::endl; return 0;}

结果如下:

其中上述调用opencv库函数耗时为9.27ms,使用at方式的耗时为9.15ms,使用iterator方式的耗时为10.83ms,使用指针方式的耗时为6.56ms。推荐使用指针的方式进行访问。


【总结】

建议图像访问的一般形式如下:

uchar *pCur,*pEnd;for(pCur=pImg,pEnd=pImg+ImageSize;pCur<pEnd;pCur++){      *pCur=f(*pCur);  //f代表处理过程语句体}

不建议使用的访问形式:

for(int i=0;i<height;i++){  for(int j=0;j<width;j++)  {      pImg[i][j]=f(pImg[i][j]);  //f代表处理过程语句体  }}


【声明】内容源于网络
0
0
AI算法之道
一个专注于深度学习、计算机视觉和自动驾驶感知算法的公众号,涵盖视觉CV、神经网络、模式识别等方面,包括相应的硬件和软件配置,以及开源项目等。
内容 573
粉丝 0
AI算法之道 一个专注于深度学习、计算机视觉和自动驾驶感知算法的公众号,涵盖视觉CV、神经网络、模式识别等方面,包括相应的硬件和软件配置,以及开源项目等。
总阅读436
粉丝0
内容573