OpenCV:Mat元素访问方法、性能、代码复杂度以及安全性分析
欢迎转载,尊重原创,所以转载请注明出处:
http://blog.csdn.net/bendanban/article/details/30527785
本文讲述了OpenCV中几种访问矩阵元素的方法,在指定平台上给出性能比较,分析每种矩阵元素访问方法的代码复杂度,易用性。
一、预备设置
本文假设你已经正确配置了OpenCV的环境,为方便大家实验,在文中也给出了编译源程序的Makefile,其内容如代码段1所示。
采用如代码段2所示的计时函数,这段代码你可以在我之前的博文中找到,abtic() 可以返回微秒(10^-6秒)级,而且兼容Windows和Linux系统。
本文使用彩色图像做实验,所以矩阵是2维的3通道的。
CC = g++ CPPFLAGS = -O3 `pkg-config --cflags opencv` CPPLIB = `pkg-config --libs opencv` OBJS = test.o main.exe : $(OBJS) $(CC) $(CPPFLAGS) $^ -o $@ $(CPPLIB) test.o: test.cpp $(CC) -c $(CPPFLAGS) $^ -o $@ clean: rm -rf *.out main.exe *.o run: ./main.exe
代码段 1. Makefile文件的内容
#if defined(_WIN32) && defined(_MSC_VER)
#include <windows.h>
double abtic() {
__int64 freq;
__int64 clock;
QueryPerformanceFrequency( (LARGE_INTEGER *)&freq );
QueryPerformanceCounter( (LARGE_INTEGER *)&clock );
return (double)clock/freq*1000*1000;
}
#else
#include <time.h>
#include <sys/time.h>
double abtic() {
double result = 0.0;
struct timeval tv;
gettimeofday( &tv, NULL );
result = tv.tv_sec*1000*1000 + tv.tv_usec;
return result;
}
#endif /* _WIN32 */
代码段 2. 计时函数abtic()的定义
二、测试算法
文中用于测试的算法:将矩阵中每个元素乘以一个标量,写入一个新的矩阵,每个通道操作独立。
如果用im(r,c,k)表示矩阵im的第r行、第c列、第k个通道的值的话,算法为:om(r,c,k) = im(r,c,k)*scale;其中scale是一个大于0、小于1的浮点数。
三、五种Mat元素的访问方法
方法1、使用Mat的成员函数at<>()
Mat的成员函数at()是一个模板函数,我们这里用的是二维矩阵,所以我们使用的at()函数的声明如代码段3所示(取自OpenCV的源文件)。
template<typename _Tp> _Tp& at(int i0, int i1);
代码段3 .at()函数的声明
代码段4是本文第二部分描述的算法的实现,矩阵元素使用at<>()函数来索引。
Vec3b pix;
for (int r = 0; r < im.rows; r++)
{
for (int c = 0; c < im.cols; c++)
{
pix = im.at<Vec3b>(r,c);
pix = pix*scale;
om.at<Vec3b>(r,c) = pix;
}
}
代码段4. 使用at<>()函数访问矩阵元素
注意:使用at函数时,应该知道矩阵元素的类型和通道数,根据矩阵元素类型和通道数来确定at函数传递的类型,代码段4中使用的是Vec3b这个元素类型,他是一个包含3个unsigned char类型向量。之所以采用这个类型来接受at的返回值,是因为,我们的矩阵im是3通道,类型为unsigned char类型的。
方法2、使用Mat的成员函数ptr<>()
此函数也是模板函数,我们将会用到的ptr函数声明如代码段5所示。此函数返回指定的数据行的首地址。
template<typename _Tp> _Tp* ptr(int i0=0);
代码段 5. ptr成员函数的声明
使用ptr<>()成员函数完成本文第二部分所述算法的代码如代码段6所示。
Vec3b *ppix_im(NULL);
Vec3b *ppix_om(NULL);
for (int r = 0; r < im.rows; r++)
{
ppix_im = im.ptr<Vec3b>(r);
ppix_om = om.ptr<Vec3b>(r);
for (int c = 0; c < im.cols; c++)
{
ppix_om[c] = ppix_im[c]*scale;
}
}
代码段 6. 使用ptr访问矩阵元素
方法3、使用迭代器
这里使用的迭代器是OpenCV自己定义的。使用迭代器完成第二部分所述算法的代码如代码段7所示。
MatIterator_<Vec3b> it_im, itEnd_im;
MatIterator_<Vec3b> it_om;
it_im = im.begin<Vec3b>();
itEnd_im = im.end<Vec3b>();
it_om = om.begin<Vec3b>();
for (; it_im != itEnd_im; it_im++, it_om++)
{
*it_om = (*it_im)*scale;
}
代码段 7. 使用迭代器访问矩阵元素
方法4、使用Mat_简化索引
Mat_这个类的元素访问比较容易一点,把原Mat类的对象可以直接赋值给Mat_对象,当然赋值操作并不会开辟新的数据空间,这点大家放心。也就是说使用Mat_时,不会在内存拷贝上花时间。使用这种方法完成第二部分所述算法的代码如代码段8所示。
Mat_<Vec3b> im_, om_;
im_ = im;
om_ = om;
for (int r = 0; r < im.rows; r++)
{
for (int c = 0; c < im.cols; c++)
{
om_(r,c) = im_(r,c) * scale;
}
}
代码段 8. 使用Mat_访问矩阵数据元素
方法5、使用OpenCV原有的实现
我们的算法实际上OpenCV中已经有实现。就是×运算符重载,代码如代码段9所示。
om = im*scale;
代码段 9. 使用OpenCV的原有实现访问矩阵元素
四、实验测试
1、测试代码
为了测试方便,将前面的方法统一写到一个c++源文件test.cpp中,其内容如代码段10所示。
/*************************************************************************
> File Name: test.cpp
> Author: aban
> Mail: sawpara@126.com
> Created Time: 2014年06月13日 星期五 18时47分19秒
************************************************************************/
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
#if defined(_WIN32) && defined(_MSC_VER)
#include <windows.h>
double abtic() {
__int64 freq;
__int64 clock;
QueryPerformanceFrequency( (LARGE_INTEGER *)&freq );
QueryPerformanceCounter( (LARGE_INTEGER *)&clock );
return (double)clock/freq*1000*1000;
}
#else
#include <time.h>
#include <sys/time.h>
double abtic() {
double result = 0.0;
struct timeval tv;
gettimeofday( &tv, NULL );
result = tv.tv_sec*1000*1000 + tv.tv_usec;
return result;
}
#endif /* _WIN32 */
#define ISSHOW 0
int main(int argc, char** argv)
{
double tRecorder(0.0);
Mat im = imread("./bigim.tif");
Mat om;
om.create(im.rows, im.cols, CV_8UC3);
#if ISSHOW
imshow("orignal Image", im);
waitKey();
#endif
float scale = 150.0f/255.0f;
// 1. using at()
tRecorder = abtic();
Vec3b pix;
for (int r = 0; r < im.rows; r++)
{
for (int c = 0; c < im.cols; c++)
{
pix = im.at<Vec3b>(r,c);
pix = pix*scale;
om.at<Vec3b>(r,c) = pix;
}
}
cout << (abtic() - tRecorder) << " using at<>()" << endl;
#if ISSHOW
imshow("Scaled Image: using at<>()", om);
waitKey();
#endif
// 2. using ptr
tRecorder = abtic();
Vec3b *ppix_im(NULL);
Vec3b *ppix_om(NULL);
for (int r = 0; r < im.rows; r++)
{
ppix_im = im.ptr<Vec3b>(r);
ppix_om = om.ptr<Vec3b>(r);
for (int c = 0; c < im.cols; c++)
{
ppix_om[c] = ppix_im[c]*scale;
}
}
cout << (abtic() - tRecorder) << " using ptr<>() " << endl;
#if ISSHOW
imshow("Scaled Image: using ptr<>()", om);
waitKey();
#endif
// 3. using iterator
tRecorder = abtic();
MatIterator_<Vec3b> it_im, itEnd_im;
MatIterator_<Vec3b> it_om;
it_im = im.begin<Vec3b>();
itEnd_im = im.end<Vec3b>();
it_om = om.begin<Vec3b>();
for (; it_im != itEnd_im; it_im++, it_om++)
{
*it_om = (*it_im)*scale;
}
cout << (abtic() - tRecorder) << " using iterator " << endl;
#if ISSHOW
imshow("Scaled Image: using iterator", om);
waitKey();
#endif
// 4. using Mat_
tRecorder = abtic();
Mat_<Vec3b> im_, om_;
im_ = im;
om_ = om;
for (int r = 0; r < im.rows; r++)
{
for (int c = 0; c < im.cols; c++)
{
om_(r,c) = im_(r,c) * scale;
}
}
cout << (abtic() - tRecorder) << " using Mat_ " << endl;
#if ISSHOW
imshow("Scaled Image: using Mat_", om);
waitKey();
#endif
// 5. using *
tRecorder = abtic();
om = im*scale;
cout << (abtic() - tRecorder) << " using * " << endl;
#if ISSHOW
imshow("Scaled Image: using *", om);
waitKey();
#endif
return 0;
}
代码段10. 测试代码
如果你想使用第一部分提到的Makefile,你需要将代码段10保存成test.cpp,或者保存成你希望的某个名字,但是同时应该修改Makfile中的所有“test.cpp”。
在正确执行之前,将代码段10中的第40行代码改成你的图片名称。
2、实验平台
CPU:Intel(R) Pentium(R) CPU G840 @ 2.80GHz
G++:4.8.2
OpenCV : 2.4.9
3、实验结果
编译选项使用-O3时,其中一次执行结果:
489570 using at<>() 467315 using ptr<>() 468603 using iterator 469041 using Mat_ 621367 using *
编译选项使用-O0 -g时,其中一次执行结果:
2.48216e+06 using at<>() 2.15397e+06 using ptr<>() 3.80784e+06 using iterator 2.38941e+06 using Mat_ 621099 using *
4、实验分析
从上面的结果可以看出,使用×时,在两种模式下,计算速度差不多,这实际是由于我们的程序调用的OpenCV的库函数,而这个库函数调用的是同一个。
如果你的产品要求执行速度,从-O3条件下的输出结果可以看出,ptr这种方式速度稍微快一点。但是他们的差别并不大,所以应该再考虑代码的复杂度。
代码复杂度用代码量(代码行数、列数)、使用变量的个数、使用变量个类型掌握难度(比如指针可能难一点)等因素来度量。
最小的就是使用×了(最后一个方法)。虽然他的复杂度较小,实际只有一行代码,但是对于实际的应用,你要想调用OpenCV已经实现的功能,首先要确定OpenCV里已经实现了这个功能。
其次,我认为复杂度较小的是方法一,因为它实际上可以不借用pix变量,完成前述算法,使用变量数较少,代码量也不多。
Mat_和ptr这两种方式的复杂度差不多,如果使用指针是一种稍微难一点的方式的话,那么Mat_的复杂度可以认为稍微小一点。
一般认为迭代器是C++里面比较高级的特性,也是学习C++最靠后的技术,再加上它使用了指针,如果指针算是比较难掌握的技术的话,使用迭代器这种方式复杂度可以说是最复杂的了。
有些情况下,需要考虑安全性,比如防止越界访问,如果你不想考虑过多边界的问题,使用迭代器也许是一种不错的选择!
五、总结
选择哪种元素访问方式,应该根据自己的实际应用环境,具体分析作出决定。主要考虑三个因素:性能、代码复杂度、安全性,根据自己的程序类型,选择。
OpenCV:Mat元素访问方法、性能、代码复杂度以及安全性分析的更多相关文章
- OpenCV:Mat元素访问方法、演出、代码的复杂性和安全性分析
欢迎转载.尊重原创,因此,请注明出处: http://blog.csdn.net/bendanban/article/details/30527785 本文讲述了OpenCV中几种訪问矩阵元素的方法, ...
- jquery iframe父子框架中的元素访问方法
在web开发中,经常会用到iframe,难免会碰到需要在父窗口中使用iframe中的元素.或者在iframe框架中使用父窗口的元素 js 在父窗口中获取iframe中的元素 1. 格式:window. ...
- OpenCV不同类型Mat的at方法访问元素时该如何确定模板函数的typename(转)
自从OpenCV推出了Mat后越来越像是Matlab了,使用起来方便了很多,但是,在用at方法访问Mat时,如何选用合适的typename类型来访问相应的Mat元素是个头疼的问题. 比如: int H ...
- 快速遍历OpenCV Mat图像数据的多种方法和性能分析 | opencv mat for loop
本文首发于个人博客https://kezunlin.me/post/61d55ab4/,欢迎阅读! opencv mat for loop Series Part 1: compile opencv ...
- 访问 HTML中元素的方法
http://www.w3school.com.cn/jsref/index.asp 1.document.getElementbyId("id1"),Html中,名称是id1 ...
- OpenCv Mat操作总结
Author:: Maddock Date: 2015-03-23 16:33:49 转载请注明出处:http://blog.csdn.net/adong76/article/details/4053 ...
- C++ Opencv Mat类型使用的几个注意事项及自写函数实现Laplace图像锐化
为了提升自己对Opencv中Mat数据类型的熟悉和掌握程度,自己尝试着写了一下Laplace图像锐化函数,一路坎坷,踩坑不断.现将代码分享如下: #include <opencv2/opencv ...
- OPENCV mat类
OpenCV参考手册之Mat类详解 目标 我们有多种方法可以获得从现实世界的数字图像:数码相机.扫描仪.计算机体层摄影或磁共振成像就是其中的几种.在每种情况下我们(人类)看到了什么是图像.但是,转换图 ...
- opencv Mat 像素操作
1 cv::Mat cv::Mat是一个n维矩阵类,声明在<opencv2/core/core.hpp>中. class CV_EXPORTS Mat { public: //a lo ...
随机推荐
- Django框架之虚拟环境搭建
创建虚拟环境篇 今天小编就来讲一下在Ubantu下如何搭建Django环境,希望能帮助那些不会搭建的童鞋^o^ 0.首先要先安装好Python环境,至于安装过程,小编就不讲了,百度一下,你懂得.. 1 ...
- 从零开始搭建口袋妖怪管理系统(1)-从Angular1.x开始
开坑,一直喜欢口袋妖怪,想着能写点有关的程序. 最近项目要改写管理系统,所以用Angular1.x搭建一个口袋妖怪管理系统试试. 巩固Ng1.x的知识+学习库的用法,然后算是记录一个系统从零开始到成型 ...
- 报错pymysql.err.DataError: (1406, "Data too long for column 'gender' at row 1")
在Django默认的admin后台创建超级用户时, 报错pymysql.err.DataError: (1406, "Data too long for column 'gender' at ...
- 【数据结构】【平衡树】无旋转treap
最近在研究平衡树,看起来这种东西又丧水又很深,感觉很难搞清楚.在Ditoly学长的建议下,我先学习了正常的treap,个人感觉这应该是平衡树当中比较好懂的而且比较好写的一种. 然而,发现带旋treap ...
- [Codeforces]853E - Lada Malina
题目大意:给出平面上$n$个带权点$f_{i}$,再给出$k$个向量$v_{i}$,每次询问给出一个点$p$和一个值$t$,求能满足$f_{i}+\sum w_{j}v_{j}=p(-t<=w_ ...
- hdu4549(费马小定理 + 快速幂)
M斐波那契数列F[n]是一种整数数列,它的定义如下: F[0] = a F[1] = b F[n] = F[n-1] * F[n-2] ( n > 1 ) 现在给出a, b, n,你能求出F[n ...
- 【Uva 10269 马里奥与公主的归途】
·马里奥n次解救了公主,现在需要从魔王的宫殿返回. ·英文题,述大意: 给定一个点数不超过100的无向图,其中的点分为两类:乡村和城堡. 输入A个乡村,B个城堡(乡村编号1~A,城堡编号A+ ...
- hihiocoder 1255(搜索)(2015ACM/ICPC北京站)
题意: 给你四个矩形,判断能否从中选出3个组成一个矩形 思路: 1.搜索,如果两个能组成一个新的,则将他们合并,继续搜索 2.暴力判断 最开始没注意到3,一直以为要用4个,WR #include &l ...
- JS按照指定的周期来调用函数方法
setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式. setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭.由 s ...
- MVC简单随笔
MVC的具体含义是:model+view+controller,即模型+视图+控制它们各自处理自己的任务: (1)模型(model):模型持有所有的数据.状态和程序逻辑.模型独立于视图和控制器.(2) ...