原文地址:
OpenCV中矩阵类详解之一:Mat
【OpenCV学习笔记】二、深入了解 cv::Mat
CV_32FC1和CV_64FC1的区别?
OpenCV里面Mat方法中的Scalar详解
1.创建一个240行 × 320列的新图像,CV_8UC1表示每个像素对应1字节,单通道,用字母U表示无符号,灰度值为100
cv::Mat image1(240, 320, CV_8UC1, 100);
CV_32FC1和CV_64FC1,前者是32位数据,后者是64位数据。因此前者类型的数据必须以指向32位数据类型的指针存取,否则会报错,而后者类型的数据必须以指向64位数据类型的指针存取,否则会报错。
也就是说,你如果用CV_32FC1,那么后面对该矩阵的输入输出的数据指针类型都应该是float,这在32位编译器上是32位浮点数,也就是单精度。
你如果用CV_64FC1,那么后面对该矩阵的输入输出的数据指针类型都应该是double,这在32位编译器上是64位浮点数,也就是双精度。
创建Mat矩阵
示例1:
//1.使用构造函数,常见的几个如下:
Mat::Mat(); //default
Mat::Mat(int rows, int cols, int type);
Mat::Mat(Size size, int type);
Mat::Mat(int rows, int cols, int type, const Scalar& s);
Mat::Mat(Size size, int type, const Scalar& s);
Mat::Mat(const Mat& m);
//参数说明:
//int rows:高
//int cols:宽
//int type:参见"Mat类型定义"
//Size size:矩阵尺寸,注意宽和高的顺序:Size(cols, rows)
//const Scalar& s:用于初始化矩阵元素的数值
//const Mat& m:拷贝m的矩阵头给新的Mat对象,但是不复制数据!相当于创建了m的一个引用对象
//例子1:创建100*90的矩阵,**矩阵元素为3通道32位浮点型**
cv::Mat M(100, 90, CV_32FC3);
//例子2:使用一维或多维数组来初始化矩阵,
double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}};
cv::Mat M = cv::Mat(3, 3, CV_64F, m);
//2.使用create函数:
Mat a = create(10, 9, CV_16U); //创建10*9的矩阵,矩阵元素为16位无符号整型
//create的一个特殊用法:如果初始化的时候没有传入size的参数,或者后面需要改变size的参数,可以使用create来调整
// make 7x7 complex matrix filled with 1+3j.
cv::Mat M(7,7,CV_32FC2,Scalar(1,3));
// and now turn M to 100x60 15-channel 8-bit matrix.
// The old content will be deallocated:隐式使用release()释放
M.create(100,60,CV_8UC(15));
示例2:
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
// 测试函数, 它创建一个图像
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;
}
示例3:
Mat M(7,7,CV_32FC2,Scalar(1,3));
解释如下:创建一个M矩阵,7行7列,类型为CV_32F,C2表示有2个通道。Scalar(1,3)是对矩阵进行初始化赋值。第一个通道全为1,第2个通道全为3。