docs: notices, licences and source/ after the plate detector swap (D718)
Browse filesLicence files, notices, model cards and the source/ folder (AGPL Corresponding Source scripts) from the CamStack repository. Only files that differ from the Hub are included. No weight file is touched.
- LICENSES/AGPL-3.0.txt +1 -1
- LICENSES/Apache-2.0.txt +2 -2
- LICENSES/NOTICE-open-lpr.txt +44 -0
- README.md +10 -7
- THIRD_PARTY_MODELS.md +4 -17
- source/README.md +4 -25
- source/build-camstack-models.py +19 -112
LICENSES/AGPL-3.0.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
Source: https://www.gnu.org/licenses/agpl-3.0.txt
|
| 2 |
Fetched: 2026-09-26 (verbatim below the line; nothing edited)
|
| 3 |
-
Applies to: Ultralytics weights: objectDetection/yolo26, objectDetection/yolov9, segmentation/yolo26-seg
|
| 4 |
Note: byte-identical to https://raw.githubusercontent.com/ultralytics/ultralytics/main/LICENSE, fetched the same day.
|
| 5 |
--------------------------------------------------------------------------------
|
| 6 |
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
|
|
| 1 |
Source: https://www.gnu.org/licenses/agpl-3.0.txt
|
| 2 |
Fetched: 2026-09-26 (verbatim below the line; nothing edited)
|
| 3 |
+
Applies to: Ultralytics weights: objectDetection/yolo26, objectDetection/yolov9, segmentation/yolo26-seg
|
| 4 |
Note: byte-identical to https://raw.githubusercontent.com/ultralytics/ultralytics/main/LICENSE, fetched the same day.
|
| 5 |
--------------------------------------------------------------------------------
|
| 6 |
GNU AFFERO GENERAL PUBLIC LICENSE
|
LICENSES/Apache-2.0.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
Source: https://www.apache.org/licenses/LICENSE-2.0.txt
|
| 2 |
Fetched: 2026-09-26 (verbatim below the line; nothing edited)
|
| 3 |
-
Applies to: RF-DETR (Roboflow), AuraFace v1 (fal.ai), U-2-Net (Xuebin Qin et al.), YAMNet (tensorflow/models), AIY Vision Birds V1 and the Coral Edge TPU models (google-coral/test_data), timm EfficientNet-Lite0, Qwen2.5 / Qwen3-VL / Qwen3.6
|
| 4 |
-
Note: none of those upstream repositories carries a NOTICE file (checked 2026-09-26 via the GitHub contents API and the HF file list), so Apache-2.0 section 4(d) adds nothing to reproduce.
|
| 5 |
--------------------------------------------------------------------------------
|
| 6 |
|
| 7 |
Apache License
|
|
|
|
| 1 |
Source: https://www.apache.org/licenses/LICENSE-2.0.txt
|
| 2 |
Fetched: 2026-09-26 (verbatim below the line; nothing edited)
|
| 3 |
+
Applies to: RF-DETR (Roboflow), AuraFace v1 (fal.ai), U-2-Net (Xuebin Qin et al.), YAMNet (tensorflow/models), AIY Vision Birds V1 and the Coral Edge TPU models (google-coral/test_data), timm EfficientNet-Lite0, Qwen2.5 / Qwen3-VL / Qwen3.6, open-lpr YOLOX-tiny plate detector (faisalthaheem/open-lpr-models, added 2026-10-06)
|
| 4 |
+
Note: none of those upstream repositories carries a NOTICE file (checked 2026-09-26 via the GitHub contents API and the HF file list), so Apache-2.0 section 4(d) adds nothing to reproduce, except open-lpr models, whose NOTICE is reproduced in NOTICE-open-lpr.txt. Its LICENSE is this text (checked 2026-10-06).
|
| 5 |
--------------------------------------------------------------------------------
|
| 6 |
|
| 7 |
Apache License
|
LICENSES/NOTICE-open-lpr.txt
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Source: https://huggingface.co/faisalthaheem/open-lpr-models/resolve/aed0df35ec2891c5a90437041ebeadb592cc516b/NOTICE
|
| 2 |
+
Fetched: 2026-10-06 (verbatim below the line; nothing edited)
|
| 3 |
+
Applies to: plateDetection/yolox-tiny-plate (open-lpr YOLOX-tiny plate detector, Apache-2.0; licence text: Apache-2.0.txt). Apache-2.0 section 4(d) requires this NOTICE to accompany redistribution.
|
| 4 |
+
--------------------------------------------------------------------------------
|
| 5 |
+
open-lpr models
|
| 6 |
+
Copyright the open-lpr contributors
|
| 7 |
+
|
| 8 |
+
This product includes software developed by third parties. The artifacts in
|
| 9 |
+
this repository are NOT all ours; per-artifact provenance is recorded in
|
| 10 |
+
manifest.json and summarised below.
|
| 11 |
+
|
| 12 |
+
--------------------------------------------------------------------------------
|
| 13 |
+
plate_yolox_tiny_640.onnx and best_ckpt.pth
|
| 14 |
+
--------------------------------------------------------------------------------
|
| 15 |
+
Produced by this project. Architecture from YOLOX (Megvii-BaseDetection/YOLOX),
|
| 16 |
+
which is licensed Apache-2.0 for both its code and its same-repository release
|
| 17 |
+
weights. Pretrained from COCO (yolox_tiny.pth), then fine-tuned on an annotated
|
| 18 |
+
licence-plate corpus plus community-contributed images pseudo-labelled by a
|
| 19 |
+
teacher model.
|
| 20 |
+
|
| 21 |
+
The training imagery is not published. It consists of user-contributed
|
| 22 |
+
photographs containing real licence plates, faces and vehicles, uploaded without
|
| 23 |
+
consent obtained. Publishing it would create a searchable index of real vehicle
|
| 24 |
+
registration numbers.
|
| 25 |
+
|
| 26 |
+
Ultralytics YOLO weights are licensed AGPL-3.0 and are deliberately NOT used,
|
| 27 |
+
redistributed, or derived from anywhere in this project.
|
| 28 |
+
|
| 29 |
+
--------------------------------------------------------------------------------
|
| 30 |
+
plate_ocr_ppocrv5_mobile.onnx and plate_ocr_dict.json
|
| 31 |
+
--------------------------------------------------------------------------------
|
| 32 |
+
NOT produced by this project. These are conversions of an upstream recognition
|
| 33 |
+
model from PaddleOCR (https://github.com/PaddlePaddle/PaddleOCR), which is
|
| 34 |
+
licensed under Apache-2.0.
|
| 35 |
+
|
| 36 |
+
The specific upstream release was never recorded and cannot be recovered from
|
| 37 |
+
the artifacts: the ONNX graph carries no producer, producer_version or
|
| 38 |
+
doc_string, and no PaddlePaddle installation, wheel or shell history survives on
|
| 39 |
+
the build host. The Apache-2.0 licence is therefore asserted at project level
|
| 40 |
+
and is NOT pinned to a verified release.
|
| 41 |
+
|
| 42 |
+
These two files are a MATCHED PAIR and must be used together. Decoding assumes
|
| 43 |
+
len(dictionary) + 2 output classes; substituting either without the other
|
| 44 |
+
produces silently incorrect text rather than an error.
|
README.md
CHANGED
|
@@ -18,7 +18,7 @@ library_name: onnx
|
|
| 18 |
|
| 19 |
This repository is a **mirror of third-party models** converted by
|
| 20 |
[CamStack](https://camstack-docs.zentik.app) into the formats its inference
|
| 21 |
-
engines run (ONNX, OpenVINO IR, CoreML
|
| 22 |
Each folder stays under the licence of the model it was made from, stated in the
|
| 23 |
folder's `README.md` and in the table below, with the full text in
|
| 24 |
[`LICENSES/`](LICENSES). The repository as a whole has no single licence; see
|
|
@@ -35,9 +35,8 @@ fetched from other repositories, with the attributions each licence requires.
|
|
| 35 |
|
| 36 |
## AGPL-3.0 models (Ultralytics)
|
| 37 |
|
| 38 |
-
`objectDetection/yolo26`, `objectDetection/yolov9`
|
| 39 |
-
|
| 40 |
-
Ultralytics weights under **AGPL-3.0** ([`LICENSES/AGPL-3.0.txt`](LICENSES/AGPL-3.0.txt)).
|
| 41 |
Their Corresponding Source is set out in
|
| 42 |
[Source offer for the AGPL-3.0 weights](THIRD_PARTY_MODELS.md#source-offer-for-the-agpl-30-weights).
|
| 43 |
|
|
@@ -50,8 +49,12 @@ This **may not be used commercially**. See the folder's card.
|
|
| 50 |
|
| 51 |
## Required attributions
|
| 52 |
|
| 53 |
-
- **Ultralytics YOLO26, YOLOv9
|
| 54 |
architecture: Chien-Yao Wang, I-Hau Yeh, Hong-Yuan Mark Liao, GPL-3.0.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
- **fast-plate-ocr**: Copyright (c) 2024 ankandrew, MIT.
|
| 56 |
- **Open Images V7** (animal-classifier training data): annotations © Google
|
| 57 |
LLC, CC BY 4.0.
|
|
@@ -77,7 +80,7 @@ marks an entry kept resolvable for a saved selection but no longer offered.
|
|
| 77 |
| `packageDetection/rfdetr-package` | `rfdetr-package` (default) | Roboflow RF-DETR | Apache-2.0 | [`Apache-2.0.txt`](LICENSES/Apache-2.0.txt) | RF-DETR-M fine-tuned to 2 classes at 576; exported to ONNX, OpenVINO IR and CoreML; training checkpoint .pth also published |
|
| 78 |
| `faceDetection/yunet` | `yunet-2023mar` (default) | OpenCV Zoo YuNet | MIT | [`MIT-yunet-reference.txt`](LICENSES/MIT-yunet-reference.txt), [`BSD-3-Clause.txt`](LICENSES/BSD-3-Clause.txt) | input contract baked into the graph (the pool RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather); internal tensor names cleared in the OpenVINO IR; ONNX fp32, OpenVINO IR fp32, CoreML fp16 (scripts/build-yunet-model.py) |
|
| 79 |
| `faceRecognition/auraface` | `auraface-r100` (default) | fal.ai AuraFace v1 (glintr100.onnx only) | Apache-2.0 | [`Apache-2.0.txt`](LICENSES/Apache-2.0.txt) | input scaling y = 2x - 1 baked ahead of the first Conv (scripts/build-auraface-model.py); converted to OpenVINO IR and CoreML FP16 |
|
| 80 |
-
| `plateDetection/
|
| 81 |
| `plateRecognition/cct-s-v2-global` | `cct-s-v2-global` (default) | ankandrew/fast-plate-ocr | MIT | [`MIT-fast-plate-ocr.txt`](LICENSES/MIT-fast-plate-ocr.txt) | input head removed (recorded in the ONNX doc_string); converted to OpenVINO IR and CoreML |
|
| 82 |
| `animalClassification/animal-classifier` | `animal-classifier` (default) | timm EfficientNet-Lite0, fine-tuned by CamStack | Apache-2.0 | [`Apache-2.0.txt`](LICENSES/Apache-2.0.txt) | fine-tuned by CamStack on the Open Images V7 animal subset (8 classes); exported to ONNX, OpenVINO IR and CoreML |
|
| 83 |
| `animalClassification/bird-classifier` | `bird-classifier` (default) | Google AIY Vision birds_V1 | Apache-2.0 | [`Apache-2.0.txt`](LICENSES/Apache-2.0.txt) | converted from TensorFlow with tf2onnx; OpenVINO IR and CoreML derived from it |
|
|
@@ -90,6 +93,6 @@ marks an entry kept resolvable for a saved selection but no longer offered.
|
|
| 90 |
|
| 91 |
This mirror holds only the models CamStack offers. Models it no longer offers
|
| 92 |
(SCRFD, ArcFace, Inception-ResNet, VGG-English, MobileCLIP S1/S2,
|
| 93 |
-
YOLOv8n-package, SSDLite MobileDet, and earlier a third-party face, PaddleOCR,
|
| 94 |
animals-10, NABirds and EfficientNet folders) were removed, and the repository
|
| 95 |
history was squashed on 2026-10-06.
|
|
|
|
| 18 |
|
| 19 |
This repository is a **mirror of third-party models** converted by
|
| 20 |
[CamStack](https://camstack-docs.zentik.app) into the formats its inference
|
| 21 |
+
engines run (ONNX, OpenVINO IR, CoreML). **Nothing here is relicensed.**
|
| 22 |
Each folder stays under the licence of the model it was made from, stated in the
|
| 23 |
folder's `README.md` and in the table below, with the full text in
|
| 24 |
[`LICENSES/`](LICENSES). The repository as a whole has no single licence; see
|
|
|
|
| 35 |
|
| 36 |
## AGPL-3.0 models (Ultralytics)
|
| 37 |
|
| 38 |
+
`objectDetection/yolo26`, `objectDetection/yolov9` and `segmentation/yolo26-seg`
|
| 39 |
+
hold Ultralytics weights under **AGPL-3.0** ([`LICENSES/AGPL-3.0.txt`](LICENSES/AGPL-3.0.txt)).
|
|
|
|
| 40 |
Their Corresponding Source is set out in
|
| 41 |
[Source offer for the AGPL-3.0 weights](THIRD_PARTY_MODELS.md#source-offer-for-the-agpl-30-weights).
|
| 42 |
|
|
|
|
| 49 |
|
| 50 |
## Required attributions
|
| 51 |
|
| 52 |
+
- **Ultralytics YOLO26, YOLOv9**: © Ultralytics, AGPL-3.0. YOLOv9
|
| 53 |
architecture: Chien-Yao Wang, I-Hau Yeh, Hong-Yuan Mark Liao, GPL-3.0.
|
| 54 |
+
- **open-lpr models** (plate detector): Copyright the open-lpr contributors,
|
| 55 |
+
Apache-2.0; YOLOX architecture and COCO pretraining by Megvii, Apache-2.0.
|
| 56 |
+
The upstream `NOTICE` is in the folder and in
|
| 57 |
+
[`LICENSES/NOTICE-open-lpr.txt`](LICENSES/NOTICE-open-lpr.txt).
|
| 58 |
- **fast-plate-ocr**: Copyright (c) 2024 ankandrew, MIT.
|
| 59 |
- **Open Images V7** (animal-classifier training data): annotations © Google
|
| 60 |
LLC, CC BY 4.0.
|
|
|
|
| 80 |
| `packageDetection/rfdetr-package` | `rfdetr-package` (default) | Roboflow RF-DETR | Apache-2.0 | [`Apache-2.0.txt`](LICENSES/Apache-2.0.txt) | RF-DETR-M fine-tuned to 2 classes at 576; exported to ONNX, OpenVINO IR and CoreML; training checkpoint .pth also published |
|
| 81 |
| `faceDetection/yunet` | `yunet-2023mar` (default) | OpenCV Zoo YuNet | MIT | [`MIT-yunet-reference.txt`](LICENSES/MIT-yunet-reference.txt), [`BSD-3-Clause.txt`](LICENSES/BSD-3-Clause.txt) | input contract baked into the graph (the pool RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather); internal tensor names cleared in the OpenVINO IR; ONNX fp32, OpenVINO IR fp32, CoreML fp16 (scripts/build-yunet-model.py) |
|
| 82 |
| `faceRecognition/auraface` | `auraface-r100` (default) | fal.ai AuraFace v1 (glintr100.onnx only) | Apache-2.0 | [`Apache-2.0.txt`](LICENSES/Apache-2.0.txt) | input scaling y = 2x - 1 baked ahead of the first Conv (scripts/build-auraface-model.py); converted to OpenVINO IR and CoreML FP16 |
|
| 83 |
+
| `plateDetection/yolox-tiny-plate` | `yolox-tiny-plate` (default) | open-lpr models (faisalthaheem/open-lpr-models), YOLOX-tiny | Apache-2.0 | [`Apache-2.0.txt`](LICENSES/Apache-2.0.txt) | input contract baked into the graph (static 1x3x640x640 RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather) and the output reshaped to the YOLOv8 layout (cx, cy, w, h, obj*cls); ONNX fp32, OpenVINO IR fp16, CoreML fp16 (scripts/build-yolox-plate-model.py) |
|
| 84 |
| `plateRecognition/cct-s-v2-global` | `cct-s-v2-global` (default) | ankandrew/fast-plate-ocr | MIT | [`MIT-fast-plate-ocr.txt`](LICENSES/MIT-fast-plate-ocr.txt) | input head removed (recorded in the ONNX doc_string); converted to OpenVINO IR and CoreML |
|
| 85 |
| `animalClassification/animal-classifier` | `animal-classifier` (default) | timm EfficientNet-Lite0, fine-tuned by CamStack | Apache-2.0 | [`Apache-2.0.txt`](LICENSES/Apache-2.0.txt) | fine-tuned by CamStack on the Open Images V7 animal subset (8 classes); exported to ONNX, OpenVINO IR and CoreML |
|
| 86 |
| `animalClassification/bird-classifier` | `bird-classifier` (default) | Google AIY Vision birds_V1 | Apache-2.0 | [`Apache-2.0.txt`](LICENSES/Apache-2.0.txt) | converted from TensorFlow with tf2onnx; OpenVINO IR and CoreML derived from it |
|
|
|
|
| 93 |
|
| 94 |
This mirror holds only the models CamStack offers. Models it no longer offers
|
| 95 |
(SCRFD, ArcFace, Inception-ResNet, VGG-English, MobileCLIP S1/S2,
|
| 96 |
+
YOLOv8n-package, SSDLite MobileDet, the YOLOv8n plate detector, and earlier a third-party face, PaddleOCR,
|
| 97 |
animals-10, NABirds and EfficientNet folders) were removed, and the repository
|
| 98 |
history was squashed on 2026-10-06.
|
THIRD_PARTY_MODELS.md
CHANGED
|
@@ -36,7 +36,7 @@ under [Non-commercial models](#non-commercial-models).
|
|
| 36 |
- RF-DETR © Roboflow, Apache-2.0. https://github.com/roboflow/rf-detr
|
| 37 |
- YuNet weights Copyright (c) 2020 Shiqi Yu, MIT. Training code: ShiqiYu/libfacedetection.train, BSD-3-Clause. https://github.com/opencv/opencv_zoo/tree/main/models/face_detection_yunet
|
| 38 |
- AuraFace v1 by fal.ai, Apache-2.0. https://huggingface.co/fal/AuraFace-v1
|
| 39 |
-
-
|
| 40 |
- fast-plate-ocr, MIT License, copyright (c) 2024 ankandrew (full notice: licenses/models/MIT-fast-plate-ocr.txt). https://github.com/ankandrew/fast-plate-ocr
|
| 41 |
- EfficientNet-Lite0 via timm (Ross Wightman), Apache-2.0. Open Images V7 annotations © Google LLC, CC BY 4.0; images listed by Google as CC BY 2.0, without warranty. https://github.com/huggingface/pytorch-image-models
|
| 42 |
- torchvision MobileNetV3, BSD-3-Clause. Trained on the Vehicle classification dataset by DrBimmer (https://huggingface.co/datasets/DrBimmer/vehicle-classification), CC BY-NC 4.0. https://github.com/pytorch/vision
|
|
@@ -62,7 +62,7 @@ Training data under attribution terms:
|
|
| 62 |
cards link here instead of repeating it.
|
| 63 |
-->
|
| 64 |
|
| 65 |
-
The folders `objectDetection/yolo26`, `objectDetection/yolov9`
|
| 66 |
modified by CamStack and distributed under AGPL-3.0. Their Corresponding Source
|
| 67 |
is published in the mirror itself, in its **`source/`** folder,
|
| 68 |
**https://huggingface.co/camstack/camstack-models/tree/main/source**, together
|
|
@@ -70,15 +70,7 @@ with the upstream checkpoints it starts from:
|
|
| 70 |
|
| 71 |
1. **The upstream checkpoints.** Ultralytics publishes
|
| 72 |
`yolo26{n,s,m,l,x}.pt`, `yolov9{t,s,m,c}.pt` and `yolo26{n,s,m}-seg.pt` at
|
| 73 |
-
https://github.com/ultralytics/assets/releases.
|
| 74 |
-
third-party YOLOv8n licence-plate fine-tunes, neither trained by CamStack:
|
| 75 |
-
its ONNX file is `best.onnx` of
|
| 76 |
-
https://huggingface.co/ml-debi/yolov8-license-plate-detection (SHA-256
|
| 77 |
-
`85d23628…15bd50`, hosted unmodified), and its CoreML and TFLite files are
|
| 78 |
-
exported from `best.pt` of
|
| 79 |
-
https://huggingface.co/Koushim/yolov8-license-plate-detection. Both cards
|
| 80 |
-
say MIT; the Ultralytics base weights keep both AGPL-3.0, as their own
|
| 81 |
-
metadata states.
|
| 82 |
2. **The build script** in `source/`: `build-camstack-models.py`, with
|
| 83 |
`convert_lib.py` (the ONNX to OpenVINO / INT8 converter it calls, the same
|
| 84 |
file CamStack's Model Studio runs) and `requirements.txt`. It holds one
|
|
@@ -106,11 +98,6 @@ Limits of a rebuild:
|
|
| 106 |
- **INT8 calibration.** An IR records how many images calibrated it, not
|
| 107 |
which ones. Calibrating on the same COCO images gives the documented build;
|
| 108 |
a different set gives an equivalent, not bit-identical, INT8 model.
|
| 109 |
-
- **`yolov8n-plate` ONNX has no checkpoint.** Its author published only the
|
| 110 |
-
ONNX file, so that file is the preferred form of the work that exists, and
|
| 111 |
-
CamStack redistributes it unmodified. CamStack's modification is the
|
| 112 |
-
OpenVINO conversion of it, which the script reproduces. The training data of
|
| 113 |
-
both plate fine-tunes is not published.
|
| 114 |
|
| 115 |
## Non-commercial models
|
| 116 |
|
|
@@ -159,7 +146,7 @@ entry kept resolvable for a saved selection but no longer offered.
|
|
| 159 |
|
| 160 |
| Catalog ids | Upstream | Code / weights | Training data | CamStack changes | Downloaded from |
|
| 161 |
| --- | --- | --- | --- | --- | --- |
|
| 162 |
-
| `
|
| 163 |
|
| 164 |
### Licence plate reading
|
| 165 |
|
|
|
|
| 36 |
- RF-DETR © Roboflow, Apache-2.0. https://github.com/roboflow/rf-detr
|
| 37 |
- YuNet weights Copyright (c) 2020 Shiqi Yu, MIT. Training code: ShiqiYu/libfacedetection.train, BSD-3-Clause. https://github.com/opencv/opencv_zoo/tree/main/models/face_detection_yunet
|
| 38 |
- AuraFace v1 by fal.ai, Apache-2.0. https://huggingface.co/fal/AuraFace-v1
|
| 39 |
+
- open-lpr models, Copyright the open-lpr contributors, Apache-2.0 (https://huggingface.co/faisalthaheem/open-lpr-models, tag detector-2026.10.1). YOLOX architecture and COCO pretraining by Megvii (https://github.com/Megvii-BaseDetection/YOLOX), Apache-2.0. The upstream NOTICE is reproduced verbatim (`NOTICE-open-lpr.txt`). https://huggingface.co/faisalthaheem/open-lpr-models
|
| 40 |
- fast-plate-ocr, MIT License, copyright (c) 2024 ankandrew (full notice: licenses/models/MIT-fast-plate-ocr.txt). https://github.com/ankandrew/fast-plate-ocr
|
| 41 |
- EfficientNet-Lite0 via timm (Ross Wightman), Apache-2.0. Open Images V7 annotations © Google LLC, CC BY 4.0; images listed by Google as CC BY 2.0, without warranty. https://github.com/huggingface/pytorch-image-models
|
| 42 |
- torchvision MobileNetV3, BSD-3-Clause. Trained on the Vehicle classification dataset by DrBimmer (https://huggingface.co/datasets/DrBimmer/vehicle-classification), CC BY-NC 4.0. https://github.com/pytorch/vision
|
|
|
|
| 62 |
cards link here instead of repeating it.
|
| 63 |
-->
|
| 64 |
|
| 65 |
+
The folders `objectDetection/yolo26`, `objectDetection/yolov9` and `segmentation/yolo26-seg` of the model mirror hold Ultralytics weights,
|
| 66 |
modified by CamStack and distributed under AGPL-3.0. Their Corresponding Source
|
| 67 |
is published in the mirror itself, in its **`source/`** folder,
|
| 68 |
**https://huggingface.co/camstack/camstack-models/tree/main/source**, together
|
|
|
|
| 70 |
|
| 71 |
1. **The upstream checkpoints.** Ultralytics publishes
|
| 72 |
`yolo26{n,s,m,l,x}.pt`, `yolov9{t,s,m,c}.pt` and `yolo26{n,s,m}-seg.pt` at
|
| 73 |
+
https://github.com/ultralytics/assets/releases.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
2. **The build script** in `source/`: `build-camstack-models.py`, with
|
| 75 |
`convert_lib.py` (the ONNX to OpenVINO / INT8 converter it calls, the same
|
| 76 |
file CamStack's Model Studio runs) and `requirements.txt`. It holds one
|
|
|
|
| 98 |
- **INT8 calibration.** An IR records how many images calibrated it, not
|
| 99 |
which ones. Calibrating on the same COCO images gives the documented build;
|
| 100 |
a different set gives an equivalent, not bit-identical, INT8 model.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
|
| 102 |
## Non-commercial models
|
| 103 |
|
|
|
|
| 146 |
|
| 147 |
| Catalog ids | Upstream | Code / weights | Training data | CamStack changes | Downloaded from |
|
| 148 |
| --- | --- | --- | --- | --- | --- |
|
| 149 |
+
| `yolox-tiny-plate` (default) | [open-lpr models (faisalthaheem/open-lpr-models), YOLOX-tiny](https://huggingface.co/faisalthaheem/open-lpr-models) | Apache-2.0 / Apache-2.0 (`Apache-2.0.txt`) | Simanno annotated plate corpus plus community-contributed photographs, pseudo-labelled by a teacher model (not published; the upstream NOTICE says the photographs were uploaded without consent obtained): UNKNOWN | input contract baked into the graph (static 1x3x640x640 RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather) and the output reshaped to the YOLOv8 layout (cx, cy, w, h, obj*cls); ONNX fp32, OpenVINO IR fp16, CoreML fp16 (scripts/build-yolox-plate-model.py) | mirror `plateDetection/yolox-tiny-plate` |
|
| 150 |
|
| 151 |
### Licence plate reading
|
| 152 |
|
source/README.md
CHANGED
|
@@ -4,13 +4,13 @@
|
|
| 4 |
|
| 5 |
This folder is the **Corresponding Source** under the GNU AGPL-3.0 for the
|
| 6 |
Ultralytics-derived weights in this mirror (`objectDetection/yolo26`,
|
| 7 |
-
`objectDetection/yolov9`, `segmentation/yolo26-seg`
|
| 8 |
The full source offer is in
|
| 9 |
[`THIRD_PARTY_MODELS.md`](../THIRD_PARTY_MODELS.md#source-offer-for-the-agpl-30-weights).
|
| 10 |
|
| 11 |
| File | What it is |
|
| 12 |
| --- | --- |
|
| 13 |
-
| `build-camstack-models.py` | Rebuilds every file in the
|
| 14 |
| `convert_lib.py` | The converter `build-camstack-models.py` calls for OpenVINO: ONNX to IR (`ov.convert_model`, FP32 or FP16-compressed), NNCF INT8 post-training quantisation, and validation of every IR. It is the same file CamStack's Model Studio runs. |
|
| 15 |
| `requirements.txt` | Python dependencies of `convert_lib.py`, with per-platform markers. |
|
| 16 |
| `export-models.py` | An older bulk exporter for YOLOv8 / YOLOv9 / YOLO11 ids. No file in this mirror needs it. |
|
|
@@ -32,8 +32,7 @@ python build-camstack-models.py --all --formats onnx,openvino # some formats
|
|
| 32 |
Results go to `./exported-models/<mirror path>`, for example
|
| 33 |
`exported-models/objectDetection/yolo26/openvino/camstack-yolo26n-320-int8.xml`.
|
| 34 |
Each build step runs in its own Python process: on macOS, CoreML, OpenVINO and
|
| 35 |
-
TensorFlow crash one another when they share one. CoreML needs macOS.
|
| 36 |
-
needs `tensorflow` and `onnx2tf`.
|
| 37 |
|
| 38 |
What the recipes encode (the hosted files record each of these):
|
| 39 |
|
|
@@ -43,13 +42,11 @@ What the recipes encode (the hosted files record each of these):
|
|
| 43 |
| `objectDetection/yolo26` | `camstack-yolo26{n,s,m,l}-{320,256}` | dynamic axes, opset 17 | flexible batch 1..16, FP16 | FP32 `<stem>.xml`; INT8 `<stem>-int8.xml` (128 images) |
|
| 44 |
| `objectDetection/yolov9` | `camstack-yolov9{t,s,m,c}-{320,640}` | static, opset 13 | static, batch 1 | FP16 `<stem>-fp16.xml`; INT8 `<stem>-int8.xml` (300 images) |
|
| 45 |
| `segmentation/yolo26-seg` | `camstack-yolo26{n,s,m}-seg` @640 | static, opset 17 | static, batch 1 | FP16 `<stem>.xml` |
|
| 46 |
-
| `plateDetection/yolov8-plate` | `camstack-yolov8n-plate` @640 | third-party file, unmodified | static, batch 1 | FP16 `<stem>.xml` (from the ONNX); also TFLite float32 |
|
| 47 |
|
| 48 |
Recorded tool versions (Ultralytics / PyTorch / coremltools / OpenVINO):
|
| 49 |
YOLO26 @640 and YOLO26-seg 8.4.24 / 2.10.0 / 9.0 / 2026.2.1; YOLO26 @320 and
|
| 50 |
@256 8.4.92 / 2.13.0 for ONNX, 2.5.1 for CoreML / 8.3.0 / 2026.2.1; YOLOv9
|
| 51 |
-
8.4.98 / 2.13.0 / 9.0 / 2026.2.1
|
| 52 |
-
OpenVINO 2026.0.0. The script warns when the installed Ultralytics differs.
|
| 53 |
With these versions a rebuild gives the hosted weights. Other versions give the
|
| 54 |
same weights in a possibly different graph. The flexible-batch CoreML exports
|
| 55 |
need coremltools 8.3 with PyTorch 2.5.1, the macOS pins in `requirements.txt`;
|
|
@@ -74,24 +71,6 @@ not record WHICH images calibrated it, so a different set gives an equivalent
|
|
| 74 |
INT8 model, not a bit-identical one. Without calibration images the INT8 files
|
| 75 |
are skipped and the run says so.
|
| 76 |
|
| 77 |
-
### `yolov8n-plate`
|
| 78 |
-
|
| 79 |
-
Two third-party YOLOv8n licence-plate fine-tunes, neither trained by CamStack:
|
| 80 |
-
|
| 81 |
-
- `onnx/camstack-yolov8n-plate.onnx` is `best.onnx` of
|
| 82 |
-
[ml-debi/yolov8-license-plate-detection](https://huggingface.co/ml-debi/yolov8-license-plate-detection)
|
| 83 |
-
(revision `8062e8e8`, SHA-256 `85d236280a1301ad98907947d284951dd2b20c23a6786ff50f7e6a8ec515bd50`),
|
| 84 |
-
hosted unmodified. Its author published no checkpoint, so the ONNX is the
|
| 85 |
-
only form of that model that exists. CamStack's modification is the OpenVINO
|
| 86 |
-
conversion, which the script reproduces from it.
|
| 87 |
-
- `coreml/` and `tflite/` are Ultralytics exports of `best.pt` of
|
| 88 |
-
[Koushim/yolov8-license-plate-detection](https://huggingface.co/Koushim/yolov8-license-plate-detection)
|
| 89 |
-
(revision `9aaa5cd4`, SHA-256 `2d95861825bb4184404344c9cf809f40fd31dba785fe54e8ba5b9a3583789822`).
|
| 90 |
-
The script downloads and verifies both files.
|
| 91 |
-
|
| 92 |
-
Both model cards say MIT. The Ultralytics YOLOv8n base weights keep both
|
| 93 |
-
AGPL-3.0, as their own metadata states. Neither publishes its training data.
|
| 94 |
-
|
| 95 |
## Licences
|
| 96 |
|
| 97 |
- **These scripts** (`build-camstack-models.py`, `convert_lib.py`,
|
|
|
|
| 4 |
|
| 5 |
This folder is the **Corresponding Source** under the GNU AGPL-3.0 for the
|
| 6 |
Ultralytics-derived weights in this mirror (`objectDetection/yolo26`,
|
| 7 |
+
`objectDetection/yolov9`, `segmentation/yolo26-seg`).
|
| 8 |
The full source offer is in
|
| 9 |
[`THIRD_PARTY_MODELS.md`](../THIRD_PARTY_MODELS.md#source-offer-for-the-agpl-30-weights).
|
| 10 |
|
| 11 |
| File | What it is |
|
| 12 |
| --- | --- |
|
| 13 |
+
| `build-camstack-models.py` | Rebuilds every file in the three folders above. One recipe per hosted file stem: upstream checkpoint, input size, the Ultralytics `export()` arguments, the OpenVINO precisions and the INT8 calibration count. Each value is what the hosted file records about itself in its metadata. |
|
| 14 |
| `convert_lib.py` | The converter `build-camstack-models.py` calls for OpenVINO: ONNX to IR (`ov.convert_model`, FP32 or FP16-compressed), NNCF INT8 post-training quantisation, and validation of every IR. It is the same file CamStack's Model Studio runs. |
|
| 15 |
| `requirements.txt` | Python dependencies of `convert_lib.py`, with per-platform markers. |
|
| 16 |
| `export-models.py` | An older bulk exporter for YOLOv8 / YOLOv9 / YOLO11 ids. No file in this mirror needs it. |
|
|
|
|
| 32 |
Results go to `./exported-models/<mirror path>`, for example
|
| 33 |
`exported-models/objectDetection/yolo26/openvino/camstack-yolo26n-320-int8.xml`.
|
| 34 |
Each build step runs in its own Python process: on macOS, CoreML, OpenVINO and
|
| 35 |
+
TensorFlow crash one another when they share one. CoreML needs macOS.
|
|
|
|
| 36 |
|
| 37 |
What the recipes encode (the hosted files record each of these):
|
| 38 |
|
|
|
|
| 42 |
| `objectDetection/yolo26` | `camstack-yolo26{n,s,m,l}-{320,256}` | dynamic axes, opset 17 | flexible batch 1..16, FP16 | FP32 `<stem>.xml`; INT8 `<stem>-int8.xml` (128 images) |
|
| 43 |
| `objectDetection/yolov9` | `camstack-yolov9{t,s,m,c}-{320,640}` | static, opset 13 | static, batch 1 | FP16 `<stem>-fp16.xml`; INT8 `<stem>-int8.xml` (300 images) |
|
| 44 |
| `segmentation/yolo26-seg` | `camstack-yolo26{n,s,m}-seg` @640 | static, opset 17 | static, batch 1 | FP16 `<stem>.xml` |
|
|
|
|
| 45 |
|
| 46 |
Recorded tool versions (Ultralytics / PyTorch / coremltools / OpenVINO):
|
| 47 |
YOLO26 @640 and YOLO26-seg 8.4.24 / 2.10.0 / 9.0 / 2026.2.1; YOLO26 @320 and
|
| 48 |
@256 8.4.92 / 2.13.0 for ONNX, 2.5.1 for CoreML / 8.3.0 / 2026.2.1; YOLOv9
|
| 49 |
+
8.4.98 / 2.13.0 / 9.0 / 2026.2.1. The script warns when the installed Ultralytics differs.
|
|
|
|
| 50 |
With these versions a rebuild gives the hosted weights. Other versions give the
|
| 51 |
same weights in a possibly different graph. The flexible-batch CoreML exports
|
| 52 |
need coremltools 8.3 with PyTorch 2.5.1, the macOS pins in `requirements.txt`;
|
|
|
|
| 71 |
INT8 model, not a bit-identical one. Without calibration images the INT8 files
|
| 72 |
are skipped and the run says so.
|
| 73 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
## Licences
|
| 75 |
|
| 76 |
- **These scripts** (`build-camstack-models.py`, `convert_lib.py`,
|
source/build-camstack-models.py
CHANGED
|
@@ -8,7 +8,6 @@ https://huggingface.co/camstack/camstack-models in
|
|
| 8 |
objectDetection/yolo26 yolo26{n,s,m,l,x} @640, yolo26{n,s,m,l} @320 and @256
|
| 9 |
objectDetection/yolov9 yolov9{t,s,m,c} @320 and @640
|
| 10 |
segmentation/yolo26-seg yolo26{n,s,m}-seg @640
|
| 11 |
-
plateDetection/yolov8-plate yolov8n-plate (third-party fine-tune, see below)
|
| 12 |
|
| 13 |
Every hosted file is one output of ONE recipe in `RECIPES` below, and the recipe
|
| 14 |
holds exactly the settings that file records about itself: the Ultralytics
|
|
@@ -40,7 +39,6 @@ Formats:
|
|
| 40 |
int8 convert_lib.quantize_openvino_int8: NNCF post-training quantisation
|
| 41 |
of the ONNX, calibrated on the first N images (sorted by file name)
|
| 42 |
of --calib-dir (default $CAMSTACK_CALIB_DIR or ./calibration-images)
|
| 43 |
-
tflite Ultralytics export(format="tflite") (needs tensorflow + onnx2tf)
|
| 44 |
|
| 45 |
INT8 calibration: the hosted INT8 IRs record subset_size 128 (YOLO26) and 300
|
| 46 |
(YOLOv9), drawn from COCO; an IR does not record WHICH images. The documented
|
|
@@ -62,7 +60,6 @@ convert_lib's). convert_lib.py is found next to this script (the mirror's
|
|
| 62 |
from __future__ import annotations
|
| 63 |
|
| 64 |
import argparse
|
| 65 |
-
import hashlib
|
| 66 |
import json
|
| 67 |
import os
|
| 68 |
import shutil
|
|
@@ -86,15 +83,6 @@ DEFAULT_CALIB_DIR = Path(os.environ.get('CAMSTACK_CALIB_DIR', 'calibration-image
|
|
| 86 |
# --- Recipe model ------------------------------------------------------------
|
| 87 |
|
| 88 |
|
| 89 |
-
@dataclass(frozen=True)
|
| 90 |
-
class Download:
|
| 91 |
-
"""A third-party file fetched by URL and verified by SHA-256."""
|
| 92 |
-
|
| 93 |
-
url: str
|
| 94 |
-
sha256: str
|
| 95 |
-
filename: str
|
| 96 |
-
|
| 97 |
-
|
| 98 |
@dataclass(frozen=True)
|
| 99 |
class OnnxExport:
|
| 100 |
"""Ultralytics export(format="onnx") arguments, as the hosted ONNX records them."""
|
|
@@ -139,28 +127,24 @@ class Recipe:
|
|
| 139 |
imgsz: int
|
| 140 |
toolchain: Toolchain
|
| 141 |
# The PyTorch checkpoint: an Ultralytics asset id (`yolo26n` -> yolo26n.pt,
|
| 142 |
-
# auto-downloaded by Ultralytics)
|
| 143 |
-
checkpoint: str
|
| 144 |
-
onnx: Optional[OnnxExport]
|
| 145 |
coreml: Optional[CoremlExport]
|
| 146 |
openvino: tuple[OpenvinoOutput, ...] = ()
|
| 147 |
calib_samples: int = 0
|
| 148 |
-
tflite: bool = False
|
| 149 |
-
onnx_verbatim: Optional[Download] = None # a third-party ONNX hosted unmodified
|
| 150 |
note: str = ''
|
| 151 |
|
| 152 |
def outputs(self) -> list[str]:
|
| 153 |
"""Mirror paths this recipe produces (an .mlpackage is a directory)."""
|
| 154 |
out: list[str] = []
|
| 155 |
-
if self.onnx is not None
|
| 156 |
out.append(f'{self.folder}/onnx/{self.name}.onnx')
|
| 157 |
if self.coreml is not None:
|
| 158 |
out.append(f'{self.folder}/coreml/{self.name}.mlpackage/')
|
| 159 |
for ov in self.openvino:
|
| 160 |
stem = f'{self.folder}/openvino/{self.name}{ov.suffix}'
|
| 161 |
out.extend([f'{stem}.xml', f'{stem}.bin'])
|
| 162 |
-
if self.tflite:
|
| 163 |
-
out.append(f'{self.folder}/tflite/{self.name}_float32.tflite')
|
| 164 |
return out
|
| 165 |
|
| 166 |
|
|
@@ -171,7 +155,6 @@ class Recipe:
|
|
| 171 |
TC_2604 = Toolchain('8.4.24', '2.10.0', '9.0', '2026.2.1')
|
| 172 |
TC_2607_REDUCED = Toolchain('8.4.92', 'onnx 2.13.0, coreml 2.5.1', '8.3.0', '2026.2.1')
|
| 173 |
TC_2607_YOLOV9 = Toolchain('8.4.98', '2.13.0', '9.0', '2026.2.1')
|
| 174 |
-
TC_PLATE = Toolchain('8.4.24 (CoreML, TFLite)', '2.10.0', '9.0', '2026.0.0')
|
| 175 |
|
| 176 |
FP16_IR = OpenvinoOutput('', 'fp16')
|
| 177 |
|
|
@@ -226,53 +209,19 @@ def _yolo26_seg(tier: str) -> Recipe:
|
|
| 226 |
)
|
| 227 |
|
| 228 |
|
| 229 |
-
# yolov8n-plate is TWO third-party YOLOv8n licence-plate fine-tunes, neither
|
| 230 |
-
# trained by CamStack (both declare MIT on their model cards; the Ultralytics
|
| 231 |
-
# base weights make them AGPL-3.0, and their own metadata says so):
|
| 232 |
-
# - the ONNX (and the OpenVINO IR converted from it) is ml-debi's `best.onnx`,
|
| 233 |
-
# hosted byte-identical. Its author published no .pt; the ONNX is the only
|
| 234 |
-
# form that exists. CamStack's modification is the IR conversion below.
|
| 235 |
-
# - the CoreML package and the TFLite file are Ultralytics exports of
|
| 236 |
-
# Koushim's `best.pt`, saved as `yolov8n-plate.pt` (the file stem is what
|
| 237 |
-
# the export metadata calls the model). The .pt is public.
|
| 238 |
-
_PLATE_ONNX = Download(
|
| 239 |
-
url='https://huggingface.co/ml-debi/yolov8-license-plate-detection/resolve/'
|
| 240 |
-
'8062e8e86734e272c60fa9a819433327ebdb66d7/best.onnx',
|
| 241 |
-
sha256='85d236280a1301ad98907947d284951dd2b20c23a6786ff50f7e6a8ec515bd50',
|
| 242 |
-
filename='camstack-yolov8n-plate.onnx',
|
| 243 |
-
)
|
| 244 |
-
_PLATE_PT = Download(
|
| 245 |
-
url='https://huggingface.co/Koushim/yolov8-license-plate-detection/resolve/'
|
| 246 |
-
'9aaa5cd490abe0c165882ba87f4f62658ab54d01/best.pt',
|
| 247 |
-
sha256='2d95861825bb4184404344c9cf809f40fd31dba785fe54e8ba5b9a3583789822',
|
| 248 |
-
filename='yolov8n-plate.pt',
|
| 249 |
-
)
|
| 250 |
-
|
| 251 |
-
PLATE = Recipe(
|
| 252 |
-
name='camstack-yolov8n-plate', folder='plateDetection/yolov8-plate', imgsz=640,
|
| 253 |
-
toolchain=TC_PLATE, checkpoint=_PLATE_PT,
|
| 254 |
-
onnx=None, onnx_verbatim=_PLATE_ONNX,
|
| 255 |
-
coreml=CoremlExport(dynamic=False, batch=1, half=False),
|
| 256 |
-
openvino=(FP16_IR,), tflite=True,
|
| 257 |
-
note='ONNX + IR: ml-debi/yolov8-license-plate-detection; CoreML + TFLite: '
|
| 258 |
-
'Koushim/yolov8-license-plate-detection (different fine-tunes)',
|
| 259 |
-
)
|
| 260 |
-
|
| 261 |
RECIPES: tuple[Recipe, ...] = (
|
| 262 |
*(_yolo26_full(t) for t in 'nsmlx'),
|
| 263 |
*(_yolo26_reduced(t, r) for t in 'nsml' for r in (320, 256)),
|
| 264 |
*(_yolov9(t, r) for t in ('t', 's', 'm', 'c') for r in (320, 640)),
|
| 265 |
*(_yolo26_seg(t) for t in 'nsm'),
|
| 266 |
-
PLATE,
|
| 267 |
)
|
| 268 |
|
| 269 |
FAMILIES = {
|
| 270 |
'yolo26': 'objectDetection/yolo26',
|
| 271 |
'yolov9': 'objectDetection/yolov9',
|
| 272 |
'yolo26-seg': 'segmentation/yolo26-seg',
|
| 273 |
-
'yolov8-plate': 'plateDetection/yolov8-plate',
|
| 274 |
}
|
| 275 |
-
ALL_FORMATS = ('onnx', 'coreml', 'openvino'
|
| 276 |
|
| 277 |
# --- Helpers ----------------------------------------------------------------
|
| 278 |
|
|
@@ -283,27 +232,6 @@ def size_mb(path: Path) -> float:
|
|
| 283 |
return sum(f.stat().st_size for f in path.rglob('*') if f.is_file()) / 1_048_576
|
| 284 |
|
| 285 |
|
| 286 |
-
def sha256_of(path: Path) -> str:
|
| 287 |
-
h = hashlib.sha256()
|
| 288 |
-
with path.open('rb') as fh:
|
| 289 |
-
for chunk in iter(lambda: fh.read(1 << 20), b''):
|
| 290 |
-
h.update(chunk)
|
| 291 |
-
return h.hexdigest()
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
def fetch(dl: Download, cache: Path) -> Path:
|
| 295 |
-
"""Download a third-party file once and refuse it if the SHA-256 differs."""
|
| 296 |
-
cache.mkdir(parents=True, exist_ok=True)
|
| 297 |
-
dest = cache / dl.filename
|
| 298 |
-
if not dest.is_file():
|
| 299 |
-
print(f' → downloading {dl.url}')
|
| 300 |
-
urllib.request.urlretrieve(dl.url, dest)
|
| 301 |
-
got = sha256_of(dest)
|
| 302 |
-
if got != dl.sha256:
|
| 303 |
-
raise RuntimeError(f'{dest} has sha256 {got}, expected {dl.sha256}')
|
| 304 |
-
return dest
|
| 305 |
-
|
| 306 |
-
|
| 307 |
def warn_toolchain(recipe: Recipe) -> None:
|
| 308 |
try:
|
| 309 |
import ultralytics
|
|
@@ -324,9 +252,7 @@ def place(src: Path, dest: Path) -> Path:
|
|
| 324 |
return dest
|
| 325 |
|
| 326 |
|
| 327 |
-
def checkpoint_path(recipe: Recipe
|
| 328 |
-
if isinstance(recipe.checkpoint, Download):
|
| 329 |
-
return str(fetch(recipe.checkpoint, cache))
|
| 330 |
return f'{recipe.checkpoint}.pt' # Ultralytics downloads its own assets
|
| 331 |
|
| 332 |
|
|
@@ -408,27 +334,24 @@ def ultralytics_export(checkpoint: str, **kwargs: object) -> Path:
|
|
| 408 |
# --- Format steps -------------------------------------------------------------
|
| 409 |
|
| 410 |
|
| 411 |
-
def build_onnx(recipe: Recipe, out: Path
|
| 412 |
dest = out / recipe.folder / 'onnx' / f'{recipe.name}.onnx'
|
| 413 |
-
if recipe.onnx_verbatim is not None:
|
| 414 |
-
print(' → ONNX: third-party file, hosted unmodified')
|
| 415 |
-
return place(fetch(recipe.onnx_verbatim, cache), dest)
|
| 416 |
assert recipe.onnx is not None
|
| 417 |
print(f' → ONNX (imgsz={recipe.imgsz}, dynamic={recipe.onnx.dynamic}, opset={recipe.onnx.opset})')
|
| 418 |
src = ultralytics_export(
|
| 419 |
-
checkpoint_path(recipe
|
| 420 |
dynamic=recipe.onnx.dynamic, simplify=True, opset=recipe.onnx.opset,
|
| 421 |
nms=False, batch=1, half=False,
|
| 422 |
)
|
| 423 |
return place(src, dest)
|
| 424 |
|
| 425 |
|
| 426 |
-
def build_coreml(recipe: Recipe, out: Path,
|
| 427 |
assert recipe.coreml is not None
|
| 428 |
c = recipe.coreml
|
| 429 |
print(f' → CoreML (imgsz={recipe.imgsz}, dynamic={c.dynamic}, batch={c.batch}, half={c.half})')
|
| 430 |
src = ultralytics_export(
|
| 431 |
-
checkpoint_path(recipe
|
| 432 |
dynamic=c.dynamic, batch=c.batch, half=c.half, nms=False,
|
| 433 |
)
|
| 434 |
dest = place(src, out / recipe.folder / 'coreml' / f'{recipe.name}.mlpackage')
|
|
@@ -459,16 +382,6 @@ def build_openvino(recipe: Recipe, onnx_path: Path, out: Path, calib_dir: Path,
|
|
| 459 |
return skipped
|
| 460 |
|
| 461 |
|
| 462 |
-
def build_tflite(recipe: Recipe, out: Path, cache: Path) -> Path:
|
| 463 |
-
print(f' → TFLite float32 (imgsz={recipe.imgsz})')
|
| 464 |
-
saved = ultralytics_export(checkpoint_path(recipe, cache), format='tflite', imgsz=recipe.imgsz)
|
| 465 |
-
stem = Path(checkpoint_path(recipe, cache)).stem
|
| 466 |
-
src = next(saved.rglob(f'{stem}_float32.tflite'), None) if saved.is_dir() else saved
|
| 467 |
-
if src is None:
|
| 468 |
-
raise RuntimeError(f'no {stem}_float32.tflite under {saved}')
|
| 469 |
-
return place(src, out / recipe.folder / 'tflite' / f'{recipe.name}_float32.tflite')
|
| 470 |
-
|
| 471 |
-
|
| 472 |
def verify_coreml_batch(mlpackage: Path, imgsz: int) -> None:
|
| 473 |
"""Predict with N=1 then N=4 on a flexible-batch package, in a throwaway child.
|
| 474 |
|
|
@@ -503,29 +416,27 @@ def run_step(report: Report, label: str, step: Callable[[], object]) -> object:
|
|
| 503 |
return None
|
| 504 |
|
| 505 |
|
| 506 |
-
def build(recipe: Recipe, formats: set[str], out: Path,
|
| 507 |
calib_samples: Optional[int], verify_coreml: bool, report: Report) -> None:
|
| 508 |
print('\n' + '=' * 70 + f'\n{recipe.name} ({recipe.folder}, {recipe.imgsz}×{recipe.imgsz})\n' + '=' * 70)
|
| 509 |
if recipe.note:
|
| 510 |
print(f' {recipe.note}')
|
| 511 |
warn_toolchain(recipe)
|
| 512 |
onnx_path: Optional[Path] = None
|
| 513 |
-
if formats & {'onnx', 'openvino'} and
|
| 514 |
-
result = run_step(report, f'{recipe.name} onnx', lambda: build_onnx(recipe, out
|
| 515 |
onnx_path = result if isinstance(result, Path) else None
|
| 516 |
if 'coreml' in formats and recipe.coreml is not None:
|
| 517 |
if sys.platform != 'darwin':
|
| 518 |
print(' ⊘ CoreML needs macOS')
|
| 519 |
report.skipped.append(f'{recipe.name} coreml')
|
| 520 |
else:
|
| 521 |
-
run_step(report, f'{recipe.name} coreml', lambda: build_coreml(recipe, out,
|
| 522 |
if 'openvino' in formats and recipe.openvino and onnx_path is not None:
|
| 523 |
samples = calib_samples or recipe.calib_samples or 300
|
| 524 |
skipped = run_step(report, f'{recipe.name} openvino',
|
| 525 |
lambda: build_openvino(recipe, onnx_path, out, calib_dir, samples))
|
| 526 |
report.skipped.extend(skipped if isinstance(skipped, list) else [])
|
| 527 |
-
if 'tflite' in formats and recipe.tflite:
|
| 528 |
-
run_step(report, f'{recipe.name} tflite', lambda: build_tflite(recipe, out, cache))
|
| 529 |
|
| 530 |
|
| 531 |
def adhoc_recipe(args: argparse.Namespace) -> Recipe:
|
|
@@ -584,15 +495,13 @@ def check_hosted() -> int:
|
|
| 584 |
def list_recipes() -> None:
|
| 585 |
for r in RECIPES:
|
| 586 |
ov = ','.join(f'{o.precision}{"→" + o.suffix if o.suffix else ""}' for o in r.openvino)
|
| 587 |
-
|
| 588 |
-
onnx = 'verbatim' if r.onnx_verbatim else (
|
| 589 |
-
f'{"dynamic" if r.onnx and r.onnx.dynamic else "static"} opset{r.onnx.opset if r.onnx else ""}')
|
| 590 |
cm = (f'{"flex≤" + str(r.coreml.batch) if r.coreml.dynamic else "static"}'
|
| 591 |
f'{" fp16" if r.coreml.half else ""}') if r.coreml else '-'
|
| 592 |
int8 = f' int8 N={r.calib_samples}' if any(o.precision == 'int8' for o in r.openvino) else ''
|
| 593 |
print(f'{r.name:30s} {r.folder:28s} {r.imgsz:4d} onnx[{onnx}] coreml[{cm}] '
|
| 594 |
-
f'openvino[{ov}]{int8}
|
| 595 |
-
f'ultralytics {r.toolchain.ultralytics} from {
|
| 596 |
|
| 597 |
|
| 598 |
def parse_args(argv: list[str]) -> argparse.Namespace:
|
|
@@ -606,10 +515,8 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
|
|
| 606 |
sel.add_argument('--check-hosted', action='store_true',
|
| 607 |
help='compare the recipes with the live mirror and exit (non-zero on a gap)')
|
| 608 |
sel.add_argument('--dry-run', action='store_true', help='print the files that would be built')
|
| 609 |
-
p.add_argument('--formats', default=','.join(ALL_FORMATS), help='subset of onnx,coreml,openvino
|
| 610 |
p.add_argument('--out', type=Path, default=Path('exported-models'))
|
| 611 |
-
p.add_argument('--cache', type=Path, default=Path('exported-models/.downloads'),
|
| 612 |
-
help='where third-party checkpoints are downloaded')
|
| 613 |
p.add_argument('--calib-dir', type=Path, default=DEFAULT_CALIB_DIR)
|
| 614 |
p.add_argument('--verify-coreml', action='store_true',
|
| 615 |
help='predict N=1 and N=4 on each flexible-batch CoreML package (in a child process)')
|
|
@@ -659,7 +566,7 @@ def main(argv: list[str]) -> int:
|
|
| 659 |
return 0
|
| 660 |
report = Report()
|
| 661 |
for r in recipes:
|
| 662 |
-
build(r, formats, args.out, args.
|
| 663 |
args.verify_coreml, report)
|
| 664 |
print('\nDone.' + (f' Failed: {report.failed}.' if report.failed else '')
|
| 665 |
+ (f' Skipped: {report.skipped}.' if report.skipped else ''))
|
|
|
|
| 8 |
objectDetection/yolo26 yolo26{n,s,m,l,x} @640, yolo26{n,s,m,l} @320 and @256
|
| 9 |
objectDetection/yolov9 yolov9{t,s,m,c} @320 and @640
|
| 10 |
segmentation/yolo26-seg yolo26{n,s,m}-seg @640
|
|
|
|
| 11 |
|
| 12 |
Every hosted file is one output of ONE recipe in `RECIPES` below, and the recipe
|
| 13 |
holds exactly the settings that file records about itself: the Ultralytics
|
|
|
|
| 39 |
int8 convert_lib.quantize_openvino_int8: NNCF post-training quantisation
|
| 40 |
of the ONNX, calibrated on the first N images (sorted by file name)
|
| 41 |
of --calib-dir (default $CAMSTACK_CALIB_DIR or ./calibration-images)
|
|
|
|
| 42 |
|
| 43 |
INT8 calibration: the hosted INT8 IRs record subset_size 128 (YOLO26) and 300
|
| 44 |
(YOLOv9), drawn from COCO; an IR does not record WHICH images. The documented
|
|
|
|
| 60 |
from __future__ import annotations
|
| 61 |
|
| 62 |
import argparse
|
|
|
|
| 63 |
import json
|
| 64 |
import os
|
| 65 |
import shutil
|
|
|
|
| 83 |
# --- Recipe model ------------------------------------------------------------
|
| 84 |
|
| 85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
@dataclass(frozen=True)
|
| 87 |
class OnnxExport:
|
| 88 |
"""Ultralytics export(format="onnx") arguments, as the hosted ONNX records them."""
|
|
|
|
| 127 |
imgsz: int
|
| 128 |
toolchain: Toolchain
|
| 129 |
# The PyTorch checkpoint: an Ultralytics asset id (`yolo26n` -> yolo26n.pt,
|
| 130 |
+
# auto-downloaded by Ultralytics).
|
| 131 |
+
checkpoint: str
|
| 132 |
+
onnx: Optional[OnnxExport]
|
| 133 |
coreml: Optional[CoremlExport]
|
| 134 |
openvino: tuple[OpenvinoOutput, ...] = ()
|
| 135 |
calib_samples: int = 0
|
|
|
|
|
|
|
| 136 |
note: str = ''
|
| 137 |
|
| 138 |
def outputs(self) -> list[str]:
|
| 139 |
"""Mirror paths this recipe produces (an .mlpackage is a directory)."""
|
| 140 |
out: list[str] = []
|
| 141 |
+
if self.onnx is not None:
|
| 142 |
out.append(f'{self.folder}/onnx/{self.name}.onnx')
|
| 143 |
if self.coreml is not None:
|
| 144 |
out.append(f'{self.folder}/coreml/{self.name}.mlpackage/')
|
| 145 |
for ov in self.openvino:
|
| 146 |
stem = f'{self.folder}/openvino/{self.name}{ov.suffix}'
|
| 147 |
out.extend([f'{stem}.xml', f'{stem}.bin'])
|
|
|
|
|
|
|
| 148 |
return out
|
| 149 |
|
| 150 |
|
|
|
|
| 155 |
TC_2604 = Toolchain('8.4.24', '2.10.0', '9.0', '2026.2.1')
|
| 156 |
TC_2607_REDUCED = Toolchain('8.4.92', 'onnx 2.13.0, coreml 2.5.1', '8.3.0', '2026.2.1')
|
| 157 |
TC_2607_YOLOV9 = Toolchain('8.4.98', '2.13.0', '9.0', '2026.2.1')
|
|
|
|
| 158 |
|
| 159 |
FP16_IR = OpenvinoOutput('', 'fp16')
|
| 160 |
|
|
|
|
| 209 |
)
|
| 210 |
|
| 211 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
RECIPES: tuple[Recipe, ...] = (
|
| 213 |
*(_yolo26_full(t) for t in 'nsmlx'),
|
| 214 |
*(_yolo26_reduced(t, r) for t in 'nsml' for r in (320, 256)),
|
| 215 |
*(_yolov9(t, r) for t in ('t', 's', 'm', 'c') for r in (320, 640)),
|
| 216 |
*(_yolo26_seg(t) for t in 'nsm'),
|
|
|
|
| 217 |
)
|
| 218 |
|
| 219 |
FAMILIES = {
|
| 220 |
'yolo26': 'objectDetection/yolo26',
|
| 221 |
'yolov9': 'objectDetection/yolov9',
|
| 222 |
'yolo26-seg': 'segmentation/yolo26-seg',
|
|
|
|
| 223 |
}
|
| 224 |
+
ALL_FORMATS = ('onnx', 'coreml', 'openvino')
|
| 225 |
|
| 226 |
# --- Helpers ----------------------------------------------------------------
|
| 227 |
|
|
|
|
| 232 |
return sum(f.stat().st_size for f in path.rglob('*') if f.is_file()) / 1_048_576
|
| 233 |
|
| 234 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
def warn_toolchain(recipe: Recipe) -> None:
|
| 236 |
try:
|
| 237 |
import ultralytics
|
|
|
|
| 252 |
return dest
|
| 253 |
|
| 254 |
|
| 255 |
+
def checkpoint_path(recipe: Recipe) -> str:
|
|
|
|
|
|
|
| 256 |
return f'{recipe.checkpoint}.pt' # Ultralytics downloads its own assets
|
| 257 |
|
| 258 |
|
|
|
|
| 334 |
# --- Format steps -------------------------------------------------------------
|
| 335 |
|
| 336 |
|
| 337 |
+
def build_onnx(recipe: Recipe, out: Path) -> Path:
|
| 338 |
dest = out / recipe.folder / 'onnx' / f'{recipe.name}.onnx'
|
|
|
|
|
|
|
|
|
|
| 339 |
assert recipe.onnx is not None
|
| 340 |
print(f' → ONNX (imgsz={recipe.imgsz}, dynamic={recipe.onnx.dynamic}, opset={recipe.onnx.opset})')
|
| 341 |
src = ultralytics_export(
|
| 342 |
+
checkpoint_path(recipe), format='onnx', imgsz=recipe.imgsz,
|
| 343 |
dynamic=recipe.onnx.dynamic, simplify=True, opset=recipe.onnx.opset,
|
| 344 |
nms=False, batch=1, half=False,
|
| 345 |
)
|
| 346 |
return place(src, dest)
|
| 347 |
|
| 348 |
|
| 349 |
+
def build_coreml(recipe: Recipe, out: Path, verify: bool) -> Path:
|
| 350 |
assert recipe.coreml is not None
|
| 351 |
c = recipe.coreml
|
| 352 |
print(f' → CoreML (imgsz={recipe.imgsz}, dynamic={c.dynamic}, batch={c.batch}, half={c.half})')
|
| 353 |
src = ultralytics_export(
|
| 354 |
+
checkpoint_path(recipe), format='coreml', imgsz=recipe.imgsz,
|
| 355 |
dynamic=c.dynamic, batch=c.batch, half=c.half, nms=False,
|
| 356 |
)
|
| 357 |
dest = place(src, out / recipe.folder / 'coreml' / f'{recipe.name}.mlpackage')
|
|
|
|
| 382 |
return skipped
|
| 383 |
|
| 384 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 385 |
def verify_coreml_batch(mlpackage: Path, imgsz: int) -> None:
|
| 386 |
"""Predict with N=1 then N=4 on a flexible-batch package, in a throwaway child.
|
| 387 |
|
|
|
|
| 416 |
return None
|
| 417 |
|
| 418 |
|
| 419 |
+
def build(recipe: Recipe, formats: set[str], out: Path, calib_dir: Path,
|
| 420 |
calib_samples: Optional[int], verify_coreml: bool, report: Report) -> None:
|
| 421 |
print('\n' + '=' * 70 + f'\n{recipe.name} ({recipe.folder}, {recipe.imgsz}×{recipe.imgsz})\n' + '=' * 70)
|
| 422 |
if recipe.note:
|
| 423 |
print(f' {recipe.note}')
|
| 424 |
warn_toolchain(recipe)
|
| 425 |
onnx_path: Optional[Path] = None
|
| 426 |
+
if formats & {'onnx', 'openvino'} and recipe.onnx:
|
| 427 |
+
result = run_step(report, f'{recipe.name} onnx', lambda: build_onnx(recipe, out))
|
| 428 |
onnx_path = result if isinstance(result, Path) else None
|
| 429 |
if 'coreml' in formats and recipe.coreml is not None:
|
| 430 |
if sys.platform != 'darwin':
|
| 431 |
print(' ⊘ CoreML needs macOS')
|
| 432 |
report.skipped.append(f'{recipe.name} coreml')
|
| 433 |
else:
|
| 434 |
+
run_step(report, f'{recipe.name} coreml', lambda: build_coreml(recipe, out, verify_coreml))
|
| 435 |
if 'openvino' in formats and recipe.openvino and onnx_path is not None:
|
| 436 |
samples = calib_samples or recipe.calib_samples or 300
|
| 437 |
skipped = run_step(report, f'{recipe.name} openvino',
|
| 438 |
lambda: build_openvino(recipe, onnx_path, out, calib_dir, samples))
|
| 439 |
report.skipped.extend(skipped if isinstance(skipped, list) else [])
|
|
|
|
|
|
|
| 440 |
|
| 441 |
|
| 442 |
def adhoc_recipe(args: argparse.Namespace) -> Recipe:
|
|
|
|
| 495 |
def list_recipes() -> None:
|
| 496 |
for r in RECIPES:
|
| 497 |
ov = ','.join(f'{o.precision}{"→" + o.suffix if o.suffix else ""}' for o in r.openvino)
|
| 498 |
+
onnx = f'{"dynamic" if r.onnx and r.onnx.dynamic else "static"} opset{r.onnx.opset if r.onnx else ""}'
|
|
|
|
|
|
|
| 499 |
cm = (f'{"flex≤" + str(r.coreml.batch) if r.coreml.dynamic else "static"}'
|
| 500 |
f'{" fp16" if r.coreml.half else ""}') if r.coreml else '-'
|
| 501 |
int8 = f' int8 N={r.calib_samples}' if any(o.precision == 'int8' for o in r.openvino) else ''
|
| 502 |
print(f'{r.name:30s} {r.folder:28s} {r.imgsz:4d} onnx[{onnx}] coreml[{cm}] '
|
| 503 |
+
f'openvino[{ov}]{int8} '
|
| 504 |
+
f'ultralytics {r.toolchain.ultralytics} from {r.checkpoint}.pt')
|
| 505 |
|
| 506 |
|
| 507 |
def parse_args(argv: list[str]) -> argparse.Namespace:
|
|
|
|
| 515 |
sel.add_argument('--check-hosted', action='store_true',
|
| 516 |
help='compare the recipes with the live mirror and exit (non-zero on a gap)')
|
| 517 |
sel.add_argument('--dry-run', action='store_true', help='print the files that would be built')
|
| 518 |
+
p.add_argument('--formats', default=','.join(ALL_FORMATS), help='subset of onnx,coreml,openvino')
|
| 519 |
p.add_argument('--out', type=Path, default=Path('exported-models'))
|
|
|
|
|
|
|
| 520 |
p.add_argument('--calib-dir', type=Path, default=DEFAULT_CALIB_DIR)
|
| 521 |
p.add_argument('--verify-coreml', action='store_true',
|
| 522 |
help='predict N=1 and N=4 on each flexible-batch CoreML package (in a child process)')
|
|
|
|
| 566 |
return 0
|
| 567 |
report = Report()
|
| 568 |
for r in recipes:
|
| 569 |
+
build(r, formats, args.out, args.calib_dir, args.calib_samples,
|
| 570 |
args.verify_coreml, report)
|
| 571 |
print('\nDone.' + (f' Failed: {report.failed}.' if report.failed else '')
|
| 572 |
+ (f' Skipped: {report.skipped}.' if report.skipped else ''))
|