株式会社インデペンデンスシステムズ横浜

システム開発エンジニアの西田五郎が運営しております。新規開発案件のご依頼をお待ちしております。

*

OpenCVで輪郭抽出から隣接領域の切り出し(その2)輪郭の直線近似と切り出し

      2014/12/28

OpenCVで画像内の輪郭抽出からその輪郭の隣接領域(四角形)を求めてその領域を切り出すという処理を作ってみました。以下の画像がその結果の例です。(※実画像サイズは大きめです。)

out1contour

今回はその2回目です。前回は輪郭抽出の処理まで書きました。今回は輪郭の直線近似から切り出しの最後まで書きます。

処理としては以下のような流れになります。

入力画像のグレースケール化

グレースケールから2値可画像の取得

2値化画像から輪郭取得

輪郭を直線近似(※今回の説明はここからです。)

直線近似した輪郭が一定以上の面積であれば輪郭に隣接する矩形を取得

隣接した矩形を切り出して表示

ここから実際のプログラムの説明です。開発環境とベースになるプログラムは以下のページのプログラムを元にしています。
OpenCVをWin32ベースで利用する(その2)OpenCVの組み込み

今回のプログラム全体は以下からダウンロード出来ます。(※必要な場合は用途に限らずご利用頂いて問題ありませんが、一切無保証です。弊社は一切の責任を負いません。)
ソース一式(※OpenCVを含むため61M程度のサイズです。)

起動すると以下のような画面になります。
0009

今回の処理は「輪郭取得」ボタンでの処理です。この部分のソースは以下です。

/*------------------------------------------------
 処理1
-------------------------------------------------*/
void func1(HWND hWnd)
{
	////MessageBox(hWnd, "func1", "debug", MB_OK);
	if (imgMatRead.rows == 0){
		MessageBox(hWnd, "画像ファイルが無効です", "エラー", MB_OK);
		return;
	}

	//入力画像、ここでは毎回ファイルから読み込む
	cv::Mat imgIn = cv::imread((const std::string&)szOpenFileName, 1); //3チャンネルカラー画像で読み込む;

	//グレースケール
	cv::Mat grayImage, binImage;
	cv::cvtColor(imgIn, grayImage, CV_BGR2GRAY);
	
	//2値化(※反転で結果が変わる、基本は背景が黒で物体が白)
	BOOL bInv = checkInv(hWnd);
	if (bInv){
		cv::threshold(grayImage, binImage, 0.0, 255.0, CV_THRESH_BINARY_INV | CV_THRESH_OTSU);
	}
	else{
		cv::threshold(grayImage, binImage, 0.0, 255.0, CV_THRESH_BINARY | CV_THRESH_OTSU);
	}
	cv::imshow("bin", binImage);

	//輪郭の座標リスト
	std::vector< std::vector< cv::Point > > contours;

	//輪郭取得
	////cv::findContours(binImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	cv::findContours(binImage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);

	// 検出された輪郭線を緑で描画
	for (auto contour = contours.begin(); contour != contours.end(); contour++){
		cv::polylines(imgIn, *contour, true, cv::Scalar(0, 255, 0), 2);
	}	

	//輪郭の数
	int roiCnt = 0;

	//輪郭のカウント	
	int i = 0;

	for (auto contour = contours.begin(); contour != contours.end(); contour++){
		std::vector< cv::Point > approx;
		
		//輪郭を直線近似する
		cv::approxPolyDP(cv::Mat(*contour), approx, 0.01 * cv::arcLength(*contour, true), true);
		
		// 近似の面積が一定以上なら取得
		double area = cv::contourArea(approx);

		if (area > 1000.0){	
			//青で囲む場合			
			cv::polylines(imgIn, approx, true, cv::Scalar(255, 0, 0), 2);
			std::stringstream sst;
			sst << "area : " << area;
			cv::putText(imgIn, sst.str(), approx[0], CV_FONT_HERSHEY_PLAIN, 1.0, cv::Scalar(0, 128, 0));
			
			//輪郭に隣接する矩形の取得
			cv::Rect brect = cv::boundingRect(cv::Mat(approx).reshape(2));
			roi[roiCnt] = cv::Mat(imgIn, brect);

			//入力画像に表示する場合
			//cv::drawContours(imgIn, contours, i, CV_RGB(0, 0, 255), 4);

			//表示
			cv::imshow("label" + std::to_string(roiCnt+1), roi[roiCnt]);
		
			roiCnt++;

			//念のため輪郭をカウント
			if (roiCnt == 99)
			{
				break;
			}
		}

		i++;
	}

	//全体を表示する場合
	//cv::imshow("coun", imgIn);

	imgMatWrite = imgIn;
}

輪郭直線近似処理
それでは、今回の説明の輪郭の直線近似処理についてです。cv::findContours()で取得した各輪郭について、cv::approxPolyDP()で直線近似を取得します。ここでのポイントは、3番目のパラメータです。

cv::approxPolyDP(cv::Mat(*contour), approx, 0.01 * cv::arcLength(*contour, true), true);
この3番目のパラメータでは「近似精度」を指定します。これは元のカーブと近似カーブの最大距離です。ここでは、輪郭線の周囲長を取得してその割合で指定しています。この値が小さい程、元の輪郭に近い直線で近似します。

以下の画像の星形状に注目します。
contour

cv::approxPolyDP(cv::Mat(*contour), approx, 0.01 * cv::arcLength(*contour, true), true);
これで実行した場合です。
0020

cv::approxPolyDP(cv::Mat(*contour), approx, 0.1 * cv::arcLength(*contour, true), true);
これで実行した場合です。
0023
青い線が直線近似です。0.1の場合は三角で近似する結果になりました。今回のプログラムでは輪郭に近くなるように0.01を使っています。

隣接矩形の取得
最後に輪郭に隣接する矩形の取得についてです。以下で取得した輪郭の直線近似に隣接する矩形を取得しています。
cv::Rect brect = cv::boundingRect(cv::Mat(approx).reshape(2));

reshapeで近似直線を2チャンネルに変更してそれから隣接する矩形を取得しています。この結果をMatの配列に格納して表示しています。これで処理結果の表示としています。ページ始めで表示している画像です。

今回はここまでです。次回は今回までの処理と同様の処理ですが、凸包を使ってみます。

関連書籍
(※OpenCV 2 プログラミングブックは私も持っています。)

(※ここからはKindle版で英語ですが最新の情報もあるようです。また価格的にはいいかもしれないです。)


 - OpenCV

AdSense

AdSense

  関連記事

OpenCVでのORBによる特徴点抽出とマッチング(その1)基本的な使い方

OpenCVでのORBアルゴリズムによる特徴点抽出とマッチングの処理についてです …

Visual Studio Community 2013のVisual C++でOpenCVを使う

Visual Studio Community 2013のVisual C++で …

OpenCVで背景差分とテスト的な動体検知

OpenCVで背景差分を試してみました。あとテスト的な動体検知も試してみました。 …

OpenCVで輪郭抽出から隣接領域の切り出し(その1)輪郭抽出まで

OpenCVで画像内の輪郭抽出からその輪郭の隣接領域(四角形)を求めてその領域を …

OpenCVをWin32ベースで利用する(その1)ダイアログベースアプリ

OpenCVをWin32ベースのアプリケーションで利用してみます。今回はまず、W …

OpenCVをWin32ベースで利用する(その5)GUIコントロールの追加

OpenCVをWin32ベースで利用するの5回目です。前回の4回目で一度終了した …

Tesseract-OCRの導入(その3)OpenCVの出力を認識する

Tesseract-OCRの導入の3回目です。前回はvc++のコンソールアプリか …

OpenCVで輪郭抽出から隣接領域の切り出し(その3)凸包の取得

OpenCVで画像内の輪郭抽出からその輪郭の隣接領域(四角形)を求めてその領域を …

OpenCVでのORBによる特徴点抽出とマッチング(その2)GUIの利用

OpenCVでのORBアルゴリズムによる特徴点抽出とマッチングの処理についての2 …

OpenCVをWin32ベースで利用する(その3)カメラ入力からの画像処理

OpenCVをWin32ベースで利用するの3回目です。前回は画像ファイルを入力に …