Home > ソフトウエア | 技術 | 画像処理 > cvFindContoursの結果から座標を得る

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 で作ったせいで輪郭が二重に出てますが.

Home > ソフトウエア | 技術 | 画像処理 > cvFindContoursの結果から座標を得る

Search
Feeds
Meta

Return to page top