C++下OpenCV学习笔记
----输入和输出XML和YAML
一.XML和YAML简介
- XML:Extensible Markup Language,可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。
- YAML:YAML Ain’t a Markup Language 的递归缩写,是一个可读性高,用来表达数据序列化的格式。原意是"Yet Another Markup Language",但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。
二.FileStorage类
- 构造函数
FileStorage(const string& source, int flags, const string& encoding=string());
第一个参数:表示存储或读取数据的文件名
第二个参数:表示操作模式
FileStorage::READ 打开文件进行读操作
FileStorage::WRITE 打开文件进行写操作
FileStorage::APPEND 打开文件进行附加操作
FileStorage::MEMORY 从source读数据,或向内部缓存写入数据(由FileStorage::release返回)
第三个参数:表示文件的编码方式。目前不支持UTF-16 XML 编码,应使用 8-bit 编码
FileStorage fs("xxx.xml", FileStorage::WRITE);
FileStorage( );
FileStorage fs;
fs.open("xxx.xml", FileStorage::WRITE);
- 写入数据:operator <<
template<typename_Tp> FileStorage& operator<<(FileStorage& fs, const _Tp& value);
第一个参数:表示需要写入数据的文件
第二个参数:表示待写入的数据
template<typename_Tp> FileStorage& operator<<(FileStorage& fs, const vector<_Tp>& vec);
第一个参数:表示需要写入数据的文件
第二个参数:表示待写入的向量值
- 文本和数字的输入
fs << "year" << 2021;
fs << "hello" << "world";
- OpenCV数据结构的输入
Mat R = Mat_<uchar>::eye(3, 3);
fs << "R" << R;
- 读取数据:operator >>
- 文本和数字的读取
int Year = (int)fs["year"];
cout << "year: " << Year << endl;
std::string str;
fs["hello"] >> str;
cout << "hello: " << str << endl;
- OpenCV数据结构的读取
Mat R;
fs["R"] >> R;
cout << "R = " << R << endl;
- vector数据结构的输入输出
- vector数据,xml / yaml文件节点,不包含子节点
- 在输入vector数据的开始和结尾要分别输入“[”,“]”
- 输入vector数据前要先输入标签名称
FileStorage fs("test.yml", FileStorage::WRITE);
fs << "src" << "{" << "src1" << "[" << 1 << 2 << 3 << "]"
<< "src2" << "[" << 4 << 5 << 6 << "]" << "}";
fs.release();
- map数据结构的输入输出
- map数据,xml / yaml文件节点,包含子节点
- 在输入map数据的开始和结尾要分别写入“{”,“}”
- 输入 map数据前要先输入标签名称
代码同上
创建一个yaml父节点(map),包含两个子节点(vector)
- FileStorage::open
打开文件.
boolFileStorage::open(const string& filename, int flags, const string&encoding=string());
第一个参数:表示存储或读取数据的文件名。其扩展名(.xml 或 .yml/.yaml) 决定文件格式(XML 或 YAML)
第二个参数:表示操作模式
第三个参数:表示文件的编码方式
- FileStorage::isOpened
检查文件是否已经打开.
boolFileStorage::isOpened();
返回true:打开
返回false:其他情况
FileStorage fs1("xxx.yml", FileStorage::READ);
FileStorage fs2("test.yml", FileStorage::READ);
bool flag1 = fs1.isOpened();
bool flag2 = fs2.isOpened();
cout << "flag1 = " << flag1 << endl;
cout << "flag2 = " << flag2 << endl;
- FileStorage::release
存储或读取操作完成后,需要关闭文件并释放缓存.
void FileStorage::release();
FileStorage fs("xxx.yml", FileStorage::WRITE);
...
fs.release();
- FileStorage::getFirstTopLevelNode
返回映射(mapping)顶层的第一个元素.
FileStorage::getFirstTopLevelNode();
FileStorage fs2("test.yml", FileStorage::READ);
string FirstNode = fs2.getFirstTopLevelNode();
cout << "The first top level node is " << FirstNode << endl;
- FileStorage::root
返回顶层映射(mapping).
FileNode FileStorage::root(int streamidx = 0);
第一个参数:表示从0开始的字符串索引。大部分情况文件中只有一个串,但YAML支持多个串,因此可以有多个。
写入文件中
FileStorage fs("test.yml", FileStorage::WRITE);
Mat src = (Mat_<double>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
fs << "src1" << src;
fs << "src2" << src;
fs.release();
读取文件中
FileStorage fs2("test.yml", FileStorage::READ);
FileNode rootnode = fs2.root();
FileNodeIterator it;
for (it = rootnode.begin();it < rootnode.end();it++)
{
Mat dst;
(*it) >> dst;
cout << dst << endl;
}
- 读数据:FileStorage::operator[]
返回指定的顶层映射元素.
FileNode FileStorage::operator[](const string& nodename) const;
FileNode FileStorage::operator[](const char* nodename) const;
第一个参数:表示文件的节点名
Mat R;
fs["R"] >> R;
cout << "R = " << R << endl;
三.写入文件
以XML为例,若想输出YAML文件(.yaml或.yml)甚至txt和doc,更改后缀名即可。
#define _CRT_SECURE_NO_WARNINGS
#include<opencv2/opencv.hpp>
#include<time.h>
using namespace cv;
int main()
{
FileStorage fs("test.yml", FileStorage::WRITE);
fs << "frameCount" << 5;
time_t rawtime;
time(&rawtime);
fs << "calibrationDate" << asctime(localtime(&rawtime));
Mat cameraMatrix = (Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1);
Mat distCoeffs = (Mat_<double>(5, 1) << 0.1, 0.01, -0.001, 0, 0);
fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;
fs << "features" << "[";
for (int i = 0; i < 3; i++)
{
int x = rand() % 640;
int y = rand() % 480;
uchar lbp = rand() % 256;
fs << "{:" << "x" << x << "y" << y << "lbp" << "[:";
for (int j = 0; j < 8; j++)
fs << ((lbp >> j) & 1);
fs << "]" << "}";
}
fs << "]";
fs.release();
printf("文件读写完毕\n");
getchar();
return 0;
}
- 若出现:
则在所有include文件前,添加:
#define _CRT_SECURE_NO_WARNINGS
- 运行结果:
四.读取文件
将写入的text.yml文件复制粘贴到工程目录下:
#define _CRT_SECURE_NO_WARNINGS
#include<opencv2/opencv.hpp>
#include<time.h>
using namespace cv;
using namespace std;
int main()
{
system("color 6F");
FileStorage fs2("test.yml", FileStorage::READ);
int frameCount = (int)fs2["frameCount"];
std::string date;
fs2["calibrationDate"] >> date;
Mat cameraMatrix2, distCoeffs2;
fs2["cameraMatrix"] >> cameraMatrix2;
fs2["distCoeffs"] >> distCoeffs2;
cout << "frameCount: " << frameCount << endl
<< "calibration date: " << date << endl
<< "camera matrix: " << cameraMatrix2 << endl
<< "distortion coeffs" << distCoeffs2 << endl;
FileNode features = fs2["features"];
FileNodeIterator it = features.begin(), it_end = features.end();
int idx = 0;
std::vector<uchar> lbpval;
for (;it != it_end;++it, idx++)
{
cout << "feature #" << idx << ": ";
cout << "x=" << (int)(*it)["x"] << ", y=" << (int)(*it)["y"] << ", lbp: (";
(*it)["lbp"] >> lbpval;
for (int i = 0;i < (int)lbpval.size();i++)
cout << " " << (int)lbpval[i];
cout << ")" << endl;
}
fs2.release();
printf("\n文件读取完毕!\n");
getchar();
return 0;
}
- 运行结果:
五.FileNode数据结构
- 文件存储节点类,对于进行读操作的文件节点用于存储每个文件元素。
- 当读取XML或YMAL文件,节点是第一个被解析并作为一个节点结合存储到存储器中。每个节点都可以作为一个包含一个数字或一个字符或其他节点的“叶子”。每个节点都有一个名字并可以通过节点的名字对节点进行访问,这些节点就组成了一个集合,就算节点没有名字也可以通过元素的索引对节点结合进行排序。
- 节点只用用来对文件的读取提供引导,而文件进行写操作后将没有数据存储在内存中。
- 构造函数
FileNode::FileNode();
FileNode::FileNode(const CvFileStorage* fs, const CvFileNode* node);
FileNode::FileNode(const FileNode& node);
- 类型:seq和map
写入
(1)seq
在子节点间,写入一对方括号[];无法为子节点命名。
(2)map
在子节点间,写入一对花括号{};可以为子节点命名。
OpenCV 最重要的 Mat 类型在存储时是以 map 方式写入的。
FileStorage fs("test.yml", FileStorage::WRITE);
fs << "seq_node" << "[";
for (int i = 0; i < 3; ++i)
fs << i;
fs << "]";
fs << "map_node" << "{";
for (int i = 0; i < 3; ++i)
fs << "node_" + std::to_string(i) << i;
fs << "}";
fs.release();
读取
(1)seq
以索引的方式去获得子节点,也可以用迭代器去访问子节点。
(2)map
用子节点的名字,即一个字符串去获得子节点(字符串为键,节点为值)。
FileStorage fs("test.yml", FileStorage::READ);
FileNode seq_node = fs["seq_node"];
for (int i = 0; i < 3; i++)
{
int num;
seq_node[i] >> num;
cout << num << endl;
}
FileNode map_node = fs["map_node"];
for (int i = 0; i < 3; i++)
{
int n;
map_node["node_" + to_string(i)] >> n;
cout << "node_" + to_string(i) << ": " << n << endl;
}
关于map型读取的分析:
---->父节点:map_code,内含三个子节点:node_0, node_1, node_2;
---->通过FileNode map_node = fs2["map_node"];
找到父节点map_node;再在父节点内寻找子节点map_node["node_" + to_string(i)]
,得到值
---->若用fs["node_" + to_string(i)]
是找不到值的,因为父节点没有node_0, node_1, node_2
六.FileNodeIterator数据结构
用于迭代访问序列(sequences)和映射表(mappings)。是一种典型的STL符号,通过node.begin()和node.end()来标识序列的开始和结束位置。
- 构造函数
FileNodeIterator::FileNodeIterator();
FileNodeIterator::FileNodeIterator(const CvFileStorage* fs, const CvFileNode* node, size_t ofs=0);
FileNodeIterator::FileNodeIterator(const FileNodeIterator& it);
- seq 型用迭代器去访问子节点
FileStorage fs("test.yml", FileStorage::READ);
FileNode seq_node = fs["seq_node"];
FileNodeIterator it = seq_node.begin();
for (; it != seq_node.end(); ++it) {
int i;
*it >> i;
cout << i << endl;
}