OpenCV 4.13.0
开源计算机视觉库 (Open Source Computer Vision)
正在加载...
正在搜索...
未找到匹配项
使用 XML / YAML / JSON 文件进行文件输入和输出

上一个教程: 离散傅里叶变换
下一个教程: 如何使用 OpenCV parallel_for_ 来并行化你的代码

原作者Bernát Gábor
兼容性OpenCV >= 3.0

目标

你将找到以下问题的答案

  • 如何使用 YAML、XML 或 JSON 文件在 OpenCV 中向文件打印和读取文本条目?
  • 如何对 OpenCV 数据结构执行相同的操作?
  • 如何对你的自定义数据结构执行此操作?
  • 如何使用 OpenCV 数据结构,例如 cv::FileStoragecv::FileNodecv::FileNodeIterator

源代码

解释

这里我们只讨论 XML、YAML 和 JSON 文件输入。你的输出(及其相应的输入)文件可能只有这些扩展名之一,并且结构也由此而来。你可以序列化两种数据结构:映射(如 STL map 和 Python 字典)和元素序列(如 STL vector)。两者的区别在于,在映射中,每个元素都有一个唯一的名称,你可以通过它访问该元素。对于序列,你需要遍历它们才能查询特定项。

  1. XML/YAML/JSON 文件打开和关闭。在向此类文件写入任何内容之前,你需要打开它,并在最后关闭它。OpenCV 中的 XML/YAML/JSON 数据结构是 cv::FileStorage 。要指定此结构绑定到硬盘上的文件,你可以使用其构造函数或此结构的 open() 函数

    无论是哪种方式,你使用的第二个参数都是一个常量,指定你将能够对其执行的操作类型:WRITE、READ 或 APPEND。文件名中指定的扩展名也决定了将使用的输出格式。如果你指定一个扩展名(例如 .xml.gz),输出甚至可以被压缩。

    cv::FileStorage 对象被销毁时,文件会自动关闭。但是,你可以通过使用 release 函数显式调用此操作

  2. 文本和数字的输入和输出。在 C++ 中,数据结构使用 STL 库中的 << 输出运算符。在 Python 中,则使用 cv::FileStorage::write() 。要输出任何类型的数据结构,我们首先需要指定其名称。在 C++ 中,我们只需将其名称推送到流中即可。在 Python 中,write 函数的第一个参数是名称。对于基本类型,你可以随后打印值。 读取是一个简单的寻址(通过 [] 运算符)和类型转换操作,或者通过 >> 运算符读取。在 Python 中,我们使用 getNode() 进行寻址,并使用 real() 。
  3. OpenCV 数据结构的输入/输出。它们与基本的 C++ 和 Python 类型完全相同。
  4. 向量(数组)和关联映射的输入/输出。正如我前面提到的,我们也可以输出映射和序列(数组、向量)。同样,我们首先打印变量的名称,然后我们必须指定我们的输出是序列还是映射。

    对于序列,在第一个元素之前打印“[”字符,在最后一个元素之后打印“]”字符。在 Python 中,调用 FileStorage.startWriteStruct(structure_name, struct_type),其中 struct_typecv2.FileNode_MAPcv2.FileNode_SEQ 以开始写入结构。调用 FileStorage.endWriteStruct() 以完成结构。

    对于映射,操作相同,但现在我们使用“{”和“}”分隔符。

    要从这些结构中读取,我们使用 cv::FileNodecv::FileNodeIterator 数据结构。cv::FileStorage 类的 [] 运算符(或 Python 中的 getNode() 函数)返回一个 cv::FileNode 数据类型。如果节点是序列,我们可以使用 cv::FileNodeIterator 迭代项。在 Python 中,at() 函数可用于访问序列元素,size() 函数返回序列的长度。

    对于映射,你可以再次使用 [] 运算符(Python 中的 at() 函数)来访问给定项(或 >> 运算符)。

  5. 读取和写入你自己的数据结构。假设你有一个数据结构,例如

    在 C++ 中,可以通过在类内部和外部添加读写函数,通过 OpenCV I/O XML/YAML 接口来序列化它(就像 OpenCV 数据结构一样)。在 Python 中,可以通过在类内部实现读写函数来实现类似的功能。对于内部部分

    在这里你可以观察到,在读取部分我们定义了如果用户尝试读取一个不存在的节点会发生什么。在这种情况下,我们只返回默认的初始化值,但是一个更详细的解决方案是例如为一个对象 ID 返回一个负一值。

    一旦你添加了这四个函数,就使用 >> 运算符进行写入,使用 << 运算符进行读取(或使用 Python 中定义的输入/输出函数)。

    或者尝试读取一个不存在的节点

结果

我们主要只是打印出定义的数字。在控制台屏幕上,你可以看到

写入完成。
读取中
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}
尝试读取 NonExisting (应该使用其默认值初始化数据结构)。
NonExisting =
{ id = , X = 0, A = 0}
提示:使用文本编辑器打开 output.xml 以查看序列化数据。

然而,更有趣的是你可能在输出 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

你可以在此处观看其运行时实例。