NAIST井村さんのラベリングを試す

OpenCVのプログラミングです。2.0betaは早々に諦めて1.1preでまた遊んでいます。

サンプル

ラベリングは領域の抽出で物体を検出する場合などに使用するフィルタ処理です。
SIFTやSURFとRANSACを組み合わせて特徴量解析を行うよりもアルゴリズムがシンプルで演算速度も期待できますが、
走査処理を繰り返すので画素数が増えると途端に重くなります。使用には注意が必要です。
アルゴリズムについてはMSDNの解説が(意外に)親切です。
今回はNAIST井村さんのサイト(阪大サーバ)からソースコードを拝借しました。

下の画像(640x480)のラベリング10000回: 36秒66
CPU: PentiumM 1.8GHz, Memory: 1GB, OS: linux 2.6.11.7, コンパイラ: g++ -O3

モノクロとはいえVGAを4msecで処理できれば十分なスペックではないでしょうか。これは期待。
WindowsのサンプルプロジェクトをDLして解凍。
vcprojファイルを開くとVC++2008変換ウィザードが始まりました。当然。
ビルドするとエラーなし。よし、実行。エラー。あれ。
ソースコードを見るとfstreamのfopen()に失敗してる。ロジック以前のところで落ちてる。ファイル読込と保存の処理を変更。

// image = bmp_manip.Read( argv[ 1 ], width, height );
image = bmp_manip.Read( "test.bmp", width, height );

// Output( dst_buf, width, height, argv[ 2 ] );
Output( dst_buf, width, height, "target.bmp" );

うまくいったー。

※別にソースが悪いわけでありませんが

何も見ないでソースを書いてみる

井村さん、なんと仕様書(PDF)まで用意してくれました。
学者さんにしては親切すぎる対応に感謝です。
とりあえずLabelingクラスと仕様書を見ながら真似マネソースコードを書いてみた。

IplImage* loadedImage = cvLoadImage("test.bmp", CV_LOAD_IMAGE_ANYCOLOR);
int wid = loadedImage->width;
int hei = loadedImage->height;
int chn = loadedImage->nChannels;
short* src_buf = new short[wid * hei];
short* dst_buf = new short[wid * hei];

LabelingSS labeling;

// copy.
char* lp = loadedImage->imageData;
short* sp = src_buf;
for(int n=0; n<(wid*hei); n++){
*sp = (short)*lp;
sp++;
lp += chn;
}
// ラベリング処理.
labeling.Exec(src_buf, dst_buf, wid, hei, true, 10);

Output( dst_buf, wid, hei, "target.bmp" );

BmpやらColorConvやらいろいろインクルードしなきゃなのでややこしい。実行してみたらサンプルと同じように処理できていたので一安心。

重い..

リアルタイムにラベリングしたくなるのが人情なので書いてみる。

bool flag = false;

int _tmain(int argc, _TCHAR* argv)
{
CvCapture* capture = cvCreateCameraCapture(CV_CAP_ANY);
IplImage* frame = cvQueryFrame(capture);
int wid = frame->width;
int hei = frame->height;
int chn = frame->nChannels;
short* src_buf = new short[wid * hei];
short* dst_buf = new short[wid * hei];
LabelingSS labeling;
while(true){
frame = cvQueryFrame(capture);
if(flag == true){
// copy.
char* lp = grayImage->imageData;
short* sp = src_buf;
for(int n=0; n<(wid*hei); n++){
*sp = (short)*lp;
sp++;
lp += chn;
}
// ラベリング処理.
labeling.Exec(src_buf, dst_buf, wid, hei, true, 10);
}
int key = cvWaitKey(1);
if(key == 'q'){
break;
}else if(key == 'l'){
flag = true;
}
}
delete
src_buf;
delete[] dst_buf;
cvReleaseImage(&grayImage);
}

でやってみると、異様に時間がかかる。WHY?
キャプチャ画像をグレースケールしてみたり、いろいろ試したけど、キャプチャした画像をサンプルプロジェクトに食わせて原因が判明。

キャプチャした画像のラベリング領域が大量だったからです。
仕様書の「入出力フォーマット」の項目を見ると

入力バッファの各画素が保持する情報は以下の意味を持つ。
・ 0: ラベリング対象外画素
・ 0 以外: ラベリング対象画素
同一の値が連続している領域が、1 つの領域とみなされる。

つまり2値化された画像を取り込めという意味でしょうか。
CANNYか何かで2値化してからラベリングしないと意味がなさそうですね。
「補足事項」にも閾値処理をしてから行うよう書いてあります。仕様書をよく読んでからでないとだめですね。反省。