OpenCV:Mat元素访问方法、性能、代码复杂度以及安全性分析

欢迎转载,尊重原创,所以转载请注明出处:

http://blog.csdn.net/bendanban/article/details/30527785

本文讲述了OpenCV中几种访问矩阵元素的方法,在指定平台上给出性能比较,分析每种矩阵元素访问方法的代码复杂度,易用性。

一、预备设置

本文假设你已经正确配置了OpenCV的环境,为方便大家实验,在文中也给出了编译源程序的Makefile,其内容如代码段1所示。

采用如代码段2所示的计时函数,这段代码你可以在我之前的博文中找到,abtic() 可以返回微秒(10^-6秒)级,而且兼容WindowsLinux系统。

本文使用彩色图像做实验,所以矩阵是2维的3通道的。

CC = g++
CPPFLAGS = -O3 `pkg-config --cflags opencv`
CPPLIB   = `pkg-config --libs opencv`

OBJS = test.o 

main.exe : $(OBJS)
  $(CC) $(CPPFLAGS) $^ -o [email protected] $(CPPLIB)

test.o: test.cpp
  $(CC) -c $(CPPFLAGS) $^ -o [email protected]

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: [email protected]
  > 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的函数是要确定函数是否存在的。

其次,我认为复杂度较小的是方法一,因为它实际上可以不借用pix变量,完成前述算法。

Mat_和ptr这两种方式的复杂度差不多,如果使用指针是一种稍微难一点的方式的话,那么Mat_的复杂度可以认为稍微小一点。

一般认为迭代器是C++里面比较高级的特性,也是学习C++最靠后的技术,再加上它使用了指针,如果指针算是比较难掌握的技术的话,使用迭代器这种方式复杂度可以说是最复杂的了。

有些情况下,需要考虑安全性,比如防止越界访问,如果你不想考虑过多边界的问题,使用迭代器也许是一种不错的选择!

五、总结

选择哪种元素访问方式,应该根据自己的实际应用环境,具体分析作出决定。主要考虑三个因素:性能、代码复杂度、安全性,根据自己的程序类型,选择。

OpenCV:Mat元素访问方法、性能、代码复杂度以及安全性分析,布布扣,bubuko.com

时间: 2024-10-29 10:47:31

OpenCV:Mat元素访问方法、性能、代码复杂度以及安全性分析的相关文章

opencv之mat元素访问

单通道mat元素访问:使用 img.at<float>(row,col) 1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <opencv/cv.h> 4 #include <opencv/highgui.h> 5 #include <opencv2/opencv.hpp> 6 7 using namespace std; 8 using namespace cv; 9 int

jquery iframe父子框架中的元素访问方法

jquery教程 在web开发中,经常会用到iframe,难免会碰到需要在父窗口中使用iframe中的元素.或者在iframe框架中使用父窗口的元素js在父窗口中获取iframe中的元素1.格式:window.frames["iframe的name值"].document.getElementById("iframe中控件的ID").click();实例:window.frames["ifm"].document.getElementById(&

60秒验证码倒计时js代码 js样式代码 方块上下左右随机移动(定时器) js中获取元素的方法 js中表单提交

60秒验证码倒计时js代码 <script type="text/javascript"> var countdown=60; function settime(val) { if (countdown == 0) { //removeAttribute() 方法删除指定的属性. disabled属性规定应该禁用 input 元素. val.removeAttribute("disabled"); val.value="免费获取验证码"

匿名对象,内部类,包的声明与访问,访问修饰符,代码块,java的API equals toString方法整理

匿名对象  匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量. public class Person{ public void eat(){ System.out.println(); } }创建一个普通对象person p=new person();创建一个匿名对象new person(); 匿名对象的特点: 1创建匿名对象直接使用,没有变量名. new Person().eat() //eat方法被一个没有名字的Person对象调用了. 2,匿名对象在没有指定其

opencv-从图像旋转学习Mat数据访问

先看一个简单的例子 代码: // ConsoleApplication3_6_23.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include<opencv2/opencv.hpp> #include<iostream> #include<vector> using namespace std; using namespace c

JS控制伪元素的方法汇总

转载自:http://www.jb51.net/article/81984.htm 一. 缘由: 本文源于在OSC社区中,有人提问如何用jq获取伪元素.我第一想法是强大的CSS Query应该可以获取伪元素吧. 然而事实上,CSS Query并不能.即我们不能通过$(":before").$(dom).find(":before")或document.querySelector(":before")来获取:before伪元素. 为此,我不得不重新

Selenium常见元素定位方法和操作

一. 定位元素方法 官网地址:http://selenium-python.readthedocs.org/locating-elements.html        这里有各种策略用于定位网页中的元素(locate elements),你可以选择最适合的方案,Selenium提供了一下方法来定义一个页面中的元素: find_element_by_id find_element_by_name find_element_by_xpath find_element_by_link_text fin

WPF中ItemsControl应用虚拟化时找到子元素的方法

wpf的虚拟化技术会使UI的控件只初始化看的到的子元素, 而不是所有子元素都被初始化,这样会提高UI性能. 但是我们经常会遇到一个问题: 应用虚拟化后看不见的子元素因为没有实际产生导致ItemContainerGenerator的查找元素方法(ContainerFromIndex / ContainerFromItem)失效. 解决办法1: (1)监听ItemsControl的ItemContainerGenerator的StatusChanged事件, 当GeneratorStatus为Con

JavaScript通过元素id和name直接获取元素的方法

概览: 偶然的机会,我在JavaScript中直接用HTML元素的id属性来获取该元素,并设置该元素的其他属性值,竟然能够正确解析不报错!于是我去查阅相关资料,也有其他同行这么用. 虽然说这种用法不是标准用法,推荐使用 document.getElementById(id);document.getElementByName(name); ,但也是一种新发现,先记录下来,以备后用. 本文讲述了JavaScript通过元素id和name直接获取元素的方法,以及自己在实践过程中总结的注意事项. 具体