1. 程式人生 > 實用技巧 >OpenCV 讀寫xml和yml檔案

OpenCV 讀寫xml和yml檔案

有時候我們處理完影象後需要儲存一下資料到檔案上,以供下一步的處理。一個比較廣泛的需求場景就是:我們對一幅影象進行特徵提取之後,需要把特徵點資訊儲存到檔案上,以供後面的機器學習分類操作。那麼如果遇到這樣的場景,我們有什麼好方法,搭建這類的小型資料庫檔案?我第一時間想到的是把這些資料全寫到檔案上,下次我們需要這些資料就把他們從檔案裡讀出來就好了。

其實更好的辦法是使用xml和yml,因為他們更具有可讀性,簡直就是為儲存資料結構而生的好方法!OpenCV提供了很好用的讀寫xml/yml的類,我們只要掌握其讀寫要領,很容易就可以實現這個小型資料庫。

xml/yml的寫操作

如何將我們的資料寫入檔案儲存下來?

一個簡單資料寫入的例子

下面是我們最常用的一些資料型別的寫入xml的操作。

 1 #include<opencv2\opencv.hpp>   
 2 #include<opencv2\highgui\highgui.hpp>
 3 
 4 using namespace std;
 5 using namespace cv;
 6 
 7 typedef struct
 8 {
 9     int x;
10     int y;
11     string s;
12 }test_t;
13 
14 
15 int main(int argc, char** argv)
16 { 17 FileStorage fs("test.xml", FileStorage::WRITE); //填入寫操作 18 19 //測試資料 20 int a1 = 2; 21 char a2 = -1; 22 string str = "hello sysu!"; 23 int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; 24 test_t t = { 3,4,"hi sysu" }; 25 map<string, int> m; 26 m["kobe"] = 100; 27 m["
james"] = 99; 28 m["curry"] = 98; 29 30 //寫入檔案操作,先寫標註在寫資料 31 fs << "int_data" << a1; 32 fs << "char_data" << a2; 33 fs << "string_data" << str; 34 35 //寫入陣列 36 fs <<"array_data"<< "["; //陣列開始 37 for (int i = 0; i < 10; i++) 38 { 39 fs << arr[i]; 40 } 41 fs << "]"; //陣列結束 42 43 //寫入結構體 44 fs << "struct_data" << "{"; //結構體開始 45 fs << "x" << t.x; 46 fs << "y" << t.y; 47 fs << "s" << t.s; 48 fs << "}"; //結構結束 49 50 51 //map的寫入 52 fs << "map_data" << "{"; //map的開始寫入 53 map<string, int>::iterator it = m.begin(); 54 for (; it != m.end(); it++) 55 { 56 fs << it->first << it->second; 57 } 58 fs << "}"; //map寫入結束 59 60 61 return 0; 62 }

開啟test.xml檔案,我們看到我們的資料儲存是這樣子的:

如果我們將檔案存為test.yml,即

FileStorage fs("test.yml", FileStorage::WRITE);

那麼我們最終的得到的test.yml是這樣子的:

我們還可以儲存為txt格式

FileStorage fs("test.txt", FileStorage::WRITE);

開啟看是這樣的:

我們還還可以儲存為doc檔案!

FileStorage fs("test.doc", FileStorage::WRITE); 

開啟看是這樣子的:

我們可以看出,顯然yml檔案的排版更加簡潔明瞭,xml檔案卻顯得有點冗餘和雜亂了。

一個複雜寫入的例子

在這裡舉一個簡易的學生資訊系統檔案的搭建,以熟悉一下較為複雜的資料結構的寫入檔案的操作流程。

 1 #include<opencv2\opencv.hpp>   
 2 #include<opencv2\highgui\highgui.hpp>
 3 
 4 using namespace std;
 5 using namespace cv;
 6 
 7 typedef struct
 8 {
 9     string phone_num;
10     string address;
11 }contact_t;
12 
13 typedef struct
14 {
15     string name;
16     int age;
17 }parents_t;
18 
19 typedef struct
20 {
21     string name;
22     int age;
23     int id;
24     contact_t contact_ways;
25     parents_t parents[2];
26 }student_t;
27 
28 int main(int argc, char** argv)
29 {
30 
31     FileStorage fs("student.xml", FileStorage::WRITE); //填入寫操作
32 
33     student_t st[3]; 
34     memset(st, 0, sizeof(st));
35 
36     //測試資料填入
37     st[0].name = "Kobe";
38     st[0].age = 21;
39     st[0].id = 1;
40     st[0].contact_ways.address = "1st building";
41     st[0].contact_ways.phone_num = "123";
42     st[0].parents[0].name = "dad";
43     st[0].parents[1].name = "mum";
44     st[0].parents[0].age = 40;
45     st[0].parents[1].age = 39;
46 
47     st[1].name = "James";
48     st[1].age = 20;
49     st[1].id = 2;
50     st[1].contact_ways.address = "2st building";
51     st[1].contact_ways.phone_num = "12223";
52     st[1].parents[0].name = "daddy";
53     st[1].parents[1].name = "mumy";
54     st[1].parents[0].age = 44;
55     st[1].parents[1].age = 38;
56 
57 
58     fs << "student" << "["; //結構體陣列的開始
59     for (int i = 0; i < 3; i++)
60     {
61         fs <<"{"; //結構體的開始
62         fs << "name" << st[i].name;
63         fs << "age" << st[i].age;
64         fs << "id" << st[i].id;
65 
66         fs << "contact_ways" << "{";  //巢狀結構體的開始
67         fs << "phone_number" << st[i].contact_ways.phone_num;
68         fs << "address" << st[i].contact_ways.address;
69         fs << "}";  //結構體結束
70 
71         fs << "parents"<<"[";  //巢狀結構體陣列開始
72         for (int j = 0; j < 2; j++)
73         {
74             fs << "{";
75             fs << "name" << st[i].parents[j].name;
76             fs << "age" << st[i].parents[j].age;
77             fs << "}";
78         }
79         fs << "]";  //巢狀結構體陣列結束
80 
81         fs << "}"; //結構體結束
82 
83     }
84     fs << "]"; // 結構體陣列結束
85 
86     return 0;
87 }

開啟student.xml檔案,如下

 1 <?xml version="1.0"?>
 2 <opencv_storage>
 3 <student>
 4   <_>
 5     <name>Kobe</name>
 6     <age>21</age>
 7     <id>1</id>
 8     <contact_ways>
 9       <phone_number>"123"</phone_number>
10       <address>"1st building"</address></contact_ways>
11     <parents>
12       <_>
13         <name>dad</name>
14         <age>40</age></_>
15       <_>
16         <name>mum</name>
17         <age>39</age></_></parents></_>
18   <_>
19     <name>James</name>
20     <age>20</age>
21     <id>2</id>
22     <contact_ways>
23       <phone_number>"12223"</phone_number>
24       <address>"2st building"</address></contact_ways>
25     <parents>
26       <_>
27         <name>daddy</name>
28         <age>44</age></_>
29       <_>
30         <name>mumy</name>
31         <age>38</age></_></parents></_>
32   <_>
33     <name>""</name>
34     <age>0</age>
35     <id>0</id>
36     <contact_ways>
37       <phone_number>""</phone_number>
38       <address>""</address></contact_ways>
39     <parents>
40       <_>
41         <name>""</name>
42         <age>0</age></_>
43       <_>
44         <name>""</name>
45         <age>0</age></_></parents></_></student>
46 </opencv_storage>

若儲存的是yml檔案,開啟如下:

 1 %YAML:1.0
 2 student:
 3    -
 4       name: Kobe
 5       age: 21
 6       id: 1
 7       contact_ways:
 8          phone_number: "123"
 9          address: "1st building"
10       parents:
11          -
12             name: dad
13             age: 40
14          -
15             name: mum
16             age: 39
17    -
18       name: James
19       age: 20
20       id: 2
21       contact_ways:
22          phone_number: "12223"
23          address: "2st building"
24       parents:
25          -
26             name: daddy
27             age: 44
28          -
29             name: mumy
30             age: 38
31    -
32       name: ""
33       age: 0
34       id: 0
35       contact_ways:
36          phone_number: ""
37          address: ""
38       parents:
39          -
40             name: ""
41             age: 0
42          -
43             name: ""
44             age: 0

xml/yml的讀操作

我們的資料已經穩妥地寫入檔案儲存下來了,接下來我們想從該檔案中讀取出我們的資料,該如何操作呢?我們繼續以上述的例子資料為例,講解讀操作。

一個簡單讀入的例子

我們舉個簡單例子,讀入上面提到test.xml的資料。

 1 #include<opencv2\opencv.hpp>   
 2 #include<opencv2\highgui\highgui.hpp>
 3 
 4 using namespace std;
 5 using namespace cv;
 6 
 7 typedef struct
 8 {
 9     int x;
10     int y;
11     string s;
12 }test_t;
13 
14 int a1;
15 int a2;
16 string str;
17 int arr[10];
18 test_t t;
19 map<string, int> m;
20 
21 //打印出學生資料,來驗證讀取檔案資訊是否成功
22 void data_info_dump()
23 {
24     cout << "a1:" << a1 << endl;
25     cout << "a2:" << a2 << endl;
26     cout << "str:" << str << endl;
27     cout << "t.x:" << t.x << endl;
28     cout << "t.y:" << t.y << endl;
29     cout << "t.s:" << t.s << endl;
30     cout << "curry:" << m["curry"] << endl;
31     cout << "kobe:" << m["kobe"] << endl;
32     cout << "james:" << m["james"] << endl;
33     for (int i = 0; i < 10; i++)
34     {
35         cout << arr[i] << endl;
36     }
37 }
38 
39 int main(int argc, char** argv)
40 {
41     FileStorage fs("test.xml", FileStorage::READ); //填入讀操作
42     a1 = (int)fs["int_data"];
43     a2 = (int)fs["char_data"];
44     str = (string)fs["string_data"];
45 
46     //讀入陣列
47     FileNode arr_node = fs["array_data"];
48     FileNodeIterator fni = arr_node.begin(); 
49     FileNodeIterator fniEnd = arr_node.end();
50     int count = 0;
51     for (; fni != fniEnd; fni++)
52     {
53         arr[count++] = (int)(*fni);
54     }
55 
56     //讀入map
57     FileNode map_node = fs["map_data"];
58     m["curry"] = (int)map_node["curry"];
59     m["james"] = (int)map_node["james"];
60     m["kobe"] = (int)map_node["kobe"];
61 
62     //讀入結構體
63     FileNode struct_node = fs["struct_data"];
64     t.x = (int)struct_node["x"];
65     t.y = (int)struct_node["y"];
66     t.s = (string)struct_node["s"];
67 
68     data_info_dump();
69 
70     return 0;
71 }

列印如下:

一個複雜讀入的例子

我們以讀取上面所提到的student.xml為例,說明如何讀取一個xml檔案資料到記憶體。

 1 #include<opencv2\opencv.hpp>   
 2 #include<opencv2\highgui\highgui.hpp>
 3 
 4 using namespace std;
 5 using namespace cv;
 6 
 7 typedef struct
 8 {
 9     string phone_num;
10     string address;
11 }contact_t;
12 
13 typedef struct
14 {
15     string name;
16     int age;
17 }parents_t;
18 
19 typedef struct
20 {
21     string name;
22     int age;
23     int id;
24     contact_t contact_ways;
25     parents_t parents[2];
26 }student_t;
27 
28 student_t st[3];
29 
30 //打印出學生資料,來驗證讀取檔案資訊是否成功
31 void stu_info_dump()
32 {
33     for (int i = 0; i < 3; i++)
34     {
35         printf("第%d個學生\n",i+1);
36         cout << "name:" << st[i].name << endl;
37         cout << "id:" << st[i].id << endl;
38         cout << "age:" << st[i].age << endl;
39         cout << "contact address:" << st[i].contact_ways.address << endl;
40         cout << "contact number:" << st[i].contact_ways.phone_num << endl;
41         cout << "father name:" << st[i].parents[0].name << endl;
42         cout << "father age:" << st[i].parents[0].age << endl;
43         cout << "mother name:" << st[i].parents[1].name << endl;
44         cout << "mother age:" << st[i].parents[1].age << endl;
45         printf("\n\n");
46     }
47 }
48 
49 int main(int argc, char** argv)
50 {
51 
52     FileStorage fs("student.xml", FileStorage::READ); //填入讀操作
53 
54     memset(st, 0, sizeof(st));
55 
56     FileNode student_node = fs["student"];//讀取根節點
57     FileNodeIterator fni = student_node.begin(); //獲取結構體陣列迭代器
58     FileNodeIterator fniEnd = student_node.end();
59     int count = 0;
60     for (; fni != fniEnd; fni++)//遍歷  
61     {
62 
63         st[count].name = (string)(*fni)["name"];
64         st[count].id = (int)(*fni)["id"];
65         st[count].age = (int)(*fni)["age"];
66     
67         //contact結構體內容
68         FileNode contact = (*fni)["contact_ways"];
69         st[count].contact_ways.address = (string)contact["address"];
70         st[count].contact_ways.phone_num = (string)contact["phone_number"];
71 
72         //parents結構體陣列內容
73         FileNode parents = (*fni)["parents"];
74         FileNodeIterator fni2 = parents.begin(); //獲取結構體陣列迭代器
75         FileNodeIterator fniEnd2 = parents.end();
76         int count2 = 0;
77         for (; fni2 != fniEnd2; fni2++)//遍歷  
78         {
79             st[count].parents[count2].name = (string)(*fni2)["name"];
80             st[count].parents[count2].age = (int)(*fni2)["age"];
81             count2++;
82         }
83 
84         count++;
85     }
86 
87 
88     stu_info_dump();
89 
90     return 0;
91 }

列印如下,這表明xml的資料已經成功讀入記憶體了。