社会人研究者が色々頑張るブログ

pythonで画像処理やパターン認識をやっていきます

Python+OpenCVによる白線検出(1)

はじめにのはじめに

完成版の記事です nsr-9.hatenablog.jp

はじめに

前回、An Efficient Lane Detection Algorithm For Lane Departure DetectionというIV学会に投稿された論文を読みました。
本手法は自動運転技術の実現に向け、Low-Powerの計算機に向けた高効率な白線検出技術を提案していました。  

今回から、この技術をpythonで実装していきます。
本当は一日で実装を終わらせたかったのですが、やってみると結構ボリューミーでした

実装手順

本論文の処理フローの内、今回はCandidate Extraction(白線の候補点検出)を実装しました。

f:id:nsr_9:20210814174055p:plain

白線の候補点検出ではHaar-Likeに似た特徴量を用いてエッジ検出を行います。
Haar-Like特徴量とそれに伴う積分画像技術に関しては、西住工房さんがとても優れた解説記事を投稿されているので、今回は本手法にフォーカスを絞って説明していきます。

Haar-Like風の特徴量

Haar-like Feature は、白領域の対応する画像の和と黒領域に対応する画像の和の差を用いた画像特徴量です。
ステップ関数に対する信号応答を解析するHaar waveletにちなんで、Haar-Like(ハールのような)画像特徴量と名付けられています。
本手法では更にHaar-Likeを簡易化することで"Haar-Like"-Like(ハールライクのような)画像特徴量になっています。
本手法とHaar-Likeの違う部分は、探索窓と矩形窓の設定方法にあります。
Haar-Likeでは、探索窓と矩形窓の位置及びサイズをランダムに、何度も選択(サンプリング)します。
それと異なり、本手法では事前に定められた区間に対して、固定の矩形窓をスライディングしていきます。

f:id:nsr_9:20210814174320p:plain

矩形窓に対する応答値の計算は、通常のHaar-Like特徴量と同様です。
Haar-Likeパターンの明るい領域と暗い領域それぞれで、平均輝度A1, A2を求め、その差分Hを求めます。

f:id:nsr_9:20210814174339p:plain

この応答値の計算は、Window内で繰り返し行うため、積分画像による高速化テクニックの使用が推奨されます。
ですが、今回は実装の容易さを優先し積分画像を使わないシンプルな実装を行いました。
以下に特徴点検出のコードを記載します。

import numpy as np
import cv2


def calc_haarlike(crop_img):
    crop_img = crop_img[:,::-1]

    threshold = 10
    rect_w = 8
    pattern_w = rect_w // 2 
    width = crop_img.shape[1]
    
    peak_index = 0
    max_value = 0
    
    for index in range(width-rect_w):
        a1 = np.mean(crop_img[:, index: index+pattern_w])
        a2 = np.mean(crop_img[:, index+pattern_w: index+rect_w])
        H = a1-a2 
        if max_value < H and H - max_value > threshold:
            max_value = H
            peak_index = index

    index = width - peak_index + rect_w
    return index if max_value > 0 else -1

calc_haarlike関数に検出範囲の画像を渡すと、白線の位置を「Window内の相対座標」として返してくれます。 参考として、以下のような切り出し画像を与えた際の、Haar-Likeの応答値を記載します。

入力画像 haar-like
f:id:nsr_9:20210814183558p:plain f:id:nsr_9:20210814183808p:plain

検出範囲の指定

ウィンドウの探索範囲を指定します。論文中には具体的な値や範囲の指定方法が記されていなかったので、えいやで決めました。
可視化するとこんな感じです。

f:id:nsr_9:20210814174450j:plain

テスト用の動画は、有志の方がアップしてくれたLane Detection Test Videoをお借りしました。 www.youtube.com

コードはこんな感じです。

import cv2
import numpy as np


def candidate_extraction(img):
    h, w = img.shape[0], img.shape[1]
    wh = 8 # windowの高さ
    window_pos = np.array([
        [[425, 600], [580, 608]],
        [[380, 615], [565, 623]],
        [[345, 635], [550, 643]],
        
        [[700, 600], [855, 608]],
        [[715, 615], [900, 623]],
        [[730, 635], [935, 643]],
    ])
    
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 
    for i, ((x1, y1), (x2, y2)) in enumerate(window_pos):
        crop_img = gray[y1: y2, x1: x2]
        peak_index = calc_haarlike(crop_img)

実行結果

以上の実装を組み合わせ、Candidate Extraction(白線の候補点検出)を行ってみました。
以下にフレーム18、262, 296の実行結果を示します。

frame 18 frame 262 frame 296
f:id:nsr_9:20210814190336j:plain f:id:nsr_9:20210814190343j:plain f:id:nsr_9:20210814190350j:plain

水色の矩形が探索範囲で、ピンク色の丸が白線の候補点です。
frame 18を見ると、正しく候補点が検出されているように見えます。
frame 262は車両が極端に右よりに走行しているシーンです。
探索範囲が白線の位置から外れていることが確認できます。
frame 296は右よりに走行していた車両が、中央に復帰したシーンです。
白線の候補点が再び正しく検出されていることが確認できます。

f:id:nsr_9:20210814193717g:plain

まとめ

今回は An Efficient Lane Detection Algorithm For Lane Departure Detection のCandidate Extractionを実装しました。 所々、怪しげな所(積分画像使ってなかったり、Haar-Likeのパターンが1種類だったり)があるので、引き続き改良改善を行っていきます。