[w13d3] 데이터 처리

GGG·2022년 5월 11일
0
post-thumbnail

(Ubuntu 18.04.6 LTS)
2022.05.11
프로그래머스 자율주행 데브코스 3기

CVAT으로 라벨링하는 경우 라벨링한 부분이 없는 이미지는 export되지 않는다는 것을 확인할 수 있었다. 이미지가 있지만 라벨링한 부분이 없는 경우를 위해서 같은 이름의 빈 annotation 파일을 만들어 주는 것이 필요했고, 코드로 구현했다.

import glob
import sys
import argparse


def parse_args():
    parser = argparse.ArgumentParser(description="video2img")
    parser.add_argument("--path1", type=str,
                        help="path1: , ex) ./JPEGImages", default=None)
    parser.add_argument("--ext1", type=str,
                        help="ex) .png", default=None)
    parser.add_argument("--txtpath", type=str,
                        help="txt path ex) ./Annotations", default=None)
    if (len(sys.argv) == 1):
        # python file opened without any argument
        parser.print_help()
        sys.exit(1)
    args = parser.parse_args()
    if args.path1 is None or args.txtpath is None:
        # 2 elements needed
        parser.print_help()
        sys.exit(1)
    return args


if __name__ == "__main__":
    args = parse_args()
    path1_filelist = glob.glob(args.path1 + "/*" + args.ext1)
    path2_filelist = glob.glob(args.txtpath + "/*.txt")

    for file_name in path1_filelist:
        tmp = file_name.replace(args.ext1, ".txt")
        tmp = tmp.replace(args.path1, args.txtpath)
        if tmp in path2_filelist:
            continue
        else:
            with open(tmp, 'a') as f:
                f.write("")

라벨링 과정에서 거리가 멀어 불분명한 대상 등을 ignore 클래스로 처리하였는데, 실제 학습에 있어서 이 클래스는 확인할 필요가 없어 annotation에서 제거가 필요하였다. 해당 부분의 처리를 하고자 ignore class 번호인 6번을 제거하는 코드를 작성했다.

import glob

file_list = glob.glob("./output/*.txt")
for file in file_list:
    with open(file, 'r') as f:
        lines = f.readlines()
    result = ""
    for line in lines:
        if line[0] == '6':
            continue
        else:
            result = result + line
    with open(file, 'w') as f:
        f.write(result)

여러 오류들을 수정하고 AWS에서 우분투 이미지를 생성하고, 학습을 실행했다. Google colab에서도 학습하는 방식을 추가적으로 찾아보려고 한다.

이외에 신호등 객체를 인식하였을 때, 신호등의 색깔을 이용하는 방식을 고려하였는데, 얻은 영상에서 조도가 높아 HSV 공간에서 색 분리에 어려움이 있었다. 현재는 다른 방식으로 세로 신호등임을 가정하였을 때, V(3 채널의 밝기 중 최대값)값이 충분히 큰 경우에 대해서 이진화를 수행하고 히스토그램을 구해 가장 값이 큰 인덱스의 위치를 기준으로 신호 판별을 수행하는 코드를 작성했다.

import cv2
import glob
import sys


def color_detect_by_histogram(frame):
    frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    frame_bin = cv2.inRange(frame_hsv, (0, 0, 225), (255, 255, 255))

    sign_len = len(frame_bin)
    hist_list = [0] * sign_len

    for y in range(len(frame_bin)):
        for x in range(len(frame_bin[0])):
            if frame_bin[y, x] != 0:
                hist_list[y] += 1

    max_idx = hist_list.index(max(hist_list))

    if max_idx < sign_len/3:
        print("red")
        return 1
    elif max_idx < sign_len*2/3:
        print("yello")
        return 1
    else:
        print("green")
        return 0


def onChange(pos):
    pass


def demo_color():
'''HSV 공간에서 색을 이용해 분리하려고 하였으나 카메라의 노출도 값이 높아 색구분이 어려움'''
    file_list = glob.glob("./resources/*")
    cv2.namedWindow("src")
    cv2.createTrackbar("hl: ", "src", 0, 255, onChange)
    cv2.createTrackbar("sl: ", "src", 0, 255, onChange)
    cv2.createTrackbar("vl: ", "src", 0, 255, onChange)
    cv2.createTrackbar("hu: ", "src", 0, 255, onChange)
    cv2.createTrackbar("su: ", "src", 0, 255, onChange)
    cv2.createTrackbar("vu: ", "src", 0, 255, onChange)
    cv2.setTrackbarPos("hu: ", "src", 255)
    cv2.setTrackbarPos("su: ", "src", 255)
    cv2.setTrackbarPos("vu: ", "src", 255)
    cv2.setTrackbarPos("vl: ", "src", 128)
    for file in file_list:
        frame = cv2.imread(file, cv2.IMREAD_COLOR)
        frame = cv2.resize(frame, None, None, 0.5, 0.5, cv2.INTER_AREA)
        frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        while True:
            tmp = cv2.waitKey(1)
            if tmp == 27:
                sys.exit(1)
            elif tmp == 110:
                break
            hl = cv2.getTrackbarPos("hl: ", "src")
            sl = cv2.getTrackbarPos("sl: ", "src")
            vl = cv2.getTrackbarPos("vl: ", "src")
            hu = cv2.getTrackbarPos("hu: ", "src")
            su = cv2.getTrackbarPos("su: ", "src")
            vu = cv2.getTrackbarPos("vu: ", "src")
            frame_bin = cv2.inRange(frame_hsv, (hl, sl, vl), (hu, su, vu))
            cv2.imshow("frame_bin", frame_bin)


def demo_color2():
'''히스토그램 접근 방식'''
    file_list = glob.glob("./resources/*")
    frame = cv2.imread(file_list[0], cv2.IMREAD_COLOR)
    frame = cv2.resize(frame, None, None, 0.5, 0.5, cv2.INTER_AREA)
    frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    while True:
        frame_bin = cv2.inRange(frame_hsv, (0, 0, 225), (255, 255, 255))
        frame_roi = frame_bin[147:295, 534:620]

        sign_len = len(frame_roi)
        hist_list = [0] * sign_len

        for y in range(len(frame_roi)):
            for x in range(len(frame_roi[0])):
                if frame_roi[y, x] != 0:
                    hist_list[y] += 1
        max_idx = hist_list.index(max(hist_list))

        if max_idx < sign_len/3:
            print("red")
        elif max_idx < sign_len*2/3:
            print("yello")
        else:
            print("green")
        cv2.imshow("frame_bin", frame_roi)
        if cv2.waitKey() == 27:
            sys.exit(1)


if __name__ == "__main__":
    demo_color()
    demo_color2()

정지선 구분을 위해서는 OpenCV를 사용해 비전 알고리즘으로 처리하고자 하였다. 조감도로 ROI를 옮기고 이진화 처리를 수행한 뒤, 영역의 10%가 넘는 영역에서 선이 검출된 경우를 정지선으로 읽고자 하는 코드를 구현했다.

import cv2
import numpy as np

cap = cv2.VideoCapture("video_2.avi")

# perspective transform
src_points = np.float32([[74, 350], [540, 350], [639, 479], [0, 479]])
dst_points = np.float32([[0, 0], [300, 0], [300, 300], [0, 300]])
per_mat = cv2.getPerspectiveTransform(src_points, dst_points)

skip_frame = 500
while True:
    ret, frame = cap.read()
    if not ret:
        break
    if skip_frame > 0:
        skip_frame -= 1
        continue
    # gray
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # bird
    gray_bird = cv2.warpPerspective(gray, per_mat, (300, 300))
    # frame_roi
    gray_roi = gray_bird[30:136, 130:171]
    gray_roi = cv2.GaussianBlur(gray_roi, (3, 3), 1)
    gray_roi_bin = cv2.inRange(gray_roi, 0, 150)

    total_area = (135-30)*(170-130)
    line_area = np.count_nonzero(gray_roi_bin)
    print(line_area, total_area)
    if (float(line_area)/total_area) > 0.1:
        cv2.rectangle(gray_bird, (130, 30), (170, 135), (255), 1)
        print("stop line")
    else:
        cv2.rectangle(gray_bird, (130, 30), (170, 135), (0), 1)

    cv2.imshow("frame", frame)
    cv2.imshow("gray_roi_bin", gray_roi_bin)
    cv2.imshow("gray_bird", gray_bird)
    if cv2.waitKey() == 27:
        print("break")
        break

현재는 단순하게 확인의 형태인데, 내일 함수의 형태로 변경하고자 한다. 추가적으로 이전의 조향각을 이용해 ROI 위치를 조정하는 것도 합리적이라 생각되는데, 내일 제어 부분에서 시간이 된다면 다뤄보고자 한다.


직진인 경우에는 중간에서 정지선을 읽는 것이 합리적이나, 좌회전을 하는 환경에서는 보다 빠른 정지선 검출을 위해 해당영역을 옮기는 것이 적합해 보인다. 곡률이 큰 경우에 직진 구간에서 ROI 위치를 사용하는 경우 한쪽 차선이 ROI 영역에 들어와 오류가 발생할 수도 있을 것이라 예상했다.

profile
GGG

0개의 댓글