目录
一、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.]
@endcodeFileStorage类支持默认构造或指定文件名的构造方式:
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 ] }



















