関連記事: 画像解析
前回の記事は「OpenCV + Pythonでの直線検出」を解説しました。今回はPythonでハフ変換(Hough)とLSDによる直線検出を比較します。
目次
1. ハフ変換(Hough Transform)
2. LSD (Line Segment Detector)
3. ハフとLSDの比較
4. 実験・コード
__4.1 データロード
__4.2 ライブラリのインストール
__4.3. 直線検出
__4.4. 結果比較
1. ハフ変換とは
ハフ変換 (Hough変換) は、画像処理で画像の特徴抽出法の一つです。現在広く用いられている変換法はRichard Duda及びPeter Hartが1972年に発明しました。ハフ変換の基本原理は点を通る直線は無限個存在し、それぞれが様々な方向を向きます。ハフ変換の目的は、それらの直線の中で、画像の「特徴点」を最も多く通るものを決定します。
直線の式は次のようになる:
ハフ変換では画像空間からρ-θパラメータ空間への変換を行います。ある画像空間上に孤立点があり、点の座標が(x, y)である場合、パラメータ空間への変換を行うとどのような結果が得られるのか、考えることにします。
ρ : 座標(x, y)を通る直線に対し、原点から垂線を下ろしたときの長さ
θ : 座標(x, y)を通る直線に対し、原点から垂線を下ろしたときにx軸となす角度
論文:Use of the Hough Transformation To. Detect Lines and Curves in Pictures
2. LSD (Line Segment Detector)
LSDは、画像上の局所的に直線の輪郭を検出することを目的です。輪郭とは、グレーレベルが暗から明、またはその逆に十分に速く変化している画像のゾーンです。 したがって、画像の勾配とレベルラインは重要な概念です。
LSDでは以下のような画像中の場所をLevel-Line、小さな領域内での輝度勾配とLevel Lineの角度を画像全体で計算したものをLevel Line Fieldと定義しています。そして生成したLevel Line Field内から直線領域候補(Line Support Regions)を計算します。
LSDのアルゴリズムは以下のようになっています。
論文:LSD: a Line Segment Detector
3. ハフとLSDの比較
ハフ
– 黒白バイナリ画像の入力データ
– 固定の形式であればパラメータにより最適化できます。
– パラメータ探索をグリッドサーチしないと行けないので時間がかかります。(パラメータの設定より)
– ランダム化された性質により、複数の実行で異なる結果になります。
LSD
– グレースケールの入力データ
– パラメータの設定が不要です。
– サブピクセルの正確な結果を提供します。
4. 実験・コード
概要:
入力データ: ウィキペディアからの画像
環境:Google Colab GPU
ライブラリ: OpenCV, Pylsd
実験:直線検出 (3枚の画像)
4.1 データロード
# 画像をロード
# 画像をロード import urllib img_src = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Peer_Vs_Chakvetadze.JPG/634px-Peer_Vs_Chakvetadze.JPG" img_path = '/tennis_court.jpg' urllib.request.urlretrieve(img_src, img_path)
(‘/tennis_court.jpg’, <http.client.HTTPMessage at 0x7f309a86cef0>)
# 画像を表示 from IPython.display import Image,display_jpeg img_path = '/tennis_court.jpg' display_jpeg(Image(img_path))
4.2 ライブラリのインストール
# pip install pylsd # python3に対応していないので、下記のバージョンを利用してください。 pip install 'ocrd-fork-pylsd == 0.0.3'
Collecting ocrd-fork-pylsd==0.0.3
Downloading https://files.pythonhosted.org/packages/6a/df/12fba60b9b3e141f515d69edd539bd066294d6b3be79b12450888819986d/ocrd_fork_pylsd-0.0.3-py3-none-any.whl (47kB)
|████████████████████████████████| 51kB 2.9MB/s
Installing collected packages: ocrd-fork-pylsd
Successfully installed ocrd-fork-pylsd-0.0.3
4.3. 直線検出の関数を作成
import cv2 import time from pylsd.lsd import lsd import numpy as np def line_detect_model(img_path): img = cv2.imread(img_path) img = cv2.resize(img,(int(img.shape[1]/1.1),int(img.shape[0]/1.1))) gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) gray = cv2.GaussianBlur(gray,(5,5),5) t1 = time.time() edges = cv2.Canny(gray,50,150,apertureSize = 3) linesH = cv2.HoughLinesP(edges, rho=1, theta=np.pi/360, threshold=50, minLineLength=50, maxLineGap=10) t2 = time.time() linesL = lsd(gray) t3 = time.time() img2 = img.copy() for line in linesH: x1, y1, x2, y2 = line[0] # 赤線を引く img2 = cv2.line(img2, (x1,y1), (x2,y2), (0,0,255), 3) cv2.imwrite('output_hough.jpg',img2) img3 = img.copy() img4 = img.copy() for line in linesL: x1, y1, x2, y2 = map(int,line[:4]) img3 = cv2.line(img3, (x1,y1), (x2,y2), (0,0,255), 3) if (x2-x1)**2 + (y2-y1)**2 > 1000: # 赤線を引く img4 = cv2.line(img4, (x1,y1), (x2,y2), (0,0,255), 3) print("Hough") print(len(linesH),"lines") print(t2-t1,"sec") print("time per a line :{:.4f}".format((t2-t1)/len(linesH))) print(" ") print("LSD") print(len(linesL),"lines") print(t3-t2,"sec") print("time per a line {:.4f}".format((t3-t2)/len(linesL))) cv2.imwrite('output_pylsd.jpg',img3)
4.4. 結果比較 実験1
# モデル作成 img_path = '/tennis_court.jpg' line_detect_model(img_path)
Hough
30 lines
0.03397536277770996 sec
time per a line :0.0011
LSD
134 lines
0.10620927810668945 sec
time per a line 0.0008
# ハフ変換の結果 from IPython.display import Image,display_jpeg img_output = 'output_hough.jpg' display_jpeg(Image(img_output))
# LSDの結果 from IPython.display import Image,display_jpeg img_output = 'output_pylsd.jpg' display_jpeg(Image(img_output))
実験2
# 画像をロード import urllib img_src = "https://vignette.wikia.nocookie.net/rubiks-cube/images/5/50/Wiki-background/revision/latest?cb=20110127110929" img_path = '/rubik.jpg' urllib.request.urlretrieve(img_src, img_path) # 画像を表示 from IPython.display import Image,display_jpeg img_path2 = '/rubik.jpg' display_jpeg(Image(img_path2))
# モデル作成 img_path = '/rubik.jpg' line_detect_model(img_path)
Hough
22 lines
0.01903223991394043 sec
time per a line :0.0009
LSD
108 lines
0.03812599182128906 sec
time per a line 0.0004
# ハフ変換の結果 from IPython.display import Image,display_jpeg img_output = 'output_hough.jpg' display_jpeg(Image(img_output))
# LSDの結果 from IPython.display import Image,display_jpeg img_output = 'output_pylsd.jpg' display_jpeg(Image(img_output))
実験3
# 画像をロード import urllib img_src = "https://upload.wikimedia.org/wikipedia/commons/thumb/e/eb/SkHwy11ShoulderBumps.jpg/640px-SkHwy11ShoulderBumps.jpg" img_path = '/road.jpg' urllib.request.urlretrieve(img_src, img_path) # 画像を表示 from IPython.display import Image,display_jpeg img_path3 = '/road.jpg' display_jpeg(Image(img_path3))
# モデル作成 img_path = '/road.jpg' line_detect_model(img_path)
Hough
20 lines
0.010786294937133789 sec
time per a line :0.0005
LSD
229 lines
0.0957939624786377 sec
time per a line 0.0004
# ハフ変換の結果 from IPython.display import Image,display_jpeg img_output = 'output_hough.jpg' display_jpeg(Image(img_output))
# LSDの結果 from IPython.display import Image,display_jpeg img_output = 'output_pylsd.jpg' display_jpeg(Image(img_output))
まとめ
実験1
Houghは直線検出がよくできました。実行時間をみると、Houghは実行時間が長くなりました。ただし、LSDの各線の実行時間が早いです。
実験2
LSDは直線検出がよくできました。実行時間をみると、Houghは実行時間が長くなりましたが、LSDの各線の実行時間が早いです。
実験3
LSDは直線検出がよくできました。Houghは曇の直線を検出しまいました。Houghは実行時間が長くなりましたが、LSDの各線の実行時間が早いです。