使用OpenCV,我们经常需要对xml文件进行操作。为此OpenCV为我们提供了FileStorage类来对XML/YAML文件进行操作。它使我们能够像操作普通文件一下来读写xml文件。
XML文件的打开和关闭
我们可以使用FileStorage的构造函数或者open()函数来对磁盘上的文件进行绑定。
string filename = "test.xml";
FileStorage fs( filename, FileStorage::WRITE );
fs.open( filename, FileStorage::READ );
文件的扩展名决定了采用的输出格式,第二个参数指定了要对文件进行操作的类型。
我们可以通过isOpened函数来判断是否正确打开文件。
if( !fs.isOpened() )
{
cerr << "filed to open!" << endl;
return 1;
}
我们可以显示的调用release函数来进行文件的关闭。也可以不调用,当FileStorage对象被销毁时,文件将自动被关闭。
fs.release();
基本数据类型的输入/输出
我们可以使用<<操作符来简单的将基本数据类型的值写入文件。
fs << "NUM" << 100;
我们还可以通过[]操作符和>>操作符来实现简单的读取。
int num;
num = (int) fs["NUM"];
cout << num << endl;
OpenCV数据结构的输入/输出
它的操作方法和基本数据类型的操作方法是相同的。
<pre name="code" class="cpp"> Mat R = Mat_<uchar>::eye( 3, 3 ),
T = Mat_<double>::zeros( 3, 1 );
fs << "R" << R;
fs<< "T" << T;
Mat R1, T1;
fs["R"] >> R1;
fs["T"] >> T1;
vectors和maps的输入/输出
我们也可以对vectors(序列)和maps进行输入/输出
对于序列,在第一个元素前输出“[”,在最后一个元素后输出"]":
// 序列
fs << "strings" << "[";
fs << "Hello" << "OpenCV" <<"!";
fs << "]";
对于序列的读取,我们使用FileNode和FileNodeIterator数据结构。FileStorage的[]操作符将返回一个FileNode数据类型。我们一般使用FileNodeIterator来迭代遍历序列化的值。
FileNode n = fs["strings"];
if( n.type() != FileNode::SEQ )
{
cerr << "strings is not a sequence!" << endl;
return 1;
}
FileNodeIterator it = n.begin(), it_end = n.end();
for(; it != it_end; ++ it )
cout << (string)*it << endl;
对于maps,输出采用同样的方法,但是采用“{”和"}"而不再是“[”和"]"。
// map
fs << "Mapping";
fs << "{" << "One" << 1;
fs << "Two" << 2 << "}";
对于maps的读取,我们使用FileNode数据结构和[]操作符来访问指定的元素。
n = fs["Mapping"];
if( n.type() != FileNode::MAP )
{
cerr << "strings is not a map!" << endl;
return 1;
}
cout << "Two " << (int)(n["Two"]) << endl;
cout << "One " << (int)(n["One"]) << endl;
读写自定义的数据结构
读写自定义的数据结构比较麻烦,我们为了使OpenCV的I/O接口能够像OpenCV内部结构一样对其进行序列化。我们需要重写read和write函数。
加入我们有如下的数据类型
class MyData
{
public:
MyData() : A(0), X(0), id() {}
public: // 数据成员
int A;
double X;
string id;
};
我们需要重写read和write函数,首先在函数内部定义如下:
void write( FileStorage& fs ) const
{
fs << "{" << "A" << A << "X" << X << "id" << id << "}";
}
void read( const FileNode& node )
{
A = (int)node["A"];
X = (double)node["X"];
id = (string)node["id"];
}
然后还需要在类的外部进行如下定义:
void write( FileStorage& fs, const 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 );
}
为了能够使用<<操作符进行对自定数据结构进行操作,还需要对<<操作符进行重载,如下所示:
ostream& operator<<(ostream& out, const MyData& m)
{
out << "{ A = " << m.A << ", ";
out << "X = " << m.X << ", ";
out << "id = " << m.id << "}";
return out;
}
之后,我们就可以对自定义数据类型进行输入/输出。
MyData m(1, 3.14, "xml file");
fs << "MyData" << m;
MyData m1, m2;
fs["MyData"] >> m1;
/* 文件中没有m2的节点 */
fs["m2"] >> m2;
cout << "MyData m1 = " << endl << m1 << endl ;
cout << "MyData m2 = " << endl << m2 << endl << endl;
完整示例
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <iostream>
#include <string>
using namespace std;
using namespace cv;
class MyData
{
public:
MyData():A(0), X(0), id(){}
MyData(int a, double b, string c)
{
A = a;
X = b;
id = c;
}
void write( FileStorage& fs ) const
{
fs << "{" << "A" << A << "X" << X << "id" << id << "}";
}
void read( const FileNode& node )
{
A = (int)node["A"];
X = (double)node["X"];
id = (string)node["id"];
}
public:
int A;
double X;
string id;
};
void write( FileStorage& fs, const 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 );
}
ostream& operator<<(ostream& out, const MyData& m)
{
out << "{ A = " << m.A << ", ";
out << "X = " << m.X << ", ";
out << "id = " << m.id << "}";
return out;
}
int main()
{
string filename = "test.xml";
Mat R = Mat_<uchar>::eye( 3, 3 ),
T = Mat_<double>::zeros( 3, 1 );
MyData m(1, 3.14, "xml file");
// write
FileStorage fs( filename, FileStorage::WRITE );
if( !fs.isOpened() )
{
cerr << "filed to open!" << endl;
return 1;
}
fs << "NUM" << 100;
// 序列
fs << "strings" << "[";
fs << "Hello" << "OpenCV" <<"!";
fs << "]";
// map
fs << "Mapping";
fs << "{" << "One" << 1;
fs << "Two" << 2 << "}";
fs << "R" << R;
fs<< "T" << T;
fs << "MyData" << m;
cout << "Write Done!" << endl;
// read
cout << endl << "Reading: " << endl;
fs.open( filename, FileStorage::READ );
int num;
num = (int) fs["NUM"];
cout << num << endl;
FileNode n = fs["strings"];
if( n.type() != FileNode::SEQ )
{
cerr << "strings is not a sequence!" << endl;
return 1;
}
FileNodeIterator it = n.begin(), it_end = n.end();
for(; it != it_end; ++ it )
cout << (string)*it << endl;
n = fs["Mapping"];
if( n.type() != FileNode::MAP )
{
cerr << "strings is not a map!" << endl;
return 1;
}
cout << "Two " << (int)(n["Two"]) << endl;
cout << "One " << (int)(n["One"]) << endl;
Mat R1, T1;
fs["R"] >> R1;
fs["T"] >> T1;
cout << endl
<< "R = " << R1 << endl;
cout << "T = " << T1 << endl << endl;
MyData m1, m2;
fs["MyData"] >> m1;
/* 文件中没有m2的节点 */
fs["m2"] >> m2;
cout << "MyData m1 = " << endl << m1 << endl ;
cout << "MyData m2 = " << endl << m2 << endl << endl;
fs.release();
return 0;
}
运行结果:
得到的xml文件内容如下: