지난 포스팅까지 해서 이제 얼추 차선으로 써먹을만한 친구들만 뽑아내는 과정까지 진행했습니다. 혹시 코드를 직접 실행시켜보신 분이라면 최소 길이 트랙바를 조절하다 보면 차선 후보가 선 하나만 나오는 게 아니라 여러 개가 겹쳐있다는 사실을 눈치채셨을 것 같은데요. 이번 포스팅은 그렇게 겹쳐있는 선들을 하나로 만들고 중심 기준으로 왼쪽 차선과 오른쪽 차선을 분리해보는 시간을 가지겠습니다.
먼저 왼쪽 차선과 오른쪽 차선을 나누는 것 부터 먼저 해보겠습니다. 미리 나눠놓고 나중에 각각의 대표선을 계산하는 게 훨씬 효율적일 것 같거든요. 사실 왼쪽선 오른쪽 선을 나누는 것은 그렇게 어려운 일이 아닙니다. 잠깐 그림을 한 번 볼까요.
그림의 퀄리티는 신경쓰지 마시기 바랍니다. 왼선을 붉은색, 오른선을 푸른색으로 구분해놓았습니다. 이 두 선을 교차시켜서 보면 뭔가 감을 잡을 것 같은데요. 양 선은 각도로 구분이 가능할 것 같다는 생각이 드시나요? 맞습니다. 그냥 단순하게 0도보다 크면 왼선, 작으면 오른선으로 구분하면 되겠죠. 어차피 각도는 60도나 -120도나 같은 말이기에 이런 식으로 구분이 가능한 겁니다.
그럼 이제 왼쪽 오른쪽 구분은 했으니 자기들끼리 나눠서 대표선을 정해주면 되겠네요. 단순하게 x1,x2,y1,y2를 끼리끼리 모아서 더한 다음에 평균을 내어볼까요?
def separate_line(lines,slope_deg):
l_lines, r_lines = lines[(slope_deg>0),:], lines[(slope_deg<0),:]
l_line = [sum(l_lines[:,0])/len(l_lines),sum(l_lines[:,1])/len(l_lines),sum(l_lines[:,2])/len(l_lines),sum(l_lines[:,3])/len(l_lines)]
r_line = [sum(r_lines[:,0])/len(r_lines),sum(r_lines[:,1])/len(r_lines),sum(r_lines[:,2])/len(r_lines),sum(r_lines[:,3])/len(r_lines)]
return l_line, r_line
구분 밑 대표선까지의 작업에 해당하는 코드입니다. 무식하게 짜는 바람에 길이가 좀 깁니다.
하나로 잘 출력되는 것 같기는 한데 이게 지금 잘나오고 있는 건지는 도통 모르겠죠? 그래서 이번엔 원본 사진 위로 겹쳐서 보도록 코드를 좀 수정해보겠습니다.
약간 파란색이 잘 안맞는 듯한 느낌이 드네요. 이유는 아시다시피 선이 여러 개 나오는 상황에서 해당 선들의 각각 좌표를 평균 내서 하나의 직선으로 만드는 방식이기 때문에 그렇습니다. 그럼 선의 최소 길이를 조금 타이트하게 잡으면 훨씬 깔끔하게 나오겠죠. 과정을 동영상으로 잠깐 첨부해보겠습니다.
결과를 보면 훨씬 낫죠? 항상 이렇게 트랙바를 계속 만지고 있을 수는 없지만 나중에 어느 정도 상황이 통제될 때는 아마 이렇게 귀찮게 안 하셔도 될 겁니다. 오늘도 긴 글 읽으시느라 고생 많으셨습니다.
import cv2
import numpy as np
def grayscale(img):
return cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
def gaussian_blur(img, kernel_size):
return cv2.GaussianBlur(img, (kernel_size, kernel_size), 0)
def canny(img, low_threshold, high_threshold):
return cv2.Canny(img, low_threshold, high_threshold)
def roi(img,h,w):
mask = np.zeros_like(img)
vertices = np.array([[(0,h), (0,h*2/3), (w,h*2/3), (w,h)]], dtype=np.int32)
cv2.fillPoly(mask, vertices, 255)
roi_img = cv2.bitwise_and(img, mask)
return roi_img
def restrict_deg(lines,min_slope,max_slope):
slope_deg = np.rad2deg(np.arctan2(lines[:,1]-lines[:,3],lines[:,0]-lines[:,2]))
lines = lines[np.abs(slope_deg)<max_slope]#cannot use and & index true catch
slope_deg = slope_deg[np.abs(slope_deg)<max_slope]
lines = lines[np.abs(slope_deg)>min_slope]
slope_deg = slope_deg[np.abs(slope_deg)>min_slope]#where can i use slope
return lines, slope_deg
def separate_line(lines,slope_deg):
l_lines, r_lines = lines[(slope_deg>0),:], lines[(slope_deg<0),:]
l_line = [sum(l_lines[:,0])/len(l_lines),sum(l_lines[:,1])/len(l_lines),sum(l_lines[:,2])/len(l_lines),sum(l_lines[:,3])/len(l_lines)]
r_line = [sum(r_lines[:,0])/len(r_lines),sum(r_lines[:,1])/len(r_lines),sum(r_lines[:,2])/len(r_lines),sum(r_lines[:,3])/len(r_lines)]
return l_line, r_line
def hough(img,h,w,min_line_len,min_slope,max_slope):
lines = cv2.HoughLinesP(img, rho=1, theta=np.pi/180, threshold=30, minLineLength=min_line_len, maxLineGap=30)#return = [[x1,y1,x2,y2],[...],...]
lines = np.squeeze(lines)#one time ok
lanes, slopes = restrict_deg(lines,min_slope,max_slope)
l_lane, r_lane = separate_line(lanes,slopes)
#lane_img = np.zeros((h, w, 3), dtype=np.uint8)
#for x1,y1,x2,y2 in l_lanes:
#cv2.line(lane_img, (int(l_lane[0]), int(l_lane[1])), (int(l_lane[2]), int(l_lane[3])), color=[0,0,255], thickness=2)
#for x1,y1,x2,y2 in r_lanes:
#cv2.line(lane_img, (int(r_lane[0]), int(r_lane[1])), (int(r_lane[2]), int(r_lane[3])), color=[255,0,0], thickness=2)
return l_lane,r_lane
def lane_detection(min_line_len,min_slope,max_slope):
origin_img = cv2.imread('./left_right.jpg')
h,w = origin_img.shape[:2]
gray_img = grayscale(origin_img)
blur_img = gaussian_blur(gray_img, 5)
canny_img = canny(blur_img, 50, 200)
roi_img = roi(canny_img,h,w)
l_lane,r_lane = hough(roi_img,h,w,min_line_len,min_slope,max_slope)
cv2.line(origin_img, (int(l_lane[0]), int(l_lane[1])), (int(l_lane[2]), int(l_lane[3])), color=[0,0,255], thickness=5)
cv2.line(origin_img, (int(r_lane[0]), int(r_lane[1])), (int(r_lane[2]), int(r_lane[3])), color=[255,0,0], thickness=5)
return origin_img
def nothing(pos):
pass
if __name__ == '__main__':
cv2.namedWindow(winname='Lane Detection')
cv2.createTrackbar('houghMinLine', 'Lane Detection', 20, 200, nothing)#don't write keyword
cv2.createTrackbar('slopeMinDeg', 'Lane Detection', 100, 180, nothing)
cv2.createTrackbar('slopeMaxDeg', 'Lane Detection', 160, 180, nothing)
while cv2.waitKey(1) != ord('q'):
min_line_len = cv2.getTrackbarPos(trackbarname='houghMinLine', winname='Lane Detection')
min_slope = cv2.getTrackbarPos('slopeMinDeg','Lane Detection')
max_slope = cv2.getTrackbarPos('slopeMaxDeg','Lane Detection')
result_img = lane_detection(min_line_len,min_slope,max_slope)
cv2.imshow('Lane Detection',result_img)
cv2.imwrite('./hough_img3.jpg',result_img)
cv2.destroyAllWindows()
#It will be great that we can select the instant roi region using click when we run the code.
'Programming > Capstone Design' 카테고리의 다른 글
[Capstone Design]4. 시리얼 통신(Serial Communication) - 1 (0) | 2022.04.17 |
---|---|
[Capstone Design]3. 차선 인식(Lane Detection) - 7 (0) | 2022.04.16 |
[Capstone Design]3. 차선 인식(Lane Detection) - 5 (0) | 2022.04.14 |
[Capstone Design]3. 차선 인식(Lane Detection) - 4 (0) | 2022.04.13 |