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.
Files changed (80) hide show
  1. {ovkit-0.2.0 → ovkit-0.4.0}/.gitignore +26 -0
  2. {ovkit-0.2.0 → ovkit-0.4.0}/PKG-INFO +134 -8
  3. {ovkit-0.2.0 → ovkit-0.4.0}/README.md +122 -6
  4. {ovkit-0.2.0 → ovkit-0.4.0}/examples/README.md +1 -0
  5. {ovkit-0.2.0 → ovkit-0.4.0}/pyproject.toml +13 -2
  6. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/__init__.py +24 -1
  7. ovkit-0.4.0/src/ovkit/__main__.py +299 -0
  8. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/constants.py +29 -0
  9. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/convert.py +2 -0
  10. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/download.py +57 -2
  11. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/errors.py +21 -0
  12. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/i18n.py +14 -0
  13. ovkit-0.4.0/src/ovkit/core/maths.py +32 -0
  14. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/model.py +28 -16
  15. ovkit-0.4.0/src/ovkit/core/progress.py +150 -0
  16. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/registry.py +62 -11
  17. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/results.py +82 -5
  18. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/gui/app.py +89 -5
  19. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/gui/controller.py +261 -23
  20. ovkit-0.4.0/src/ovkit/hub.py +225 -0
  21. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/image/ops.py +0 -12
  22. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/manifests/aliases.yaml +11 -1
  23. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/manifests/labels.yaml +12 -0
  24. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/manifests/models.yaml +17 -24
  25. ovkit-0.4.0/src/ovkit/manifests/ocr_ko.yaml +36 -0
  26. ovkit-0.4.0/src/ovkit/manifests/revived.yaml +119 -0
  27. ovkit-0.4.0/src/ovkit/manifests/rtdetr.yaml +84 -0
  28. ovkit-0.4.0/src/ovkit/manifests/tts.yaml +66 -0
  29. ovkit-0.4.0/src/ovkit/manifests/vision_extra.yaml +40 -0
  30. ovkit-0.4.0/src/ovkit/manifests/zoo.yaml +83 -0
  31. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/__init__.py +26 -1
  32. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/analyze.py +10 -10
  33. {ovkit-0.2.0/src/ovkit/solutions → ovkit-0.4.0/src/ovkit/pipelines}/anomaly.py +11 -5
  34. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/attention.py +3 -3
  35. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/base.py +12 -4
  36. ovkit-0.4.0/src/ovkit/pipelines/classroom.py +400 -0
  37. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/gaze.py +5 -5
  38. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/plates.py +2 -2
  39. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/privacy.py +2 -2
  40. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/reid.py +9 -9
  41. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/scene.py +16 -6
  42. ovkit-0.4.0/src/ovkit/pipelines/speak.py +331 -0
  43. ovkit-0.4.0/src/ovkit/pipelines/teach.py +454 -0
  44. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/temporal.py +4 -8
  45. ovkit-0.4.0/src/ovkit/pipelines/text.py +132 -0
  46. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/pipelines/tracking.py +3 -3
  47. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/__init__.py +5 -0
  48. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/audio.py +10 -7
  49. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/base.py +2 -5
  50. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/classify.py +2 -8
  51. ovkit-0.4.0/src/ovkit/recognize/depth.py +97 -0
  52. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/detect.py +4 -1
  53. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/generic.py +2 -6
  54. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/ocr.py +30 -3
  55. ovkit-0.2.0/src/ovkit/__main__.py +0 -181
  56. ovkit-0.2.0/src/ovkit/face/__init__.py +0 -8
  57. ovkit-0.2.0/src/ovkit/face/analyzer.py +0 -7
  58. ovkit-0.2.0/src/ovkit/pipelines/text.py +0 -81
  59. ovkit-0.2.0/src/ovkit/solutions/__init__.py +0 -14
  60. ovkit-0.2.0/src/ovkit/solutions/ocr.py +0 -7
  61. ovkit-0.2.0/src/ovkit/solutions/reid.py +0 -7
  62. ovkit-0.2.0/src/ovkit/solutions/tracking.py +0 -7
  63. {ovkit-0.2.0 → ovkit-0.4.0}/LICENSE +0 -0
  64. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/audio/__init__.py +0 -0
  65. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/audio/ops.py +0 -0
  66. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/audio/plot.py +0 -0
  67. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/__init__.py +0 -0
  68. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/backend.py +0 -0
  69. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/core/tasks.py +0 -0
  70. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/data/imagenet1000.txt +0 -0
  71. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/genai/__init__.py +0 -0
  72. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/genai/pipelines.py +0 -0
  73. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/gui/__init__.py +0 -0
  74. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/image/__init__.py +0 -0
  75. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/manifests/genai.yaml +0 -0
  76. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/manifests/omz.yaml +0 -0
  77. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/py.typed +0 -0
  78. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/face.py +0 -0
  79. {ovkit-0.2.0 → ovkit-0.4.0}/src/ovkit/recognize/pose.py +0 -0
  80. {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.2.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 3 lines.** One `Model` class, clean `Results`, 30+
53
- ready models — with `AUTO`/`NPU`/`GPU` devices, async throughput, and INT8.
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
  [![CI](https://github.com/leeyunjai82/ovkit/actions/workflows/ci.yml/badge.svg)](https://github.com/leeyunjai82/ovkit/actions/workflows/ci.yml)
56
67
  [![Docs](https://img.shields.io/badge/docs-github.io-0a7d8c)](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
- `ovkit capabilities` prints the list; **`ovkit gui` opens a window** where you can
153
- click through them against your webcam or a picture.
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 (35 total)**; the
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") # tinyllama_chat from the mirror
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 3 lines.** One `Model` class, clean `Results`, 30+
8
- ready models — with `AUTO`/`NPU`/`GPU` devices, async throughput, and INT8.
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
  [![CI](https://github.com/leeyunjai82/ovkit/actions/workflows/ci.yml/badge.svg)](https://github.com/leeyunjai82/ovkit/actions/workflows/ci.yml)
11
12
  [![Docs](https://img.shields.io/badge/docs-github.io-0a7d8c)](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
- `ovkit capabilities` prints the list; **`ovkit gui` opens a window** where you can
108
- click through them against your webcam or a picture.
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 (35 total)**; the
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") # tinyllama_chat from the mirror
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.2.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
- all = ["openvino-genai", "optimum-intel", "anomalib", "nncf"]
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.2.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",