OpenGL de プログラミング - sequence編02
現在地 >> メニュー >> OpenCV >> sequence編02
INDEX:sequence編01<< sequence編02 >> sequence編03

目次

ハフ変換の直線検出

ハフ変換の詳細は略。(codezine:javaを使った丁寧な説明をされております)
ただ、次の事は覚えておきたい。
  • (x, y)を通過する全ての直線群は、一本のHough曲線で表される。
  • パラメータ平面上の一点は、直線一本に対応する。

直線検出には「cvHoughLines2関数」を使う。
CvSeq* cvHoughLines2(
	CvArr*  image, void* line_storage, int method, 
	double  rho, double theta, 
	int threshold, double param1=0, double param2=0)
[image]:1チャネルの2値画像。なおかつ深度は8bit。
[line_storage]:保存先にするメモリストレージ
[method]:使用するハフ変換の種類
	CV_HOUGH_STANDARD → 標準(CV_32FC2のデータを使う)
	CV_HOUGH_PROBABILISTIC → 確率的ハフ変換(CV_32SC4のデータを使う)
	CV_HOUGH_MULTI_SCALE → マルチスケールハフ変換(標準型と同じ)
[rho、theta]:「ピクセル単位での距離分解能」と「ラジアン単位で表される角度分解能」
[threshold]:閾値。これより大きい投票数のものが直線となる。
[param1]:略。(リファレンス参照)
[param2]:略。(リファレンス参照)
▲返り値は検出した直線のシーケンス。
このシーケンスからデータを取り出して、画像に直線を引いたりする。

使い方

  1. メモリストレージ、シーケンスの用意。
  2. 画像をCannyエッジオペレータ等で2値化。
  3. ハフ変換
  4. 検出した直線を格納したシーケンスを使って処理をする。

【例】
IplImage *imgA = cvLoadImage(filename,0);
… …
cvCanny(imgA,imgA,10,50);

//メモリストレージとシーケンスを用意
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* lines = 0;
//ハフ変換による直線の検出
lines = cvHoughLines2( imgA,storage,CV_HOUGH_STANDARD,1,CV_PI/180,100,0,0 );
… …

データを取り出す1

得られるのはあくまでも(ρ,θ)である。

float *line;
float rho,theta;
CvPoint pt1, pt2;

double x0,y0;
for(int loop = 0; loop < lines->total;++loop)
{
	//ρとθの取り出し
	line = reinterpret_cast<float *>( cvGetSeqElem(lines,loop) );
	rho = line[0];
	theta = line[1];
		
	//ρとθから座標直線を通過するある点を取得
	x0 = rho * cosf(theta);
	y0 = rho * sinf(theta);
		
	//注目点からのある量だけ移動した2点を取得
	pt1 = cvPoint( 
		cvRound(x0 + 1000*(-sinf(theta) ) ), 
		cvRound(y0 + 1000*( cosf(theta) ) ) 
		);
	pt2 = cvPoint(
		cvRound(x0 - 1000*(-sinf(theta) ) ),
		cvRound(y0 - 1000*( cosf(theta) ) )
		);

	… …
}

データを取り出す2

確率的ハフ変換である「CV_HOUGH_PROBABILISTIC」を指定した時は、
「CV_32SC4」がシーケンスに格納される。

IplImage *imgA = cvLoadImage(filename,0);
… …
//メモリストレージとシーケンスを用意
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* lines = 0;
//ハフ変換による直線の検出(確率的)
lines = cvHoughLines2( imgA,storage,CV_HOUGH_PROBABILISTIC,1,CV_PI/180,80,30,10 );

IplImage *imgA_out = cvCreateImage(cvGetSize(imgA),IPL_DEPTH_8U,3);
cvCvtColor(imgA,imgA_out,CV_GRAY2RGB);//直線描画用

CvPoint *line;
for(int loop = 0; loop < MIN(lines->total,100);++loop)
{
	line = reinterpret_cast<CvPoint *>( cvGetSeqElem(lines,loop) );
	cvLine(imgA_out, line[0], line[1], CV_RGB(255,0,0), 3, 8 );
}

cvNamedWindow("CV_HOUGH_PROBABILISTIC");
cvShowImage("CV_HOUGH_PROBABILISTIC",imgA_out);

サンプルコード1


ハフ変換による円の検出


ハフ変換による円の検出を行う時には、cvHoughCircles関数を使う。
CVAPI(CvSeq*) cvHoughCircles( 
	CvArr* image, void* circle_storage, int method, 
	double dp, double min_dist,
	double param1 CV_DEFAULT(100), double param2 CV_DEFAULT(100),
	int min_radius CV_DEFAULT(0),	 int max_radius CV_DEFAULT(0)
)
【image】:8bitグレイスケール画像。2値画像の必要はない。
【circle_storage】:検出した円を格納するシーケンス。(CV_32FC3のデータが格納されていく)
【method】:「CV_HOUGH_GRADIENT」を指定する
【dp】:画像分解能に対する投票分解能の比率の逆数(1を指定するとそのまま。2 の場合は,投票空間の幅と高さは半分...etc)
【min_dist】:検出される円の中心同士の最小距離
【param1、param2】:Canny エッジに渡される大きい方の閾値/円の中心を検出する際の投票数の閾値
【 min_radius/max_radius】:円の半径の最小値/最大値

データを取り出す

円の検出で得られるデータは、円の中心座標と半径である。
IplImage *imgA = cvLoadImage(filename,0);
… …
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* circles = 0;

//ハフ変換による円の検出
circles = cvHoughCircles(imgA,storage,CV_HOUGH_GRADIENT,1,imgA->width/10);
IplImage *imgA_out = cvCreateImage(cvGetSize(imgA),IPL_DEPTH_8U,3);
cvCvtColor(imgA,imgA_out,CV_GRAY2RGB);//直線描画用

float *circle;//(x,y,r)がくる
CvPoint pt;

for(int loop = 0; loop < circles->total;++loop)
{
	circle = reinterpret_cast<float *>(cvGetSeqElem(circles,loop));
	pt = cvPoint(cvRound(circle[0]),cvRound(circle[1]));//中心座標(x,y)を取得
	cvCircle(imgA_out,pt,cvRound(circle[2]),CV_RGB(255,0,0),2);//円として検出したものを描画
}

cvNamedWindow("cvHoughCircles");
cvShowImage("cvHoughCircles",imgA_out);

サンプルコード2