Video Utils¶
supervision.utils.video.VideoInfo
dataclass
¶
A class to store video information, including width, height, fps and total number of frames.
Attributes:
| Name | Type | Description |
|---|---|---|
width |
int
|
width of the video in pixels |
height |
int
|
height of the video in pixels |
fps |
float
|
frames per second of the video as a float. Common values include 23.976, 24.0, 25.0, 29.97, 30.0, 59.94, and 60.0. |
total_frames |
int | None
|
total number of frames in the video, default is None. An estimate from container metadata that can be zero, negative, or lower than the number of frames the video holds. |
Examples:
import supervision as sv
video_info = sv.VideoInfo.from_video_path(video_path="<SOURCE_VIDEO_FILE>")
video_info
# VideoInfo(width=3840, height=2160, fps=25.0, total_frames=538)
video_info.resolution_wh
# (3840, 2160)
Source code in src/supervision/utils/video.py
Methods:¶
from_video_path(video_path: str) -> VideoInfo
classmethod
¶
Read video metadata from a file path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Path to the video file. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
A |
VideoInfo
|
class: |
Examples:
import supervision as sv
video_info = sv.VideoInfo.from_video_path(video_path="<SOURCE_VIDEO_FILE>")
# VideoInfo(width=3840, height=2160, fps=25.0, total_frames=538)
Source code in src/supervision/utils/video.py
supervision.utils.video.VideoSink
¶
Context manager that saves video frames to a file using OpenCV.
Attributes:
| Name | Type | Description |
|---|---|---|
target_path |
The path to the output file where the video will be saved. |
|
video_info |
Information about the video resolution, fps, and total frame count. |
|
codec |
FOURCC code for video format |
Example
import supervision as sv
video_info = sv.VideoInfo.from_video_path("<SOURCE_VIDEO_PATH>")
frames_generator = sv.get_video_frames_generator("<SOURCE_VIDEO_PATH>")
with sv.VideoSink(target_path="<TARGET_VIDEO_PATH>", video_info=video_info) as sink:
for frame in frames_generator:
sink.write_frame(frame=frame)
Source code in src/supervision/utils/video.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | |
Methods:¶
__enter__() -> VideoSink
¶
Open the underlying video writer for context-managed frame output.
Source code in src/supervision/utils/video.py
__exit__(exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_traceback: TracebackType | None) -> None
¶
Release the writer without replacing an error from the managed block.
A secondary release failure is logged; a cleanup-only failure propagates. The sink is marked closed in either case.
Source code in src/supervision/utils/video.py
write_frame(frame: npt.NDArray[np.uint8]) -> None
¶
Writes a single video frame to the target video file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
NDArray[uint8]
|
The video frame to be written to the file. The frame must be in BGR color format. |
required |
Source code in src/supervision/utils/video.py
supervision.utils.video.FPSMonitor
¶
A class for monitoring frames per second (FPS) to benchmark latency.
Source code in src/supervision/utils/video.py
Attributes¶
fps: float
property
¶
Computes and returns the average FPS based on the stored time stamps.
Returns:
| Type | Description |
|---|---|
float
|
The average FPS across the recorded intervals. Returns 0.0 if fewer |
float
|
than two time stamps are stored. |
Methods:¶
__init__(sample_size: int = 30) -> None
¶
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
int
|
The maximum number of observations for latency benchmarking. |
30
|
Examples:
import supervision as sv
frames_generator = sv.get_video_frames_generator(
source_path="<SOURCE_FILE_PATH>")
fps_monitor = sv.FPSMonitor()
for frame in frames_generator:
# your processing code here
fps_monitor.tick()
fps = fps_monitor.fps
Source code in src/supervision/utils/video.py
reset() -> None
¶
supervision.utils.video.get_video_frames_generator(source_path: str, stride: int = 1, start: int = 0, end: int | None = None, iterative_seek: bool = False, prefetch: int = 0) -> Generator[npt.NDArray[np.uint8], None, None]
¶
Get a generator that yields the frames of the video.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
The path of the video file. |
required |
|
int
|
Indicates the interval at which frames are returned, skipping stride - 1 frames between each. |
1
|
|
int
|
Indicates the starting position from which video should generate frames |
0
|
|
int | None
|
Indicates the ending position at which video
should stop generating frames. If None, video will be read to the end.
A zero or negative frame count reported by the container means the
count is unknown: |
None
|
|
bool
|
If True, the generator will seek to the
|
False
|
|
int
|
If > 0, decode frames in a background thread and buffer up to
this many frames in a bounded queue. Useful when the consumer (e.g.
CPU inference) is the bottleneck and can overlap with decode I/O.
This works best when the consumer releases the GIL during frame
processing (common for numpy/PyTorch/ONNX C-extension calls). Pure
Python per-frame consumers that hold the GIL (for example, heavy
Python loops, PIL usage, or pandas |
0
|
Returns:
| Type | Description |
|---|---|
None
|
A generator that yields the frames of the video. |
Raises:
| Type | Description |
|---|---|
Exception
|
If |
ValueError
|
If |
RuntimeError
|
If |
Note
For live camera streams, use cv2.VideoCapture with an integer device
index directly.
:func:~supervision.utils.video.get_video_frames_generator is designed
for file-based
sources; cv2.VideoCapture must be released by the caller when done.
This requires OpenCV to be installed — the PyAV-based fallback used
when OpenCV is unavailable only supports file paths, not webcam device
indexes; passing an integer source to it always leaves the capture
closed (isOpened() returns False):
Examples:
import supervision as sv
for frame in sv.get_video_frames_generator(source_path="<SOURCE_VIDEO_PATH>"):
...
# Prefetch frames in a background thread to overlap I/O with CPU inference:
for frame in sv.get_video_frames_generator(
source_path="<SOURCE_VIDEO_PATH>", prefetch=8
):
...
Source code in src/supervision/utils/video.py
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | |
supervision.utils.video.process_video(source_path: str, target_path: str, callback: Callable[[npt.NDArray[np.uint8], int], npt.NDArray[np.uint8]], *, max_frames: int | None = None, prefetch: int = 32, writer_buffer: int = 32, show_progress: bool = False, progress_message: str = 'Processing video', preserve_audio: bool = False) -> None
¶
Process video frames asynchronously using a threaded pipeline.
This function orchestrates a three-stage pipeline to optimize video processing throughput:
- Reader thread: Continuously reads frames from the source video file and
enqueues them into a bounded queue (
frame_read_queue). The queue size is limited by theprefetchparameter to control memory usage. - Main thread (Processor): Dequeues frames from
frame_read_queue, applies the user-definedcallbackfunction to process each frame, then enqueues the processed frames into another bounded queue (frame_write_queue) for writing. The processing happens in the main thread, simplifying use of stateful objects without synchronization. - Writer thread: Dequeues processed frames from
frame_write_queueand writes them sequentially to the output video file. Shutdown waits for any active write to finish before releasing the output sink.
On a processing or writing failure, shutdown stops the reader instead of decoding the rest of the source. An active decode is allowed to finish before the capture is released; queued writes finish unless the writer itself fails. If a callback fails, a secondary reader failure is logged before the original callback exception is re-raised.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Path to the input video file. |
required |
|
str
|
Path where the processed video will be saved. |
required |
|
Callable[[NDArray[uint8], int], NDArray[uint8]]
|
Function called for each frame, accepting the frame as a numpy array and its zero-based index, returning the processed frame. |
required |
|
int | None
|
Optional maximum number of frames to process. If None, the entire video is processed (default). A value larger than the number of frames in the video processes the whole video. |
None
|
|
int
|
Maximum number of frames buffered by the reader thread. Controls memory use; default is 32. |
32
|
|
int
|
Maximum number of frames buffered before writing. Controls output buffer size; default is 32. |
32
|
|
bool
|
Whether to display a tqdm progress bar during processing. Default is False. |
False
|
|
str
|
Description shown in the progress bar. |
'Processing video'
|
|
bool
|
If True, copy the audio stream from |
False
|
Returns:
| Type | Description |
|---|---|
None
|
None |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the reader thread fails to open or decode the source
video, raised as |
TypeError
|
If |
Example
import supervision as sv
from rfdetr import RFDETRMedium
model = RFDETRMedium()
box_annotator = sv.BoxAnnotator()
def callback(frame, frame_index):
# Frames are BGR; RF-DETR expects RGB. Return the annotated frame.
detections = model.predict(frame[:, :, ::-1])
return box_annotator.annotate(frame.copy(), detections)
sv.process_video(
source_path="source.mp4",
target_path="target.mp4",
callback=callback,
preserve_audio=True,
)
Source code in src/supervision/utils/video.py
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 | |