동영상에서 추론 실행
동영상 파일과 실시간 카메라에서 YOLO26을 실행하고, 메모리 사용량을 제한하며, 객체를 추적해 각 객체를 한 번씩만 세고, 모델이 프레임 속도를 따라가는지 확인합니다.
배포된 비전 시스템 대부분은 컨베이어, 출입문, 도로를 비추는 카메라처럼 동영상을 처리합니다. 동영상에는 단일 이미지에는 없는 두 가지 문제가 있습니다. 메모리: 긴 녹화 영상을 RAM에 전부 저장할 수 없습니다. 식별: 감지기가 같은 사람을 60개 프레임에서 보고 60명으로 보고합니다.
이 강의에서는 Predict 모드로 동영상을 프레임 단위로 스트리밍하고, 객체 추적을 추가해 각 사람을 한 번씩만 세고, 직접 카메라 루프를 작성하고, 모델이 처리 속도를 따라가는지 측정합니다.
실습형
예제: 동영상 파일에서 Predict 실행#
동영상에도 이미지와 동일한 Predict 모드를 사용합니다. 이 62프레임 클립에는 아트리움을 가로지르는 사람들이 담겨 있습니다:
yolo predict model=yolo26n.pt source=https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4video 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/predictframe 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초 동안 여기에 있었다"와 같은 로직을 구현할 수 있습니다.

| 추적기 | 속도 | 강건성 |
|---|---|---|
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 67track.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을 사용하세요. 다음 강의를 참조하세요. |
여러 카메라를 동시에 처리하는 방법과 관련 스레딩은 다중 스트림 추론에서 다룹니다.
이런 실수는 피하세요#
증상: 긴 녹화 영상을 처리할 때 프로세스가 종료될 때까지 메모리 사용량이 계속 증가하거나 실시간 카메라 호출이 반환되지 않습니다. 원인: 이를 사용하지 않으면 Python 호출은 반환 전에 프레임마다 Results 객체를 하나씩 수집합니다. 해결 방법: stream=True을 전달하고 제너레이터를 순회하십시오 — 제너레이터는 순회하기 전까지 아무 작업도 하지 않습니다.
증상: persist=True 없이 cv2_loop.py을 실행했을 때 개수가 27로 떨어졌고 가장 높은 ID도 27이었습니다. ID가 매 프레임마다 다시 시작되어 "고유" 개수는 가장 바쁜 프레임의 개수에 불과했습니다. 해결 방법: 프레임마다 한 번씩 track을 호출할 때마다 persist=True를 전달하세요.
증상: 사람은 32명인데 개수는 67명으로 표시됩니다. 원인: 확정되지 않은 트랙 때문에 ID에 공백이 생깁니다. 해결 방법: track.py처럼 ID 집합의 크기를 세세요.
기본 트래커는 릴리스에 따라 바뀔 수 있습니다. 버전이 달라져도 개수를 비교할 수 있도록 tracker="bytetrack.yaml"(또는 원하는 트래커)을 전달하세요.
정답 보기
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