4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

OpenCV(C++)チュートリアル:XMLとYAMLを使ったファイル入出力

4
Last updated at Posted at 2018-12-23

公式チュートリアルのFile Input and Output using XML and YAML filesの要約。
筆者の環境はdebian9

身につくこと

  • YAMLまたはXMLの読み書き
  • (同上)OpenCVのデータ構造の読み書き
  • (同上)ユーザ定義データ構造の読み書き
  • cv::FileStorage, cv::FileNode, cv::FileNodeIteratorの使用法

コード全体

上記の各入出力を実行する。引数に出力ファイル名を取る。

io.cpp
# include <opencv2/core.hpp>
# include <iostream>
# include <string>
using namespace cv;
using namespace std;
static void help(char** av)
{
    cout << endl
        << av[0] << " shows the usage of the OpenCV serialization functionality."         << endl
        << "usage: "                                                                      << endl
        <<  av[0] << " outputfile.yml.gz"                                                 << endl
        << "The output file may be either XML (xml) or YAML (yml/yaml). You can even compress it by "
        << "specifying this in its extension like xml.gz yaml.gz etc... "                  << endl
        << "With FileStorage you can serialize objects in OpenCV by using the << and >> operators" << endl
        << "For example: - create a class and have it serialized"                         << endl
        << "             - use it to read and write matrices."                            << endl;
}
class MyData
{
public:
    MyData() : A(0), X(0), id()
    {}
    explicit MyData(int) : A(97), X(CV_PI), id("mydata1234") // explicit to avoid implicit conversion
    {}
    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"];
    }
public:   // Data Members
    int A;
    double X;
    string id;
};
//OpenCVの入出力インターフェースで独自データ構造を扱うためにwriteとread関数が必要
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);
}
// This function will print our custom class to the console
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)
{
    if (ac != 2)
    {
        help(av);
        return 1;
    }
    string filename = av[1];
    { //write
        Mat R = Mat_<uchar>::eye(3, 3),
            T = Mat_<double>::zeros(3, 1);
        MyData m(1);
        FileStorage fs(filename, FileStorage::WRITE);
        fs << "iterationNr" << 100;
        fs << "strings" << "[";                              // text - string sequence
        fs << "image1.jpg" << "Awesomeness" << "../data/baboon.jpg";
        fs << "]";                                           // close sequence
        fs << "Mapping";                              // text - mapping
        fs << "{" << "One" << 1;
        fs <<        "Two" << 2 << "}";
        fs << "R" << R;                                      // cv::Mat
        fs << "T" << T;
        fs << "MyData" << m;                                // your own data structures
        fs.release();                                       // explicit close
        cout << "Write Done." << endl;
    }
    {//read
        cout << endl << "Reading: " << endl;
        FileStorage fs;
        fs.open(filename, FileStorage::READ);
        int itNr;
        //fs["iterationNr"] >> itNr;
        itNr = (int) fs["iterationNr"];
        cout << itNr;
        if (!fs.isOpened())
        {
            cerr << "Failed to open " << filename << endl;
            help(av);
            return 1;
        }
        FileNode n = fs["strings"];                         // Read string sequence - Get node
        if (n.type() != FileNode::SEQ)
        {
            cerr << "strings is not a sequence! FAIL" << endl;
            return 1;
        }
        FileNodeIterator it = n.begin(), it_end = n.end(); // Go through the node
        for (; it != it_end; ++it)
            cout << (string)*it << endl;
        n = fs["Mapping"];                                // Read mappings from a sequence
        cout << "Two  " << (int)(n["Two"]) << "; ";
        cout << "One  " << (int)(n["One"]) << endl << endl;
        MyData m;
        Mat R, T;
        fs["R"] >> R;                                      // Read cv::Mat
        fs["T"] >> T;
        fs["MyData"] >> m;                                 // Read your own structure_
        cout << endl
            << "R = " << R << endl;
        cout << "T = " << T << endl << endl;
        cout << "MyData = " << endl << m << endl << endl;
        //存在しないデータの場合デフォルト値にする
        cout << "Attempt to read NonExisting (should initialize the data structure with its default).";
        fs["NonExisting"] >> m;
        cout << endl << "NonExisting = " << endl << m << endl;
    }
    cout << endl
        << "Tip: Open up " << filename << " with a text editor to see the serialized data." << endl;
    return 0;
}


コンパイル

g++ io.cpp  -I/usr/local/include/opencv2 -I/usr/local/include/opencv -L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_highgui -lopencv_imgproc

実行

引数に適当な出力ファイル名(.xml or .yaml)

./a.out out.xml

結果

ターミナル出力:

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

説明

入力はXML,YAMLのみ考える。入出力はどちらかの拡張子が指定されていて、それぞれ対応するデータ構造を持っているとする。扱いたいデータ構造はmapping(STLのmapのようなもの)やsequence(STLのvectorのようなもの)。

1. XML/YAMLのOpen/Close

cv::FileStorageを使う。
ファイルを指定するにはコンストラクタかopen()を使う。

string filename = "I.xml";
//第一引数:ファイル名の拡張子は自動で判定される。*.xml.gz*のようにすれば圧縮も扱える。
//第二引数:操作の種類はWRITE, READ, APPENDが指定できる
FileStorage fs(filename, FileStorage::WRITE);
//...
fs.open(filename, FileStorage::READ);

ファイルはcv::FileStorageオブジェクトが破棄された時自動的に閉じられるが、明示的にもできる:

fs.release();

2. テキスト、数値の入出力

<<演算子を使う。どんなデータ型でも出力するには名前を指定する必要があり、基本型については次のようにできる。

//<iterationNr>100</iterationNr>を出力することになる
fs << "iterationNr" << 100;

読み込みは[]とキャストか、または>>演算子。

int itNr;
fs["iterationNr"] >> itNr;
itNr = (int) fs["iterationNr"];

3.OpenCVデータ構造の入出力

基本的なC++と同じ。

//cv::Mat作成
Mat R = Mat_<uchar >::eye  (3, 3),
    T = Mat_<double>::zeros(3, 1);
//書き込み
fs << "R" << R;  
fs << "T" << T;
//読み込み
fs["R"] >> R; 
fs["T"] >> T;

4. vector(配列)と連想マップ

sequenceとmapも出力できる。
最初に名前を指定して、次にsequenceとmapどちらなのか示す記号を指定する。
sequenceは要素を[]で囲む

fs << "strings" << "[";                              // text - string sequence
fs << "image1.jpg" << "Awesomeness" << "baboon.jpg";
fs << "]";                                           // close sequence

mapは{}

fs << "Mapping";                              // text - mapping
fs << "{" << "One" << 1;
fs <<        "Two" << 2 << "}";

読み込みはcv::FileNodecv::FileNodeIteratorを使う。cv::FileStorageクラスの[]演算子はcv::FileNodeデータ型を返す。返り値がシーケンシャルならcv::FileNodeIteratorでイテレートできる。

//FileNode型が返る
FileNode n = fs["strings"];
//シーケンシャルか確認 
if (n.type() != FileNode::SEQ)
{
    cerr << "strings is not a sequence! FAIL" << endl;
    return 1;
}
FileNodeIterator it = n.begin(), it_end = n.end(); 
//イテレートできる
for (; it != it_end; ++it)
    cout << (string)*it << endl;

mapについては、[]演算子(または>>)でアイテムにアクセスできる

n = fs["Mapping"];//シーケンスからマップを読み出している
cout << "Two  " << (int)(n["Two"]) << "; ";
cout << "One  " << (int)(n["One"]) << endl << endl;

5.ユーザ定義データの読み書き

次のようなデータ構造があったとする

class MyData
{
public:
      MyData() : A(0), X(0), id() {}
public:   // Data Members
   int A;
   double X;
   string id;
};

クラスの内部と外部の両方にreadwrite関数をもたせれば、OpenCVの入出力インターフェースでシリアライズできる。
次に示すのはクラス内の方。

void write(FileStorage& fs) const  
{
  fs << "{" << "A" << A << "X" << X << "id" << id << "}";
}
void read(const FileNode& node)   
{
  A = (int)node["A"];
  X = (double)node["X"];
  id = (string)node["id"];
}

クラス外の方。

void write(FileStorage& fs, const std::string&, const MyData& x)
{
    x.write(fs);
}
void read(const FileNode& node, MyData& x, const MyData& default_value = MyData())
{
    if(node.empty())
        //デフォルト値にする
        x = default_value;
    else
        x.read(node);
}

これらを追加したら、>>で書き込み、<<で読み込みできる。

MyData m(1); //ユーザ定義データ構造
fs << "MyData" << m; //mから読み込み
fs["MyData"] >> m; //mへ書き込み

存在しないものを書き込もうとすると

//NonExistingは書き込まれていない
fs["NonExisting"] >> m;
cout << endl << "NonExisting = " << endl << m << endl;
/*
Attempt to read NonExisting (should initialize the data structure with its default).
NonExisting =
{ id = , X = 0, A = 0}
デフォルト値が読み込まれる
*/
4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?