图像存储特点:
但凡谈及图像,一般指的是一个二维的数组,我们知道数组在计算机内存中的存放就是一个连续的地址空间,该地址空间可以由第一个像素和最后一个像素的存储地址决定;也可以由第一像素和总的像素个数所决定。二维数组的各元素在内存中的存放顺序是按行存储的,即在此连续地址的内存空间中先存放第一行的所有元素,再存放第二行的内存数据,依次存放直至图像最后一行。
使用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代表处理过程语句体}}

