-
Notifications
You must be signed in to change notification settings - Fork 0
CCTV Idle Detection Pipeline 2026 05
한동대학교 리빙랩 CCTV 위에서 차량 공회전(정차 + 시동 ON + 운전자 탑승 + ≥ 20초) 을 탐지하는 파이프라인 설계와 의사결정 기록. 메인 프로젝트: CCTV 자전거 경로 추적 & 차량 공회전 탐지

핵심 설계 한 줄:
공회전 = (정지 지속) 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% 가 그대로 공회전 미탐지로 새어 나가기 때문입니다.
처음에는 매 프레임 단순 검사로 충분할 줄 알았습니다:
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 FalseZone 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개의 시간순
FrameObservation시퀀스 입력 -
smoothing_window_sec(기본 2초) 단위 rolling 으로 정지 판정 - 매 프레임
_signal(i)= stopped AND (require_driver → has_driver) AND (require_brake → brake_prob ≥ 임계) - 연속 True 구간 추출 → 지속시간 ≥
T_idle_sec(기본 20초) 이면 IdleEvent 양성 - 이벤트별
has_driver_ratio,brake_on_ratio,avg_brake_prob집계
| 필드 | 기본값 | 의미 |
|---|---|---|
| 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 윈도우 |
| 필드 | 기본값 | 의미 |
|---|---|---|
| approach_timeout_sec | 3.0 | APPROACHING → BOARDED 잠그는 최대 시간 |
| dismount_movement_px | 80.0 | 하차 후보가 되는 차량 이동 거리 |
| dismount_stop_window_sec | 2.0 | (예약) 이동 후 정지 인정 시간 |
단위 테스트 18개 (idle 10 + boarding 8) 모두 통과.
원본 영상: bike_idle_original/A구역.mp4 (4,143 frames, ~3분)
- 차량 트랙 수: 215
- 시각화 산출물:
-
track_visualization_h264.mp4(141MB) — 전체 영상 + 트랙별 고유 색 + #ID 라벨 -
per_track_videos/track_NNN.mp421개 — 장기 등장 차량별 follow crop
-
위 한눈에 보기 이미지가 frame 2500 의 한 컷입니다. 주차장 한쪽에 20대가 동시에 잡혀 있고 각 트랙이 고유 색·#ID 로 구분됩니다.
- 원인: 부감 시점에서 차내 운전자가 person 으로 안 잡힘
- 대응: boarding 상태기계 도입 → 재추론 대기 중
- 현상: track #4, #12, #21 등에서 빨간 차체에 대해 brake_prob 가 단발적으로 튐
- 원인: 학습 데이터(Tail_Lamp_KLETECH) 가 블랙박스 정면 시점이라 부감과 도메인 갭. 3 모델 비교 페이지 참고
-
대응:
- brake_prob 에 5–10초 rolling 평균 적용
- require_brake=False 로 보조 신호 유지 (단독 판정에 안 씀)
- 현장 영상 도착 후 fine-tune
- 원인: 한 주차장 영상에 들어오고 나가는 차량을 모두 트랙으로 잡으니 수가 많음
- 대응: 트랙별 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
Deepvisions | AI Engineer 2026.03 ~ 재직중
2026.05 ~ | @ Deepvisions 캠퍼스 CCTV 4대 · 자전거 OCR + 차량 공회전 다중 신호
2026.04 ~ | @ Deepvisions 포도밭 침입 탐지 (5종 multi-class · 라즈베리파이 4 실시간)
2026.03 ~ | @ Deepvisions 드론 이미지 기반 객체 탐지 + GSD calibration + 수확량 예측
- 프로젝트 메인
- 관련 연구 종합 + 한계 (2026-05) ← 최신
- 수확량 close-up 4장 + 3-Model (2026-05-19)
- 드론 포도 수확량 예측 — 파이프라인 (2026-05)
- 드론 포도송이 탐지 — 학습 변천사 (2026-04)
- SAM3 vs Fine-tuned YOLO
- Grounding DINO vs YOLO Top3 비교 요약
- YOLO Baseline Top3 비교 요약
- YOLO Model Comparison Summary
- 포도 탐지를 위한 데이터 수집
- 포도 수확량 측정을 위한 Object Detection
2025.03 ~ 2025.08 | 카카오테크부트캠프 | ✅ 종료 AI 기반 데스크테리어 추천 서비스
- Name: Woody (이동재)
- Focus: Vision AI, LLM Integration, Backend Engineering
- GitHub: @ehdwo0427
- Email: ehdwo0427@naver.com
- 포트폴리오 : 포트폴리오