重磅乾貨,第一時間送達
經過幾個月的努力,小白終於完成了市面上第一本OpenCV 4入門書籍《從零學習OpenCV 4》。為了更讓小夥伴更早的了解最新版的OpenCV 4,小白與出版社溝通,提前在公眾號上連載部分內容,請持續關注小白。
除了圖像數據之外,有時程序中的尺寸較小的Mat類矩陣、字符串、數組等
數據也需要進行保存,這些數據通常保存成XML文件或者YAML文件。本小節中將介紹如何利用OpenCV 4中的函數將數據保存成XML文件或者YAML文件以及如何讀取這兩種文件中的數據。
XML是一種元標記語言,所謂元標記就是使用者可以根據自身需求定義自己的標記,例如可以用<age>、<color>等標記來定義數據的含義,例如用<age>24</age>來表示age數據的數值為24。XML是一種結構化的語言,通過XML語言可以知道數據之間的隸屬關係,例如<color><red>100</red><blue>150</blue></color>表示在color數據中含有兩個名為red和blue的數據,兩者的數值分別是100和150。通過標記的方式,無論以任何形式保存數據,只要文件滿足XML格式,那麼讀取出來的數據就不會出現混淆和歧義。XML文件的擴展名是「.xml」。
YMAL是一種以數據為中心的語言,通過「變量:數值」的形式來表示每個數據的數值,通過不同的縮進來表示不同數據之間的結構和隸屬關係。YMAL可讀性高,常用來表達資料序列的格式,它參考了多種語言,包括XML、C語言、Python、Perl等。YMAL文件的擴展名是「.ymal」或者「.yml」。
OpenCV 4中提供了用於生成和讀取XML文件和YMAL文件的FileStorage類,類中定義了初始化類、寫入數據和讀取數據等方法。我們在使用該FileStorage類時首先需要對其進行初始化,初始化可以理解為聲明需要操作的文件和操作類型。OpenCV 4提供了兩種初始化的方法,分別是不輸入任何參數的初始化(可以理解為只定義,並未初始化)和輸入文件名稱和操作類型的初始化。後者初始化構造函數的函數原型在代碼清單2-35中給出。
代碼清單2-35 FileStorage()函數原型
cv::FileStorage::FileStorage(const String & filename,
int flags,
const String & encoding = String()
)
表2-8 FileStorage()構造函數中對文件操作類型常用標誌及含義
標誌參數
簡記
含義
READ
0
讀取文件中的數據
WRITE
1
向文件中重新寫入數據,會覆蓋之前的數據
APPEND
2
向文件中繼續寫入數據,新數據在原數據之後
MEMORY
4
將數據寫入或者讀取到內部緩衝區
該函數是FileStorage類的構造函數,用於聲明打開的文件名稱和操作的類型。函數第一個參數是打開的文件名稱,參數是字符串類型,文件的擴展名是「.xml」、「.ymal」或者「.yml」。打開得文件可以已經存在或者未存在,但是當對文件進行讀取操作時需要是已經存在的文件。第二個參數是對文件進行的操作類型標誌,例如對文件進行讀取操作、寫入操作等,常用參數及含義在表2-8給出,由於該標誌量在FileStorage類中,因此在使用時需要加上類名作為前綴,例如「FileStorage::WRITE」。最後一個參數是文件的編碼格式,目前不支持UTF-16 XML編碼,需要使用UTF-8 XML編碼,通常情況下使用該參數的默認值即可。
打開文件後,可以通過FileStorage類中的isOpened()函數判斷是否成功打開文件,如果成功打開文件,該函數返回true,如果打開文件失敗,則該函數返回false。
FileStorage類中默認構造函數沒有任何參數,因此沒有聲明打開的文件和操作的類型,此時需要通過FileStorage類中的open()函數單獨進行聲明,該函數的函數原型在代碼清單2-36中給出。
代碼清單2-36 open()函數原型
virtual bool cv::FileStorage::open(const String & filename,
int flags,
const String & encoding = String()
)
該函數補充了默認構造函數沒有聲明打開文件的缺點,函數可以指定FileStorage類打開的文件,如果成功打開文件,則返回值為true,否則為false。函數中所有的參數及含義都與代碼清單2-35中的相同,因此這裡不再進行贅述。同樣,通過該函數打開文件後仍然可以通過FileStorage類中的isOpened()函數判斷是否成功打開文件。
打開文件後,類似C++中創建的數據流,可以通過「<<」操作符將數據寫入文件中,或者通過「>>」操作符從文件中讀取數據。除此之外,還可以通過FileStorage類中的write()函數將數據寫入文件中,該函數的函數原型在代碼清單2-37中給出。
代碼清單2-37 write()函數原型
void cv::FileStorage::write(const String & name,
int val
)
該函數能夠將不同數據類型的變量名稱和變量值寫入到文件中。該函數的第一個參數是寫入文件中的變量名稱。第二個參數是變量值,代碼清單2-37中的變量值是int類型,但是在FileStorage類中提供了write()函數的多個重載函數,分別用於實現將double、String、Mat、vector<String>類型的變量值寫入到文件中。
使用操作符向文件中寫入數據時與write()函數類似,都需要聲明變量名和變量值,例如變量名為「age」,變量值為「24」,可以通過「file<<」age」<<24」來實現。如果某個變量的數據是一個數組,可以用「[]」將屬於同一個變量的變量值標記出來,例如「file<<」age」<<「[」<<24<<25<<」]」」。如果某些變量隸屬於某個變量,可以用「{}」表示變量之間的隸屬關係,例如「file<<」age」<<「{」<<」Xiaoming」<<24<<」Wanghua」<<25<<」}」」。
讀取文件中的數據時,只需要通過變量名就可以讀取變量值。例如「file ["x"] >> xRead」是讀取變量名為x的變量值。但是,當某個變量中含有多個數據或者含有子變量時,就需要通過FileNode節點類型和迭代器FileNodeIterator進行讀取,例如某個變量的變量值是一個數組,首先需要定義一個file ["age"]的FileNode節點類型變量,之後通過迭代器遍歷其中的數據。另外一種方法可以不使用迭代器,通過在變量後邊添加「[]」地址的形式讀取數據,例如FileNode[0]表示數組變量中的第一個數據,FileNode[「Xiaoming」]表示「age」變量中的「Xiaoming」變量的數據,依次向後添加「[]」地址實現多節點數據的讀取。
為了了解如何生成和讀取XML文件和YMAL文件,在代碼清單2-38中給出了實現文件寫入和讀取的示例程序。程序中使用write()函數和「<<」操作符兩種方式向文件中寫入數據,使用迭代器和「[]」地址兩種方式從文件中讀取數據。數據的寫入和讀取方法在前面已經介紹,在代碼清單2-38中需要重點了解如何通過程序實現寫入與讀取。程序生成的XML文件和YMAL文件中的數據在圖2-10給出,讀取文件數據的結果在圖2-9給出。
代碼清單2-38 myXMLandYAML.cpp保存和讀取XML和YAML文件
1. #include <opencv2/opencv.hpp>
2. #include <iostream>
3. #include <string>
4.
5. using namespace std;
6. using namespace cv;
7.
8. int main(int argc, char** argv)
9. {
10. system("color F0");
11.
12. string fileName = "datas.yaml";
13.
14. FileStorage fwrite(fileName, FileStorage::WRITE);
15.
16.
17. Mat mat = Mat::eye(3, 3, CV_8U);
18. fwrite.write("mat", mat);
19.
20. float x = 100;
21. fwrite << "x" << x;
22.
23. String str = "Learn OpenCV 4";
24. fwrite << "str" << str;
25.
26. fwrite << "number_array" << "[" <<4<<5<<6<< "]";
27.
28. fwrite << "multi_nodes" << "{" << "month" << 8 << "day" << 28 << "year"
29. << 2019 << "time" << "[" << 0 << 1 << 2 << 3 << "]" << "}";
30.
31.
32. fwrite.release();
33.
34.
35. FileStorage fread(fileName, FileStorage::READ);
36.
37. if (!fread.isOpened())
38. {
39. cout << "打開文件失敗,請確認文件名稱是否正確!" << endl;
40. return -1;
41. }
42.
43.
44. float xRead;
45. fread["x"] >> xRead;
46. cout << "x=" << xRead << endl;
47.
48.
49. string strRead;
50. fread["str"] >> strRead;
51. cout << "str=" << strRead << endl;
52.
53.
54. FileNode fileNode = fread["number_array"];
55. cout << "number_array=[";
56.
57. for (FileNodeIterator i = fileNode.begin(); i != fileNode.end(); i++)
58. {
59. float a;
60. *i >> a;
61. cout << a<<" ";
62. }
63. cout << "]" << endl;
64.
65.
66. Mat matRead;
67. fread["mat="] >> matRead;
68. cout << "mat=" << mat << endl;
69.
70.
71. FileNode fileNode1 = fread["multi_nodes"];
72. int month = (int)fileNode1["month"];
73. int day = (int)fileNode1["day"];
74. int year = (int)fileNode1["year"];
75. cout << "multi_nodes:" << endl
76. << " month=" << month << " day=" << day << " year=" << year;
77. cout << " time=[";
78. for (int i = 0; i < 4; i++)
79. {
80. int a = (int)fileNode1["time"][i];
81. cout << a << " ";
82. }
83. cout << "]" << endl;
84.
85.
86. fread.release();
87. return 0;
88. }
圖2-9 myXMLandYAML.cpp程序文件讀取結果
圖2-10 myXMLandYAML.cpp程序生成的XML文件和YAML文件
經過幾個月的努力,市面上第一本OpenCV 4入門書籍《從零學習OpenCV 4》將於今年12月左右由人民郵電出版社發行。如果小夥伴覺得內容有幫助,希望到時候多多支持!
關注小白的小夥伴可以提前看到書中的內容,我們創建了學習交流群,歡迎各位小夥伴添加小白微信加入交流群,添加小白時請備註「學習OpenCV 4」。
覺得文章對自己有幫助的小夥伴點個「在看」