0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【OpenCV/C++】cv::canny()等で得たエッジをブロック化する

Posted at

はじめに

cv::canny等で得たエッジは、物体の輪郭となる場合がありますが、
複数の物体がある場合に、エッジをブロック化して、
物体毎の大まかな範囲を得て、物体検出の一助になるようにします。
同一物体で複数のブロックをまたぐものは無いとすれば、
処理する範囲を限定することが出来ます。
今回は二値化マスク画像を結果とします。

ブロック化手順

ブロック化手順はシンプルです。
エッジが見つかった場合は、行と列がブロックサイズの白の矩形で塗りつぶします。
同一ブロック内に、ほかのエッジが見つかっても既に塗りつぶされている事にします。

実装の手順

 今回の実装は以下の様になります。
 

  1. 画像の取り込み
  2. カラー画像をグレースケール画像に変換
  3. Gaussian Blurをかける。
  4. Cannyなどでエッジを検出する。
  5. 輪郭を探して位置情報をためておく。
  6. エッジのブロック化関数(makeBlockMaskFromEdge)を呼び出す。
  7. 結果を表示する。

入力画像はOpenCVのサンプルの「basketball1.png」とします。

basketball1.png

ソースコード

main.cpp

#include <opencv2/opencv.hpp>
#include <stdio.h>


//エッジからブロック化したマスクを作成する
bool makeBlockMaskFromEdge(const cv::Mat& edge_mat,cv::Mat& mask_mat,std::vector<cv::Rect>& edge_rects,int block_size);


int main(int argc, char* argv[])
{
	//グレイスケールとして読み込み
	cv::Mat sample_mat = cv::imread("basketball1.png",0);
	// 平滑化を行います.これがないと誤検出が起こりやすくなります.
	cv::GaussianBlur(sample_mat,sample_mat,cv::Size(9,9),2,2);
	//canny法でエッジ検出
	cv::Mat edges;
	cv::Canny(sample_mat,edges,150,200);
	//輪郭を得る
	std::vector<std::vector<cv::Point>> contours;
	std::vector<cv::Rect> edge_rects;
	cv::findContours(edges,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);
	for(int idx = 0;idx < contours.size();idx++)
	{
		//面積チェック
		double area = cv::contourArea(contours[idx]);
		if(area < 1)
		{
			continue;
		}
		//点群に外接する傾いていない矩形を求めます
		cv::Rect R = cv::boundingRect(contours[idx]);
		//輪郭Rect追加
		edge_rects.push_back(R);
	}
	//エッジからブロック化したマスクを作成する
	cv::Mat mask_mat;
	makeBlockMaskFromEdge(edges,mask_mat,edge_rects,32);

	//結果表示
	cv::imshow("basketball1",sample_mat);
	cv::imshow("edges",edges);
	cv::imshow("mask_mat",mask_mat);
	cv::waitKey(0);

	return 0;
}

//---------------------------------------------------------------------------
//エッジからブロック化したマスクを作成する
//---------------------------------------------------------------------------
bool makeBlockMaskFromEdge(const cv::Mat& edge_mat,cv::Mat& mask_mat,std::vector<cv::Rect>& edge_rects,int block_size)
{
	//マスクの作成
	mask_mat = cv::Mat::zeros(edge_mat.size(),CV_8UC1);

	//全てのエッジを検索
	for(int idx = 0;idx < edge_rects.size();idx++)
	{
		//エッジ情報取得
		cv::Rect& edge_rect = edge_rects[idx];
		//エッジの領域
		cv::Rect  div_edge_rect;

		//ブロックを考慮した新しい左上座標
		div_edge_rect.x      = block_size * (edge_rect.x/block_size);
		div_edge_rect.y      = block_size * (edge_rect.y/block_size);
		//ブロックを考慮した新しい幅と高さ
		int new_w = edge_rect.width  + (edge_rect.x - div_edge_rect.x);
		int new_h = edge_rect.height + (edge_rect.y - div_edge_rect.y);

		div_edge_rect.width  = block_size * (1 + new_w /block_size);
		div_edge_rect.height = block_size * (1 + new_h/block_size);

		if(div_edge_rect.x + div_edge_rect.width > edge_mat.cols)
		{
			div_edge_rect.width = edge_mat.cols - div_edge_rect.x;
		}
		if(div_edge_rect.y + div_edge_rect.height > edge_mat.rows)
		{
			div_edge_rect.height = edge_mat.rows - div_edge_rect.y;
		}
		if(div_edge_rect.width < 1 || div_edge_rect.height < 1)
		{
			continue;
		}

		//エッジ画像の切り出し
		cv::Mat   div_edge_mat  = cv::Mat(edge_mat,div_edge_rect);
		//マスク画像の切り出し
		cv::Mat   div_mask_mat  = cv::Mat(mask_mat,div_edge_rect);
		//エッジを最後まで検索
		cv::Mat        temp_mat    = div_edge_mat.clone();
		int            all_sz      = temp_mat.cols * temp_mat.rows;
		unsigned char *p_msk_start = mask_mat.ptr<unsigned char>(0);
		unsigned char *p_edg_start = temp_mat.ptr<unsigned char>(0);
		unsigned char *p_edg_pos   = p_edg_start;

		while(true)
		{
			//エッジを探す
			int sz = all_sz - static_cast<int>(p_edg_pos - p_edg_start);
			unsigned char *p_next_edg_pos = static_cast<unsigned char *>(std::memchr(p_edg_pos,255,sz));
			//無い場合は終了
			if(p_next_edg_pos == nullptr)
			{
				break;
			}
			//見つかった座標から処理する範囲をセット
			int df = static_cast<int>(p_next_edg_pos - p_edg_start);
			int x  = df%temp_mat.step;
			int y  = df/temp_mat.step;
			int l  = block_size * (x/block_size);
			int t  = block_size * (y/block_size);
			int w  = block_size;
			int h  = block_size;

			cv::Rect range_rect(l,t,w,h);
			//マスクの更新
			cv::rectangle(div_mask_mat,range_rect,cv::Scalar(255),-1);
			//使用エッジの消去
			cv::rectangle(temp_mat,range_rect,cv::Scalar(0),-1);

			//次の検索位置
			p_edg_pos = p_next_edg_pos;
		}
	}

	return true;
}

動作結果

  1. cv::Canny()によるエッジ化画像

しきい値の操作をすると検出されるエッジが変化します。

edges.png

  1. ブロック化画像

一つの物体で一つのブロックになるのが理想です。
ブロックサイズを変えて見ると、結果が変化します。

block.png

以上.

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?