포스트

YOLO 실시간 웹캠 탐지 학습 정리

YOLO 실시간 웹캠 탐지 학습 정리

realtime_detect.py를 만들고 돌려보면서 다룬 개념들을 정리한다. 어제 쓴 카메라 권한 트러블슈팅 글이 “무엇이 문제였나”였다면, 이 글은 “그 프로그램이 어떤 원리로 동작하는가”에 가깝다.

만든 것

웹캠에서 영상을 계속 받아오면서, 프레임 한 장 한 장에 YOLOv8 모델을 돌려 “이 사각형 안에 사람이 있다”, “저 사각형 안에 컵이 있다” 같은 결과를 실시간으로 화면에 그려주는 프로그램.

YOLO는 어떻게 “실시간”으로 탐지하나

옛날 방식(2-stage 탐지기)은 사진을 보고 “이 근처에 뭔가 있을 것 같다” 하는 후보 영역을 먼저 수백 개 찍어본 다음, 그 후보들을 하나씩 다시 들여다보며 “이건 사람, 이건 아님”을 판정하는 방식이었다 — 사진 전체를 촘촘한 그리드로 나눠서 칸마다 확대경으로 두 번 들여다보는 셈이다.

YOLO(“You Only Look Once”)는 이름 그대로 사진 전체를 한 번만 훑어서 동시에 “여기에 뭐가 있고 그건 무슨 종류다”를 한 번의 신경망 통과로 끝낸다. 두 번 들여다보지 않으니 훨씬 빠르고, 그래서 초당 수십 프레임이 필요한 웹캠 실시간 처리에 적합하다.

코드에서 보이는 부분은 model.predict(frame, conf=args.conf)가 프레임 하나를 모델에 넣고, results[0].plot()이 탐지된 박스·클래스명·신뢰도를 그림 위에 그려주는 부분이다. --conf(신뢰도 임계값)를 낮추면 애매한 것도 다 표시해서 오탐이 늘고, 올리면 확실한 것만 남아서 놓치는 게 늘어난다 — 재현율과 정밀도의 트레이드오프다.

실시간 영상 처리 루프의 구조

영화 필름을 한 장씩 넘겨보는 것과 같다. cap.read()로 한 장(프레임)을 받고, 그 한 장에 탐지를 돌리고, 화면에 그리고, 다음 장으로 넘어간다. 이걸 아주 빠르게(초당 몇십 번) 반복하면 사람 눈에는 “움직이는 영상”처럼 보인다.

1
2
3
4
5
6
while True:
    ok, frame = cap.read()               # 필름 한 장 받기
    results = model.predict(frame, ...)  # 그 한 장에 탐지
    annotated = results[0].plot()        # 박스 그리기
    cv2.imshow(..., annotated)           # 화면에 표시
    if cv2.waitKey(1) & 0xFF == ord("q"): break  # q 누르면 탈출

FPS(초당 프레임 수)는 1 / (이번 프레임 처리에 걸린 시간)으로 계산했다 — 모델이 무거우면(예: yolov8n → yolov8s → yolov8m) 프레임당 처리 시간이 늘어나서 FPS가 떨어진다. 정확도와 속도는 여기서도 트레이드오프다.

uv로 환경을 관리하는 이유

pip install -r requirements.txt는 “이 재료들 대충 이 버전대로 사줘”라고 부탁하는 것에 가깝고, 실제로 뭐가 깔렸는지는 그날그날 서버 상황에 따라 조금씩 달라질 수 있다. uv는 재료 목록(pyproject.toml)과 실제로 산 영수증(uv.lock, 정확한 버전 전부 고정)을 같이 남겨서, 나중에 누가 uv run으로 다시 실행해도 오늘과 똑같은 조합이 깔리는 걸 보장한다.

1
2
$ uv add ultralytics opencv-python   # 의존성 추가 + lock 갱신
$ uv run python realtime_detect.py   # lock에 적힌 그대로 실행

macOS 카메라 권한에서 헷갈렸던 부분

macOS는 “어떤 앱이 카메라를 쓸 수 있는가”를 앱 단위로 기억한다(TCC). 문제는 Claude Code가 대신 실행한 프로세스는 “Claude” 앱 소속으로 취급되고, 직접 연 터미널에서 실행한 프로세스는 “터미널” 소속으로 취급된다는 점이다. 둘은 서로 다른 권한 항목이라, 하나를 허용해도 다른 쪽은 여전히 막혀 있었다.

그래서 에이전트가 백그라운드로 실행했을 때는 계속 not authorized to capture video로 실패했고, 직접 터미널에서 실행하니 macOS가 권한 팝업을 제대로 띄워서 성공했다. 자세한 건 어제 글에 남겼다.

다음에 더 해볼 만한 것

  • --model yolov8s.pt로 바꿔서 정확도 vs FPS 체감 비교해보기
  • --save output.mp4로 탐지 영상 저장해서 신뢰도 낮은 오탐 구간 찾아보기
  • 특정 클래스만 걸러서 그리기(예: 사람만) — results[0].boxes.cls로 필터링

정리

  1. YOLO는 한 번의 신경망 통과로 위치+클래스를 동시에 뽑아서 실시간이 가능하다.
  2. 실시간 루프는 결국 “프레임 받기 → 탐지 → 그리기 → 다음 프레임”의 반복이고, FPS는 그 한 바퀴가 얼마나 빠른지의 척도다.
  3. uv는 lock 파일로 “누가 언제 돌려도 같은 환경”을 보장한다.
  4. macOS 카메라 권한은 “어떤 앱이 실행했는가” 기준이라, 에이전트 권한과 터미널 권한은 따로 관리된다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.