단계 7 — 차선 인식 (HSV·ROI)

차시 4차시 · 선수 학습 단계 6

이번 단계 목표 영상 속에서 차선을 찾고, 차선의 중심 위치를 좌표로 계산한다.

1. 학습 목표

2. 이론

2-1. 차선 인식의 5단계 파이프라인

HSV 파이프라인
1) ROI 자르기   → 하단 50%만 (하늘·먼 풍경 제거)
2) HSV 변환     → 조명에 강한 색공간
3) 색상 마스크  → 흰색 차선만 추출 (cv2.inRange)
4) 모폴로지     → 노이즈 제거 (열림/닫힘)
5) 윤곽선·중심점 → cv2.findContours + 모멘트

2-2. ROI(관심 영역) 설정

영상 전체를 처리하면 하늘·전봇대·간판이 차선처럼 잘못 인식될 수 있습니다. 하단 50%만 잘라서 처리하면:

height, width = frame.shape[:2]
roi = frame[height//2:, :]   # 하단 절반

2-3. 모폴로지 (Morphology)

마스크에 작은 점·구멍이 있으면 윤곽선 검출이 부정확해집니다.

연산효과
팽창(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)  # 구멍 메움

2-4. 윤곽선과 무게중심

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)과의 차이가 조향에 쓸 오차입니다.

2-5. 본 차량의 하이브리드 방식

lane_detector.py두 ROI를 사용합니다.

┌────────────────────┐
│                    │  ← 처리 안 함
│                    │
│ ━━━━━━━━━━━━━━━━━ │  far ROI (멀리 있는 차선) → 각도 계산
│                    │
│ ━━━━━━━━━━━━━━━━━ │  near ROI (가까운 차선)  → 위치 계산
│                    │
└────────────────────┘

가까운 ROI의 위치 + 먼 ROI와의 각도를 함께 사용하면 곡선·직선 모두에서 안정적입니다.

3. 코드 흐름 따라가기

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

4. 활동

활동 6-A: 차선 마스크 만들기 ⭐ Easy

학생용 프로그램 단계7_HSV튜닝.py를 실행:

  1. 트랙바로 White Brightness Min을 200~240 사이에서 조정
  2. 흰 차선 부분만 흰색이 되도록 튜닝
  3. 만족스러운 값을 기록
파라미터본인 값
White Brightness Min
White Saturation Max
Yellow Hue Min/Max
Yellow Brightness Min

활동 6-B: 차선 중심점 시각화 ⭐⭐ Standard

학생용 프로그램에 다음 기능을 추가하시오.

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) 차선을 만들어 카메라로 비춤 → 카메라를 좌·우로 움직이며 빨간 원이 차선 중심에 따라 움직이는지 확인

활동 6-C: 곡선 차선 도전 ⭐⭐⭐ Challenge

원형 또는 S자 차선을 만들고, 차량을 천천히 끌면서 측정:

시나리오error 값 변화 패턴
직선
좌커브
우커브
차선 끊김(짧은 점선)

고찰

5. 핵심 개념 정리

개념설명
ROI영상의 일부 영역만 잘라 처리
마스크조건 만족 픽셀만 흰색으로 표시
모폴로지노이즈 제거·구멍 메움을 위한 형태소 연산
윤곽선(contour)같은 색 영역의 경계선
모멘트영역의 무게중심·면적을 계산하는 수학적 도구
error화면 중심 - 차선 중심, 조향에 쓰는 값

6. 자기평가

항목12345
차선 인식 5단계 파이프라인을 설명할 수 있다
HSV 마스크를 본인 환경에 맞게 튜닝할 수 있다
차선 중심점을 화면에 표시할 수 있다
다양한 조명에서 안정적인 마스크를 만들었다