System Overview
AIDON AI CCTV — 건설현장 안전 감지 시스템
CCTV 영상을 AI가 실시간으로 분석해 안전모 미착용·낙상·화재·연기·위험구역 침입을 자동 감지하고 운영자에게 알람을 전달합니다. 모든 이벤트는 영상·스냅샷과 함께 보관되어 사후 점검 및 리포트에 활용됩니다.
5종 위험 감지다채널 동시 모니터링2단계 처리 (1차 필터 → 2차 AI)위험구역 (ROI) 지정10초 영상 자동 보관주간 · 월간 PDF 리포트
01
이 웹이 무엇을 하나요?
기능 개요
- • CCTV 영상에서 위험 상황 자동 감지
- • 5초 이상 지속되면 알람 발생
- • 영상·스냅샷·이벤트 자동 보관
이용 대상
- • 운영자(Manager) — 모니터링·확인 처리
- • 관리자(Admin) — 카메라·계정·ROI 관리
- • 인증: 이메일 + 비밀번호 (JWT)
기술 구성
- • 백엔드: Python · FastAPI · Ultralytics YOLOv8
- • 프론트엔드: Next.js · React · TypeScript
- • 데이터베이스: PostgreSQL (Docker)
02
어떤 위험을 감지하나요?
안전모 미착용
경고 (Warning)
- 언제
- 작업자가 안전모를 쓰지 않고 작업하는 상황
- 어떻게
- 사람을 탐지한 뒤, PPE 모델로 머리 영역에 안전모가 있는지 확인
- 임계치
- 5초 이상 지속 시
- 알람
- 노란색 경고 + 알림 팝업 + 소리
위험구역 침입
경고 (Warning)
- 언제
- 관리자가 지정한 위험구역에 비인가 작업자가 머무는 상황
- 어떻게
- 사람의 발 좌표가 폴리곤 안에 들어왔는지 매 프레임 확인
- 임계치
- 기본 5초 이상 체류 (카메라별 조절)
- 알람
- 노란색 경고 + 알림 팝업
낙상 감지
긴급 (Critical)
- 언제
- 추락 또는 의식 잃은 채로 쓰러진 상황
- 어떻게
- 사람 박스의 y좌표 속도가 빠르거나, 종횡비가 누운 모양인데 정지
- 임계치
- 10초 이상 자세 미변화
- 알람
- 빨간색 긴급 + 알림 팝업 + 강한 소리
화재 감지
긴급 (Critical)
- 언제
- 불꽃이 보이는 상황
- 어떻게
- 먼저 HSV 색공간에서 빨강/주황 픽셀을 검사하고, 통과하면 Fire 모델로 확정
- 임계치
- 5초 이상 지속 (찰나의 용접 불꽃 제거)
- 알람
- 빨간색 긴급 + 알림 팝업
연기 감지
긴급 (Critical)
- 언제
- 연기가 보이는 상황
- 어떻게
- Fire 모델이 연기까지 함께 분류
- 임계치
- 5초 이상 지속
- 알람
- 빨간색 긴급 + 알림 팝업
03
사용된 AI 모델
1차 (사람 탐지)
yolov8n.pt
Ultralytics 공식 (자동 다운로드)
- 용도
- 사람 박스를 매 프레임 탐지 + ByteTrack으로 추적. 사람이 없는 채널은 PPE/Fire 호출 자체를 건너뜀.
- 크기
- ≈ 6 MB
- 속도
- ~50ms/frame (CPU 기준)
- env
YOLO_PRIMARY_WEIGHTS
기본 클래스
personbicyclecar...
2차 (PPE)
ppe.pt
snehilsanyal — 건설현장 안전장비
- 용도
- 사람이 있을 때만 호출. 같은 사람(track id)은 30초마다 한 번만 재검증.
- 크기
- ≈ 22 MB
- 속도
- ~120ms/frame (CPU 기준)
- env
YOLO_PPE_WEIGHTS
기본 클래스
HardhatNO-HardhatNO-MaskNO-Safety VestSafety Vest...
2차 (화재/연기)
fire.pt
Abonia1 — 화재·연기
- 용도
- HSV 빨강/주황 픽셀이 2% 이상일 때만 호출. 평상시엔 거의 호출되지 않음.
- 크기
- ≈ 18 MB
- 속도
- ~80ms/frame (CPU 기준)
- env
YOLO_FIRE_WEIGHTS
기본 클래스
Firesmokedefault
모델 교체 가이드
- 1새 가중치 파일(
.pt)을backend/weights/폴더에 저장. - 2
backend/.env의 해당 변수 값을 새 파일 이름으로 변경. 예:YOLO_PPE_WEIGHTS=my-ppe-v2.pt - 3백엔드 재시작:
python -m uvicorn main:app --port 8000 - 4이 페이지를 새로고침해 모델 카드의 "현재 로드된 클래스"가 갱신됐는지 확인.
참고 · 모델이 출력하는 클래스 이름과 시스템이 이해하는 위험 유형이 다르면
backend/models/yolov8_model.py 의 DANGER_CLASS_ALIASES 에 매핑을 추가하세요. (예: "no-hardhat" → "no_helmet")04
기획서 대비 — 무엇이 맞고 무엇이 어긋났나
요약
기획서가 제안한 8개 항목 중 3개는 정확히 일치(1차 필터·HSV·ByteTrack), 2개는 외부 모델로 대체(PPE·화재 — 자체 파인튜닝 안 됨), 3개는 미적용(낙상 포즈 추정·TensorRT·DeepStream). 또한 PPE는 5종 위반 유형 중 안전모 1종만 실제 알람으로 활성화되어 있음.
| 기획서 명시 | 현재 구현 | 매칭 |
|---|---|---|
| YOLOv8n 사람 탐지 (1차) | yolov8n.pt (Ultralytics 자동 다운로드) | 정확히 일치 |
| ByteTrack 객체 추적 | Ultralytics 내장 ByteTrack + lap | 정확히 일치 |
| HSV 빨강/주황 사전 필터 | OpenCV HSV + 2% 임계치 (FIRE_HSV_THRESHOLD) | 정확히 일치 |
| YOLOv8s PPE 분류 (안전모/안전벨트/안전화/조끼 5클래스) | ppe.pt (snehilsanyal, 10클래스) 안전벨트·안전화는 모델 자체에 클래스 없음. 안전조끼는 감지되지만 알람 매핑 안 됨. | 부분/대체 |
| Custom Fire-CNN (자체 파인튜닝) | fire.pt (Abonia1 — 외부 공개 모델) YOLO 기반 화재/연기 모델이지만 우리 현장 데이터로 파인튜닝하지 않은 상태. | 부분/대체 |
| RTMPose 포즈 추정 (낙상 2차) | 없음 — 룰 기반(y속도/종횡비/정지)만 쪼그려 앉기·점심시간 휴식과 진짜 의식 잃은 상태 구분이 어려움 → 오탐 위험. | 미적용 |
| TensorRT FP16 가속 | PyTorch CPU (자동 CUDA fallback) GPU 서버 도입 후 적용 예정. CPU 환경에서는 사람 탐지 ~50ms/frame. | 미적용 |
| DeepStream 7 / Triton Inference Server | OpenCV + FFmpeg (소프트웨어 디코딩) 32~64채널 운영 시 필요. 현재는 1~4채널만 가능. | 미적용 |
PPE — 1종만 알람
현재 ppe.pt는 10클래스를 감지하지만, 알람으로 가는 건 안전모(no_helmet) 1종뿐입니다.
- 안전모 미착용 — 활성화됨
- 안전조끼 미착용 — 모델은 감지, 알람 매핑 안 됨
- 안전벨트·안전화 — 모델 클래스 자체 없음
안전조끼는
backend/models/yolov8_model.py의 DANGER_CLASSES에 "no_safety_vest"를 추가하면 즉시 활성화됨.낙상 — 포즈 추정 빠짐
기획서는 RTMPose 17개 키포인트 + 5초 시퀀스 분석을 명시했지만, 현재는 박스 모양·속도 룰만 사용합니다.
- 급강하 (y속도) — 추락 직후 잡힘
- 종횡비 + 정지 — 누운 자세 잡힘
- 포즈 검증 없음 — 쪼그려 앉기·점심 휴식 오탐 가능
yolov8n-pose.pt (자동 다운로드)를 후보 영역에만 호출하면 정확도 ↑.화재 — 외부 모델 그대로
기획서의 "Custom Fire-CNN(자체 파인튜닝)"이 아니라, 외부 공개 모델인 fire.pt (Abonia1)를 그대로 사용 중입니다.
- HSV 사전 필터로 호출 빈도 절감
- 연기(smoke) 클래스 정확도 낮음 — 모델 한계
- 현장 영상으로 파인튜닝 안 됨
현장 영상으로 파인튜닝하거나, 더 좋은 화재 전용 모델로 교체 필요.
성능 격차 — CPU 데모 (현재) vs GPU 운영 (기획서 목표)
| 지표 | 현재 | 기획서 목표 | 비고 |
|---|---|---|---|
| 1차 사람 탐지 속도 | ~50ms/frame (CPU) | ~1~2ms/frame (GPU TensorRT FP16) | 25~50배 격차 — GPU 필수 |
| PPE 추론 평균 지연 | ~120ms/frame (CPU) | ~10ms (GPU) | 약 10배 |
| 동시 처리 채널 | 1~4채널 안정 | 32~64채널 (설계서 §5) | GPU + DeepStream 도입 시 가능 |
| 화재 탐지 정확도 | 외부 모델 그대로 | 현장 영상으로 파인튜닝 | 용접·노을 반사 오탐 가능성. 현장 데이터 수집 필요 |
| 낙상 탐지 정확도 | 룰 기반만 (포즈 없음) | 포즈 추정 + 5초 시퀀스 | yolov8-pose 또는 RTMPose 추가 시 개선 |
| PPE 위반 종류 수 | 안전모(no_helmet) 1종만 알람 | 안전모·안전벨트·안전화·조끼 4종 + 마스크 | 안전조끼는 코드 한 줄 추가로 즉시 활성. 나머지는 별도 모델 필요. |
CPU 환경에서 즉시 개선 가능
- 안전조끼 미착용 알람 활성화코드 1줄 — DANGER_CLASSES 매핑 추가. 약 5분 작업.
- 낙상 2차 검증에 yolov8-pose 추가후보 시점에만 호출 — CPU 부담 최소. 약 30~60분 작업.
- 현장 영상 수집 시작클립이 자동 저장되므로, 라벨링만 시작하면 추후 파인튜닝 데이터로 활용 가능.
GPU 서버 도입 후
- TensorRT FP16 변환기존 .pt를 .engine으로 변환 — CPU 대비 5~10배 속도.
- DeepStream 7 또는 Triton 도입32~64채널 동시 처리.
- 자체 데이터로 PPE·Fire 파인튜닝한국 건설현장 특화로 정확도 향상.
- NVDEC 하드웨어 디코딩4K 다채널 처리 시 GPU 디코더 활용.
05
이렇게 동작합니다 (2단계 처리 흐름)
1차 필터 (모든 채널 · 항상)
- ① 카메라 영상 받기 + 디코딩
- ② 4 fps로 추론용 프레임 추출
- ③ yolov8n 으로 사람 탐지 + 추적(ByteTrack)
- ④ 룰 엔진: ROI 폴리곤 · 낙상 자세 · HSV 색
가벼움 · 빠름 · 항상 돈다
트리거 큐
in-process
(운영 시 Redis)
2차 AI (트리거 시에만)
- ① PPE 분류 (비동기 큐, 사람 있을 때만)
- ② Fire 분류 (HSV 통과 시)
- ③ 5초 지속 검증 (SustainTracker)
- ④ 확정 시 → DB 저장 + 클립 녹화 + 알람
무거움 · 가끔 돈다
알람 정책 — 어떻게 알람 폭주를 막나
① 5초 지속 검증
한 프레임만 잡힌 가짜 트리거(용접 불꽃, 화면 가장자리 스침)는 알람으로 안 뜸.짧게 끝난 트리거는 triggers 테이블에 'expired'로만 기록.
② ACK 기반 차단
같은 (카메라, 유형)의 미확인 알람이 1건이라도 있으면 같은 종류 새 알람은 차단.운영자가 ACK 하기 전까지는 같은 인시던트로 묶임.
③ ACK 시 즉시 해제
확인 처리 누르면 그 (카메라, 유형)의 dedup 상태가 리셋되어 즉시 다음 인시던트가 알람.
06
각 페이지 안내
대시보드
/전체 상태 한눈에. 활성 카메라·24시간/주간 감지·미확인 알람·시간별 추이·카메라별 순위 등.
실시간 영상
/live선택한 카메라를 그리드로 동시 모니터링. 위험 감지 시 빨간 박스·깜빡임·소리·우하단 팝업.
카메라 관리
/camerasAdminRTSP URL/파일/웹캠 등록·삭제·활성 토글, 연결 테스트, 카메라별 ROI 폴리곤 편집.
위험 감지 기록
/events유형 → 날짜 → 시간순으로 정리된 알람 아카이브. 영상/스냅샷(분석/원본) 보기 + 확인 처리.
안전 리포트
/reports이번 주/지난 주/이번 달 등 기간별 PDF 리포트 다운로드. 변환율 통계 포함.
사용자 관리
/usersAdmin운영자 계정 추가·비활성화·비밀번호 재설정·권한 변경.
07
튜닝 가능한 설정 (.env 환경변수)
| 환경변수 | 기본값 | 의미 |
|---|---|---|
DATABASE_URL | postgresql+psycopg2://ai_cctv:ai_cctv_dev@127.0.0.1:5433/ai_cctv | DB 접속 정보. Docker Postgres 사용. |
FRONTEND_ORIGIN | http://localhost:3000 | CORS 허용 출처. |
JWT_SECRET | (dev 기본값 — 운영 시 반드시 교체) | JWT 토큰 서명 키. |
JWT_ACCESS_MINUTES | 60 | access 토큰 유효시간(분). |
JWT_REFRESH_DAYS | 7 | refresh 토큰 유효시간(일). |
ADMIN_EMAIL / ADMIN_PASSWORD | admin@local / admin1234 | users 테이블이 비어 있을 때 첫 부팅 시드 관리자. |
YOLO_PRIMARY_WEIGHTS | yolov8n.pt | 1차 사람 탐지 가중치. |
YOLO_PPE_WEIGHTS | ppe.pt | 2차 PPE 분류 가중치. |
YOLO_FIRE_WEIGHTS | fire.pt | 2차 화재/연기 가중치. |
YOLO_PRIMARY_CONF / YOLO_PPE_CONF / YOLO_FIRE_CONF | 0.30 / 0.35 / 0.35 | 각 모델의 confidence 임계치. 낮을수록 민감. |
FIRE_HSV_THRESHOLD | 0.02 (=2%) | 1차 화재 게이트. 프레임의 빨강/주황 픽셀 비율이 이 값을 넘을 때만 fire.pt 호출. |
FALL_Y_VELOCITY_NORM | 0.30 | 낙상 1차 — y좌표가 1초에 화면 높이의 이만큼 떨어지면 후보. |
FALL_ASPECT_RATIO | 1.5 | 낙상 1차 — 가로/세로 비율이 이 값 이상이면 누운 자세 후보. |
FALL_STILL_SECONDS / FALL_STILL_MOVE_FRAC | 3.0 / 0.05 | 낙상 — 누운 자세가 N초 정지(이동 < 화면 너비의 X%)면 후보. |
SUSTAIN_NO_HELMET / SUSTAIN_FIRE / SUSTAIN_SMOKE / SUSTAIN_FALL | 5 / 5 / 5 / 10 | 유형별 알람 발생을 위한 지속 시간(초). 이 시간 미만이면 무시. |
RTSP_URL_TEST | rtsp://… | RTSP 연결 테스트용 기본 URL. |
WEBCAM_DEVICE_NAME / WEBCAM_RTSP_URL | … | 노트북 웹캠을 RTSP로 송출하는 ffmpeg 설정. |
파일 위치:
backend/.env · 변경 후 백엔드 재시작 필요.08
기술 스택
| 계층 | 현재 사용 중 | 상태 | 비고 |
|---|---|---|---|
| 추론 프레임워크 | Ultralytics YOLOv8 + PyTorch | 구현됨 | 운영 시 NVIDIA DeepStream / Triton + TensorRT 검토 |
| 객체 탐지 / 추적 | yolov8n + ByteTrack | 구현됨 | ByteTrack은 Ultralytics 내장 사용 |
| 안전장비 분류 | ppe.pt (10클래스) | 구현됨 | 교체 가능 — snehilsanyal 기반 |
| 화재/연기 분류 | fire.pt (3클래스) | 구현됨 | Abonia1 기반 — 연기 정확도 향상 필요 |
| 포즈 추정 | 룰 기반 (y속도/종횡비) | 부분 구현 | GPU 도입 시 RTMPose 또는 yolov8-pose 추가 |
| 스트리밍/디코딩 | OpenCV + FFmpeg (소프트웨어) | 부분 구현 | 운영 GPU에서 NVDEC 하드웨어 디코딩으로 |
| 비동기 큐 | in-process queue.Queue | 부분 구현 | 운영 시 Redis로 교체 (멀티 프로세스 워커) |
| 백엔드 API | FastAPI + Uvicorn | 구현됨 | JWT 인증 (PyJWT + bcrypt) |
| 데이터베이스 | PostgreSQL 17 (Docker) | 구현됨 | 로컬은 :5433 컨테이너 |
| 프론트엔드 | Next.js 16 + React 19 + Tailwind | 구현됨 | Recharts로 차트 시각화 |
| PDF 리포트 | ReportLab + 맑은고딕 | 구현됨 | 한글 폰트 임베드 |
| 영상 클립 인코딩 | ffmpeg + H.264 (libx264) | 구현됨 | 10초 ±5s 클립을 브라우저 호환 mp4로 |
09
운영 환경 시나리오
현재: 개인 PC(CPU) + Docker Postgres + 1~4채널 데모 영상. 본인 PC만으로도 모든 기능이 동작하고 있습니다.
| 적용 위치 | ① 엣지 | ② 하이브리드 | ③ 중앙 |
|---|---|---|---|
| 1차 필터 | 현장 미니PC + GPU | 현장 미니PC + GPU | 본사 서버 |
| 2차 AI | 현장 미니PC + GPU | 본사 (트리거 시 영상 전송) | 본사 서버 |
| 회선 트래픽 | 이벤트 메타데이터만 | 메타데이터 + 트리거 클립 | 전체 영상 (1.5Gbps+) |
| 본사 GPU 부하 | 거의 0 | 이벤트 시에만 | 상시 40~70% |
10
앞으로 할 일 (로드맵)
최근 완료 (2026-05-18 검증 라운드)
- 구현됨1차/2차 트리거 분리 — 실제 워커 루프 동작 확인rtsp_worker._inference_loop에서 1차 사람 탐지 매 프레임 / 2차 PPE는 사람 있을 때만 + 30초 재검증 / 2차 Fire는 HSV 통과 시만 / ROI·낙상 룰 동시 적용 — 카운터(primary_calls/ppe_calls/ppe_gated/fire_gated)가 stats에 누적되어 대시보드에 노출됨.
- 구현됨ROI 폴리곤 편집 → DB → 워커 실시간 적용 흐름에디터 저장 시 PATCH /cameras/{id}/rois → WorkerManager.refresh_rois → StreamWorker.set_rois 즉시 반영. 다음 추론 틱부터 발 좌표 기반 체류 시간 누적.
- 구현됨대시보드 — 2단계 트리거 변환율 + 추론 큐 카드Row 5에 트리거 유형별 총/확정/만료/변환율 표 + PPE 비동기 큐 사용량·처리량·평균 지연 카드.
- 구현됨메뉴/페이지 권한 가드 (admin / manager 분리)Sidebar는 /users만 admin 전용. /cameras 페이지에 isAdmin 가드 추가 — 등록 폼·활성 토글·ROI·삭제 버튼이 관리자에게만 보임 (운영자는 403 받지 않고 UI 단계에서 차단).
- 구현됨PDF 안전 리포트 — 트리거 변환율 섹션 렌더 검증데이터 있는 기간에서 "트리거"·"변환율"·"확정"·"1차"·"2차" 모두 정상 출력. 평가 §4 위험도 점수와 §5 권고문에 변환율 임계치(<5%) 자동 인용.
AI 모델 개선
- 계획됨현장 데이터로 PPE 모델 파인튜닝지금 PPE 모델은 외부 데이터로 학습 — 실제 현장 영상으로 학습하면 정확도 ↑
- 계획됨연기 감지 모델 교체/추가현재 fire.pt의 smoke 클래스 정확도가 낮음
- 계획됨RTMPose 또는 YOLOv8-pose로 낙상 2차 검증지금은 룰 기반만. 포즈 추정 추가하면 오탐 감소
- 계획됨허가 작업자 식별 (헬멧 색상 또는 ID 카드)ROI 침입을 비인가/인가로 분류
인프라 (GPU 서버 도입 시)
- 계획됨TensorRT FP16 변환 (CPU → GPU 5~10배 가속)
- 계획됨DeepStream / Triton 도입 (32~64채널 대응)
- 계획됨NVDEC 하드웨어 디코딩
- 계획됨Redis 큐 + 멀티 프로세스 워커
운영 기능
- 계획됨외부 알람 발송 (SMS/이메일/카카오톡/Slack)
- 계획됨카메라 다운/오류 시 운영자 통보
- 계획됨라이브 그리드에 ROI 폴리곤 오버레이 표시지금은 ROI 편집 페이지에서만 다각형이 보임
- 계획됨리포트 자동 발송 스케줄링
- 계획됨다중 현장(파트너사) 지원
프로덕션 준비
- 계획됨HTTPS + TLS 인증서
- 계획됨HttpOnly 쿠키 + CSRF (현재 localStorage 토큰)
- 계획됨Alembic DB 마이그레이션 (현재 ad-hoc ALTER)
- 계획됨Sentry 에러 추적
- 계획됨Prometheus + Grafana 모니터링
- 계획됨Postgres 자동 백업
- 계획됨GitHub Actions CI/CD
- 계획됨단위·통합 테스트 추가 (현재 0%)
11
디렉토리 구조
AI_CCTV/
├── backend/ # FastAPI 서버
│ ├── main.py # 앱 진입점 (lifespan, 라우터 등록)
│ ├── .env # ★ 환경변수 (모델·DB·인증 등 모든 설정)
│ ├── docker-compose.yml # Postgres 컨테이너
│ ├── weights/ # ★ AI 모델 가중치 (.pt 파일)
│ │ ├── yolov8n.pt # 1차 사람 탐지 (자동 다운로드)
│ │ ├── ppe.pt # 2차 PPE 분류
│ │ └── fire.pt # 2차 화재/연기
│ ├── models/yolov8_model.py # 모델 로더 + 클래스 매핑
│ ├── triggers/ # 1차 트리거 룰
│ │ ├── ppe_trigger.py # 30초 재검증 + 위반 캐시
│ │ ├── fire_trigger.py # HSV 사전 필터
│ │ ├── roi_trigger.py # 폴리곤 + 체류 시간
│ │ ├── fall_trigger.py # y속도 + 종횡비
│ │ └── sustain.py # 5초 지속 검증 + triggers 테이블
│ ├── rtsp_worker.py # 카메라당 캡처+추론 스레드
│ ├── worker_manager.py # 카메라별 워커 등록·라이프사이클
│ ├── inference_service.py # 2차 PPE 비동기 큐
│ ├── clip_recorder.py # 링버퍼 → H.264 mp4
│ ├── api/ # FastAPI 라우터
│ │ ├── auth.py / users.py # 로그인 / 사용자 CRUD
│ │ ├── cameras.py # 카메라 + ROI + 스냅샷
│ │ ├── events.py / triggers.py # 이벤트 / 트리거 조회
│ │ ├── stats.py / reports.py # 통계 / PDF 리포트
│ │ ├── stream.py # WebSocket 멀티구독자
│ │ ├── detect.py # 정지 이미지 검출
│ │ └── webcam.py # 노트북 캠 ffmpeg 제어
│ ├── db/ # DB
│ │ ├── database.py / models.py # 엔진 + ORM 모델
│ │ └── repository.py # 쿼리 + dedup + 통계
│ ├── auth/ # bcrypt + JWT
│ ├── reports/pdf_builder.py # 한글 PDF 생성
│ └── captures/clips/ # 자동 녹화된 mp4 (이벤트별)
│
└── frontend/ # Next.js 16
├── app/
│ ├── page.tsx # 대시보드
│ ├── live/ # 실시간 그리드
│ ├── cameras/ # 카메라 관리 + [id]/roi 편집
│ ├── events/ # 위험 감지 기록
│ ├── reports/ # PDF 리포트
│ ├── users/ # 사용자 관리
│ ├── architecture/ # ★ 이 페이지
│ └── login/ # 로그인
├── components/ # 재사용 컴포넌트
└── lib/ # 타입 + API 클라이언트 + 인증