1. Image Read == 카메라 영상신호를 이미지로 읽기
2. Warping == 원rms 변환으로 이미지 변형. 4점을 지정해서 저번에 했던 perspective 를 진행함
3. Gaussian Blur == 노이즈 제거
4. Threshold == 이진 이미지로 변환 , cvtColor 를 통해 BGR 을 HLS으로 포맷 변환후 이진화 처리
5. Historgram == 히스토그램에서 차선의 x 좌표 위치 추출
6. Sliding Window == 슬라이딩 윈도우 좌우 x 좌표에 9개 씩 쌓기
7. Polyfit == 2차 함수 그래프로 차선 그리기
8. 차선 영역 표시 == 원본 이미지에 차선 영역 겹치게 오버레이
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
import cv2, random, math, copy
Width = 640
Height = 480
cap = cv2.VideoCapture("xycar_track1.mp4")
window_title = 'camera'
warp_img_w = 320
warp_img_h = 240
warpx_margin = 20
warpy_margin = 3
nwindows = 9
margin = 12
minpix = 5
lane_bin_th = 145
warp_src = np.array([
[230-warpx_margin, 300-warpy_margin],
[45-warpx_margin, 450+warpy_margin],
[445+warpx_margin, 300-warpy_margin],
[610+warpx_margin, 450+warpy_margin]
], dtype=np.float32)
warp_dist = np.array([
[0,0],
[0,warp_img_h],
[warp_img_w,0],
[warp_img_w, warp_img_h]
], dtype=np.float32)
calibrated = True
if calibrated:
mtx = np.array([
[422.037858, 0.0, 245.895397],
[0.0, 435.589734, 163.625535],
[0.0, 0.0, 1.0]
])
dist = np.array([-0.289296, 0.061035, 0.001786, 0.015238, 0.0])
cal_mtx, cal_roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (Width, Height), 1, (Width, Height))
def calibrate_image(frame):
global Width, Height
global mtx, dist
global cal_mtx, cal_roi
tf_image = cv2.undistort(frame, mtx, dist, None, cal_mtx)
x, y, w, h = cal_roi
tf_image = tf_image[y:y+h, x:x+w]
return cv2.resize(tf_image, (Width, Height))
def warp_image(img, src, dst, size):
M = cv2.getPerspectiveTransform(src, dst)
Minv = cv2.getPerspectiveTransform(dst, src)
warp_img = cv2.warpPerspective(img, M, size, flags=cv2.INTER_LINEAR)
return warp_img, M, Minv
def warp_process_image(img):
global nwindows
global margin
global minpix
global lane_bin_th
blur = cv2.GaussianBlur(img,(5, 5), 0)
_, L, _ = cv2.split(cv2.cvtColor(blur, cv2.COLOR_BGR2HLS))
_, lane = cv2.threshold(L, lane_bin_th, 255, cv2.THRESH_BINARY)
histogram = np.sum(lane[lane.shape[0]//2:,:], axis=0)
midpoint = np.int(histogram.shape[0]/2)
leftx_current = np.argmax(histogram[:midpoint])
rightx_current = np.argmax(histogram[midpoint:]) + midpoint
window_height = np.int(lane.shape[0]/nwindows)
nz = lane.nonzero()
left_lane_inds = []
right_lane_inds = []
lx, ly, rx, ry = [], [], [], []
out_img = np.dstack((lane, lane, lane))*255
for window in range(nwindows):
win_yl = lane.shape[0] - (window+1)*window_height
win_yh = lane.shape[0] - window*window_height
win_xll = leftx_current - margin
win_xlh = leftx_current + margin
win_xrl = rightx_current - margin
win_xrh = rightx_current + margin
cv2.rectangle(out_img,(win_xll,win_yl),(win_xlh,win_yh),(0,255,0), 2)
cv2.rectangle(out_img,(win_xrl,win_yl),(win_xrh,win_yh),(0,255,0), 2)
good_left_inds = ((nz[0] >= win_yl)&(nz[0] < win_yh)&(nz[1] >= win_xll)&(nz[1] < win_xlh)).nonzero()[0]
good_right_inds = ((nz[0] >= win_yl)&(nz[0] < win_yh)&(nz[1] >= win_xrl)&(nz[1] < win_xrh)).nonzero()[0]
left_lane_inds.append(good_left_inds)
right_lane_inds.append(good_right_inds)
if len(good_left_inds) > minpix:
leftx_current = np.int(np.mean(nz[1][good_left_inds]))
if len(good_right_inds) > minpix:
rightx_current = np.int(np.mean(nz[1][good_right_inds]))
lx.append(leftx_current)
ly.append((win_yl + win_yh)/2)
rx.append(rightx_current)
ry.append((win_yl + win_yh)/2)
left_lane_inds = np.concatenate(left_lane_inds)
right_lane_inds = np.concatenate(right_lane_inds)
#left_fit = np.polyfit(nz[0][left_lane_inds], nz[1][left_lane_inds], 2)
#right_fit = np.polyfit(nz[0][right_lane_inds] , nz[1][right_lane_inds], 2)
lfit = np.polyfit(np.array(ly),np.array(lx),2)
rfit = np.polyfit(np.array(ry),np.array(rx),2)
out_img[nz[0][left_lane_inds], nz[1][left_lane_inds]] = [255, 0, 0]
out_img[nz[0][right_lane_inds] , nz[1][right_lane_inds]] = [0, 0, 255]
cv2.imshow("viewer", out_img)
#return left_fit, right_fit
return lfit, rfit
def draw_lane(image, warp_img, Minv, left_fit, right_fit):
global Width, Height
yMax = warp_img.shape[0]
ploty = np.linspace(0, yMax - 1, yMax)
color_warp = np.zeros_like(warp_img).astype(np.uint8)
left_fitx = left_fit[0]*ploty**2 + left_fit[1]*ploty + left_fit[2]
right_fitx = right_fit[0]*ploty**2 + right_fit[1]*ploty + right_fit[2]
pts_left = np.array([np.transpose(np.vstack([left_fitx, ploty]))])
pts_right = np.array([np.flipud(np.transpose(np.vstack([right_fitx, ploty])))])
pts = np.hstack((pts_left, pts_right))
color_warp = cv2.fillPoly(color_warp, np.int_([pts]), (0, 255, 0))
newwarp = cv2.warpPerspective(color_warp, Minv, (Width, Height))
return cv2.addWeighted(image, 1, newwarp, 0.3, 0)
def start():
global Width, Height, cap
_, frame = cap.read()
while not frame.size == (Width*Height*3):
_, frame = cap.read()
continue
print("start")
while cap.isOpened():
_, frame = cap.read()
image = calibrate_image(frame)
warp_img, M, Minv = warp_image(image, warp_src, warp_dist, (warp_img_w, warp_img_h))
left_fit, right_fit = warp_process_image(warp_img)
lane_img = draw_lane(image, warp_img, Minv, left_fit, right_fit)
cv2.imshow(window_title, lane_img)
cv2.waitKey(1)
if __name__ == '__main__':
start()
1. warp_src == 주어진 영상의 원본 사다리꼴 좌표의 값을 잘 찾아서 넣어준다.
2. war_dist == 임의의 직사각형의 좌표 값을 넣어준다.
3. calibrated == 자이카 카메라에 장착된 Calibration 보정값을 사용. \
4. warp_image == M은 perspective transform 을 하는 매트릭스 , Minv 는 그 반대의 매트릭스 , warp_img 는 M의 변환이 적용이된 이미지
5. warp_process_image == 1. GaussianBlur 2. cvtColor 를 통해 BGR에서 HLS으로 변환
(HLS 포맷에서는 L채널을 이용하면 흰색선을 쉽게 구분할 수 있으며 LAB 포맷에서는 B채널을 이용하면 노란색선을 쉽게 구분할 수 있다.) split을 통해 HLS을 받아온다.
threshold를 통해 L 채널을 바이너리화 해주고 lane 에 넣어준다.
lane 에서 세로축 반으로 slice 해준후에 axis =0 즉 (240,320) 인 lane에서 첫번쨰 축 을 싹다 더해줌으로 (320,) 으로 histogram 을 만들어 줄 수 있습니다.
midpoint 160을 기준으로 leftx_current ,rightx_current 를 알아내준다.
쌓을 window의 사이즈 , margin, height 등을 결제해준다.
good_left_inds 는 위에서 nz[0] nz[1] 에 담긴것을 알아야 하는데 240 * 320 픽셀에 담긴 값중 0이 아닌것들을 index들을 nz[0] 에는 index[row][col] 중에 row 파트가 담겨잇고 nz[1]에는 col 이 담겨 있다. nz[0][12] 에 0 이 아닌게 존재한다. 라는 뜻이다. 즉 240 * 320 = 76800 중에 8360개의 흰 픽셀이 존재한다는 것이다.
이제 위에 나온 window의 사이즈 를 만족하는 픽셀들을 추려 낸다 & 를 이용해서 완전히 4가지 조건을 만족하는지 , 즉 window안에 들어오는지 를 체크해준다.
근데 확인해보면 무슨 7000 이상의 큰값들이 들어있다. 8360개의 흰점들중에 위의 window안에 들어가는것들이 309개 그리고 그것들의 index[row][col] 중 row 값만 나타난것이다.
이것을 nz[1][good_left_inds] 를 해주면 nz[1]들의 값중에 good_left_inds를 index로 삼는 nz[1]의 값들만이 나온다. 즉 window의 사이즈를 만족하는 x 축, (index[row][col] 중에 col) 값들이 튀어 나오는것이다. 그것을 평균을 구해서 새로운 기준점인 leftx_current를 구하는 것이다. 오른쪽도 동일하다.
lx ly rx ty 에 차곡 차곡 x,y좌표들의 중심점들을 모아둔다. lfit ,rfit은 이차곡선 함수를 만들때 써준다.
out_img[nz[0][left_lane_inds], nz[1][left_lane_inds]] = [255,0,0] 해당되는 왼쪽 선들의 픽셀 값을 BLUE로 바꿔준다.
6. draw_lane == 위의 비스듯함 사다리꼴을 perspective inverse를 통해서 다시 원래의 이미지에 겹쳐그릴수 있게 변환 시켜준다.
ploty =는 y축 좌표들을 쭈욱 넣어준다. 그것을 이차곡선함수를 통해 x값들을 구해준다.
pts 에는 480개의 x좌표 y좌표 들이 있는데 이건 각각 오른쪽 왼쪽 240 개 씩 합 한 값들이다.
color_wrap 은 빨간색 선사이와 파란색 선사이를 초록으로 가득 채우는 polygon을 생성해서 넣어준다.
newwrap 은 이제 Persepective를 invert해준 것을 넣어준다. 이것을 오버레이 기존이미지에 오버레이 시켜준다.
'자율주행 > ROS python (xytron 강의)' 카테고리의 다른 글
Houghline ,와핑기법, 원근변화 (0) | 2021.01.25 |
---|---|
line_find.py 주어진 영상에서 차선 찾기 (0) | 2021.01.22 |
Rosbag file을 통해 비디오생성 (0) | 2021.01.19 |
Opencv tutorial_1 (0) | 2021.01.18 |
8자 주행 ,ultrasonic 내 코드 정리 (0) | 2021.01.14 |