C++ OpenCV讀寫XML或YAML文件的方法詳解
前言
本節(jié)我們將認(rèn)識XML和YAML這兩種文件類型。
所謂XML,即eXtensible Markup Language,翻譯成中文為“可擴(kuò)展標(biāo)識語言”。首先,XML是一種元標(biāo)記語言。所謂元標(biāo)記,就是開發(fā)者可以根據(jù)自身需要定義自己的標(biāo)記,比如可以定義標(biāo)記<book>、<name>。任何滿足XML命名規(guī)則的名稱都可以標(biāo)記,這就向不同的應(yīng)用程序打開了的大門。此外,XML是一種語義、結(jié)構(gòu)化語言,它描述了文檔的結(jié)構(gòu)與語義。
YAML是YAML Ain’t a Markup Language(YAML不是一種置標(biāo)語言)的縮寫。YAML是一個(gè)可讀性高,用來表達(dá)資料序列的格式。它參考了其他多種語言,包括:XML、C語言、Python、Perl以及電子郵件格式RFC2822。
1.如何使用
XML和YAML是使用非常廣泛的文件格式??梢岳肵ML或者YAML格式的文件存儲(chǔ)和還原各式各樣的數(shù)據(jù)結(jié)構(gòu)。當(dāng)然,它們還可以存儲(chǔ)和載入任意復(fù)雜的數(shù)據(jù)結(jié)構(gòu),其中就包括了OpenCV相關(guān)周邊的數(shù)據(jù)結(jié)構(gòu),以及各種原始數(shù)據(jù)類型,如整數(shù)、浮點(diǎn)數(shù)和文本字符串。
我們一般使用如下過程來寫入或者讀取數(shù)據(jù)到XML或YAML文件中。
(1)實(shí)例化一個(gè)FileStorage類的對象,用默認(rèn)帶參數(shù)的構(gòu)造函數(shù)完成初始化,或者用FileStorage::open()成員函數(shù)輔助初始化。
(2)使用流操作符<<進(jìn)行文件寫入操作,或者>>進(jìn)行文件讀取操作,類似C++中的文件輸入輸出流。
(3)使用FileStorage::release()函數(shù)析構(gòu)掉FileStorage類對象,同時(shí)關(guān)閉文件。
1.1第一步:XML、YAML文件的打開
(1)準(zhǔn)備文件寫操作
FileStorage是OpenCV中XML和YAML文件的存儲(chǔ)類,封裝了所有相關(guān)的信息。它是OpenCV從文件中讀數(shù)據(jù)或向文件中寫數(shù)據(jù)時(shí)必須要使用的一個(gè)類。
此類的構(gòu)造函數(shù)為FileStorage::FileStorage,有兩個(gè)重載,如下:
C++:FileStorage::FileStorage() C++:FileStorage::FileStorage(const string& source, int flags, const string& encoding=string())
構(gòu)造函數(shù)在實(shí)際使用中,一般有兩種方法。
1)對于第二種帶參數(shù)的構(gòu)造函數(shù),進(jìn)行寫操作范例如下:
FileStorage fs("abc.xml", FileStorage::WRITE);
2)對于第一種不帶參數(shù)的構(gòu)造函數(shù),可以使用其成員函數(shù)FileStorage::open進(jìn)行數(shù)據(jù)的寫操作,范例如下:
FileStorage fs; fs.open("abc.xml",FileStorage::WRITE);
(2)準(zhǔn)備文件讀操作
上面講到的都是以FileStorage::WRITE為標(biāo)識符的寫操作,而讀操作,采用FileStorage::READ標(biāo)識符即可,相關(guān)示例代碼如下:
1)第一種方式
FileStorage fs(“abc.xml”, FileStorage::READ);
2)第二種方式
FileStorage fs; fs.open(“abc.xml”, FileStorage::READ);
另外需要注意的是,上面的這些操作示例是對XML文件為例子作演示的,而對YAML文件,操作方法是類似的,就是將XML文件換為YAML文件即可。
1.2 第二步:進(jìn)行文件讀寫操作
(1)文本和數(shù)字的輸入和輸出
定義好FileStorage類對象之后,寫入文件可以使用<<運(yùn)算符,例如:
fs<<"iterationNr"<<100;
而讀取文件,使用>>運(yùn)算符,例如:
int itNr; fs["iterationNr"]>>itNr; itNr = (int) fs["iterationNr"];
(2)OpenCV數(shù)據(jù)結(jié)構(gòu)的輸入和輸出
關(guān)于OpenCV數(shù)據(jù)結(jié)構(gòu)的輸入和輸出,和基本的C++形式相同,范例如下:
// 數(shù)據(jù)結(jié)構(gòu)的初始化 Mat R = Mat_<uchar>::eye(3,3); Mat T = Mat_<double>::zeros(3, 1); // 向Mat中寫入數(shù)據(jù) fs << "R" << R; fs << "T" << T; // 從Mat中讀取數(shù)據(jù) fs["R"] >> R; fs["T"] >> T;
1.3 第三步:vector(array)和map的輸入和輸出
對于vector結(jié)構(gòu)的輸出和輸出,要注意在第一個(gè)元素前加上”[“,在最后一個(gè)元素后加上”]“。例如:
fs << "strings"<<"["; //開始讀入string文本序列 fs << "image1.jpg" << "Awesomeness" << "baboon.jpg"; fs << "]"; //關(guān)閉序列
而對于map結(jié)構(gòu)的操作,使用的符號是”{“和”}“,例如:
fs << "Mapping";//開始讀入Mapping文本 fs << "{" << "One" << 1; fs << "Two" << 2 << "}";
讀取這些數(shù)據(jù)結(jié)構(gòu)的時(shí)候,會(huì)用到FileNode和FileNodeIterator數(shù)據(jù)結(jié)構(gòu)。對FileStorage類的“[”、“]”操作符會(huì)返回FileNode數(shù)據(jù)類型;對于一連串的node,可以使用FileNodeIterator結(jié)構(gòu),例如:
```cpp FileNode n = fs["strings"];//讀取字符串序列以得到節(jié)點(diǎn) if (n.type()!=FileNode::SEQ) { cerr << "發(fā)生錯(cuò)誤!字符串不是一個(gè)序列" << endl; return 1; } FileNodeIterator it = n.begin(),it_end = n.end(); //遍歷節(jié)點(diǎn) for(;it!=it_end;it++) cout << (string)*it << endl;
1.4 第四步:文件關(guān)閉
需要注意的是,文件關(guān)閉操作會(huì)在FileStorage類銷毀時(shí)自動(dòng)進(jìn)行,但我們也可以顯式調(diào)用其析構(gòu)函數(shù)FileStorage::release()實(shí)現(xiàn)。FileStorage::release()函數(shù)會(huì)析構(gòu)掉FileStorage類對象,同時(shí)關(guān)閉文件。
調(diào)用過程非常簡單,如下:fs.release();
2.代碼展示
2.1 寫文件
#include<opencv2/opencv.hpp> #include<time.h> using namespace cv; int main() { //初始化 FileStorage fs("test.yaml", FileStorage::WRITE); //開始文件寫入 fs << "frameCount" << 5; time_t rawtime; time(&rawtime); fs << "calibrationDate" << asctime(localtime(&rawtime));//讀取時(shí)間量 Mat cameraMatrix = (Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1); Mat distCoeffs = (Mat_<double>(5, 1) << 0.1, 0.01, -0.001, 0, 0);//畸變參數(shù) fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;//讀取Mat型cameraMatrix,distcoeffs的內(nèi)容 fs << "feature" << "["; for (int i = 0; i < 3; i++) { int x = rand() % 640; int y = rand() % 480; uchar ibp = rand() % 256; fs << "{:" << "x" << x << "y" << y << "ibp" << "[:"; for (int j = 0; j < 8; j++) fs << ((ibp >> j) & 1); fs << "]" << "}"; } fs << "]"; fs.release(); printf("完畢,請?jiān)诠こ棠夸浵虏榭次募?"); getchar(); return 0; }
上面的示例將一個(gè)整數(shù)、一個(gè)文本字符串(標(biāo)定日期)、2 個(gè)矩陣和一個(gè)自定義結(jié)構(gòu)“feature”存儲(chǔ)到 YML,其中包括特征坐標(biāo)和 LBP(局部二進(jìn)制模式)值。這是樣本的輸出:
%YAML:1.0
frameCount: 5
calibrationDate: "Fri Jun 17 14:09:29 2011\n"
cameraMatrix: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ 1000., 0., 320., 0., 1000., 240., 0., 0., 1. ]
distCoeffs: !!opencv-matrix
rows: 5
cols: 1
dt: d
data: [ 1.0000000000000001e-01, 1.0000000000000000e-02,
-1.0000000000000000e-03, 0., 0. ]
features:
- { x:167, y:49, lbp:[ 1, 0, 0, 1, 1, 0, 1, 1 ] }
- { x:298, y:130, lbp:[ 0, 0, 0, 1, 0, 0, 1, 1 ] }
- { x:344, y:158, lbp:[ 1, 1, 0, 0, 0, 0, 1, 0 ] }
作為練習(xí),您可以將上面示例中的“.yml”替換為“.xml”或“.json”,然后查看相應(yīng)的 XML 文件的外觀。
通過查看示例代碼和輸出可以注意到幾件事:
- 生成的 YAML(和 XML/JSON)由可以嵌套的異構(gòu)集合組成。有兩種類型的集合:命名集合(映射)和未命名集合(序列)。在映射中,每個(gè)元素都有一個(gè)名稱并通過名稱訪問。這類似于 C/C++ 中的std::map結(jié)構(gòu)以及 Python 中的字典。在序列中元素沒有名稱,它們通過索引訪問。這類似于 C/C++ 中的std::vector數(shù)組以及 Python 中的列表、元組。“異構(gòu)”意味著每個(gè)單一集合的元素可以有不同的類型。
- YAML/XML/JSON 中的頂級集合是一個(gè)映射。每個(gè)矩陣存儲(chǔ)為一個(gè)映射,矩陣元素存儲(chǔ)為一個(gè)序列。然后,有一個(gè)特征序列,其中每個(gè)特征都表示一個(gè)映射,以及嵌套序列中的 lbp 值。
- 當(dāng)您寫入映射(結(jié)構(gòu))時(shí),您寫入元素名稱后跟其值。當(dāng)您寫入一個(gè)序列時(shí),您只需一個(gè)一個(gè)地寫入元素。OpenCV 數(shù)據(jù)結(jié)構(gòu)(例如cv::Mat)的編寫方式與簡單的 C 數(shù)據(jù)結(jié)構(gòu)完全相同 - 使用<<運(yùn)算符。
- 要編寫映射,首先寫入特殊字符串{,然后將元素作為對 ( fs << <element_name> << <element_value>) 寫入,然后寫入結(jié)束符}。
- 要編寫一個(gè)序列,首先要編寫特殊的字符串[,然后編寫元素,然后編寫結(jié)束]。
- 在 YAML/JSON(但不是 XML)中,映射和序列可以以類似 Python 的緊湊內(nèi)聯(lián)形式編寫。在上面的示例中,矩陣元素以及每個(gè)特征,包括它的 lbp 值,都以這種內(nèi)聯(lián)形式存儲(chǔ)。要以緊湊的形式存儲(chǔ)映射/序列,請放在:開始字符之后,例如使用{:代替{和[:代替[。當(dāng)數(shù)據(jù)寫入 XML 時(shí),那些額外:的將被忽略。
2.2 讀文件
#include<opencv2/opencv.hpp> #include<time.h> using namespace cv; using namespace std; int main() { //改變consolo字體顏色 system("color 6F"); //初始化 FileStorage fs2("test.yaml", FileStorage::READ); //開始文件讀取 //法一,對FileNode操作 int frameCount2 = (int)fs2["framecount2"]; std::string date;//定義字符串 date //法二,使用FileNode運(yùn)算符>> fs2["calibrationDate"] >> date; Mat cameraMatrix2, distCoeffs2; fs2["cameraMatrix"] >> cameraMatrix2; fs2["distCoeffs"] >> distCoeffs2;//讀取 cout << "frameCount2:" << frameCount2 << endl << "calibration date:" << date << endl << "camera matrix:" << cameraMatrix2 << endl << "distortion coeffs:" << distCoeffs2 << endl; FileNode feature = fs2["feature"]; FileNodeIterator it = feature.begin(), it_end = feature.end();//定義it int idx = 0; std::vector<uchar>ibpval;//定義向量容器ibpal //使用FileNodeIterator歷遍序列(讀?。? for (; it != it_end; it++, idx++) { cout << "feature#" << idx << ":"; cout << "x=" << (int)(*it)["x"] << ",y=" << (int)(*it)["y"] << ",ibp:("; //也可以使用filenod>>std::vector操作符很容易讀取數(shù)值陣列 (*it)["ibp"] >> ibpval; for (int i = 0; i < (int)ibpval.size(); i++) cout << "" << (int)ibpval[i]; cout << ")" << endl; } fs2.release(); printf("讀取完畢,請按任意鍵結(jié)束-"); getchar(); return 0; }
2.3 完整的示例代碼
/* * filestorage_sample demonstrate the usage of the opencv serialization functionality */ #include "opencv2/core.hpp" #include <iostream> #include <string> using std::string; using std::cout; using std::endl; using std::cerr; using std::ostream; using namespace cv; static void help(char** av) { cout << "\nfilestorage_sample demonstrate the usage of the opencv serialization functionality.\n" << "usage:\n" << av[0] << " outputfile.yml.gz\n" << "\n outputfile above can have many different extensions, see below." << "\nThis program demonstrates the use of FileStorage for serialization, that is in use << and >> in OpenCV\n" << "For example, how to create a class and have it serialize, but also how to use it to read and write matrices.\n" << "FileStorage allows you to serialize to various formats specified by the file end type." << "\nYou should try using different file extensions.(e.g. yaml yml xml xml.gz yaml.gz etc...)\n" << endl; } struct MyData { MyData() : A(0), X(0), id() { } explicit MyData(int) : A(97), X(CV_PI), id("mydata1234") { } int A; double X; string id; 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"]; } }; //These write and read functions must exist as per the inline functions in operations.hpp 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); } static ostream& operator<<(ostream& out, const MyData& m){ out << "{ id = " << m.id << ", "; out << "X = " << m.X << ", "; out << "A = " << m.A << "}"; return out; } int main(int ac, char** av) { cv::CommandLineParser parser(ac, av, "{@input||}{help h ||}" ); if (parser.has("help")) { help(av); return 0; } string filename = parser.get<string>("@input"); if (filename.empty()) { help(av); return 1; } //write { FileStorage fs(filename, FileStorage::WRITE); cout << "writing images\n"; fs << "images" << "["; fs << "image1.jpg" << "myfi.png" << "baboon.jpg"; cout << "image1.jpg" << " myfi.png" << " baboon.jpg" << endl; fs << "]"; cout << "writing mats\n"; Mat R =Mat_<double>::eye(3, 3),T = Mat_<double>::zeros(3, 1); cout << "R = " << R << "\n"; cout << "T = " << T << "\n"; fs << "R" << R; fs << "T" << T; cout << "writing MyData struct\n"; MyData m(1); fs << "mdata" << m; cout << m << endl; } //read { FileStorage fs(filename, FileStorage::READ); if (!fs.isOpened()) { cerr << "failed to open " << filename << endl; help(av); return 1; } FileNode n = fs["images"]; if (n.type() != FileNode::SEQ) { cerr << "images is not a sequence! FAIL" << endl; return 1; } cout << "reading images\n"; FileNodeIterator it = n.begin(), it_end = n.end(); for (; it != it_end; ++it) { cout << (string)*it << "\n"; } Mat R, T; cout << "reading R and T" << endl; fs["R"] >> R; fs["T"] >> T; cout << "R = " << R << "\n"; cout << "T = " << T << endl; MyData m; fs["mdata"] >> m; cout << "read mdata\n"; cout << m << endl; cout << "attempting to read mdata_b\n"; //Show default behavior for empty matrix fs["mdata_b"] >> m; cout << "read mdata_b\n"; cout << m << endl; } cout << "Try opening " << filename << " to see the serialized data." << endl << endl; //read from string { cout << "Read data from string\n"; string dataString = "%YAML:1.0\n" "mdata:\n" " A: 97\n" " X: 3.1415926535897931e+00\n" " id: mydata1234\n"; MyData m; FileStorage fs(dataString, FileStorage::READ | FileStorage::MEMORY); cout << "attempting to read mdata_b from string\n"; //Show default behavior for empty matrix fs["mdata"] >> m; cout << "read mdata\n"; cout << m << endl; } //write to string { cout << "Write data to string\n"; FileStorage fs(filename, FileStorage::WRITE | FileStorage::MEMORY | FileStorage::FORMAT_YAML); cout << "writing MyData struct\n"; MyData m(1); fs << "mdata" << m; cout << m << endl; string createdString = fs.releaseAndGetString(); cout << "Created string:\n" << createdString << "\n"; } return 0; }
到此這篇關(guān)于C++ OpenCV讀寫XML或YAML文件的方法詳解的文章就介紹到這了,更多相關(guān)C++讀寫XML YAML文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言通過三步翻轉(zhuǎn)法實(shí)現(xiàn)單詞倒置詳解
這篇文章主要為大家分享了用三步翻轉(zhuǎn)法將一句話的單詞進(jìn)行倒置的方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-05-05visual?studio?2022?編譯出來的文件被刪除并監(jiān)視目錄中的文件變更(示例詳解)
這篇文章主要介紹了visual?studio?2022?編譯出來的文件被刪除?并監(jiān)視目錄中的文件變更,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08C語言函數(shù)的遞歸和調(diào)用實(shí)例分析
一個(gè)函數(shù)在它的函數(shù)體內(nèi)調(diào)用它自身稱為遞歸調(diào)用。這種函數(shù)稱為遞歸函數(shù)。C語言允許函數(shù)的遞歸調(diào)用。在遞歸調(diào)用中,主調(diào)函數(shù)又是被調(diào)函數(shù)。執(zhí)行遞歸函數(shù)將反復(fù)調(diào)用其自身,每調(diào)用一次就進(jìn)入新的一層2013-07-07