OpenCV持久化(二)

陽光守望者發表於2013-12-28

如何利用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; 這句這麼簡單。

最後輸出結果如下:

 

 

相關文章