OpenCV 4.13.0
Open Source Computer Vision
読み込み中...
検索中...
見つかりません
🤖 AIによる機械翻訳(非公式) — これは OpenCV 4.13.0 公式リファレンス(英語)を AI (Claude) で自動翻訳したものです。訳に誤りを含む場合があります。正確な情報は 公式英語版(原文) を参照してください。
XML / YAML / JSON ファイルによるファイル入出力

前のチュートリアル: 離散フーリエ変換
次のチュートリアル: OpenCVのparallel_for_を使ってコードを並列化する方法

原著者Bernát Gábor
互換性OpenCV >= 3.0

目的

次の疑問に対する答えが見つかる。

  • YAML、XML、JSONファイルを使って、OpenCVでテキストエントリをファイルに書き込んだり読み込んだりするにはどうすればよいか?
  • OpenCVのデータ構造に対して同じ操作を行うにはどうすればよいか?
  • 独自のデータ構造に対してはどうすればよいか?
  • cv::FileStoragecv::FileNodecv::FileNodeIterator などのOpenCVデータ構造はどのように使うのか?

ソースコード

解説

ここではXML、YAML、JSONのファイル入力についてのみ説明する。出力(およびそれに対応する入力)ファイルは、これらの拡張子のいずれか1つだけを持ち、それに応じた構造になる。シリアライズできるデータ構造には2種類ある。マッピング(STLのmapやPythonの辞書のようなもの)と要素のシーケンス(STLのvectorのようなもの)である。両者の違いは、mapではすべての要素が一意の名前を持ち、それを通じてアクセスできる点にある。シーケンスの場合は、特定の項目を取得するためにそれらをたどっていく必要がある。

  1. XML/YAML/JSONファイルのオープンとクローズ。 そのようなファイルに内容を書き込む前に、ファイルを開き、最後に閉じる必要がある。OpenCVにおけるXML/YAML/JSONデータ構造はcv::FileStorageである。この構造をハードドライブ上のどのファイルに結び付けるかを指定するには、そのコンストラクタかこのopen()関数のいずれかを使う。

    どちらを使う場合でも、2番目の引数はそれらに対して実行できる操作の種類を指定する定数である: WRITE、READ、APPEND。ファイル名に指定された拡張子は、使用される出力フォーマットも決定する。.xml.gzのような拡張子を指定すれば、出力を圧縮することさえできる。

    ファイルはcv::FileStorageオブジェクトが破棄されると自動的に閉じられる。ただし、release関数を使って明示的に呼び出すこともできる。

  2. テキストと数値の入出力。 C++では、このデータ構造はSTLライブラリの << 出力演算子を使う。Pythonでは、代わりにcv::FileStorage::write()を使う。任意の種類のデータ構造を出力するには、まずその名前を指定する必要がある。C++では、その名前をストリームにプッシュするだけでよい。Pythonでは、write関数の最初の引数が名前になる。基本型の場合は、それに続けて値を出力すればよい: 読み込みは単純なアドレッシング([] 演算子を介する)とキャスト操作、または >> 演算子を介した読み込みである。Pythonでは、getNode() でアドレス指定し real() を使う:
  3. OpenCVデータ構造の入出力。 これらは基本的なC++およびPythonの型とまったく同じように振る舞う:
  4. ベクトル(配列)と連想マップの入出力。 前述のとおり、mapやシーケンス(配列、ベクトル)も出力できる。ここでもまず変数の名前を出力し、その後に出力がシーケンスかmapのどちらであるかを指定する必要がある。

    シーケンスでは、最初の要素の前に "[" 文字を、最後の要素の後に "]" 文字を出力する。Pythonでは、FileStorage.startWriteStruct(structure_name, struct_type) を呼び出して構造の書き込みを開始する。ここで struct_typecv2.FileNode_MAP または cv2.FileNode_SEQ である。FileStorage.endWriteStruct() を呼び出して構造を終了する。

    mapでも要領は同じだが、今度は "{" と "}" の区切り文字を使う。

    これらから読み込むには、cv::FileNodecv::FileNodeIteratorのデータ構造を使う。cv::FileStorageクラスの [] 演算子(またはPythonのgetNode()関数)はcv::FileNodeデータ型を返す。ノードがシーケンスである場合は、cv::FileNodeIteratorを使って項目を反復処理できる。Pythonでは、at()関数を使ってシーケンスの要素にアドレス指定でき、size()関数はシーケンスの長さを返す。

    マップ(map)に対しては、[] 演算子(PythonではAt() 関数)を再び使って指定した要素にアクセスできる(>> 演算子も使える):

  5. 独自のデータ構造を読み書きする。 次のようなデータ構造があるとする:

    C++では、(OpenCVのデータ構造の場合と同様に)クラスの内側と外側に read 関数と write 関数を追加することで、OpenCVのI/O XML/YAMLインターフェースを通じてこれをシリアライズできる。Pythonでは、クラス内に read 関数と write 関数を実装することでこれに近いことができる。内側の部分については:

    ここでは、read セクションで、ユーザーが存在しないノードを読み込もうとした場合の動作を定義していることが分かる。この場合は単にデフォルトの初期化値を返しているが、より丁寧な解決策としては、たとえばオブジェクトIDに対してマイナス1の値を返すといった方法もある。

    これら4つの関数を追加したら、書き込みには >> 演算子を、読み込みには << 演算子を使う(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 で見ることができる。