ovkit 0.2.0__tar.gz → 0.4.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.
- {ovkit-0.2.0 → ovkit-0.4.0}/.gitignore +26 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/PKG-INFO +134 -8
- {ovkit-0.2.0 → ovkit-0.4.0}/README.md +122 -6
- {ovkit-0.2.0 → ovkit-0.4.0}/examples/README.md +1 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/pyproject.toml +13 -2
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/__init__.py +24 -1
- ovkit-0.4.0/src/ovkit/__main__.py +299 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/constants.py +29 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/convert.py +2 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/download.py +57 -2
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/errors.py +21 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/i18n.py +14 -0
- ovkit-0.4.0/src/ovkit/core/maths.py +32 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/model.py +28 -16
- ovkit-0.4.0/src/ovkit/core/progress.py +150 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/registry.py +62 -11
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/results.py +82 -5
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/gui/app.py +89 -5
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/gui/controller.py +261 -23
- ovkit-0.4.0/src/ovkit/hub.py +225 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/image/ops.py +0 -12
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/manifests/aliases.yaml +11 -1
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/manifests/labels.yaml +12 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/manifests/models.yaml +17 -24
- ovkit-0.4.0/src/ovkit/manifests/ocr_ko.yaml +36 -0
- ovkit-0.4.0/src/ovkit/manifests/revived.yaml +119 -0
- ovkit-0.4.0/src/ovkit/manifests/rtdetr.yaml +84 -0
- ovkit-0.4.0/src/ovkit/manifests/tts.yaml +66 -0
- ovkit-0.4.0/src/ovkit/manifests/vision_extra.yaml +40 -0
- ovkit-0.4.0/src/ovkit/manifests/zoo.yaml +83 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/__init__.py +26 -1
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/analyze.py +10 -10
- {ovkit-0.2.0/src/ovkit/solutions → ovkit-0.4.0/src/ovkit/pipelines}/anomaly.py +11 -5
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/attention.py +3 -3
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/base.py +12 -4
- ovkit-0.4.0/src/ovkit/pipelines/classroom.py +400 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/gaze.py +5 -5
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/plates.py +2 -2
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/privacy.py +2 -2
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/reid.py +9 -9
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/scene.py +16 -6
- ovkit-0.4.0/src/ovkit/pipelines/speak.py +331 -0
- ovkit-0.4.0/src/ovkit/pipelines/teach.py +454 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/temporal.py +4 -8
- ovkit-0.4.0/src/ovkit/pipelines/text.py +132 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/tracking.py +3 -3
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/__init__.py +5 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/audio.py +10 -7
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/base.py +2 -5
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/classify.py +2 -8
- ovkit-0.4.0/src/ovkit/recognize/depth.py +97 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/detect.py +4 -1
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/generic.py +2 -6
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/ocr.py +30 -3
- ovkit-0.2.0/src/ovkit/__main__.py +0 -181
- ovkit-0.2.0/src/ovkit/face/__init__.py +0 -8
- ovkit-0.2.0/src/ovkit/face/analyzer.py +0 -7
- ovkit-0.2.0/src/ovkit/pipelines/text.py +0 -81
- ovkit-0.2.0/src/ovkit/solutions/__init__.py +0 -14
- ovkit-0.2.0/src/ovkit/solutions/ocr.py +0 -7
- ovkit-0.2.0/src/ovkit/solutions/reid.py +0 -7
- ovkit-0.2.0/src/ovkit/solutions/tracking.py +0 -7
- {ovkit-0.2.0 → ovkit-0.4.0}/LICENSE +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/audio/__init__.py +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/audio/ops.py +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/audio/plot.py +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/__init__.py +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/backend.py +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/tasks.py +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/data/imagenet1000.txt +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/genai/__init__.py +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/genai/pipelines.py +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/gui/__init__.py +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/image/__init__.py +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/manifests/genai.yaml +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/manifests/omz.yaml +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/py.typed +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/face.py +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/pose.py +0 -0
- {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/segment.py +0 -0
|
@@ -217,3 +217,29 @@ __marimo__/
|
|
|
217
217
|
|
|
218
218
|
# Streamlit
|
|
219
219
|
.streamlit/secrets.toml
|
|
220
|
+
|
|
221
|
+
# --- what running ovkit leaves behind ---------------------------------------
|
|
222
|
+
# python examples/speak.py -> speak.wav, speak.png
|
|
223
|
+
# ovkit run detect photo.jpg -> photo_out.jpg
|
|
224
|
+
# python scripts/verify_capabilities.py -> .verify/
|
|
225
|
+
#
|
|
226
|
+
# Examples and scripts write their results into the folder you ran them from,
|
|
227
|
+
# which for a fresh clone is the repo root. Every rule here is rooted (leading
|
|
228
|
+
# slash) on purpose: a picture someone deliberately adds under docs/ or tests/
|
|
229
|
+
# is still tracked, and only the clutter at the top is hidden.
|
|
230
|
+
/*.jpg
|
|
231
|
+
/*.jpeg
|
|
232
|
+
/*.png
|
|
233
|
+
/*.bmp
|
|
234
|
+
/*.webp
|
|
235
|
+
/*.wav
|
|
236
|
+
/*.mp4
|
|
237
|
+
# `ovkit run` names its output after the input, whatever the extension.
|
|
238
|
+
/*_out.*
|
|
239
|
+
# examples/teach_ai.py — a classifier you taught, saved next to the code.
|
|
240
|
+
/my-ai/
|
|
241
|
+
# Scratch folders the scripts default to.
|
|
242
|
+
/.verify/
|
|
243
|
+
/tts_out/
|
|
244
|
+
/supertonic_ir/
|
|
245
|
+
/report.txt
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ovkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A simple Python inference API for OpenVINO: one import, one Model class, clean Results — with AUTO/NPU, async, and INT8.
|
|
5
5
|
Project-URL: Homepage, https://github.com/leeyunjai82/ovkit
|
|
6
6
|
Project-URL: Documentation, https://leeyunjai82.github.io/ovkit/
|
|
@@ -27,9 +27,12 @@ Requires-Dist: pillow>=9.0
|
|
|
27
27
|
Requires-Dist: pyyaml>=6.0
|
|
28
28
|
Provides-Extra: all
|
|
29
29
|
Requires-Dist: anomalib; extra == 'all'
|
|
30
|
+
Requires-Dist: mediapipe; extra == 'all'
|
|
30
31
|
Requires-Dist: nncf; extra == 'all'
|
|
31
32
|
Requires-Dist: openvino-genai; extra == 'all'
|
|
32
|
-
Requires-Dist: optimum-intel; extra == 'all'
|
|
33
|
+
Requires-Dist: optimum-intel[openvino]>=1.20; extra == 'all'
|
|
34
|
+
Requires-Dist: rtdetr[train]>=0.6; extra == 'all'
|
|
35
|
+
Requires-Dist: transformers>=4.40; extra == 'all'
|
|
33
36
|
Provides-Extra: anomaly
|
|
34
37
|
Requires-Dist: anomalib; extra == 'anomaly'
|
|
35
38
|
Provides-Extra: dev
|
|
@@ -39,8 +42,15 @@ Requires-Dist: ruff; extra == 'dev'
|
|
|
39
42
|
Provides-Extra: genai
|
|
40
43
|
Requires-Dist: openvino-genai; extra == 'genai'
|
|
41
44
|
Requires-Dist: optimum-intel; extra == 'genai'
|
|
45
|
+
Provides-Extra: hand
|
|
46
|
+
Requires-Dist: mediapipe; extra == 'hand'
|
|
47
|
+
Provides-Extra: hf
|
|
48
|
+
Requires-Dist: optimum-intel[openvino]>=1.20; extra == 'hf'
|
|
49
|
+
Requires-Dist: transformers>=4.40; extra == 'hf'
|
|
42
50
|
Provides-Extra: quant
|
|
43
51
|
Requires-Dist: nncf; extra == 'quant'
|
|
52
|
+
Provides-Extra: train
|
|
53
|
+
Requires-Dist: rtdetr[train]>=0.6; extra == 'train'
|
|
44
54
|
Description-Content-Type: text/markdown
|
|
45
55
|
|
|
46
56
|
<div align="center">
|
|
@@ -49,8 +59,9 @@ Description-Content-Type: text/markdown
|
|
|
49
59
|
|
|
50
60
|
# ovkit
|
|
51
61
|
|
|
52
|
-
**OpenVINO inference in
|
|
53
|
-
ready models — with `AUTO`/`NPU`/`GPU` devices,
|
|
62
|
+
**OpenVINO inference in one line.** One `Model` class, clean `Results`, and 19
|
|
63
|
+
composed capabilities over 68 ready models — with `AUTO`/`NPU`/`GPU` devices,
|
|
64
|
+
async throughput, INT8, and your own detector trainable in the same package.
|
|
54
65
|
|
|
55
66
|
[](https://github.com/leeyunjai82/ovkit/actions/workflows/ci.yml)
|
|
56
67
|
[](https://leeyunjai82.github.io/ovkit/)
|
|
@@ -83,6 +94,8 @@ for row in r.found: # [{'name': '사람', 'name_en': 'person'
|
|
|
83
94
|
|
|
84
95
|
for r in Model("track", 0): # webcam / video / "mic" -> a stream
|
|
85
96
|
print(r, r.elapsed_ms, r.device) # 2x person (#1, #4) 14.2 GPU
|
|
97
|
+
if not r.show("track"): # a window; q or Esc ends the loop
|
|
98
|
+
break
|
|
86
99
|
```
|
|
87
100
|
|
|
88
101
|
The same call also runs whole **capabilities** — several models chained into one
|
|
@@ -114,6 +127,8 @@ Python 3.10+. For development, install from source:
|
|
|
114
127
|
git clone https://github.com/leeyunjai82/ovkit.git && cd ovkit
|
|
115
128
|
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
116
129
|
pip install -e ".[dev]"
|
|
130
|
+
|
|
131
|
+
python scripts/check.py --fix # what CI runs: ruff, black, pytest
|
|
117
132
|
```
|
|
118
133
|
|
|
119
134
|
## Capabilities
|
|
@@ -138,6 +153,13 @@ camera index).
|
|
|
138
153
|
| `attention` | `1 person looking at: laptop` | gaze + object detection (ray-cast into the boxes) |
|
|
139
154
|
| `anonymize` | the picture with every face pixelated | face (and plate) detection + redaction |
|
|
140
155
|
| `face_match` | `('yunjai', 0.81)` — who this is | embedding + cosine matching against your gallery |
|
|
156
|
+
| `count` | `pencil 3 · cup 1` (optionally one kind only) | detection + per-kind tally |
|
|
157
|
+
| `posture` | `neck 32° — sit up (6s)` | pose + neck angle **over time** |
|
|
158
|
+
| `exercise` | `squat x 12 (down)` | pose + joint-angle hysteresis (squat, push-up) |
|
|
159
|
+
| `attendance` | `present 24/26` + `roll.csv` | face detection + roster matching |
|
|
160
|
+
| `teach` | your own classes, from example photos | embedding + cosine k-NN (5 modes) |
|
|
161
|
+
| `depth` | `nearest: bottom-left · 34% of the frame is close` + a colour map | Depth Anything V2 Small |
|
|
162
|
+
| `remove_background` | the subject, saved as a transparent PNG | U2-Net |
|
|
141
163
|
|
|
142
164
|
```python
|
|
143
165
|
from ovkit import Model, list_pipelines
|
|
@@ -149,8 +171,78 @@ Model("face_analyze", attributes=("age_gender",)) # configure what runs
|
|
|
149
171
|
```
|
|
150
172
|
|
|
151
173
|
Aliases: `ocr`, `anpr`, `blur`, `driver`, `describe`, `faces`, `people`, `vehicle`, `tracking`, `reid`.
|
|
152
|
-
|
|
153
|
-
|
|
174
|
+
|
|
175
|
+
## Teach your own AI (no GPU)
|
|
176
|
+
|
|
177
|
+
A Teachable Machine in five lines — an embedding model turns examples into
|
|
178
|
+
vectors, new inputs match the nearest ones. Five modes: `photo` (default),
|
|
179
|
+
`face` (expressions), `hand` (needs `ovkit[hand]`), `upper`, `body`:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
ai = Model("teach") # or Model("가르치기")
|
|
183
|
+
ai.learn("can", "photos/cans/") # a folder per thing to recognise
|
|
184
|
+
ai.learn("bottle", "photos/bottles/")
|
|
185
|
+
print(ai.guess("new_photo.jpg")) # ('can', 0.93)
|
|
186
|
+
print(ai.score("test_photos/")) # accuracy + what it confuses
|
|
187
|
+
ai.save("recycling") # -> Documents/ovkit/recycling.json
|
|
188
|
+
for r in ai.predict(0, stream=True): ... # webcam over YOUR classes
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Korean method names work too: `배우기`, `맞혀봐`, `점수`, `저장`. Collect
|
|
192
|
+
examples with the webcam: `from ovkit.pipelines.teach import collect; collect("가위", 30)`.
|
|
193
|
+
|
|
194
|
+
## Any model on the Hugging Face Hub
|
|
195
|
+
|
|
196
|
+
The Open Model Zoo is an archive; the models people want are on the Hub.
|
|
197
|
+
`optimum-intel` converts almost any of them to OpenVINO IR, so ovkit does not
|
|
198
|
+
have to mirror a model to serve it:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
pip install "ovkit[hf]" # the converter — needed once
|
|
202
|
+
ovkit pull google/vit-base-patch16-224
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
Model("google/vit-base-patch16-224", "photo.jpg") # tabby 0.94
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Converting is the heavy half and happens once; what lands in the cache is a
|
|
210
|
+
plain OpenVINO model, so **running it afterwards needs nothing but ovkit** — a
|
|
211
|
+
classroom machine can be handed the cache and never see PyTorch. The pull also
|
|
212
|
+
writes the model's own class names and the normalisation its image processor
|
|
213
|
+
declares, so results read as answers and not as confident nonsense.
|
|
214
|
+
|
|
215
|
+
Tasks: image classification, zero-shot (CLIP) classification, feature
|
|
216
|
+
extraction. Detection and segmentation come from the registry instead
|
|
217
|
+
(`detect`, `segment`) — optimum-intel does not export those.
|
|
218
|
+
|
|
219
|
+
## Train your own detector
|
|
220
|
+
|
|
221
|
+
RT-DETR, trainable, Apache-2.0 end to end — an Ultralytics-style workflow with
|
|
222
|
+
no AGPL code or weights anywhere (`pip install "ovkit[train]"`):
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
from ovkit import RTDETR
|
|
226
|
+
|
|
227
|
+
model = RTDETR("rtdetr-r18") # COCO weights, downloaded on first use
|
|
228
|
+
model.train(data="data.yaml", epochs=100) # the YOLO-format labels you already have
|
|
229
|
+
model.val(data="data.yaml").box.map50 # mAP50 / mAP50-95
|
|
230
|
+
model.export(half=True) # -> IR + labels.txt
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The three sizes are `rtdetr_r18` (20M, 46.4 COCO AP — what `detect` uses),
|
|
234
|
+
`rtdetr_r34` (31M, 48.9) and `rtdetr_r50` (43M, 53.1), mirrored like every
|
|
235
|
+
other model. Running them needs nothing but ovkit; `ovkit[train]` adds PyTorch
|
|
236
|
+
for fine-tuning:
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
Model("detect", "street.jpg") # r18 — keeps up with a webcam
|
|
240
|
+
Model("rtdetr_r50", "street.jpg") # when you want the accuracy instead
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
A fine-tuned model's exported IR drops straight into `Model("path/to/best.xml")` — the
|
|
244
|
+
`labels.txt` written next to it means your classes answer by name. CLI:
|
|
245
|
+
`ovkit train --data data.yaml`, `ovkit val`, `ovkit export`.
|
|
154
246
|
|
|
155
247
|
## Supported tasks
|
|
156
248
|
|
|
@@ -169,7 +261,7 @@ Every single-model task below runs end-to-end through the same 3 lines — swap
|
|
|
169
261
|
| LLM / STT (GenAI) | `llm`, `stt` | generated text | [llm.py](https://github.com/leeyunjai82/ovkit/blob/main/examples/llm.py) / [stt.py](https://github.com/leeyunjai82/ovkit/blob/main/examples/stt.py) |
|
|
170
262
|
| NLP / audio / time series | `qa`, `translation`, `noise_suppression`, `time_series` | tensors via `model.infer()` | [denoise_audio.py](https://github.com/leeyunjai82/ovkit/blob/main/examples/denoise_audio.py) |
|
|
171
263
|
|
|
172
|
-
The registry exposes **one well-tested model per capability (
|
|
264
|
+
The registry exposes **one well-tested model per capability (43 OpenVINO IR + 13 GenAI)**; the
|
|
173
265
|
[HF mirror](https://huggingface.co/leeyunjai/ovkit-models) hosts the full
|
|
174
266
|
Apache-2.0 OMZ set (other tiers, `int8`, `sparse` variants) — surfacing a
|
|
175
267
|
variant is a one-line edit ([catalog](https://leeyunjai82.github.io/ovkit/models.html)).
|
|
@@ -261,7 +353,7 @@ ovkit devices # available OpenVINO devices
|
|
|
261
353
|
```python
|
|
262
354
|
from ovkit.genai import pipeline
|
|
263
355
|
|
|
264
|
-
llm = pipeline("llm") #
|
|
356
|
+
llm = pipeline("llm") # qwen25_1_5b_instruct from the mirror
|
|
265
357
|
print(llm.generate("Explain OpenVINO in one sentence.", max_new_tokens=64))
|
|
266
358
|
|
|
267
359
|
stt = pipeline("stt") # whisper_base
|
|
@@ -285,6 +377,40 @@ automatically and results render with overlays.
|
|
|
285
377
|
|
|
286
378
|
</details>
|
|
287
379
|
|
|
380
|
+
## The model mirror
|
|
381
|
+
|
|
382
|
+
Everything ovkit downloads comes from one repository,
|
|
383
|
+
[leeyunjai/ovkit-models](https://huggingface.co/leeyunjai/ovkit-models) — one
|
|
384
|
+
copy for an offline site to take, one repository to keep alive. Models that
|
|
385
|
+
originate elsewhere (the RT-DETR sizes, Depth Anything V2 Small, U2-Net) are
|
|
386
|
+
copied in, and their home stays registered as the fallback.
|
|
387
|
+
|
|
388
|
+
To refresh it after adding a model, run the **Sync the model mirror** workflow
|
|
389
|
+
from the Actions tab (dry run by default; tick *upload* to copy). It needs an
|
|
390
|
+
`HF_TOKEN` repository secret with write access, set once. The same thing runs
|
|
391
|
+
locally:
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
export HF_TOKEN=hf_...
|
|
395
|
+
python scripts/sync_mirror.py # what is missing
|
|
396
|
+
python scripts/sync_mirror.py --upload # copy just that
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
It lists the target first and copies only what is absent, so re-running is safe.
|
|
400
|
+
|
|
401
|
+
The traffic goes the other way too. A mirror only ever added to fills up with
|
|
402
|
+
models that left the lineup and weights uploaded under an old path — and it is
|
|
403
|
+
the thing a school clones whole:
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
python scripts/audit_mirror.py # what no manifest references
|
|
407
|
+
python scripts/audit_mirror.py --prune # delete just that
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
A model's `README.md`, `LICENSE` and `labels.txt` stay as long as the model
|
|
411
|
+
does, and go with it when it leaves. Deletions are ordinary Hub commits, so
|
|
412
|
+
the history still has them.
|
|
413
|
+
|
|
288
414
|
## Adding a model
|
|
289
415
|
|
|
290
416
|
Models are data, not code — one manifest entry
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
# ovkit
|
|
6
6
|
|
|
7
|
-
**OpenVINO inference in
|
|
8
|
-
ready models — with `AUTO`/`NPU`/`GPU` devices,
|
|
7
|
+
**OpenVINO inference in one line.** One `Model` class, clean `Results`, and 19
|
|
8
|
+
composed capabilities over 68 ready models — with `AUTO`/`NPU`/`GPU` devices,
|
|
9
|
+
async throughput, INT8, and your own detector trainable in the same package.
|
|
9
10
|
|
|
10
11
|
[](https://github.com/leeyunjai82/ovkit/actions/workflows/ci.yml)
|
|
11
12
|
[](https://leeyunjai82.github.io/ovkit/)
|
|
@@ -38,6 +39,8 @@ for row in r.found: # [{'name': '사람', 'name_en': 'person'
|
|
|
38
39
|
|
|
39
40
|
for r in Model("track", 0): # webcam / video / "mic" -> a stream
|
|
40
41
|
print(r, r.elapsed_ms, r.device) # 2x person (#1, #4) 14.2 GPU
|
|
42
|
+
if not r.show("track"): # a window; q or Esc ends the loop
|
|
43
|
+
break
|
|
41
44
|
```
|
|
42
45
|
|
|
43
46
|
The same call also runs whole **capabilities** — several models chained into one
|
|
@@ -69,6 +72,8 @@ Python 3.10+. For development, install from source:
|
|
|
69
72
|
git clone https://github.com/leeyunjai82/ovkit.git && cd ovkit
|
|
70
73
|
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
71
74
|
pip install -e ".[dev]"
|
|
75
|
+
|
|
76
|
+
python scripts/check.py --fix # what CI runs: ruff, black, pytest
|
|
72
77
|
```
|
|
73
78
|
|
|
74
79
|
## Capabilities
|
|
@@ -93,6 +98,13 @@ camera index).
|
|
|
93
98
|
| `attention` | `1 person looking at: laptop` | gaze + object detection (ray-cast into the boxes) |
|
|
94
99
|
| `anonymize` | the picture with every face pixelated | face (and plate) detection + redaction |
|
|
95
100
|
| `face_match` | `('yunjai', 0.81)` — who this is | embedding + cosine matching against your gallery |
|
|
101
|
+
| `count` | `pencil 3 · cup 1` (optionally one kind only) | detection + per-kind tally |
|
|
102
|
+
| `posture` | `neck 32° — sit up (6s)` | pose + neck angle **over time** |
|
|
103
|
+
| `exercise` | `squat x 12 (down)` | pose + joint-angle hysteresis (squat, push-up) |
|
|
104
|
+
| `attendance` | `present 24/26` + `roll.csv` | face detection + roster matching |
|
|
105
|
+
| `teach` | your own classes, from example photos | embedding + cosine k-NN (5 modes) |
|
|
106
|
+
| `depth` | `nearest: bottom-left · 34% of the frame is close` + a colour map | Depth Anything V2 Small |
|
|
107
|
+
| `remove_background` | the subject, saved as a transparent PNG | U2-Net |
|
|
96
108
|
|
|
97
109
|
```python
|
|
98
110
|
from ovkit import Model, list_pipelines
|
|
@@ -104,8 +116,78 @@ Model("face_analyze", attributes=("age_gender",)) # configure what runs
|
|
|
104
116
|
```
|
|
105
117
|
|
|
106
118
|
Aliases: `ocr`, `anpr`, `blur`, `driver`, `describe`, `faces`, `people`, `vehicle`, `tracking`, `reid`.
|
|
107
|
-
|
|
108
|
-
|
|
119
|
+
|
|
120
|
+
## Teach your own AI (no GPU)
|
|
121
|
+
|
|
122
|
+
A Teachable Machine in five lines — an embedding model turns examples into
|
|
123
|
+
vectors, new inputs match the nearest ones. Five modes: `photo` (default),
|
|
124
|
+
`face` (expressions), `hand` (needs `ovkit[hand]`), `upper`, `body`:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
ai = Model("teach") # or Model("가르치기")
|
|
128
|
+
ai.learn("can", "photos/cans/") # a folder per thing to recognise
|
|
129
|
+
ai.learn("bottle", "photos/bottles/")
|
|
130
|
+
print(ai.guess("new_photo.jpg")) # ('can', 0.93)
|
|
131
|
+
print(ai.score("test_photos/")) # accuracy + what it confuses
|
|
132
|
+
ai.save("recycling") # -> Documents/ovkit/recycling.json
|
|
133
|
+
for r in ai.predict(0, stream=True): ... # webcam over YOUR classes
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Korean method names work too: `배우기`, `맞혀봐`, `점수`, `저장`. Collect
|
|
137
|
+
examples with the webcam: `from ovkit.pipelines.teach import collect; collect("가위", 30)`.
|
|
138
|
+
|
|
139
|
+
## Any model on the Hugging Face Hub
|
|
140
|
+
|
|
141
|
+
The Open Model Zoo is an archive; the models people want are on the Hub.
|
|
142
|
+
`optimum-intel` converts almost any of them to OpenVINO IR, so ovkit does not
|
|
143
|
+
have to mirror a model to serve it:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pip install "ovkit[hf]" # the converter — needed once
|
|
147
|
+
ovkit pull google/vit-base-patch16-224
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
Model("google/vit-base-patch16-224", "photo.jpg") # tabby 0.94
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Converting is the heavy half and happens once; what lands in the cache is a
|
|
155
|
+
plain OpenVINO model, so **running it afterwards needs nothing but ovkit** — a
|
|
156
|
+
classroom machine can be handed the cache and never see PyTorch. The pull also
|
|
157
|
+
writes the model's own class names and the normalisation its image processor
|
|
158
|
+
declares, so results read as answers and not as confident nonsense.
|
|
159
|
+
|
|
160
|
+
Tasks: image classification, zero-shot (CLIP) classification, feature
|
|
161
|
+
extraction. Detection and segmentation come from the registry instead
|
|
162
|
+
(`detect`, `segment`) — optimum-intel does not export those.
|
|
163
|
+
|
|
164
|
+
## Train your own detector
|
|
165
|
+
|
|
166
|
+
RT-DETR, trainable, Apache-2.0 end to end — an Ultralytics-style workflow with
|
|
167
|
+
no AGPL code or weights anywhere (`pip install "ovkit[train]"`):
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from ovkit import RTDETR
|
|
171
|
+
|
|
172
|
+
model = RTDETR("rtdetr-r18") # COCO weights, downloaded on first use
|
|
173
|
+
model.train(data="data.yaml", epochs=100) # the YOLO-format labels you already have
|
|
174
|
+
model.val(data="data.yaml").box.map50 # mAP50 / mAP50-95
|
|
175
|
+
model.export(half=True) # -> IR + labels.txt
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The three sizes are `rtdetr_r18` (20M, 46.4 COCO AP — what `detect` uses),
|
|
179
|
+
`rtdetr_r34` (31M, 48.9) and `rtdetr_r50` (43M, 53.1), mirrored like every
|
|
180
|
+
other model. Running them needs nothing but ovkit; `ovkit[train]` adds PyTorch
|
|
181
|
+
for fine-tuning:
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
Model("detect", "street.jpg") # r18 — keeps up with a webcam
|
|
185
|
+
Model("rtdetr_r50", "street.jpg") # when you want the accuracy instead
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
A fine-tuned model's exported IR drops straight into `Model("path/to/best.xml")` — the
|
|
189
|
+
`labels.txt` written next to it means your classes answer by name. CLI:
|
|
190
|
+
`ovkit train --data data.yaml`, `ovkit val`, `ovkit export`.
|
|
109
191
|
|
|
110
192
|
## Supported tasks
|
|
111
193
|
|
|
@@ -124,7 +206,7 @@ Every single-model task below runs end-to-end through the same 3 lines — swap
|
|
|
124
206
|
| LLM / STT (GenAI) | `llm`, `stt` | generated text | [llm.py](https://github.com/leeyunjai82/ovkit/blob/main/examples/llm.py) / [stt.py](https://github.com/leeyunjai82/ovkit/blob/main/examples/stt.py) |
|
|
125
207
|
| NLP / audio / time series | `qa`, `translation`, `noise_suppression`, `time_series` | tensors via `model.infer()` | [denoise_audio.py](https://github.com/leeyunjai82/ovkit/blob/main/examples/denoise_audio.py) |
|
|
126
208
|
|
|
127
|
-
The registry exposes **one well-tested model per capability (
|
|
209
|
+
The registry exposes **one well-tested model per capability (43 OpenVINO IR + 13 GenAI)**; the
|
|
128
210
|
[HF mirror](https://huggingface.co/leeyunjai/ovkit-models) hosts the full
|
|
129
211
|
Apache-2.0 OMZ set (other tiers, `int8`, `sparse` variants) — surfacing a
|
|
130
212
|
variant is a one-line edit ([catalog](https://leeyunjai82.github.io/ovkit/models.html)).
|
|
@@ -216,7 +298,7 @@ ovkit devices # available OpenVINO devices
|
|
|
216
298
|
```python
|
|
217
299
|
from ovkit.genai import pipeline
|
|
218
300
|
|
|
219
|
-
llm = pipeline("llm") #
|
|
301
|
+
llm = pipeline("llm") # qwen25_1_5b_instruct from the mirror
|
|
220
302
|
print(llm.generate("Explain OpenVINO in one sentence.", max_new_tokens=64))
|
|
221
303
|
|
|
222
304
|
stt = pipeline("stt") # whisper_base
|
|
@@ -240,6 +322,40 @@ automatically and results render with overlays.
|
|
|
240
322
|
|
|
241
323
|
</details>
|
|
242
324
|
|
|
325
|
+
## The model mirror
|
|
326
|
+
|
|
327
|
+
Everything ovkit downloads comes from one repository,
|
|
328
|
+
[leeyunjai/ovkit-models](https://huggingface.co/leeyunjai/ovkit-models) — one
|
|
329
|
+
copy for an offline site to take, one repository to keep alive. Models that
|
|
330
|
+
originate elsewhere (the RT-DETR sizes, Depth Anything V2 Small, U2-Net) are
|
|
331
|
+
copied in, and their home stays registered as the fallback.
|
|
332
|
+
|
|
333
|
+
To refresh it after adding a model, run the **Sync the model mirror** workflow
|
|
334
|
+
from the Actions tab (dry run by default; tick *upload* to copy). It needs an
|
|
335
|
+
`HF_TOKEN` repository secret with write access, set once. The same thing runs
|
|
336
|
+
locally:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
export HF_TOKEN=hf_...
|
|
340
|
+
python scripts/sync_mirror.py # what is missing
|
|
341
|
+
python scripts/sync_mirror.py --upload # copy just that
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
It lists the target first and copies only what is absent, so re-running is safe.
|
|
345
|
+
|
|
346
|
+
The traffic goes the other way too. A mirror only ever added to fills up with
|
|
347
|
+
models that left the lineup and weights uploaded under an old path — and it is
|
|
348
|
+
the thing a school clones whole:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
python scripts/audit_mirror.py # what no manifest references
|
|
352
|
+
python scripts/audit_mirror.py --prune # delete just that
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
A model's `README.md`, `LICENSE` and `labels.txt` stay as long as the model
|
|
356
|
+
does, and go with it when it leaves. Deletions are ordinary Hub commits, so
|
|
357
|
+
the history still has them.
|
|
358
|
+
|
|
243
359
|
## Adding a model
|
|
244
360
|
|
|
245
361
|
Models are data, not code — one manifest entry
|
|
@@ -15,6 +15,7 @@ pip install -r examples/requirements.txt # fastapi / uvicorn (web demos only)
|
|
|
15
15
|
| [`pose.py`](pose.py) | `pose` | keypoints/skeleton |
|
|
16
16
|
| [`classify.py`](classify.py) | `classify` | top-5 classes |
|
|
17
17
|
| [`ocr.py`](ocr.py) | `text_recognition` | decoded text |
|
|
18
|
+
| [`speak.py`](speak.py) | `읽어주기` | text read aloud, saved as a WAV |
|
|
18
19
|
| [`super_resolution.py`](super_resolution.py) | `super_resolution` | upscaled image |
|
|
19
20
|
| [`background_matting.py`](background_matting.py) | `background_matting_mobilenetv2` | cut the subject out (needs frame + empty-scene photo) |
|
|
20
21
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ovkit"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "A simple Python inference API for OpenVINO: one import, one Model class, clean Results — with AUTO/NPU, async, and INT8."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -33,7 +33,18 @@ dependencies = [
|
|
|
33
33
|
genai = ["openvino-genai", "optimum-intel"]
|
|
34
34
|
anomaly = ["anomalib"]
|
|
35
35
|
quant = ["nncf"]
|
|
36
|
-
|
|
36
|
+
train = ["rtdetr[train]>=0.6"]
|
|
37
|
+
hand = ["mediapipe"]
|
|
38
|
+
hf = ["optimum-intel[openvino]>=1.20", "transformers>=4.40"]
|
|
39
|
+
all = [
|
|
40
|
+
"openvino-genai",
|
|
41
|
+
"optimum-intel[openvino]>=1.20",
|
|
42
|
+
"transformers>=4.40",
|
|
43
|
+
"anomalib",
|
|
44
|
+
"nncf",
|
|
45
|
+
"mediapipe",
|
|
46
|
+
"rtdetr[train]>=0.6",
|
|
47
|
+
]
|
|
37
48
|
dev = ["pytest>=7", "ruff", "black"]
|
|
38
49
|
|
|
39
50
|
[project.urls]
|
|
@@ -24,6 +24,7 @@ whatever describes what was found. :func:`list_pipelines` shows them all.
|
|
|
24
24
|
|
|
25
25
|
from __future__ import annotations
|
|
26
26
|
|
|
27
|
+
from . import hub
|
|
27
28
|
from .core.errors import (
|
|
28
29
|
ConversionError,
|
|
29
30
|
DownloadError,
|
|
@@ -40,10 +41,32 @@ from .core.registry import list_models
|
|
|
40
41
|
from .core.results import Boxes, Keypoints, Masks, Probs, Results
|
|
41
42
|
from .pipelines import Pipeline, list_pipelines
|
|
42
43
|
|
|
43
|
-
__version__ = "0.
|
|
44
|
+
__version__ = "0.4.0"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def __getattr__(name: str):
|
|
48
|
+
"""``ovkit.RTDETR`` re-exports the standalone ``rtdetr`` package.
|
|
49
|
+
|
|
50
|
+
Training lives in its own project (Apache-2.0 RT-DETR with an
|
|
51
|
+
Ultralytics-style API); ovkit re-exports it so a trained detector and the
|
|
52
|
+
capabilities sit behind one import. ``pip install "ovkit[train]"``.
|
|
53
|
+
"""
|
|
54
|
+
if name == "RTDETR":
|
|
55
|
+
try:
|
|
56
|
+
from rtdetr import RTDETR
|
|
57
|
+
except ImportError as exc:
|
|
58
|
+
raise ImportError(
|
|
59
|
+
'Training needs the rtdetr package: pip install "ovkit[train]"\n'
|
|
60
|
+
'Its exported IR runs here either way: Model("your-model.xml").'
|
|
61
|
+
) from exc
|
|
62
|
+
return RTDETR
|
|
63
|
+
raise AttributeError(f"module 'ovkit' has no attribute {name!r}")
|
|
64
|
+
|
|
44
65
|
|
|
45
66
|
__all__ = [
|
|
46
67
|
"Model",
|
|
68
|
+
"hub",
|
|
69
|
+
"RTDETR",
|
|
47
70
|
"Pipeline",
|
|
48
71
|
"list_pipelines",
|
|
49
72
|
"Results",
|