OpenCVのファイルストレージでマップのキーを取得する
マップはキーとバリューで構成されるけども、どんなキーがあるのかを知る術が無い(OpenCV1.X系の場合)。
多分内部的にはキー値のデータはどこかに持っているはずなんで、色々調べた。
リファレンスによるとCvFileNodeの各データ型へのポインタが共用体で宣言されている。
http://opencv.jp/opencv-1.0.0/document/opencvref_cxcore_file.html
/* ファイルストレージの基本要素 - スカラー,またはコレクション */ typedef struct CvFileNode { int tag; struct CvTypeInfo* info; /* 型情報(ユーザ定義オブジェクト用,そうでない場合は0) */ union { double f; /* 浮動小数点のスカラー値 */ int i; /* 整数のスカラー値 */ CvString str; /* 文字列 */ CvSeq* seq; /* シーケンス(整列されたファイルノードのコレクション) */ struct CvMap* map; /* マップ(名前付けされたファイルノードのコレクション) */ } data; } CvFileNode;
struct CvMap が怪しいが、これに騙されてはいけない。ソースをちゃんと参照すると違う型になっている。
opencv/inclue/opencv/extypes.h (Line:1745)
typedef struct CvGenericHash CvFileNodeHash; /* Basic element of the file storage - scalar or collection: */ typedef struct CvFileNode { int tag; struct CvTypeInfo* info; /* type information (only for user-defined object, for others it is 0) */ union { double f; /* scalar floating-point number */ int i; /* scalar integer number */ CvString str; /* text string */ CvSeq* seq; /* sequence (ordered collection of file nodes) */ CvFileNodeHash* map; /* map (collection of named file nodes) */ } data; } CvFileNode;
本当に怪しいのは struct CvMap ではなくて CvFileNodeHash
typedef元である struct CvGenericHash は、ファイルストレージ関連の実装ファイルにあった。
cxcore/cxpersistence.cpp (Line:144)
typedef struct CvGenericHash { CV_SET_FIELDS() int tab_size; void** table; } CvGenericHash;
セットで宣言されていてデータは void** table にある。
データの取り方はセットの上位データのシーケンスでいけるはず。どのデータ型で取り出すかが問題だけど、cxpersistence.cpp を色々見てみると CvFileMapNode* として取り出すみたい。
cxcore/cxpersistence.cpp (Line:154)
typedef struct CvFileMapNode { CvFileNode value; const CvStringHashNode* key; struct CvFileMapNode* next; } CvFileMapNode;
キーとバリューそのままの名前のフィールドがある。
opencv/cxtypes.h (Line:1735)
/* All the keys (names) of elements in the readed file storage are stored in the hash to speed up the lookup operations: */ typedef struct CvStringHashNode { unsigned hashval; CvString str; struct CvStringHashNode* next; } CvStringHashNode;
出た。(const CvStringHashNode*)->str がキーっぽい。
まとめると、CvFileNode がマップタイプの場合、(CvGenericHash *CvFileNode.data.map)->table にデータがある。
それを CvFileMapNode* として取り出して key->str を参照すればよさそう。
ただし、CvGenericHash と CvFileMapNode は隠された構造体なので、自前で明示的に宣言してあげる必要がある。
それらをふまえて マップのキーと値を表示するプログラム
#include <iostream> #include <string> #include <vector> #include <opencv/cv.h> #include <opencv/highgui.h> using namespace std; typedef struct CvGenericHash { CV_SET_FIELDS() int tab_size; void** table; } CvGenericHash; typedef struct CvFileMapNode { CvFileNode value; const CvStringHashNode* key; struct CvFileMapNode* next; } CvFileMapNode; vector<string> getKeys(const CvFileNode *node) { vector<string> result; if (CV_NODE_IS_MAP(node->tag)) { CvGenericHash *map = (CvGenericHash*)node->data.map; int n = map->active_count; result.resize(n); for (int i = 0; i < n; i++) { CvFileMapNode *p = CV_GET_SEQ_ELEM(CvFileMapNode, map, i); result.at(i) = string(p->key->str.ptr); } } return result; } int main(int argc, const char *argv[]) { const char *filename = "sample.xml"; CvFileStorage *fs = cvOpenFileStorage(filename, NULL, CV_STORAGE_READ); CvFileNode *node = cvGetRootFileNode(fs); vector<string> keys = getKeys(node); typedef vector<string>::iterator I; for (I i = keys.begin(); i != keys.end(); i++) { const char *key = i->c_str(); const char *value = cvReadStringByName(fs, node, key); cout << key << " = " << value << endl; } cvReleaseFileStorage(&fs); return 0; }
OpenCV2.X系では既存のメソッドだけでもっと簡単に取得できます。
じゃあ、そっち使えよという...
#include <iostream> #include <string> #include <vector> #include <opencv/cv.h> #include <opencv/highgui.h> using namespace std; vector<string> getKeys(const cv::FileNode *node) { vector<string> result; if (node->isMap()) { result.reserve(node->size()); cv::FileNodeIterator iter = node->begin(); while (iter != node->end()) { result.push_back((*iter).name()); iter++; } } return result; } int main(int argc, const char *argv[]) { const char *filename = "sample.xml"; cv::FileStorage fs(filename, cv::FileStorage::READ); cv::FileNode node = fs.root(); vector<string> keys = getKeys(&node); typedef vector<string>::iterator I; for (I i = keys.begin(); i != keys.end(); i++) { const char *key = i->c_str(); const char *value = string(node[key]).c_str(); cout << key << " = " << value << endl; } return 0; }
<?xml version="1.0"?> <opencv_storage> <C>".c"</C> <Perl>".pl"</Perl> <Ruby>".rb"</Ruby> <Python>".py"</Python> <Objective-C>".m"</Objective-C> </opencv_storage>