2015. 1.15.
2017.10. 3. OpenCV3.3.0とVisual C++ 2017で動作確認済み、ただし、文末の付記参照
石立 喬

OpenCVとVisual C++による画像処理と認識(9)

----- Hough変換を用いて、円を検出する -----

 ハフ(Hough)変換による直線の検出は、すでに述べた。ここでは、ハフ変換による円の検出を取り上げる。OpenCVには、勾配を利用した円検出用HoughCircles関数が準備されており、標準的なHough変換よりも高速である。道路標識検出への応用も探る。

勾配に基づくハフ変換による円検出
 ハフ変換については、すでに「Visual C++ 2010 Expressを用いた易しい画像処理(2) --- ハフ(Hough)変換を用いて直線と円弧を検出する ---」に詳しく述べてある。
 画像上のXY座標における点P(x, y)が、半径rで中心(xc, yc)の円に属している場合には、
  r2 = (x - xc)2 + (y - yc)2
が成り立つ。これにより、点(x, y)をr = F(xc, yc)の関数に変換するのが、円のための標準的なHough変換である。円の場合には三次元のaccumulatorを使うので、計算時間は非常に長くなり、所要メモリー領域も増える。
 直線の場合と同様に、高速アルゴリズムが存在し、OpenCVもこれを使用している。これは、勾配に基づくハフ変換(Gradient-Based Hough Transform)で、与えられたエッジの勾配(gradient)から、その垂直方向に円の中心があると判断し、まず円中心についてのvotingを行う。これは二次元Hough変換処理である。続いて、求まった円中心から半径方向にピクセルのヒストグラムを採り、その最大の場所に円周があると推測する。この処理は一次元である。三次元処理を二次元と一次元に分けるので、2-1ハフ変換と呼ばれる。
 図1は、Cannyエッジ検出法で得た点Pの勾配から、半径をrと仮定した場合の円中心の座標を求める計算式を示す。実際には、HoughCircles関数に外部から引数として与えられたminRadiusとmaxRadiusの間にrがあるとされるので、この区間のXY座標位置に対してvotingを行う。
 勾配の方向は、使用するCannyエッジ検出法で決まり、グレイスケール濃度の高い方から低い方へ(明るい方から暗い方へ)の向きとするので、この垂線方向が円中心を指すと仮定するのに心配な面もある。本資料の最後にある「勾配に基づくハフ変換とその影響」を参照されたい。


図1 勾配(gradient)から円の中心を求める


ノイズの少ない大きな対象物でHoughCircles関数を実行する
 図2および図3にプログラムを示し、下記から成っている。良い結果を得やすい条件である。
1)読み込んだ画像にGaussianBlurを実施してノイズを除去して原画像src_imageとし、表示する。
2)グレイスケール画像gray_imageに変換し、表示する。
3)グレイスケール画像に対して、HoughCirclesを実行する。
4)得られた円を、原画像のコピーであるsrc_image1上に描画する。
 HoughCirclesでは、結果として得られるcirclesベクトルは、三つの要素から成り、
     circles[0] ----- 円中心のX座標
     circles[1] ----- 円中心のY座標
     circles[2] ----- 円の半径
のようになっているので、circle関数で円を描く。
 ここまでが図2である。
5)原画像src_imageをRGB成分に分解する。
6)R成分についてHoughCirclesを実施し、検出した円を赤色で原画像src_image上に描画する。
7)G成分、B成分についても同様に緑色、青色で描画する。
8)原画像src_imageを表示する。

使用したOpenCV関数の説明
◎HoughCircles関数
 勾配に基づくHough変換を行う関数で、

  HoughCircles(グレイスケール原画像、結果円ベクトル、CV_HOUGH_GRADIENT、解像度比の逆数、円中心の最小距離、Cannyのしきい値(高い方)、円中心のしきい値(vote数)、最小半径、最大半径)

のように引数を与える。CV_HOUGH_GRADIENTは検出方法であるが、現在は、これしかない。解像度比の逆数は1が一般的で、入力画像の解像度と等しい。Cannyのしきい値は、高い方のみの設定であるが、低い方は、自動的にその半分として設定される。最小半径と最大半径は、それぞれ0がデフォルトであるが、ノイズのないよほど理想的な場面でしか使えない。


図2 原画像からノイズを除去し、グレイ化してHoughCirclesを実施する



図3 ノイズを除去したカラー原画像をRGB成分に分解し、それぞれにHoughCirclesを実施する


 図4は実行結果である。左上はGaussianBlurを実施した原画像、左下は、それをグレイスケール化した画像である。右下はグレイスケール画像から円を検出した結果で、しきい値などの調整はしたものの、比較的余裕をもって検出できた。
 図の右上は、カラー原画像をRGB各成分に分解して、それらから円を検出したもので、各成分の色で色を描いてある。結論的には、ここで用いたサンプル画像に関する限り、グレイスケール画像の使用で十分であった。
 グレイスケール画像は、RGB各成分に一定の係数をかけて加算したものなので、出力がRGB個別の出力よりは相対的に大きい。これを、同じしきい値レベルで検出したので、グレイスケールの方が分が良かったのは理解できる。
 「駐車禁止」の内側の円は、色々と工夫して試してみたが、検出できなかった。これは、HoughCurcles関数の仕様(円中心を先に検出し、その中心を持つ円の最大を求める)によるものと思われる。


図4 グレイスケール画像で十分に検出できている


ノイズの多い小さな対象物の画像でHoughCircles関数を実行する
 図5と図6にプログラムを示すが、改めての説明は不要と思われる。厳しい結果が予想される条件である。


図5 グレイスケール画像でHoughCirclesを実行する



図6 RGB成分に分解してHoughCirclesを実施する


 図7は実行結果である。左上はGaussianBlurを実施した原画像、左下は、それをグレイスケール化した画像である。右下はグレイスケール画像から円を検出した結果で、小さい円の場合はは、ノイズの影響を受けやすいので、しきい値などの調整に非常に苦労した。
 ここで用いたサンプル画像に関する限り、グレイスケール画像の使用で十分であった。RGB分解の場合、しきい値をさらに下げれば、上の「左折禁止」も円として検出できたが、偽の円も出てしまった。


図7 厳しく条件を付けて、ようやく検出できた二個の円



Hough変換を用いた直線や円の検出はむつかしい
 Hough変換の解説資料には、容易に検出できる、比較的理想的な対象物の例が多い。ここでも、図4の例のように、ノイズが少なく、画像全体に占める対象物の大きさが大きい場合には、かなりの余裕度(HoughCircles関数に与える引数の許容範囲が広い)をもって検出できている。
 しかし、図7の場合は、ノイズが多く対象物が構成するピクセルの数が少ないので、非常に厳しい。そこで、使用したHoughCircles関数の引数の一部を変えてみて、影響を調べた。
 あらかじめノイズを除去しておくべきがどうかについては、面白い結果が出た。ノイズがある程度ある方が、対象物に対するvotingも増やしてくれるので、しきい値がぎりぎりの場合には、ノイズがあった方が良いことがある。もちろん、ノイズがありすぎると、偽の円が検出されてしまう。
 RGB別に分解した方が良いか、グレイスケールが良いかは、対象物と背景の色の関係で異なる。RGB別に分解すると、それぞれの出力は、グレイスケールよりは低下し、一定のしきい値を超えられない場合が生じる。図7の右上に示した例は、それに該当する。この場合、しきい値50を少し下げると「左折禁止」の標識も、円として認められた。
 図8~10に、その他の変更例を示す。

   図8 最大半径の影響(50の場合)

 このプログラムでは、最終的に、最大半径を45と
設定してある。この値を中心に、±5だけ変化させて
みた。
 最大半径を40にしたら、上の「左折禁止」は検出さ
れなかった。図は最大半径を50に増やした場合で、
左に大きな偽の円が検出された

   図9 ノイズ除去の影響(ノイズありの場合)

 図8のままの条件で、原画像のノイズ除去を止めた
ら、沢山の偽の円が現れた
 ノイズがなかった時にあった左の大きな偽の円が
小さくなっている。

   図10 円間最小距離の影響(50の場合)

 図9で、多数の偽の円が現れたのは、円中心の最
小距離10が小さ過ぎたと考え、50に増やしたら、
偽の円がかなり減った


勾配に基づくハフ変換とその影響
 資料によると、ハフ変換に用いられる勾配(gradient)は、Cannyエッジ検出時のものが用いられ、勾配の向きは、グレイスケール濃度の高い方から低い方へ(明るい方から暗い方へ)である。
 勾配の向きによって円の中心の方向が決まるので、対象物の周辺に対する濃淡の関係が気になる。そこで、濃淡の順序が逆になるサンプルを用意して調べてみた。
 図11の左は、ハフ変換関数

  HoughCircles(gray_image, circles, CV_HOUGH_GRADIENT, 1, 10, 100, param2, 0, 0);

において、param2(円の中心としてカウントされた数のしきい値)を36に設定した場合で、左半分の、周辺の白(濃度255)に対して対象物をグレイ(濃度128)にした場合は、一番小さい円が検出されていない。
 この状態は、param2を26まで下げてようやく改善され、図11の右に示すように、白地の中の小さい円が検出された。しかし、右半分の黒地にグレイのサンプルで偽の円が出てしまった。


図11 円中心のカウント数のしきい値を変えた結果


 数多くの例を試していないので、これだけで結論付けるのはどうかと思うが、HoughCircles関数は、暗い背景に対して明るい対象物を探すのが得意のようである。これは、エッジの勾配を頼りに円を探す方法の宿命なのかもしれない。少し手数をかければ、円弧としての曲り(curvature)を検出することもできるので、これを利用すれば、改善される可能性がある。

結 論
 OpenCVに使われているHoughCircles関数の原理は、勾配 に基づくもので、標準のHough変換とは異なり、若干理解しにくい。まず、エッジ検出の際のエッジの勾配から、円の中心方向を求め、円中心のaccumulatorに積み上げて行き(二次元ハフ変換)、そこを中心に半径方向にエッジピクセルのヒストグラムを採り、円半径を求める(一次元処理)。
 Hough変換による円の検出も、完全な自動ではなく、条件設定が非常に厳しい。各種のしきい値を外部から、試行錯誤で与える必要がある。たとえ、Hough変換で円が検出されたとしても、最終的な判断にはまだ遠く、ここから先が本当に重要な部分があるのは変わりがない。

付 記
 OpenCV3.3.0とVisual C++ 2017を使用するにあたり、下記を参照されたい。それ以外は、古いプログラムをそのまま使用できる。
 1)従来のインクルードファイルをそのまま使用しても良いが、#include <opencv2/opencv.hpp>と変更しても問題はない。
 2)従来のint _tmain(int argc, _TCHAR* argv[])をそのまま使用しても良いが、int main()にしても差支えない。
 3)vectorを使用するのに、using namespace std;の追加が必要である(従来は無くても良かったような気がする)。






「Visual C++の勉強部屋」(目次)へ