その他
■ ラベリング
OpenCV自体には(OpenCV-1.0.0 時点において)ラベリングを行う関数は存在しないが, OpenCV Library Wikiで紹介されている"Blob extraction library"を利用して,画像のラベリングを行うことができる. このライブラリは,MIL(Matrox Imaging Library)における,MIL blob detection モジュールの機能を代替するものを,OpenCVを利用して実装してある. Win32環境で動作するものと,Linux環境で動作するものが,それぞれ配布されている. デフォルトでは,スタティックライブラリ(.lib あるいは .a)の形式で提供されるが,ソースからダイナミックリンク(あるいは,共有)ライブラリを作成することもできる. blobとは,データベースでいうところの型ではなく,"小さな塊"という 意味で用いられている(コンピュータグラフィックスやビジョンの世界では,メタボールを表現する際に用いられたりもする). つまり,画像中から個別の小さな塊(blob)を抽出する作業(≈ラベリング) を行うためのクラスであり,以下の特徴がある(詳細は,上述のホームページを参照すること).- 二値画像あるいはグレースケール画像から,8近傍においてつながっている領域を抽出する.これらの領域は blob として参照される.
- 画像中の注目オブジェクトを得るために,取得した blob 集合に対してフィルタリングを行う.この処理は,CBlobResult の Filter メソッドによって実現される.
サンプル
ラベリング CBlobResult
入力画像に対してラベリングを行い,面積と周囲長を表示する
サンプルコード
#include <cv.h> #include <highgui.h> #include "BlobResult.h" int main (int argc, char **argv) { int i; IplImage *src_img = 0, *dst_img = 0; CBlobResult blobs; CBlob blob; CvPoint p1, p2; CvFont font; char text[64]; CBlob blobWithBiggestPerimeter, blobWithLessArea; // (1)画像を読み込む if (argc != 2 || (src_img = cvLoadImage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0) return -1; dst_img = cvCreateImage (cvSize (src_img->width, src_img->height), IPL_DEPTH_8U, 1); cvCvtColor (src_img, dst_img, CV_BGR2GRAY); cvInitFont (&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1, CV_AA); // (2)ラベリングを行う blobs = CBlobResult (dst_img, NULL, 100, false); // (3)面積でフィルタリング blobs.Filter (blobs, B_INCLUDE, CBlobGetArea (), B_INSIDE, 1000, 50000); for (i = 0; i < blobs.GetNumBlobs (); i++) { // (4)各blobの最小包含矩形(傾き無し)を求める blob = blobs.GetBlob (i); p1.x = (int) blob.MinX (); p1.y = (int) blob.MinY (); p2.x = (int) blob.MaxX (); p2.y = (int) blob.MaxY (); // (5)各blobの包含矩形,インデックス,面積,周長を描画 cvRectangle (src_img, p1, p2, CV_RGB (255, 0, 0), 2, 8, 0); sprintf (text, "[%d] %d,%d", blob.Label (), (int) blob.area, (int) blob.Perimeter ()); cvPutText (src_img, text, cvPoint (p1.x, p1.y - 5), &font, cvScalarAll (255)); } // (6)最大の周長を持つblobを,緑色で塗りつぶす blobs.GetNthBlob (CBlobGetPerimeter (), 0, blobWithBiggestPerimeter); blobWithBiggestPerimeter.FillBlob (src_img, CV_RGB (0, 255, 0)); // (7)最小の面積を持つblobを,青色で塗りつぶす blobs.GetNthBlob (CBlobGetArea (), blobs.GetNumBlobs () - 1, blobWithLessArea); blobWithLessArea.FillBlob (src_img, CV_RGB (0, 0, 255)); // (8)画像を表示,キーが押されたときに終了 cvNamedWindow ("Labeling", CV_WINDOW_AUTOSIZE); cvShowImage ("Labeling", src_img); cvWaitKey (0); cvDestroyWindow ("Labeling"); cvReleaseImage (&src_img); cvReleaseImage (&dst_img); return 0; }
// (1)画像を読み込む
入力画像を,カラー画像として読み込む.また,ラベリング対象となる画像は,
グレースケールあるいは二値画像である必要があるので,画像の変換を行う.
// (2)ラベリングを行う
1番目の引数に,ラベリング対象となる画像を与える.
2番目の引数は,マスク画像,3番目の引数は,blobかそれ以外かを決める閾値(0-255),
4番目の引数は,各blobモーメントの計算を行うか否かのフラグである.
戻り値として,オブジェクトCBlobResultを返す.
// (3)面積でフィルタリング
Filter()メソッドは,指定されたプロパティにより,blobをフィルタリングする.
1番目の引数は,前のラベリング処理で得られたオブジェクト.
2番目の引数は,フィルタリング作業(条件に当てはまったblobを,含む"B_INCLUDE"か,含まない"B_EXCLUDE"か).
3番目の引数は,フィルタリングに用いるプロパティを決定する関数.
4番目の引数は,フィルタリング条件(B_EQUAL,B_NOT_EQUAL,B_GREATER,B_LESS,B_GREATER_OR_EQUAL,B_LESS_OR_EQUAL,B_INSIDE,B_OUTSIDE)
5番目と6番目の引数は,下限および上限(これは,指定されないこともある)となる閾値.
今回は,面積が,1000 - 50000 [pixel] の範囲にあるblobだけを残している.
// (4)各blobの最小包含矩形(傾き無し)を求める
各blobにおいて最小,最大のx,y座標をもつピクセルを参照することで,そのblobを包含する矩形を求める.
// (5)各blobの包含矩形,インデックス,面積,周囲長を描画
各blobの包含矩形,およびblobインデックス,面積,周囲長を描画する.
ここで,blobの周囲長は,blobの境界となるピクセルの個数であり,
Edges()メソッドにより参照(CvSeq*型)できる.
ただし,これらのピクセルは,ラスタースキャンされた際の走査順に並んでいるだけなので,そのまま輪郭を描く事はできない.
// (6)最大の周囲長を持つblobを,緑色で塗りつぶす
CBlobGetPerimeter()によって得られたblobの周囲の長さが,最大(0番目)のものを,
CBlobオブジェクトとして取得し,FillBlob()メソッドによって塗りつぶす.
// (7)最小の面積を持つblobを,青色で塗りつぶす
(6)と同様に,CBlobGetArea()によって得られたblobの面積が,最小
(blobs.GetNumBlobs()-1番目,つまり最後)のものを,
CBlobオブジェクトとして取得し,FillBlob()メソッドによって塗りつぶす.
// (8)画像を表示,キーが押されたときに終了
画像を実際に表示し,何かキーが押されるまで待つ.