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