データの読み込み
■ データの読み込み
OpenCVでは,cvWriteなどの関数によって出力されたデータを読み込む関数が複数用意されている. 単純なマップの場合は,関数cvGetFileNodeByName()でノードを取得し,関数cvRead()で読み込みを行う. シーケンスの場合は,CvSeqReaderを利用してデータを走査する. その場合は,関数cvReadIntByName()によって直接値を得る,あるいは関数cvGetHashedNode()によって得られるユニークな値のポインタを利用して値を得る,などの方法を利用する.サンプル
IplImage構造体情報の読み込み cvRead, cvGetFileNodeByName
IplImage構造体の情報をファイルから読み込む
サンプルコード
#include <cv.h> #include <highgui.h> int main (int argc, char *argv[]) { IplImage *color_img, *gray_img; CvRect roi_color, roi_gray; CvFileStorage *fs; CvFileNode *node; // (1)ファイルを読み込む if (argc != 2 || (fs = cvOpenFileStorage (argv[1], 0, CV_STORAGE_READ)) == 0) return -1; node = cvGetFileNodeByName (fs, NULL, "color_img"); color_img = (IplImage *) cvRead (fs, node); node = cvGetFileNodeByName (fs, NULL, "gray_img"); gray_img = (IplImage *) cvRead (fs, node); cvReleaseFileStorage (&fs); // (2)ROI情報を取得し矩形を描いた後,解放 roi_color = cvGetImageROI (color_img); roi_gray = cvGetImageROI (gray_img); cvResetImageROI (color_img); cvResetImageROI (gray_img); cvRectangle (color_img, cvPoint (roi_color.x, roi_color.y), cvPoint (roi_color.x + roi_color.width, roi_color.y + roi_color.height), CV_RGB (255, 0, 0)); cvRectangle (gray_img, cvPoint (roi_gray.x, roi_gray.y), cvPoint (roi_gray.x + roi_gray.width, roi_gray.y + roi_gray.height), cvScalar (0)); // (3)画像を描画 cvNamedWindow ("Color Image", CV_WINDOW_AUTOSIZE); cvShowImage ("Color Image", color_img); cvNamedWindow ("Grayscale Image", CV_WINDOW_AUTOSIZE); cvShowImage ("Grayscale Image", gray_img); cvWaitKey (0); cvDestroyWindow ("Color Image"); cvDestroyWindow ("Grayscale Image"); cvReleaseImage (&color_img); cvReleaseImage (&gray_img); return 0; }
// (1)ファイルを読み込む
コマンド引数で指定されたファイルをオープンし,関数cvOpenFileStorage()によりファイルストレージポインタを得る.
関数cvGetFileNodeByName()により,"color_img"と,"gray_img"という名前からノードを取得し,
関数cvRead()によりデータを読み込む.
// (2)ROI情報を取得し矩形を描いた後,解放
関数cvGetImageROI()により,読み込んだIplImage構造体からROIの情報を抽出し,その矩形を描く.
また,そのために画像のROIを解放しておく.
// (3)画像を描画
実際に,ファイルから読み込まれた画像を表示し,何かキーが押されるまで待つ.
実行結果例
ここで読み込まれているファイルは,IplImage構造体情報の保存で保存されたxmlファイルである.マップのシーケンスを読み込む cvGetHashedKey, cvGetFileNode
二つのエントリを持つマップのシーケンスをファイルから読み込む.これは,リファレンスマニュアル内のサンプルとほぼ同一である.
サンプルコード
#include <cv.h> #include <stdio.h> int main (int argc, char **argv) { // (1)ファイルストレージのオープン,ハッシュドキーの計算,シーケンスノードの取得 CvFileStorage *fs = cvOpenFileStorage ("sequence.yml", 0, CV_STORAGE_READ); CvStringHashNode *x_key = cvGetHashedKey (fs, "x", -1, 1); CvStringHashNode *y_key = cvGetHashedKey (fs, "y", -1, 1); CvFileNode *points = cvGetFileNodeByName (fs, 0, "points"); // (2)シーケンスリーダを初期化,各ノードを順次取得 if (CV_NODE_IS_SEQ (points->tag)) { CvSeq *seq = points->data.seq; int i, total = seq->total; CvSeqReader reader; cvStartReadSeq (seq, &reader, 0); for (i = 0; i < total; i++) { CvFileNode *pt = (CvFileNode *) reader.ptr; #if 1 // (3)高速バージョン CvFileNode *xnode = cvGetFileNode (fs, pt, x_key, 0); CvFileNode *ynode = cvGetFileNode (fs, pt, y_key, 0); assert (xnode && CV_NODE_IS_INT (xnode->tag) && ynode && CV_NODE_IS_INT (ynode->tag)); int x = xnode->data.i; /* あるいは x = cvReadInt( xnode, 0 ); */ int y = ynode->data.i; /* あるいは y = cvReadInt( ynode, 0 ); */ #elif 1 // (4)低速バージョン.x_keyとy_keyを使わない CvFileNode *xnode = cvGetFileNodeByName (fs, pt, "x"); CvFileNode *ynode = cvGetFileNodeByName (fs, pt, "y"); assert (xnode && CV_NODE_IS_INT (xnode->tag) && ynode && CV_NODE_IS_INT (ynode->tag)); int x = xnode->data.i; /* あるいは x = cvReadInt( xnode, 0 ); */ int y = ynode->data.i; /* あるいは y = cvReadInt( ynode, 0 ); */ #else // (5)さらに低速だが,使いやすいバージョン int x = cvReadIntByName (fs, pt, "x", 0); int y = cvReadIntByName (fs, pt, "y", 0); #endif // (6)データを表示し,次のシーケンスノードを取得 CV_NEXT_SEQ_ELEM (seq->elem_size, reader); printf ("%d: (%d, %d)\n", i, x, y); } } cvReleaseFileStorage (&fs); return 0; }
// (1)ファイルストレージのオープン,ハッシュドキーの計算,シーケンスノードの取得
最初に,ファイル名を指定してファイルストレージをオープンする.
また,各シーケンスの二つのエントリのキーである"x","y"に対するユニークな値を
関数cvGetHashedKey()により計算して,CvStringHashNode型のポインタを取得する.
ここで,関数cvGetHashedKey()の第3引数は,キーの名前の長さであるので,
ここでは,"1"を指定しても良いが,0以下の値を指定することで自動的に文字列の長さが計算される.
オープンされたファイルノードから,"points"という名前を持つシーケンスノードのポインタを取得する.
// (2)シーケンスリーダを初期化,各ノードを順次取得
関数cvStartReadSeq()を用いて,シーケンスリーダによる連続読み出し処理を初期化し,順次ノードを読み込む.
ノードの取得は,初期化されたシーケンスリーダが指すノード(ptrメンバ)をキャストすることで行われる.
また,これらの処理の前に,あらかじめ取得したファイルノードの種類がシーケンスであることを確認するために,
マクロCV_NODE_IS_SEQ()を利用している.
ノードのメンバtagを利用して,そのノードの種類を知るマクロは,CV_NODE_IS_*という形で,cxtypes.h内で定義されている.
// (3)高速バージョン
あらかじめ計算されたCvStringHashNode型のポインタを利用して,"x","y"の名前を持つノードを取得する.
これは,キーの文字列の内容を比較するのではなく,ユニークなポインタを比較することによりノードを求めるため,
後述の関数cvGetFieleNodeByName()と比べると若干高速に動作する.
また,取得したノードの値を直接参照する方が,関数cvReadInt()を利用して値を読み出すよりも若干高速である.
// (4)低速バージョン.x_keyとy_keyを使わない
関数cvGetFieleNodeByName()によりノードを取得する事以外は,前述の例と同様.
// (5)さらに低速だが,使いやすいバージョン
関数cvReadIntByName()は,その内部で関数cvGetFileNodeByNameと,関数cvReadInt()を呼び出している.
関数cvReadInt()の呼び出し分のオーバヘッドがかかるため,上述の例よりもさらに少しだけ低速になる.
// (6)データを表示し,次のシーケンスノードを取得
上述のいずれかの方法で取得された値を表示する.
また,
マクロCV_NEXT_SEQ_ELEM()により,シーケンスリーダの指すノードを要素のサイズ分だけ進めて(あるいはブロックを切替えて),
次のノードを取得する.