#stub

複数枚の画像から,各座標における最頻値を計算する方法.
主に,固定カメラで撮影されたビデオシーケンスから背景画像を計算する方法

#contents

*CvSparseMat* cvCreateSparseMat(int dimensions, const int* sizes, int type) [#ycedc219]

疎な多次元行列CvSparseMat型を生成する.画像におけるcvCreateImage,(密な)行列におけるcvCreateMat.

**引数 [#fcfa2b96]
-dimensions:int型の次元数
-sizes:各次元の長さを格納したint型の配列
-type:int型のtype.cvCreateMatと同じ

**返り値 [#ce87c193]
-生成されたCvSparseMat型のポインタ

*サンプルコード [#uf285a14]

#geshi(c++,number){{
// RGBの3次元分.各色は0-255の値をとるので,各次元の大きさを256に設定.
int dimensionSize[]            = {256, 256, 256};
const int NUMBER_OF_DIMENSIONS = 3;
IplImage *image                = cvLoadImage(inputFileName[0]);

/*---生成---*/
// ヒストグラムの集合をnewで生成
CvSparseMat **histogram = new CvSparseMat* [image->width*image->height];
for(int i = 0;i < image->width*image->height;i++){
    // ピクセルごとにヒストグラム用のCvSparseMatを作成
    // typeがCV_16UC1なので,カウントは2byte=65536回までカウント可能
    histogram[i]	= cvCreateSparseMat(NUMBER_OF_DIMENSIONS, dimensionSize, CV_16UC1);
}


// 入力画像と同じサイズの画像を用意
IplImage *background = cvCloneImage(image);
cvReleaseImage(&image);

for(int frame = 0;frame < FRAME_LENGTH;frame++){
    image = cvLoadImage(inputFileName[frame]);
    
    /*---データ格納 ---*/
    for(int y = 0;y < image->height;y++){
    for(int x = 0;x < image->width;x++){
        // RGBの値を取得
        CvScalar color = cvGet2D(image, y, x);

        // RGBによる3次元座標を構築してdimensionSizeに格納
        for(int i = 0;i < NUMBER_OF_DIMENSIONS;i++){
            dimensionSize[i] = color.val[i];
        }

        // dimensionSizeで指定した座標(色)が過去何回現れたか取得
        // 1度も現れてない場合は0が返される
        CvScalar count = cvGetND(histogram[y*image->width+x], dimensionSize);
        // カウントアップ
        count.val[0]++;
        // 格納する
        cvSetND(histogram[y*image->width+x], dimensionSize, count); 
    }
    }

    cvReleaseImage(&image);
}

for(int y = 0;y < image->height;y++){
for(int x = 0;x < image->width;x++){
    // 内部のリスト構造のデータを手繰るためのイタレータ
    CvSparseMatIterator mat_iterator;
    // 初期化と同時に先頭ノードを返す関数
    // 各ピクセルごとに先頭ノードを得る
    CvSparseNode *node  = cvInitSparseMatIterator(histogram[y*image->width+x], &mat_iterator);
    CvSparseNode *head  = node;
    
    /*---リストを連結---*/
    for(;node != 0;node->next != NULL){
        // 疎行列内はリストが連結してるとは限らないので,
        // GetNextSparseNodeを使って次ノードを手繰る.
        node->next = cvGetNextSparseNode(&mat_iterator);
    }
    
    /*---ソーティング---*/
    // リスト構造をソーティングする関数を別途定義しておくこと
    // ここではマージソートを想定
    // headノードが指す番地は最頻値になる
    sort(head);
    
    /*---最頻値画像---*/
    // 最頻値を取得
    // 疎行列内の特定の座標の値を取得するdefine文 CV_NODE_VAL
    // unsigned int count = *(unsigned int*)CV_NODE_VAL(histogram[i], node);
    // 疎行列内の座標(この場合は色)を取得するdefine文 CV_NODE_IDX
    const int* index = CV_NODE_IDX(histogram[y*image->width+x], head);
    
    CvScalar color;
    for(int i = 0;i < NUMBER_OF_DIMENSIONS;i++){
        color.val[i] = index[i];
    }
    cvSet2D(background, y, x, color);
}
}

/*---解放---*/
for(int i = 0;i < image->width*image->height;i++){
    cvReleaseSparseMat(&histogram[i]);
}
delete [] histogram;
}}
// 疎行列内の座標を取得するdefine文
// const int* idx      = CV_NODE_IDX(histogram[i], node);
// 疎行列内の特定の座標の値を取得するdefine文
// unsigned char count = *(unsigned char*)CV_NODE_VAL(histogram[i], node);

*解説 [#w0b847a9]
-RGBで最頻値を作る場合,1ピクセル分のヒストグラムを作るだけで256*256*256≒16MBも必要とする
-QVGAの分だけヒストグラムを用意すると256*256*256*320*240=1200GB(≒1.2TB)必要となる.
-しかし,実際ヒストグラムの殆どはゼロである.
-存在した色の分だけメモリを確保することで,メモリの使用量を減らす
-よって疎な行列CvSparseMatを用いる
**生成 [#d5f575f1]
-OpenCVにおける画像や行列と同じく,ポインタに対して領域を確保する関数(この場合はcvCreateSparseMat)を呼ぶ
-このプログラムでは各ピクセルごとにヒストグラムを作るのでピクセル数分CvSparseMat*を用意する
**データ格納 [#td56554d]
-cvGetNDを使ってある色が過去に何回現れたか確認する
-何回現れたかを取得する場合,多次元配列中の座標をint型の配列で渡す
-cvGetNDは一度も現れてない場合,0を返す.
-cvSetNDを使ってカウントアップした値を書き戻す
**リストを連結 [#l1ab5f2d]
-CvSparseMat内のノードは,全部連結されてる訳でなく,内部でブツ切れになっている
-全ノードを連結させるために,cvGetNextSparseNodeを使う
**ソーティング [#i3dde387]
-リストを使ったソーティングなので,マージソートとかがお勧め.
-そのうち中身を書きます.
**最頻値画像 [#e356068e]
-配列内の値は「出現回数」
-最頻値は,ヒストグラム内で最大の値が存在する座標(色)
-CV_NODE_IDXを使って,色を取得する
-CV_NODE_IDXはcxtypes.h内でdefineされている
#geshi(c++,number,start=797){{
#define CV_NODE_VAL(mat,node)   ((void*)((uchar*)(node) + (mat)->valoffset))
#define CV_NODE_IDX(mat,node)   ((int*)((uchar*)(node) + (mat)->idxoffset))
}}
**解放 [#o63007fb]
-他の構造体と同じくcvReleaseする必要がある
-内部のリスト構造を書き換えてしまったので,cvReleaseされるのか少し不安だったが,とりあえず問題なさそう.
-未検証

*メモリ使用量 [#na0ef800]
-画像サイズと観測するデータの枚数にもよるけれど,結局メモリ使用量が数百MB-1GBに達してしまい,1ピクセルごとにソーティングするより増えてしまう
--1ピクセル分のヒストグラムを作るのには256*256*256=16MB必要
--50枚のQVGA画像を蓄積するのに320*240*3*50≒11MB必要
-と言うわけで,もっとメモリを削減したい場合は一旦メモリに格納して1ピクセル毎に最頻値を取得するべし
-ちなみに,道路画像のように彩度の低い画像で試したときは,RGBのヒストグラムを作るより,X,Y,R,G,Bの5次元ヒストグラムを作って一発ソーティングした方がメモリ使用量が少なかった.

*実体ファイル [#mb060261]
-src/cxcore/cxarray.cpp
-include/opencv/cxtypes.h

ジャンル[[:OpenCV]][[:OpenCV 2.1]]準拠

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS