OpenCV + Pythonでの直線検出

pip install opencv-python==3.4.2.17

前回の記事は画像解析の「KerasでのData Augmentationの解説」を説明しました。今回はOpenCV + Pythonで直線検出を説明します。

OpenCVのハフ(Hough)変換は、画像の中から直線や図形を検出したい際によく用いられる手法の一つです。画像に含まれる直線、破線、点線を検出することができます。今回は以下の画像の線を消してみます。次には駐車場の白線検出をします。

1. 画像の読み込み

In [1]:
import cv2
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
%matplotlib inline

img = cv2.imread("tic_tac_toe.png")

plt.figure(figsize=(5, 5))
img2 = img[:,:,::-1]
plt.xticks([]), plt.yticks([]) 
plt.imshow(img)
Out[1]:
<matplotlib.image.AxesImage at 0x26eac91b8d0>

2. グレースケールに変換します。

In [2]:
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

cv2.imwrite("gray.jpg", gray)
img_p = cv2.imread('gray.jpg')
img_p2 = img[:,:,::-1]
plt.figure(figsize=(5, 5))
plt.xticks([]), plt.yticks([]) 
plt.imshow(img_p)
Out[2]:
<matplotlib.image.AxesImage at 0x26eac982be0>

3. ネガポジ変換で反転

この変換で白と黒を逆転させ、白黒はっきりさせます。

cv2.Canny

Canny(const Mat& image, Mat& edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false)

image – 8ビット,シングルチャンネルの入力画像

edges – 出力されるエッジのマップ. image と同じサイズ,同じ型です

threshold1 – ヒステリシスが存在する処理の,1番目の閾値

threshold2 – ヒステリシスが存在する処理の,2番目の閾値

apertureSize – Sobel() オペレータのアパーチャサイズ

In [3]:
edges = cv2.Canny(gray,50,150,apertureSize = 3)

cv2.imwrite("edges.jpg", edges)
img_p = cv2.imread('edges.jpg')
img_p2 = img[:,:,::-1]
plt.figure(figsize=(5, 5))
plt.xticks([]), plt.yticks([]) 
plt.imshow(img_p)
Out[3]:
<matplotlib.image.AxesImage at 0x26eacab5e10>

4. ハフ変換で直線検出

対象の画像に対して、パラメータ値の調整が必要になります。ハフ変換の設定を説明します。

cv2.HoughLinesP

HoughLines(Mat& image, vector& lines, double rho, double theta, int threshold, double srn=0, double stn=0)

image – 入力画像

lines – 検出された線分が出力されるベクトル

rho – ピクセル単位で表される投票空間の距離分解能

theta – ラジアン単位で表される投票空間の角度分解能

threshold – 投票の閾値パラメータ

minLineLength – 最小の線分長

maxLineGap – 2点が同一線分上にあると見なす場合に許容される最大距離

In [4]:
lines = cv2.HoughLinesP(edges, 
                        rho=1, 
                        theta=np.pi/360, 
                        threshold=100, 
                        minLineLength=200, 
                        maxLineGap=6)
print(lines[:5])
[[[202 289 202   6]]

 [[  5  96 288  96]]

 [[200 289 200   6]]

 [[  5  98 288  98]]

 [[  5 190 288 190]]]

直線検出して、赤線を引いて画像を作成します。

In [5]:
for line in lines:
    x1, y1, x2, y2 = line[0]

    # 赤線を引く
    red_line_img = cv2.line(img, (x1,y1), (x2,y2), (0,0,255), 3)


cv2.imwrite("output.jpg", red_line_img)
img_p = cv2.imread('output.jpg')
imgr_p2 = img[:,:,::-1]
plt.figure(figsize=(5, 5))
plt.xticks([]), plt.yticks([]) 
plt.imshow(img_p2)
Out[5]:
<matplotlib.image.AxesImage at 0x26eacaf25c0>

5. 線を消して、画像を作成します。

In [6]:
for line in lines:
    x1, y1, x2, y2 = line[0]
    # 線を消す(白で線を引く)
    no_lines_img = cv2.line(img, (x1,y1), (x2,y2), (255,255,255), 3)

cv2.imwrite("output2.jpg", no_lines_img)
img_p = cv2.imread('output2.jpg')
imgr_p2 = img[:,:,::-1]
plt.figure(figsize=(5, 5))
plt.xticks([]), plt.yticks([]) 
plt.imshow(img_p)
Out[6]:
<matplotlib.image.AxesImage at 0x26eacb29780>

実験:駐車場の白線検出

以下の駐車場の画像から白線検出します。

In [7]:
img_p = cv2.imread('parking.png')
imgr_p2 = img[:,:,::-1]
plt.figure(figsize=(8,8))
plt.xticks([]), plt.yticks([]) 
plt.imshow(img_p)
Out[7]:
<matplotlib.image.AxesImage at 0x26eacb5e128>
In [8]:
import cv2
import numpy as np
%matplotlib inline
img = cv2.imread("parking.png")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,90,450,apertureSize = 3)
lines = cv2.HoughLinesP(edges, rho=1, theta=np.pi/360, threshold=60, minLineLength=10, maxLineGap=10)
for line in lines:
    x1, y1, x2, y2 = line[0]
    red_line_img = cv2.line(img, (x1,y1), (x2,y2), (0,0,255), 3)

cv2.imwrite("output3.jpg", red_line_img)
Out[8]:
True

結果

CannyとHoughLinesPのパラメタを調整して、駐車場の白線検出できました。

In [9]:
img_p = cv2.imread('output3.jpg')
imgr_p2 = img[:,:,::-1]
plt.figure(figsize=(8,8))
plt.xticks([]), plt.yticks([]) 
plt.imshow(img_p)
Out[9]:
<matplotlib.image.AxesImage at 0x26eacb95c88>
In [ ]: