EyeNet: 웹캠 피드를 실행 가능한 보안 사고로 전환하기
요약
실시간 웹캠 피드를 구조화된 보안 사고로 전환하는 EyeNet 시스템의 구축 과정을 설명합니다. 단순 객체 탐지를 넘어 트래킹, 얼굴 인식, 유니폼 준수 여부 확인을 결합하여 오탐지를 줄이고 실행 가능한 이벤트를 생성하는 파이프라인을 제안합니다.
핵심 포인트
- 단순 탐지를 넘어 구조화된 사고(Structured incidents)로 전환하는 파이프라인 구축
- SharedFrameBuffer를 통한 단일 카메라 리더-다수 소비자 아키텍처 설계
- YOLOv8, ByteTrack, dlib 등을 활용한 멀티 시그널 탐지 구현
- 트래킹 지속성을 기반으로 한 알림 필터링으로 오탐지(False positives) 최소화
저는 원본 영상을 실행 가능한 사고로 전환하는 실시간 캠퍼스 감시 시스템을 구축했습니다 — EyeNet의 작동 방식은 다음과 같습니다.
제가 온라인에서 보는 대부분의 "AI 감시" 데모는 객체 탐지(Object Detection) 단계에서 멈춥니다: 경계 상자(Bounding box), 신뢰도 점수(Confidence score), 그것으로 끝입니다. 이는 보안 팀에게 실제로 유용하지 않습니다. 단 하나의 깜빡이는 프레임 때문에 초당 수백 번씩 "칼이 감지됨"이라고 말하는 카메라는 차라리 카메라가 없는 것보다 못합니다.
그래서 저는 EyeNet을 구축했습니다 — 탐지를 구조화되고, 쿼리가 가능하며, 실행 가능한 이벤트로 끝나는 파이프라인의 첫 번째 단계로 취급하는 카메라 우선(Camera-first) 실시간 사고 대응 시스템입니다.
문제점
어떤 캠퍼스 보안 팀이나 징계 팀과 이야기해 봐도 똑같은 고충을 듣게 될 것입니다:
- 인간은 하루 종일, 매일, 20개의 카메라 피드를 동시에 지켜볼 수 없습니다.
- 알림은 전화나 WhatsApp을 통해 발생합니다 — 구조가 없고, 추적 가능성이 없으며, 이력이 없습니다.
- 단순한 컴퓨터 비전 (CV) 시스템은 지속성(Persistence)과 쿨다운(Cooldowns)을 강제하지 않으면 끊임없이 오탐(False positives)을 발생시킵니다.
- 무언가 잘못되었을 때 스냅샷을 포함하여 검토 가능한 감사 추적(Audit trail)을 가진 사람이 아무도 없습니다.
EyeNet의 목표: 라이브 비디오 피드를 소음이 아닌 **구조화된 사고(Structured incidents)**로 전환하는 것입니다.
아키텍처
Camera → SharedFrameBuffer → DetectionPipeline → ObjectTracker → DetectionEvent
↓
EventBus (priority queue)
...
제가 다시 선택하더라도 똑같이 할 몇 가지 결정 사항들:
하나의 카메라 리더, 다수의 소비자. SharedFrameBuffer가 /dev/video0를 독점적으로 소유하므로 파이프라인, 대시보드 스트림, 그리고 향후 추가될 어떤 소비자도 동일한 장치를 두고 다투지 않습니다.
단일 모델이 아닌 프레임당 멀티 시그널 탐지.
- 얼굴 인식 (Face recognition): 등록된 인코딩(
face-recognition/ dlib)과 대조하며, 임계값(threshold)을 기준으로 L2 거리(L2 distance)를 통해 매칭합니다. - 유니폼 준수 여부 (Uniform compliance): 알려진 얼굴에 대해서만 수행됩니다. 얼굴 박스 아래의 몸통 ROI(관심 영역)를 추정하여 HSV로 변환한 뒤, 연한 파란색 원단의 비율을 확인합니다. 이 작업에는 복잡한 분류기(classifier)가 필요하지 않습니다.
- 위험 탐지 (Hazard detection): YOLOv8을 통해 수행되며, 키워드 허용 목록(칼, 총, 불, 연기, 도끼, 검...)을 통해 필터링됩니다. 클래스별 신뢰도 임계값(confidence thresholds)과 최소 경계 상자(bounding-box) 면적을 적용하여 아주 작은 오탐지(false positives)를 제거합니다.
알림 전 트래킹 (Tracking before alerting). 모든 탐지 결과는 ByteTrack(supervision을 통해 수행하며, 설치되지 않은 경우 대체 ID 체계 사용)을 거칩니다. 위험 요소나 미확인 얼굴은 트랙이 최소 프레임 수만큼 지속된 경우에만 알림으로 전환되며, 트랙당 단 한 번만 발생합니다. 이 단일 결정 덕분에 대다수의 오탐지 알림을 제거할 수 있었습니다.
심각도를 인지하는 이벤트 버스 (Severity-aware event bus). 이벤트는 FIFO(선입선출) 방식으로 처리되지 않습니다. 대신 우선순위 큐(priority queue)에 들어가며, CRITICAL(CRITICAL (총, 불, 폭탄))은 항상 LOW (유니폼 위반)보다 앞서 처리됩니다. 여러 워커 스레드(worker threads)를 가진 프로세스 내 EventBus는 등록된 핸들러(DB, 알림, SSE)로 독립적으로 이벤트를 전달하므로, 느린 SMTP 호출이 데이터 저장(persistence)을 방해하지 않습니다.
단순 레이블링이 아닌 이상 점수 산출 (Anomaly scoring, not just a label). 모든 이벤트는 탐지 유형별 기본 가중치, 모델의 신뢰도, 그리고 트랙이 지속된 시간을 바탕으로 0~100 사이의 긴급도 점수를 부여받습니다. 이 점수는 알림 메타데이터에 저장되며 SMS 알림을 제어하는 기준이 됩니다 (임계값 이상일 때만 발송).
두 단계의 쿨다운 (Cooldowns at two levels). 심각도에 따라 키가 지정된 시스템 전체 쿨다운(CRITICAL은 1분, LOW는 최대 24시간)과, 이벤트 하위 유형별로 키가 지정된 SMS 발신기의 별도 인메모리(in-memory) 쿨다운이 존재합니다. 알림 스팸을 방지하기 위한 이중 안전장치입니다.
지속성 및 대시보드 (Persistence & the dashboard)
알림과 메트릭은 **WAL 모드 (WAL mode)**가 활성화된 SQLite에 저장됩니다. 이는 파이프라인이 계속 쓰기 작업을 수행하는 동안 준수한 동시 읽기(concurrent-read) 동작을 얻을 수 있는 저렴한 방법입니다. Flask 대시보드는 다음과 같은 기능을 제공합니다:
- 실시간 MJPEG 비디오 피드 (Live MJPEG video feed)
- **Server-Sent Events (SSE)**를 통한 실시간 알림 (웹소켓 (websockets) 불필요)
- 유형/심각도별 알림 필터링, 스냅샷 썸네일 및 확인 (acknowledge) 버튼
- 지난 24시간 동안 집계된 시간별 분석 데이터
- 몇 초마다
fps, 탐지 횟수, 처리 지연 시간 (latency)을 폴링(polling)하는 메트릭 패널
인증 (Auth)은 현재 의도적으로 단순하게 설계되었습니다 — bcrypt로 해싱된 단일 관리자 계정과 Flask 세션 쿠키를 사용합니다. 다중 역할 권한 (Multi-role permissions)은 아직 해결되지 않았으며 로드맵에 포함되어 있습니다.
알림 (Notifications)
- Twilio를 통한 SMS: 미확인 얼굴 및 위험 요소에 대해 발송되며, 이상 점수 (anomaly score) ≥ 25인 경우에만 제한적으로 작동합니다.
- SMTP를 통한 이메일: 규정 위반 사항에 대해 학생의 기관 이메일로 발송됩니다.
스택 (Stack)
Python 3.9, Flask, OpenCV, Ultralytics YOLOv8, face-recognition (dlib), ByteTrack를 위한 supervision, SQLite (WAL), Twilio, bcrypt, 카메라 장치 패스스루 (passthrough)를 포함한 Docker Compose.
다음에 해결할 과제
- 단일 프로세스를 넘어 확장해야 할 경우, 프로세스 내 SSE 큐를 멀티 프로세스 안전한 방식 (Redis pub/sub)으로 교체
- 단일 관리자 계정 대신 적절한 역할 기반 액세스 제어 (RBAC) 도입
- 얼굴 인코딩 (face encodings)을 독립된 pickle 파일 대신 DB에 바인딩
- API 경로에 대한 속도 제한 (Rate limiting) 적용
- 단순히 확인/무시하는 수준을 넘어선 실제 사고 검토 워크플로우(notes, assignment, escalation states) 구축
데모 (Demo)
실행 영상은 다음과 같습니다: youtube.com/watch?v=iarosznhHE8
체험하기 / 코드 살펴보기
저장소: github.com/SplinterSword/EyeNet
이벤트 기반 컴퓨터 비전 (CV) 파이프라인, 알림 피로 (alert fatigue) 문제, 또는 동시 부하 상황에서의 SQLite 설정 등 유사한 작업을 하고 계신다면 의견을 나누고 싶습니다. 위의 설계 결정 사항들에 대해 궁금한 점이 있다면 댓글로 기꺼이 답변해 드리겠습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기