OpenCV 讀寫xml和yml檔案
阿新 • • 發佈:2020-12-23
有時候我們處理完影象後需要儲存一下資料到檔案上,以供下一步的處理。一個比較廣泛的需求場景就是:我們對一幅影象進行特徵提取之後,需要把特徵點資訊儲存到檔案上,以供後面的機器學習分類操作。那麼如果遇到這樣的場景,我們有什麼好方法,搭建這類的小型資料庫檔案?我第一時間想到的是把這些資料全寫到檔案上,下次我們需要這些資料就把他們從檔案裡讀出來就好了。
其實更好的辦法是使用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的資料已經成功讀入記憶體了。