OpenCV 4.12.0
開源計算機視覺
載入中...
搜尋中...
無匹配項
使用 XML / YAML / JSON 檔案進行檔案輸入和輸出

上一個教程: 離散傅立葉變換
下一個教程: 如何使用 OpenCV parallel_for_ 並行化你的程式碼

原始作者Bernát Gábor
相容性OpenCV >= 3.0

目標

你將找到以下問題的答案

  • 如何使用 YAML、XML 或 JSON 檔案在 OpenCV 中向檔案列印和讀取文字條目?
  • 如何對 OpenCV 資料結構執行相同的操作?
  • 如何為你的自定義資料結構完成此操作?
  • 如何使用 OpenCV 資料結構,例如 cv::FileStoragecv::FileNodecv::FileNodeIterator

原始碼

解釋

這裡我們只討論 XML、YAML 和 JSON 檔案輸入。你的輸出(及其相應的輸入)檔案可能只有這些副檔名之一以及由此產生的結構。你可以序列化兩種資料結構:對映(如 STL map 和 Python 字典)和元素序列(如 STL vector)。它們之間的區別在於,在對映中,每個元素都有一個唯一的名稱,你可以透過該名稱訪問它。對於序列,你需要遍歷它們以查詢特定項。

  1. XML/YAML/JSON 檔案開啟和關閉。 在向此類檔案寫入任何內容之前,你需要開啟它,並在結束時關閉它。OpenCV 中的 XML/YAML/JSON 資料結構是 cv::FileStorage。要指定此結構繫結到硬碟上的檔案,你可以使用其建構函式或此結構的 open() 函式。

    無論你使用哪種方式,第二個引數都是一個常量,指定你可以對它們執行的操作型別:WRITE(寫入)、READ(讀取)或 APPEND(追加)。檔名稱中指定的副檔名也決定了將使用的輸出格式。如果指定 .xml.gz 等副檔名,輸出甚至可以被壓縮。

    cv::FileStorage 物件被銷燬時,檔案會自動關閉。但是,你可以透過使用 release 函式顯式呼叫它。

  2. 文字和數字的輸入和輸出。 在 C++ 中,資料結構使用 STL 庫中的 << 輸出運算子。在 Python 中,則使用 cv::FileStorage::write()。要輸出任何型別的資料結構,我們首先需要指定其名稱。在 C++ 中,我們只需簡單地將名稱推送到流中即可。在 Python 中,寫入函式的第一個引數是名稱。對於基本型別,你可以接著列印值。 讀取很簡單,透過 [] 運算子定址並進行型別轉換操作,或者透過 >> 運算子讀取。在 Python 中,我們使用 getNode() 進行定址,並使用 real()。
  3. OpenCV 資料結構的輸入/輸出。 這些資料結構的輸入/輸出行為與基本的 C++ 和 Python 型別完全相同。
  4. 向量(陣列)和關聯對映的輸入/輸出。 如前所述,我們也可以輸出對映和序列(陣列、向量)。同樣,我們首先列印變數的名稱,然後必須指定輸出是序列還是對映。

    對於序列,在第一個元素之前列印“[”字元,在最後一個元素之後列印“]”字元。在 Python 中,呼叫 FileStorage.startWriteStruct(structure_name, struct_type),其中 struct_typecv2.FileNode_MAPcv2.FileNode_SEQ,以開始寫入結構。呼叫 FileStorage.endWriteStruct() 以完成結構。

    對於對映,操作相同,但現在我們使用“{”和“}”分隔符。

    要從這些結構中讀取資料,我們使用 cv::FileNodecv::FileNodeIterator 資料結構。cv::FileStorage 類的 [] 運算子(或 Python 中的 getNode() 函式)返回 cv::FileNode 資料型別。如果節點是序列,我們可以使用 cv::FileNodeIterator 遍歷各項。在 Python 中,可以使用 at() 函式訪問序列的元素,size() 函式返回序列的長度。

    對於對映,你可以再次使用 [] 運算子(Python 中的 at() 函式)來訪問給定項(或 >> 運算子)。

  5. 讀取和寫入自己的資料結構。 假設你有一個數據結構,例如

    在 C++ 中,可以透過在類內部和外部新增讀寫函式來透過 OpenCV I/O XML/YAML 介面(就像 OpenCV 資料結構一樣)序列化它。在 Python 中,可以透過在類內部實現讀寫函式來接近這個目標。對於內部部分:

    在這裡你可以觀察到,在讀取部分,我們定義瞭如果使用者嘗試讀取一個不存在的節點時會發生什麼。在這種情況下,我們只返回預設初始化值,但一個更詳細的解決方案是返回例如物件 ID 的負一值。

    一旦你添加了這四個函式,就可以使用 >> 運算子進行寫入,使用 << 運算子進行讀取(或者 Python 中定義的輸入/輸出函式)。

    或者嘗試讀取一個不存在的資料。

結果

我們主要只是打印出定義的數字。在你的控制檯螢幕上你可以看到:

Write Done.
Reading
100image1.jpg
Awesomeness
baboon.jpg
Two 2; One 1
R = [1, 0, 0;
0, 1, 0;
0, 0, 1]
T = [0; 0; 0]
MyData =
{ id = mydata1234, X = 3.14159, A = 97}
Attempt to read NonExisting (should initialize the data structure with its default).
NonExisting =
{ id = , X = 0, A = 0}
Tip: Open up output.xml with a text editor to see the serialized data.

然而,更有趣的是你在輸出 XML 檔案中可能看到的內容:

<?xml version="1.0"?>
<opencv_storage>
<iterationNr>100</iterationNr>
<strings>
image1.jpg Awesomeness baboon.jpg</strings>
<Mapping>
<One>1</One>
<Two>2</Two></Mapping>
<R type_id="opencv-matrix">
<rows>>3</rows>
<cols>>3</cols>
<dt>u</dt>
<data>
1 0 0 0 1 0 0 0 1</data></R>
<T type_id="opencv-matrix">
<rows>>3</rows>
<cols>1</cols>
<dt>d</dt>
<data>
0. 0. 0.</data></T>
<MyData>
<A>97</A>
<X>3.1415926535897931e+000</X>
<id>mydata1234</id></MyData>
</opencv_storage>

或者 YAML 檔案:

%YAML:1.0
iterationNr: 100
strings
- "image1.jpg"
- Awesomeness
- "baboon.jpg"
Mapping
One: 1
Two: 2
R: !!opencv-matrix
rows: 3
cols: 3
dt: u
data: [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]
T: !!opencv-matrix
rows: 3
cols: 1
dt: d
data: [ 0., 0., 0. ]
MyData
A: 97
X: 3.1415926535897931e+000
id: mydata1234

你可以在此處在 YouTube 上觀看此內容的執行時例項。