OpenCVで画像から円を検出したいです。
cv2.HoughCircles 関数を使えば円を検出することができる。
ただ、その前に前処理をしないときれいに円が検出されないぞ。
✔️ 本記事のテーマ
OpenCVで円を検出する方法
✔️ 読者さんへの前置きメッセージ
本記事は「OpenCV の cv2.HoughCircles 関数」について書いています。
この記事を読むことで
「cv2.HoughCircles の使い方 や 円検出に必要な前処理」
について理解できます。
OpenCVを使うことで画像中の円を検出することができます。
OpenCVの画像処理は画像に対する前処理が重要となることが多いですが、
円の検出も同様に前処理がとても大切です。
この記事では、OpenCV を使って円を検出する方法と
それに必要な前処理を合わせて解説します。
それでは、解説していきましょう。
OpenCV で円を検出するサンプルコード
OpenCV で円を検出するサンプルコードは以下のようになります。
この記事ではこのサンプルコードに沿って解説していきます。
import cv2
import numpy as np
img = cv2.imread("./sample.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1, minDist=20, param1=100, param2=60, minRadius=0, maxRadius=0)
circles = np.uint16(np.around(circles))
for circle in circles[0, :]:
# 円周を描画する
cv2.circle(img, (circle[0], circle[1]), circle[2], (0, 165, 255), 5)
# 中心点を描画する
cv2.circle(img, (circle[0], circle[1]), 2, (0, 0, 255), 3)
cv2.imwrite("sample_after.png", img)
このサンプルコードによって
↑このような画像から
↑このような円(オレンジ線部分)を検出することができます。
では、順番に解説していきましょう。
OpenCV で円を検出する手順
OpenCVで画像中から円を検出する手順は以下の通りです。
- 画像の読み込み
- 画像のグレースケール化
- 画像中から円を検出する
- 検出した情報をキャストして丸める
- 検出した円を書き込む
- 画像の書き出し
実際に、円を検出する処理は手順3 だけなのですが、
前処理となる手順2 や後処理となる手順 5はとても大切です。
また、円を検出している手順3 でも引数などを調整する必要があります。
では、円を検出する方法を手順ごとに解説していきましょう。
画像の読み込み
まず、OpenCVで画像処理を行うために対象の画像を読み込みます。
画像の読み込みにはOpenCV のcv2.imread関数を使います。
以下のように、対象の画像パスを引数で指定することで、画像の読み込みを行えます。
img = cv2.imread("./sample.png")
なお、OpenCV の cv2.imread関数については
「OpenCVで画像を読み込む方法【Python】」
の記事で詳しく解説しています。
画像のグレースケール化
次に、画像のグレースケール化を行います。
画像をグレースケールにすることで後続のcv2.HoughCircles 関数にインプットすることができます。
画像のグレースケール化には OpenCV のcv2.cvtColor関数を使います。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
なお、OpenCV の cv2.cvtColor関数については
「OpenCVでグレースケール画像を簡単に作成する【Python】」
の記事で詳しく解説しています。
画像中から円を検出する
ここまでの前処理が完了したら、円を検出していきましょう。
円の検出には cv2.HoughCircles 関数を使用します。
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1, minDist=20, param1=100, param2=60, minRadius=0, maxRadius=0)
cv2.HoughCircles 関数は戻り値として検出した円の情報を Ndarray で返します。
この戻り値が少しややこしいのですが、以下のような形状になっています。
[ [ [円の中心点のx座標, 円の中心点のy座標, 円の半径], [円の中心点のx座標, 円の中心点のy座標, 円の半径], … ] ]
検出した円の数だけ要素がある配列が3重にネストされて返ってきます。
(なぜ3重にネストされているのかはわかりません…)
cv2.HoughCircles 関数は多くの引数を取ります。
そしてこの引数が正しく円を検出する上で重要になってきます。
それぞれ解説しましょう。
cv2.HoughCircles 関数の第1引数: image
第1引数: image は画像データ です。
ここまでで前処理を実施した画像データを渡しましょう。
サンプルコードでは グレースケール画像 gray を渡しています。
cv2.HoughCircles 関数の第2引数: method
第2引数: method はハフ変換の手法です。
OpenCVの 独自コードで指定します。
この引数はOpenCVの内部的な話になる上に、
ドキュメントもないのでどのようなコードが使えるのか具体的にわかりません。
おまじない的に cv2.HOUGH_GRADIENT を渡しておくのが良いと思います。
cv2.HoughCircles 関数の第3引数: dp
第3引数: dp は投票器の解像度です。
float 型で指定します。
dp の値が大きいほど検出基準が緩くなり、値が小さいほど検出基準が厳しくなります。
ケースや画像にもよりますが、0.8 ~ 1.2 くらいの幅で調整するのが良いかと思います。
2以上を渡すと誤検出が多くなり、0だとerrorが発生します。
cv2.error: OpenCV(4.4.0) /private/var/folders/nz/vv4_9tw56nv9k3tkvyszvwg80000gn/T/pip-req-build-gi6lxw0x/opencv/modules/imgproc/src/hough.cpp:2257: error: (-211:One of the arguments' values is out of range) dp, min_dist and canny_threshold must be all positive numbers in function 'HoughCircles'
cv2.HoughCircles 関数の第4引数: minDist
第4引数: minDist は 検出される円同士が最低限離れていなければならない距離です。
float 型で指定します。
この引数で同じ円に対する重複検出を調整します。
cv2.HoughCircles 関数はハフ検出を行うので、同じ円に対して複数の検出結果を返してしまいます。
そこで、検出する円同士の距離を定義することで、重複検出をなくします。
試しに、minDist=10 とすると、
以下のように一つの円に対して重複検出されてしまいます。
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1, minDist=10, param1=100, param2=60, minRadius=0, maxRadius=0)
cv2.HoughCircles 関数の第5引数: param1
第5引数: param1 は Canny法のHysteresis処理の閾値 です。
float 型で指定します。
cv2.HoughCircles 関数では内部でCanny法によるエッジ検出を行っています。
Canny法によるエッジ検出では、上限値以下、下限値以上のものを正しいエッジとして判断します。
この上限値を param1 で設定しています。
また、下限値は param1 の1/2が自動的に設定されます。
Canny法によるエッジ検出の細かな調整をしたい場合は、この引数を変更すれば良いのですが、
よく分からないのであれば、100前後を固定で渡せば良いと思います。
個人的には、この引数で調整するくらいなら、
他の引数で調整した方がきれいな円を検出できると思います。
cv2.HoughCircles 関数の第6引数: param2
第6引数: param2 は円の中心を検出する際の閾値です。
この引数が円検出処理の重要な鍵となっています。
閾値を低い値にすると円の誤検出が多くなり、高い値にすると未検出が多くなります。
インプットする画像データに対して、
どの値が円の重複検出 / 未検出 が少なくなるかを試しながら調整していくのが良いです。
試しに、param2=30 とすると、
以下のように一つの円に対して重複検出されてしまいます。
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1, minDist=20, param1=100, param2=30, minRadius=0, maxRadius=0)
cv2.HoughCircles 関数の第7引数: minRadius
第7引数: minRadius は検出する円の半径の下限です。
Int 型で指定します。
minRadius で設定した値以下の半径の円を検出対象外とします。
試しに、minRadius=50 とすると、以下のように半径が小さな円が検出されなくなります。
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1, minDist=20, param1=100, param2=60, minRadius=50, maxRadius=0)
ちなみに、 minRadius はデフォルト引数なので、
引数を渡さなくともcv2.HoughCircles 関数は動きます。
なお、デフォルト引数については
「pythonのデフォルト引数についての解説」
の記事で解説しています。
cv2.HoughCircles 関数の第8引数: maxRadius
第8引数: maxRadius は検出する円の半径の上限です。
Int 型で指定します。
maxRadius で設定した値以上の半径の円を検出対象外とします。
試しに、maxRadius = とすると、以下のように半径が大きな円が検出されなくなります。
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1, minDist=20, param1=100, param2=60, minRadius=0, maxRadius=200)
ちなみに、 maxRadius もデフォルト引数なので、
引数を渡さなくともcv2.HoughCircles 関数は動きます。
検出した情報をキャストして丸める
cv2.HoughCircles 関数の戻り値は np.floatという Numpy の独自クラス型になっています。
この検出した情報をもとに、画像中に円を描画するためにはcv2.circle関数を使用します。
ただ、cv2.circle関数のいくつかの引数は Int型である必要があるので、
このタイミングで np.float型から整数値に丸め、さらに16ビット情報にキャストします。
circles = np.uint16(np.around(circles))
この処理をしないと、後続の cv2.circle 関数を使う時点でerrorが発生します。
なお、Numpyライブラリ の詳しい情報については
「Numpyの使い方と便利な関数」の記事で解説しています。
検出した円を書き込む
circles には「検出した円の数だけの座標配列」が格納されているので、
それらをfor文で回しながら、円を書き込んでいきます。
for circle in circles[0, :]:
# 円周を描画する
cv2.circle(img, (circle[0], circle[1]), circle[2], (0, 165, 255), 5)
# 中心点を描画する
cv2.circle(img, (circle[0], circle[1]), 2, (0, 0, 255), 3)
少し補足説明をすると、
circles は3重のネスト構造になっているので、
forループの対象を circles[0, :] とすることで余分なネストを外しています。
そして、このループ中の circle には
[ 中心点のx座標, 中心点のy座標, 円の半径 ] という情報が格納されています。
よって、それらを各ループの中で circle[0], circle[1], circle[2] として取り出しています。
円の描画にはcv2.circle関数を使用しています。
OpenCV の cv2.circle関数 の詳細や使い方については
「【OpenCV】cv2.circle関数の使い方【円を描画する】」
の記事で詳しく解説しています。
画像の書き出し
最後に、検出した円を書き込んだ画像データを画像ファイルとして書き出します。
画像データの書き出しにはOpenCV のcv2.imwrite関数を使います。
第1引数に画像データ、第2引数に保存先のファイルパスを指定します。
cv2.imwrite("sample_after.png", line_img)
なお、OpenCV の のcv2.imwrite関数については
「OpenCVで画像を保存する方法【Python】」
の記事で詳しく解説しています。
画像処理プログラミングを独学で勉強するなら
画像から円を検出する方法について解説しました。
OpenCVは画像処理には欠かすことのできないライブラリです。
もし、OpenCVについて独学でスキルをつけるなら、以下の書籍がオススメです。
この書籍はOpenCVの基礎から応用までを
丁寧にかつ詳細に解説しています。
OpenCVのほぼ全てを網羅しているとも言えるほどの徹底ぶりなので、
関数のリファレンスとしても使用することができます。
本記事で解説したcv2.HoughCircles関数も掲載されています。
エンジニアとしての自身の価値をチェックする(完全無料)
エンジニアとして、
自分の価値がどれくらいのものかご存知でしょうか?
エンジニアとしてIT業界に身を置いていると
今の会社でずっと働くのか、フリーランスとして独立するのか …
と様々な選択肢があります。
どの選択肢が正解なのかを見極めるためにも、選択肢を広げるためにも
自身の価値を知っておくことはとても重要です。
TechClips ME では、
職務経歴書をアップロードするだけで企業からのスカウトを受けることができます。
▼▼▼▼▼
▲▲▲▲▲
しかもTechClips MEでは想定年収を企業から提示してくれるので、
自身の価値を数字で分かりやすくたしかめることができます。
登録はもちろん完全無料なので、一度登録してみると良いかもしれません。
コメント