Ultralytics YOLO27: 대기자 명단에 등록하세요.
Get Started
첫 YOLO 모델 학습하기·배포·레슨 9/10
강의초급

동영상에서 추론 실행

동영상 파일과 실시간 카메라에서 YOLO26을 실행하고, 메모리 사용량을 제한하며, 객체를 추적해 각 객체를 한 번씩만 세고, 모델이 프레임 속도를 따라가는지 확인합니다.

배포된 비전 시스템 대부분은 컨베이어, 출입문, 도로를 비추는 카메라처럼 동영상을 처리합니다. 동영상에는 단일 이미지에는 없는 두 가지 문제가 있습니다. 메모리: 긴 녹화 영상을 RAM에 전부 저장할 수 없습니다. 식별: 감지기가 같은 사람을 60개 프레임에서 보고 60명으로 보고합니다.

이 강의에서는 Predict 모드로 동영상을 프레임 단위로 스트리밍하고, 객체 추적을 추가해 각 사람을 한 번씩만 세고, 직접 카메라 루프를 작성하고, 모델이 처리 속도를 따라가는지 측정합니다.

학습 성과

메모리 사용량을 제한하면서 YOLO26으로 동영상이나 카메라 입력을 스트리밍하고, 추적으로 고유 객체 수를 세며, 프레임당 측정 시간(ms)을 바탕으로 입력 속도를 따라가는지 판단할 수 있습니다.

속성 과정
이미 사용법을 알고 계신다면, 간단히 요약해 드리겠습니다.
  1. model(video, stream=True)은 메모리 사용량을 제한하면서 프레임마다 Results 하나를 생성합니다.

  2. model.track(video, stream=True, persist=True, tracker='bytetrack.yaml')은 boxes.id을 추가합니다.

  3. 가장 높은 ID가 아니라 ID 집합을 사용해 고유 객체 수를 셉니다.

  4. 예산 = 1000 ÷ 입력 FPS입니다. 이를 result.speed['inference']과 비교합니다.

실습형

예제: 동영상 파일에서 Predict 실행#

동영상에도 이미지와 동일한 Predict 모드를 사용합니다. 이 62프레임 클립에는 아트리움을 가로지르는 사람들이 담겨 있습니다:

yolo predict model=yolo26n.pt source=https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4
video 1/1 (frame 1/62) solutions_ci_demo.mp4: 384x640 22 persons, 27.3ms
...
video 1/1 (frame 62/62) solutions_ci_demo.mp4: 384x640 22 persons, 54.8ms
Results saved to runs/detect/predict
  • frame 1/62: Ultralytics가 동영상을 디코딩하고 각 프레임을 이미지처럼 처리합니다.
  • 22 persons: 해당 프레임의 감지 결과입니다. 같은 보행자가 프레임마다 다시 집계됩니다.
  • 27.3ms ... 54.8ms: 머신에서 실행 중인 다른 작업에 따라 프레임별 처리 시간이 달라집니다.

주석이 추가된 동영상은 runs/detect/predict/solutions_ci_demo.mp4입니다. 직접 만든 모델도 같은 방식으로 작동합니다. yolo26n.pt을 runs/detect/pills_v1/weights/best.pt로 바꾸면 됩니다.

긴 입력 및 실시간 입력 스트리밍#

감지 결과에 따라 동작하는 코드를 작성하려면 스트림을 반복 처리합니다. 각 반복에서 프레임 하나의 Results을 얻은 다음 폐기합니다:

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
source = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"

frames, people = 0, 0
for result in model(source, stream=True, classes=[0], verbose=False):  # one Results object per frame
    frames += 1
    people += len(result.boxes)
print(f"{frames} frames, {people / frames:.1f} people per frame")
62 frames, 22.2 people per frame

같은 루프에서 웹캠 인덱스(source=0)나 rtsp://camera.local/stream과 같은 카메라 URL을 사용할 수 있으며, 스트림이 끝나거나 직접 중지할 때까지 실행됩니다. 매 두 번째 프레임만 처리하려면 vid_stride=2를 추가합니다. 이 루프를 감싸는 브라우저 UI를 만들려면 Streamlit 실시간 추론 가이드에서 루프를 앱으로 구성하는 방법을 확인하세요.

예제: 각 사람을 한 번씩만 세기#

"프레임당 22명"은 원하는 수치가 아닙니다. 원하는 수치는 "몇 명이 지나갔는가"입니다. 다중 객체 추적은 프레임 간에 바운딩 박스를 연결하며, Track 모드는 boxes.id에서 각 객체에 ID를 부여합니다:

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
source = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"

seen = set()
for result in model.track(source, stream=True, persist=True, classes=[0], tracker="bytetrack.yaml", verbose=False):
    if result.boxes.is_track:
        seen.update(result.boxes.id.int().tolist())
print(f"{len(seen)} unique people tracked")
32 unique people tracked

트랙이 아직 확인되지 않은 프레임에서는 is_track이 False이고, boxes.id는 None입니다. ID를 사용해 객체 수를 세거나, 이동 경로를 추적하거나, "이 객체가 3초 동안 여기에 있었다"와 같은 로직을 구현할 수 있습니다.

고속도로에서 Ultralytics YOLO가 추적하는 차량: 모든 자동차, 버스, 트럭에 지속 ID 레이블과 이동 궤적이 표시됩니다

추적기속도강건성
bytetrack.yaml더 빠르고 간단함객체가 잠시 사라지면 ID를 더 자주 잃습니다
botsort.yaml더 느림카메라가 움직일 때 ID를 더 잘 유지합니다(움직임 보정; 외형 기반 ReID는 with_reid을 통해 선택적으로 사용 가능).

tracker을 지정하지 않으면 Ultralytics는 현재 기본값인 tracktrack.yaml을 사용합니다. Track 모드에는 ocsort.yaml, deepocsort.yaml, fasttrack.yaml도 포함됩니다. 추적기를 선택하고 튜닝하는 방법과 추적기를 기반으로 객체 수 세기, 히트맵, 속도 추정을 구현하는 방법은 ByteTrack, BoT-SORT, TrackTrack으로 객체 추적하기에서 다룹니다.

예제: 직접 카메라 루프 작성#

실제 애플리케이션에서는 프레임을 직접 읽어 크기 조정, 자르기, 타임스탬프 추가, 다른 코드와 카메라 공유 등을 수행한 다음 각 프레임을 모델에 전달하는 경우가 많습니다. 이때 persist=True이 호출 간에 추적기를 유지합니다:

import cv2

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
cap = cv2.VideoCapture("solutions_ci_demo.mp4")  # saved locally by the commands above; or 0, or an rtsp:// URL

seen = set()
while cap.isOpened():
    ok, frame = cap.read()
    if not ok:
        break
    result = model.track(frame, persist=True, classes=[0], tracker="bytetrack.yaml", verbose=False)[0]
    if result.boxes.is_track:
        seen.update(result.boxes.id.int().tolist())
cap.release()
print(f"{len(seen)} unique people, highest ID {max(seen)}")
32 unique people, highest ID 67

track.py과 동일한 32명입니다. 가장 높은 ID는 32가 아니라 67입니다. 추적기는 확인되지 않은 잠정 트랙에도 ID를 할당하므로 ID에 간격이 생깁니다.

입력 속도 따라가기#

30 FPS 카메라는 33ms마다 프레임을 전달하며, 실제 처리 속도는 디코딩, 추론, 저장 중 가장 느린 단계에 의해 결정됩니다. 노트북 CPU에서 yolo26n은 실행 중인 다른 작업에 따라 384×640 프레임당 26~46ms가 걸렸습니다. 예산에 근접하거나 이를 넘는 수준이어서 여유가 없습니다. 처리 속도가 입력 속도를 따라가지 못하면 다음 순서로 조정해 보세요:

조정 항목효과
더 작은 모델(s 대신 n)대개 2~3배 더 빠릅니다
imgsz을 낮춥니다(640 대신 480).픽셀이 적을수록 연산량이 줄어듭니다. 작은 객체가 가장 먼저 영향을 받습니다.
vid_stride=2작업량이 절반으로 줄어듭니다. 느리게 움직이는 장면에 적합합니다.
GPU 또는 최적화된 내보내기 모델가장 큰 성능 향상 효과를 얻을 수 있습니다. NVIDIA용 TensorRT 엔진 또는 INT8을 사용하세요. 다음 강의를 참조하세요.

여러 카메라를 동시에 처리하는 방법과 관련 스레딩은 다중 스트림 추론에서 다룹니다.

이런 실수는 피하세요#

stream=True 누락

증상: 긴 녹화 영상을 처리할 때 프로세스가 종료될 때까지 메모리 사용량이 계속 증가하거나 실시간 카메라 호출이 반환되지 않습니다. 원인: 이를 사용하지 않으면 Python 호출은 반환 전에 프레임마다 Results 객체를 하나씩 수집합니다. 해결 방법: stream=True을 전달하고 제너레이터를 순회하십시오 — 제너레이터는 순회하기 전까지 아무 작업도 하지 않습니다.

직접 작성한 루프에서 persist=True 누락

증상: persist=True 없이 cv2_loop.py을 실행했을 때 개수가 27로 떨어졌고 가장 높은 ID도 27이었습니다. ID가 매 프레임마다 다시 시작되어 "고유" 개수는 가장 바쁜 프레임의 개수에 불과했습니다. 해결 방법: 프레임마다 한 번씩 track을 호출할 때마다 persist=True를 전달하세요.

가장 높은 ID로 개수 세기

증상: 사람은 32명인데 개수는 67명으로 표시됩니다. 원인: 확정되지 않은 트랙 때문에 ID에 공백이 생깁니다. 해결 방법: track.py처럼 ID 집합의 크기를 세세요.

트래커를 명시적으로 지정하기

기본 트래커는 릴리스에 따라 바뀔 수 있습니다. 버전이 달라져도 개수를 비교할 수 있도록 tracker="bytetrack.yaml"(또는 원하는 트래커)을 전달하세요.

사용해 보기

솔루션의 track_any.py을 데모 클립과 짧은 자체 동영상에서 실행합니다. 고유 객체 수, 프레임당 평균 추론 시간(ms), 해당 시간이 입력의 프레임 시간에 맞는지, 맞지 않는 경우 가장 먼저 조정할 항목을 포함해 세 줄로 보고합니다. yolo track ... save=True을 사용해 주석이 추가된 결과물 하나를 저장하고, 가림 현상 중에도 ID가 안정적으로 유지되는지 확인합니다. ID가 깜박이면 tracker=botsort.yaml를 사용해 보세요.

완료 조건
다음 조건을 모두 충족하면 레슨을 완료한 것입니다.
  • yolo predict 또는 yolo track에서 주석이 추가된 동영상을 저장했습니다.

  • 스트리밍 스크립트에서 boxes.id 값의 집합을 사용해 고유 객체 수를 출력합니다.

  • 측정한 프레임당 처리 시간(ms)을 입력 프레임 시간과 비교하고, 가장 먼저 조정할 속도 개선 항목을 정했습니다.

정답 보기
import sys

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
seen, times = set(), []
for result in model.track(sys.argv[1], stream=True, persist=True, tracker="bytetrack.yaml", verbose=False):
    times.append(result.speed["inference"])
    if result.boxes.is_track:
        seen.update(result.boxes.id.int().tolist())
print(f"{len(seen)} unique objects, {sum(times) / len(times):.1f} ms inference per frame")

python track_any.py your_video.mp4으로 실행합니다.

32 unique objects, 30.5 ms inference per frame (26-46 ms across runs on a laptop CPU)
Source is 30 FPS -> 33 ms budget: fits only on a quiet machine, with no headroom
First lever: export to OpenVINO or ONNX for this CPU, then vid_stride=2 if still short
다음 단계

Python에서 이미지와 동영상에 모델을 사용할 수 있습니다. 마지막 단계는 프로덕션 런타임에서 로드할 수 있는 형식으로 모델을 내보내는 것입니다.