Home > Archives > 2009-08

2009-08

cvFindContoursの結果から座標を得る

OpenCVのcvFindContours()は2値画像から輪郭を取ってきてくれるのだが,
その結果から輪郭線のピクセル座標をどうやって取得するのか?
というのを調べて,実際にやってみた.
(最悪「cvDrawContours()で描いてみてピクセルの色で判断」とかいう手もあるが…)

今回は輪郭線を構成する全ピクセルの座標が必要なのではなくて,輪郭をそれとなく折れ線近似した結果の頂点群の座標くらいが得られればよい状況だったので,

  1. cvFindContour()で輪郭を検出してもらう
  2. 各輪郭を,cvApproxPoly()で折れ線近似する
  3. 折れ線の頂点座標をもらう

という手順をコーディングしてみた.この手順をまとめた関数は以下の通り.(C++)

//cvFindContour -> cvApproxPoly
//の順番で処理.輪郭を多角形近似した際の頂点座標群を得る.
//※入力画像pSrc8U1Cは,cvFindContourの仕様により変更される.
//	嫌ならコピーを渡すこと.
typedef std::vector< std::vector<CvPoint> >	Type_ApproxContourContainer;

bool GetApproxPolyContours(	IplImage *pSrc8U1C,	//constでない入力画像
							Type_ApproxContourContainer *pDst,	//結果受取場所
							double approx_accuracy_param	//cvApproxPolyに渡す近似精度パラメタ
							)
{
	//引数チェック
	if( approx_accuracy_param < 0 )return false;
	if(  pDst==NULL  ||  !CheckImgType( pSrc8U1C, IPL_DEPTH_8U, 1 )  )return false;

	//cvFindContoursでContourを検出
	CvMemStorage* storage = cvCreateMemStorage(0);
	CvSeq* contour = 0;

	int contour_count = cvFindContours( pSrc8U1C, storage, &contour, sizeof(CvContour), CV_RETR_LIST , CV_CHAIN_APPROX_SIMPLE );
	if( contour_count <= 0 )return false;

	//各ContourをcvApproxPolyで近似して,結果格納域にその頂点座標群を保存
	pDst->resize( contour_count );
	Type_ApproxContourContainer::iterator iDst = pDst->begin();

	for( ; contour != 0; contour = contour->h_next )
	{
		CvSeq *approx = cvApproxPoly( contour, sizeof(CvContour), NULL, CV_POLY_APPROX_DP, approx_accuracy_param );

		iDst->resize( approx->total );
		for( int i=0; i<approx->total; i++ )
		{
			iDst->at(i) = *CV_GET_SEQ_ELEM( CvPoint, approx, i );
		}
		iDst++;
	}

	//後始末
	cvReleaseMemStorage( &storage );
	return true;
}

(最初の方にあるCheckImgType(…)は,渡されてきた画像が8bit1chであるかどうかをチェックしているもの.簡素すぎるのでコードは省略.)

cvFindContours()で検出されたcontour群の巡回は,リファレンスのcvDrawContours()のとこにあるサンプル通りに,h_nextで行う(v_nextはいつ使うのであろうか?).
(このサンプルでは後始末処理が省略されている.storageをcvReleaseMemStrorage()で後始末する必要があると思う.)

cvApproxPoly()は,「第3引数がNULLならば入力が使っていた場所に結果を保存する」とのこと(結果としてデータ量が増えた場合とか大丈夫なのだろうか?とりあえず無事に動いているようだが).
精度に関するパラメタは,近似後の折れ線と近似前のデータとの間の許容距離のようなものだと思われる(←アルゴリズム名をググって出てきたページにあった絵だけからの想像).(試しに負の値を入れてみたら即死なされたので注意)

各contourが持っているデータ(cvFindContours()の引数指定で,結果をチェインコードではなく点列にしているので,データは多分点座標でCvPoint型)には,CV_GET_SEQ_ELEMなるマクロでアクセスできる模様で,これで無事に座標を取得できた.

なお,今回はcvApproxPoly()の処理が入っているが,不要な場合は取っ払えばOK.
↑のコードでいえば,近似処理を省く最も簡単な方法は
  CvSeq *approx = cvApproxPoly(…)
のところを,
  CvSeq *approx = contour;
としてしまえばいい.
※ただし,cvFindContours()自体も,引数指定によってデータを近似するのでその点は注意.

Contour
上記関数を使って精度パラメタ=5.0で取得できた点列を赤線でつないでみた.
この関数に渡す入力画像を cvCanny→cvDilate で作ったせいで輪郭が二重に出てますが.

  • Comments (Close): 0
  • Trackbacks (Close): 0

気まぐれ Programming TIPS 【2】

【2】 現在時刻の取得(C)

気まぐれに思いついた内容を、備忘録的に殴り書くものでございます。

今回は現在時刻を取得する方法について。
現在時刻を取得するには、標準Cライブラリのtime関数を使います。

time_t time(time_t *time);

引数timeは時刻の格納場所へのポインタ。
戻り値は1970年1月1日0時以降の経過秒数。

time関数で取得できるシステム時刻は、経過秒数なのでわかりやすい形に変換する。

struct tm *localtime(const time_t time);

tm構造体のメンバは以下の通り、
tm_sec – 秒(0~59)
tm_min – 分(0~59)
tm_hour – 時間(0~23)
tm_mday – 日(1~31)
tm_mon – 月(0~11、1月=0)
tm_year – 年(実際の西暦から1900を引いた数)
tm_wday – 曜日(0~6、日曜日=0)
tm_yday – 年内の通産日(0~365、1月1日=0)
tm_isdst – 夏時間が有効な場合は正の値、夏時間が無効な場合は0、夏時間がどうかが不明な場合は負の値

time関数で取得したtime_t変数をlocaltime関数に渡すと、
タイムゾーンに合わせてtm構造体に変換される。

以下サンプルコード

  char week[7][3] = {"日","月","火","水","木","金","土"};  time_t time_now; struct tm  *local;  time(&time_now); local = localtime(&time_now);  printf("%d年%02d月%02d日(%s)\n",local->tm_year+1900,local->tm_mon+1,local->tm_mday,week[local->tm_wday]); printf("%02d時%02d分%02d秒\n", local->tm_hour, local->tm_min, local->tm_sec);  

実行結果
2009年08月17日(月)
14時02分15秒

  • Comments (Close): 0
  • Trackbacks (Close): 0

Home > Archives > 2009-08

Search
Feeds
Meta

Return to page top