OpenCV第二章 数据载入、显示与保存
第二章 数据载入、显示与保存
2.1 图像存储容器
2.1.1 Mat类介绍
Mat分为矩阵头和指向存储数据的矩阵指针两部分。
代码清单2-1 创建Mat类
cv::Mat a; //创建一个名为a的矩阵头
a = cv::imread("test.jpd"); //向a中赋值图像数据,矩阵指针指向像素数据
cv::Mat b = a; //复制矩阵头,并命名为b
代码清单2-2 声明一个指定类型的Mat类
cv::Mat A = Mat_<double>(3,3); //创建一个3*3的矩阵用于存放double类型数据
代码清单2-3 通过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); //创建单通道矩阵,c1标识可以省略
2.1.2 Mat类构造与赋值
1.Mat类的构造
代码清单2-4 默认构造函数使用方式
cv::Mat::Mat();
代码清单2-5 利用矩阵尺寸和类型参数构造Mat类
cv::Mat::Mat(int rows,
int cols,
int type
)
- rows:构造矩阵的行数
- cols:矩阵的列数
- type:矩阵中存储的数据类型
代码清单2-6 用Size()结构构造Mat
cv::Mat(Size size(),
int type
)
- size:二维数组变量尺寸,通过Size(cols,rows)进行赋值
- type:与代码清单2-5中的参数一致
代码清单2-7 用Size()结构构造Mat示例
cv::Mat a(Size(480,640),CV_8UC1); //构造一个行为640、列为480的单通道矩阵
cv::Mat b(Size(480,640),CV_32FC3); //构造一个行为640、列为480的3通道矩阵
代码清单2-8 利用已有矩阵构造Mat类
cv::Mat::Mat(const Mat & m);
- m:是已经构建完成的Mat类矩阵数据
提示:如果希望复制两个一模一样的Mat类而彼此之间不会受影响,那么可以使用m=a.clone()实现
代码清单2-9 构造已有Mat类的子类
cv::Mat::Mat(const Mat & m,
const Range & rowRange,
const Range & rowRange = Rang::all()
)
- m:是已经构建完成的Mat类矩阵数据
- rowRange:在已有矩阵中需要截取的行数范围,是一个Rang变量,例如从第2行到第5行可以表示位Rang(2,5)
- rowRange:在已有矩阵中需要截取的列数范围,是一个Rang变量,例如从第2列到第5列可以表示位Rang(2,5),默认所有列都会截取。
代码清单2-10 在原Mat中截取子Mat类
cv::Mat b(a, Rang(2,5), Rang(2,5)); //从a中截取部分数据构造b
cv::Mat c(a, Rang(2,5)); //默认最后一个参数构成c
2.3 视频加载与摄像头调用
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
system("color F0"); //更改输出界面颜色
VideoCapture video("cup.mp4");
if (video.isOpened())
{
cout << "视频中图像的宽度=" << video.get(CAP_PROP_FRAME_WIDTH) << endl;
cout << "视频中图像的高度=" << video.get(CAP_PROP_FRAME_HEIGHT) << endl;
cout << "视频帧率=" << video.get(CAP_PROP_FPS) << endl;
cout << "视频的总帧数=" << video.get(CAP_PROP_FRAME_COUNT);
}
else
{
cout << "请确认视频文件名称是否正确" << endl;
return -1;
}
while (1)
{
Mat frame;
video >> frame;
if (frame.empty())
{
break;
}
imshow("video", frame);
waitKey(1000 / video.get(CAP_PROP_FPS));
}
waitKey();
return 0;
}
2.4 数据保存
2.4.1 图像的保存
#include <iostream>
#include <opencv2\opencv.hpp>
using namespace std;
using namespace cv;
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); // 绿色通道
bgra[2] = saturate_cast<uchar>((float(mat.rows - i)) / ((float)mat.rows) * UCHAR_MAX); // 红色通道
bgra[3] = saturate_cast<uchar>(0.5 * (bgra[1] + bgra[2])); // Alpha通道
}
}
}
int main(int agrc, char** agrv)
{
// Create mat with alpha channel
Mat mat(480, 640, CV_8UC4);
AlphaMat(mat);
vector<int> compression_params;
compression_params.push_back(IMWRITE_PNG_COMPRESSION); //PNG格式图像压缩标志
compression_params.push_back(9); //设置最高压缩质量
bool result = imwrite("alpha.png", mat, compression_params);
if (!result)
{
cout << "保存成PNG格式图像失败" << endl;
return -1;
}
cout << "保存成功" << endl;
return 0;
}
2.4.2 视频的保存
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img;
VideoCapture video(0); //使用某个摄像头
//读取视频
//VideoCapture video;
//video.open("cup.mp4");
if (!video.isOpened()) // 判断是否调用成功
{
cout << "打开摄像头失败,请确实摄像头是否安装成功";
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'); // 选择编码格式
//OpenCV 4.0版本设置编码格式
//int codec = CV_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) //按ESC案件退出视频保存
{
break;
}
}
// 退出程序时刻自动关闭视频流
//video.release();
//writer.release();
return 0;
}
2.4.3 保存和读取XML和YMAL文件
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
system("color F0"); //修改运行程序背景和文字颜色
//string fileName = "datas.xml"; //文件的名称
string fileName = "datas.yaml"; //文件的名称
//以写入的模式打开文件
cv::FileStorage fwrite(fileName, cv::FileStorage::WRITE);
//存入矩阵Mat类型的数据
Mat mat = Mat::eye(3, 3, CV_8U);
fwrite.write("mat", mat); //使用write()函数写入数据
//存入浮点型数据,节点名称为x
float x = 100;
fwrite << "x" << x;
//存入字符串型数据,节点名称为str
String str = "Learn OpenCV 4";
fwrite << "str" << str;
//存入数组,节点名称为number_array
fwrite << "number_array" << "[" <<4<<5<<6<< "]";
//存入多node节点数据,主名称为multi_nodes
fwrite << "multi_nodes" << "{" << "month" << 8 << "day" << 28 << "year"
<< 2019 << "time" << "[" << 0 << 1 << 2 << 3 << "]" << "}";
//关闭文件
fwrite.release();
//以读取的模式打开文件
cv::FileStorage fread(fileName, cv::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;
//读取含多个数据的number_array节点
FileNode fileNode = fread["number_array"];
cout << "number_array=[";
//循环遍历每个数据
for (FileNodeIterator i = fileNode.begin(); i != fileNode.end(); i++)
{
float a;
*i >> a;
cout << a<<" ";
}
cout << "]" << endl;
//读取Mat类型数据
Mat matRead;
fread["mat="] >> matRead;
cout << "mat=" << mat << endl;
//读取含有多个子节点的节点数据,不使用FileNode和迭代器进行读取
FileNode fileNode1 = fread["multi_nodes"];
int month = (int)fileNode1["month"];
int day = (int)fileNode1["day"];
int year = (int)fileNode1["year"];
cout << "multi_nodes:" << endl
<< " month=" << month << " day=" << day << " year=" << year;
cout << " time=[";
for (int i = 0; i < 4; i++)
{
int a = (int)fileNode1["time"][i];
cout << a << " ";
}
cout << "]" << endl;
//关闭文件
fread.release();
return 0;
}