opencv C艹:读取视频文件,保存图像,视频文件,读取保存XML YAML文件
《opencv4快速入门》
认识认识模块
D:\opencv\build\include\opencv2 路径下
- calib3d 主要包含相机标定,立体视觉的功能:物体姿势估计,三维重建,摄像头标定
- core,包含库的基本结构和操作,比如数据结构,绘图函数,数组操作等函数
- dnn,深度学习模块,包含构建网络,加载序列化的模型,但是不支持训练,只能推理
- features2d,处理图像特征点,特征检测,描述匹配之类的
- flann,这个模块是高维的近似近邻快速搜索算法库 主要包含快速近似近邻搜索与聚类等。
- gapi,加速图像处理的框架,没有特定的算法
- highgui,创建操作显示图像的窗口,处理鼠标事件和键盘命令,提供交互式可视化的界面
- imgcodecs 图片的保存和读取
- imgproc,图像处理模型,滤波,几何变换,直方图,特征检测目标检测等。
- ml,机器学习模块,统计分类,回归,聚类等
- objdetect,目标检测慕课,Haar特征等
- photo,摄影模型用来图片修复,去噪
- stitching,图像拼接,包含特征点的寻找和匹配,估计选择,自动校准,接缝等(啊这)
- video ,视频分析模型,用于运动估计,背景分离,对象跟踪等
- videoio ,视频输入,输出模块。
Mat
Mat类用于存储矩阵数据,可以自动管理内存,解决内存释放的问题。用来保存矩阵类型的数据,包括向量,矩阵,图像数据等。分为矩阵头和指向数据的指针两部分,矩阵头包括矩阵的尺寸,存储方法,地址,引用次数等。大小是个常数,opencv中复制和传递图像,就只是复制了矩阵头和指向数据的地址。 创建Mat时可以先创建矩阵头再赋值。
using namespace cv;
Mat a; // 名为a的矩阵头
a = imread("3.jpg"); // 向a中赋值图像数据,矩阵指针指向像素数据
Mat b=a; // 复制矩阵头,命名b,虽然有各自的矩阵头,但数据指针指向的时一个数据Mat c = Mat_<double>(3,3) // 创建3*3 的矩阵存放double类型的数据。
// 同样的有 flaot,uchar,char 和模板,CV_64F
通过其中一个矩阵头改变数据,另一个矩阵头指向的数据同样变化。
但是当a 变量删除时,b不会指向一个i空数据,只有两个变量都没了才会删除数据。
这就是矩阵头的引用次数标记了引用矩阵数据的次数, 只有值为0才会释放数据。
为了避免在不同环境下因变量位数长度不同而造成程序执行问题,OpenCV 根据数值变量存储位数长度定义了数据类型,后加一个C表示通道
Mat a(640,640,CV_8UC3); // 创建一个640*640 8位三通道的矩阵
Mat a(3,3,CV_8U); // 单通道,C1可以省略
Mat的构造方法:
Mat ()Mat (int rows, int cols, int type) Mat (Size size, int type) size:二维数组尺寸 Size(cols,rows),注意行列顺序Mat a(Size(480,640), CV_8UC1); 行640,列480Mat (int rows, int cols, int type, const Scalar &s) 构造的同时赋值(相同的值)Mat a(2,2,CV_8UC3, Scalar(0,0,255)); 创建三通道矩阵,通道值位0,0,255。Mat (Size size, int type, const Scalar &s)Mat (int ndims, const int *sizes, int type)Mat (const std::vector< int > &sizes, int type)Mat (int ndims, const int *sizes, int type, const Scalar &s)Mat (const std::vector< int > &sizes, int type, const Scalar &s)Mat (const Mat &m) 利用已有矩阵构建,只是复制了矩阵头,这辆指向同一数据,m=a.clone() 复制数据Mat (int rows, int cols, int type, void *data, size_t step=AUTO_STEP)Mat (Size size, int type, void *data, size_t step=AUTO_STEP)Mat (int ndims, const int *sizes, int type, void *data, const size_t *steps=0)Mat (const std::vector< int > &sizes, int type, void *data, const size_t *steps=0)Mat (const Mat &m, const Range &rowRange, const Range &colRange=Range::all())再已有矩阵中截取一个范围,rowRange是一个Range变量 Range(2,5) 截取2至5行,colRange 截取列,默认全截这个截取的矩阵仍然与原Mat 共享数据,改一个另一个也会改变Mat (const Mat &m, const Rect &roi)Mat (const Mat &m, const Range *ranges)Mat (const Mat &m, const std::vector< Range > &ranges)Mat a = (Mat_<int>(3, 3) << 1 , 2 , 3 , 4 , 5, 6 , 7 , 8 , 9) 枚举赋值,用数据流赋值
Mat c=Mat_<int>(3,3); // 循环赋值
for (int i =0;i<c.rows;i++)
{for(int j=0; j<c.cols;j++){c.at<int>(i,j)= i+j; // 类型一样}
}
Mat a = Mat::eye(3,3, CV_8UC1); // 单位矩阵
Mat b = (Mat_<int>(1,3)<<1,2,3);
Mat c = Mat::diag(b); // 对角矩阵,参数是Mat类型的一维变量,用来存放堆笑元素的值
Mat d = Mat::ones(3,3, CV_8UC1); // 全为1
Mat e = Mat::zeros(3,3, CV_8UC3); // 全为0利用数组进行赋值
float a[] = {1,2,3,4,5,1,2,3};
Mat b = Mat(2,2,CV_32FC2,a); // 拆分方式根据矩阵的尺寸和通道数,数组不够填充别的值。多了就省略Mat类的运算,看作普通的矩阵就行
e = a+b 矩阵加减法,两个矩阵类型相同
f = 2*a
h = d/2. 与常数的运算,保留矩阵的类型
g = a-1 逐元素减1 j = a*b 矩阵的乘法
double k = a.dot(b) 矩阵的内积,一个行向量和列向量的点乘
m = a.mul(b) 逐元素对应相乘
元素的读取
比如之前的 at方法对有每一位的读取,多通道的Mat矩阵存储的形式类似于三维数组,先存储第一个元素的每个通道的数据,然后第二个元素每个通道的数据,每一行就如此存储。找到每个元素的起始位置,就可以找到这个元素中每个通道的数据
- at<type>,at方法读取单通道矩阵元素。多通道就返回一个元素多个数据,使用Vec3b, Vec3s, Vec3d, Vec3f, Vec3i ,Vec3w,6种类型用于表示同意元素的3个通道数据。b uchar, s short , w ushort ,d double ,f float ,i int ,3表示三个通道,Vec2b表示二通道uchar类型
Mat a = (Mat_<uchar>(3,3) << 1,2,3,4,5,6,7,8,9);
int Value = (int) a.at<uchar>(0,0); Mat b(3,4,CV_8UC3,Scalar(0,0,1);
Vec3b vc3 = b.at<Vec3b>(0,0); // 与矩阵类型对应
int first = (int)vc3.val[0];
int second = (int)vc3.val[1];
int third = (int)vc3.val[2];
- ptr指针读取元素
矩阵中每一行的元素都是挨着存放,找到每一行元素的起始位置,读取不同位置的元素只要移动指针就可。
Mat b(3,4,CV_8UC3,Scalar(0,0,1);
for (int i=0; i<b.rows;i++) // 每一行
{uchar* ptr = b.ptr<uchar>(i); // 定义指针,声明指向哪一行for (int j =0;j<b.clos*b.channels();j++) // 每一行存储的数据数量为列数与通道的乘积。{cout<<(int)ptr[j]<<endl;}
}
// 读取第2行数据中第三个数据,a.ptr<uchar>(1)[2];
- 通过迭代器访问元素
Mat类也是一个容器,也有迭代器。
MatIterator_<uchar> it = a.begin<uchar>(); // 迭代器遍历类型
MatIterator_<uchar> it_end = a.end<uchar>();
for (int i=0; it != it_end; it++)
{cout<<(int)(*it)<< " "; // 解引用,输出每一个元素的每个通道if((++i% a.cols) == 0){cout<<endl;}
}
- 通过矩阵元素地址定位的方式读取元素
(int)(*(b.data+b.step[0] * row + b.step[1] * col + channel));
row 某个数据的行数,col列数,channel某个数据所在元素的通道。就是第row行,第col列,第channel通道的数据。也是通过首地址移动若干位读取数据。
图片的读取和显示
Mat cv::imread ( const String & filename,
int flags = IMREAD_COLOR
)
读取图片,返回一个Mat,读取失败返回空矩阵,可以使用Mat.empty() 判断。Win中默认使用自带的编码器 libjpeg,libpng等来读取图像文件,linux就需要自己下载,能否读取文件看文件的内容而不是后缀,.jpg 改成.exe 一样可以读取,相反就不行。
flags参数设置读取样式,原样读取,灰度,彩图,多位数,缩小等。功能不冲突的前提下可以同时声明多个,用 | 隔开
void cv::namedWindow ( const String & winname,
int flags = WINDOW_AUTOSIZE
)
显示图像时没有主动定义窗口,会自动生成。需要添加滑动条这类的需要手动创建。创建一个窗口变量,用于显示图像和滑动条。创建窗口时已经存在同名的,就不会再执行任何操作,创建的窗口使用 cv::destroyWindow() and cv::destroyAllWindows() 来释放资源,一个指定名字,一个释放全部。但实际上程序的退出会自动关闭应用程序的所有资源,可以不用主动释放。
flags声明窗口属性,设置是否可调大小,显示图像是否填充+
void cv::imshow ( const String & winname,
InputArray mat
)
再指定窗口中显示图像,第二个参数是InputArray,是一个类型声明引用,用于输出参数的标记,就当作Mat就行。
VideoCapture ()
VideoCapture (const String &filename, int apiPreference=CAP_ANY)
apiPreference 读取视频时设置的属性,编码格式,是否使用OpenNI等。
可以读取处理视频流,也可以是图片序列或者视频的URL,图片序列将多个图像的名字统一为 "前缀+数字“ 的形式,比如 img_%02d.jpg ,可以读取文件夹下 img_00.jpg, img_01.jpg…就可以自动搜索合适的标志。 isOpened() 函数进行判断是否读取成。
通过VideoCapture 将视频文件加载到了这个类变量里,使用视频中的图片使用,使用>> 将图片导入Mat中,当VideoCapture里所有图像赋值给了Mat,后再次赋值就变成空矩阵了。使用 empty() 判断是否读取完毕。
还提供了查看视频属性的get() 函数,通过输入指定的标记获取视频属性。
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;int main(int argc, char *argv[])
{system("color F0"); // 摄像头就 video(0)VideoCapture video("D:\\opencv\\sources\\samples\\data\\Megamind.avi");if (video.isOpened()){cout << video.get(3) << video.get(4) << video.get(5) << video.get(7);}while (1){Mat frame;video >> frame;if (frame.empty()){break;}imshow("img", frame);waitKey(1000 / video.get(CAP_PROP_FPS));}waitKey();return 0;
}
数据保存
bool cv::imwrite ( const String & filename,InputArray img,const std::vector< int > & params = std::vector< int >()
)
将Mat保存为图片,
- 16 位无符号 CV 16U 图像可以保存成 PNG JPEG TIFF 式文件
- 32 位浮点 CV_32F 图像可以保存成 PFM TIFF OpenEXR Radiance HDR 格式文化
- 4 通道 (Alpha 通道 图像可以保存成 PNG 式文件.
函数第三个参数在一般情况下不需要填写 ,保存成指定的文件格式只需要直接在第一个参数
后面更改文件后缀,但是当需要保存的 Mat 类矩阵中数据比较特殊 〈如 16 位深度数据 )就需要
要设置第 3个参数.
// 保存图像
#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; // maximum unsigned char value 蓝色通道,255/*saturate_cast 是为了防止颜色溢出原理大致如下if(data<0) data=0; elseif(data>255) data=255;*/bgra[1] = saturate_cast<uchar>((float(mat.cols - j)) / ((float)mat.cols)); //绿色bgra[2] = saturate_cast<uchar>((float(mat.rows - j)) / ((float)mat.rows)); //redbgra[3] = saturate_cast<uchar>(0.5 * (bgra[1] + bgra[2])); //alpha 通道}}
}int main(int argc, char *argv[])
{system("color E");Mat mat(480, 640, CV_8UC4); // 创建个四通道的matAlphaMat(mat);vector<int> compression_params;compression_params.push_back(IMWRITE_PNG_COMPRESSION); // imwrite 第三个参数设置的方式compression_params.push_back(9); bool result = imwrite("alpha.png", mat, compression_params);if (!result){cout << "error";return -1;}cout << "okk";return 0;
}
视频的保存
cv::VideoWriter::VideoWriter ( const String & filename,int fourcc,double fps,Size frameSize,bool isColor = true
)
fourcc视频编码器,-1 自动搜索合适的。fps保存的视频帧率,可以实现二倍速,慢速等。frameSize保存视频的尺寸,但是要与图像的尺寸相同。。。isColor 是否保存彩色视频。保存视频就使用 << or write() 。isOpened() 判断是否创建成功,release() 关闭视频流。
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;int main(int argc, char *argv[])
{Mat img;VideoCapture video(0);if (!video.isOpened()){cout << "error";return -1;}video >> img; // 读取视频if (img.empty()){cout << "error";return -1;}bool isColor = (img.type() == CV_8UC3); // 判断相机类型是否为彩色VideoWriter writer;int codec = VideoWriter::fourcc('M', 'J', 'P', 'G'); // 选择编码格式double fps = 25.;string filename = "a.avi";writer.open(filename, codec, fps, img.size(), isColor); // 创建保存视频文件的视频流if (!writer.isOpened()) // 视频流是否创建成功{cout << "error";return -1;}while (1){if (!video.read(img)) // 摄像头断开,或视频读取完成,执行完毕{cout << "error";break;}writer.write(img); // 写入视频流// writer<<img;imshow("img", img);char c = waitKey(50); // ESC退出视频程序if (c == 27){break;}}video.release(); // 程序退出自动释放资源,可以不用writer.release();return 0;
}
保存读取XML 和YMAL文件
程序中尺寸较小的Mat类矩阵,字符串,数组等数据也需要保存,这种数据通常保存为XML or YAML文件。
YAML文件通过”变量:数值“的方式表示每个数据,通过缩进表示数据之间的结构和隶属关系。扩展就是 .ymal .yml
cv::FileStorage::FileStorage ( const String & filename,
int flags,
const String & encoding = String()
)
flags对文件进行的操作类型标志,encoding,编码格式 UTF-8 XML编码。声明打开的文件名和操作的类型。但是文件的操作需要已经存在的文件。
默认的FileSotrage的构造函数没有参数,一般需要open() 函数单独声明,传入参数。isOpened() 判断是否打开文件,使用 << 写入数据(file<<“age”<<24;)(write() 变量名,变量值),表示某个变量是个数组使用”[] " 来表示,比如“ file<<“age”<<"["<<21<<22<<"]" “。隶属关系使用{} 表示,例如” file<<“age”<<”{"<<“qbx”<<20<<“lh”<<22<<"}" ".
>> 读取数据。通过变量名读取变量值 file[“x”] >> xRead ,读取名为x的变量。当某个变量有多个数据或自变量时,需要通过FileNode 节点类型和迭代器 FileNodeIterator 读取。
virtual bool cv::FileStorage::open ( const String & filename,int flags,const String & encoding = String()
) void cv::FileStorage::write ( const String & name,
int val // 经过函数重载,可以是很多其他格式
) #include <iostream>
#include <opencv2/opencv.hpp>
#include <string>
using namespace std;
using namespace cv;int main(int argc, char *argv[])
{system("color E");string filename = "data.xml";// string filename = "data.yaml";FileStorage fwrite(filename, FileStorage::WRITE); // 写入模式,需要加前缀的Mat mat = Mat::eye(3, 3, CV_8U);fwrite.write("mat", mat); // 写入数据。使用write方法float x = 100;fwrite << "x" << x; // 跟python的字典一样,使用流方法String str = "hi, hhh"; fwrite << "str" << str;fwrite << "number_array" << "[" << 4 << 5 << 6 << "]"; // 表示一个数组 [] fwrite << "multi_nodes" << "{" << "month" << 8 << "day" << 28 << "year" //{} 表示隶属关系<< 2020 << "time" << "[" << 0 << 1 << 2 << 3 << "]" << "}";fwrite.release(); // 关闭文件FileStorage fread(filename, FileStorage::READ); // 读取文件数据if (!fread.isOpened()){cout << "error";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"]; // 含有多个数据或自变量的,通过节点类型和迭代器进行读取cout << "number_array = [";for (FileNodeIterator i = fileNode.begin(); i != fileNode.end(); i++){float a;*i >> a;cout << a << " ";}cout << "]" << endl;Mat matRead;fread["mat"] >> matRead;cout << "mat = " << mat << endl;FileNode fileNodel = fread["multi_nodes"];int mouth = (int)fileNodel["mouth"]; // 从节点中读取自变量int day = (int)fileNodel["day"];int year = (int)fileNodel["year"];cout << "multi_nodes:" << endl<< " mouth=" << mouth << " day=" << day << " year=" << year;cout << " time = [";for (int i = 0; i < 4; i++){int a = (int)fileNodel["time"][i]; // 另一种方法不用迭代器,就一级一级的找cout << a << " ";}cout << "]" << endl;fread.release();return 0;
}
小结
opencv C艹:读取视频文件,保存图像,视频文件,读取保存XML YAML文件相关推荐
- XML/YAML文件的输入输出
本文主要阐述XML/YAML文件在OpenCV3中如何实现输入输出操作. <1>: XML/YAML文件,顾名思义指的是文件的格式,即以.XML为后缀的文件和以.YAML为后缀的文 ...
- 【web测试】不同格式的文件操作(txt,csv,excel,json,xml,yaml)
[web测试]数据驱动 python操作文件 txt文件 csv格式 excel文件 json格式 xml文件 yaml格式 python操作文件 txt文件 # 读取r 写入w;rb-二进制读取;w ...
- python写xml文件 数据量特别大_python处理xml大文件[xml.sax]
博客已迁移, 新地址 ===================== 今天遇到大文件处理,使用dom占用资源太多,改用sax处理 dom和sax区别可以自己google下 需求:读取xml数据文件,文件较 ...
- OpenCV XML和YAML文件的文件输入和输出
OpenCV XML和YAML文件的文件输入和输出 XML和YAML文件的文件输入和输出 目标 源代码 解释 XML / YAML文件打开和关闭. 文本和数字的输入和输出. OpenCV数据结构的输入 ...
- OpenCV之core 模块. 核心功能(2)基本绘图 随机数发生器绘制文字 离散傅立叶变换 输入输出XML和YAML文件 与 OpenCV 1 同时使用
基本绘图 目的 本节你将学到: 如何用 Point 在图像中定义 2D 点 如何以及为何使用 Scalar 用OpenCV的函数 line 绘 直线 用OpenCV的函数 ellipse 绘 椭圆 用 ...
- OpenCV中XML文件和YAML文件的读写
OpenCV中XML文件和YAML文件的读写 代码如下: #include <opencv2/core/core.hpp> #include <iostream> #inclu ...
- 【C++】42.使用YAML文件进行参数配置、读取与生成YAML文件
关于配置文件的使用,除了本文的yaml的方法,经常使用的还有proto的方法,请参考我另一篇博客:proto相关用法:方法2.. c++中如果要使用YAML,需要安装yaml-cpp库,安装完成后使用 ...
- 图像视频压缩编码概述
图像视频压缩编码概述 图像视频的特点 图像压缩 图像编码方法 统计编码的基本理论 霍夫曼编码 香农编码 算术编码 游程编码 变换编码 预测编码 图像视频的特点 首先,图像视频信号十分直观确切,其可以较 ...
- Li‘s 影像组学视频学习笔记(16)-pyradiomics里的yaml文件
本笔记来源于B站Up主: 有Li 的影像组学系列教学视频 本节(16)主要介绍: pyradiomics的yaml文件参数设置解读 pyradiomics 官网对yaml文件有详细说明: The th ...
最新文章
- python做直方图-python实现直方图的应用
- 学习笔记Hadoop(八)—— Hadoop集群的安装与部署(5)—— Hadoop配置参数介绍、Hadoop集群启动与监控
- python高阶函数
- 如何插入8bit量化节点(tensorflow)
- MSDN Visual系列:在WSSv3中编程方式激活单个文档库的审核功能
- C语言变量和数据类型
- 7月份Github上最热门的Java开源项目
- 1.如何判断正交表对错
- matlab 平方根法解方程组,matlab改进平方根法
- win2008 有几个版本?区别是什么?
- 手工定制眼镜将风靡中国(lyy bros)
- 计算机必须设置默认打印机,电脑系统怎么默认打印机 默认打印机的设置教程...
- php 生成300dpi图片,canvas生成图片只有96dpi,打印需要300dpi, 请问如何修改这个信息....
- ubuntu安装qtcreator后没有桌面图标
- 城市易涝点(隧道、涵洞)监测系统方案
- qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
- 全国第一所蚂蚁链大学落地江西 2020区块链创新应用高峰论坛圆满落幕
- 转载:知其所以然(以算法学习为例) 作者: 刘未鹏
- 揭秘跨越普通权限的神秘地带
- 最佳Linux笔记本电脑编程
热门文章
- Linux哪个命令显示文件内容,显示文件内容的Linux命令有哪些?Linux培训
- 【腾讯云】Ubuntu16.04下搭建Java开发环境一站式服务(WinSCP、PuTTY、JDK、MySQL、Tomcat)
- 读《终身成长》知 我命由我不由天 馁
- 动态规划初识(从dfs到dfs优化到动态规划顺推和逆推)
- Oracle中where后面加if,SQL:WHERE子句中的IF子句
- 大话C#的进阶业务场景必知点解析 第8节 算法上道C#根据时间起卦像程序
- 80后:管理还是激励
- 朱萧木首谈独立创业,与罗老师总会相逢
- Selenium应用系列2
- 南京林业大学计算机专升本,专转本之南京林业大学