Opencv learning--C++--data loading, display and storage

1. Image storage container

Digital images are stored in the form of matrix in the computer.
In c++, strings are stored in the program as strings, and integers are stored as ints. Similarly, OpenCV provides a Mat class to store matrix data.

2. Introduction to the Mat class

OpenCV introduces the C++ interface, provides the Mat class to store data, and uses automatic memory management technology to solve the problem of automatic release of memory (it seems to use shared_ptr? Brothers who understand are welcome to correct and exchange), when the variable is not When needed again, the memory is released immediately.

The Mat class is divided into two parts: the matrix head and the matrix pointer pointing to the stored data. The matrix header contains the matrix size, storage method, address and reference count, etc. The size of the matrix head is a constant and will not change with the size of the matrix (similar to the pointer?)

In most cases, the size of the matrix header is far smaller than the size of the data in the matrix, so the main overhead in the process of image copying and transmission is to store the matrix data.

2.1 Create Mat class

In order to solve the problem of storage, when copying and transferring images in OpenCV, only the matrix header and the pointer to the stored data are copied, so when creating the Mat class, you can create the matrix header first and then assign the data:

//创建Mat类
cv::Mat a;                                    //创建一个名为a的矩阵头
a = cv::imread("test.jpg");          //向a中赋值图像数据,矩阵指针指向像素数据
cv::Mat b = a;							   //复制矩阵头,并命名为b(我个人理解为多引用了一次,触发了矩阵头中的shared_ptr引用次数的增加)

The code first creates a matrix header named a, then reads an image and points the matrix pointer in a to the pixel data of the image, and finally copies the contents of the matrix header a to the matrix header b. Modify the image data through any matrix head, and the data pointed to by the other matrix head will also change accordingly. But when the a variable is deleted, the b variable will not point to an empty data, and the matrix data will be released only when both variables are deleted.

2.2 Declare a Mat class of a specified type

The data that the Mat class can store are double, float, uchar, unsigned char, and custom templates.
We can declare a Mat class variable that stores a specified type through the following code:

//声明一个指定类型的Mat类
cv::Mat A = Mat_<double>(3,3)//创建一个3*3的矩阵用于存放double数据

2.3 Create Mat class through OpenCV data type

After we understand the data types that can be defined, we also need to define the number of channels (channels) of image data. For example, grayscale images are single-channel data, and color images are 3-channel or 4-channel data. OpenCV also defines the number of channels, C1, C2, C3, C4 represent single channel, dual channel, 3 channel and 4 channel respectively. For example, CV_8U represents an 8-bit unsigned integer with a value range of 0-255, CV_8UC3 represents a three-channel color image, and CV_8UC1 represents a single-channel grayscale image. code show as below:

//通过OpenCV数据类型创建Mat类
cv::Mat a (640,480,CV_8UC3)  // 创建一个640*480的3通道矩阵用于存放彩色图像
cv::Mat a (3, 3, CV_8UC1)       // 创建一个3*3的8位无符号整数的单通道矩阵
cv::Mat a (3, 3, CV_8U)           // 创建一个3*3的单通道矩阵,c1标识符可以省略

3. Data storage

3.1 imwrite save image

OpenCV provides the imwrite() function to save the Mat class matrix as an image file. The prototype code of this function is:

bool cv::imwrite(
	const String& filename,                                                 图像保存到本地的文件名
	InputArray img,															imwrite接收的mat矩阵变量,即我们需要保存的mat类矩阵
	Const std::vector<int> & params = std::vector<int>()                    保存图像的格式以及相关的参数
)
imwrite函数的返回值是bool类型,当保存成功时返回true, 保存失败时返回false

Among them, the setting method of the third parameter of the imwrite function is as follows:

imwrite()函数中第三个参数的设置方式:
vector<int> compression_params;                                                               
compression_params.push_back(IMWRITE_PNG_COMPRESSION);      保存为PNG格式的图像,图像质量0 - 9,值越大表示图像的尺寸越小,压缩时间越长,默认值为1
compression_params.push_back(9);             

The following is a simple code to demonstrate the specific implementation of the imwrite function:

#include <iostream>
#include <opencv2\opencv.hpp>
#include <vector>
#include <string>

using namespace std;
using namespace cv;

//bool cv::imwrite(
//	const String& filename,                                                                         图像保存到本地的文件名
//	InputArray img,																					  imwrite接收的mat矩阵变量,即我们需要保存的mat类矩阵
//	Const std::vector<int> & params = std::vector<int>()                          保存图像的格式以及相关的参数
//)
//imwrite函数的返回值是bool类型,当保存成功时返回true, 保存失败时返回false。

//imwrite()函数中第三个参数的设置方式:
//vector<int> compression_params;                                                               
//compression_params.push_back(IMWRITE_PNG_COMPRESSION);      保存为PNG格式的图像,图像质量0 - 9,值越大表示图像的尺寸越小,压缩时间越长,默认值为1
//compression_params.push_back(9);                                                            设置的压缩参数为9

void AlphaMat(Mat & mat)
{
    
    
	CV_Assert(mat.channels() == 4);
	for (int i = 0; i < mat.rows; i++)
	{
    
    
		for (int j = 0; j < mat.cols; j++)
		{
    
    
			Vec4b& bgra = mat.at<Vec4b>(i, j);
			bgra[0] = UCHAR_MAX;																										//蓝色通道
			bgra[1] = saturate_cast<uchar>((float(mat.cols - j)) / ((float)mat.cols) * UCHAR_MAX);   //绿色通道  saturate_cast函数是为了防止对像素操作时的颜色溢出
			bgra[2] = saturate_cast<uchar>((float(mat.cols - i)) / ((float)mat.rows) * UCHAR_MAX); //红色通道
			bgra[3] = saturate_cast<uchar>(0.5 * (bgra[1] + bgra[2]));													//alpha通道
		}
	}
}

int main()
{
    
    
	//create mat with alpha channel
	Mat mat(480, 640, CV_8UC4);   //使用Mat类生成一个mat对象,矩阵的尺寸为480 * 640, 并且是四通道的矩阵形式
	AlphaMat(mat);                            //将实例化的mat作为变量传入自定义的 AlphaMat方法中

	vector<int> compression_params;//设置imwrite函数中的第三个参数,这里我们将图片保存为PNG格式,图像压缩质量为9,如果想设置成jpg或者其他格式,可以在官网中检索此API
	compression_params.push_back(IMWRITE_PNG_COMPRESSION);
	compression_params.push_back(9);

	bool result = imwrite("alpha.png", mat, compression_params); //接收imwrite函数的返回值作为下面输出的判断依据
	if (!result)
	{
    
    
		cout << "保存PNG图像失败" << endl;
		return -1;
	}
	else
	{
    
    
		cout << "保存PNG图像成功" << endl;
		return 0;
	}
}

When we run this code, we get this result:
insert image description here

3.2 The VideoWriter class reads camera data and saves it as a video, or reads a local video and saves it

In our work and study, sometimes we need to generate a video from multiple images or directly save the data read by the camera as a video file. OpenCV provides the VideoWriter() class to realize the function of saving multiple images into video files. The constructor of this class is as follows:

VideoWriter的构造函数
cv::VideoWriter::VideoWriter();										//默认构造函数,后续通过open()函数来设置保存文件的名称、编解码器、帧数等一系列参数。
cv::VideoWriter::VideoWriter(                                      //直接构造,在实例化视频流的同时,直接设置参数。
	const String & filename,												//保存的文件名
	int fourcc,																	//设置编解码器,参数设置可以在官网查询
	double fps,																	//设置保存视频的帧率
	Size framesize,															//设置保存的视频的尺寸
	bool isColor = true														//保存的视频是否为彩色视频
)

Use a piece of code to see how to call the camera and save the video data recorded by the camera:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

//VideoWriter的构造函数
//cv::VideoWriter::VideoWriter();										//默认构造函数,后续通过open()函数来设置保存文件的名称、编解码器、帧数等一系列参数。
//cv::VideoWriter::VideoWriter(                                      //直接构造,在实例化视频流的同时,直接设置参数。
//	const String & filename,												//保存的文件名
//	int fourcc,																	//设置编解码器,参数设置可以在官网查询
//	double fps,																	//设置保存视频的帧率
//	Size framesize,															//设置保存的视频的尺寸
//	bool isColor = true														//保存的视频是否为彩色视频
//)

int main()
{
    
    
	Mat img;

	VideoCapture video(0);												//使用某个摄像头
	
	//VideoCapture video;													//读取某一个视频文件
	//video.open("xxx.mp4");

	if (!video.isOpened())
	{
    
    
		cout << "打开摄像头失败,请确认摄像头是否安装成功" << endl;
		return -1;
	}

	video >> img;																//获取图像

	//判断是否成功获取图像
	if (img.empty())															//判断读取图形是否成功
	{
    
    
		cout << "没有获取到图像" << endl;
		return -1;
	}
	bool isColor = (img.type() == CV_8UC3);					//判断相机(视频)类型是否为彩色

	VideoWriter writer;														//实例化保存视频的视频流对象,这一点与之前的保存图像数据不同
	int codec = VideoWriter::fourcc('M', 'J', 'P', 'G');		//选择编码格式

	double fps = 25.0;                                                       //设置视频帧率
	string filename = "live.avi";										//保存的视频文件名称
	writer.open(filename, codec, fps, img.size(), isColor);  //创建保存视频文件的视频流

	if (!writer.isOpened())												//判断视频流是否创建成功
	{
    
    
		cout << "打开视频文件失败,请确认是否为合法输入" << endl;
		return -1;
	}

	while (1)
	{
    
    
		//检查是否执行完成
		if (!video.read(img))                                                //判断能否继续从摄像头或者视频文件中读取下一帧图像
		{
    
    
			cout << "摄像头断开连接或者视频读取完成" << endl;
			break;
		}

		writer.write(img);													//把图像写入视频流
		//writer << img;

		imshow("Live", img);												//显示图像
		char c = waitKey(50);
		if (c == 27)
		{
    
    
			break;
		}
	}
	video.release();															//退出程序时关闭视频流
	writer.release();
	return 0;


}

After running, we can see that we have called the camera of the computer, and the data read by the camera has been continuously stored at this time:
insert image description here
if we want to quit reading the video, we can press the esc key twice to quit reading. At this time, when we open the path where our project is located, we can find the video file we just read!
insert image description here

3.3 Save and read xml files and yaml files

3.3.1 xml file and yaml file

Before learning how to operate xml files and yaml files in OpenCV, let's first understand what xml files and yaml files are.
When saving data, in addition to larger data files such as image data, we usually also save some smaller Mat-like matrices, strings, arrays and other data, which are usually saved as xml files or yaml files.
xml file is a meta markup language. The so-called meta tag language means that users can customize their own tags according to their own needs:

<age>24</age> 来表示age的数值为24

<color>       来表示color数据中含有两个名为red和blue的数据,两者的数值分别是100和150
	<red>100</red>
	<blue>150</blue>
</color>

Through the way of marking, no matter what form the data is saved in, as long as the file meets the XML format, there will be no ambiguity and confusion in the retrieved data. XML files have the extension ".xml".

3.3.2 Reading and writing of xml and yaml data

OpenCV4 provides the FileStorage class for generating and reading XML files and YAML files, which defines methods for initializing classes, writing data, and reading data. When we use the FileStorage class, we need to initialize it first. Initialization can be understood as declaring the files and operation types that need to be operated. OpenCV4 provides two initialization methods, which are initialization without entering any parameters, that is, instantiate an object first, and then use the open method in FileStorage to initialize. Or we can use his constructor to initialize:

FileStorage构造函数原型:
cv::FileStorage::FileStorage(
	const String  & filename,                          //打开的文件名称
	int flags,													//对文件进行的操作类型标志
	const String  & encoding = String()			//编码格式
)

This function is the constructor of FileStorage, which is used to declare the name of the opened file and the type of operation.
After opening the file, you can use the isOpened() function in the FileStorage class to judge whether the file is successfully opened. Returns true if the file was opened successfully, false if the file was not successfully opened.

After opening the file, similar to creating a data stream in c++, data can be written into the file through the << operator, and data can be read from the file through the >> operator. In addition, we can also use the write() function in the FileStorage class to write data into the file, the code is as follows:

void cv::FileStorage::write(
	const String & name,   //写入文件中的变量名称
	int val				   //变量的值
)

When using an operator to write data to a file, you also need to declare the variable name and variable value. For example, the variable name is "age" and the variable value is "24", which can be achieved by "file<<"age"<<24".

file<<"age"<<24;
file<<"age"<<"["<<24<<25<<"]";
file<<"age"<<"{"<<"xiaoming"<<24<<"wanghua"<<25<<"}";  //表示从属关系

//读取变量
最常见的是:
file["x"] >> xRead;  读取变量名为x的变量值

如果某个变量中含有多个数据或者含有子变量时,需要通过FileNode节点类型和迭代器FileNodeIterator进行读取:
例如,写入一个数组数据到file中:
file<<"age"<<"["<<24<<25<<"]";
读取file中的age变量名的第2个变量值:
FileNode fn = file["age"];
cout << fn[1];

例如,写入一个含有子变量的变量进入file中:
file<<"age"<<"{"<<"xiaoming"<<24<<"wanghua"<<25<<"}";  //表示从属关系
读取file中的age变量名下的xiaoming子变量的变量值:
FileNode fn = file["age"];
cout<< fn["xiaoming"];

More specifically, we demonstrate how to read and write xml and yaml files through a complete code. In this program, the write function and the operator << are used to write data to the file, and the iterator and [] (address) are used to read data from the file:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <string>

using namespace std;
using namespace cv;

//FileStorage构造函数原型:
//cv::FileStorage::FileStorage(
//	const String  & filename,                          //打开的文件名称
//	int flags,													//对文件进行的操作类型标志
//	const String  & encoding = String()			//编码格式
//)


int main()
{
    
    
	system(" ");						//修改运行程序背景和文字颜色
	
	string filename = "datas.yaml";	//自定义文件的名称
	//string filename = "datas.xml";

	FileStorage fwrite(filename, FileStorage::WRITE);    //以写入的模式打开文件

	Mat mat = Mat::eye(3, 3, CV_8U);  //创建一个单通道的mat矩阵来存放数据
	fwrite.write("mat", mat);                 //使用write函数来写入数据

	float x = 100;                                 //存入浮点型数据,节点名称为x
	fwrite << "x" << x;						  //写入

	String str = "Learn opencv 4";		  //存入字符串型数据,节点名称为str
	fwrite << "str" << str;

	fwrite << "number_array" << "[" << 4 << 5 << 6 << "]";		//存入数组,节点名称为number_array

	//存入多node节点,主节点名称为multi_nodes
	fwrite << "multi_nodes" << "{" << "month" << 8 << "day" << 28 << "year" << 2019 << "time" << "[" << 0 << 1 << 2 << 3 << "]" << "}";   

	fwrite.release();                            //关闭文件
	//==========================================================================================
	//以读取的模式打开文件
	FileStorage fread(filename, FileStorage::READ);

	if (!fread.isOpened())					  //判断是否打开文件
	{
    
    
		cout << "文件打开失败,请确认文件名称是否正确" << endl;
		return -1;
	}

	float xRead;
	fread["x"] >> xRead;					 //读取浮点型数据
	cout << "x=" << xRead << endl;

	string strRead;								 //读取字符串数据
	fread["str"] >> strRead;
	cout << "str=" << strRead << endl;

	FileNode filenode = fread["number_array"];  //读取含多个数据的number_array节点
	cout << "number_array=" << "[";

	for (FileNodeIterator i = filenode.begin(); i != filenode.end(); i++) //循环遍历每个数据
	{
    
    
		float a;
		*i >> a;
		cout << a << " ";
	}
	cout << "]" << endl;

	Mat matRead;							 //读取mat类型数据
	fread["mat="] >> matRead;     //这里读取的时候记得带上=号
	cout << "mat=" << mat << endl;


	FileNode filenode_1 = fread["multi_nodes"];
	int month = (int)filenode_1["month"];
	int day = (int)filenode_1["day"];
	int year = (int)filenode_1["year"];
	cout << "multi_nodes=" << endl;
	cout << "  month=" << month << "  day=" << day << "   year=" << year;
	cout << "    time=[";
	for (int i = 0; i < 4; i++)
	{
    
    
		int a = (int)filenode_1["time"][i];
		cout << a << " ";
	}
	cout << "]" << endl;

	fread.release();
	return 0;

}

The following is the result after the code runs:
insert image description here
and the yaml file generated by the code (here I open it with pycharm):
insert image description here
So far, the data saving in OpenCV has come to a paragraph, my level is limited, if there is an error in the text, I hope to see Officials can forgive my ignorance and lack of knowledge, and comment to help correct me, thank you very much~

Guess you like

Origin blog.csdn.net/ycx_ccc/article/details/127900218