使用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;
}



运行结果:

opencv 读取 yuv 文件 opencv读取xml文件_操作符

得到的xml文件内容如下:

opencv 读取 yuv 文件 opencv读取xml文件_OpenCV_02