首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【从零学习OpenCV】保存和读取XML和YMAL文件

【从零学习OpenCV】保存和读取XML和YMAL文件

作者头像
小白学视觉
发布2019-11-15 17:09:29
2.4K0
发布2019-11-15 17:09:29
举报

经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍《从零学习OpenCV 4》。为了更让小伙伴更早的了解最新版的OpenCV 4,小白与出版社沟通,提前在公众号上连载部分内容,请持续关注小白。

除了图像数据之外,有时程序中的尺寸较小的Mat类矩阵、字符串、数组等

数据也需要进行保存,这些数据通常保存成XML文件或者YAML文件。本小节中将介绍如何利用OpenCV 4中的函数将数据保存成XML文件或者YAML文件以及如何读取这两种文件中的数据。

XML是一种元标记语言,所谓元标记就是使用者可以根据自身需求定义自己的标记,例如可以用<age>、<color>等标记来定义数据的含义,例如用<age>24</age>来表示age数据的数值为24。XML是一种结构化的语言,通过XML语言可以知道数据之间的隶属关系,例如<color><red>100</red><blue>150</blue></color>表示在color数据中含有两个名为red和blue的数据,两者的数值分别是100和150。通过标记的方式,无论以任何形式保存数据,只要文件满足XML格式,那么读取出来的数据就不会出现混淆和歧义。XML文件的扩展名是“.xml”。

YMAL是一种以数据为中心的语言,通过“变量:数值”的形式来表示每个数据的数值,通过不同的缩进来表示不同数据之间的结构和隶属关系。YMAL可读性高,常用来表达资料序列的格式,它参考了多种语言,包括XML、C语言、Python、Perl等。YMAL文件的扩展名是“.ymal”或者“.yml”。

OpenCV 4中提供了用于生成和读取XML文件和YMAL文件的FileStorage类,类中定义了初始化类、写入数据和读取数据等方法。我们在使用该FileStorage类时首先需要对其进行初始化,初始化可以理解为声明需要操作的文件和操作类型。OpenCV 4提供了两种初始化的方法,分别是不输入任何参数的初始化(可以理解为只定义,并未初始化)和输入文件名称和操作类型的初始化。后者初始化构造函数的函数原型在代码清单2-35中给出。

代码清单2-35 FileStorage()函数原型
cv::FileStorage::FileStorage(const String & filename,
                                  int  flags,
                                  const String & encoding = String()
                                  )
  • filename:打开的文件名称。
  • flags:对文件进行的操作类型标志,常用参数及含义在表2-8给出。
  • encodin:编码格式,目前不支持UTF-16 XML编码,需要使用UTF-8 XML编码。

表2-8 FileStorage()构造函数中对文件操作类型常用标志及含义

标志参数

简记

含义

READ

0

读取文件中的数据

WRITE

1

向文件中重新写入数据,会覆盖之前的数据

APPEND

2

向文件中继续写入数据,新数据在原数据之后

MEMORY

4

将数据写入或者读取到内部缓冲区

该函数是FileStorage类的构造函数,用于声明打开的文件名称和操作的类型。函数第一个参数是打开的文件名称,参数是字符串类型,文件的扩展名是“.xml”、“.ymal”或者“.yml”。打开得文件可以已经存在或者未存在,但是当对文件进行读取操作时需要是已经存在的文件。第二个参数是对文件进行的操作类型标志,例如对文件进行读取操作、写入操作等,常用参数及含义在表2-8给出,由于该标志量在FileStorage类中,因此在使用时需要加上类名作为前缀,例如“FileStorage::WRITE”。最后一个参数是文件的编码格式,目前不支持UTF-16 XML编码,需要使用UTF-8 XML编码,通常情况下使用该参数的默认值即可。

打开文件后,可以通过FileStorage类中的isOpened()函数判断是否成功打开文件,如果成功打开文件,该函数返回true,如果打开文件失败,则该函数返回false。

FileStorage类中默认构造函数没有任何参数,因此没有声明打开的文件和操作的类型,此时需要通过FileStorage类中的open()函数单独进行声明,该函数的函数原型在代码清单2-36中给出。

代码清单2-36 open()函数原型
virtual bool cv::FileStorage::open(const String & filename,
                                         int  flags,
                                         const String & encoding = String()
                                         )
  • filename:打开的文件名称。
  • flags:对文件进行的操作类型标志,常用参数及含义在表2-8给出。
  • encodin:编码格式,目前不支持UTF-16 XML编码,需要使用UTF-8 XML编码。

该函数补充了默认构造函数没有声明打开文件的缺点,函数可以指定FileStorage类打开的文件,如果成功打开文件,则返回值为true,否则为false。函数中所有的参数及含义都与代码清单2-35中的相同,因此这里不再进行赘述。同样,通过该函数打开文件后仍然可以通过FileStorage类中的isOpened()函数判断是否成功打开文件。

打开文件后,类似C++中创建的数据流,可以通过“<<”操作符将数据写入文件中,或者通过“>>”操作符从文件中读取数据。除此之外,还可以通过FileStorage类中的write()函数将数据写入文件中,该函数的函数原型在代码清单2-37中给出。

代码清单2-37 write()函数原型
void cv::FileStorage::write(const String & name,
                   int  val
              )
  • name:写入文件中的变量名称。
  • val:变量值。

该函数能够将不同数据类型的变量名称和变量值写入到文件中。该函数的第一个参数是写入文件中的变量名称。第二个参数是变量值,代码清单2-37中的变量值是int类型,但是在FileStorage类中提供了write()函数的多个重载函数,分别用于实现将double、String、Mat、vector<String>类型的变量值写入到文件中。

使用操作符向文件中写入数据时与write()函数类似,都需要声明变量名和变量值,例如变量名为“age”,变量值为“24”,可以通过“file<<”age”<<24”来实现。如果某个变量的数据是一个数组,可以用“[]”将属于同一个变量的变量值标记出来,例如“file<<”age”<<“[”<<24<<25<<”]””。如果某些变量隶属于某个变量,可以用“{}”表示变量之间的隶属关系,例如“file<<”age”<<“{”<<”Xiaoming”<<24<<”Wanghua”<<25<<”}””。

读取文件中的数据时,只需要通过变量名就可以读取变量值。例如“file ["x"] >> xRead”是读取变量名为x的变量值。但是,当某个变量中含有多个数据或者含有子变量时,就需要通过FileNode节点类型和迭代器FileNodeIterator进行读取,例如某个变量的变量值是一个数组,首先需要定义一个file ["age"]的FileNode节点类型变量,之后通过迭代器遍历其中的数据。另外一种方法可以不使用迭代器,通过在变量后边添加“[]”地址的形式读取数据,例如FileNode[0]表示数组变量中的第一个数据,FileNode[“Xiaoming”]表示“age”变量中的“Xiaoming”变量的数据,依次向后添加“[]”地址实现多节点数据的读取。

为了了解如何生成和读取XML文件和YMAL文件,在代码清单2-38中给出了实现文件写入和读取的示例程序。程序中使用write()函数和“<<”操作符两种方式向文件中写入数据,使用迭代器和“[]”地址两种方式从文件中读取数据。数据的写入和读取方法在前面已经介绍,在代码清单2-38中需要重点了解如何通过程序实现写入与读取。程序生成的XML文件和YMAL文件中的数据在图2-10给出,读取文件数据的结果在图2-9给出。

代码清单2-38 myXMLandYAML.cpp保存和读取XML和YAML文件
1.  #include <opencv2/opencv.hpp>
2.  #include <iostream>
3.  #include <string>
4.  
5.  using namespace std;
6.  using namespace cv;
7.  
8.  int main(int argc, char** argv)
9. {
10.    system("color F0"); //修改运行程序背景和文字颜色
11.    //string fileName = "datas.xml"; //文件的名称
12.    string fileName = "datas.yaml"; //文件的名称
13.    //以写入的模式打开文件
14.    FileStorage fwrite(fileName, FileStorage::WRITE);
15.    
16.    //存入矩阵Mat类型的数据
17.    Mat mat = Mat::eye(3, 3, CV_8U);
18.    fwrite.write("mat", mat); //使用write()函数写入数据
19.    //存入浮点型数据,节点名称为x
20.    float x = 100;
21.    fwrite << "x" << x;
22.    //存入字符串型数据,节点名称为str
23.    String str = "Learn OpenCV 4";
24.    fwrite << "str" << str;
25.    //存入数组,节点名称为number_array
26.    fwrite << "number_array" << "[" <<4<<5<<6<< "]";
27.    //存入多node节点数据,主名称为multi_nodes
28.    fwrite << "multi_nodes" << "{" << "month" << 8 << "day" << 28 << "year"
29.      << 2019 << "time" << "[" << 0 << 1 << 2 << 3 << "]" << "}";
30.  
31.    //关闭文件
32.    fwrite.release();
33.  
34.    //以读取的模式打开文件
35.    FileStorage fread(fileName, FileStorage::READ);
36.    //判断是否成功打开文件
37.    if (!fread.isOpened())
38.    {
39.      cout << "打开文件失败,请确认文件名称是否正确!" << endl;
40.      return -1;
41.    }
42.  
43.    //读取文件中的数据
44.    float xRead;
45.    fread["x"] >> xRead; //读取浮点型数据
46.    cout << "x=" << xRead << endl;
47.  
48.    //读取字符串数据
49.    string strRead;
50.    fread["str"] >> strRead;
51.    cout << "str=" << strRead << endl;
52.  
53.    //读取含多个数据的number_array节点
54.    FileNode fileNode = fread["number_array"];
55.    cout << "number_array=[";
56.    //循环遍历每个数据
57.    for (FileNodeIterator i = fileNode.begin(); i != fileNode.end(); i++)
58.    {
59.      float a;
60.      *i >> a;
61.      cout << a<<" ";
62.    }
63.    cout << "]" << endl;
64.  
65.    //读取Mat类型数据
66.    Mat matRead;
67.    fread["mat="] >> matRead;
68.    cout << "mat=" << mat << endl;
69.  
70.    //读取含有多个子节点的节点数据,不使用FileNode和迭代器进行读取
71.    FileNode fileNode1 = fread["multi_nodes"];
72.    int month = (int)fileNode1["month"];
73.    int day = (int)fileNode1["day"];
74.    int year = (int)fileNode1["year"];
75.    cout << "multi_nodes:" << endl 
76.      << " month=" << month << " day=" << day << " year=" << year;
77.    cout << " time=[";
78.    for (int i = 0; i < 4; i++)
79.    {
80.      int a = (int)fileNode1["time"][i];
81.      cout << a << " ";
82.    }
83.    cout << "]" << endl;
84.    
85.    //关闭文件
86.    fread.release();
87.    return 0;
88.  }

图2-9 myXMLandYAML.cpp程序文件读取结果

图2-10 myXMLandYAML.cpp程序生成的XML文件和YAML文件

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小白学视觉 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档