有时我们需要保存一些训练数据,或是相机标定结果,亦或是临时存储一些图片等等。这时,我们可以使用FileStorage加上FileNode来完成这些操作。
FileStorage这个类可以看成是纸+笔的功能,负责保存内容和写入内容;而FileNode可以看成是眼睛,读取纸上的内容。FileStorage拥有真正的内容,而FileNode仅拥有读取的功能。这两个类支持读写XML, YAML, JSON 格式文件,后缀名为.xml , .yml , .yaml , .json
假设我们现在要写入的内容是这样的:
字段 | 内容 |
---|---|
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] |
FileStorage storage("file.xml", FileStorage::WRITE);
像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;
}
像strings就是一个数组,里面包含多个String元素,这种类型的写法为:
storage<<"strings", "[:", "123", "456", "end", "]";//Note:重载了逗号运算符
也就是说数组卸载中括号中,只是开始的括号后面要加个冒号,这么做貌似能更好的兼容python之类的,具体的解释可以看OpenCV的注释。
数组保存数据类型相同的值,而元组可以保存不同数据类型的值,本质是数组里套了数组,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 << "]"; //元组写入完毕,加上结尾括号
也就是说,元组还是包含在中括号中,只是每一个元素要包含在大括号中,同样的,为了保持兼容,我们括号开头都跟一个冒号。
我们以上面的代码为例来读其生成的内容。
FileStorage storage("file.xml", FileStorage::READ);
cout << "matrix:", storage["matrix"].mat(), '\n', //!Note重载了逗号运算符
"int:", storage["int"].real(), '\n',
"double:", storage["double"].real(), '\n';
这里我同样重载了逗号运算符,具体的可以看前面的文章:C++逗号和移位运算符简化写入操作
由于FileNodeIterator的存在,并且FileNode有begin()和end()方法,于是我们可以用C++11简洁的遍历方式!
cout << "strings:";
for (auto&iter : storage["strings"]) {
cout << iter.string(), ','; //!重载了逗号运算符
}
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代码:
#include
#include
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;
}