Beautiful, fully configurable bounding boxes for object detection.
Boxes, labels, fills, glow, glass, film grain and tracking trails, described by one Style,
tuned live in the Studio, saved as YAML and rendered with one call.
Website · Quickstart · Styles · Studio · Python API · CLI · Changelog
YOLO26n detections on a rainy Manhattan crossing, rendered with style="cinematic".
Quickstart
pip install "visionstyle[yolo]"
import cv2
import visionstyle as vs
from ultralytics import YOLO
frame = cv2.imread("street.jpg")
result = YOLO("yolo26n.pt")(frame)[0]
dets = vs.Detections.from_ultralytics(result)
cv2.imwrite("out.jpg", vs.annotate(frame, dets, style="cinematic"))
No model handy? visionstyle render sample -s neon -o out.jpg renders a bundled photo with its
shipped detections.
visionstyle only draws, so any detector works: pass boxes as lists, NumPy arrays or torch tensors (see Python API).
Install options
| Command | Adds |
|---|---|
pip install visionstyle |
The renderer: NumPy, OpenCV (headless), Pillow, pydantic, PyYAML |
pip install "visionstyle[yolo]" |
Ultralytics YOLO for from_ultralytics, the CLI --model flag and video tracking |
pip install "visionstyle[studio]" |
FastAPI + Uvicorn for the Studio web app |
pip install "visionstyle[all]" |
Everything above |
Python 3.10 – 3.13.
Why visionstyle
- One object describes the look. A
Stylecovers boxes, strokes, fills, line patterns, labels, effects and trails. Every option is a typed, validated attribute, and the whole thing round-trips to YAML. - Twenty-two presets to start from. Use one by name, or copy its YAML and make it yours.
- Looks the same at any resolution. Sizes are in reference pixels and scale with the frame, and each object's outline and label scale with its size, so distant objects stay light.
- Built for video. Keep one
Annotatorper stream and you get tracking trails, marching dashes, hue cycling and pulses that carry across frames. - A Studio to design in. Tune a style visually on your own image or video. The Python package
renders every preview, so what you see is exactly what
annotate()produces.
What it can draw
Nine looks, each built to show off a different part of Style: box shapes, line patterns, fills,
label treatments, effects and trails. Every tile is rendered by the package itself, and the style
behind each one is in docs/showcase.py
if you want to borrow it.
Presets
Twenty-two ready-made presets to start from. Each one is a plain YAML file you can copy and edit (see all of them on the same scene).
Twelve general-purpose looks:
| Preset | Look | Preset | Look |
|---|---|---|---|
default |
Clean rectangle, solid tag | neon |
Glowing hue-cycling gradient outlines |
minimal |
Hairline outline, bare text | hud |
Double frame, reticle marks, inside mono labels, dotted trails |
corners |
Thin L-brackets, monospace tag | cinematic |
Rounded amber/teal frames, gradient fill, pill labels, glow, filmic grade |
rounded |
Rounded corners, gradient tint, pill label | tracking |
Bold per-track trails, id-first labels |
dashed |
Dashed perimeter with marching ants | spotlight |
Dims everything outside the detections |
glass |
Frosted-glass fill and label | confidence |
Stroke and tag color follow the confidence score |
Ten built for a job, a field or an audience:
| Preset | For | Look |
|---|---|---|
target |
Following one subject | Red lock-on brackets that pulse, center cross, track trail, dimmed frame |
redact |
Anonymizing people, faces, plates | Blurs the inside of each box and marks it REDACTED |
review |
Auditing labels and model output | Class id, name and score on every box, uniform sizes, tinted fill |
safety |
PPE, intrusion and restricted-zone alerts | Amber/black hazard-tape outline, hatch fill, loud tag |
broadcast |
Sports and TV graphics | No boxes: a glowing path at each player's feet and a number bubble |
cctv |
Security and surveillance | Grayscale grainy frame, phosphor-green boxes and ID tags |
traffic |
Traffic and smart-city monitoring | Vehicles amber, people cyan, two-wheelers lime |
documentary |
Wildlife, nature, editorial video | Thin cream brackets, underlined names, warm film grade |
blueprint |
Robotics and technical write-ups | Cool desaturated frame, fine dashed boxes, center marks, [class id] tags |
high-contrast |
Projectors, outdoor screens, low vision | Thick yellow outlines with a dark halo, large black-on-yellow tags |
target draws every detection it's given, so pass only the one you're following
(for example dets.subset(dets.track_id == 3)), or hide the others in the Studio's Objects tab.
visionstyle presets list
visionstyle presets show cinematic
visionstyle presets export cinematic -o my-look.yaml
Studio
Design a style visually on your own image, video and model, then save it as a preset the package loads by name.
pip install "visionstyle[studio,yolo]"
visionstyle studio # opens http://127.0.0.1:8420
- Source. Pick a bundled scene or upload an image or video, set the confidence threshold and
run detection. Videos are tracked once, up to a minute long, and the detections are saved with
the video, so restyling, scrubbing and moving the confidence slider replay them without
running the model again. Longer uploads can be trimmed to their first minute. The sample video
ships with its tracks; tracking a new video needs the
yoloextra. - Design. Start from a preset, then adjust box, stroke, fill, line, label, effects, tracking and global settings, or search for any setting by name. Save next to the Style menu stores the look as a named preset.
- Objects. Filter by class, hide objects or isolate a single track. Scrub or play the timeline, and save the annotated frame from the preview menu.
- Export. Copy or download the YAML, a self-contained Python snippet, or a prompt for AI coding tools.
Saved presets go to ~/.visionstyle/presets and load by name anywhere on that machine with
vs.Style.preset("my-look"). To keep them in a project instead, run
visionstyle studio --presets-dir ./styles, commit styles/<name>.yaml, and load it with
vs.Style.load("styles/my-look.yaml") (or set VISIONSTYLE_PRESETS_DIR=./styles to load by name).
Keyboard shortcuts
| Key | Action |
|---|---|
| Space | Play / pause |
| ← / → | Previous / next frame |
| Shift + ← / → | Jump ten frames |
| R | Reset the style |
Focused controls keep their own keyboard behavior.
Python API
import cv2
import visionstyle as vs
frame = cv2.imread("street.jpg")
# 1. Detections: boxes in pixels, everything else optional
dets = vs.Detections(
xyxy=[[590, 650, 720, 1040], [1060, 660, 1520, 1000]],
class_name=["person", "car"],
confidence=[0.93, 0.88],
track_id=[14, 31],
)
# or: dets = vs.Detections.from_ultralytics(model.predict(frame)[0])
# 2. A style: preset name, YAML path, or built in code
style = vs.Style.preset("cinematic")
style.label.components = ["track_id", "text"] # every option is a typed attribute
style.trail.enabled = True
# 3. Render. Keep one Annotator per video stream so trails and animations carry across frames.
annotator = vs.Annotator(style)
out = annotator.annotate(frame, dets)
# One-liner for stills
out = vs.annotate(frame, dets, style="minimal")
xyxy is pixel x1, y1, x2, y2; Detections.from_xywh, from_xywh_topleft and from_dicts
cover other formats. Frames are BGR uint8 like OpenCV; pass rgb=True for RGB arrays.
What you can configure
| Section | Options |
|---|---|
| Box | rectangle, rounded (radius), corners (bracket length, curved elbows), reticle, none; double line; center mark |
| Stroke | Thickness, opacity, color (palette per class/track, confidence ramp, or any hex/rgb/name) |
| Fill | On/off, opacity, color, solid / gradient (5 directions) / hatch |
| Line | solid / dashed / dotted, dash and gap sizes, multi-color segments or perimeter gradient, animation march / hue_cycle / pulse |
| Label | Ordered components (text, confidence, track_id, class_id, custom template), 9 anchors × inside/outside, vertical tags on the sides, fonts (Inter, JetBrains Mono or your .ttf), size, weight, uppercase, solid / pill / glass / underline / none backgrounds, border, padding, formats |
| Effects | Glow, shadow, frosted glass, spotlight dimming, vignette, film grain, color grade |
| Tracking | Trail length, anchor (feet/center/top), solid / dotted / dashed / ribbon, fade and taper, smoothing, glow, points |
| Global | Palette (built-in or custom list), per-class color overrides, confidence threshold, resolution scaling, per-object scaling, fps |
How sizes scale
All sizes are in reference pixels at about 1080p and scale automatically with the frame
(style.scale = "auto"), so one style looks the same on a webcam and a 4K photo.
On top of that, strokes and label tags scale with each detected object's size
(style.object_scale, on by default): far-away objects get thin outlines and small tags, close-up
ones get heavier outlines and larger text. Tune strength and min_factor / max_factor,
restrict it with apply_to: box | label, or set enabled: false for constant sizes.
Save and share styles
style.save("my_style.yaml") # anywhere
style.save_preset("my-look") # ~/.visionstyle/presets/my-look.yaml
vs.Style.preset("my-look") # found by name from now on
vs.presets.list() # built-in + yours
Presets are looked up in $VISIONSTYLE_PRESETS_DIR, then ~/.visionstyle/presets, then the
built-ins, then as a file path.
CLI
visionstyle render photo.jpg -s neon --model yolo26n.pt -o out.jpg
visionstyle render clip.mp4 -s tracking --model yolo26n.pt --track -o out.mp4
visionstyle render 0 -s hud --model yolo26n.pt --track -o webcam.mp4 # webcam index
visionstyle render photo.jpg -d detections.json -s corners # no model needed
visionstyle gallery photo.jpg --model yolo26n.pt -o gallery.png # every preset at once
visionstyle presets list | show NAME | export NAME -o my.yaml
visionstyle schema -o style.schema.json
detections.json is a list of {"xyxy": [...], "class_name": "...", "confidence": 0.9, "track_id": 1}.
Development
uv sync --all-extras --group dev
uv run pytest && uv run ruff check . && uv run mypy src
cd studio && npm install
npm run dev # Vite dev server on :5173, proxying /api to :8420
npm run build # -> src/visionstyle/studio/static (bundled into the wheel)
When src/visionstyle/style/schema.py changes, regenerate the frontend schema and types:
uv run visionstyle schema -o studio/schema.json && (cd studio && npm run gen:types)
Releasing and the website
Releases. Bump __version__ in src/visionstyle/__init__.py, add a section to
CHANGELOG.md, then tag vX.Y.Z and push the tag. release.yml builds the Studio and the wheel,
checks them, publishes to PyPI through trusted publishing and creates a GitHub release.
Website. The project site deploys from site/
through GitHub Actions on every push to main; see
site/README.md.
Credits
Sample photos: see CREDITS.md.
Fonts: Inter and JetBrains Mono,
both under the SIL Open Font License.
Author
Built by Basel Mather. See more of my work at baselhusam.com.
Released under the MIT License.
Metadata
Release files for visionstyle 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| visionstyle-0.1.1.tar.gz | 4.3 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| visionstyle-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 8.6 MB
Release files / visionstyle-0.1.1.tar.gz
| Download URL | visionstyle-0.1.1.tar.gz |
|---|---|
| Size | 4.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4c0f4a957436700ec56d9460e46f2a16fa29a60c72259ccec91dd263cce33ad2
|
|
BLAKE2b-256 checksum How to use checksums |
0631073236242a253407320d8b34c9cfbedb019ea08546747c8ee607234b0698
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency logRelease files / visionstyle-0.1.1-py3-none-any.whl
| Download URL | visionstyle-0.1.1-py3-none-any.whl |
|---|---|
| Size | 4.3 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a7127604a2ced57323bbfa04c7c8c22e66f447df08c0192948348fa407a97eb8
|
|
BLAKE2b-256 checksum How to use checksums |
7de927a8d8db891e34e071106012523ce583994846e10436f03ff84cb044348c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency log