目录
一、FileStorage类
1.1 FileStorage类说明
1.2 FileStorage类写入说明
1.3 FileStorage类读取说明
二、FileStorage类应用示例
2.1 应用代码
2.2 工程组织(Makefile)
2.3 编译及测试
一、FileStorage类
1.1 FileStorage类说明
FileStorage类在opencv2\core\persistence.hpp中定义:
namespace cv {
//...
class CV_EXPORTS_W FileStorage
{
//...
};
}
FileStorage类支持XML(.xml,<http://www.w3c.org/XML>)、YAML(.yml or .yaml,<http://www.yaml.org>)、JSON(.json,<http://www.json.org/>)格式的文件读写。XML使用嵌套标记来表示层次结构,而YAML则使用缩进(类似于Python编程语言)。
XML:
@code{.xml}
<?xml version="1.0">
<opencv_storage>
<A type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>f</dt>
<data>1. 0. 0. 0. 1. 0. 0. 0. 1.</data>
</A>
</opencv_storage>
@endcode
YAML:
@code{.yaml}
%YAML:1.0
A: !!opencv-matrix
rows: 3
cols: 3
dt: f
data: [ 1., 0., 0., 0., 1., 0., 0., 0., 1.]
@endcode
FileStorage类支持默认构造或指定文件名的构造方式:
CV_WRAP FileStorage();
CV_WRAP FileStorage(const String& filename, int flags, const String& encoding=String());
如果采用默认构造时,需要稍后使用open函数打开指定文件:
CV_WRAP virtual bool open(const String& filename, int flags, const String& encoding=String());
一旦成功打开了想要写入的文件,便可以像对标准输出流输出数据一样使用操作符cv::FileStorage::operator<<()进行写入操作,或cv::FileStorage::operator>>()进行读取操作。可以以这种简单的方式写入,是因为函数内部为调用开发者完成了许多复杂的工作。
cv::FileStorage支持读写、追加内容等文件操作,通过内置的枚举值Mode来明确的,需要进行flags指定,flags可以是多个枚举值的并集,例如READ|WRITE。同时还内置了操作状态State,在cv::FileStorage类进行写入数据操作(operator <<)时需要进行cv::FileStorage对象的状态判断:
//! file storage mode
enum Mode
{
READ = 0, //!< value, open the file for reading
WRITE = 1, //!< value, open the file for writing
APPEND = 2, //!< value, open the file for appending
MEMORY = 4, /**< flag, read data from source or write data to the internal buffer (which is
returned by FileStorage::release) */
FORMAT_MASK = (7<<3), //!< mask for format flags
FORMAT_AUTO = 0, //!< flag, auto format
FORMAT_XML = (1<<3), //!< flag, XML format
FORMAT_YAML = (2<<3), //!< flag, YAML format
FORMAT_JSON = (3<<3), //!< flag, JSON format
BASE64 = 64, //!< flag, write rawdata in Base64 by default. (consider using WRITE_BASE64)
WRITE_BASE64 = BASE64 | WRITE, //!< flag, enable both WRITE and BASE64
};
enum State
{
UNDEFINED = 0,
VALUE_EXPECTED = 1,
NAME_EXPECTED = 2,
INSIDE_MAP = 4
};
1.2 FileStorage类写入说明
cv::FileStorage内部数据的存储主要有两种形式,“mapping”(键/值对)和“sequence”(一系列未命名的条目)。在最顶层,所写入的数据都在一个mapping中,在该mapping中,可以放置其他的mappings或者sequences,甚至在mapping中继续放入mapping等,只要愿意。
myFileStorage <<"someInteger"<< 27;// save an array
myFileStorage <<"anArray"<<cv::Mat::eye(3,3,CV_32F);// save an integer
如果要创建一个序列条目,首先你得为它提供一个string类型的名字,接下来才是序列数据。条目内容可以是数字(整型或浮点型等),一个字符串或者别的OpenCV数据类型。
如果想要创建一个新的mapping或者sequence,可以使用特殊符号{(用于mapping)或者[(用于sequence)。一旦开始创建,就可以为其添加元素,最终以}或者]分别结束一个mapping或者sequence。
myFileStorage <<"theCat"<<"{";
myFileStorage <<"fur"<<"gray"<<"eyes"<<"green"<<"weightLbs"<< 16;
myFileStorage <<"}";
一旦完成创建一个mapping,需要按顺序输入条目名以及对应的值,像你在最顶层的mapping完成的工作一样。如果创建的是sequence,只需要一个接一个地输入元素即可,直到sequence结束。
myFileStorage<<"theTeam"<<"[";
myFileStorage <<"eddie"<<"tom"<<"scott";
myFileStorage <<"]";
一旦完成写工作,便可以使用成员函数cv::FileStorage::release()关闭该文件。
1.3 FileStorage类读取说明
FileStorage类在使用操作符cv::FileStorage::operator>>()进行读取操作时,实际返回时FileNode类的实例对象。
FileNode类同样定义在opencv2\core\persistence.hpp中:
namespace cv {
//...
class CV_EXPORTS_W_SIMPLE FileNode
{
//...
};
}
当成功构建一个cv::FileNode对象之后,便可以利用它来完成许多工作。如果它直接表示一个实际的对象(或者一个数字或者字符串),你就可以直接使用重载操作符cv::FileNode::operator>>(),将它的值加载到对应类型的变量之中。
cv::Mat anArray;
myFileStorage["calibrationMatrix"]>> anArray;
cv::FileNode对象同样支持直接赋值给一些基本数据类型。 cv::FileNode类支持的数据类型如下:
//! type of the file storage node
enum
{
NONE = 0, //!< empty node
INT = 1, //!< an integer
REAL = 2, //!< floating-point number
FLOAT = REAL, //!< synonym or REAL
STR = 3, //!< text string in UTF-8 encoding
STRING = STR, //!< synonym for STR
SEQ = 4, //!< sequence
MAP = 5, //!< mapping
TYPE_MASK = 7,
FLOW = 8, //!< compact representation of a sequence or mapping. Used only by YAML writer
UNIFORM = 8, //!< if set, means that all the collection elements are numbers of the same type (real's or int's).
//!< UNIFORM is used only when reading FileStorage; FLOW is used only when writing. So they share the same bit
EMPTY = 16, //!< empty structure (sequence or mapping)
NAMED = 32 //!< the node has a name (i.e. it is element of a mapping).
};
通过cv::FileNode对象输出操作符获取数据和采用赋值操作符获取数据是等价的:
int aNumber;
myFileStorage["someInteger"]>> aNumber;
与下面这种方式等价:
int aNumber;
aNumber =(int)myFileStorage["someInteger"];
针对cv::FileNode类,还提供了一种标准的STL表示法, 即FileNodeIterator,node.begin(),node.end()表示序列的开始和结束,存储在node中,也可以通过operator ++ ()或operator ++ (int)进行移动。
namespace cv {
//...
class CV_EXPORTS FileNodeIterator
{
//...
};
}
二、FileStorage类应用示例
2.1 应用代码
在opencv2\core\persistence.hpp中,还给出了FileStorage类写入及读取.yml文件的示例代码,本文将在基于该示例代码上,编写一个完整的案例工程,先创建.yml格式文件并写入内容,再通过一个新的FileStorage实例对象读取该文件内存,并解析打印显示相关内容;
先创建一个目录文件file_storage,在该文件目录下,创建文件main.cpp和Makefile文件,其中main.cpp如下,通过fileCreateAndSave函数创建 一个tets.yml文件并写入数据,再通过fileReadAndShow函数读取该文件内容并打印输出显示。其中main.cpp源码如下:
#include <opencv2/opencv.hpp>
#include <time.h>
#include <iostream>
void fileCreateAndSave()
{
cv::FileStorage fs("test.yml",cv::FileStorage::WRITE);
fs <<"frameCount"<< 5;
time_t rawtime; time(&rawtime);fs<<"calibrationDate"<< asctime(localtime(&rawtime));
cv::Mat cameraMatrix =(
cv::Mat_<double>(3,3)
<<1000,0,320,0,1000,240,0,0,1
);
cv::Mat distCoeffs =(
cv::Mat_<double>(5,1)
<<0.1,0.01,-0.001,0,0
);
fs <<"cameraMatrix"<< cameraMatrix<<"distCoeffs"<< distCoeffs;
fs <<"features"<<"[";
for( int i=0; i<3;i++)
{
int x = rand()% 640;
int y = rand()% 480;
uchar lbp = rand()% 256;
fs<<"{:"<<"x"<<x<<"y"<<y<<"lbp"<<"[:";
for( int j=0;j<8;j++)
fs <<((lbp >>j)&1);
fs<<"]"<<"}";
}
fs<<"]";
fs.release();
}
void fileReadAndShow()
{
cv::FileStorage fs2("test.yml",cv::FileStorage::READ);
// first method: use (type) operator on FileNode.
int frameCount =(int)fs2["frameCount"];
// second method: use cv::FileNode::operator >>
//
std::string date;
fs2["calibrationDate"] >> date;
cv::Mat cameraMatrix2, distCoeffs2;
fs2["cameraMatrix"] >> cameraMatrix2;
fs2["distCoeffs"] >> distCoeffs2;
std::cout <<"frameCount:" << frameCount <<std::endl
<<"calibration date:" << date <<std::endl
<<"camera matrix:" << cameraMatrix2<<std::endl
<<"distortion coeffs:" << distCoeffs2 <<std::endl;
cv::FileNode features = fs2["features"];
cv::FileNodeIterator it = features.begin(), it_end = features.end();
int idx=0;
std::vector<uchar> lbpval;
// iterate through a sequence using FileNodeIterator
for(; it != it_end;++it,idx++)
{
std::cout <<"feature #"<<idx<<":";
std::cout <<"x="<<(int)(*it)["x"]<<",y="<<(int)(*it)["y"]<<",lbp:(";
//(Note: easily read numerical arrays using FileNode >> std::vector.)
//
(*it)["lbp"]>>lbpval;
for( int i=0; i<(int)lbpval.size(); i++)
std::cout <<""<<(int)lbpval[i];
std::cout <<")"<<std::endl;
}
fs2.release();
}
int main( int argc,char* argv[])
{
fileCreateAndSave();
fileReadAndShow();
return 0;
};
2.2 工程组织(Makefile)
工程组织Makefile文件如下(本文是采用win下MinGW方式编译的,如何搭建opencv库+MinGW编译的请参考本专栏的opencv库安装编译博文,C/C++开发,win下OpenCV+MinGW编译环境搭建_搭建mingw编译环境_py_free-物联智能的博客-CSDN博客)。
#/bin/sh
CX= g++
BIN := ./
TARGET := fileStorage.exe
FLAGS := -std=c++11 -static
SRCDIR := ./
#INCLUDES
INCLUDEDIR := -I"../../opencv_MinGW/include"
staticDir := ../../opencv_MinGW/x64/mingw/staticlib/
LIBDIR := -L $(staticDir) -lopencv_world460 -lade -lIlmImf -lquirc -lzlib \
-llibjpeg-turbo -llibopenjp2 -llibpng -llibprotobuf -llibtiff -llibwebp \
-lgdi32 -lComDlg32 -lOleAut32 -lOle32 -luuid
source := $(wildcard $(SRCDIR)/*.cpp)
$(TARGET) :
$(CX) $(FLAGS) $(INCLUDEDIR) $(source) -o $(BIN)/$(TARGET) $(LIBDIR)
clean:
rm $(BIN)/$(TARGET)
2.3 编译及测试
进入file_storage目录,make -j*编译案例,如下:
运行程序如下:
输出的test.yml文件如下:
%YAML:1.0
---
frameCount: 5
calibrationDate: "Sun Jul 2 16:49:48 2023\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:41, y:227, lbp:[ 0, 1, 1, 1, 1, 1, 0, 1 ] }
- { x:260, y:449, lbp:[ 0, 0, 1, 1, 0, 1, 1, 0 ] }
- { x:598, y:78, lbp:[ 0, 1, 0, 0, 1, 0, 1, 0 ] }