打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
OpenCV-图像像素遍历操作的三种方法对比(程序提速)

作者:Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

场景需求

       使用OpenCV,避免不了的就是对图像像素进行操作,遍历操作更是家常便饭,当图像数据不多时,各类方法的速度差不太多;但面对数据计算次数非常多,尤其在进行迭代、拟合、递归等数据反复计算的工作时,此时不得不面对的就是程序性能、算法提速和优化的问题了。

       在OpenCV中有三种最常用的像素操作方法,分别是动态地址操作法、迭代器操作法和指针操作法。下面简单介绍下该三种方法的优劣和使用场景:

  1. 动态地址操作法。最常用,比如直接A.at,操作Mat矩阵A第i行第j列的像素值,简洁明了,适合对某个或某几个值直接操作时使用,定位非常方便,也可以遍历计算使用,但是速度方面比其他两个方法都要慢;
  2. 迭代器操作法。运用了C++中STL的理念,通过迭代器的方式,获取矩信息的头尾(begin和end),然后依次操作,该方法适合连续计算时使用,速度和动态地址操作法差不多,比指针法慢,但是胜在安全性好;
  3. 指针法。我最喜欢的遍历法,一遇到遍历操作必用ptr,该方法缺点就是指针容易越界,编代码时要慎重,确保安全和稳定,但是速度没得说。

       三种方法一般情况在debug下运行差异性很明显,release下没那么明显;但是一旦遍历的函数复杂性加大了,release下的差异性就会体现出来了,指针法绝对是最快的,我在做图像迭代拟合计算时,用指针法替代动态地址法,速度至少提高5-10倍,一点不夸张。。。

      下面简单写了段测试代码,展示下三种方法的使用方式,顺便测试下debug和release下的运行时间情况。

C++实现代码

// 动态地址操作法
for (int i = 0; i < A.rows; i++)
{
for (int j = 0; j < A.cols; j++)
{
P.at<float>(i, j) *= 2;
}
}

// 迭代器操作法
Mat_<float>::iterator it = B1.begin<float>();
Mat_<float>::iterator itend = B1.end<float>();
for (; it!=itend; ++it)
{
(*it)*= 2;
}


// 指针操作法
for (int i = 0; i < A.rows; i++)
{
float *data = B2.ptr<float>(i);
for (int j = 0; j < A.cols; j++)
{
data[j]*= 2;
}
}

测试代码

#include<iostream>
#include<opencv2/opencv.hpp>
#include<ctime>
using namespace std;
using namespace cv;
int main(void)
{
Mat A = Mat::zeros(10000, 10000, CV_32FC1);
// 随意创建一个A矩阵
for (int i = 0; i < A.rows; i++)
{
for (int j = 0; j < A.cols; j++)
{
A.at<float>(i, j) = rand()%100/100.f;
}
}
Mat B0,B1,B2;
B0 = A.clone();
B1 = A.clone();
B2 = A.clone();

// 动态地址操作法
double time0 = static_cast<double>(getTickCount());
for (int i = 0; i < A.rows; i++)
{
for (int j = 0; j < A.cols; j++)
{
B0.at<float>(i, j) *= 2;
}
}
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "    动态地址法运行时间为:" << time0 << "秒" << endl << endl;

// 迭代器操作法
double time1 = static_cast<double>(getTickCount());
Mat_<float>::iterator it = B1.begin<float>();
Mat_<float>::iterator itend = B1.end<float>();
for (; it!=itend; ++it)
{
(*it)*= 2;
}
time1 = ((double)getTickCount() - time1) / getTickFrequency();
cout << "    迭代器法运行时间为:" << time1 << "秒" << endl << endl;

// 指针操作法
double time2 = static_cast<double>(getTickCount());
for (int i = 0; i < A.rows; i++)
{
float *data = B2.ptr<float>(i);
for (int j = 0; j < A.cols; j++)
{
data[j]*= 2;
}
}
time2 = ((double)getTickCount() - time2) / getTickFrequency();
cout << "    指针法运行时间为:" << time2<< "秒" << endl << endl;

system("pause");
return 0;
}

测试效果

      release下:

图1 release下运行时间对比

      debug下:

图2 debug下运行时间对比

       综上所述,追求安全性就用迭代器,追求方便和便于定位就用动态地址at,追求极致速度就用ptr指针。

       如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
C/C++
023.指向数组的指针
Python进阶系列(一)
重学 Java 设计模式:实战迭代器模式「模拟公司组织架构树结构关系,深度迭代遍历人员信息输出场景」
C++迭代器 iterator详解
java集合框架Collection
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服