OpenCV를 이용한 고급 커브 차선 감지
스토리
소개
차선 표시는 모든 자율주행 시스템의 중추로, 차량이 교통 상황을 안전하게 통과하도록 안내합니다. 이전 직선 차선 감지 프로젝트를 바탕으로 그림자, 폐색, 고르지 않은 노면 같은 복잡한 환경에서도 안정적으로 작동하는 견고한 커브 차선 감지 파이프라인을 개발했습니다.
완전한 이미지 처리 워크플로는 파이썬과 OpenCV로 구현되었습니다:
- 왜곡 보정
- 원근 변환
- 소벨 필터링
- 히스토그램 피크 감지
- 슬라이딩 윈도우 검색
- 다항식 곡선 피팅
- 감지된 차선 오버레이
- 실시간 비디오 처리
이전 시스템의 한계
이전 모델은 직선 차선에 국한되어 곡선, 그림자, 부분 폐색에서 어려움을 겪었습니다. 새로운 파이프라인은 에지 기반 필터링과 동적 슬라이딩 윈도우 접근법을 도입해 이러한 단점을 해결했습니다.
왜곡 보정
카메라 렌즈는 측정치를 왜곡할 수 있는 방사 왜곡을 도입합니다. 알려진 패턴(일반적으로 비대칭 체커보드)에 대해 카메라를 보정하면 전체 데이터셋을 보정하는 왜곡 모델을 생성합니다.
보정 절차는 체커보드 이미지를 여러 장 촬영하고, cv2.findChessboardCorners()로 코너를 감지하고, cv2.calibrateCamera()로 카메라 매트릭스와 왜곡 계수를 계산하는 과정을 포함합니다. 보정된 이미지는 cv2.undistort()로 생성됩니다.
아래는 보정된 체커보드 이미지의 나란히 비교로, 미묘하지만 중요한 개선을 보여줍니다.
사용된 보정 코드:
def undistort_img():
# 객체 포인트 준비 0,0,0 … 8,5,0
obj_pts = np.zeros((6*9,3), np.float32)
obj_pts[:,:2] = np.mgrid[0:9, 0:6].T.reshape(-1,2)
objpoints = []
imgpoints = []
images = glob.glob('camera_cal/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (9,6), None)
if ret:
objpoints.append(obj_pts)
imgpoints.append(corners)
img_size = (img.shape[1], img.shape[0])
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size, None, None)
dst = cv2.undistort(img, mtx, dist, None, mtx)
dist_pickle = {'mtx': mtx, 'dist': dist}
pickle.dump(dist_pickle, open('camera_cal/cal_pickle.p', 'wb'))
return dst
def undistort(img, cal_dir='camera_cal/cal_pickle.p'):
with open(cal_dir, 'rb') as f:
file = pickle.load(f)
mtx, dist = file['mtx'], file['dist']
return cv2.undistort(img, mtx, dist, None, mtx)
undistort_img()
img = cv2.imread('camera_cal/calibration1.jpg')
dst = undistort(img)
함수는 함께 제공되는 주피터 노트북의 Code 섹션에서도 확인 가능합니다.
아래는 주행 장면에 적용된 왜곡 보정 예시입니다. 시각적 차이는 미묘하지만 정확한 기하학은 후처리에 필수적입니다.
원근 변환
카메라 공간에서 직접 커브 차선을 감지하는 것은 어렵습니다. 이미지를 조감도 뷰로 변환하면 차선이 평평한 평면에 있다고 가정하고 높은 정밀도로 다항식을 피팅할 수 있습니다.
변환은 cv2.getPerspectiveTransform()과 cv2.warpPerspective()로 수행합니다. 소스와 대상 포인트는 차선 기하학을 보존하면서 가시성을 극대화하도록 선택됩니다.
def perspective_warp(img, dst_size=(1280,720), src=np.float32([(0.43,0.65),(0.58,0.65),(0.1,1),(1,1)]), dst=np.float32([(0,0),(1,0),(0,1),(1,1)])):
img_size = np.float32([img.shape[1], img.shape[0]])
src = src * img_size
dst = dst * np.float32(dst_size)
M = cv2.getPerspectiveTransform(src, dst)
return cv2.warpPerspective(img, M, dst_size)
소벨 필터링
컬러 필터링만으로는 밝은 콘크리트를 차선 표시로 오분류할 수 있습니다. 대신 그래디언트 기반 에지 감지를 사용합니다. 소벨 연산자는 HLS 색상 공간에서 수평 그래디언트를 계산해 고 대비 차선 에지를 강조하고 노면은 억제합니다.
결과 이진 마스크는 조명 변화에 강건하며 슬라이딩 윈도우 검색의 입력으로 사용됩니다.
히스토그램 피크 감지
슬라이딩 윈도우 적용 전, 이진 이미지의 x축을 따라 히스토그램을 계산해 차선의 대략적 수평 위치를 식별합니다. 양쪽에서 가장 높은 피크가 좌우 차선의 베이스 포인트에 해당합니다.
슬라이딩 윈도우 검색
베이스 포인트를 사용해 슬라이딩 윈도우 알고리즘이 각 차선에 속하는 픽셀 인덱스를 반복적으로 수집합니다. 그런 다음 각 차선에 2차 다항식을 피팅해 정확한 곡률 추정과 차선 중심 대비 차량 위치를 실시간으로 가능하게 합니다.
최종 오버레이는 감지된 차선 영역, 곡률, 차량 오프셋을 실시간으로 표시합니다.
자세히 보기: Curved Lane Detection