feat(utils): add TkImageWindow and switch to opencv-python-headless#2320
feat(utils): add TkImageWindow and switch to opencv-python-headless#2320Borda wants to merge 41 commits into
TkImageWindow and switch to opencv-python-headless#2320Conversation
Borda
commented
Jun 14, 2026
- Add sv.TkImageWindow: tkinter+pillow desktop window replacing cv2.imshow/waitKey/destroyAllWindows under headless OpenCV; supports BGR/grayscale/BGRA frames, key polling, mouse callbacks, context manager
- Switch pyproject.toml from opencv-python to opencv-python-headless (breaking: cv2.imshow/waitKey/namedWindow no longer provided transitively; restore with pip install opencv-python)
- Migrate all 17 runnable examples from cv2.imshow/waitKey/destroyAllWindows to sv.TkImageWindow
- Add docstring with webcam ownership pattern to VideoInfo.from_video_path and get_video_frames_generator
- Add breaking-change CHANGELOG entry and two FAQ entries covering headless and webcam usage
- Add 18 tests for TkImageWindow covering show(), wait_key(), close(), context manager, mouse callbacks, _bgr_to_pil
- Add sv.TkImageWindow: tkinter+pillow desktop window replacing cv2.imshow/waitKey/destroyAllWindows under headless OpenCV; supports BGR/grayscale/BGRA frames, key polling, mouse callbacks, context manager - Switch pyproject.toml from opencv-python to opencv-python-headless (breaking: cv2.imshow/waitKey/namedWindow no longer provided transitively; restore with pip install opencv-python) - Migrate all 17 runnable examples from cv2.imshow/waitKey/destroyAllWindows to sv.TkImageWindow - Add docstring with webcam ownership pattern to VideoInfo.from_video_path and get_video_frames_generator - Add breaking-change CHANGELOG entry and two FAQ entries covering headless and webcam usage - Add 18 tests for TkImageWindow covering show(), wait_key(), close(), context manager, mouse callbacks, _bgr_to_pil --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #2320 +/- ##
========================================
Coverage 87% 87%
========================================
Files 71 72 +1
Lines 10485 10664 +179
========================================
+ Hits 9098 9254 +156
- Misses 1387 1410 +23 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR introduces a tkinter+Pillow-based image display utility (sv.TkImageWindow) so runnable examples (and user code) can display frames without relying on OpenCV HighGUI, and switches the core dependency from opencv-python to opencv-python-headless to avoid pulling GUI libraries transitively.
Changes:
- Added
sv.TkImageWindow(plus_bgr_to_pil) and exported it from the top-level package. - Switched
pyproject.toml(and lockfile) dependency toopencv-python-headless. - Migrated runnable examples away from
cv2.imshow/cv2.waitKeytosv.TkImageWindow, added tests, and documented the breaking change in docs.
Reviewed changes
Copilot reviewed 24 out of 25 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Updates locked dependencies reflecting the move to opencv-python-headless. |
| pyproject.toml | Switches runtime dependency from opencv-python to opencv-python-headless. |
| src/supervision/utils/image_window.py | Adds TkImageWindow implementation and BGR/BGRA/grayscale conversion helper. |
| src/supervision/init.py | Exports TkImageWindow at the package top level. |
| src/supervision/utils/video.py | Adds/updates VideoInfo.from_video_path docstring content. |
| tests/utils/test_image_window.py | Adds unit tests for TkImageWindow and _bgr_to_pil. |
| docs/faq.md | Adds FAQ entries covering headless OpenCV GUI removal and webcam guidance. |
| docs/changelog.md | Adds breaking-change entry describing the OpenCV wheel switch and mitigation. |
| examples/traffic_analysis/ultralytics_example.py | Replaces cv2.imshow/waitKey usage with TkImageWindow. |
| examples/traffic_analysis/inference_example.py | Replaces cv2.imshow/waitKey usage with TkImageWindow. |
| examples/time_in_zone/ultralytics_stream_example.py | Uses TkImageWindow instead of OpenCV HighGUI in streaming callback. |
| examples/time_in_zone/ultralytics_naive_stream_example.py | Uses TkImageWindow instead of OpenCV HighGUI. |
| examples/time_in_zone/ultralytics_file_example.py | Uses TkImageWindow instead of OpenCV HighGUI. |
| examples/time_in_zone/scripts/draw_zones.py | Migrates interactive zone drawing from OpenCV HighGUI to TkImageWindow mouse+key handling. |
| examples/time_in_zone/rfdetr_stream_example.py | Uses TkImageWindow instead of OpenCV HighGUI in streaming callback. |
| examples/time_in_zone/rfdetr_naive_stream_example.py | Uses TkImageWindow instead of OpenCV HighGUI. |
| examples/time_in_zone/rfdetr_file_example.py | Uses TkImageWindow instead of OpenCV HighGUI. |
| examples/time_in_zone/inference_stream_example.py | Uses TkImageWindow instead of OpenCV HighGUI in streaming callback. |
| examples/time_in_zone/inference_naive_stream_example.py | Uses TkImageWindow instead of OpenCV HighGUI. |
| examples/time_in_zone/inference_file_example.py | Uses TkImageWindow instead of OpenCV HighGUI. |
| examples/speed_estimation/yolo_nas_example.py | Uses TkImageWindow instead of OpenCV HighGUI. |
| examples/speed_estimation/ultralytics_example.py | Uses TkImageWindow instead of OpenCV HighGUI. |
| examples/speed_estimation/inference_example.py | Uses TkImageWindow instead of OpenCV HighGUI. |
| examples/count_people_in_zone/ultralytics_example.py | Uses TkImageWindow instead of OpenCV HighGUI. |
| examples/count_people_in_zone/inference_example.py | Uses TkImageWindow instead of OpenCV HighGUI. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@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>
…into drop/cv2-gui
…into drop/cv2-gui
…failure
patch("PIL.ImageTk.PhotoImage") resolved via pkgutil.resolve_name on
Python 3.12+, which calls getattr(PIL, "ImageTk") — fails on headless
runners without python3-tk. Replace with patch.dict("sys.modules",
{"PIL.ImageTk": fake_imagetk}) which injects the mock before the
from PIL import ImageTk call inside show(), avoiding the import
entirely.
---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
InferencePipeline calls on_prediction() from a worker thread; tkinter requires all Tk calls from the thread that created Tk() root — macOS enforces this strictly (crash), Linux is undefined behavior. Revert inference_stream_example, rfdetr_stream_example, and ultralytics_stream_example to cv2.imshow/waitKey (requires opencv-python, not headless). --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…ption docs - Replace 'Drop-in replacement' with 'Functional replacement' — wait_key returns str not int, mouse callback signature differs from cv2, only left-button events captured - Add 'Differences from cv2' section documenting all three breaking behavioural differences for migrating callers - Correct documented failure mode for headless environments: raises AttributeError (PIL.ImageTk missing) not TclError when python3-tk absent; distinguish that from display-unavailable case (TclError) --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
PR description promised a webcam/VideoCapture note on this function but the docstring was left without guidance. Add a Note section showing the cv2.VideoCapture pattern with explicit release in a finally block for callers wanting live camera access. --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
TkImageWindow is public API (exported in __all__) but had no docs page. Add docs/utils/image_window.md and wire it into mkdocs.yml under Utils. --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…into drop/cv2-gui
- Replace bare `list[np.ndarray]` with `list[npt.NDArray[np.floating]]` in _merge_obb_corners signature — fixes mypy missing-type-args error introduced by #2312 OBB NMM merge --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
# Conflicts: # examples/time_in_zone/inference_stream_example.py # examples/time_in_zone/rfdetr_stream_example.py # examples/time_in_zone/ultralytics_stream_example.py
|
General question: what happens when someone installs |
…xports [resolve group] PR #2320 — item 2 --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: OpenAI Codex <codex@openai.com>
…llow; drop banner comments Bug: _on_mouse forwarded raw label-widget event.x/event.y unscaled while _fit_image scales+letterboxes the displayed frame, so mouse coordinates drifted from image-pixel space after any window resize (user-confirmed). Fix stores display scale/offset at render time and inverts the transform in _on_mouse, clamped to the original image bounds. Also extends sv.cv2_to_pillow to support grayscale and BGRA inputs (it previously only handled 3-channel BGR) and makes _bgr_to_pil delegate to it instead of duplicating the conversion logic. Drops four decorative section-separator comment blocks per reviewer request. [resolve group] PR #2320 — items 1, 2, 5, 8 --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: OpenAI Codex <codex@openai.com>
…ing admonition Updates the TkImageWindow -> ImageWindow rename mention. Deduplicates the repeated pip uninstall/install snippet under "How to restore GUI support" and "Co-installation warning" into a single cross-referenced block, and reframes the opencv-python-headless breaking-change note as a mkdocs !!! warning admonition instead of a regular bullet so it is not mistaken for a routine change. [resolve group] PR #2320 — items 2, 6, 7 --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: OpenAI Codex <codex@openai.com>
--- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…1 section The 0.29.1 tag is already released; filing this PR's changelog entries there falsely implied the feature shipped in a past release. Moved the ImageWindow "Added" bullet and the opencv-python-headless breaking-change warning into the Unreleased section instead. Also adds test coverage flagged by the QA gate: an end-to-end resize-then-click path (_on_configure -> _on_mouse) and mouse-coordinate mapping under keep_aspect_ratio=False (stretched mode), both previously untested. [resolve] PR #2320 — Step 9 QA gate fixes --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: OpenAI Codex <codex@openai.com>
- Add ImageWindow.is_open so display loops can stop cleanly when the Tk window is closed.\n\n- Update examples, docs, and FAQ guidance to use the new window-state check and clarify OpenCV wheel selection.\n\n- Tighten grayscale crop tests and cap opencv-python-headless below 5 for compatibility. --- Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>