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

图像存储特点:

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

使用opencv遍历图像方式:

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

彩色图像灰度化原理:
在这里插入图片描述
公式: Gray(i,j)=0.299R(i,j)+0.587G(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代表处理过程语句体
  }
}

关注公众号《AI算法之道》,获取更多AI算法资讯
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值