【OpenCV:从零到一】03:Mat对象的创建与使用

前言
这是我《OpenCV:从零到一》专栏的第三篇博客,想看跟多请戳
本文概要
void copyTo(Mat mat) Mat clone()
void convertTo(Mat dst, int type)
bool empty();
uchar* ptr(i=0)
create() Scalar();
zaros() ones()
案例代码
大概内容:各种方式创建Mat以及Mat的使用。

#include <opencv2/core/core.hpp> 
#include <opencv2/imgcodecs.hpp> 
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/highgui/highgui_c.h>
#include <iostream>

using namespace cv;
using std::cout;//引入cout和endl
using std::endl;

int main(int argc, char** args) {
	Mat image = imread("D:\\86186\\Documents\\opencv\\lena.jpg", IMREAD_GRAYSCALE);//读进来的时候变成灰色
	if (image.empty()) {//Mat.empty() Returns true if the array has no elements.
		//The method returns true if Mat::total() is 0 or if Mat::data is NULL.
		cout << "could not find the image resource..." << endl;
		return -1;
	}
	namedWindow("My Image", CV_WINDOW_AUTOSIZE);
	imshow("My Image", image);

	Mat M;
	M.create(4, 3, CV_8UC2);//创建一个四行三列的矩阵,一个通道8bit无符号双通道
	//CV_是前缀,8是bit数,U是类型(还有F,S上一篇有讲过)而C就是通道的意思,后面的数字自然就代表通道数了
	
	M = Scalar(127, 127);//default constructor 用于给通道赋值,从上面知道M是双通道的,scalar给每个通道都赋值为127
	cout << "M = " << endl << " " << M << endl;//这里输出可以看见是四行六列的,可以验证我们上一篇提到的模型
	uchar* firstRow = M.ptr<uchar>(0);//ptr重载多达20个,但是都是两到三个参数,都是行列之类,返回值是指针,具体使用具体分析
	printf("%d\n", *firstRow);

	//Mat构造函数
	Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	cout << "C = " << endl << " " << C << endl;
	//将 m 赋值给新创建的对象,此处不会对图像数据进行复制,m 和新对象共用图像数据(注意和后面copyTo和clone的区别)
	Mat D = Mat::Mat(C);//D复制C的头

	Mat mat1 = Mat::ones(1, 5, CV_32F);   // [1,1,1,1,1]
	Mat mat2 = mat1;   // mat2与mat1指向同一内存地址
	Mat mat3 = Mat::zeros(1, 5, CV_32F);  // [0,0,0,0,0]

	mat1 = mat3.clone();   // mat1被重新分配内存,通过mat1不能改变mat2的内容
	cout << mat1 << endl;   // [0,0,0,0,0]
	cout << mat2 << endl;   // [1,1,1,1,1]

	mat3.copyTo(mat1); // mat1未被重新分配内存,通过mat1可以改变mat2的内容
	cout << mat1 << endl;   // [0,0,0,0,0] 
	cout << mat2 << endl;   // [0,0,0,0,0]
	//mat可以直接用cout输出

	Mat dst;//destination 终点,目的地
	image.convertTo(dst, CV_8U);

	//zeros,创建全为0的矩阵,ones和zeros如出一辙,只不过ones为全为1的矩阵,zeros有三个重载
	Mat A = Mat::zeros(3, 3, CV_32F);//(rows,cols,type)
	int a[] = { 3,4 };//const int* sz
	Mat B = Mat::zeros(2, a, CV_32F);//(int ndims,const int* sz ,int type)
	Mat E = Mat::zeros(Size(100, 70), CV_32F);//(Size size,int type)
	waitKey(0);
	return 0;
}

解析及注意事项

  • Mat对象创建方式有create和Mat构造函数,注意两者区别
  • Mat对象的复制方式有clone、copyTo和Mat构造函数,注意三者区别(构造函数比较懒)
  • create被很多需要输出矩阵的OpenCV函数调用
  • 矩阵类型就是上一篇提到的深度类型加上个通道,这些类型本质上来说都是int,但是为了方便使用都进行了宏定义,把鼠标指向宏名可以看到原始值
  • 创建矩阵和复制矩阵无非就只用知道两件事:1,要用多少空间 2,是矩阵什么类型 各种重载都是几十几十的来,看似繁杂,其实就是围绕以上两个问题来。其中花样比较多的是第一个问题。
  • Scalar是一个可以为通道赋值的基础结构体,而Size是参数形式和他差不多,但是范围有很大区别,Size定义的是像素数量,图片的长宽,每个像素有一一个对应的Scalar
  • zeros和ones是两个很像的函数,也是用来创造Mat,但是他们每个像素的内容是确定的,即他们的Scalar是确定。
  • convertTo用于变换图像的深度,也就是灰度范围

全注释代码

#include <opencv2/core/core.hpp> 
#include <opencv2/imgcodecs.hpp> 
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/highgui/highgui_c.h>
#include <iostream>

using namespace cv;
using std::cout;//引入cout和endl
using std::endl;

int main(int argc, char** args) {
	Mat image = imread("D:\\86186\\Documents\\opencv\\lena.jpg", IMREAD_GRAYSCALE);//读进来的时候变成灰色
	if (image.empty()) {//Mat.empty() Returns true if the array has no elements.
		//The method returns true if Mat::total() is 0 or if Mat::data is NULL.
		cout << "could not find the image resource..." << endl;
		return -1;
	}
	namedWindow("My Image", CV_WINDOW_AUTOSIZE);
	imshow("My Image", image);

	Mat M;
	M.create(4, 3, CV_8UC2);//创建一个四行三列的矩阵,一个通道8bit无符号双通道
	//CV_是前缀,8是bit数,U是类型(还有F,S上一篇有讲过)而C就是通道的意思,后面的数字自然就代表通道数了
	/*
	Allocates new array data if needed.分配空间给数组
	Most new-style OpenCV functions and methods that produce arrays call this method for each output array.
	多数新式需要输出的函数内部会调用create.也就是说:
	Mat color;
	...
	//Mat gray(color.rows, color.cols, color.depth());
	Mat gray;//上面一样语句可以省略后面的堆参数,因为cvtColor会帮你把color和gray规格一样,即调用了create语句
	cvtColor(color, gray, COLOR_BGR2GRAY);
	参数
	int rows	New number of rows.
	int cols	New number of columns.
	int type	New matrix type.//为了记忆方便一般用宏代替而不是直接写数字
	create有三个重载,他们的描述都是This is an overloaded member function, provided for convenience. 
	It differs from the above function only in what argument(s) it accepts.也就是说他们只有参数不同
	三个重载type都是必须的而(rows + cols)可以替换为一下几组:
	Size 或 sizes 或 ndims  + sizes
	Size   size重载很多的类型,常见的是这样的 Size(100,70) 注意和下面的区别
	const int * sizes	Array of integers specifying a new array shape.
	int ndims New array dimensionality.
	*/
	M = Scalar(127, 127);//default constructor 用于给通道赋值,从上面知道M是双通道的,scalar给每个通道都赋值为127
	/*
	typedef struct Scalar
	{
	    double val[4];
	}Scalar;
	上面是scalar原型,最大长度是四,不填默认0
	*/
	cout << "M = " << endl << " " << M << endl;//这里输出可以看见是四行六列的,可以验证我们上一篇提到的模型
	uchar* firstRow = M.ptr<uchar>(0);//ptr重载多达20个,但是都是两到三个参数,都是行列之类,返回值是指针,具体使用具体分析
	printf("%d\n", *firstRow);

	//Mat构造函数的29个重载(爷吐了)
	/*
	其中create有的参数形式Mat都有,下面列举几种其他常见Mat构造函数
	Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
	创建行数为 rows,列数为 col,类型为 type 的图像,此构造函数不创建图像数据所需内存,而是直接使用 data 所指内存,图像的行步长由 step指定
	Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
	创建大小为 size,类型为 type 的图像,此构造函数不创建图像数据所需内存,而是直接使用 data 所指内存,图像的行步长由 step 指定
	Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange)
	创建的新图像为 m 的一部分,具体的范围由 rowRange 和 colRange 指定,此构造函数也不进行图像数据的复制操作,新图像与 m 共用图像数据
	Mat::Mat(const Mat& m, const Rect& roi)
	创建的新图像为 m 的一部分,具体的范围 roi 指定,此构造函数也不进行图像数据的复制操作,新图像与 m 共用图像数据
	*/
	Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	cout << "C = " << endl << " " << C << endl;
	//将 m 赋值给新创建的对象,此处不会对图像数据进行复制,m 和新对象共用图像数据(注意和后面copyTo和clone的区别)
	Mat D=Mat::Mat(C);//D复制C的头

	/*                                                copyTo和clone的区别
	Mat.copyTo(dst,[])
	The major difference is that when the destination matrix and the source matrix have the same type and size, 
	copyTo will not change the address of the destination matrix, while clone will always allocate a new address for the destination matrix.
	后面一个参数可以表示复制的部分,如src.copyTo( dst, detected_edges); 是将src中detected_edges矩阵对应的非零部分(即边缘检测结果)复制到dst中。
	Mat.clone();无参返回值为Mat类型
	Creates a full copy of the array and the underlying data.
	The method creates a full copy of the array. The original step[] is not taken into account. 
	So, the array copy is a continuous array occupying total()*elemSize() bytes.
	*/
	Mat mat1 = Mat::ones(1, 5, CV_32F);   // [1,1,1,1,1]
	Mat mat2 = mat1;   // mat2与mat1指向同一内存地址
	Mat mat3 = Mat::zeros(1, 5, CV_32F);  // [0,0,0,0,0]

	mat1 = mat3.clone();   // mat1被重新分配内存,通过mat1不能改变mat2的内容
	cout << mat1 << endl;   // [0,0,0,0,0]
	cout << mat2 << endl;   // [1,1,1,1,1]

	mat3.copyTo(mat1); // mat1未被重新分配内存,通过mat1可以改变mat2的内容
	cout << mat1 << endl;   // [0,0,0,0,0] 
	cout << mat2 << endl;   // [0,0,0,0,0]
	//mat可以直接用cout输出
	
	//convertTo
	/*
	convertTo(dst,rtype,scale,shift)用于图片灰度范围的缩放,后面两个参数都是可选的
	dst(i)=src(i)xscale+shift
	Converts an array to another data type with optional scaling.The method converts source pixel values to the target data type. 
	saturate_cast<> is applied at the end to avoid possible overflows
	这个比例因子不是放大缩小的因子,实际上范围的意思,比如你原来的图是255的灰度图,你现在转成float型数据,并且归一化到0-1,那么就是scale=1.0/255,如果还是想0-255.0 那么就直接1
	rtype:desired output matrix type or, rather, the depth since the number of channels are the same as the input has; 
	期望输出矩阵的类型,确切的来说是深度(depth()),因为输入输入通道数是一样的
	if rtype is negative, the output matrix will have the same type as the input.如果值为负则保持一致
	*/
	Mat dst;//destination 终点,目的地
	image.convertTo(dst, CV_8U);

	//zeros,创建全为0的矩阵,ones和zeros如出一辙,只不过ones为全为1的矩阵,zeros有三个重载
	Mat A = Mat::zeros(3, 3, CV_32F);//(rows,cols,type)
	int a[] = { 3,4};//const int* sz
	Mat B = Mat::zeros(2, a, CV_32F);//(int ndims,const int* sz ,int type)
	Mat B = Mat::zeros(Size(100, 70), CV_32F);//(Size size,int type)
	waitKey(0);
	return 0;
}

翻译笔记
destination n. 目的地,终点 =dst
dimensionality n. 维度;幅员;广延 =dim
convert vt. 使转变;转换 =cvt
scalar adj. 标量的;数量的;梯状的,分等级的
n. [数] 标量;[数] 数量
scale n. 规模;比例;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章