はじめに
画像処理を学習する機会に恵まれたので、昨今話題の自動運転関係の遊びをやってみます。
白線などの誘導の目印になるものに沿って、車体を走らせるのが定番アイデアのようです。そのため、今回は白線を検出することを目指します。
今回のゴールは下図。
画像処理の流れは、3ステップ。
白線を直線として検出できないか?という定番の作戦です。
- 入力画像をグレースケールに変換
- グレースケール画像の二値化
- 直線(線分)を検出する
コードを書いていく
Step0. 準備
Python 3.10.8、opencv 4.7.0を利用します。まずimportしていく。
import cv2
import numpy as np
from matplotlib import pyplot as plt
Step1. 入力画像をグレースケールに変換する
入力画像を読み込み、グレースケールに変換します。
(分析に活用するため、ヒストグラムも一応取得します)
# 画像の読み込み
img = cv2.imread("input_img.jpg")
# グレースケールに変換
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ヒストグラムの取得
# 引数:入力画像、チャンネル指定、マスク画像(利用する場合)、BINの数、画素値の範囲[0〜256]
img_hist_cv = cv2.calcHist([gray_img], [0], None, [256], [0, 256])
Step2. 二値化
グレースケール画像を二値化します。白線を残していきます。
# 二値化する画素値のしきい値設定
thresh = 180
# 引数:グレースケール画像、しきい値、しきい値以上の画素に割り当てる値、処理内容。
# 返り値:retval、2値画像
# 判別分析法を利用しない場合は、retval = (入力したしきい値)
ret, bin_img = cv2.threshold(gray_img, thresh, 255, cv2.THRESH_BINARY)
Step3. Hough変換による、直線検出(線分検出)
白線は、画像中では直線(線分)とみなすことができるはずです。
直線(線分)の検出には、Hough変換を利用します。
# Hough変換による直線検出
# rho:投票空間の距離分解能
# theta:投票空間の角度分解能
# threshold : 直線とする投票数のしきい値
# minLineLength : 最小の線分の長さ
# maxLineGap : 2点が同一線分上にあるとみなす最大間隔
# 返り値は、線分情報(始点のxy座標、終点のxy座標の値)
lines = cv2.HoughLinesP(bin_img, rho=1, theta=np.pi/360, threshold=80, minLineLength=80, maxLineGap=1)
# 入力画像に検出した直線を赤線で引く。
for line in lines:
x1, y1, x2, y2 = line[0]# lineの要素は座標配列[x1、y1、x2、y2]のみ
red_lines_img = cv2.line(img, (x1,y1), (x2,y2), (0,0,255), 3)
Step4. 各処理結果を描画する
OpenCVは、画像のチャンネルを「BGR」として扱っています。一方、matplotlibは「RGB」で扱うことに注意が必要です。
えっ?色おかしくない?と何度もなりました。
# --- グレースケール ---
plt.subplot(321)
plt.imshow(cv2.cvtColor(gray_img, cv2.COLOR_BGR2RGB))
plt.title('Input image (Gray scale)')
# --- 二値化 ---
plt.subplot(323)
plt.imshow(cv2.cvtColor(bin_img, cv2.COLOR_BGR2RGB))
plt.title('Binary image')
# --- Hough変換 ---
plt.subplot(325)
plt.imshow(cv2.cvtColor(red_lines_img, cv2.COLOR_BGR2RGB))
plt.title('Hough transform')
# --- Histogram ---
plt.subplot(324)
plt.plot(img_hist_cv, label="histogram")
plt.axvline(x=ret,color='orange',linestyle='--', label="threshold") # しきい値はオレンジ色の破線で描画
plt.legend(fontsize=7)# 凡例の表示
plt.title('Histogram & Threshold')
plt.tight_layout()# 図やグラフ同士が重ならないようにする。
plt.savefig("result.png")# 出力結果を画像で保存する
plt.show()
おわりに
今回は白線を検出してみました。しかし、空やガードレールも直線(線分)として検出され、白線だけ綺麗に検出できませんでした。仮に誤検出した空に沿って走行した場合、すぐにガードレールに直撃してしまいますね。
入力画像やパラメーター設定など、工夫が必要だとわかりました。
(参考)入力画像には、写真素材ルーム様の素材を利用いたしました。