1) ROI 자르기 → 하단 50%만 (하늘·먼 풍경 제거) 2) HSV 변환 → 조명에 강한 색공간 3) 색상 마스크 → 흰색 차선만 추출 (cv2.inRange) 4) 모폴로지 → 노이즈 제거 (열림/닫힘) 5) 윤곽선·중심점 → cv2.findContours + 모멘트
영상 전체를 처리하면 하늘·전봇대·간판이 차선처럼 잘못 인식될 수 있습니다. 하단 50%만 잘라서 처리하면:
height, width = frame.shape[:2] roi = frame[height//2:, :] # 하단 절반
마스크에 작은 점·구멍이 있으면 윤곽선 검출이 부정확해집니다.
| 연산 | 효과 |
|---|---|
| 팽창(Dilate) | 흰 영역 키움 → 작은 구멍 메움 |
| 침식(Erode) | 흰 영역 줄임 → 작은 노이즈 제거 |
| 열림(Opening) | 침식 → 팽창 (작은 노이즈 제거) |
| 닫힘(Closing) | 팽창 → 침식 (작은 구멍 메움) |
kernel = np.ones((3, 3), np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 노이즈 제거 mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 구멍 메움
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 가장 큰 윤곽선 선택
largest = max(contours, key=cv2.contourArea)
# 무게중심 (모멘트)
M = cv2.moments(largest)
if M['m00'] > 0:
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
cx가 차선의 가로 중심 위치입니다. 화면 가로 중심(width/2)과의 차이가 조향에 쓸 오차입니다.
lane_detector.py는 두 ROI를 사용합니다.
┌────────────────────┐ │ │ ← 처리 안 함 │ │ │ ━━━━━━━━━━━━━━━━━ │ far ROI (멀리 있는 차선) → 각도 계산 │ │ │ ━━━━━━━━━━━━━━━━━ │ near ROI (가까운 차선) → 위치 계산 │ │ └────────────────────┘
가까운 ROI의 위치 + 먼 ROI와의 각도를 함께 사용하면 곡선·직선 모두에서 안정적입니다.
python/lane_detector.py 일부 발췌:
def detect(self, frame):
# 1) ROI
h, w = frame.shape[:2]
roi = frame[int(h * self.roi_y_start):, :]
# 2) HSV
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
# 3) 마스크
white_mask = cv2.inRange(hsv, self.white_lower, self.white_upper)
yellow_mask = cv2.inRange(hsv, self.yellow_lower, self.yellow_upper)
mask = cv2.bitwise_or(white_mask, yellow_mask)
# 4) 모폴로지
kernel = np.ones((3, 3), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
# 5) 윤곽선 + 중심
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
largest = max(contours, key=cv2.contourArea)
M = cv2.moments(largest)
if M['m00'] > 0:
cx = int(M['m10'] / M['m00'])
return cx, ..., debug_frame
return None, None, debug_frame
학생용 프로그램 단계7_HSV튜닝.py를 실행:
| 파라미터 | 본인 값 |
|---|---|
| White Brightness Min | |
| White Saturation Max | |
| Yellow Hue Min/Max | |
| Yellow Brightness Min |
학생용 프로그램에 다음 기능을 추가하시오.
M = cv2.moments(largest)
if M['m00'] > 0:
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
# TODO: 영상에 (cx, cy) 위치에 빨간 원 그리기
# cv2.circle(frame, ...)
# TODO: 화면 중심에 파란 세로선 그리기
# cv2.line(frame, ...)
# TODO: 화면 중심과 차선 중심의 거리(error) 화면에 표시
# cv2.putText(frame, f"Error: {...}", ...)
미션: A4 종이 위에 흰색 테이프(폭 5cm) 차선을 만들어 카메라로 비춤 → 카메라를 좌·우로 움직이며 빨간 원이 차선 중심에 따라 움직이는지 확인
원형 또는 S자 차선을 만들고, 차량을 천천히 끌면서 측정:
| 시나리오 | error 값 변화 패턴 |
|---|---|
| 직선 | |
| 좌커브 | |
| 우커브 | |
| 차선 끊김(짧은 점선) |
고찰
| 개념 | 설명 |
|---|---|
| ROI | 영상의 일부 영역만 잘라 처리 |
| 마스크 | 조건 만족 픽셀만 흰색으로 표시 |
| 모폴로지 | 노이즈 제거·구멍 메움을 위한 형태소 연산 |
| 윤곽선(contour) | 같은 색 영역의 경계선 |
| 모멘트 | 영역의 무게중심·면적을 계산하는 수학적 도구 |
| error | 화면 중심 - 차선 중심, 조향에 쓰는 값 |
| 항목 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 차선 인식 5단계 파이프라인을 설명할 수 있다 | |||||
| HSV 마스크를 본인 환경에 맞게 튜닝할 수 있다 | |||||
| 차선 중심점을 화면에 표시할 수 있다 | |||||
| 다양한 조명에서 안정적인 마스크를 만들었다 |