img-show is a thin, opinionated wrapper around OpenCV for displaying images. It accepts NumPy arrays, PyTorch tensors, and PIL images; auto-coerces shapes, dtypes, and channel orders; resizes windows to fit the screen; alpha-composites RGBA over a checkerboard; and renders inline as PNG when running inside a Jupyter notebook.
img-show depends only on NumPy at install time. OpenCV is selected through one of four mutually-exclusive extras so you can match it to your environment (desktop, server, or contrib build):
pip install img-show[opencv] # standard build with GUI
pip install img-show[opencv-contrib] # standard + contrib modules
pip install img-show[opencv-headless] # no GUI (servers, CI, Jupyter-only)
pip install img-show[opencv-contrib-headless] # headless + contrib modulesPick exactly one. The extras are mutually exclusive at the resolver level (uv enforces this via a conflicts entry); installing two OpenCV variants side-by-side results in a broken cv2 import.
If you already manage cv2 yourself (for example with a system package or a custom wheel), install the bare package:
pip install img-showimg-show will detect the existing cv2 import at runtime.
- Python >= 3.10
- NumPy >= 1.24.4
- OpenCV >= 4.5.4.60 (provided by one of the extras above)
- Tkinter (used only to query screen size; if unavailable a 1920x1080 fallback is used)
- PIL / Pillow — enables passing
PIL.Image.Imageobjects directly; not required for NumPy or PyTorch inputs. - PyTorch — enables passing
torch.Tensorobjects directly. - IPython — required only when running inside a Jupyter notebook; used for inline PNG rendering.
import numpy as np
from img_show import show_img
img = np.random.rand(256, 256, 3)
show_img(img)At import time img-show probes the runtime environment and exposes two read-only flags:
from img_show import HAS_CV2, IS_HEADLESS
HAS_CV2 # bool — True when cv2 is importable
IS_HEADLESS # bool | None — True for headless cv2 builds,
# False for GUI builds,
# None when cv2 is missing or the provider
# cannot be identifiedHeadless detection uses importlib.metadata.packages_distributions() to look up which PyPI distribution provides the cv2 import name. The check is fast and runs once at import.
Use these flags to branch your own code if you want to avoid RuntimeError from the display functions:
from img_show import HAS_CV2, IS_HEADLESS, show_img
if HAS_CV2 and IS_HEADLESS is False:
show_img(my_image)
else:
# fall back to saving or logging the array
...Each public function has a different capability surface. The table below describes what must be available for each function to succeed:
| Function | cv2 required | GUI build required | Display required | Works in Jupyter |
|---|---|---|---|---|
coerce_img |
no | no | no | yes |
show_img |
yes | yes (outside Jupyter only) | yes (outside Jupyter only) | yes (renders inline as PNG) |
show_imgs |
yes | yes (outside Jupyter only) | yes (outside Jupyter only) | yes (renders inline as PNG) |
close_all |
yes | no | no | yes (no-op if no windows) |
When a requirement is unmet, the function raises RuntimeError at call time with a message that tells the user which extra to install. coerce_img is the only function that always works, because it is pure NumPy.
When img-show detects a ZMQInteractiveShell (the standard Jupyter kernel), show_img and show_imgs encode each image to PNG with cv2.imencode and emit it through IPython.display.Image, captioned by the window name. The GUI and display requirements are skipped on this path, so a headless OpenCV build inside Jupyter is fully supported.
# In a Jupyter cell
from img_show import show_img
show_img(my_image, window_name='Step 3 output')import numpy as np
import torch
from PIL import Image
from img_show import show_img
show_img(np.random.rand(256, 256, 3)) # NumPy array
show_img(torch.randn(3, 256, 256)) # PyTorch tensor (channels-first)
show_img(Image.open('photo.png')) # PIL imagePIL images use RGB channel order; img-show reorders them to BGR for correct OpenCV display. NumPy and PyTorch inputs are assumed to be BGR already (or single-channel) and pass through unchanged.
Singleton dimensions are squeezed automatically, and channels-first layouts are transposed to channels-last:
show_img(np.random.rand(1, 256, 256, 3)) # leading singleton stripped
show_img(np.random.rand(1, 1, 3, 256, 256)) # channels-first detected and fixed
show_img(np.random.rand(256, 256, 1)) # grayscale with trailing singletonThe table below describes the dtype that coerce_img returns. The display path (show_img / show_imgs) then converts every result to uint8 before handing it to OpenCV — uint16 is scaled by 1/257, floats in [0, 1] are scaled by 255, and anything else is clipped into [0, 255].
| Input dtype | coerce_img output |
|---|---|
uint8 |
passed through unchanged |
uint16 |
passed through unchanged (display path will rescale to uint8) |
bool |
scaled to 0/255 uint8 |
| other integer | scaled to the 0-1 range as float64 |
| float | scaled to 0-1 if out of range or near-constant |
Four-channel images (RGBA from PIL, or BGRA arrays) are composited over a gray checkerboard at display time. The original four-channel array is preserved by coerce_img; compositing happens only in the display pipeline.
If the image is taller than the screen (minus a 250-pixel toolbar margin) or wider than the screen, a resizable window is created and sized to fit while preserving aspect ratio. Otherwise an auto-sizing window is created.
from img_show import show_imgs
show_imgs(
[img1, img2, img3],
window_names=['Original', 'Filtered', 'Diff'],
)When window_names is omitted the names default to Image 1, Image 2, ... A collision with an already-tracked window appends (2), (3), ...
Pass destroy_window=False (or destroy_windows=False) to leave windows open after show_img/show_imgs returns. The names are tracked internally; call close_all to dismiss them later:
from img_show import show_img, close_all
show_img(img1, window_name='Step 1', destroy_window=False)
show_img(img2, window_name='Step 2', destroy_window=False)
# ... later
close_all()close_all silently ignores windows that have already been closed by other means (for example by clicking the X button).
Display a single image. Coerces and prepares the input, then either opens an OpenCV window or renders inline in Jupyter.
| Parameter | Type | Default | Description |
|---|---|---|---|
img |
Any | NumPy array, PyTorch tensor, or PIL image. | |
window_name |
str | ' ' |
Window title (also used as the caption in Jupyter). |
wait_delay |
int | 0 |
Milliseconds to wait for a keypress when do_wait=True. 0 waits indefinitely. |
do_wait |
bool | True |
When True, block on cv2.waitKey. When False, paint the window briefly and return. |
destroy_window |
bool | True |
When True, close the window after waiting. When False, leave it open and track it for close_all. |
Raises RuntimeError when OpenCV is missing, when a headless build is installed and the call is made outside Jupyter, or when no display environment is available outside Jupyter.
Display multiple images. Each image opens its own window (or renders inline in Jupyter).
| Parameter | Type | Default | Description |
|---|---|---|---|
imgs |
Iterable[Any] | Collection of images. | |
window_names |
Iterable[str] | None | None |
Names matching imgs. When None, auto-generated as Image 1, Image 2, ... |
wait_delay |
int | 0 |
Same as show_img. |
do_wait |
bool | True |
Same as show_img. |
destroy_windows |
bool | True |
Same as show_img's destroy_window. |
Raises ValueError when len(window_names) != len(imgs). Same RuntimeError cases as show_img.
Convert a NumPy array, PyTorch tensor, or PIL image into a displayable NumPy array. Returns a uint8, uint16, or normalized floating-point array depending on the input. Four-channel inputs remain four-channel; alpha compositing is applied only by the display path.
This function has no runtime requirements beyond NumPy and always works.
Close every window opened by show_img or show_imgs with destroy_window=False and clear the tracking set. Already-closed windows are ignored silently.
Raises RuntimeError when OpenCV is missing.
HAS_CV2: bool— True whencv2is importable.IS_HEADLESS: bool | None— True for a headless cv2 build, False for a GUI build, None when cv2 is missing or the provider cannot be identified.open_window_names: set[str]— Names of currently tracked windows. Modified byshow_img,show_imgs, andclose_all.
img-show is licensed under the MIT License.
Ben Elfner