学习OpenCV(一)从Mat讲起

本博客所用OpenCV版本为2.4.3,运行环境为Visual Studio2012。

学习OpenCV是一个比较漫长的过程,希望我能够坚持!

(一)从Mat讲起

Mat是OpenCV中用于存放图像的数据结构。我们知道,图像在计算机中是以数组的形式存放的。Mat正是描述的这样一种数据结构。通过调用相关方法,我们能够实现对图像的输入输出以及一些操作。同时,Mat又不止可以作为图像容器,它也可以作为一种比较纯粹的描述矩阵这种数学对象的结构。它比C中的IplImage好的地方在于,由于它的“计数器”机制,我们不需要对它进行手动的内存回收,从而避免了常常困扰C/C++程序员的“内存泄露”问题。

让我们先来看看如何使用Mat来进行图像的输入输出。能够看到程序按照自己所想输出图片还是一件比较令人高兴的事情吧!

下面我将直接贴出代码,再进行解释。

#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
//#include <opencv/cv.hpp>
using namespace std;
using namespace cv;

int main()
{
	string namePic="pic.jpg";
	
	//Part 1  Read and Load Image
	/************************************************************************/
	/* 下面的代码显示了如何用imread函数进行图像读取操作                                                                     */
	/************************************************************************/
	
	Mat Img_Color=imread (namePic,CV_LOAD_IMAGE_COLOR);     //生成了彩色图
	Mat Img_Gray=imread (namePic,CV_LOAD_IMAGE_GRAYSCALE);   //生成了灰度图
	
	/************************************************************************/
	/* 下面的代码显示了如何用imshow函数进行图像的显示                                                                     */
	/************************************************************************/
	
	imshow ("彩色小女孩",Img_Color);
	imshow ("灰度小女孩",Img_Gray);
        waitKey();
	return 0;
}

现在看一下程序运行结果吧:


下面我们来解释一下代码:

头文件包含中除了C++标准库iostream以外,我们还使用到了highgui.cpp和core.cpp。其中前者包含了对图像进行IO操作要用到的一些类和宏的定义,后者包含了一些核心内容。

代码中图像的读入操作是使用函数imread()实现的。imread()函数的原型如下:

Mat imread(const string& filename, int flags=1 )

可以看到,返回值是一个Mat对象,第一个参数为字符串型,表示源图像的名称(如果不在源文件目录下,应该写成绝对路径),第二个参数决定了读入图像的颜色格式。上例代码中所出现的CV_LOAD_IMAGE_COLOR和CV_LOAD_IMAGE_GRAYSCALE分别表示读入彩色图像和灰度图像。

代码中图像的输出操作使用了函数imshow()实现。imshow()函数的原型如下:

void imshow(const string& winname, InputArray mat)

可以看到,第一个参数为字符串型,定义了窗口的名称,第二个参数为Mat,表明要显示的图片。

如果用过MATLAB,就会发现这两个函数和MATLAB中的图像输入输出函数名是完全一样的。而在阅读OpenCV 的tutorial文档时,也会发现不少与MATLAB相似的地方,比如在OpenCV中产生单位阵就可以调用Mat::eye()实现,是不是和MATLAB很相似呢??

至于代码最后的WaitKey函数,是为了防止代码运行结果一闪而过而加上去的。函数原型如下:

int waitKey(int delay=0)

参数代表程序运行至此停留的毫秒数,缺省值为0.代表forever。但是要注意,这里的延时并不是很准确,而是根据系统当前运行的任务多少而有变化,只是说至少会延迟delay毫秒时间。


Mat的数据结构包含了两部分数据,一部分是矩阵头,表明了矩阵的基本信息。另一部分就是用于存储图像的矩阵数组了。由于图像一般都很很大,所以对矩阵数组这部分内存进行频繁的读写复制操作是很费时间的。为了克服这个问题,OpenCV允许两个Mat对象有不同的矩阵头,却包含同一块矩阵数据。这样,在复制操作的时候,就避开了对大块内存空间的读写,节省了时间。比如下面的几种赋值方式,它们都只是复制了Mat A的矩阵头,同时复制了指向图像矩阵的内存的指针,但是却没有复制矩阵:

Mat C=A;      //利用等号
Mat E(A);      //利用Mat的构造函数

如果想要用矩形截取图像的一部分呢??我们可以使用如下的构造函数:

Mat B=Mat(A,Rect(0,0,100,100));      //截取A的100*100的矩形区域

那么如果我们处于某种需要,确实要复制矩阵呢??我们可以使用Mat类实例提供的clone方法和copyto方法。如下所示:

Mat G;
A.copyto(G);
Mat H=A.clone();


当然了,我们刚开始介绍Mat的时候就已经说过,Mat不仅可以视为一个图像的容器,还可以作为一个矩阵这样的数学对象。下面我们就来看一下如何生成这样的Mat对象实例。

首先,我们可以使用Mat的构造函数,通过查阅文档可以知道,Mat的构造函数可谓是多种多样,下面举的例子只是其中之一,更多内容还是应该访问网页http://docs.opencv.org/modules/core/doc/basic_structures.html#Mat来获得。

Mat B(20,20,CV_8UC1,Scalar(1));

这样,就建立了一个20*20,并且矩阵全部元素都是1的矩阵。

其次,我们可以使用Create函数。如下所示:
Mat B;
B.create (10,10,CV_8UC3);


不过要注意的是,这样方法不能同时对B矩阵元素进行初始化操作,就像这个样子:




或者,我们可以使用MATLAB风格的代码进行赋值:
Mat E = Mat::eye(4, 4, CV_64F);  //生成单位阵
Mat E=Mat::ones(2, 2, CV_32F);  //生成全1阵
Mat E=Mat::zeros(2, 2, CV_8UC1);//生成0矩阵
 
获取某个矩阵的一行或者一列可以采取下面的方式:
Mat RowClone = C.row(1).clone();

关于Mat这种数据结构的简单介绍就到这里,更多的还是应该去查阅相关手册文档,才能得到更加完整的认识。
 



posted on 2013-11-01 20:31  奥特曼  阅读(345)  评论(0编辑  收藏  举报

导航