【OpenCV学习笔记】二、深入了解 cv::Mat

最近在系统地学习OpenCV,将学习的过程在此做一个记录,主要以代码+注释的方式记录学习过程。

cv::Mat有两个必不可少的组成部分:一个头部和一个数据块。头部包含了矩阵的所有相关信息(大小、通道数量、数据类型等);数据块包含了图像中所有像素的值。头部有一个指向数据块的指针,即data属性。cv::Mat有一个很重要的属性,即只有在明确要求时,内存块才会被复制。实际上,大多数操作仅仅复制了cv::Mat的头部,因此多个对象会同时指向同一个数据块。 

下面的程序可用来测试cv::Mat数据结构的不同属性(代码+注释):

以下代码的要点是:对于cv::Mat,调用拷贝构造函数的初始化或者Mat之间进行赋值,这两种情况都是浅拷贝,即任何一个变动,其他的都会一起受影响。用copyTo方法clone方法进行Mat的复制是深拷贝。 

#include 
#include 
#include 
// 测试函数, 它创建一个图像
cv::Mat function() {
	// 创建图像
	cv::Mat ima(500, 500, CV_8U, 50);
	// 返回图像
	return ima;
} 
int main() 
{
	// 定义图像窗口
	cv::namedWindow("Image 1");
	cv::namedWindow("Image 2");
	cv::namedWindow("Image 3");
	cv::namedWindow("Image 4");
	cv::namedWindow("Image 5");
	cv::namedWindow("Image");
	/*我们需要指定每个矩阵元素的类型,这里我们用CV_8U表示每个像素
	对应1字节,用字母U表示无符号,你也可用字母S表示有符号。对于
	彩色图像,你应该用三通道类型(CV_8UC3),也可以定义16位和
	32位的整数(有符号或无符号),例如CV_16SC3。我们甚至可以使
	用32位和64位的浮点数(例如CV_32F)。*/
	// 创建一个240行 × 320列的新图像
	cv::Mat image1(240, 320, CV_8U, 100);
	cv::imshow("Image", image1); // 显示图像
	cv::waitKey(0); // 等待按键
	/*我们可以随时用create方法分配或重新分配图像的数据块,如果图
	像已经分配,首先其原来的内容会被释放。*/
	// 重新分配一个新的图像
	image1.create(200, 200, CV_8U);
	image1 = 200;
	cv::imshow("Image", image1); // 显示图像
	cv::waitKey(0); // 等待按键
	/*图像(或矩阵)的每个元素都可以包含多个值(例如彩色图像中的三
	个通道),因此OpenCV引入了一个简单的数据结构cv::Scalar,
	用于在调用函数时传递像素值。该结构通常包含一个或三个值。*/
	// 创建一个红色的图像
	// 通道次序为BGR
	cv::Mat image2(240, 320, CV_8UC3, cv::Scalar(0, 0, 255));
	// 或者:
	// cv::Mat image2(cv::Size(320,240),CV_8UC3);
	// image2= cv::Scalar(0,0,255);
	cv::imshow("Image", image2); // 显示图像
	cv::waitKey(0); // 等待按键
	// 读入一个图像
	cv::Mat image3 = cv::imread("puppy.bmp");
	/*一旦没有了指向cv::Mat对象的引用, 分配的内存就会被自动释
		放。 这一点非常方便, 因为它避免了C++动态内存分配中经常发生的
		内存泄漏问题。 这是OpenCV 2中一个关键的机制, 它的实现方法是
		通过cv::Mat实现计数引用和浅复制。 因此, 当在两个图像之间赋
		值时, 图像数据( 即像素) 并不会被复制, 此时两个图像都指向同一
		个内存块。 这同样适用于图像间的值传递或值返回。 由于维护了一个
		引用计数器, 因此只有当图像的所有引用都将释放或赋值给另一个图
		像时, 内存才会被释放;*/
	// 所有这些图像都指向同一个数据块。
	//下面的两个图像,对其中的任何一个做转换都会影响到其他图像。
	cv::Mat image4(image3);
	image1 = image3;
	/*如果要对图像内容做一个深复制, 你可以使用copyTo方法, 在此情况下
	目标图像会调用create方法。 另一个生成图像副本的方法是clone, 即创建
	一个完全相同的新图像*/
	// 这些图像是源图像的副本图像
	image3.copyTo(image2);
	cv::Mat image5 = image3.clone();
	// 转换图像用来测试
	cv::flip(image3, image3, 1);
	// 检查哪些图像在处理过程中受到了影响
	cv::imshow("Image 3", image3);
	cv::imshow("Image 1", image1);
	cv::imshow("Image 2", image2);
	cv::imshow("Image 4", image4);
	cv::imshow("Image 5", image5);
	cv::waitKey(0); // 等待按键
	// 从函数中获取一个灰度图像
	cv::Mat gray = function();
	cv::imshow("Image", gray); // 显示图像
	cv::waitKey(0); // 等待按键
	// 作为灰度图像读入
	image1 = cv::imread("puppy.bmp", CV_LOAD_IMAGE_GRAYSCALE);
	/*如果你需要把一个图像复制到另一个图像中, 而两者的数据类型不一
	定相同, 那就要使用convertTo方法.需要注意的是, 这两个图像的通道数量
	必须相同。*/
	image1.convertTo(image2, CV_32F, 1 / 255.0, 0.0);
	cv::imshow("Image", image2); // 显示图像
	cv::waitKey(0); // 等待按键
	return 0;
}

程序运行结果:从左往下,image1至image5。

【OpenCV学习笔记】二、深入了解 cv::Mat_第1张图片



你可能感兴趣的:(OpenCV学习笔记)