opencv学习(十七)之XML和YAML文件读写操作

来源:互联网 发布:json框架有哪些 编辑:程序博客网 时间:2024/06/08 09:43

可能大部分人到现在接触的XML和YAML文件很少,等以后训练人脸模型进行人脸识别的时候用的就多了。现在先了解一下这两种文件类型。
XML:Extensible Markup Language,可扩展标记语言,标准通用语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML的简单使其易于在任何应用程序中读写数据,这使XML很快成为数据交换的唯一公共语言。可扩展标记语言文件的内容包括几乎所有的万国码Unicode字符
YAML : Yet Another Markup Language,从其字面意识可以知道YAML是另一种标记语言,但是为了强调这种语言以数据作为中心,而不是以置标语言为重点,它是一种直观的能够被电脑识别的数据序列化格式,是一个可读性高并且容易被人类于都,容易和脚本语言交互,用来表达资料序列的编程语言。它是XML的数据描述语言,语法比XML简单。

(1)FileStorage
opencv提供了对XML和YAML文件进行读写操作的FileStorage类,其定义有两种形式,如下:

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

根据其两种不同的构造形式,可以有两种FileStorage使用方法
. 第一种:其构造函数不带参数,可以定义一个FileStorage对象,通过FileStorage的成员函数对XML和YAML文件进行读写操作如

FileStorage fs;fs.open("123.xml",FileStorage::WRITE);

对于open函数其构造函数如下:

C++: bool FileStorage::open(const string& filename, int flags, const string& encoding=string());

其参数含义与第二种构造函数相同,见下面说明。

. 第二种:其构造函数带参数,对参数进行解释如下
.const string& source: 读入文件的名字或字符串,文件的扩展名.xml或.yml/.yaml决定了文件使XML类型还是YAML类型。如果文件是一个压缩文件,可以在扩展名后缀条件.gz进行访问,如:myHugeMatrix.xml.gz。如果FileStorage::WRITE和FileStorage::MEMORY都指定了,sourec仅能输出指定类型文件(.xml或.yml等)
. int flags: 操作类型标识符,FileStorage类的操作标识符有如下类型:
。FileStorage::READ: 打开文件进行读的操作
。FileStorage::WRITE: 打开文件进行写的操作
。FileStorage::APPEND: 打开文件进行补充操作(Open the file for appending)
。FileStorage::MEMORY:从输入的文件(source)读取数据或把数据写入到内部缓冲器(通过FileStorage::release进行内存释放)
. const string& encoding=string(): 文件结束符。当前还不支持UTF-16类型的XML,必须用8-bit的结束符来代替。

可以通过FileStorage::isOpened函数来判断文件是否正确打开,如果正确打开则函数返回true,否则返回false。对文件进行操作应该养成对文件是否操作成功进行判断的习惯。

(2)FileNode
FileNode是文件存储节点类,对于进行读操作的文件节点用于存储每个文件元素。当读取XML或YMAL文件,节点是第一个被解析并作为一个节点结合存储到存储器中。每个节点都可以作为一个包含一个数字或一个字符或其他节点的“叶子”。每个节点都有一个名字并可以通过节点的名字对节点进行访问,这些节点就组成了一个集合,就算节点没有名字也可以通过元素的索引对节点结合进行排序。文件节点类型可以通过FileNode::type()方法进行指定。
值得注意的是节点只用用来对文件的读取提供引导,而文件进行写操作后将没有数据存储在内存中。FileNode有三种构造形式:

C++: FileNode::FileNode();C++: FileNode::FileNode(const CvFileStorage* fs, const CvFileNode* node);C++: FileNode::FileNode(const FileNode& node);

(3)FileNodeIterator
FileNodeIterator用于迭代访问序列(sequences)和映射表(mappings).是一种典型的STL符号,通过node.begin()和node.end()来标识序列的开始和结束位置。其也有三种构造形式,如下:

C++: FileNodeIterator::FileNodeIterator()C++: FileNodeIterator::FileNodeIterator(const CvFileStorage* fs, const CvFileNode* node, size_t ofs=0)C++: FileNodeIterator::FileNodeIterator(const FileNodeIterator& it)

示例代码:

#include <iostream>#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <opencv2/opencv.hpp>using namespace std;using namespace cv;class MyData{public:    MyData():A(0),X(0),id(){}    explicit MyData(int):A(97),X(CV_PI), id("mydata1234"){} //explicit to avoid implicit conversion    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:    //data members    int A;    double X;    string id;};//These write and read functions must be defined for the serialization in FileStorage to workstatic void write(FileStorage&fs, const 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);}//This function will print our custom class to the consolestatic 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){    if(ac != 2)    {        //help(av);        return 1;    }    string filename = av[1];    {        //write        Mat R = Mat_<uchar>::eye(3, 3), T = Mat_<double>::zeros(3,1);        MyData m(1);        FileStorage fs(filename, FileStorage::WRITE);        fs << "iterationNr"<<100;        fs << "strings" << "[";        fs << "image1.jpg" << "Awesomeness" << "baboon.jpg";        fs << "]";        fs << "Mapping";        fs << "{" << "One" << 1;        fs <<        "Two" << 2 << "}";        fs << "R" << R;        fs << "T" << T;        fs << "MyData" << m;        fs.release();        cout << "Write Done." << endl;    }    {//read        cout << endl << "Reading: " << endl;        FileStorage fs;        fs.open(filename, FileStorage::READ);        int itNr;        //fs["iterationNr"] >> itNr;        itNr = (int)fs["iterationNr"];        cout << itNr;        if(!fs.isOpened())        {            cerr << "Failed to open " << filename << endl;            return 1;        }        FileNode n = fs["strings"];        if(n.type() != FileNode::SEQ)        {            cerr << "string is not a sequence! FAIL" << endl;            return 1;        }        FileNodeIterator it = n.begin(), it_end = n.end();        for (; it != it_end; ++it)        {            cout << (string)*it << endl;        }        n = fs["Mapping"];        cout << "Two " << (int)(n["Two"]) << "; ";        cout << "One " << (int)(n["One"]) << endl << endl;        MyData m;        Mat R, T;        fs["R"] >> R;        fs["T"] >> T;        fs["MyData"] >> m;        cout << endl << "R = " << R << endl;        cout << "T = " << T << endl << endl;        cout << "MyData = " << endl << m << endl << endl;        //Show default behavior for non existing nodes        cout << "Attempt to read NonExisting(should initialize the data structure with its defaule).";        fs["NonExisting"] >> m;        cout << endl << "NonExisting = " << endl << m << endl;    }    cout << endl << "Tip: Open up" << filename << "with a text editor to see the serialized data." << endl;    return 0;}

程序分析:
1.XML/YAML文件打开和关闭
前面已经讲过,可以使用类FileStorage实现对文件的打开操作。对于文件的关闭而言,当FileStorage对象销毁时文件会自动关闭,也可以显式调用FileStorage::release()函数进行。如fs.release()

2.输入输出文本和数字
在对XML和YAML文件进行输入输出操作时,和C++中STL用相同的操作符“<<”输出(写入文件)和“>>”输入(读取文件),对于基本的数据类型可以这样打印数值.
输出任意一种数据类型之前首先需要指出变量的名称,可以通过打印变量的名称来达到这个目的,如下:

fs << "iterationNr" << 100;

读取文件使用基本操作符”>>”如:

int itNr;fs["iterationNr"] >> itNr;itNr = (int)fs["iterationNr"];

3.opencv数据结构的输入输出
opencv数据结构的输入输出和基本的C++输入输出形式相同

Mat R = Mat_<uchar>::eye(3, 3), T = Mat_<double>::zeros(3, 1);fs << "R" << R;     //R数据写入文件fs << "T" << T;     //T数据写入文件fs["R"] >> R;           //从文件中读取数据Rfs["T"] >> T;           //从文件中读取数据T

4.vector(arrays)和maps输入输出
就向前面程序中写的,我们同样可以对array,vector和maps进行输出操作,首先打印出变量的名字,之后支出要输入变量的类型是序列(vector, array)还是maps.

如果数据类型是vector和array类,要在元素前面加上”[“在元素后面加上”]”符号,如下:

fs << "string" << "[";fs << "image1.jpg" << "Awesomeness" << "baboon.jpg";fs << "]";

对于数据结构是maps类型,需要在元素前面和最后加上”{“和”}”符号,如下:

fs << "Mapping";fs << "{" << "One" << 1;fs        << "Two" << "}";

从文件中读取这些数据的时候我们需要用到FileNode和FileNodeIterator数据结构。FileStorage类操作符”[]”返回的是一个FileNode数据类型,对于连续的节点,可以通过FileNodeIterator进行迭代遍历,其函数功能和C++中iterator一样。

FileNode n = fs["string"];if(n.type() != FileNode::SEQ){    cerr << "string is not a sequence! FAIL" << endl;    return 1;}FileNodeIterator it = n.begin(), it_end = n.end();for(; it != it_end; ++it){    cout << (string)*it << endl;}

对于maps类型的数据结构可以再次使用”[]”操作符来访问给定的变量对象如下:

n = fs["Mapping"];cout << "Two " << (int)(n[""Two]) << "; ";cout << "One " << (int)(n["One"]) << endl << endl;

5.读取和吸入自己定义的数据结构
假定已经定义了如下数据结构:

class MyData{public:    MyData():A(0),X(0),id(){}public:int A;double X;string id;};

可以通过opencv提供的对XML和YAML文件的接口,在类内或类外各添加添加一个读取函数和一个写入函数实现对文件的连续操作如下:

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 std::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);}

从这里就可以看出,如果我们定义了一个读取部分试图读取一个不存在的节点,这种情况下就会返回一个默认初始化的值,更详细的解决方案是返回-1作为项目代码。
一旦添加了这四个函数可以使用”>>”和”<<”进行写入和读取的操作。

MyData m(1);fs << "MyData" <<m;     //自定义的数据结构fs["MyData"] >> m;      //读取自己的数据结构

或尝试读取一个不存在文件

fs["NonExisting"] >> m;cout << endl << "NonExisting = " << endl << m << endl;

程序运行结果:

Linux环境运行方式如下,首先要先建立一个CMakeLists.txt文件,其语法格式请参考《opencv之在Linux下编译opencv程序的两种方式g++、cmake》对于程序中出现的argc及argv[]运行方式,请参考《程序命令行argc\argv》
下面分别给出在Linux和Windows下命令运行的截图以供参考
Linux环境(Ubuntu)
这里写图片描述
可以看出在其根文件夹下生成了mydata1234.xml文件
这里写图片描述

Windows(win 7)运行如下:
这里写图片描述
在其Debug文件夹下生成mydata文件
这里写图片描述

0 0