試しにPythonでハフ変換で直線検出するコードを書いてみました。OpenCVの関数だと投票数(θ-ρのヒストグラム)がわからなかったので自作してみます。アルゴリズムはシンプルなのでコードとしてもとても短くわかりやすいものになりました。
最後にOpenCVとの検算もしてみました。
ハフ変換での直線検出
ハフ変換でどこに直線があるか検出します。アルゴリズムの解説は以前やったので
今回は実装のみ。
Pythonでの実装
ビックリするほど短いです。処理速度はどうだかわかりませんが。
流れとしては
θ-ρ空間への投票(OpenCVに倣い、θとρの分解能を引数に)
ピーク検出(閾値以上でかつ4隣接の中で大きい値の座標を取得)
です。θを-180°から180°でループさせています。
詳細は割愛。記述量が少ないので読めば多分わかります。閾値は適宜変更が必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | import cv2 import numpy as np ## # @brief ハフ変換 # @param img 2値画像(0が背景それ以外がエッジ) # @param rho Rho方向の刻み # @param theta 角度方向の刻み(ラジアン) # @return θ-ρ空間のヒストグラム def hough_lines(img, rho, theta): y, x = np.where(img) hough = [] #rhoの最大値は画像の対角 rho_max = np.ceil(np.sqrt(img.shape[ 0 ] * img.shape[ 0 ] + img.shape[ 1 ] * img.shape[ 1 ]) / rho) rng = np. round (np.pi / theta) for i in np.arange( - rng, rng): rho2 = np. round ((x * np.cos(i * theta) + y * np.sin(i * theta)) / rho) hist, _ = np.histogram(rho2, bins = np.arange( 0 , rho_max)) hough.append(hist) return np.array(hough) ## # @brief ピーク検出 # @param H θ-ρ空間のヒストグラム # @param threshold 投票数の閾値 # @return ピーク位置でのθ-ρの値 def detectPeak(H, threshold): H[H < threshold] = 0 peak = (H[ 1 : - 1 , 1 : - 1 ] > = H[ 0 : - 2 , 1 : - 1 ]) * (H[ 1 : - 1 , 1 : - 1 ] > = H[ 2 :, 1 : - 1 ]) * \ (H[ 1 : - 1 , 1 : - 1 ] > = H[ 1 : - 1 , 0 : - 2 ]) * (H[ 1 : - 1 , 1 : - 1 ] > = H[ 1 : - 1 , 2 :]) * \ (H[ 1 : - 1 , 1 : - 1 ] > 0 ) return np.array(np.where(peak)) + 1 thres = 100 # 検出閾値 rho = 0.5 # ρの分解能 theta = np.pi / 180 # θの分解能 img = cv2.imread( "line-detect.jpg" )[:, :, 1 ] edges = cv2.Canny(img, 50 , 150 , apertureSize = 3 ) hough = hough_lines(edges, rho, theta) peak = detectPeak(hough, thres) # ピークでの投票数 bins = [hough[peak[ 0 , i], peak[ 1 , i]] for i in range (peak.shape[ 1 ])] # 検算 lines = cv2.HoughLines(edges, rho, theta, thres) |
OpenCVでの検算
コードは1行で完了。
1 | lines = cv2.HoughLines(edges, rho, theta, thres) |
2値化済みのこんな画像(64×64 pixel)

θの分解能を1度刻み、ρの分解能を0.5pixel単位としました。そのθ-ρ画像がこんな感じ。高さ方向に-180~180°、横方向にρです。(なので厳密にはρ-θ空間かな…)

軸の向きがよく見る向きと異なりますが、いい感じに見えます。
また、2値化前のこんな画像(300 x 300 pixel )も試しました。(こいつはCannyで細線取りました)

入力画像(こいつのGreen Channelを使いました)

エッジ検出画像(Canny)

θ-ρ空間のヒストグラム。θの分解能を1度刻み、ρの分解能を1pixel単位としました。
これらの画像に対しては検出されたρとθはOpenCVでの検算結果と完全一致しました。投票数に対して昇順ではないですが投票数を見ても正しそうです。
またOpenCVではρに符号がついています。代わりにθに符号はついていない様です。原点からの距離がマイナスでマイナス方向の回転を表現している様です。
まとめ
簡単に実装できちゃいました。これだとθ-ρ空間のヒストグラムも取れるので他に使い道があるかもしれません。
コメント