Skip to content

feat(utils): add TkImageWindow and switch to opencv-python-headless#2320

Open
Borda wants to merge 27 commits into
developfrom
drop/cv2-gui
Open

feat(utils): add TkImageWindow and switch to opencv-python-headless#2320
Borda wants to merge 27 commits into
developfrom
drop/cv2-gui

Conversation

@Borda

@Borda Borda commented Jun 14, 2026

Copy link
Copy Markdown
Member
  • 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>
Copilot AI review requested due to automatic review settings June 14, 2026 23:42
@Borda Borda requested a review from SkalskiP as a code owner June 14, 2026 23:42
@socket-security

socket-security Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedopencv-python-headless@​4.13.0.929910010010070

View full report

@codecov

codecov Bot commented Jun 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.39716% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 83%. Comparing base (934da12) to head (589b2af).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop   #2320    +/-   ##
========================================
  Coverage       83%     83%            
========================================
  Files           69      70     +1     
  Lines         9616    9757   +141     
========================================
+ Hits          7941    8060   +119     
- Misses        1675    1697    +22     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 to opencv-python-headless.
  • Migrated runnable examples away from cv2.imshow/cv2.waitKey to sv.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.

Comment thread src/supervision/utils/image_window.py
Comment thread src/supervision/utils/image_window.py
Comment thread src/supervision/utils/image_window.py Outdated
Comment thread docs/changelog.md Outdated
Comment thread src/supervision/utils/video.py
Comment thread docs/faq.md Outdated
Borda and others added 15 commits June 15, 2026 02:17
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>
…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>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 27 changed files in this pull request and generated 5 comments.

Comment thread src/supervision/utils/image_window.py Outdated
Comment thread src/supervision/utils/image_window.py
Comment thread examples/time_in_zone/ultralytics_stream_example.py Outdated
Comment thread examples/time_in_zone/rfdetr_stream_example.py Outdated
Comment thread examples/time_in_zone/inference_stream_example.py Outdated
- 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>
Borda and others added 3 commits June 16, 2026 14:23
… race

- [resolve #4] fix docstring: show() raises ModuleNotFoundError (not
  AttributeError) when python3-tk absent; add apt-get/brew install hint
- [resolve #5] clear _key_queue in _reset_window_refs() so stale
  keypresses from a previous session don't fire after close()+show()
- [resolve #6] wait_key() returns None immediately when _root is None
  and queue is empty — matches cv2.waitKey() contract (no ghost window)
- [resolve #9] re-check _key_queue before wait_variable in
  _wait_for_key_or_close() to close the _on_key arrival race window

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- [resolve #3] add Added: sv.TkImageWindow entry with key differences
  from cv2 (wait_key return type, mouse signature, left-button-only)
- [resolve #2] replace 'resolver conflict' with accurate 'silent
  co-install with filesystem collision' — pip installs both wheels
  silently; whichever ran last wins; advise explicit uninstall
- [resolve #8] add python3-tk system package install instructions
  (apt-get / brew) in TkImageWindow entry

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- [resolve #7] add test_wait_key_returns_none_when_no_window — tests
  fix #6 (early return when no window and queue empty)
- add test_wait_key_returns_none_when_closed_mid_wait — tests interaction
  of fix #5 + fix #9 (close mid-wait clears queue, wait_key returns None)
- add test_close_clears_key_queue — tests fix #5 (_key_queue cleared
  on close)
- collapse two structurally-identical show() error-path tests into one
  parametrized test (PT006-compliant tuple first arg)

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
@SkalskiP

Copy link
Copy Markdown
Collaborator

@Borda I took a quick look at the code. I agree, and I like this direction. Let me know when it'll be ready.

Borda and others added 2 commits June 26, 2026 13:27
- Add _on_configure binding that rescales stored image to new window dims
- Add _update_display() to separate frame rendering from show() plumbing
- Add _fit_image() helper: aspect-ratio fit or free-form stretch
- Expose keep_aspect_ratio param on TkImageWindow (default True)
- Add 16 tests: TestFitImage, TestTkImageWindowUpdateDisplay, TestTkImageWindowOnConfigure

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
@Borda Borda added the enhancement New feature or request label Jun 26, 2026
@Borda

Borda commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

This is expected as it refers to the contribution of this PR

* [404] <https://supervision.roboflow.com/latest/utils/image_window/> | Rejected status code (this depends on your "accept" configuration): Not Found

# 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants