つれづれなる備忘録

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

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を用いてハフ変換による直線検出、確率的ハフ変換による直線検出をテスト画像に適用した例を紹介した。