輪郭処理
■ 輪郭処理
OpenCVの輪郭処理では,検出された輪郭(を表す点列)に対しての処理を行う. 輪郭の近似や,長さ,面積の算出,また,二分木構造を利用した輪郭同士の比 較などを行う関数が用意されている.サンプル
点列を包含する矩形 cvBoundingRect
点列を包含する矩形を求める
サンプルコード
#include <cv.h> #include <highgui.h> #include <time.h> int main (int argc, char **argv) { int i; IplImage *img = 0; CvMemStorage *storage = cvCreateMemStorage (0); CvSeq *points; CvRNG rng = cvRNG (time (NULL)); CvPoint pt; CvRect rect; // (1)画像を確保し初期化する img = cvCreateImage (cvSize (640, 480), IPL_DEPTH_8U, 3); cvZero (img); // (2)点列を生成する points = cvCreateSeq (CV_SEQ_ELTYPE_POINT, sizeof (CvSeq), sizeof (CvPoint), storage); for (i = 0; i < 20; i++) { pt.x = cvRandInt (&rng) % (img->width / 2) + img->width / 4; pt.y = cvRandInt (&rng) % (img->height / 2) + img->height / 4; cvSeqPush (points, &pt); cvCircle (img, pt, 3, CV_RGB (0, 255, 0), CV_FILLED); } // (3)点列を包含する矩形を求めて描画する rect = cvBoundingRect (points, 0); cvRectangle (img, cvPoint (rect.x, rect.y), cvPoint (rect.x + rect.width, rect.y + rect.height), CV_RGB (255, 0, 0), 2); // (4)画像の表示,キーが押されたときに終了 cvNamedWindow ("BoundingRect", CV_WINDOW_AUTOSIZE); cvShowImage ("BoundingRect", img); cvWaitKey (0); cvDestroyWindow ("BoundingRect"); cvReleaseImage (&img); cvReleaseMemStorage (&storage); return 0; }
// (1)画像を確保し初期化する
このサンプルでは,外部画像を読み込まずに,プログラム内部で画像を用意する.
また,作成されたカラー画像を,黒(0)で塗りつぶして初期化する.
// (2)点列を生成する
関数cvRandInt()を用いて,画像内のある範囲に収まるようなランダムな点列を生成する.
また,個々の点を中心とする小さな円を画像に描画し,点の位置を表す.
このサンプルでは,ランダムな点列を用いているが,もちろん事前に検出した
輪郭(を構成する点列)に対しても,処理を行うことができる.
// (3)点列を包含する矩形を求めて描画する
関数cvBoundingRect()を用いて,点列を包含する最小の矩形(ただし傾いていない)を求める.
関数cvMinAreaRect2()との違いは,この傾きを考慮するか否かである.
cvBoundingRect()はcvRectを返すが,cvMinAreaRect2()はcvBox2Dを返す.
求められたcvRectを画像に描画する.
// (4)画像の表示,キーが押されたときに終了
結果の画像を表示し,何かキーが押されるまで待つ.
実行結果例
輪郭領域の面積と輪郭の長さ cvContourArea, cvArcLength
輪郭によって区切られた領域の面積と,輪郭の長さを求める
サンプルコード
#include <cv.h> #include <highgui.h> #include <time.h> #include <math.h> #include <stdio.h> int main (int argc, char **argv) { int i, size = 500; char text[2][64]; double scale, area, length; IplImage *img = 0; CvMemStorage *storage = cvCreateMemStorage (0); CvSeq *points; CvRNG rng = cvRNG (time (NULL)); CvPoint pt0, pt1; CvRect rect; CvFont font; // (1)画像を確保し初期化する img = cvCreateImage (cvSize (size, size), IPL_DEPTH_8U, 3); cvZero (img); // (2)点列を生成する points = cvCreateSeq (CV_SEQ_POLYLINE, sizeof (CvSeq), sizeof (CvPoint), storage); scale = cvRandReal (&rng) + 0.5; pt0.x = int (cos (0) * size / 4 * scale + size / 2); pt0.y = int (sin (0) * size / 4 * scale + size / 2); cvCircle (img, pt0, 2, CV_RGB (0, 255, 0)); cvSeqPush (points, &pt0); for (i = 1; i < 20; i++) { scale = cvRandReal (&rng) + 0.5; pt1.x = int (cos (i * 2 * CV_PI / 20) * size / 4 * scale + size / 2); pt1.y = int (sin (i * 2 * CV_PI / 20) * size / 4 * scale + size / 2); cvLine (img, pt0, pt1, CV_RGB (0, 255, 0), 2); pt0.x = pt1.x; pt0.y = pt1.y; cvCircle (img, pt0, 3, CV_RGB (0, 255, 0), CV_FILLED); cvSeqPush (points, &pt0); } cvLine (img, pt0, *CV_GET_SEQ_ELEM (CvPoint, points, 0), CV_RGB (0, 255, 0), 2); // (3)包含矩形,面積,長さを求める rect = cvBoundingRect (points, 0); area = cvContourArea (points); length = cvArcLength (points, CV_WHOLE_SEQ, 1); // (4)結果を画像に書き込む cvRectangle (img, cvPoint (rect.x, rect.y), cvPoint (rect.x + rect.width, rect.y + rect.height), CV_RGB (255, 0, 0), 2); snprintf (text[0], 64, "Area: wrect=%d, contour=%d", rect.width * rect.height, int (area)); snprintf (text[1], 64, "Length: rect=%d, contour=%d", 2 * (rect.width + rect.height), int (length)); cvInitFont (&font, CV_FONT_HERSHEY_SIMPLEX, 0.7, 0.7, 0, 1, CV_AA); cvPutText (img, text[0], cvPoint (10, img->height - 30), &font, cvScalarAll (255)); cvPutText (img, text[1], cvPoint (10, img->height - 10), &font, cvScalarAll (255)); // (5)画像を表示,キーが押されたときに終了 cvNamedWindow ("BoundingRect", CV_WINDOW_AUTOSIZE); cvShowImage ("BoundingRect", img); cvWaitKey (0); cvDestroyWindow ("BoundingRect"); cvReleaseImage (&img); cvReleaseMemStorage (&storage); return 0; }
// (1)画像を確保し初期化する
このサンプルでは,外部画像を読み込まずに,プログラム内部で画像を用意する.
また,作成されたカラー画像を,黒(0)で塗りつぶして初期化する.
// (2)点列を生成する
関数cvRandInt()を用いて,画像内のある範囲に収まるようなランダムな点列を生成する.
ただし,面積を求める際に分かり易いように自己交差しないようにする.
また,個々の点を中心とする小さな円を画像に描画し,点の位置を表す.
さらに,閉じた領域になるように更に点列同士を結ぶ線分を描画する.
このサンプルでは,ランダムな点列を用いているが,もちろん事前に検出した
輪郭(を構成する点列)などに対しても,処理を行うことができる.
// (3)包含矩形,面積,長さを求める
前述のサンプルと同様に,関数cvBoundingRect()を用いて点列を包含する矩形領域を求める.
さらに,関数cvContourArea()によって点列が構成する輪郭の面積を求め,
関数cvArcLength()にってその長さ(周囲長)を求める.
cvContourArea()の2番目の引数は,輪郭のどの部分の面積を計算するかを表しており,
この引数が指定されない場合にはデフォルト引数であるCV_WHOLE_SEQが用いられる.
この場合は,輪郭が表す領域全ての面積が求められる.
cvArcLength()の2番目の引数の意味も同様であり,3番目の引数に0より大きい値を指定することで,輪郭を閉曲線として扱う.
その他の値については,リファレンスを参照すること.
// (4)結果を画像に書き込む
包含矩形,包含矩形の面積と長さ,輪郭の面積と長さ,を画像に書き込む.
// (5)画像を表示,キーが押されたときに終了
結果の画像を表示し,何かキーが押されるまで待つ.