OpenCv学习笔记(四)--Mat基本图像容器Mat对象信息头,矩阵体的创建,深复制,浅复制详解

1--我们知道Mat是一个图像容器类,这个数据结构由两部分组成:
		1--矩阵头--即class Mat类所实例化的类对象所开辟的空间里面存储的数据---就是这个矩阵的信息,当我们以
		   Mat object;这样声明类对象的时候,也仅仅是创建了一个Mat的信息头,并没有创建矩阵体,也就是说,我们并
		   没有给将要存储的图像开辟相应的空间
		2--矩阵头--包含:
			1--矩阵的尺寸----比如---class Mat这个类中的----数据成员rows,cols---就可以指定图像的尺寸
			2--存储方法------对应---各种Mat的构造函数
			3--存储地址
			4--和一个指向----存储所有像素值的矩阵的----指针
2--因此,当在程序中,传递图像并创建拷贝时,大的开销是由矩阵造成的,而不是信息头。
3--OpenCv是一个图像处理库,囊括了大量的图像处理函数,为了解决问题,通常要使用库中的多个函数,因此,在函数中传
   递图像是家常便饭的事情.同时,不要忘了我们正在讨论的是计算量很大的图形处理算法,因此,除非万不得已,我们不应该
   拷贝大的图像,因为这会降低程序的速度
4--为了搞定这个问题,OpenCv使用---引用计数机制,其思路就是让每个Mat对象有自己的信息头,但共享一个矩阵。通过让矩阵
   指针指向同一地址而实现。而拷贝构造函数则只拷贝:
		1--信息头
		2--矩阵指针
	而不拷贝矩阵.

/*********************************************************************************************
程序功能:
        Mat基本图像容器Mat对象信息头,矩阵体的创建,深复制,浅复制详解
编写环境:
        OpenCv2.4.8+VS2010
地点时间:
        陕西师范大学 2016.4.25
作者信息:
        九月
**********************************************************************************************/
/********************************【头文件.命名空间包含部分】***********************************/
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/core/core.hpp>
#include<iostream>

using namespace cv;
using namespace std;

#define MAT_INFO_HEADER_SIZE  "Mat图像容器类信息头所占内存空间的大小----->"
#define WINDOW_SRC_NAME       "【原始图像】"
#define WINDOW_DST_NAME       "【复制构造函数图像】"
#define WINDOW_ASSIGN_NAME    "【赋值图像】"
#define WINDOW_ROI_NAME       "【ROI原图像部分数据】"
#define WINDOW_RANGE_NAME     "【Range指定的图像数据部分】"

/*****************************************【main()函数】**************************************/
int main(int argc,char** argv)
{

	/****
	*   在这里需要说明的一点是,C++中类对象的定义和java中对象的定义是有巨大区别的:
	*【1】C++中,当定义一个类的对象时,就为其分配了类的存储空间---说的通俗一点,就是说C++中,当用
	*     一个类去定义一个类对象时,其实就是将类---进行了实例化,产生了一个类的----具体实例
	*【2】而java中,类对象的定义和类对对象的实例化是分开进行的:
	*    【1】Person lili---类对象的定义---开辟了一个四个字节的空间----lili这个类对象其实相当于
	*         C++中的一个对象指针,此时并没有产生类的---具体实例
	*    【2】Java中只能手动的,用new关键字去实例化一个类的对象,如下所示:
	*         lili=new Person();
	****/

	//【1】只创建了信息头部分
	Mat src,assign;
	//【2】我们在这里测试一下,Mat信息头所占内存空间的大小
	cout<<MAT_INFO_HEADER_SIZE<<sizeof(src)<<"字节"<<endl;

	//【3】在这里,为矩阵开辟了内存空间---这相当于---矩阵体
	src=imread("D:\\scenery.png",CV_LOAD_IMAGE_COLOR);
    //【4】显示图片
	imshow(WINDOW_SRC_NAME,src);

	//【5】使用拷贝构造函数,只复制矩阵的信息头-----典型的浅复制
	Mat dst(src);
	assign=src;
	//【6】显示图片
	imshow(WINDOW_DST_NAME,dst);
	imshow(WINDOW_ASSIGN_NAME,assign);

	/****
	*【1】通过上述代码的结果可知.所有的Mat对象最终都指向了一个也是唯一的一个----数据矩阵。虽然它们的
	*     信息头不同,但是通过任何一个对象对图像矩阵所做的改变也会影响其它对象
	*【2】实际上,不同的Mat类对象,只是访问相同数据的不同途径而已
	****/

	/****
	*【1】这里介绍一个比较厉害的功能:你可以创建只引用---图像矩阵部分数据---的信息头。比如想要创建一个
	*     感兴趣的区域(ROI),你只需要创建包含边界信息的信息头
	*【2】实例如下所示:
	*     Mat dstROI(src,Rect(0,0,100,100))
	*     Mat dstROI_1(Range:all(),Range(1,3))
	****/

	//【7】使用一个矩形,定义ROI区域
	Mat dstROI(src,Rect(0,0,200,200));
	//【8】创建窗口+显示图像
	namedWindow(WINDOW_ROI_NAME,CV_WINDOW_AUTOSIZE);
	imshow(WINDOW_ROI_NAME,dstROI);

	//【9】用行rows和列cols截取原图指定区域的图像
	//【10】指定的src图像的区域包括图像的所有行和从第0列到第199列
	//【11】Mat Mat::operator()( Range _rowRange, Range _colRange ) const----为src对象的子数组创建
	        //新的信息头,底下的相当于src.colRange()
	Mat dstRange=src(Range::all(),Range(0,200));
	namedWindow(WINDOW_RANGE_NAME,CV_WINDOW_AUTOSIZE);
	imshow(WINDOW_RANGE_NAME,dstRange);

	/**
	*【1】现在你也许会问,如果矩阵属于多个Mat对象,那么当不在需要它时,谁来负责清理呢?答案是:最后一个
	*     使用它的对象。通过引用计数机制来实现.无论什么时候,有人拷贝一个Mat对象的信息头,都会增加矩阵的
	*     引用次数;反之,当一个头被释放后,这个计数减一;当计数为零时,矩阵会被清理。
	*【2】在这块我们通过Mat src这样的方法创建的类对象都是----类的静态对象,在程序运行的过程中,这样的对象
	*     占用的空间的分配和释放的时间点是固定的
	***/

	/**
	*【1】但是,某些时候,你仍会想拷贝矩阵本身(不只是信息头和矩阵指针),这时你可以用---深复制---函数:
	*    Mat clone() const;
	*	 void copyTo( OutputArray m ) const;
	*	 void copyTo( OutputArray m, InputArray mask ) const;
	**/
	Mat dstDeep1=src.clone();
	Mat dstDeep2;
	src.copyTo(src);
    //【12】经过深复制后,这时,我们再改变,就再也不会影响原图像了

	//【13】比如说,我们现在将图像中的所有元素都置为白色
	//【14】存取彩色图像的像素
	for(int i=0;i<dstDeep1.rows;i++)
	{
		for(int j=0;j<dstDeep1.cols;j++)
		{
			dstDeep1.at<Vec3b>(i,j)[0]=255;//蓝色通道
			dstDeep1.at<Vec3b>(i,j)[1]=255;//红色通道
			dstDeep1.at<Vec3b>(i,j)[2]=255;//绿色通道
		}
	}

	imshow("【原始图像】",src);
	imshow("【经过深复制处理过后的图像】",dstDeep1);

	waitKey(0);
}

现在,总结一下,我们需要记住的是:
	1--OpenCv函数中输出图像的内存分配是自动完成的(如果不是特别指定的话)
	2--使用OpenCv的C++接口时,不需要考虑内存释放问题
	3--复制运算符和拷贝构造函数只拷贝---信息头
	4--使用函数clone()或者copyTo()来拷贝一副图像的矩阵





时间: 2024-10-29 00:53:46

OpenCv学习笔记(四)--Mat基本图像容器Mat对象信息头,矩阵体的创建,深复制,浅复制详解的相关文章

JSP学习笔记四:JSP语法之内置对象

JSP有9个内置对象,分别是request(请求对象).response(响应对象). pageContext(页面上下文对象).session(会话对象).application(应用程序对象).out(输出对象).config(配置对象).page(页面对象)和exception(例外对象). 那么,这些对象是怎么来的呢?我们看一下转译文件. public void _jspService(final javax.servlet.http.HttpServletRequest request

opencv学习笔记(四)投影

opencv学习笔记(四)投影 任选了一张图片用于测试,图片如下所示: 1 #include <cv.h> 2 #include <highgui.h> 3 using namespace std; 4 using namespace cv; 5 int main() 6 { 7 IplImage * src = cvLoadImage("cat.png", 0); //强制转化读取图像为灰度图 8 cvShowImage("灰度图像", s

opencv学习笔记(03)——遍历图像(迭代器法)

1 #include <opencv2\highgui\highgui.hpp> 2 #include <opencv2\imgproc\imgproc.hpp> 3 #include <opencv2\core\core.hpp> 4 5 void colorReduce(cv::Mat& img, int div=64); 6 7 8 int main() 9 { 10 cv::Mat img_orginal = cv::imread("F:\\i

opencv学习笔记(02)——遍历图像(指针法)

#include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <iostream> void colorReduce(cv::Mat& image, int div=64) { int nr = image.rows; int nc = image.cols * image.

opencv学习笔记(01)——操作图像的像素

1 #include <opencv2\core\core.hpp> 2 #include <opencv2\highgui\highgui.hpp> 3 #include <opencv2\imgproc\imgproc.hpp> 4 #include <iostream> 5 6 7 void salt(cv::Mat& image, int n) 8 { 9 10 for(int k=0; k<n; k++) 11 { 12 13 int

OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 2013-03-23 17:44 16963人阅读 评论(28) 收藏 举报 分类: 机器视觉(34) 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] KAZE系列笔记: OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 OpenCV学习笔记(28)KA

Opencv学习笔记(六)SURF学习笔记

原创文章,转载请注明出处:http://blog.csdn.net/crzy_sparrow/article/details/7392345 本人挺菜的,肯定有非常多错误纰漏之处 ,希望大家不吝指正. 看了harris角点检測之后,開始研究SURF角点检測,发现挺复杂的,一时也仅仅了解了大概,把了解的东西总结下,以便下次深入学习. SURF角点检測算法是对SIFT的一种改进,主要体如今速度上,效率更高.它和SIFT的主要差别是图像多尺度空间的构建方法不同. 在计算视觉领域,尺度空间被象征性的表述

OpenCV学习笔记(01)我的第一个OpenCV程序(环境配置)

昨天刚刚考完编译原理,私心想着可以做一些与考试无关的东西了.一直想做和图像处理相关的东西,趁这段时间有空学习一下OpenCV,搭建环境真是一件麻烦的事情,搞了近三个小时终于OK了.先来张图: 大致描述一下步骤吧: 一.安装前准备 1.VS2012(网上看到很多用的VS2010,但是基本不影响) 2.OpenCV 安装包(我下载的是最新的2.4.9) 二.安装OpenCV 1.解压OPenCV 说是安装,其实就是解压,OpenCV的Windows安装程序就是一个自解压程序: 这里我解压到C:\Pr

OpenCV学习笔记[3]Java Demo人脸识别

OpenCV学习笔记:Java Demo人脸识别 [简介] 我记得在很久以前,CSDN似乎搞过一个活动,给一个橘子林的照片,让程序计算相片里有多少个橘子.之所以对这个问题记忆犹新,是因为在专业学习初期,相比于排序遍历搜索等简单算法而言,"图像识别"算法一直是难以理解的东西,而我偏偏又痴迷于此,不管自己多么无知,对于令我迷惑的问题总是充满着解决的渴望. 通过对OpenCV的初步了解,我发现图像识别的很多问题都可以用它方便的解决,本次将是一个来自官方的人脸识别的实例,我们提供图像,使用内置