visionstyle 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. visionstyle-0.1.0/.gitignore +32 -0
  2. visionstyle-0.1.0/CHANGELOG.md +9 -0
  3. visionstyle-0.1.0/LICENSE +21 -0
  4. visionstyle-0.1.0/PKG-INFO +251 -0
  5. visionstyle-0.1.0/README.md +206 -0
  6. visionstyle-0.1.0/pyproject.toml +103 -0
  7. visionstyle-0.1.0/src/visionstyle/__init__.py +50 -0
  8. visionstyle-0.1.0/src/visionstyle/assets/fonts/Inter-Variable.ttf +0 -0
  9. visionstyle-0.1.0/src/visionstyle/assets/fonts/JetBrainsMono-Medium.ttf +0 -0
  10. visionstyle-0.1.0/src/visionstyle/assets/fonts/JetBrainsMono-SemiBold.ttf +0 -0
  11. visionstyle-0.1.0/src/visionstyle/assets/fonts/OFL-Inter.txt +93 -0
  12. visionstyle-0.1.0/src/visionstyle/assets/fonts/OFL-JetBrainsMono.txt +93 -0
  13. visionstyle-0.1.0/src/visionstyle/assets/samples/CREDITS.md +10 -0
  14. visionstyle-0.1.0/src/visionstyle/assets/samples/city-walkthrough.detections.json +1 -0
  15. visionstyle-0.1.0/src/visionstyle/assets/samples/city-walkthrough.mp4 +0 -0
  16. visionstyle-0.1.0/src/visionstyle/assets/samples/night.detections.json +174 -0
  17. visionstyle-0.1.0/src/visionstyle/assets/samples/night.jpg +0 -0
  18. visionstyle-0.1.0/src/visionstyle/assets/samples/street.detections.json +114 -0
  19. visionstyle-0.1.0/src/visionstyle/assets/samples/street.jpg +0 -0
  20. visionstyle-0.1.0/src/visionstyle/cli.py +296 -0
  21. visionstyle-0.1.0/src/visionstyle/color.py +179 -0
  22. visionstyle-0.1.0/src/visionstyle/detections.py +284 -0
  23. visionstyle-0.1.0/src/visionstyle/integrations/__init__.py +1 -0
  24. visionstyle-0.1.0/src/visionstyle/integrations/ultralytics.py +75 -0
  25. visionstyle-0.1.0/src/visionstyle/presets/__init__.py +31 -0
  26. visionstyle-0.1.0/src/visionstyle/presets/cinematic.yaml +33 -0
  27. visionstyle-0.1.0/src/visionstyle/presets/confidence.yaml +14 -0
  28. visionstyle-0.1.0/src/visionstyle/presets/corners.yaml +19 -0
  29. visionstyle-0.1.0/src/visionstyle/presets/dashed.yaml +14 -0
  30. visionstyle-0.1.0/src/visionstyle/presets/default.yaml +16 -0
  31. visionstyle-0.1.0/src/visionstyle/presets/glass.yaml +22 -0
  32. visionstyle-0.1.0/src/visionstyle/presets/hud.yaml +32 -0
  33. visionstyle-0.1.0/src/visionstyle/presets/minimal.yaml +17 -0
  34. visionstyle-0.1.0/src/visionstyle/presets/neon.yaml +27 -0
  35. visionstyle-0.1.0/src/visionstyle/presets/rounded.yaml +17 -0
  36. visionstyle-0.1.0/src/visionstyle/presets/spotlight.yaml +20 -0
  37. visionstyle-0.1.0/src/visionstyle/presets/tracking.yaml +25 -0
  38. visionstyle-0.1.0/src/visionstyle/py.typed +0 -0
  39. visionstyle-0.1.0/src/visionstyle/render/__init__.py +6 -0
  40. visionstyle-0.1.0/src/visionstyle/render/annotator.py +767 -0
  41. visionstyle-0.1.0/src/visionstyle/render/canvas.py +197 -0
  42. visionstyle-0.1.0/src/visionstyle/render/effects.py +106 -0
  43. visionstyle-0.1.0/src/visionstyle/render/labels.py +136 -0
  44. visionstyle-0.1.0/src/visionstyle/render/shapes.py +217 -0
  45. visionstyle-0.1.0/src/visionstyle/render/text.py +68 -0
  46. visionstyle-0.1.0/src/visionstyle/render/trails.py +146 -0
  47. visionstyle-0.1.0/src/visionstyle/studio/__init__.py +1 -0
  48. visionstyle-0.1.0/src/visionstyle/studio/server.py +799 -0
  49. visionstyle-0.1.0/src/visionstyle/studio/static/assets/Inter-Variable-VF2RPR_K.ttf +0 -0
  50. visionstyle-0.1.0/src/visionstyle/studio/static/assets/JetBrainsMono-Medium-Bpf1P7_Y.ttf +0 -0
  51. visionstyle-0.1.0/src/visionstyle/studio/static/assets/JetBrainsMono-SemiBold-B3giN8Lc.ttf +0 -0
  52. visionstyle-0.1.0/src/visionstyle/studio/static/assets/chroma-press-light-symbol-transparent-96-CaRLT1ZH.png +0 -0
  53. visionstyle-0.1.0/src/visionstyle/studio/static/assets/chroma-press-watermark-white-64-BWEwGAwx.png +0 -0
  54. visionstyle-0.1.0/src/visionstyle/studio/static/assets/index-DGrWe6F3.js +60 -0
  55. visionstyle-0.1.0/src/visionstyle/studio/static/assets/index-ddVzsbCF.css +1 -0
  56. visionstyle-0.1.0/src/visionstyle/studio/static/index.html +16 -0
  57. visionstyle-0.1.0/src/visionstyle/style/__init__.py +57 -0
  58. visionstyle-0.1.0/src/visionstyle/style/migrate.py +20 -0
  59. visionstyle-0.1.0/src/visionstyle/style/presets.py +126 -0
  60. visionstyle-0.1.0/src/visionstyle/style/schema.py +464 -0
@@ -0,0 +1,32 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .venv/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+ .pytest_cache/
11
+ .coverage
12
+ htmlcov/
13
+
14
+ # Frontend
15
+ studio/node_modules/
16
+ studio/dist/
17
+ src/visionstyle/studio/static/*
18
+
19
+ # Models / outputs
20
+ *.pt
21
+ *.onnx
22
+ *.engine
23
+ runs/
24
+ out/
25
+ *.mp4
26
+ !src/visionstyle/assets/samples/*.mp4
27
+
28
+ # OS / editors
29
+ .DS_Store
30
+ .idea/
31
+ .vscode/
32
+ _site/
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-09-24
4
+
5
+ - Initial release: configurable box / stroke / fill / line / label / effects / trail styles,
6
+ built-in presets, Ultralytics integration, CLI and the Studio app.
7
+ - Per-object scaling (`style.object_scale`, on by default): stroke weight, corner geometry and
8
+ label size now follow each detected box's size relative to the frame, clamped to
9
+ `min_factor`..`max_factor`; `apply_to` limits it to the box or the label.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Basel Mather
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,251 @@
1
+ Metadata-Version: 2.5
2
+ Name: visionstyle
3
+ Version: 0.1.0
4
+ Summary: Beautiful, fully configurable bounding boxes, labels and tracking trails for object detection.
5
+ Project-URL: Homepage, https://github.com/baselhusam/visionstyle
6
+ Project-URL: Repository, https://github.com/baselhusam/visionstyle
7
+ Project-URL: Documentation, https://baselhusam.github.io/visionstyle/
8
+ Project-URL: Issues, https://github.com/baselhusam/visionstyle/issues
9
+ Project-URL: Changelog, https://github.com/baselhusam/visionstyle/blob/main/CHANGELOG.md
10
+ Author-email: Basel Mather <baselmathar@gmail.com>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: annotation,bounding-box,computer-vision,object-detection,tracking,visualization,yolo
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Multimedia :: Graphics
23
+ Classifier: Topic :: Scientific/Engineering :: Image Recognition
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Requires-Dist: numpy>=1.24
27
+ Requires-Dist: opencv-python-headless>=4.8
28
+ Requires-Dist: pillow>=10.0
29
+ Requires-Dist: pydantic>=2.5
30
+ Requires-Dist: pyyaml>=6.0
31
+ Provides-Extra: all
32
+ Requires-Dist: fastapi>=0.110; extra == 'all'
33
+ Requires-Dist: lap>=0.5.12; extra == 'all'
34
+ Requires-Dist: python-multipart>=0.0.9; extra == 'all'
35
+ Requires-Dist: ultralytics>=8.3; extra == 'all'
36
+ Requires-Dist: uvicorn[standard]>=0.29; extra == 'all'
37
+ Provides-Extra: studio
38
+ Requires-Dist: fastapi>=0.110; extra == 'studio'
39
+ Requires-Dist: python-multipart>=0.0.9; extra == 'studio'
40
+ Requires-Dist: uvicorn[standard]>=0.29; extra == 'studio'
41
+ Provides-Extra: yolo
42
+ Requires-Dist: lap>=0.5.12; extra == 'yolo'
43
+ Requires-Dist: ultralytics>=8.3; extra == 'yolo'
44
+ Description-Content-Type: text/markdown
45
+
46
+ <p align="center">
47
+ <picture>
48
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/baselhusam/visionstyle/main/docs/images/hero-chroma-press-dark.png">
49
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/baselhusam/visionstyle/main/docs/images/hero-chroma-press-light.png">
50
+ <img src="https://raw.githubusercontent.com/baselhusam/visionstyle/main/docs/images/hero-chroma-press-light.png" alt="visionstyle — expressive computer-vision styling" width="100%">
51
+ </picture>
52
+ </p>
53
+
54
+ # visionstyle
55
+
56
+ **Beautiful, fully configurable bounding boxes for object detection.**
57
+ Boxes, labels, fills, glow, glass, film grain and tracking trails — described by one `Style`,
58
+ tuned live in the Studio, saved as YAML, rendered with one call.
59
+
60
+ ```python
61
+ import visionstyle as vs
62
+
63
+ dets = vs.Detections(xyxy=boxes, class_id=classes, confidence=scores, track_id=ids, names=model.names)
64
+ frame = vs.annotate(frame, dets, style="cinematic")
65
+ ```
66
+
67
+ [![CI](https://github.com/baselhusam/visionstyle/actions/workflows/ci.yml/badge.svg)](https://github.com/baselhusam/visionstyle/actions/workflows/ci.yml)
68
+ ![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)
69
+ ![License](https://img.shields.io/badge/license-MIT-green)
70
+
71
+ **[Explore visionstyle on GitHub Pages →](https://baselhusam.github.io/visionstyle/)**
72
+
73
+ The site deploys through GitHub Actions. Its static source and update notes live in [`site/README.md`](https://github.com/baselhusam/visionstyle/blob/main/site/README.md).
74
+
75
+ ---
76
+
77
+ ## Install
78
+
79
+ ```bash
80
+ pip install visionstyle # core: numpy, opencv-headless, pillow, pydantic, pyyaml
81
+ pip install "visionstyle[yolo]" # + ultralytics for the demos / CLI model support
82
+ pip install "visionstyle[studio]" # + fastapi/uvicorn for the Studio web app
83
+ pip install "visionstyle[all]"
84
+ ```
85
+
86
+ ## Presets
87
+
88
+ Twelve built-in looks. Every one is a plain YAML file you can copy and edit.
89
+
90
+ <p align="center"><img src="https://raw.githubusercontent.com/baselhusam/visionstyle/main/docs/images/gallery.jpg" alt="Gallery of the built-in presets" width="100%"></p>
91
+
92
+ | Preset | Look |
93
+ |---|---|
94
+ | `default` | Clean rectangle, solid tag |
95
+ | `minimal` | Hairline outline, bare text |
96
+ | `corners` | Thin L-brackets, monospace tag |
97
+ | `rounded` | Rounded corners, gradient tint, pill label |
98
+ | `dashed` | Dashed perimeter with marching ants |
99
+ | `glass` | Frosted-glass fill and label |
100
+ | `neon` | Glowing hue-cycling gradient outlines |
101
+ | `hud` | Double frame, reticle marks, inside mono labels, dotted trails |
102
+ | `cinematic` | Rounded amber/teal frames, gradient fill, pill labels, glow, filmic grade |
103
+ | `tracking` | Bold per-track trails, id-first labels |
104
+ | `spotlight` | Dims everything outside the detections |
105
+ | `confidence` | Stroke and tag color follow the confidence score |
106
+
107
+ ```bash
108
+ visionstyle presets list
109
+ visionstyle presets show cinematic
110
+ ```
111
+
112
+ ## Python API
113
+
114
+ ```python
115
+ import cv2
116
+ import visionstyle as vs
117
+
118
+ frame = cv2.imread("street.jpg")
119
+
120
+ # 1. detections: boxes in pixels, everything else optional
121
+ dets = vs.Detections(
122
+ xyxy=[[590, 650, 720, 1040], [1060, 660, 1520, 1000]],
123
+ class_name=["person", "car"],
124
+ confidence=[0.93, 0.88],
125
+ track_id=[14, 31],
126
+ )
127
+ # or: dets = vs.Detections.from_ultralytics(model.predict(frame)[0])
128
+
129
+ # 2. a style: preset name, YAML path, or built in code
130
+ style = vs.Style.preset("cinematic")
131
+ style.label.components = ["track_id", "text"] # every option is a typed attribute
132
+ style.trail.enabled = True
133
+
134
+ # 3. render. Keep one Annotator per video stream so trails/animations carry across frames.
135
+ annotator = vs.Annotator(style)
136
+ out = annotator.annotate(frame, dets)
137
+
138
+ # one-liner for stills
139
+ out = vs.annotate(frame, dets, style="minimal")
140
+ ```
141
+
142
+ `Detections` accepts lists, NumPy arrays or torch tensors; `xyxy` is pixel `x1, y1, x2, y2`
143
+ (`Detections.from_xywh`, `from_xywh_topleft`, `from_dicts` also exist). Frames are BGR uint8 like
144
+ OpenCV; pass `rgb=True` for RGB arrays.
145
+
146
+ ### What you can configure
147
+
148
+ | Section | Options |
149
+ |---|---|
150
+ | **Box** | `rectangle`, `rounded` (radius), `corners` (bracket length, curved elbows), `reticle`, `none`; double line; center mark |
151
+ | **Stroke** | thickness, opacity, color (`palette` per class/track, `confidence` ramp, or any hex/rgb/name) |
152
+ | **Fill** | on/off, opacity, color, `solid` / `gradient` (5 directions) / `hatch` |
153
+ | **Line** | `solid` / `dashed` / `dotted`, dash & gap sizes, multi-color `segments` or perimeter `gradient`, animation `march` / `hue_cycle` / `pulse` |
154
+ | **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 |
155
+ | **Effects** | glow, shadow, frosted glass, spotlight dimming, vignette, film grain, color grade |
156
+ | **Tracking** | trail length, anchor (feet/center/top), `solid` / `dotted` / `dashed` / `ribbon`, fade & taper, smoothing, glow, points |
157
+ | **Global** | palette (built-in or custom list), per-class color overrides, confidence threshold, resolution scaling, per-object scaling (strength, clamps, box/label), fps |
158
+
159
+ All sizes are in *reference pixels* at ~1080p and scale automatically with the frame size
160
+ (`style.scale = "auto"`), so one style looks the same on a webcam and a 4K photo.
161
+ On top of that, strokes and label tags scale with each detected object's size
162
+ (`style.object_scale`, on by default): far-away objects get thin outlines and small tags, close-up
163
+ ones get heavier outlines and larger text. Tune `strength`, `min_factor` / `max_factor`, or restrict
164
+ it with `apply_to: box | label`; set `enabled: false` for constant sizes.
165
+
166
+ ### Save and share styles
167
+
168
+ ```python
169
+ style.save("my_style.yaml") # anywhere
170
+ style.save_preset("my-look") # ~/.visionstyle/presets/my-look.yaml
171
+ vs.Style.preset("my-look") # found by name from now on
172
+ vs.presets.list() # built-in + yours
173
+ ```
174
+
175
+ Lookup order: `$VISIONSTYLE_PRESETS_DIR` → `~/.visionstyle/presets` → built-ins → file path.
176
+
177
+ ## Studio
178
+
179
+ Design a style visually, on your own image and model, and save it as a preset the package loads by name.
180
+
181
+ ```bash
182
+ pip install "visionstyle[studio,yolo]"
183
+ visionstyle studio # opens http://127.0.0.1:8420
184
+ ```
185
+
186
+ <p align="center"><img src="https://raw.githubusercontent.com/baselhusam/visionstyle/main/docs/images/studio.jpg" alt="The visionstyle Studio" width="100%"></p>
187
+
188
+ * **Source:** choose a bundled scene or upload an image or video, set the confidence threshold,
189
+ and run detection. The sample video includes tracks; new video tracking requires the YOLO extra.
190
+ * **Design:** start with a preset, then adjust boxes, strokes, labels, effects, and trails beside
191
+ the preview. The **Python package itself** renders every change, matching `annotate()` output.
192
+ **Save** (next to the Style menu) stores the look as a named preset with an optional description;
193
+ the Style menu lists your saved presets, and built-ins stay in the Library.
194
+ * **Objects:** filter by class, hide objects, or isolate a track. Scrub video frames or play the
195
+ timeline; use the preview menu to save an annotated frame.
196
+ * **Export:** copy or download the YAML, a self-contained Python snippet, or a prompt for AI
197
+ coding tools.
198
+
199
+ Presets saved in the Studio go to `~/.visionstyle/presets` and load by name anywhere on that machine
200
+ with `vs.Style.preset("my-look")`. To keep them with a project instead, start the Studio from the
201
+ repo with `visionstyle studio --presets-dir ./styles`; each preset becomes `styles/<name>.yaml`,
202
+ which you commit and load with `vs.Style.load("styles/my-look.yaml")` (or set
203
+ `VISIONSTYLE_PRESETS_DIR=./styles` to load them by name).
204
+
205
+ Keyboard shortcuts: **Space** plays or pauses, **← / →** steps through video frames, **Shift +
206
+ ← / →** jumps ten frames, and **R** resets the style. Focused controls keep their own keyboard behavior.
207
+
208
+ ## CLI
209
+
210
+ ```bash
211
+ visionstyle render photo.jpg -s neon --model yolo11n.pt -o out.jpg
212
+ visionstyle render clip.mp4 -s tracking --model yolo11n.pt --track -o out.mp4
213
+ visionstyle render 0 -s hud --model yolo11n.pt --track -o webcam.mp4 # webcam index
214
+ visionstyle render photo.jpg -d detections.json -s corners # no model needed
215
+ visionstyle gallery photo.jpg --model yolo11n.pt -o gallery.png # every preset at once
216
+ visionstyle presets list | show NAME | export NAME -o my.yaml
217
+ visionstyle schema -o style.schema.json
218
+ ```
219
+
220
+ `detections.json` is a list of `{"xyxy": [...], "class_name": "...", "confidence": 0.9, "track_id": 1}`.
221
+
222
+ ## Development
223
+
224
+ ```bash
225
+ uv sync --all-extras --group dev
226
+ uv run pytest && uv run ruff check . && uv run mypy src
227
+
228
+ cd studio && npm install
229
+ npm run dev # Vite dev server on :5173 proxying /api to :8420
230
+ npm run build # -> src/visionstyle/studio/static (bundled into the wheel)
231
+ ```
232
+
233
+ When `src/visionstyle/style/schema.py` changes, regenerate the frontend schema and types:
234
+
235
+ ```bash
236
+ uv run visionstyle schema -o studio/schema.json && (cd studio && npm run gen:types)
237
+ ```
238
+
239
+ Releases: bump `__version__` in `src/visionstyle/__init__.py`, update `CHANGELOG.md`, tag `vX.Y.Z`
240
+ and push. `release.yml` builds the frontend + wheel, publishes to PyPI via trusted publishing (register
241
+ the `pypi` environment / publisher once on pypi.org) and creates a GitHub release.
242
+ For a dry run first, start the `TestPyPI` workflow from the Actions tab: it publishes the current
243
+ commit as `X.Y.Z.devN` to test.pypi.org (environment `testpypi`), installs it into a clean venv and
244
+ smoke-tests the CLI and the Studio.
245
+
246
+ ## Credits
247
+
248
+ Sample photos: see [`src/visionstyle/assets/samples/CREDITS.md`](https://github.com/baselhusam/visionstyle/blob/main/src/visionstyle/assets/samples/CREDITS.md).
249
+ Fonts: [Inter](https://rsms.me/inter/) and [JetBrains Mono](https://www.jetbrains.com/lp/mono/), both under the SIL Open Font License.
250
+
251
+ MIT © Basel Mather
@@ -0,0 +1,206 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/baselhusam/visionstyle/main/docs/images/hero-chroma-press-dark.png">
4
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/baselhusam/visionstyle/main/docs/images/hero-chroma-press-light.png">
5
+ <img src="https://raw.githubusercontent.com/baselhusam/visionstyle/main/docs/images/hero-chroma-press-light.png" alt="visionstyle — expressive computer-vision styling" width="100%">
6
+ </picture>
7
+ </p>
8
+
9
+ # visionstyle
10
+
11
+ **Beautiful, fully configurable bounding boxes for object detection.**
12
+ Boxes, labels, fills, glow, glass, film grain and tracking trails — described by one `Style`,
13
+ tuned live in the Studio, saved as YAML, rendered with one call.
14
+
15
+ ```python
16
+ import visionstyle as vs
17
+
18
+ dets = vs.Detections(xyxy=boxes, class_id=classes, confidence=scores, track_id=ids, names=model.names)
19
+ frame = vs.annotate(frame, dets, style="cinematic")
20
+ ```
21
+
22
+ [![CI](https://github.com/baselhusam/visionstyle/actions/workflows/ci.yml/badge.svg)](https://github.com/baselhusam/visionstyle/actions/workflows/ci.yml)
23
+ ![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)
24
+ ![License](https://img.shields.io/badge/license-MIT-green)
25
+
26
+ **[Explore visionstyle on GitHub Pages →](https://baselhusam.github.io/visionstyle/)**
27
+
28
+ The site deploys through GitHub Actions. Its static source and update notes live in [`site/README.md`](https://github.com/baselhusam/visionstyle/blob/main/site/README.md).
29
+
30
+ ---
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install visionstyle # core: numpy, opencv-headless, pillow, pydantic, pyyaml
36
+ pip install "visionstyle[yolo]" # + ultralytics for the demos / CLI model support
37
+ pip install "visionstyle[studio]" # + fastapi/uvicorn for the Studio web app
38
+ pip install "visionstyle[all]"
39
+ ```
40
+
41
+ ## Presets
42
+
43
+ Twelve built-in looks. Every one is a plain YAML file you can copy and edit.
44
+
45
+ <p align="center"><img src="https://raw.githubusercontent.com/baselhusam/visionstyle/main/docs/images/gallery.jpg" alt="Gallery of the built-in presets" width="100%"></p>
46
+
47
+ | Preset | Look |
48
+ |---|---|
49
+ | `default` | Clean rectangle, solid tag |
50
+ | `minimal` | Hairline outline, bare text |
51
+ | `corners` | Thin L-brackets, monospace tag |
52
+ | `rounded` | Rounded corners, gradient tint, pill label |
53
+ | `dashed` | Dashed perimeter with marching ants |
54
+ | `glass` | Frosted-glass fill and label |
55
+ | `neon` | Glowing hue-cycling gradient outlines |
56
+ | `hud` | Double frame, reticle marks, inside mono labels, dotted trails |
57
+ | `cinematic` | Rounded amber/teal frames, gradient fill, pill labels, glow, filmic grade |
58
+ | `tracking` | Bold per-track trails, id-first labels |
59
+ | `spotlight` | Dims everything outside the detections |
60
+ | `confidence` | Stroke and tag color follow the confidence score |
61
+
62
+ ```bash
63
+ visionstyle presets list
64
+ visionstyle presets show cinematic
65
+ ```
66
+
67
+ ## Python API
68
+
69
+ ```python
70
+ import cv2
71
+ import visionstyle as vs
72
+
73
+ frame = cv2.imread("street.jpg")
74
+
75
+ # 1. detections: boxes in pixels, everything else optional
76
+ dets = vs.Detections(
77
+ xyxy=[[590, 650, 720, 1040], [1060, 660, 1520, 1000]],
78
+ class_name=["person", "car"],
79
+ confidence=[0.93, 0.88],
80
+ track_id=[14, 31],
81
+ )
82
+ # or: dets = vs.Detections.from_ultralytics(model.predict(frame)[0])
83
+
84
+ # 2. a style: preset name, YAML path, or built in code
85
+ style = vs.Style.preset("cinematic")
86
+ style.label.components = ["track_id", "text"] # every option is a typed attribute
87
+ style.trail.enabled = True
88
+
89
+ # 3. render. Keep one Annotator per video stream so trails/animations carry across frames.
90
+ annotator = vs.Annotator(style)
91
+ out = annotator.annotate(frame, dets)
92
+
93
+ # one-liner for stills
94
+ out = vs.annotate(frame, dets, style="minimal")
95
+ ```
96
+
97
+ `Detections` accepts lists, NumPy arrays or torch tensors; `xyxy` is pixel `x1, y1, x2, y2`
98
+ (`Detections.from_xywh`, `from_xywh_topleft`, `from_dicts` also exist). Frames are BGR uint8 like
99
+ OpenCV; pass `rgb=True` for RGB arrays.
100
+
101
+ ### What you can configure
102
+
103
+ | Section | Options |
104
+ |---|---|
105
+ | **Box** | `rectangle`, `rounded` (radius), `corners` (bracket length, curved elbows), `reticle`, `none`; double line; center mark |
106
+ | **Stroke** | thickness, opacity, color (`palette` per class/track, `confidence` ramp, or any hex/rgb/name) |
107
+ | **Fill** | on/off, opacity, color, `solid` / `gradient` (5 directions) / `hatch` |
108
+ | **Line** | `solid` / `dashed` / `dotted`, dash & gap sizes, multi-color `segments` or perimeter `gradient`, animation `march` / `hue_cycle` / `pulse` |
109
+ | **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 |
110
+ | **Effects** | glow, shadow, frosted glass, spotlight dimming, vignette, film grain, color grade |
111
+ | **Tracking** | trail length, anchor (feet/center/top), `solid` / `dotted` / `dashed` / `ribbon`, fade & taper, smoothing, glow, points |
112
+ | **Global** | palette (built-in or custom list), per-class color overrides, confidence threshold, resolution scaling, per-object scaling (strength, clamps, box/label), fps |
113
+
114
+ All sizes are in *reference pixels* at ~1080p and scale automatically with the frame size
115
+ (`style.scale = "auto"`), so one style looks the same on a webcam and a 4K photo.
116
+ On top of that, strokes and label tags scale with each detected object's size
117
+ (`style.object_scale`, on by default): far-away objects get thin outlines and small tags, close-up
118
+ ones get heavier outlines and larger text. Tune `strength`, `min_factor` / `max_factor`, or restrict
119
+ it with `apply_to: box | label`; set `enabled: false` for constant sizes.
120
+
121
+ ### Save and share styles
122
+
123
+ ```python
124
+ style.save("my_style.yaml") # anywhere
125
+ style.save_preset("my-look") # ~/.visionstyle/presets/my-look.yaml
126
+ vs.Style.preset("my-look") # found by name from now on
127
+ vs.presets.list() # built-in + yours
128
+ ```
129
+
130
+ Lookup order: `$VISIONSTYLE_PRESETS_DIR` → `~/.visionstyle/presets` → built-ins → file path.
131
+
132
+ ## Studio
133
+
134
+ Design a style visually, on your own image and model, and save it as a preset the package loads by name.
135
+
136
+ ```bash
137
+ pip install "visionstyle[studio,yolo]"
138
+ visionstyle studio # opens http://127.0.0.1:8420
139
+ ```
140
+
141
+ <p align="center"><img src="https://raw.githubusercontent.com/baselhusam/visionstyle/main/docs/images/studio.jpg" alt="The visionstyle Studio" width="100%"></p>
142
+
143
+ * **Source:** choose a bundled scene or upload an image or video, set the confidence threshold,
144
+ and run detection. The sample video includes tracks; new video tracking requires the YOLO extra.
145
+ * **Design:** start with a preset, then adjust boxes, strokes, labels, effects, and trails beside
146
+ the preview. The **Python package itself** renders every change, matching `annotate()` output.
147
+ **Save** (next to the Style menu) stores the look as a named preset with an optional description;
148
+ the Style menu lists your saved presets, and built-ins stay in the Library.
149
+ * **Objects:** filter by class, hide objects, or isolate a track. Scrub video frames or play the
150
+ timeline; use the preview menu to save an annotated frame.
151
+ * **Export:** copy or download the YAML, a self-contained Python snippet, or a prompt for AI
152
+ coding tools.
153
+
154
+ Presets saved in the Studio go to `~/.visionstyle/presets` and load by name anywhere on that machine
155
+ with `vs.Style.preset("my-look")`. To keep them with a project instead, start the Studio from the
156
+ repo with `visionstyle studio --presets-dir ./styles`; each preset becomes `styles/<name>.yaml`,
157
+ which you commit and load with `vs.Style.load("styles/my-look.yaml")` (or set
158
+ `VISIONSTYLE_PRESETS_DIR=./styles` to load them by name).
159
+
160
+ Keyboard shortcuts: **Space** plays or pauses, **← / →** steps through video frames, **Shift +
161
+ ← / →** jumps ten frames, and **R** resets the style. Focused controls keep their own keyboard behavior.
162
+
163
+ ## CLI
164
+
165
+ ```bash
166
+ visionstyle render photo.jpg -s neon --model yolo11n.pt -o out.jpg
167
+ visionstyle render clip.mp4 -s tracking --model yolo11n.pt --track -o out.mp4
168
+ visionstyle render 0 -s hud --model yolo11n.pt --track -o webcam.mp4 # webcam index
169
+ visionstyle render photo.jpg -d detections.json -s corners # no model needed
170
+ visionstyle gallery photo.jpg --model yolo11n.pt -o gallery.png # every preset at once
171
+ visionstyle presets list | show NAME | export NAME -o my.yaml
172
+ visionstyle schema -o style.schema.json
173
+ ```
174
+
175
+ `detections.json` is a list of `{"xyxy": [...], "class_name": "...", "confidence": 0.9, "track_id": 1}`.
176
+
177
+ ## Development
178
+
179
+ ```bash
180
+ uv sync --all-extras --group dev
181
+ uv run pytest && uv run ruff check . && uv run mypy src
182
+
183
+ cd studio && npm install
184
+ npm run dev # Vite dev server on :5173 proxying /api to :8420
185
+ npm run build # -> src/visionstyle/studio/static (bundled into the wheel)
186
+ ```
187
+
188
+ When `src/visionstyle/style/schema.py` changes, regenerate the frontend schema and types:
189
+
190
+ ```bash
191
+ uv run visionstyle schema -o studio/schema.json && (cd studio && npm run gen:types)
192
+ ```
193
+
194
+ Releases: bump `__version__` in `src/visionstyle/__init__.py`, update `CHANGELOG.md`, tag `vX.Y.Z`
195
+ and push. `release.yml` builds the frontend + wheel, publishes to PyPI via trusted publishing (register
196
+ the `pypi` environment / publisher once on pypi.org) and creates a GitHub release.
197
+ For a dry run first, start the `TestPyPI` workflow from the Actions tab: it publishes the current
198
+ commit as `X.Y.Z.devN` to test.pypi.org (environment `testpypi`), installs it into a clean venv and
199
+ smoke-tests the CLI and the Studio.
200
+
201
+ ## Credits
202
+
203
+ Sample photos: see [`src/visionstyle/assets/samples/CREDITS.md`](https://github.com/baselhusam/visionstyle/blob/main/src/visionstyle/assets/samples/CREDITS.md).
204
+ Fonts: [Inter](https://rsms.me/inter/) and [JetBrains Mono](https://www.jetbrains.com/lp/mono/), both under the SIL Open Font License.
205
+
206
+ MIT © Basel Mather
@@ -0,0 +1,103 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "visionstyle"
7
+ dynamic = ["version"]
8
+ description = "Beautiful, fully configurable bounding boxes, labels and tracking trails for object detection."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "Basel Mather", email = "baselmathar@gmail.com" }]
13
+ keywords = ["computer-vision", "object-detection", "bounding-box", "annotation", "yolo", "visualization", "tracking"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "Intended Audience :: Science/Research",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Scientific/Engineering :: Image Recognition",
24
+ "Topic :: Multimedia :: Graphics",
25
+ "Typing :: Typed",
26
+ ]
27
+ dependencies = [
28
+ "numpy>=1.24",
29
+ "opencv-python-headless>=4.8",
30
+ "pillow>=10.0",
31
+ "pydantic>=2.5",
32
+ "pyyaml>=6.0",
33
+ ]
34
+
35
+ [project.optional-dependencies]
36
+ yolo = ["ultralytics>=8.3", "lap>=0.5.12"] # lap: ByteTrack/BoT-SORT assignment
37
+ studio = ["fastapi>=0.110", "uvicorn[standard]>=0.29", "python-multipart>=0.0.9"]
38
+ all = ["visionstyle[yolo,studio]"]
39
+
40
+ [project.scripts]
41
+ visionstyle = "visionstyle.cli:main"
42
+
43
+ [project.urls]
44
+ Homepage = "https://github.com/baselhusam/visionstyle"
45
+ Repository = "https://github.com/baselhusam/visionstyle"
46
+ Documentation = "https://baselhusam.github.io/visionstyle/"
47
+ Issues = "https://github.com/baselhusam/visionstyle/issues"
48
+ Changelog = "https://github.com/baselhusam/visionstyle/blob/main/CHANGELOG.md"
49
+
50
+ [dependency-groups]
51
+ dev = [
52
+ "pytest>=8.0",
53
+ "pytest-cov>=5.0",
54
+ "ruff>=0.6",
55
+ "mypy>=1.10",
56
+ "httpx>=0.27",
57
+ "types-PyYAML",
58
+ "pre-commit>=3.7",
59
+ ]
60
+
61
+ [tool.hatch.version]
62
+ path = "src/visionstyle/__init__.py"
63
+
64
+ [tool.hatch.build]
65
+ # the studio bundle is gitignored (built by `npm run build`) but must ship in the wheel
66
+ artifacts = ["src/visionstyle/studio/static/**"]
67
+
68
+ [tool.hatch.build.targets.wheel]
69
+ packages = ["src/visionstyle"]
70
+
71
+ [tool.hatch.build.targets.sdist]
72
+ include = ["/src/visionstyle", "/README.md", "/LICENSE", "/CHANGELOG.md"]
73
+
74
+ [tool.ruff]
75
+ line-length = 100
76
+ target-version = "py310"
77
+ src = ["src", "tests"]
78
+
79
+ [tool.ruff.lint]
80
+ select = ["E", "F", "W", "I", "UP", "B", "SIM", "RUF", "N", "C4", "PIE", "T20"]
81
+ ignore = ["RUF012", "B008"]
82
+
83
+ [tool.ruff.lint.per-file-ignores]
84
+ "tests/*" = ["T20", "E501"]
85
+ "examples/*" = ["T20"]
86
+ "src/visionstyle/cli.py" = ["T20"]
87
+
88
+ [tool.mypy]
89
+ strict = true
90
+ disable_error_code = ["no-any-return", "type-arg"]
91
+ files = ["src/visionstyle"]
92
+ plugins = ["pydantic.mypy"]
93
+ ignore_missing_imports = true
94
+ warn_unused_ignores = false
95
+
96
+ [[tool.mypy.overrides]]
97
+ module = ["cv2", "cv2.*", "ultralytics", "ultralytics.*"]
98
+ ignore_missing_imports = true
99
+
100
+ [tool.pytest.ini_options]
101
+ testpaths = ["tests"]
102
+ addopts = "-q"
103
+ markers = ["yolo: tests that download and run a real YOLO model"]
@@ -0,0 +1,50 @@
1
+ """visionstyle - beautiful, configurable bounding boxes for object detection.
2
+
3
+ >>> import visionstyle as vs
4
+ >>> dets = vs.Detections(xyxy=[[50, 40, 220, 300]], class_name=["person"], confidence=[0.93])
5
+ >>> out = vs.annotate(frame, dets, style="cinematic")
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from visionstyle import presets
11
+ from visionstyle.color import PALETTES, parse_color
12
+ from visionstyle.detections import Detection, Detections
13
+ from visionstyle.render.annotator import Annotator, annotate
14
+ from visionstyle.render.trails import TrailBuffer
15
+ from visionstyle.style.schema import (
16
+ BoxStyle,
17
+ EffectsStyle,
18
+ FillStyle,
19
+ LabelStyle,
20
+ LinePattern,
21
+ ObjectScaleStyle,
22
+ PaletteSpec,
23
+ StrokeStyle,
24
+ Style,
25
+ TrailStyle,
26
+ )
27
+
28
+ __version__ = "0.1.0"
29
+
30
+ __all__ = [
31
+ "PALETTES",
32
+ "Annotator",
33
+ "BoxStyle",
34
+ "Detection",
35
+ "Detections",
36
+ "EffectsStyle",
37
+ "FillStyle",
38
+ "LabelStyle",
39
+ "LinePattern",
40
+ "ObjectScaleStyle",
41
+ "PaletteSpec",
42
+ "StrokeStyle",
43
+ "Style",
44
+ "TrailBuffer",
45
+ "TrailStyle",
46
+ "__version__",
47
+ "annotate",
48
+ "parse_color",
49
+ "presets",
50
+ ]