Skip to content

CCTV Idle Detection Pipeline 2026 05

ehdwo0427 edited this page May 27, 2026 · 1 revision

공회전 탐지 파이프라인 — 다중 신호 + 상태기계 (2026-05)

한동대학교 리빙랩 CCTV 위에서 차량 공회전(정차 + 시동 ON + 운전자 탑승 + ≥ 20초) 을 탐지하는 파이프라인 설계와 의사결정 기록. 메인 프로젝트: CCTV 자전거 경로 추적 & 차량 공회전 탐지

목차

한눈에 보기

Zone A — 21 vehicles tracked simultaneously with #ID labels

핵심 설계 한 줄:

공회전 = (정지 지속) AND (운전자 탑승) AND (시간 임계 ≥ 20초)
        ⊕ (보조: 후미등 ON 확률)

이 한 줄을 코드로 옮기는 데 가장 많은 시간을 쓴 곳은 "운전자 탑승" 을 어떻게 측정하느냐 였습니다. 부감 시점에선 차내 운전자가 매 프레임 안 잡혀서 단순 임플리먼테이션으로는 has_driver_ratio = 0% 가 나옵니다. 이걸 시간축 상태기계로 우회한 것이 이 파이프라인의 차별점입니다.

신호 정의

신호 출처 측정 단위
정지(stopped) ByteTrack 차량 박스 중심 좌표 분산 smoothing_window_sec(2s) 동안 std ≤ 5px
탑승(boarded) person 박스가 차량 ROI 안 등장 → 사라짐 시간축 상태기계 결과
후미등(brake_on) EfficientNet-B0 분류기 출력 prob ≥ 0.5

세 신호 중 정지·탑승은 필수, 후미등은 보조. 후미등을 필수로 강제하면 데이터셋 도메인 갭에서 오는 FN 23% 가 그대로 공회전 미탐지로 새어 나가기 때문입니다.

왜 boarding 상태기계가 필요했는가

처음에는 매 프레임 단순 검사로 충분할 줄 알았습니다:

def _has_driver(car_bbox, person_bboxes):
    for px1, py1, px2, py2 in person_bboxes:
        pcx, pcy = (px1+px2)/2, (py1+py2)/2
        if car_bbox.contains(pcx, pcy):
            return True
    return False

Zone A dry-run 결과: 모든 트랙의 has_driver_ratio 가 0%.

원인은 명확했습니다. CCTV 부감 30~40° 시점에서는 차량 안 운전자가 차량 박스 위에서 매 프레임 person 으로 잡히지 않습니다. 천장·창틀에 가려져 사라집니다. 단순 검사로는 영원히 측정할 수 없는 신호였습니다.

대신 시간축으로 보면 패턴이 분명합니다:

NO_DRIVER   →  APPROACHING  →  BOARDED   →  DISMOUNTED
              (person 박스       (person      (차량이 충분히
               차량 ROI 안        ROI 안에서   이동한 뒤
               등장)              사라짐)       person 재등장)
  • APPROACHING: ROI 진입 후 approach_timeout_sec (기본 3초) 안에 사라지면 → BOARDED 잠금. 안 사라지면 통행자로 보고 NO_DRIVER 리셋.
  • BOARDED: 트랙이 끊길 때까지 유지. 차량이 dismount_movement_px (기본 80px) 이상 이동한 뒤 person 박스가 ROI 에 다시 등장하면 → DISMOUNTED.
  • DISMOUNTED: 종단 상태. 다시 탑승하려면 새 APPROACHING.

각 차량 트랙마다 BoardingTracker 인스턴스 하나가 붙어 매 프레임 갱신됩니다. FrameObservation.has_driver 는 raw 검사 결과가 아니라 state == BOARDED 로 채워집니다.

단위 테스트 8개로 상태 전이를 모두 검증:

  • 사람 등장 → 곧 사라짐 → BOARDED
  • 사람이 ROI 에 너무 오래 머무름 → NO_DRIVER 리셋
  • BOARDED 후 차량 정지 + 사람 재등장 → BOARDED 유지 (이동 거리 부족)
  • BOARDED 후 차량 충분히 이동 + 사람 재등장 → DISMOUNTED

판정 알고리즘

src/cctv_bid/logic/idle.py — detect_idle_events()

  1. 트랙 1개의 시간순 FrameObservation 시퀀스 입력
  2. smoothing_window_sec (기본 2초) 단위 rolling 으로 정지 판정
  3. 매 프레임 _signal(i) = stopped AND (require_driver → has_driver) AND (require_brake → brake_prob ≥ 임계)
  4. 연속 True 구간 추출 → 지속시간 ≥ T_idle_sec (기본 20초) 이면 IdleEvent 양성
  5. 이벤트별 has_driver_ratio, brake_on_ratio, avg_brake_prob 집계

IdlePolicy 파라미터

필드 기본값 의미
T_idle_sec 20.0 최소 지속 시간 (초)
stop_center_std_max 5.0 정지 판정 박스 중심 std 임계 (픽셀)
require_driver True 탑승 필수 여부
require_brake False True 면 brake_prob ≥ 임계도 강제
brake_threshold 0.5 brake_prob ON 인정 임계
smoothing_window_sec 2.0 정지 판정 rolling 윈도우

BoardingPolicy 파라미터

필드 기본값 의미
approach_timeout_sec 3.0 APPROACHING → BOARDED 잠그는 최대 시간
dismount_movement_px 80.0 하차 후보가 되는 차량 이동 거리
dismount_stop_window_sec 2.0 (예약) 이동 후 정지 인정 시간

단위 테스트 18개 (idle 10 + boarding 8) 모두 통과.

Zone A dry-run 결과

원본 영상: bike_idle_original/A구역.mp4 (4,143 frames, ~3분)

  • 차량 트랙 수: 215
  • 시각화 산출물:
    • track_visualization_h264.mp4 (141MB) — 전체 영상 + 트랙별 고유 색 + #ID 라벨
    • per_track_videos/track_NNN.mp4 21개 — 장기 등장 차량별 follow crop

위 한눈에 보기 이미지가 frame 2500 의 한 컷입니다. 주차장 한쪽에 20대가 동시에 잡혀 있고 각 트랙이 고유 색·#ID 로 구분됩니다.

발견된 한계와 대응

1. has_driver_ratio = 0%

  • 원인: 부감 시점에서 차내 운전자가 person 으로 안 잡힘
  • 대응: boarding 상태기계 도입 → 재추론 대기 중

2. brake_prob false positive 의심

  • 현상: track #4, #12, #21 등에서 빨간 차체에 대해 brake_prob 가 단발적으로 튐
  • 원인: 학습 데이터(Tail_Lamp_KLETECH) 가 블랙박스 정면 시점이라 부감과 도메인 갭. 3 모델 비교 페이지 참고
  • 대응:
    • brake_prob 에 5–10초 rolling 평균 적용
    • require_brake=False 로 보조 신호 유지 (단독 판정에 안 씀)
    • 현장 영상 도착 후 fine-tune

3. 215 트랙 동시 표시의 가독성

  • 원인: 한 주차장 영상에 들어오고 나가는 차량을 모두 트랙으로 잡으니 수가 많음
  • 대응: 트랙별 HSV hash 고유 색 (211 prime × tid mod 180) + #ID 박스 좌상단 라벨 + per-track follow crop 영상 별도 추출

코드 구조

src/cctv_bid/
├ logic/
│  ├ idle.py          # detect_idle_events, IdlePolicy, FrameObservation, IdleEvent
│  └ boarding.py      # BoardingTracker, BoardingPolicy, BoardingState (4-state machine)
├ pipeline/
│  └ inference_idle.py # YOLO.track + ByteTrack + per-track BoardingTracker 통합
└ taillight/
   ├ model.py         # BrakeLightNet (ResNet18 / MBV3-L / EffNet-B0)
   ├ dataset.py       # ImageFolder 호환 + class alias
   ├ transforms.py    # Albumentations
   └ predict.py       # BrakeLightClassifier 추론 래퍼

scripts/
├ run_inference_idle.py           # CLI — result.json, summary.txt, visualization.mp4, per_track parquet
├ render_track_visualization.py   # 트랙별 고유 색 + #ID 라벨 시각화
├ extract_per_track_videos.py     # 차량별 follow crop 영상
├ train_brakelight.py             # 후미등 분류기 학습
└ coco_brakelight_to_classify.py  # COCO detection → ImageFolder 변환

tests/
├ test_idle.py        # 10개
├ test_boarding.py    # 8개
└ test_brakelight.py  # 7개

다음 작업

  • boarding 상태기계 모듈 + 단위 테스트
  • inference_idle.py 에 BoardingTracker 통합
  • Zone A 재추론 — boarding 로직 적용 후 has_driver_ratio 회복 검증
  • visualization 에 person 박스 별도 색 + #P{id} 추가
  • idle 정책 파라미터 튜닝 (require_brake, brake_threshold)
  • brake_prob rolling 평균 5–10초 적용으로 false positive 감소
  • 카메라 도착 후 실데이터로 후미등 분류기 fine-tune (Phase 5)
  • 평가 — 실제 공회전 50회 + 미공회전 50회 → P/R/F1, T=20s vs 30s

Woody's AI Backend Engineering Log


💼 About

Deepvisions | AI Engineer 2026.03 ~ 재직중


🚀 Projects (최신순)

CCTV 자전거 경로 & 공회전 탐지 — 한동대학교 리빙랩

2026.05 ~ | @ Deepvisions 캠퍼스 CCTV 4대 · 자전거 OCR + 차량 공회전 다중 신호

야생동물 탐지 — RPi 엣지 배포

2026.04 ~ | @ Deepvisions 포도밭 침입 탐지 (5종 multi-class · 라즈베리파이 4 실시간)

포도밭 병해충 탐지 및 수확량 예측

2026.03 ~ | @ Deepvisions 드론 이미지 기반 객체 탐지 + GSD calibration + 수확량 예측


📦 종료된 프로젝트

OnTheTop

2025.03 ~ 2025.08 | 카카오테크부트캠프 | ✅ 종료 AI 기반 데스크테리어 추천 서비스


AI Notes


About

Clone this wiki locally