つれづれなる備忘録

日々の発見をあるがままに綴る

OpenCVの使い方38 ~ 円検出

今回はOpenCVを用いた円検出について紹介する。

1. ハフ変換による円検出

 直線と同様に画像中の円を検出するためにハフ変換を用いて円検出する関数が実装されている。

以下のチュートリアルと同じOpenCVのロゴを使って円検出を実行する。

labs.eecs.tottori-u.ac.jp

"OpenCVのロゴ"
OpenCVのロゴ

以下はgoogle colabで画像を読み込む場合のコードを示す。

import cv2
import numpy as np
from matplotlib import pyplot as plt

from google.colab import files
uploaded_file = files.upload()
uploaded_file_name = next(iter(uploaded_file))
print(uploaded_file_name)

orig = cv2.imread(uploaded_file_name)
src = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
plt.imshow(gray,cmap='gray')
plt.title('Original')
circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20,
                            param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
    # draw the outer circle
    cv2.circle(src,(i[0],i[1]),i[2],(0,255,0),2)
    # draw the center of the circle
    cv2.circle(src,(i[0],i[1]),2,(0,0,255),3)
plt.figure(figsize=(3,3))
plt.imshow(src)

チュートリアルの通り、ロゴ中の円の要素を検出できていることがわかる。

"OpenCVロゴの円検出"
OpenCVロゴの円検出

2. 一般的な画像の円検出

 ロゴよりも複雑な一般的な画像でハフ変換による円検出を試してみる。例えば、以下の地球の画像をテスト画像とする。

"地球"
地球

チュートリアルと同じ設定で円を検出してみる。

circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20,
                            param1=50,param2=30,minRadius=0,maxRadius=0)

以下のように無数に円が検出される。

地球の円検出

cv2.HoughCirclesの4番目の引数は、検出する円同士の最低距離であり、小さいと同じ円が重複して検出される。また、param1はエッジ検出の閾値、param2は円中心の検出の閾値であり、これらの閾値を大きくすることで、円の検出感度を下げる。

circles = cv2.HoughCircles(gray,cv2.HOUGH_GRADIENT,1,200,
                            param1=80,param2=100,minRadius=0,maxRadius=0)

地球の輪郭を含む数個の円まで検出される円を減らすことができた。人の目では地球の輪郭ぐらいしかわからないが、雲のつながり方で円として検出されているように思える。

"地球の円検出2"
地球の円検出2

3. まとめ

 今回はハフ変換を用いた円の検出と、円の検出感度の調整方法について紹介した。

OpenCVの使い方37 ~ 直線検出

今回はOpenCVを用いた直線検出について紹介する。

1. ハフ変換による直線検出

 画像中の直線を検出するためにOpenCVではハフ変換を用いて直線検出する関数が実装されている。

詳しい解説は labs.eecs.tottori-u.ac.jp

ここでは実際の画像に直線検出を適用した結果について示していく。

2. テスト画像

テスト画像として標準画像データベースのPixel rulerを使用する。

"Pixel Ruler"
Pixel Ruler

画像のいたるところに直線があり、直線検出のテストとしては良さそうだ。

以下はgoogle colabで画像を読み込む場合のコードを示す。

import cv2
import numpy as np
from matplotlib import pyplot as plt

from google.colab import files
uploaded_file = files.upload()
uploaded_file_name = next(iter(uploaded_file))
print(uploaded_file_name)

orig = cv2.imread(uploaded_file_name)
src = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
plt.imshow(gray,cmap='gray')
plt.title('Original')

3. ハフ変換の適用

ハフ変換を適用するには、2値化画像を用いるが、2値化画像としてエッジ検出した画像を用いる。 以下のCanny法により検出されたエッジ画像は以下のようになる。

"エッジ検出画像"
エッジ検出画像

edges = cv2.Canny(gray,50,150,apertureSize = 3)
lines = cv2.HoughLines(edges,1,np.pi/180,400)

ハフ変換を用いた直線検出を実行する関数はcv2.HoughLines()を用いる。検出した直線の戻り値は極座標のρ(画像原点からの直線までの距離) , θ(直線の法線と横軸の角度)となる。最初の引数は2値化画像、2番目はρの精度(pixel)、3番目はθの精度(radian)、最後は直線と判別するための投票数(感度)をあらわす。

4. 検出した直線の描画

 最後に検出した直線の極座標ρ, θから元の画像に直線を重ね描きする。cv2.HoughLines()では検出した直線の長さに関する情報は含まれないので、以下のようにρ, θと適当な長さ1000を用いて描画する直線の開始、終点の2点x1,x2,y1,y2を生成する。

src2=np.copy(src)
for rho,theta in zip(lines[:,0,0],lines[:,0,1]):
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a*rho
    y0 = b*rho
    x1 = int(x0 + 1000*(-b))
    y1 = int(y0 + 1000*(a))
    x2 = int(x0 - 1000*(-b))
    y2 = int(y0 - 1000*(a))
    cv2.line(src2,(x1,y1),(x2,y2),(255,0,0),1)

plt.imshow(src2)
plt.title('Line detection')

ρ, θ
ハフ変換により検出された直線

5. 確率的ハフ変換

 確率的ハフ変換は、直線検出するための画素をランダムに選ぶことで通常のハフ変換よりも計算負荷を減らすアルゴリズムになっている。 OpenCVではcv2.HoughLinesP()を用いるが、第4引数まではcv2.HoughLines()と同じで5番目の引数minLineLengthは検出する線の最小長さ、最後のmaxLineGapは検出された2本の線を1本とみなす間隔になっている。また返り値は線分の始点、終点の座標であり直感的に使いやすい。

src3=np.copy(src)
minLineLength = 100
maxLineGap = 10
lines = cv2.HoughLinesP(edges,1,np.pi/180,100,minLineLength,maxLineGap)
for x1,y1,x2,y2 in zip(lines[:,0,0],lines[:,0,1],lines[:,0,2],lines[:,0,3]):
    cv2.line(src3,(x1,y1),(x2,y2),(0,255,0),2)
plt.imshow(src3)

以下元の画像と検出した線を重ね描きしたものを示すが、格子線のとりこぼしや、目盛り部分の直線が検出できていない。

"確率的ハフ変換による直線検出"
確率的ハフ変換による直線検出

そこで細かい線分を検出するために、minLineLength, minLineGapHoughtLinesP()の投票数を小さくして検出感度を上げる。

すべて検出できてきないが、目盛りの一部や格子部分での検出が向上した。

minLineLength = 5
maxLineGap = 5
lines = cv2.HoughLinesP(edges,1,np.pi/180,10,minLineLength,maxLineGap)

"確率的ハフ変換高検出感度"
確率的ハフ変換高検出感度

6. まとめ

 今回は、画像の直線検出としてOpenCVを用いてハフ変換による直線検出、確率的ハフ変換による直線検出をテスト画像に適用した例を紹介した。

今週のお題「上半期ベスト◯◯」

今週のお題「上半期ベスト◯◯」

今年もあっという間に半年が過ぎてしまったが、今週のお題は「上半期ベスト◯◯」ということで、「上半期ベストトピック」として今年大きく変わった話題について取り上げたいと思う。

新型コロナ5類移行

この話題が、上期で一番変わったことだと思う。個人的には、コロナ前にも6月、7月に風邪にかかったことはあるので相変わらず用心は怠らないようにしているが、過度に気にしなくなったという点では上期一番の話題だったと思う。

Chat GPT (生成AI)

この話題も現在進行形で、今の世の中を大きく変える可能性がある。個人的にも試してみると結構使えるということがわかった。

atatat.hatenablog.com

会社でも情報漏れの対応はあるが、積極的に使っていこうという姿勢になっている。

桜咲く

毎年の定番だが、桜シーズンはやっぱり景色がきれいなので、本ブログでも話題としてたびたび取り上げている。

atatat.hatenablog.com