ファイルへのXML/YAML形式でのデータ保存/読み込み

23 2月 2010 Under: opencv2.x-samples

C

データの保存

#include <cv.h>

int
main (int argc, char **argv)
{
  char filename[] = "save_cv.xml";	// file name
  int i;
  int a;
  float b;
  CvMat** mat;
  CvFileStorage *cvfs;

  // (1)create sample data
  a = 10;
  b = 0.1;
  mat = (CvMat**)cvAlloc(3*sizeof(CvMat*));
  for(i=0;i<3;i++){
    mat[i] = cvCreateMat(3,3,CV_32FC1);
    cvSet(mat[i], cvScalarAll(i), NULL);
  }

  // (2)open file storage
  cvfs = cvOpenFileStorage(filename,NULL,CV_STORAGE_WRITE);

  // (3)write data to file
  cvWriteInt(cvfs, "a", a);
  cvWriteReal(cvfs, "b", b);

  cvStartWriteStruct(cvfs, "mat_array", CV_NODE_SEQ, NULL, cvAttrList(NULL, NULL));	// create node
  for(i=0; i<3; i++){
    cvWrite(cvfs, NULL, mat[i], cvAttrList(NULL, NULL));
  }
  cvEndWriteStruct(cvfs);

  // (4)close file storage
  cvReleaseFileStorage(&cvfs);

  // release mat
  for(i=0; i<3; i++){
    cvReleaseMat(mat+i);
  }
  cvFree(mat);

  return 0;
}

// (1)サンプルデータを作成します.

ここではファイルに保存するサンプルデータを生成しています.このサンプルでは,int型データが1つ,float型データが1つ,CvMat型構造体の配列(要素数3)を保存します.

// (2)ファイルストレージを開きます.

関数cvOpenFileStorageを用いて,書き込みモードでファイルを開きます. 第1引数に指定したファイル名が,拡張子”.xml”ならばXML形式で,”.yaml”,”.yml”,”.txt”などであればYAML形式で保存します.第3引数で書き込みモードであることを指定します.

// (3)データをファイルへ書きこみます.

(2)で開いたファイルに対し,データを書きこみます.OpenCVにおけるデータの保存は,tree型の階層構造をとります.この例では,トップノードの下にint型データaとfloat型データbを,またトップノードの下に”mat_array”というノードを作成し,その下にCvMat型配列matのそれぞれの要素を格納しています. int型のデータに対しては関数cvWriteInt,float等の浮動小数点型データに対しては関数cvWriteRealを使用します. どちらの関数も第1引数にファイルストレージへのポインタ,第2引数に保存するデータの名前,第3引数に実際に保存するデータを指定します.

新しくノードを作成するには関数cvStartWriteStructを使用します.第1引数はファイルストレージへのポインタ,第2引数は作成するノードの名前,第3引数は”CV_NODE_SEQ”が指定されたときはノードの下の要素は名前を持たずに連続的に保存され,”CV_NODE_MAP”の時はノードの下の各要素は名前を持ちます.今回は配列の保存なので”CV_NODE_SEQ”を指定しました.cvStartWriteStructで作成されたノードは,ノード内にデータの書き込みが終了したら必ずcvEndWriteStructで閉じる必要があります.ここで作成した”mat_array”ノードの下に関数cvWriteを使ってデータの書き込みを行います.cvWriteはOpenCVで定義された構造体を保存するための関数です.

// (4)ファイルストレージを閉じます.

関数cvOpenFileStorageを用いて開いたファイルを,関数cvReleaseFileStorageを用いて閉じます.

データの読み込み

#include <stdio.h>
#include <cv.h>

int
main (int argc, char **argv)
{
  char filename[] = "save_cv.xml";	// file name
  int i,j,k;
  CvFileStorage *cvfs;
  CvFileNode *node, *fn;
  CvSeq *s;
  int total;

  // (1)memory space for loading data
  int a;
  float b;
  CvMat** mat = (CvMat**)cvAlloc(3*sizeof(CvMat*));

  // (2)open file storage
  cvfs = cvOpenFileStorage(filename, NULL, CV_STORAGE_READ);

  // (3)read data from file storage
  node = cvGetFileNodeByName(cvfs, NULL, "");	// Get Top Node

  a = cvReadIntByName(cvfs, node, "a", 0);
  b = cvReadRealByName(cvfs, node, "b", 0);

  fn = cvGetFileNodeByName(cvfs,node,"mat_array");

  s = fn->data.seq;
  total = s->total;
  for(i=0;i<total;i++){
    mat[i] = (CvMat*)cvRead(cvfs,(CvFileNode*)cvGetSeqElem(s,i), NULL);
  }

  // (4)close file storage
  cvReleaseFileStorage(&cvfs);

  // (5)print loaded data
  printf("a:%d\n", a);
  printf("b:%f\n", b);
  for(i=0; i<3; i++){
    printf("mat%d:\n",i);
    for(j=0;j<mat[i]->rows;j++){
      for(k=0;k<mat[i]->cols;k++){
	printf("%f,",cvmGet(mat[i],j,k));
      }
      printf("\n");
    }
  }

  // release mat
  for(i=0; i<3; i++){
    cvReleaseMat(mat+i);
  }
  cvFree(mat);

  return 0;
}

// (1)データを読み込むデータ領域を確保します.

ここでは先程保存したファイルを読み込み,そのデータを保存するための領域を確保しています.

// (2)ファイルストレージを開きます.

関数cvOpenFileStorageを用いて,読み込みモードでファイルを開きます.第3引数で読み込みモードであることを指定します.

// (3)データをファイルから読みこみます.

(2)で開いたファイルから,データを読みこみます.まずcvGetFileNodeByNameでファイルに格納されたtree型データ構造のトップノードを取得します.第1引数が(2)で取得したファイルストレージへのポインタ,第2引数が親ノードへのポインタでここではトップノードなのでNULLを指定,第3引数はノードの名前(ここでは特に名前をつけていないため空文字列)を指定します.データの読み込みは int型のデータに対しては関数cvReadIntByName,float等の浮動小数点型データに対しては関数cvReadRealByNameを使用します. どちらの関数も第1引数にファイルストレージへのポインタ,第2引数にノードへのポインタ,第3引数に保存時に付与したデータの名前を指定します.

次に関数cvGetFileNodeByNameを使用して”mat_array”ノードを取得します.第2引数には親ノードであるトップノードのポインタを指定します.保存時に”CV_NODE_SEQ”と指定したノードはCvSeq構造体としてデータを取得することで,それぞれの要素にアクセスすることが可能になります.

// (4)ファイルストレージを閉じます.

関数cvOpenFileStorageを用いて開いたファイルを,関数cvReleaseFileStorageを用いて閉じます.

// (5)データを出力します.

データが正しく読み取れたかの確認のために,データを標準出力で表示します.

C++

データの保存

#include <cv.h>

using namespace std;

int
main (int argc, char **argv)
{
  char filename[] = "save_cv.xml";	// file name

  // (1)create sample data
  int a = 10;
  float b = 0.1;

  cv::Mat mat[3];
  for(int i=0; i<3; i++){
    mat[i].create(3,3,CV_32FC1);
    mat[i].setTo(cv::Scalar(i));
  }

  // (2)open file storage
  cv::FileStorage	cvfs(filename,CV_STORAGE_WRITE);

  // (3)write data to file
  cv::write(cvfs,"a",a);
  cv::write(cvfs,"b",b);
  cv::WriteStructContext ws(cvfs, "mat_array", CV_NODE_SEQ);	// create node
  for(int i=0; i<3; i++){
    cv::write(cvfs,"",mat[i]);
  }

  return 0;
}

// (1)サンプルデータを作成します.

ここではファイルに保存するサンプルデータを生成しています.このサンプルでは,int型データが1つ,float型データが1つ,cv::Matクラス配列(要素数3)に格納したデータを保存します.

// (2)ファイルストレージを開きます.

関数cv::FileStorageクラスのインスタンスを書き込みモードで生成することでファイルを開きます.このインスタンスのデストラクタが呼び出されたときに,自動的にファイルは閉じられます.第1引数に指定したファイル名が,拡張子”.xml”ならばXML形式で,”.yaml”,”.yml”,”.txt”などであればYAML形式で保存します.第2引数で書き込みモードであることを指定します.

// (3)データをファイルへ書きこみます.

(2)で開いたファイルに対し,データを書きこみます.OpenCVにおけるデータの保存は,tree型の階層構造をとります.この例では,トップノードの下にint型データaとfloat型データbを,またトップノードの下に”mat_array”というノードを作成し,その下にcv::Mat型配列のそれぞれの要素を格納しています. C++インターフェースでのデータ保存は,全て関数cv::writeを使用します. 第1引数にファイルストレージへのポインタ,第2引数に保存するデータの名前,第3引数に実際に保存するデータを指定します.

C++インターフェースで新しくノードを作成するにはcv::WriteStructContextクラスのインスタンスを生成します.コンストラクタの第1引数はファイルストレージへのポインタ,第2引数は作成するノードの名前,第3引数は”CV_NODE_SEQ”が指定されたときはノードの下の要素は名前を持たずに連続的に保存され,”CV_NODE_MAP”の時はノードの下の各要素は名前を持ちます.今回は配列の保存なので”CV_NODE_SEQ”を指定しました.ここで作成されたノードは,インスタンスwsのデストラクタが呼び出された時に自動的に閉じられます.ここで作成した”mat_array”ノードの下に関数cv::writeを使ってMat配列データの書き込みを行っています.

データの読み込み

#include <iostream>
#include <cv.h>

using namespace std;

int
main (int argc, char **argv)
{
  char filename[] = "save_cv.xml";	// file name
  int i,j,k;

  // (1)memory space for loading data
  int a;
  float b;
  cv::Mat mat[3];

  // (2)open file storage
  cv::FileStorage cvfs(filename,CV_STORAGE_READ);

  // (3)read data from file storage
  cv::FileNode node(cvfs.fs, NULL);	// Get Top Node
  a = node["a"];
  b = node["b"];
  cv::FileNode fn = node[string("mat_array")];
	
  for(i=0;i<fn.size();i++){
    cv::read(fn[i], mat[i]);
  }

  // (4)print loaded data
  cout << "a:" << a << endl;
  cout << "b:" << b << endl;

  for(i=0; i<3; i++){
    cout << "mat" << i << ":" << endl;
    for(j=0;j<mat[i].rows;j++){
      for(k=0;k<mat[i].cols;k++){
	cout << mat[i].at<float>(j,k) << ",";
      }
      cout << endl;
    }
  }

  return 0;
}

// (1)データを読み込むデータ領域を確保します.

ここでは先程保存したファイルを読み込み,そのデータを保存するための領域を確保しています.

// (2)ファイルストレージを開きます.

関数cv::FileStorageクラスのインスタンスを読み込みモードで生成することでファイルを開きます.このインスタンスのデストラクタが呼び出されたときに,自動的にファイルは閉じられます.第1引数に指定したファイル名,第2引数で読み込みモードであることを指定します.

// (3)データをファイルから読みこみます.

(2)で開いたファイルから,データを読みこみます.まずcv::FileNodeインスタンスを生成することで,ファイルに格納されたtree型データのトップノードを取得します.第1引数が(2)で生成したcv::FileStorageインスタンスのメンバであるCvFileStorage構造体へのポインタ,第2引数が親ノードへのポインタでここではトップノードなのでNULLを指定します.この例のようにノードからint型やfloat型のデータや子ノードを読み込む場合はcv::FileNodeのインスタンスにデータ名やノード名を添字で指定することで読み込むことが可能です.また,OpenCVのC++インターフェースで定義されたクラスは関数cv::readで読み込むことが可能です.

// (4)データを出力します.

データが正しく読み取れたかの確認のために,データを標準出力で表示します.

実行結果例

生成したXMLファイル

<?xml version="1.0"?>
<opencv_storage>
<a>10</a>
<b>0.1000000014901161</b>
<mat_array>
  <_ type_id="opencv-matrix">
    <rows>3</rows>
    <cols>3</cols>
    <dt>f</dt>
    <data>
      0. 0. 0. 0. 0. 0. 0. 0. 0.</data></_>
  <_ type_id="opencv-matrix">
    <rows>3</rows>
    <cols>3</cols>
    <dt>f</dt>
    <data>
      1. 1. 1. 1. 1. 1. 1. 1. 1.</data></_>
  <_ type_id="opencv-matrix">
    <rows>3</rows>
    <cols>3</cols>
    <dt>f</dt>
    <data>
      2. 2. 2. 2. 2. 2. 2. 2. 2.</data></_></mat_array>
</opencv_storage>

読み込んだデータの出力結果

a:10
b:0.100000
mat0:
0.000000,0.000000,0.000000,
0.000000,0.000000,0.000000,
0.000000,0.000000,0.000000,
mat1:
1.000000,1.000000,1.000000,
1.000000,1.000000,1.000000,
1.000000,1.000000,1.000000,
mat2:
2.000000,2.000000,2.000000,
2.000000,2.000000,2.000000,
2.000000,2.000000,2.000000,