OpenCV FileStorage读写操作

引言

有时我们需要保存一些训练数据,或是相机标定结果,亦或是临时存储一些图片等等。这时,我们可以使用FileStorage加上FileNode来完成这些操作。

FileStorage 和 FileNode

FileStorage这个类可以看成是纸+笔的功能,负责保存内容和写入内容;而FileNode可以看成是眼睛,读取纸上的内容。FileStorage拥有真正的内容,而FileNode仅拥有读取的功能。这两个类支持读写XML, YAML, JSON 格式文件,后缀名为.xml , .yml , .yaml , .json

FileStorage写入操作

假设我们现在要写入的内容是这样的:

字段 内容
matrix 1,2;
3,4
int 1
double 2.2
strings “123”,”456”,”end”
features x:0,y:0,lb[0,1,2,3]
x:1,y:1,lb[0,1,2,3]
x:2,y:4,lb[0,1,2,3]
1. 打开文件
FileStorage storage("file.xml", FileStorage::WRITE);
2. 单值写入

像matrix,int,double这三个字段对应单值,这种情况下写法为:

storage <<
            "matrix", (Mat_<float>(2, 2) << 1, 2, 3, 4),
            "int", 1,
            "double", 2.2;

这里我重载了FileStorage的逗号运算符,可看下之前的文章:C++逗号和移位运算符简化写入操作

FileStorage& operator , (FileStorage& out, const T& data)
{
    out << data;
    return out;
}
3. 数组写入

像strings就是一个数组,里面包含多个String元素,这种类型的写法为:

storage<<"strings", "[:", "123", "456", "end", "]";//Note:重载了逗号运算符

也就是说数组卸载中括号中,只是开始的括号后面要加个冒号,这么做貌似能更好的兼容python之类的,具体的解释可以看OpenCV的注释。

4. 元组 / 嵌套数组 写入

数组保存数据类型相同的值,而元组可以保存不同数据类型的值,本质是数组里套了数组,OpenCV没有那样注释,这是我自己的理解。要是错了,请大家指正。上面表格中的features就可以看成是元组,可以这样写入

storage<<"features", "[:"; //元组依然包含在中括号中
for (int i = 0; i < 3; ++i) {
    storage << "{:",  //元素以大括号开头,!Note 重载了逗号运算符
            "x", i,
            "y", i*i,
            "lb", "[:";  //元素可以包含数组、元组嵌套
    for (int j = 0; j < 4; ++j) {
            storage << j;
    }
    storage << "]" << "}"; //一个元素写入结束,加上结尾括号
}
storage << "]"; //元组写入完毕,加上结尾括号

也就是说,元组还是包含在中括号中,只是每一个元素要包含在大括号中,同样的,为了保持兼容,我们括号开头都跟一个冒号。

FileNode读操作

我们以上面的代码为例来读其生成的内容。

1. 打开文件
FileStorage storage("file.xml", FileStorage::READ); 
2. 读单值
cout << "matrix:", storage["matrix"].mat(), '\n',        //!Note重载了逗号运算符
            "int:", storage["int"].real(), '\n',
            "double:", storage["double"].real(), '\n';

这里我同样重载了逗号运算符,具体的可以看前面的文章:C++逗号和移位运算符简化写入操作

3. 读数组

由于FileNodeIterator的存在,并且FileNode有begin()和end()方法,于是我们可以用C++11简洁的遍历方式!

cout << "strings:";
for (auto&iter : storage["strings"]) {
    cout << iter.string(), ',';  //!重载了逗号运算符
}
4. 读元组 / 嵌套数组
        cout << "features:" << endl;
        for (auto& iter : storage["features"]) {
            cout << "-  {",
                "x:", iter["x"].real(), ",",
                "y:", iter["y"].real(), ",",
                "lb:[";
            for (auto& lbIter : iter["lb"]) {
                cout << lbIter.real(), ",";
            }
            cout << "]}\n";
        }

怎么样,用C++11的方式遍历简单吧,如果数组里面还套了数组,可以继续嵌套for遍历。

完整demo代码

最后奉上完整的读写demo代码:

#include <opencv2\core.hpp>
#include <iostream>
using namespace cv;
using std::cout;
using std::endl;
using std::ostream;
template <typename T>
FileStorage& operator , (FileStorage& out, const T& data)
{
    out << data;
    return out;
}
template<typename T>
ostream& operator ,(ostream& out, const T& data)
{
    out << data;
    return out;
}
int main()
{
    //write
    {
        FileStorage storage("file.xml", FileStorage::WRITE);
        storage <<
            "matrix", (Mat_<float>(2, 2) << 1, 2, 3, 4),
            "int", 1,
            "double", 2.2,
            "strings", "[:", "123", "456", "end", "]",
            "features", "[:";
        for (int i = 0; i < 3; ++i) {
            storage << "{:",
                "x", i,
                "y", i*i,
                "lb", "[:";
            for (int j = 0; j < 4; ++j) {
                storage << j;
            }
            storage << "]" << "}";
        }
        storage << "]";
    }
    //read
    {
        FileStorage storage("file.xml", FileStorage::READ);     
        cout << "matrix:", storage["matrix"].mat(), '\n',
            "int:", storage["int"].real(), '\n',
            "double:", storage["double"].real(), '\n';
        cout << "strings:";
        for (auto&iter : storage["strings"]) {
            cout << iter.string(), ',';
        }   
        cout << endl;
        cout << "features:" << endl;
        for (auto& iter : storage["features"]) {
            cout << "-  {",
                "x:", iter["x"].real(), ",",
                "y:", iter["y"].real(), ",",
                "lb:[";
            for (auto& lbIter : iter["lb"]) {
                cout << lbIter.real(), ",";
            }
            cout << "]}\n";
        }
    }
    return 0;

}

运行结果

写文件

生成的xml

读文件

xml读取结果

Guess you like

Origin blog.csdn.net/qq_16952303/article/details/80259660