エアガン測定

エアガンに関する色々を測定したりしなかったりしています http://www.eonet.ne.jp/~daisaku-tech/index.html

PythonとOpenCVで車線検出

PythonOpenCVで、車線を検出するプログラムを作成しました。チョー単純なロジックなので、条件が想定外になると検出できないんですが、ある程度検出できています。


PythonとOpenCVによる車線検出

 

映像から1フレームの画像を取り出し、グレースケールに変換してからヒストグラムを平坦化します。空の部分は不要なので消去します。それとは別に、フレームの画像をHSVに変換してから一定の範囲の黄色だけを抽出します。これは、白線の検出用にグレースケールに変換してしまうと、黄色がわからなくなるため、別途黄色だけ抽出する、ということです。

抽出した黄色部分もグレースケールに変換してから、Cannyを使用して輪郭を抽出します。その輪郭画像に対してHoughLinesを使用して直線を検出しています。

使用している道路の映像は、PS4のゲーム、The Crew2のものです。画像処理用としては、実写の映像よりもだいぶ条件がいい状態です。実写の映像を自分で撮影してくるのはメンドイので横着しました。

# -*- coding: utf-8 -*-
"""
車線検出
Cannyによるエッジ検出とHoughLinesによる直線検出
python 3.6
opencv 4.1
"""
import cv2
import numpy as np
import sys

# コマンドライン引数 1番目=入力映像ファイル名 2番目=出力映像ファイル名 3番目=出力輪郭線ファイル名
args = sys.argv

if len(args) == 1:
    sys.exit(1)
    
input_file = args[1]

output_file = None
contour_file = None
if len(args) >= 3:
    output_file = args[2]
if len(args) == 4:
    contour_file = args[3]


def equalize(img_org):
    """ 平坦化
    """
    img_tmp = cv2.cvtColor(img_org, cv2.COLOR_BGR2GRAY)

    # ヒストグラム平坦化
    img_tmp = cv2.equalizeHist(img_tmp)

    # 画面の上半分消去
    count = np.array([[0, 0], [1279, 0], [1279, 340], [0, 340]])
    img_tmp = cv2.fillPoly(img_tmp, pts=[count], color=(0))

    return img_tmp


def yellow(img_org):
    """ 黄色線抽出
    HSVに変換してから黄色だけを抽出する
    """
    img_hsv = cv2.cvtColor(img_org, cv2.COLOR_BGR2HSV)

    # 日本の道路の黄色線
    # lower_color = np.array([0, 40, 50], np.uint8)
    # upper_color = np.array([15, 200, 200], np.uint8)
    
    # The Crew2の黄色線
    lower_color = np.array([15, 100, 50], np.uint8)
    upper_color = np.array([40, 200, 200], np.uint8)

    # マスク画像の生成
    img_mask = cv2.inRange(img_hsv, lower_color, upper_color)

    # フレーム画像とマスク画像の共通の領域を抽出
    img_tmp = cv2.bitwise_and(img_hsv, img_hsv, mask=img_mask)
    img_tmp = cv2.cvtColor(img_tmp, cv2.COLOR_BGR2GRAY)
    ret, img_tmp = cv2.threshold(img_tmp,
                                 10,                # 閾値
                                 256,               # 画素値の最大値
                                 cv2.THRESH_BINARY) # 2値化type

    # 画面の上半分消去
    count = np.array([[0, 0], [1279, 0], [1279, 340], [0, 340]])
    img_tmp = cv2.fillPoly(img_tmp, pts=[count], color=(0))

    return img_tmp
    

def road(img):
    """ 車線検出・描画
    HoughLinesを使用して一定の長さの直線を検出・描画
    """
    road = np.zeros((720, 1280, 3), np.uint8)
    lines = cv2.HoughLines(img,
                           1,          # lines
                           np.pi/180,  # rho
                           150)        # theta
    if lines is not None:
        for line in lines:
            for rho, theta in line:
                a = np.cos(theta)
                b = np.sin(theta)
                x0 = a*rho
                y0 = b*rho
                x1 = int(x0 + 2000*(-b))
                y1 = int(y0 + 2000*(a))
                x2 = int(x0 - 2000*(-b))
                y2 = int(y0 - 2000*(a))

                # 水平に近い線は描画しない
                if abs(a) < 0.15:
                    continue
                # print("rho,theta,a,b,x0,y0", rho, theta, a, b, x0, y0)

                cv2.line(road, (x1, y1), (x2, y2), (0 ,0, 255), 2)

    # 画面の上半分消去
    count = np.array([[0, 0], [1279, 0], [1279, 340], [0, 340]])
    road = cv2.fillPoly(road, pts=[count], color=(0, 0, 0))

    return road


def main():
    cap = cv2.VideoCapture(input_file)

    # 描画した映像の録画用
    fmt = cv2.VideoWriter_fourcc("m", "p", "4", "v")
    size = (1280, 720)
    if output_file is not None:
        output_writer = cv2.VideoWriter(output_file, fmt, 30, size)
    if contour_file is not None:
        contour_writer = cv2.VideoWriter(contour_file, fmt, 30, size)
    
    while(cap.isOpened()):
        # 1フレームの画像読み込み
        img_org = cap.read()[1]

        if img_org is None:
            break

        # グレースケールに変換してから平坦化
        img_tmp = equalize(img_org)
        # 黄色線は別に抽出してからグレースケールに変換
        img_yellow = yellow(img_org)
        # 2つの画像をorする
        img_tmp = cv2.bitwise_or(img_tmp, img_yellow)

        # blurをかけて細かな部分をつぶす
        img_tmp = cv2.blur(img_tmp, ksize=(5, 5))
        # 輪郭抽出
        img_for_draw = cv2.Canny(img_tmp,
                                 200,     # threshold1
                                 255,     # threshold2
                                 apertureSize = 3)
        # 車線検出・描画
        road_lines = road(img_for_draw)

        # グレースケールからRGBに変換
        img_for_draw = cv2.cvtColor(img_for_draw, cv2.COLOR_GRAY2BGR)

        # 元の画像と描画した車線を合成
        img = cv2.bitwise_or(img_org, road_lines)

        cv2.imshow("img", img)

        # 映像ファイルに保存
        if output_file is not None:
            output_writer.write(img)
        if contour_file is not None:
            contour_writer.write(img_for_draw)
        
        # qキーが押されたら終了
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    main()