使用XML文件输入和输出和YAML文件
目标
你会发现以下问题的答案:
- 如何打印和阅读文本条目文件和OpenCV使用YAML或XML文件?
- 如何为OpenCV做同样的数据结构?
- 为你的数据结构如何做到这一点?
- 使用OpenCV的数据结构等FileStorage,FileNode或FileNodeIterator。
源代码
你可以下载 这 从 在这里或找到它样品/ cpp / tutorial_code / / file_input_output / file_input_output.cpp核心OpenCV的源代码库。
下面是一个示例代码如何实现所有目标的东西枚举列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
#include <opencv2/core/core.hpp>
#include <iostream>
#include <string>
using namespace cv;
using namespace std;
class MyData
{
public:
MyData() : A(0), X(0), id()
{}
explicit MyData(int) : A(97), X(CV_PI), id("mydata1234") // explicit to avoid implicit conversion
{}
void write(FileStorage& fs) const //Write serialization for this class
{
fs << "{" << "A" << A << "X" << X << "id" << id << "}";
}
void read(const FileNode& node) //Read serialization for this class
{
A = (int)node["A"];
X = (double)node["X"];
id = (string)node["id"];
}
public: // Data Members
int A;
double X;
string id;
};
//These write and read functions must be defined for the serialization in FileStorage to work
static void write(FileStorage& fs, const std::string&, const MyData& x)
{
x.write(fs);
}
static void read(const FileNode& node, MyData& x, const MyData& default_value = MyData()){
if(node.empty())
x = default_value;
else
x.read(node);
}
// This function will print our custom class to the console
static ostream& operator<<(ostream& out, const MyData& m)
{
out << "{ id = " << m.id << ", ";
out << "X = " << m.X << ", ";
out << "A = " << m.A << "}";
return out;
}
int main(int ac, char** av)
{
if (ac != 2)
{
help(av);
return 1;
}
string filename = av[1];
{ //write
Mat R = Mat_<uchar>::eye(3, 3),
T = Mat_<double>::zeros(3, 1);
MyData m(1);
FileStorage fs(filename, FileStorage::WRITE);
fs << "iterationNr" << 100;
fs << "strings" << "["; // text - string sequence
fs << "image1.jpg" << "Awesomeness" << "baboon.jpg";
fs << "]"; // close sequence
fs << "Mapping"; // text - mapping
fs << "{" << "One" << 1;
fs << "Two" << 2 << "}";
fs << "R" << R; // cv::Mat
fs << "T" << T;
fs << "MyData" << m; // your own data structures
fs.release(); // explicit close
cout << "Write Done." << endl;
}
{//read
cout << endl << "Reading: " << endl;
FileStorage fs;
fs.open(filename, FileStorage::READ);
int itNr;
//fs["iterationNr"] >> itNr;
itNr = (int) fs["iterationNr"];
cout << itNr;
if (!fs.isOpened())
{
cerr << "Failed to open " << filename << endl;
help(av);
return 1;
}
FileNode n = fs["strings"]; // Read string sequence - Get node
if (n.type() != FileNode::SEQ)
{
cerr << "strings is not a sequence! FAIL" << endl;
return 1;
}
FileNodeIterator it = n.begin(), it_end = n.end(); // Go through the node
for (; it != it_end; ++it)
cout << (string)*it << endl;
n = fs["Mapping"]; // Read mappings from a sequence
cout << "Two " << (int)(n["Two"]) << "; ";
cout << "One " << (int)(n["One"]) << endl << endl;
MyData m;
Mat R, T;
fs["R"] >> R; // Read cv::Mat
fs["T"] >> T;
fs["MyData"] >> m; // Read your own structure_
cout << endl
<< "R = " << R << endl;
cout << "T = " << T << endl << endl;
cout << "MyData = " << endl << m << endl << endl;
//Show default behavior for non existing nodes
cout << "Attempt to read NonExisting (should initialize the data structure with its default).";
fs["NonExisting"] >> m;
cout << endl << "NonExisting = " << endl << m << endl;
}
cout << endl
<< "Tip: Open up " << filename << " with a text editor to see the serialized data." << endl;
return 0;
}
|
解释
这里我们只讨论XML和YAML文件输入。 您的输出文件(及其各自的输入)可能只有其中一个扩展和来自这个结构。 他们是两种类型的数据结构可以序列化:映射STL(如地图)元素序列(如STL向量)。 之间的区别是,在地图上每个元素都有一个唯一的名称通过你可能访问它。 序列需要通过他们来查询一个特定的项目。
-
XML / YAML文件打开和关闭。之前你写任何内容这样的文件你需要打开它,最后关闭它。 中的XML数据结构/ YAML OpenCVFileStorage。 指定这个结构,文件绑定在你的硬盘你可以使用它的构造函数或open()的函数:
string filename = "I.xml"; FileStorage fs(filename, FileStorage::WRITE); //... fs.open(filename, FileStorage::READ);
任何一个使用第二个参数是一个常数的指定类型的操作你可以在他们:写,读或附加。 中指定的扩展文件名也将使用这种番茄的输出格式。 输出可能甚至压缩等如果您指定一个扩展.xml.gz。
文件时自动关闭FileStorage对象被摧毁。 然而,你可以通过显式地调用释放功能:
fs.release(); // explicit close
-
输入和输出的文本和数字。数据结构使用相同的< <输出操作符STL库。 输出任何类型的数据结构,我们需要指定它的名字。 我们这样做只是打印的名字。 基本类型你可以参照这个打印的值:
fs << "iterationNr" << 100;
阅读是一个简单的解决(通过[]操作符)和铸造操作或读通过> >操作符:
int itNr; fs["iterationNr"] >> itNr; itNr = (int) fs["iterationNr"];
-
OpenCV的输入/输出数据结构。这些行为完全就像c++的基本类型:
Mat R = Mat_<uchar >::eye (3, 3), T = Mat_<double>::zeros(3, 1); fs << "R" << R; // Write cv::Mat fs << "T" << T; fs["R"] >> R; // Read cv::Mat fs["T"] >> T;
-
向量的输入/输出(数组)和关联映射。正如我之前提到的,我们可以输出地图和序列(数组、向量)。 我们首先打印变量的名称,然后我们必须指定如果我们的输出是一个序列或地图。
序列的第一个元素之前打印“(”字符之后,最后一个“]”的角色:
fs << "strings" << "["; // text - string sequence fs << "image1.jpg" << "Awesomeness" << "baboon.jpg"; fs << "]"; // close sequence
地图的钻是一样的但是现在我们使用“{”和“}”分隔符字符:
fs << "Mapping"; // text - mapping fs << "{" << "One" << 1; fs << "Two" << 2 << "}";
从这些我们使用阅读FileNode和FileNodeIterator数据结构。 []的运营商FileStorage返回一个类FileNode数据类型。 如果我们可以使用的节点顺序FileNodeIterator遍历项目:
FileNode n = fs["strings"]; // Read string sequence - Get node if (n.type() != FileNode::SEQ) { cerr << "strings is not a sequence! FAIL" << endl; return 1; } FileNodeIterator it = n.begin(), it_end = n.end(); // Go through the node for (; it != it_end; ++it) cout << (string)*it << endl;
地图你可以再次使用[]操作符访问给定的项目(或> >操作符):
n = fs["Mapping"]; // Read mappings from a sequence cout << "Two " << (int)(n["Two"]) << "; "; cout << "One " << (int)(n["One"]) << endl << endl;
-
阅读和编写自己的数据结构。假设您有一个数据结构,如:
class MyData { public: MyData() : A(0), X(0), id() {} public: // Data Members int A; double X; string id; };
可以通过序列化XML / YAML OpenCV I / O接口(就像OpenCV数据结构的情况下)通过添加一个读和写函数类的内部和外部。 里面的部分:
void write(FileStorage& fs) const //Write serialization for this class { fs << "{" << "A" << A << "X" << X << "id" << id << "}"; } void read(const FileNode& node) //Read serialization for this class { A = (int)node["A"]; X = (double)node["X"]; id = (string)node["id"]; }
那么你需要添加以下功能定义在类:
void write(FileStorage& fs, const std::string&, const MyData& x) { x.write(fs); } void read(const FileNode& node, MyData& x, const MyData& default_value = MyData()) { if(node.empty()) x = default_value; else x.read(node); }
在这里你可以观察到在阅读部分我们定义如果用户试图读取一个不存在的节点。 在这种情况下我们只是返回默认的初始化值,但是一个更详细的解决方案是换取实例-一个值对象的ID。
一旦你添加这四个功能为编写和使用> >运算符< <操作符读:
MyData m(1); fs << "MyData" << m; // your own data structures fs["MyData"] >> m; // Read your own structure_
或者尝试读一个不存在的阅读:
fs["NonExisting"] >> m; // Do not add a fs << "NonExisting" << m command for this to work cout << endl << "NonExisting = " << endl << m << endl;
结果
主要是我们刚刚打印出定义的数字。 你的控制台屏幕上可以看到:
Write Done.
Reading:
100image1.jpg
Awesomeness
baboon.jpg
Two 2; One 1
R = [1, 0, 0;
0, 1, 0;
0, 0, 1]
T = [0; 0; 0]
MyData =
{ id = mydata1234, X = 3.14159, A = 97}
Attempt to read NonExisting (should initialize the data structure with its default).
NonExisting =
{ id = , X = 0, A = 0}
Tip: Open up output.xml with a text editor to see the serialized data.
然而,更有趣的是你可以看到输出xml文件中:
<?xml version="1.0"?>
<opencv_storage>
<iterationNr>100</iterationNr>
<strings>
image1.jpg Awesomeness baboon.jpg</strings>
<Mapping>
<One>1</One>
<Two>2</Two></Mapping>
<R type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>u</dt>
<data>
1 0 0 0 1 0 0 0 1</data></R>
<T type_id="opencv-matrix">
<rows>3</rows>
<cols>1</cols>
<dt>d</dt>
<data>
0. 0. 0.</data></T>
<MyData>
<A>97</A>
<X>3.1415926535897931e+000</X>
<id>mydata1234</id></MyData>
</opencv_storage>
或者是YAML文件:
%YAML:1.0
iterationNr: 100
strings:
- "image1.jpg"
- Awesomeness
- "baboon.jpg"
Mapping:
One: 1
Two: 2
R: !!opencv-matrix
rows: 3
cols: 3
dt: u
data: [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]
T: !!opencv-matrix
rows: 3
cols: 1
dt: d
data: [ 0., 0., 0. ]
MyData:
A: 97
X: 3.1415926535897931e+000
id: mydata1234