服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C++ OpenCV读写XML或YAML文件的方法详解

C++ OpenCV读写XML或YAML文件的方法详解

2022-11-25 13:38求则得之,舍则失之 C/C++

XML是一种元标记语言。所谓元标记,就是开发者可以根据自身需要定义自己的标记。YAML是一个可读性高,用来表达资料序列的格式。本文将通过C++和OpenCV实现这两种文件的读写,需要的可以参考一下

前言

本节我们将认识XML和YAML这两种文件类型。

所谓XML,即eXtensible Markup Language,翻译成中文为“可扩展标识语言”。首先,XML是一种元标记语言。所谓元标记,就是开发者可以根据自身需要定义自己的标记,比如可以定义标记<book>、<name>。任何满足XML命名规则的名称都可以标记,这就向不同的应用程序打开了的大门。此外,XML是一种语义、结构化语言,它描述了文档的结构与语义。

YAML是YAML Ain’t a Markup Language(YAML不是一种置标语言)的缩写。YAML是一个可读性高,用来表达资料序列的格式。它参考了其他多种语言,包括:XML、C语言、Python、Perl以及电子邮件格式RFC2822。

1.如何使用

XML和YAML是使用非常广泛的文件格式。可以利用XML或者YAML格式的文件存储和还原各式各样的数据结构。当然,它们还可以存储和载入任意复杂的数据结构,其中就包括了OpenCV相关周边的数据结构,以及各种原始数据类型,如整数、浮点数和文本字符串。

我们一般使用如下过程来写入或者读取数据到XML或YAML文件中。

(1)实例化一个FileStorage类的对象,用默认带参数的构造函数完成初始化,或者用FileStorage::open()成员函数辅助初始化。

(2)使用流操作符<<进行文件写入操作,或者>>进行文件读取操作,类似C++中的文件输入输出流。

(3)使用FileStorage::release()函数析构掉FileStorage类对象,同时关闭文件。

1.1第一步:XML、YAML文件的打开

(1)准备文件写操作

FileStorage是OpenCV中XML和YAML文件的存储类,封装了所有相关的信息。它是OpenCV从文件中读数据或向文件中写数据时必须要使用的一个类。

此类的构造函数为FileStorage::FileStorage,有两个重载,如下:

?
1
2
C++:FileStorage::FileStorage()
C++:FileStorage::FileStorage(const string& source, int flags, const string& encoding=string())

构造函数在实际使用中,一般有两种方法。

1)对于第二种带参数的构造函数,进行写操作范例如下:

?
1
FileStorage fs("abc.xml", FileStorage::WRITE);

2)对于第一种不带参数的构造函数,可以使用其成员函数FileStorage::open进行数据的写操作,范例如下:

?
1
2
FileStorage fs;
fs.open("abc.xml",FileStorage::WRITE);

(2)准备文件读操作

上面讲到的都是以FileStorage::WRITE为标识符的写操作,而读操作,采用FileStorage::READ标识符即可,相关示例代码如下:

1)第一种方式

?
1
FileStorage fs(“abc.xml”, FileStorage::READ);

2)第二种方式

?
1
2
FileStorage fs;
fs.open(“abc.xml”, FileStorage::READ);

另外需要注意的是,上面的这些操作示例是对XML文件为例子作演示的,而对YAML文件,操作方法是类似的,就是将XML文件换为YAML文件即可。

1.2 第二步:进行文件读写操作

(1)文本和数字的输入和输出

定义好FileStorage类对象之后,写入文件可以使用<<运算符,例如:

?
1
fs<<"iterationNr"<<100;

而读取文件,使用>>运算符,例如:

?
1
2
3
int itNr;
fs["iterationNr"]>>itNr;
itNr = (int) fs["iterationNr"];

(2)OpenCV数据结构的输入和输出

关于OpenCV数据结构的输入和输出,和基本的C++形式相同,范例如下:

?
1
2
3
4
5
6
7
8
9
// 数据结构的初始化
Mat R = Mat_<uchar>::eye(3,3);
Mat T = Mat_<double>::zeros(3, 1);
// 向Mat中写入数据
fs << "R" << R;
fs << "T" << T;
// 从Mat中读取数据
fs["R"]  >>  R;
fs["T"] >> T;

1.3 第三步:vector(array)和map的输入和输出

对于vector结构的输出和输出,要注意在第一个元素前加上”[“,在最后一个元素后加上”]“。例如:

?
1
2
3
fs << "strings"<<"["//开始读入string文本序列
fs << "image1.jpg" << "Awesomeness" << "baboon.jpg";
fs << "]"; //关闭序列

而对于map结构的操作,使用的符号是”{“和”}“,例如:

?
1
2
3
fs << "Mapping";//开始读入Mapping文本
fs << "{" << "One" << 1;
fs << "Two" << 2 << "}";

读取这些数据结构的时候,会用到FileNode和FileNodeIterator数据结构。对FileStorage类的“[”、“]”操作符会返回FileNode数据类型;对于一连串的node,可以使用FileNodeIterator结构,例如:

?
1
2
3
4
5
6
7
8
9
10
```cpp
FileNode n = fs["strings"];//读取字符串序列以得到节点
if (n.type()!=FileNode::SEQ)
{
    cerr << "发生错误!字符串不是一个序列" << endl;
    return 1;
}
FileNodeIterator it = n.begin(),it_end = n.end(); //遍历节点
for(;it!=it_end;it++)
cout << (string)*it << endl;

1.4 第四步:文件关闭

需要注意的是,文件关闭操作会在FileStorage类销毁时自动进行,但我们也可以显式调用其析构函数FileStorage::release()实现。FileStorage::release()函数会析构掉FileStorage类对象,同时关闭文件。

调用过程非常简单,如下:fs.release();

2.代码展示

2.1 写文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<opencv2/opencv.hpp>
#include<time.h>
using namespace cv;
 
int main()
{
    //初始化
    FileStorage fs("test.yaml", FileStorage::WRITE);
 
    //开始文件写入
    fs << "frameCount" << 5;
    time_t rawtime; time(&rawtime);
    fs << "calibrationDate" << asctime(localtime(&rawtime));//读取时间量
     Mat cameraMatrix = (Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1);
    Mat distCoeffs = (Mat_<double>(5, 1) << 0.1, 0.01, -0.001, 0, 0);//畸变参数
    fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;//读取Mat型cameraMatrix,distcoeffs的内容
    fs << "feature" << "[";
    for (int i = 0; i < 3; i++)
    {
        int x = rand() % 640;
        int y = rand() % 480;
        uchar ibp = rand() % 256;
 
        fs << "{:" << "x" << x << "y" << y << "ibp" << "[:";
        for (int j = 0; j < 8; j++)
            fs << ((ibp >> j) & 1);
        fs << "]" << "}";
    }
    fs << "]";
    fs.release();
    printf("完毕,请在工程目录下查看文件-");
    getchar();
 
    return 0;
}

上面的示例将一个整数、一个文本字符串(标定日期)、2 个矩阵和一个自定义结构“feature”存储到 YML,其中包括特征坐标和 LBP(局部二进制模式)值。这是样本的输出:

%YAML:1.0
frameCount: 5
calibrationDate: "Fri Jun 17 14:09:29 2011\n"
cameraMatrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 1000., 0., 320., 0., 1000., 240., 0., 0., 1. ]
distCoeffs: !!opencv-matrix
   rows: 5
   cols: 1
   dt: d
   data: [ 1.0000000000000001e-01, 1.0000000000000000e-02,
       -1.0000000000000000e-03, 0., 0. ]
features:
   - { x:167, y:49, lbp:[ 1, 0, 0, 1, 1, 0, 1, 1 ] }
   - { x:298, y:130, lbp:[ 0, 0, 0, 1, 0, 0, 1, 1 ] }
   - { x:344, y:158, lbp:[ 1, 1, 0, 0, 0, 0, 1, 0 ] }

作为练习,您可以将上面示例中的“.yml”替换为“.xml”或“.json”,然后查看相应的 XML 文件的外观。

通过查看示例代码和输出可以注意到几件事:

  • 生成的 YAML(和 XML/JSON)由可以嵌套的异构集合组成。有两种类型的集合:命名集合(映射)和未命名集合(序列)。在映射中,每个元素都有一个名称并通过名称访问。这类似于 C/C++ 中的std::map结构以及 Python 中的字典。在序列中元素没有名称,它们通过索引访问。这类似于 C/C++ 中的std::vector数组以及 Python 中的列表、元组。“异构”意味着每个单一集合的元素可以有不同的类型。
  • YAML/XML/JSON 中的顶级集合是一个映射。每个矩阵存储为一个映射,矩阵元素存储为一个序列。然后,有一个特征序列,其中每个特征都表示一个映射,以及嵌套序列中的 lbp 值。
  • 当您写入映射(结构)时,您写入元素名称后跟其值。当您写入一个序列时,您只需一个一个地写入元素。OpenCV 数据结构(例如cv::Mat)的编写方式与简单的 C 数据结构完全相同 - 使用<<运算符。
  • 要编写映射,首先写入特殊字符串{,然后将元素作为对 ( fs << <element_name> << <element_value>) 写入,然后写入结束符}。
  • 要编写一个序列,首先要编写特殊的字符串[,然后编写元素,然后编写结束]。
  • 在 YAML/JSON(但不是 XML)中,映射和序列可以以类似 Python 的紧凑内联形式编写。在上面的示例中,矩阵元素以及每个特征,包括它的 lbp 值,都以这种内联形式存储。要以紧凑的形式存储映射/序列,请放在:开始字符之后,例如使用{:代替{和[:代替[。当数据写入 XML 时,那些额外:的将被忽略。

2.2 读文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include<opencv2/opencv.hpp>
#include<time.h>
using namespace cv;
using namespace std;
 
int main()
{
    //改变consolo字体颜色
    system("color 6F");
 
    //初始化
    FileStorage fs2("test.yaml", FileStorage::READ);
 
    //开始文件读取
    //法一,对FileNode操作
    int frameCount2 = (int)fs2["framecount2"];
    std::string date;//定义字符串 date
 
    //法二,使用FileNode运算符>>
    fs2["calibrationDate"] >> date;
 
    Mat cameraMatrix2, distCoeffs2;
    fs2["cameraMatrix"] >> cameraMatrix2;
    fs2["distCoeffs"] >> distCoeffs2;//读取
 
    cout << "frameCount2:" << frameCount2 << endl
        << "calibration date:" << date << endl
        << "camera matrix:" << cameraMatrix2 << endl
        << "distortion coeffs:" << distCoeffs2 << endl;
 
    FileNode feature = fs2["feature"];
    FileNodeIterator it = feature.begin(), it_end = feature.end();//定义it
    int idx = 0;
    std::vector<uchar>ibpval;//定义向量容器ibpal
 
    //使用FileNodeIterator历遍序列(读取)
    for (; it != it_end; it++, idx++)
    {
        cout << "feature#" << idx << ":";
        cout << "x=" << (int)(*it)["x"] << ",y=" << (int)(*it)["y"] << ",ibp:(";
        //也可以使用filenod>>std::vector操作符很容易读取数值阵列
        (*it)["ibp"] >> ibpval;
        for (int i = 0; i < (int)ibpval.size(); i++)
            cout << "" << (int)ibpval[i];
        cout << ")" << endl;
    }
 
 
    fs2.release();
    printf("读取完毕,请按任意键结束-");
    getchar();
 
    return 0;
}

2.3 完整的示例代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*
 * filestorage_sample demonstrate the usage of the opencv serialization functionality
 */
#include "opencv2/core.hpp"
#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::endl;
using std::cerr;
using std::ostream;
using namespace cv;
static void help(char** av)
{
  cout << "\nfilestorage_sample demonstrate the usage of the opencv serialization functionality.\n"
      << "usage:\n"
      <<  av[0] << " outputfile.yml.gz\n"
      << "\n   outputfile above can have many different extensions, see below."
      << "\nThis program demonstrates the use of FileStorage for serialization, that is in use << and >>  in OpenCV\n"
      << "For example, how to create a class and have it serialize, but also how to use it to read and write matrices.\n"
      << "FileStorage allows you to serialize to various formats specified by the file end type."
          << "\nYou should try using different file extensions.(e.g. yaml yml xml xml.gz yaml.gz etc...)\n" << endl;
}
struct MyData
{
  MyData() :
    A(0), X(0), id()
  {
  }
  explicit MyData(int) :
    A(97), X(CV_PI), id("mydata1234")
  {
  }
  int A;
  double X;
  string id;
  void write(FileStorage& fs) const //Write serialization for this class
  {
    fs << "{" << "A" << A << "X" << X << "id" << id << "}";
  }
  void read(const FileNode& node)  //Read serialization for this class
  {
    A = (int)node["A"];
    X = (double)node["X"];
    id = (string)node["id"];
  }
};
//These write and read functions must exist as per the inline functions in operations.hpp
static void write(FileStorage& fs, const std::string&, const MyData& x){
  x.write(fs);
}
static void read(const FileNode& node, MyData& x, const MyData& default_value = MyData()){
  if(node.empty())
    x = default_value;
  else
    x.read(node);
}
static ostream& operator<<(ostream& out, const MyData& m){
  out << "{ id = " << m.id << ", ";
  out << "X = " << m.X << ", ";
  out << "A = " << m.A << "}";
  return out;
}
int main(int ac, char** av)
{
  cv::CommandLineParser parser(ac, av,
    "{@input||}{help h ||}"
  );
  if (parser.has("help"))
  {
    help(av);
    return 0;
  }
  string filename = parser.get<string>("@input");
  if (filename.empty())
  {
    help(av);
    return 1;
  }
  //write
  {
    FileStorage fs(filename, FileStorage::WRITE);
    cout << "writing images\n";
    fs << "images" << "[";
    fs << "image1.jpg" << "myfi.png" << "baboon.jpg";
    cout << "image1.jpg" << " myfi.png" << " baboon.jpg" << endl;
    fs << "]";
    cout << "writing mats\n";
    Mat R =Mat_<double>::eye(3, 3),T = Mat_<double>::zeros(3, 1);
    cout << "R = " << R << "\n";
    cout << "T = " << T << "\n";
    fs << "R" << R;
    fs << "T" << T;
    cout << "writing MyData struct\n";
    MyData m(1);
    fs << "mdata" << m;
    cout << m << endl;
  }
  //read
  {
    FileStorage fs(filename, FileStorage::READ);
    if (!fs.isOpened())
    {
      cerr << "failed to open " << filename << endl;
      help(av);
      return 1;
    }
    FileNode n = fs["images"];
    if (n.type() != FileNode::SEQ)
    {
      cerr << "images is not a sequence! FAIL" << endl;
      return 1;
    }
    cout << "reading images\n";
    FileNodeIterator it = n.begin(), it_end = n.end();
    for (; it != it_end; ++it)
    {
      cout << (string)*it << "\n";
    }
    Mat R, T;
    cout << "reading R and T" << endl;
    fs["R"] >> R;
    fs["T"] >> T;
    cout << "R = " << R << "\n";
    cout << "T = " << T << endl;
    MyData m;
    fs["mdata"] >> m;
    cout << "read mdata\n";
    cout << m << endl;
    cout << "attempting to read mdata_b\n";   //Show default behavior for empty matrix
    fs["mdata_b"] >> m;
    cout << "read mdata_b\n";
    cout << m << endl;
  }
  cout << "Try opening " << filename << " to see the serialized data." << endl << endl;
  //read from string
  {
    cout << "Read data from string\n";
    string dataString =
        "%YAML:1.0\n"
        "mdata:\n"
        "   A: 97\n"
        "   X: 3.1415926535897931e+00\n"
        "   id: mydata1234\n";
    MyData m;
    FileStorage fs(dataString, FileStorage::READ | FileStorage::MEMORY);
    cout << "attempting to read mdata_b from string\n";   //Show default behavior for empty matrix
    fs["mdata"] >> m;
    cout << "read mdata\n";
    cout << m << endl;
  }
  //write to string
  {
    cout << "Write data to string\n";
    FileStorage fs(filename, FileStorage::WRITE | FileStorage::MEMORY | FileStorage::FORMAT_YAML);
    cout << "writing MyData struct\n";
    MyData m(1);
    fs << "mdata" << m;
    cout << m << endl;
    string createdString = fs.releaseAndGetString();
    cout << "Created string:\n" << createdString << "\n";
  }
  return 0;
}

到此这篇关于C++ OpenCV读写XML或YAML文件的方法详解的文章就介绍到这了,更多相关C++读写XML YAML文件内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/weixin_43229348/article/details/124620459

延伸 · 阅读

精彩推荐
  • C/C++C++构造函数初始化顺序详解

    C++构造函数初始化顺序详解

    这篇文章主要介绍了C++构造函数初始化顺序详解,是对C++代码的运行机制深入探讨,需要的朋友可以参考下...

    C++教程网10062021-02-04
  • C/C++C++中的Switch 语句详情

    C++中的Switch 语句详情

    在日常的开发当中,我们经常会遇到一种情况,我们用一个变量表示状态。比如关闭-激活-完成,当我们需要判断状态的时候,就需要罗列if-else语句。今天...

    梁唐11542022-02-21
  • C/C++C++稀疏矩阵的各种基本运算并实现加法乘法

    C++稀疏矩阵的各种基本运算并实现加法乘法

    今天小编就为大家分享一篇关于C++稀疏矩阵的各种基本运算并实现加法乘法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友...

    fanxingyue7622021-07-22
  • C/C++C++实现连连看消除算法

    C++实现连连看消除算法

    这篇文章主要为大家详细介绍了C++实现连连看消除算法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    求索路途7192021-07-16
  • C/C++C++使用JsonCpp库操作json格式数据示例

    C++使用JsonCpp库操作json格式数据示例

    这篇文章主要介绍了C++使用JsonCpp库操作json格式数据,结合实例形式详细分析了JsonCpp库的下载及C++使用JsonCpp库对json格式数据序列化相关操作技巧,需要的朋友...

    ggjucheng10382021-05-17
  • C/C++C语言合并两个带头节点升序排列链表

    C语言合并两个带头节点升序排列链表

    这篇文章主要为大家详细介绍了C语言合并两个带头节点升序排列链表的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Judy_c11232021-07-24
  • C/C++用C语言实现三子棋小游戏

    用C语言实现三子棋小游戏

    这篇文章主要为大家详细介绍了用C语言实现三子棋小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    技术新人王小明11102021-11-15
  • C/C++C语言实现常用字符串库函数(推荐)

    C语言实现常用字符串库函数(推荐)

    这篇文章主要介绍了C语言实现常用字符串库函数,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可...

    3 ERROR(s)7602022-02-20