FileStorage
OpenCV 中的 FileStorage
類能夠讀寫硬盤中的.xml
和.yaml
文件,這里我們只討論對 .xml
的以下幾種操作:
- 寫入(
FileStorage::WRITE
,覆蓋寫) - 追加(
FileStorage::APPEND
,追加寫) - 讀取(
FileStorage::WRITE
)
FileStorage
以 FileNode
為單位存儲數據,且無法刪改某個已有 FileNode
的內容,想實現刪改功能,得自己造輪子……
寫入FileNode
FileNode
有兩種類型,seq
和 map
:
FileStorage fs("data.xml", FileStorage::WRITE);
// seq_node 是一個 seq 型的節點, 以它為父節點,存入10個數據
fs << "seq_node" << "[";
for(size_t i = 0; i < 10; ++i){
fs << i;
}
fs << "]";
// map_node 是一個 map 型節點, 以它為父節點,存入10個數據
fs << "map_node" << "{";
for(size_t i = 0; i < 10; ++i){
fs << "node_" + to_string(i) << i;
}
fs << "}";
fs.release();
通過上面這段代碼,我們可以看到 seq
和 map
這兩種類型的節點,在寫入數據時的差別:前者在子節點間,寫入一對方括號[]
, 而后者寫入花括號{}
;前者在寫入子節點的時候,無法為子節點命名,而后者可以。OpenCV 最重要的 Mat
類型在存儲時是以 map
方式寫入的。
讀入FileNode
seq
和 map
節點在讀入數據的時候,前者以索引的方式去獲得子節點,后者用子節點的名字,即一個字符串去獲得子節點(字符串為鍵,節點為值):
FileStorage fs("data.xml", FileStorage::READ);
vector<int> a, b;
// seq_node 是一個 seq 型的節點
FileNode seq_node = fs["seq_node"];
for(size_t i = 0; i < 10; ++i){
seq_node[i] >> a[i];
}
// map_node 是一個 map 型節點
FileNode map_node = fs["map_node"];
for(size_t i = 0; i < 10; ++i){
fs["node_" + to_string(i)] >> b[i];
}
fs.release();
seq
型節點既然能以索引去取子節點,那自然有人會想到,能不能用迭代器去訪問子節點呢?答案是可以。OpenCV為我們提供了FileNode的迭代器:
// seq_node 是一個 seq 型的節點
FileNode seq_node = fs["seq_node"];
FileNodeIterator it = seq_node.begin();
for(; it != seq_node.end(); ++it){
*it >> a[i];
}
自定義類型的讀寫
需要重載 write
和 read
函數:
struct MyData{
int i;
string str;
Mat I;
}
// 自定義寫入
void write(FileStorage &fs, const string &, const MyData &mydata){
fs << "{"
<< "index" << mydata.i
<< "str" << mydata.str
<< "img" << mydata.I
<< "}"
}
// 自定義讀取
void read(const FileNode &node, MyData &mydata, const MyData &default_val = MyData()){
if(node.empty()) mydata = default_val;
else {
node["index"] >> mydata.i;
node["str"] >> mydata.str;
node["img"] >> mydata.I;
}
}
本作品采用知識共享署名-非商業性使用 4.0 國際許可協議進行許可。