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

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

OpenCV

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

投稿日:2014年11月9日 更新日:

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版で英語ですが最新の情報もあるようです。また価格的にはいいかもしれないです。)


AdSense

AdSense

-OpenCV

執筆者:

関連記事

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

OpenCVで背景差分を試してみました。あとテスト的な動体検知も試してみました。背景差分とはあらかじめ取得した画像を背景画像として、観測時点の画像とその背景画像との差分を取ることによりその差分を前景領 …

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

Visual Studio Community 2013のVisual C++でOpenCVを使う方法についてです。Visual Studio Community 2013については前回書きましたが、 …

Raspberry Pi 4にOpenCVをインストールしてPythonで動作確認

Raspberry Pi 4にOpenCVをインストールして動作確認をしました。Pythonでのプログラミングを想定して動作確認はPythonで行いました。

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

OpenCVで画像内の輪郭抽出からその輪郭の隣接領域(四角形)を求めてその領域を切り出すという処理を作ってみました。前回までのプログラムとほぼ同じですが、前回までは輪郭抽出 → 直線近似 → 隣接領域 …

OpenCV2 cv::Matクラスの基礎

OpenCV2のcv::Matクラスの基礎についてです。OpenCV 2.0からはC++のインターフェースが追加されました。cv::MatクラスはそのC++インターフェースで画像を扱うクラスです。まず …