ランダムな点列を包含する矩形

29 1月 2010 Under: opencv2.x-samples

C

#include <cv.h>
#include <highgui.h>

int
main (int argc, char **argv)
{
  int i;
  IplImage *img = 0;
  CvMemStorage* storage = cvCreateMemStorage(0);
  CvSeq *points;
  CvRNG rng = cvRNG(cvGetTickCount());
  CvPoint pt;
  CvRect rect;
  
  // (1)allocate and initialize an image
  img = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 3);
  cvZero(img);

  // (2)generate and draw a sequence of random 2d points
  points = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
  for( i = 0 ;i < 50; 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, 8, 0);
  }
  
  // (3)calculate and draw a bounding rectangle of points
  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, 8, 0);

  // (4)show the iamge, and quit when any key pressed
  cvNamedWindow ("BoundingRect", CV_WINDOW_AUTOSIZE);
  cvShowImage ("BoundingRect", img);
  cvWaitKey (0);

  cvDestroyWindow("BoundingRect");
  cvReleaseImage(&img);
  cvReleaseMemStorage(&storage);
  
  return 0;
}

// (1)画像領域を確保し,初期化します.

cvCreateImage によって,指定のサイズ,ビット深度の画像を作成した後に,cvZero を用いて画像を黒で塗りつぶして初期化します.この関数は,次のようにマクロ定義されたものです.

#define cvZero cvSetZero

// (2)ランダムな2次元座標列を生成し,その座標を中心とする円を描画します.

cvCreateSeq によって確保されたシーケンスに,ランダムに生成された座標を代入します. cvRandInt によって生成された疑似乱数は,画像の中心部(全体の面積の1/4)にあるように調整され, cvSeqPush によりシーケンスに追加されます.また,それぞれの座標を中心として円を描画する(cvCircle)ことで,生成された点列を可視化します.

// (3)点列の包含矩形を求め,それを描画します.

cvBoundingRect を用いて,生成された点列を包含するような最小の矩形を求めます.ただし,この矩形は傾き(回転)が考慮されません.これが cvMinAreaRect2 との違いであり,前者は cvRect を返しますが,後者は CvBox2D を返します.求められた矩形を cvRectangle によって描画します.

// (4)画像を表示し,何かキーが押されると終了します.

結果画像を表示し,何かキーが押された場合にはプログラムを終了します.

C++

#include <cv.h>
#include <highgui.h>

using namespace cv;

int
main (int argc, char **argv)
{
  // (1)allocate and initialize an image
  Mat img = Mat::zeros(Size(640, 480), CV_8UC3);
  if(!img.data) return -1;

  // (2)generate a sequence of random 2d points
  Mat points(Size(50, 1), CV_32SC2);
  /// randu(points, Scalar(img.cols/4, img.rows/4), Scalar(img.cols*3./4., img.rows*3./4.));
  RNG(getTickCount()).fill(points, RNG::UNIFORM, Scalar(img.cols/4, img.rows/4), Scalar(img.cols*3./4, img.rows*3./4));

  // (3)draw the points as small circles
#if 1
  MatIterator_<Vec2i> it = points.begin<Vec2i>(), it_end = points.end<Vec2i>();
  for(; it!=it_end; ++it) {
    circle(img, Point((*it)[0], (*it)[1]), 3, Scalar(0,255,0), -1);
  }
#elif 1
  vector<Mat> planes;
  split(points, planes);
  MatIterator_<int> it0 = planes[0].begin<int>(), it0_end = planes[0].end<int>();
  MatIterator_<int> it1 = planes[1].begin<int>();
  for(; it0 != it0_end; ++it0, ++it1) {
    circle(img, Point(*it0, *it1), 3, Scalar(0,255,0), -1);
  }
#else
  /// only if points has no gap
  int *p = points.ptr<int>();
  int inc = points.elemSize()/sizeof(int);
  for(int i=0; i<points.cols; i++, p+=inc) {
    circle(img, Point(p[0], p[1]), 3, Scalar(0,255,0), -1);
  }
#endif

  // (4)calculate and draw a bounding rectangle of points
  Rect brect = boundingRect(points);
  rectangle(img, brect.tl(), brect.br(), Scalar(0,0,255), 2);

  // (5)show the iamge, and quit when any key pressed
  namedWindow("img", CV_WINDOW_AUTOSIZE);
  imshow("img", img);
  waitKey(0);
  
  return 0;
}

// (1)画像領域を確保し,初期化します.

Matlab 形式の Mat::zeros を利用して,0で初期化された Mat クラスのインスタンスを作成します.

// (2)ランダムな2次元座標列を生成します.

指定範囲内(上述のCの例と同様)に一様分布する疑似乱数で,行列を埋めます.
ここでは,低レベルな関数 RNG を利用して行列を埋めていますが, randu を利用しても同様のことが可能です.ただし,randu は,常に同じシードを利用するデフォルト乱数生成器を利用するため,常に同じ順列の乱数が生成されます.実行の度に異なる乱数を使用したい場合には,例えばここで用いているように getTickCount を利用して乱数を生成します.

// (3)それぞれの点を描画します.

イテレータを利用して行列内を横断し, circle を用いて各点を中心とする円を描画します.
また,マクロでコメントアウトされているように,
・チャンネル毎(x座標とy座標)のイテレータを利用する方法

  vector<Mat> planes;
  split(points, planes);
  MatIterator_<int> it0 = planes[0].begin<int>(), it0_end = planes[0].end<int>();
  MatIterator_<int> it1 = planes[1].begin<int>();
  for(; it0 != it0_end; ++it0, ++it1) {
    circle(img, Point(*it0, *it1), 3, Scalar(0,255,0), -1);
  }

・ポインタを利用する方法(gapがない場合)

  int *p = points.ptr<int>();
  int inc = points.elemSize()/sizeof(int);
  for(int i=0; i<points.cols; i++, p+=inc) {
    circle(img, Point(p[0], p[1]), 3, Scalar(0,255,0), -1);
  }

などでも同様のことが可能です.

// (4)点列の包含矩形を求め,それを描画します.

boundingRect を用いて,生成された点列を包含するような最小の矩形を求めます.ただし,この矩形は傾き(回転)が考慮されません.これが minAreaRect との違いであり,前者は Rect を返しますが,後者は RotatedRect を返します.求められた矩形を rectangle によって描画します.

// (5)画像を表示し,何かキーが押されると終了します.

結果画像を表示し,何かキーが押された場合にはプログラムを終了します.

実行結果例