Commit Graph

25 Commits

Author SHA1 Message Date
Erik 541b0226cd
feat: add image URL loader (#2372)
- Added image loading from HTTP and HTTPS URLs with descriptive URL validation errors
- Added optional caching for image URL loads using the shared Supervision cache
- Improved URL downloads with atomic file replacement and shared download behavior across image loading and asset downloads
- Updated image decoding compatibility with Pillow fallbacks when OpenCV decoding or encoding is unavailable

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Jirka Borovec <6035284+Borda@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-07-28 21:24:55 +02:00
Mahbod b20d6eac46
feat(utils): add prefetch to `get_video_frames_generator` (#2273)
* feat(utils): add prefetch to get_video_frames_generator
* fix(utils): harden _prefetched_frames_generator threading safety
* test(utils): add prefetch combination and minimum-queue tests
* docs(utils): improve prefetch documentation, validation, and test docstrings
* fix(utils): harden prefetch reader-thread exception handling + docs
* docs(changelog): sync front-matter date_modified
* test(utils): harden and extend prefetch test coverage
* test(utils): cover buffered-frames-before-error and zero-frame prefetch cases

---------

Co-authored-by: Jirka Borovec <6035284+Borda@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-21 14:12:08 +02:00
Jirka Borovec 1efa5b8eaa
feat(cv2): complete fallback integration (#2439)
* feat(cv2): complete fallback integration
* fallback-fixes: reject invalid addWeighted dtype; O(N) approxPolyDP anchor seeding
* tests: copyMakeBorder sequence parity; drop non-empty facade-import assert; fix Windows path separator in boundary check
* fix(cv2): copyMakeBorder scalar value only fills channel 0 on multichannel images

---------

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2026-07-17 07:50:45 +02:00
Jirka Borovec 20b7c085b7
feat(video): require PyAV during cv2 transition (#2438)
- Add the PyAV-backed file-video and audio fallback to the compatibility layer.
- Declare PyAV alongside OpenCV until the final dependency-removal integration.
- _VideoWriter now rejects is_color=False (NotImplementedError) instead of
  silently dropping it, since the PyAV fallback only encodes 3-channel frames.
- _mux_audio cleanup (container closes, temp-file removal) is now best-effort
  so a failing close/remove in finally can no longer mask the primary result
  or the original exception.
- The subprocess used to validate the cv2-free fallback had no timeout;
  a hang (import deadlock, codec probe stall) could block the whole CI
  run. Added a 60s timeout so a hang fails fast with a clear traceback
  instead of an opaque suite-wide stall.
- process_video(preserve_audio=True) docstring still described the old
  ffmpeg-based muxing; audio remuxing was reimplemented with PyAV and no
  longer requires an external ffmpeg executable.
- get_video_frames_generator's documented webcam fallback
  (`_cv2.VideoCapture(0)`) silently fails under the PyAV backend: the
  BackendUnavailableError raised for integer sources was swallowed with no
  logging, so isOpened() just returns False with zero diagnostic signal.
  Doc note now states the limitation explicitly and the capture logs a
  warning instead of failing silently.

---------

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2026-07-16 20:13:15 +02:00
Jirka Borovec 8ecd9a6680
refactor(cv2): add optional backend facade (#2430)
* refactor(cv2): add optional backend facade
* test: guard real cv2 oracle import for cv2-less environments
* test: preserve existing PYTHONPATH in subprocess import tests
* lint: auto-fix violations after resolve cycle
* fix(typing): remove obsolete suppressions
* test(cv2): parametrize constant alignment tests and refactor fallback validation
* test(cv2): simplify constant grouping and optimize REQUIRED_SYMBOLS validation

---------

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-07-15 20:53:24 +02:00
Jirka Borovec 16814acff3
feat(utils): add `TkImageWindow` to unblock switch to `opencv-python-headless` (#2320)
- Added `sv.ImageWindow`, a Tkinter/Pillow-based desktop image viewer with BGR, grayscale, and BGRA support, keyboard polling, left-click callbacks, context-manager usage, window-state checks, and clean close handling
- Added responsive image resizing with optional aspect-ratio preservation and correctly mapped mouse coordinates after scaling or letterboxing
- Updated compatible runnable examples to use `sv.ImageWindow`, while retaining OpenCV display APIs for worker-thread streaming examples that are incompatible with Tkinter
- Improved `sv.cv2_to_pillow` to support grayscale and BGRA images
- Updated webcam guidance to clarify capture ownership and explicit `VideoCapture` cleanup
- Fixed image-window event handling to prevent stale keypresses, ghost windows, close-time races, and blocked waits after the window closes

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-07-15 15:06:55 +02:00
Jirka Borovec 75023c5f2f
fix: remaining review findings in dataset, docs, and tests (#2416)
- Added `sv.mask_to_roi` as an explicit migration path for exclusive mask bounds
- Fixed COCO, CreateML, and Pascal VOC export validation to reject ambiguous or colliding dataset paths before writing
- Fixed in-memory `DetectionDataset` split and merge behavior
- Fixed `supervision` imports to avoid loading ByteTrack until it is used
- Fixed detection conversion helpers to support coordinate-convention migration while preserving legacy inclusive defaults
- Fixed Azure tag mapping, anchor rounding, and line-zone smoothing to avoid incorrect or ghost detections
- Fixed video processing shutdown handling for timeout and full-queue cases
- Improved downloader, validator, documentation, and regression coverage for the shipped dataset, detection, annotator, image, and video behavior

---------

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-07-08 23:00:32 +02:00
Jirka Borovec 23a2227ae7
fix(docs): resolve review deprecation follow-ups (#2415)
- Extend active deprecation removals to 0.31.0 and align deprecated API docs, changelog, and warnings.
- Add missing reference docs for VLM, conversion helpers, geometry, metrics extras, and tracker deprecation notices.
- Raise when ImageSink cannot write an image and cover the failure path with a regression test.
- Correct conversion and deprecated docs to match exported names and restore KeyPoints.confidence.
- Add regression coverage for SUPERVISION_DEPRECATION_WARNING precedence and document ImageSink.save_image() failure behavior.

* test: add validation and behavior tests for Color, Position, and polygon approximation adjustments

---------

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-07-08 14:30:32 +02:00
Jirka Borovec c3413a8f10
fix(annotators): resolve annotator medium findings (#2407)
- Added deterministic color lookup with flexible palette resolution and clear errors for empty palettes
- Improved annotator and utility handling for warning formatting, plotting imports, and icon caching
- Added validation for keypoint edges, MediaPipe inputs, and VideoSink state

---------

Co-authored-by: Codex <codex@openai.com>
2026-07-06 22:43:35 +02:00
Jirka Borovec 78aec073c4
test: cover public API gaps and dataset split (#2399)
* test: cover public API gaps and dataset split
* test(sinks): switch VideoSink to AVI/MJPG and add ImageSink clearing test
* test(detection): add box_non_max_merge 6-column class-separation tests
* test(dataset): drop deprecated dict API and strengthen class-id assertion
* test(public_api): strengthen importability check with getattr

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2026-07-03 20:57:42 +02:00
Jirka Borovec 15dbbb5cb1
fix(annotators): clip crops, fix heatmap wrap, release capture (#2393)
- Fixed annotators to avoid internal deprecation warnings from image overlay usage while preserving the public deprecated wrapper
- Fixed CropAnnotator crashes for partially out-of-frame detections by clipping crops to scene bounds and skipping degenerate boxes
- Fixed HeatMapAnnotator heat disappearing after 256 accumulated frames
- Fixed video frame generation to release the capture when iteration ends early
- Updated documentation for overlay deprecation, crop clipping behavior, and video capture release guarantees

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2026-07-03 11:03:45 +02:00
Jirka Borovec 0a95bae8a8
chore: bump minimum Python to 3.10 (#2260)
- Drop Python 3.9 from CI test matrix
- requires-python = ">=3.10" in pyproject.toml
- ruff target-version py39 → py310
- mypy python_version 3.9 → 3.10
- Remove Python 3.9 classifier

---------

Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-29 14:45:30 +02:00
Jirka Borovec 10b538373b
chore: postpone annotations and add validation refinements (#2357)
Add postponed annotations to test modules and modernize one test helper annotation for Python 3.9-compatible collection.

---------

Co-authored-by: Codex <codex@openai.com>
2026-06-27 08:36:26 +02:00
Vikas saini 7239af5048
Refactor/remove asserts annotators image (#2354)
- ValueError → TypeError in all three ensure_*_image_for_* decorators (conversion.py): the decorator intercepts non-ndarray/PIL inputs before the wrapped body runs, so the inner raises were unreachable; fixing at the decorator level fixes all annotators at once
- Remove 5 dead isinstance guards from key_points/annotators.py and 4 from utils/image.py (all now covered by the decorator fix)
- Remove dead assert isinstance(scene, Image.Image) from RichLabelAnnotator.annotate (ensure_pil_image_for_class_method guarantees PIL.Image before inner body)
- Add @ensure_cv2_image_for_class_method to VertexLabelAnnotator.annotate for PIL parity (only decorated annotator missing it)
- Rewrite tests to call public API directly (no __wrapped__ bypass); replace with TestAnnotatorInputValidation class (parametrized, IDs, AAA) + parametrized test_image_utils_wrong_type_raises
- Add Raises: TypeError sections to all 6 annotator .annotate() docstrings, 4 image util docstrings, and 3 decorator docstrings

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: jirka <6035284+Borda@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2026-06-26 17:51:01 +02:00
Andrew Barnes b82d6f95be
fix: normalize file extension filters (#2298)
* fix: normalize file extension filters
* fix(file): add is_file and empty-ext guards
* test(file): collapse extension tests into parametrized form
* fix: match multi-part extension suffix tails

---------

Co-authored-by: jirka <6035284+Borda@users.noreply.github.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Codex <codex@openai.com>
2026-06-08 13:41:25 +02:00
Andrew Barnes b18a30ea1a
fix: support grayscale letterbox images (#2297)
- Delete letterbox_image alpha block (lines 266-270): block wrote to
  caller's input array, not image_with_borders; used wrong coordinate
  system (resized vs original dims); redundant since cv2.copyMakeBorder
  already sets alpha=0 in padded regions when given a 3-element value
- Add test_letterbox_image_for_rgba_opencv_image: asserts padded alpha=0,
  interior alpha preserved, and input array not mutated after call
- Update letterbox_image docstring: image param lists (H,W,3)/(H,W,4)/
  (H,W)/PIL shapes; add Note on BGRA alpha behavior; add grayscale doctest

---------

Co-authored-by: jirka <6035284+Borda@users.noreply.github.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-06-07 11:06:21 -06:00
Piotr Skalski 1e8a48559b
Revert "feat: store keypoints on detections (#2290)" (#2291)
This reverts commit e03111e67c.
2026-06-04 11:43:48 -06:00
Jirka Borovec e03111e67c
feat: store keypoints on detections (#2290)
* docs: add API design principles to contribution guidelines
* feat: store keypoints on detections
* test: add "keypoints" to internal test cases
* docs: document keypoints field semantics and add docstring + dtype guard
* refactor: deduplicate keypoints shape check and add K-mismatch guard
* docs: clarify Detections.keypoints vs sv.KeyPoints decision rule and add KeyPoints filter example
* feat(key_points): add KeyPoints.from_detections() cross-container adapter
* test: extend keypoints test coverage — dtype guard, __eq__, dynamic field sets
* test: expand keypoints test coverage (M7/M8)
* fix: correct validate_xy expected_shape and dimensionality message
* fix: add ndim guard in KeyPoints.from_detections
* test: add unit tests for KeyPoints.from_detections adapter
* refactor: fix class_id cast and import formatting in key_points

---------

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-06-04 09:09:37 -06:00
SATISH K C 7e28315595
fix: preserve audio stream in process_video (#2252)
- Move `tempfile.mkstemp` + `os.close` inside `try` block so OSError (disk full,
  unwritable dir) is caught by the existing `except Exception` handler instead of
  propagating to the caller, preserving the warn-and-degrade contract
- Pass `dir=os.path.dirname(os.path.abspath(video_path))` so the temp file is on
  the same filesystem as the output, restoring `os.rename` semantics in `shutil.move`
- Initialise `tmp_path = None` before `try`; guard `finally` with
  `tmp_path is not None` to satisfy mypy and avoid referencing an unbound name
- Add `-loglevel error -nostats` so ffmpeg only writes actual errors to stderr
  (eliminates progress/stats spam that would buffer in PIPE indefinitely)
- Decode `result.stderr` and include it in the warning when ffmpeg exits
  non-zero, so failure messages surface diagnostically instead of being discarded
- Change bare `process_video(...)` call to `sv.process_video(...)` so the
  example matches the public API pattern and does not raise NameError for users
  copying the snippet
- Remove unused `import cv2` which was never referenced in the example body
- Clarify that missing/failing ffmpeg warns and continues rather than raising
- Add install hint for ffmpeg (apt/brew)
- Note that audio is truncated to match the processed video duration (-shortest)
- test_mux_audio_moves_file_on_success: mock subprocess.run returncode=0;
  assert shutil.move is called once with video_path as destination — catches
  any regression that drops the move call after a successful ffmpeg run
- test_mux_audio_swallows_subprocess_exception: mock subprocess.run raising
  OSError; assert no exception escapes _mux_audio and original file is intact
- Fix failed_result.stderr = b"" in test_mux_audio_warns_on_ffmpeg_failure
  to match the updated _mux_audio which now decodes result.stderr
- Skip _mux_audio when writer_worker.is_alive() after join timeout to avoid
  muxing an incomplete output file
- Fix test_mux_audio_moves_file_on_success: patch os.replace (not shutil.move)
  to match implementation changed in 2027938d
- Move four test_mux_audio_* free functions into TestMuxAudio class
- Strip mux_audio_ prefix from method names; class carries the unit
- Condense multi-line docstrings to single-line per testing rules
- Collapse test_warns_when_ffmpeg_missing, test_warns_on_ffmpeg_failure,
  test_swallows_subprocess_exception into one parametrized
  test_file_unchanged_on_failure[ffmpeg_missing|ffmpeg_fails|subprocess_raises]
- Promote two class methods back to module-level functions
- Collapse nested with-patch statements into single with a, b: form

---------

Co-authored-by: jirka <6035284+Borda@users.noreply.github.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-19 14:09:45 +02:00
abritton2002 51dd062407
fix: VideoInfo.fps returns float instead of truncated int (#2210)
* VideoInfo.fps returns float instead of int

Truncating the raw CAP_PROP_FPS value with int() causes timing drift for
non-integer frame rates (23.976, 29.97, 59.94). Over a long video this
accumulates into noticeable sync errors — e.g. 23 vs 23.976 drifts ~1s
per minute of footage.

Changes:
- VideoInfo.fps type annotation: int -> float
- from_video_path: int(video.get(CAP_PROP_FPS)) -> float(...)
- ByteTrack.frame_rate type annotation: int -> float (already converts
  to int internally via max_time_lost = int(frame_rate / 30.0 * buffer))
- Tests: assert fps is float, add float_fps_video_path fixture at 23.976

* fix: update examples to cast float fps to int where required
* fix: wrap long docstring line in FPSBasedTimer (ruff E501)
* test: remove unused float_fps_video_path fixture

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Jirka Borovec <6035284+Borda@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-13 20:05:19 +02:00
Copilot c2dedba3db
feat: replace print statements with structured logging (#2154)
* feat: implement logging system to replace print statements
* refactor: rename get_logger to _get_logger to mark as internal
* test: add logger tests, fix corrupted-file warning test, add docstring example

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Borda <6035284+Borda@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-02-20 13:14:53 +01:00
kymillev ae2c356321
Fix mask_annotate for int dtypes (#1445)
* Fix mask_annotate for int dtypes
* Add depreciation warning
* Add dtype=bool to test masks
* Remove ValueError (testing)
* Ensure boolean masks are consistently used in `Detections` and update validations, tests, and warnings for stricter type handling.
* Apply suggestions from code review

---------

Co-authored-by: Jirka Borovec <6035284+Borda@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-02-20 10:43:56 +01:00
Jirka Borovec 8e4fedf3f1
Add `match` arguments to all `pytest.raises` for more precise error validation in tests (#2145) 2026-02-10 21:22:55 +09:00
Jirka Borovec 2d0404e67c
Extend docstrings to test cases for enhanced readability (#2130)
* Extend docstrings to test cases for enhanced readability and consistency across testing modules
* Extend test case docstrings across multiple modules for improved clarity and consistency
* Refactor test case docstrings for clarity and consistency in `test_annotators.py`

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-02-03 17:28:33 +01:00
Jirka Borovec b8a2a6996c
refactor: reorganize project structure and update module imports (#2112)
* refactor: reorganize project structure and update module imports
* configure setuptools to use 'src' layout and adjust package discovery
* refactor: move tests to `tests` directory and update imports accordingly
* update lint and mypy configurations to align with `src` and `tests` structure
* update test imports to use corrected `tests.helpers` module path
* fix(pre_commit): 🎨 auto format pre-commit hooks
2026-02-03 15:24:47 +01:00