在OpenCV中,採用FileStorage類進行資料持久化,可以採用XML或YAML格式儲存資料。
將資料寫入XML或YAML檔案,可採用以下步驟:
1、建立FileStorage物件。可以呼叫建構函式 FileStorage::FileStorage(),並傳入檔名引數;或者呼叫預設建構函式,然後呼叫 FileStorage::open()。
2、利用過載輸出操作符<<,在operations.hpp中,輸出操作符<<為模板函式:
template<typename _Tp> static inline FileStorage& operator << (FileStorage& fs, const _Tp& value) { if( !fs.isOpened() ) return fs; if( fs.state == FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP ) CV_Error( CV_StsError, "No element name has been given" ); write( fs, fs.elname, value ); if( fs.state & FileStorage::INSIDE_MAP ) fs.state = FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP; return fs; }
3、呼叫FileStorage::release(),關閉檔案,並釋放所有記憶體。
FileStorage在core.hpp定義如下:
class CV_EXPORTS_W FileStorage { public: //! file storage mode enum { READ=0, //! read mode WRITE=1, //! write mode APPEND=2, //! append mode MEMORY=4, FORMAT_MASK=(7<<3), FORMAT_AUTO=0, FORMAT_XML=(1<<3), FORMAT_YAML=(2<<3) }; enum { UNDEFINED=0, VALUE_EXPECTED=1, NAME_EXPECTED=2, INSIDE_MAP=4 }; //! the default constructor CV_WRAP FileStorage(); //! the full constructor that opens file storage for reading or writing CV_WRAP FileStorage(const string& source, int flags, const string& encoding=string()); //! the constructor that takes pointer to the C FileStorage structure FileStorage(CvFileStorage* fs); //! the destructor. calls release() virtual ~FileStorage(); //! opens file storage for reading or writing. The previous storage is closed with release() CV_WRAP virtual bool open(const string& filename, int flags, const string& encoding=string()); //! returns true if the object is associated with currently opened file. CV_WRAP virtual bool isOpened() const; //! closes the file and releases all the memory buffers CV_WRAP virtual void release(); //! closes the file, releases all the memory buffers and returns the text string CV_WRAP string releaseAndGetString(); //! returns the first element of the top-level mapping CV_WRAP FileNode getFirstTopLevelNode() const; //! returns the top-level mapping. YAML supports multiple streams CV_WRAP FileNode root(int streamidx=0) const; //! returns the specified element of the top-level mapping FileNode operator[](const string& nodename) const; //! returns the specified element of the top-level mapping CV_WRAP FileNode operator[](const char* nodename) const; //! returns pointer to the underlying C FileStorage structure CvFileStorage* operator *() { return fs; } //! returns pointer to the underlying C FileStorage structure const CvFileStorage* operator *() const { return fs; } //! writes one or more numbers of the specified format to the currently written structure void writeRaw( const string& fmt, const uchar* vec, size_t len ); //! writes the registered C structure (CvMat, CvMatND, CvSeq). See cvWrite() void writeObj( const string& name, const void* obj ); //! returns the normalized object name for the specified file name static string getDefaultObjectName(const string& filename); Ptr<CvFileStorage> fs; //!< the underlying C FileStorage structure string elname; //!< the currently written element vector<char> structs; //!< the stack of written structures int state; //!< the writer state };
從XML或YAML檔案中讀取資料,可採用以下步驟:
1、利用建構函式 FileStorage::FileStorage()或方法 FileStorage::open()開啟檔案,這個過程包含解析檔案,並將檔案節點和資料存入記憶體中。
2、讀取節點資料。利用下標操作符 FileStorage::operator [](), FileNode::operator []()或 FileNodeIterator。
3、呼叫 FileStorage::release() 關閉檔案。
OpenCV利用 FileNode 儲存XML或YAML檔案中的每個節點,並用於讀寫。FileNode在 core.hpp 檔案中定義如下:
class CV_EXPORTS_W_SIMPLE FileNode { public: //! 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 REF=4, //!< integer of size size_t. Typically used for storing complex dynamic structures where some elements reference the others SEQ=5, //!< sequence MAP=6, //!< mapping TYPE_MASK=7, FLOW=8, //!< compact representation of a sequence or mapping. Used only by YAML writer USER=16, //!< a registered object (e.g. a matrix) EMPTY=32, //!< empty structure (sequence or mapping) NAMED=64 //!< the node has a name (i.e. it is element of a mapping) }; //! the default constructor CV_WRAP FileNode(); //! the full constructor wrapping CvFileNode structure. FileNode(const CvFileStorage* fs, const CvFileNode* node); //! the copy constructor FileNode(const FileNode& node); //! returns element of a mapping node FileNode operator[](const string& nodename) const; //! returns element of a mapping node CV_WRAP FileNode operator[](const char* nodename) const; //! returns element of a sequence node CV_WRAP FileNode operator[](int i) const; //! returns type of the node CV_WRAP int type() const; //! returns true if the node is empty CV_WRAP bool empty() const; //! returns true if the node is a "none" object CV_WRAP bool isNone() const; //! returns true if the node is a sequence CV_WRAP bool isSeq() const; //! returns true if the node is a mapping CV_WRAP bool isMap() const; //! returns true if the node is an integer CV_WRAP bool isInt() const; //! returns true if the node is a floating-point number CV_WRAP bool isReal() const; //! returns true if the node is a text string CV_WRAP bool isString() const; //! returns true if the node has a name CV_WRAP bool isNamed() const; //! returns the node name or an empty string if the node is nameless CV_WRAP string name() const; //! returns the number of elements in the node, if it is a sequence or mapping, or 1 otherwise. CV_WRAP size_t size() const; //! returns the node content as an integer. If the node stores floating-point number, it is rounded. operator int() const; //! returns the node content as float operator float() const; //! returns the node content as double operator double() const; //! returns the node content as text string operator string() const; //! returns pointer to the underlying file node CvFileNode* operator *(); //! returns pointer to the underlying file node const CvFileNode* operator* () const; //! returns iterator pointing to the first node element FileNodeIterator begin() const; //! returns iterator pointing to the element following the last node element FileNodeIterator end() const; //! reads node elements to the buffer with the specified format void readRaw( const string& fmt, uchar* vec, size_t len ) const; //! reads the registered object and returns pointer to it void* readObj() const; // do not use wrapper pointer classes for better efficiency const CvFileStorage* fs; const CvFileNode* node; };
寫XML、YAML檔案時主要注意的幾點:
1、XML和YAML可以巢狀的兩種集合型別:對映(mappings)、序列(sequences)。對映集合類似STL中的std::map和Python中的字典,序列集合類似STL中std::vector和 Python的序列;
2、當寫入對映結構的資料時,節點的值(value)緊跟著節點的鍵(key);當寫入序列結構的資料時,一個一個寫入即可;
3、寫入對映結構資料時,格式如下:fs << "{" << element_key << element_value << "}"
4、寫入序列結構資料時,格式如下:fs << "[" << element_value << …… << "]"
5、寫入對映、序列巢狀的資料時,以"{:"代替"{","[:" 代替 "["。
示例程式碼如下:
#include <QtCore/QCoreApplication> #include <opencv2/core/core.hpp> #include <time.h> #include <iostream> void writeYAML() { cv::FileStorage fs("../file/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; unsigned char 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 readYAML() { cv::FileStorage fs2("../file/test.yml",cv::FileStorage::READ); int frameCount = (int)fs2["frameCount"]; std::string date; fs2["calibrationDate"] >> date; cv::Mat cameraMatrix2, distCoeffs2; fs2["cameraMatrix"] >> cameraMatrix2; fs2["distCoeffs"] >> distCoeffs2; std::cout << "frameCount: " << frameCount << std::endl; std::cout << "calibration date: " << date << std::endl; std::cout << "camera matrix: " << cameraMatrix2 << std::endl; std::cout << "distortion coeffs: " << distCoeffs2 << std::endl; cv::FileNode features = fs2["features"]; cv::FileNodeIterator it = features.begin(), it_end = features.end(); int idx = 0; std::vector<unsigned char> lbpval; for(; it != it_end; ++it, idx++) { std::cout << "features #" << idx << ": "; std::cout << "x=" << (int)(*it)["x"] << ", y=" << (int)(*it)["y"] << ", lbp: ("; (*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[]) { // QCoreApplication a(argc, argv); // return a.exec(); // writeYAML(); readYAML(); return 0; }
寫入的YAML檔案如下:
讀取YAML檔案輸出如下: