如何利用OpenCV持久化自己的資料結構?我們來看看OpenCV中的一個例子。
MyData.hpp定義自己的資料結構MyData如下:
#ifndef MYDATA_HPP #define MYDATA_HPP #include <opencv2/core/core.hpp> #include <iostream> #include <string> using namespace std; using namespace cv; class MyData { public: MyData() : A(0), X(0), id() {} explicit MyData(int) : A(97), X(CV_PI), id("mydata1234") // explicit to avoid implicit conversion {} void write(FileStorage& fs) const //Write serialization for this class { fs << "{" << "A" << A << "X" << X << "id" << id << "}"; } void read(const FileNode& node) //Read serialization for this class { A = (int)node["A"]; X = (double)node["X"]; id = (string)node["id"]; } public: // Data Members int A; double X; string id; }; //These write and read functions must be defined for the serialization in FileStorage to work static void write(FileStorage& fs, const std::string&, const MyData& x) { x.write(fs); } static void read(const FileNode& node, MyData& x, const MyData& default_value = MyData()) { if(node.empty()) x = default_value; else x.read(node); } // This function will print our custom class to the console static ostream& operator<<(ostream& out, const MyData& m) { out << "{ id = " << m.id << ", "; out << "X = " << m.X << ", "; out << "A = " << m.A << "}"; return out; } #endif // MYDATA_HPP
注意,外部靜態函式 write 和 read 必須定義,即:
static void write(FileStorage& fs, const std::string&, const MyData& x);
static void read(const FileNode& node, MyData& x, const MyData& default_value = MyData());
原因稍後說明。
主函式main.cpp如下:
1 #include <QtCore/QCoreApplication> 2 #include "MyData.hpp" 3 4 int main(int argc, char *argv[]) 5 { 6 // QCoreApplication a(argc, argv); 7 // return a.exec(); 8 9 string filename = "../file/test.xml"; 10 { //write 11 Mat R = Mat_<uchar>::eye(3, 3), 12 T = Mat_<double>::zeros(3, 1); 13 MyData m(1); 14 15 FileStorage fs(filename, FileStorage::WRITE); 16 17 fs << "iterationNr" << 100; 18 fs << "strings" << "["; // text - string sequence 19 fs << "image1.jpg" << "Awesomeness" << "baboon.jpg"; 20 fs << "]"; // close sequence 21 22 fs << "Mapping"; // text - mapping 23 fs << "{" << "One" << 1; 24 fs << "Two" << 2 << "}"; 25 26 fs << "R" << R; // cv::Mat 27 fs << "T" << T; 28 29 fs << "MyData" << m; // your own data structures 30 31 fs.release(); // explicit close 32 cout << "Write Done." << endl; 33 } 34 35 {//read 36 cout << endl << "Reading: " << endl; 37 FileStorage fs; 38 fs.open(filename, FileStorage::READ); 39 40 int itNr; 41 //fs["iterationNr"] >> itNr; 42 itNr = (int) fs["iterationNr"]; 43 cout << itNr; 44 if (!fs.isOpened()) 45 { 46 cerr << "Failed to open " << filename << endl; 47 //help(av); 48 return 1; 49 } 50 51 FileNode n = fs["strings"]; // Read string sequence - Get node 52 if (n.type() != FileNode::SEQ) 53 { 54 cerr << "strings is not a sequence! FAIL" << endl; 55 return 1; 56 } 57 58 FileNodeIterator it = n.begin(), it_end = n.end(); // Go through the node 59 for (; it != it_end; ++it) 60 cout << (string)*it << endl; 61 62 63 n = fs["Mapping"]; // Read mappings from a sequence 64 cout << "Two " << (int)(n["Two"]) << "; "; 65 cout << "One " << (int)(n["One"]) << endl << endl; 66 67 68 MyData m; 69 Mat R, T; 70 71 fs["R"] >> R; // Read cv::Mat 72 fs["T"] >> T; 73 fs["MyData"] >> m; // Read your own structure_ 74 75 cout << endl 76 << "R = " << R << endl; 77 cout << "T = " << T << endl << endl; 78 cout << "MyData = " << endl << m << endl << endl; 79 80 //Show default behavior for non existing nodes 81 cout << "Attempt to read NonExisting (should initialize the data structure with its default)."; 82 fs["NonExisting"] >> m; 83 cout << endl << "NonExisting = " << endl << m << endl; 84 } 85 86 cout << endl 87 << "Tip: Open up " << filename << " with a text editor to see the serialized data." << endl; 88 89 return 0; 90 }
可以看到第29行: fs << "MyData" << m;
當執行到 fs << "MyData" 時,將呼叫OpenCV原始碼operations.hpp中的輸出操作符:
static inline FileStorage& operator << (FileStorage& fs, const char* str)
返回 FileStorage 的引用,繼續輸入自定義的資料m時,將呼叫operations.hpp中的模板函式:
1 template<typename _Tp> static inline FileStorage& operator << (FileStorage& fs, const _Tp& value) 2 { 3 if( !fs.isOpened() ) 4 return fs; 5 if( fs.state == FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP ) 6 CV_Error( CV_StsError, "No element name has been given" ); 7 write( fs, fs.elname, value ); 8 if( fs.state & FileStorage::INSIDE_MAP ) 9 fs.state = FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP; 10 return fs; 11 }
可以看到第7行:write( fs, fs.elname, value );
在operations.hpp中,可以看到兩個OpenCV匯出函式:
CV_EXPORTS_W void write( FileStorage& fs, const string& name, const Mat& value ); CV_EXPORTS void write( FileStorage& fs, const string& name, const SparseMat& value );
CV_EXPORTS_W void read(const FileNode& node, Mat& mat, const Mat& default_mat=Mat() ); CV_EXPORTS void read(const FileNode& node, SparseMat& mat, const SparseMat& default_mat=SparseMat() );
回到上面的問題,為什麼必須要定義外部靜態函式write和read:
static void write(FileStorage& fs, const std::string&, const MyData& x); static void read(const FileNode& node, MyData& x, const MyData& default_value = MyData());
因此,我們在MyData.hpp中定義的外部靜態函式write和read過載了OpenCV中operations.hpp的read和write函式。
所以,上述呼叫第7行:write( fs, fs.elname, value ) 將呼叫我們自己定義的 write和read 函式。然後如MyData.hpp所示,在MyData類內部定義方法實現自定義資料結構的持久化。
而對外介面用起來就像 fs << "MyData" << m; 這句這麼簡單。
最後輸出結果如下: