对比
今天看代码的时候,需要处理图像信息,在访问像素的时候,以往大都直接使用at进行访问,但在这里看到了使用data+step直接计算地址进行访问的方法。项目中需要处理很多图像,对访问相率有很高的要求,正好学习对比一下几种访问方式。
首先定义一个常见的三通道图片:
cv::Mat img = cv::Mat(cv::Size(1920, 1080), CV_8UC3, cv::Scalar::all(255));
使用at进行访问
//使用.at()进行访问
double t1 = static_cast<double>(cv::getTickCount());
for (int row = 0; row < img.rows; ++row) {
for (int col = 0; col < img.cols; ++col) {
cv::Vec3b a(1,2,3);
img.at<cv::Vec3b>(row, col) = a;
}
}
double t2 = static_cast<double>(cv::getTickCount());
std::cout << "Cost time (at):" << (t2 - t1) / cv::getTickFrequency() << endl;
使用迭代器进行访问
//使用迭代器进行访问
double t1 = static_cast<double>(cv::getTickCount());
cv::Mat_<cv::Vec3b>::iterator it = img.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::iterator itend = img.end<cv::Vec3b>();
for (; it != itend; ++it){
(*it)[0] = 0;
(*it)[1] = 0;
(*it)[2] = 0;
}
double t2 = static_cast<double>(cv::getTickCount());
std::cout << "Cost time (iterator ):" << (t2 - t1) / cv::getTickFrequency() << endl;
使用ptr指针进行访问
cv::Mat中提供ptr函数访问任意一行像素的首地址
//使用ptr指针进行访问
double t1 = static_cast<double>(cv::getTickCount());
int nc = img.cols * img.channels();
for (int j = 0; j<img.rows; j++)
{
uchar* data = img.ptr<uchar>(j);
for (int i = 0; i<nc; i++)
{
data[i] = 0;
}
}
std::cout << "Cost time (ptr):" << (t2 - t1) / cv::getTickFrequency() << endl;
使用step直接计算像素地址
step是Mat的一个public属性,表示Mat不同维度占据的字节大小,可以用来快速定位像素的位置。关于这个的理解可以参考博客链接: Mat成员变量-step
// 使用直接计算地址的方式进行访问
double t1 = static_cast<double>(cv::getTickCount());
for (int row = 0; row < img.rows; ++row) {
uchar *uc_pixel = img.data + row * img.step;
for (int col = 0; col < img.cols; ++col) {
uc_pixel[0] = 0;
uc_pixel[1] = 0;
uc_pixel[2] = 0;
uc_pixel += 3;
}
}
double t2 = static_cast<double>(cv::getTickCount());
std::cout << "cost time (step):" << (t2 - t1) / cv::getTickFrequency() << endl;
最后输出的时间为:
cost time (at):0.116068
cost time (iterator ):0.0598725
cost time (ptr) :0.0119962
cost time (step):0.0086011
结论
效率依次递增,使用step直接计算地址的效率是最高的,at效率是最低的。at方式的访问在底层采用的也是指针,但每次都要根据输入的参数对地址重新计算和定位,因此相对于移动指针来定位下一个元素的位置的方式来说,效率就有慢;