@H_403_1@使用OpenCV,我们经常需要对xml文件进行操作。为此OpenCV为我们提供了FileStorage类来对XML/YAML文件进行操作。它使我们能够像操作普通文件一下来读写xml文件。
XML文件的打开和关闭
@H_403_1@我们可以使用FileStorage的构造函数或者open()函数来对磁盘上的文件进行绑定。
string filename = "test.xml"; FileStorage fs( filename,FileStorage::WRITE );
fs.open( filename,FileStorage::READ );@H_403_1@
文件的扩展名决定了采用的输出格式,第二个参数指定了要对文件进行操作的类型。
@H_403_1@我们可以通过isOpened函数来判断是否正确打开文件。
if( !fs.isOpened() ) { cerr << "filed to open!" << endl; return 1; }
@H_403_1@我们可以显示的调用release函数来进行文件的关闭。也可以不调用,当FileStorage对象被销毁时,文件将自动被关闭。
fs.release();
@H_403_1@基本数据类型的输入/输出
@H_403_1@我们可以使用<<操作符来简单的将基本数据类型的值写入文件。
fs << "NUM" << 100;
@H_403_1@我们还可以通过[]操作符和>>操作符来实现简单的读取。
int num; num = (int) fs["NUM"]; cout << num << endl;
OpenCV数据结构的输入/输出
@H_403_1@它的操作方法和基本数据类型的操作方法是相同的。
<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;
@H_403_1@vectors和maps的输入/输出
@H_403_1@我们也可以对vectors(序列)和maps进行输入/输出
@H_403_1@对于序列,在第一个元素前输出“[”,在最后一个元素后输出"]":
// 序列 fs << "strings" << "["; fs << "Hello" << "OpenCV" <<"!"; fs << "]";
@H_403_1@对于序列的读取,我们使用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 << "}";
@H_403_1@对于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;
读写自定义的数据结构
@H_403_1@读写自定义的数据结构比较麻烦,我们为了使OpenCV的I/O接口能够像OpenCV内部结构一样对其进行序列化。我们需要重写read和write函数。
@H_403_1@加入我们有如下的数据类型
class MyData { public: MyData() : A(0),X(0),id() {} public: // 数据成员 int A; double X; string id; };
@H_403_1@我们需要重写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"]; }
@H_403_1@然后还需要在类的外部进行如下定义:
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 ); }@H_403_1@为了能够使用<<操作符进行对自定数据结构进行操作,还需要对<<操作符进行重载,如下所示:
ostream& operator<<(ostream& out,const MyData& m) { out << "{ A = " << m.A << ","; out << "X = " << m.X << ","; out << "id = " << m.id << "}"; return out; }@H_403_1@之后,我们就可以对自定义数据类型进行输入/输出。
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),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 MyData& default_value = MyData() ) { if( node.empty() ) /* 定义了如果节点不存在的情况下的默认输出值 */ x = default_value; else x.read( node ); } ostream& operator<<(ostream& out,"; out << "id = " << m.id << "}"; return out; } int main() { string filename = "test.xml"; Mat R = Mat_<uchar>::eye( 3,1 ); MyData m(1,"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; }
运行结果: