输入输出XML和YAML文件
目的
你将得到以下几个问题的答案:
- 如何将文本写入YAML或XML文件,及如何从从OpenCV中读取YAML或XML文件中的文本
- 如何利用YAML或XML文件存取OpenCV数据结构
- 如何利用YAML或XML文件存取自定义数据结构?
- OpenCV中相关数据结构的使用方法,如 :xmlymlpers:FileStorage <filestorage>,FileNode或FileNodeIterator.
代码
你可以点击此处下载或直接从OpenCV代码库中找到源文件。samples/cpp/tutorial_code/core/file_input_output/file_input_output.cpp。
以下用简单的示例代码演示如何逐一实现所有目的.
502_20@
@H_ |
502_20@
@H_ |
代码分析
这里我们仅讨论XML和YAML文件输入。你的输出(和相应的输入)文件可能仅具有其中一个扩展名以及对应的文件结构。XML和YAML的串行化分别采用两种不同的数据结构:mappings(就像STL map) 和element sequence(比如 STL vector>。二者之间的区别在map中每个元素都有一个唯一的标识名供用户访问;而在sequences中你必须遍历所有的元素才能找到指定元素。
-
XML\YAML 文件的打开和关闭。在你写入内容到此类文件中前,你必须先打开它,并在结束时关闭它。在OpenCV中标识XML和YAML的数据结构是FileStorage。要将此结构和硬盘上的文件绑定时,可使用其构造函数或者open()函数:
502_20@@H_无论以哪种方式绑定,函数中的第二个参数都以常量形式指定你要对文件进行操作的类型,包括:WRITE,READ 或 APPEND。文件扩展名决定了你将采用的输出格式。如果你指定扩展名如.xml.gz,输出甚至可以是压缩文件。
当FileStorage对象被销毁时,文件将自动关闭。当然你也可以显示调用release函数:
502_20@ @H_502_20@@H_输入\输出文本和数字。数据结构使用与STL相同的 << 输出操作符。输出任何类型的数据结构时,首先都必须指定其标识符,这通过简单级联输出标识符即可实现。基本类型数据输出必须遵循此规则:
fs 100; @H_502_20@ @H_502_20@读入则通过简单的寻址(通过 [] 操作符)操作和强制转换或 >> 操作符实现:
int itNr; fs["iterationNr"] >> itNr; itNr "iterationNr"]; @H_502_20@ @H_502_20@输入\输出OpenCV数据结构。其实和对基本类型的操作方法是相同的:
Mat R <uchar >::eye (T 1); fs // 写 cv::Mat fs << T; fs[// 读 cv::Mat fs[>> T; @H_502_20@ @H_502_20@输入\输出 vectors(数组)和相应的maps.之前提到我们也可以输出maps和序列(数组,vector)。同样,首先输出变量的标识符,接下来必须指定输出的是序列还是map。
对于序列,在第一个元素前输出”[“字符,并在最后一个元素后输出”]“字符:
// 文本 - 字符串序列 fs "baboon.jpg"; fs // 序列结束 @H_502_20@ @H_502_20@对于maps使用相同的方法,但采用”{“和”}“作为分隔符。
// 文本 - mapping fs 1; fs "}"; @H_502_20@ @H_502_20@对于数据读取,可使用FileNode和FileNodeIterator数据结构。FileStorage的[] 操作符将返回一个FileNode数据类型。如果这个节点是序列化的,我们可以使用FileNodeIterator来迭代遍历所有元素。
FileNode n // 读取字符串序列 - 获取节点 ::SEQ) { cerr << endl; 1; } FileNodeIterator it // 遍历节点 ++it) cout << endl; @H_502_20@ @H_502_20@对于maps类型,可以用 [] 操作符访问指定的元素(或者 >> 操作符):
n // 从序列中读取map cout "; "; cout << endl; @H_502_20@ @H_502_20@读写自定义数据类型。假设你定义了如下数据类型:
: MyData() id() {} // 数据成员 int A; double X; string id; }; @H_502_20@ @H_502_20@添加内部和外部的读写函数,就可以使用OpenCV I/O XML/YAML接口对其进行序列化(就像对OpenCV数据结构进行序列化一样)。内部函数定义如下:
//对自定义类进行写序列化 { fs "}"; } //从序列读取自定义类 { A "A"]; X "X"]; id "id"]; } @H_502_20@ @H_502_20@接下来在类的外部定义以下函数:
& x) { x.write(fs); } = MyData()) { if(node.empty()) x = default_value; else x.read(node); } @H_502_20@ @H_502_20@这儿可以看到,如果读取的节点不存在,我们返回默认值。更复杂一些的解决方案是返回一个对象ID为负值的实例。
一旦添加了这四个函数,就可以用 >> 操作符和 << 操作符分别进行读,写操作:
MyData m(1); fs << m; // 写自定义数据结构 fs[// 读自定义数据结构 @H_502_20@ @H_502_20@或试着读取不存在的值:
fs[>> m; // 请注意不是 fs << "NonExisting" << m cout << endl; @H_502_20@ @H_502_20@ @H_502_20@结果
好的,大多情况下我们只输出定义过的成员。在控制台程序的屏幕上,你将看到:
502_20@ @H_502_20@@H_<?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 <cols>1<dt>d<data> 0. 0. 0.</data></T> <MyData> <A>97</A> <X>3.1415926535897931e+000</X> <id>mydata1234</id></MyData> </opencv_storage> @H_502_20@ @H_502_20@或YAML文件:
@H_502_1809@