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

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

OpenCV

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

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

OpenCVで画像内の輪郭抽出からその輪郭の隣接領域(四角形)を求めてその領域を切り出すという処理を作ってみました。前回までのプログラムとほぼ同じですが、前回までは輪郭抽出 → 直線近似 → 隣接領域 の流れでしたが、今回は、輪郭抽出 → 凸包の取得 → 隣接領域を試しました。以下の画像がその結果の例です。(※実画像サイズは大きめです。)

convexhull1

前回までの結果とほぼ同じですが結果の画像の緑の線が輪郭で、青い線が一定以上の面積の輪郭から取り出した凸包です。

「凸包」とはある図形を含む最小の凸図形のことです。以下の図形で青い線です。ここでは輪郭の凸包ですが点集合に対する凸包も定義出来ます。どうやら「輪ゴムで囲うようにぴったりと囲んだ線の図形」という表現をよく使うようです。今回のプログラムでは最終的にこの画面のように凸包に隣接する矩形を取得して切り出します。
0014

このペットボトルの画像で取り出した画像の一部です。
0003

0001

0009

0013

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

今回の処理は以下です。ボタンの「凸包取得」での処理です。

/*------------------------------------------------
処理2
-------------------------------------------------*/
void func2(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);
	}

	//輪郭の座標リスト
	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::convexHull(*contour, approx);

		//凸包の取得面積が一定以上なら取得
		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::imshow("label" + std::to_string(roiCnt + 1), roi[roiCnt]);

			roiCnt++;

			//念のため矩形の数をチェック
			if (roiCnt == 99)
			{
				break;
			}
		}

		i++;
	}

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

	//必要に応じて保存したい画像を設定する
	imgMatWrite = imgIn;
}

今回のポイントの凸包の取得についてだけ書きます。その他は前回までを参照して下さい。

以下が凸包を取得する処理です。
//凸包の取得(頂点はデフォルトの反時計回り)
cv::convexHull(*contour, approx);

convexHullで凸包を取得しますが、ここでは省略してデフォルトになっている3番目のパラメータで頂点の順序を指定します。デフォルトが反時計回りで、3番目にtrueを指定すると時計回りになります。最終的にはこの凸包に隣接する矩形を取得して切り出しています。このあたりは前回と同様です。

このconvexHullでの頂点の回りですが、座標を取得する場合に便利な場合があります。例えば以下のように四角形という前提で座標を取り出す場合、以下のように取り出せました。この処理はプログラムの「凸包で四角形取得」ボタンでの処理です。四角形が認識できる画像で四角形が取得出来ます。
Mat roi= cv::Mat(imgIn, cv::Rect(approx[2].x, approx[2].y, approx[3].x – approx[2].x, approx[0].y – approx[3].y));

最後にまとめ的にですが、前回の輪郭抽出から直線近似、今回の輪郭抽出から凸包取得それぞれ場合によって使いこなしていければというところです。

今回でこのシリーズは終了です。OpenCVについては今後も書きたいと思います。

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

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


AdSense

AdSense

-OpenCV

執筆者:

関連記事

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

OpenCVをWin32ベースで利用するの4回目です。前回はカメラ入力からの画像処理(静止画)を扱いましたが、今回は動画を処理します。動画と言っても特に変わることはなくキャプチャしたフレームごとに処理 …

NuGetでOpenCVを導入する

Visual Studio Express 2013 for Windows Desktopの開発環境でNuGetを使ってOpenCVを導入してみました。その方法についてです。 まずは、OpenCV( …

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

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

OpenCVをWin32ベースで利用する(その2)OpenCVの組み込み

OpenCVをWin32ベースで利用するの2回目です。前回はベースになるダイアログベースのアプリケーションを作成しました。今回はこのアプリケーションにOpenCVを組み込んで実際に動かしてみます。 ( …

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

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