File size: 7,996 Bytes
fb3cc07 90c9878 0755c89 fb3cc07 90c9878 0755c89 fb3cc07 0755c89 90c9878 0755c89 8d6abb9 2774701 8d6abb9 0755c89 90c9878 0755c89 1f57eba 2774701 0755c89 8d6abb9 0755c89 fb3cc07 0755c89 bc56020 2991ff3 0755c89 fb3cc07 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | <!-- Tracked in the CamStack repository as hf/camstack-models/source/README.md; edit it there, not on the Hub. The scripts beside it are copied by hf/camstack-models/upload.sh from scripts/ and packages/addon-model-studio/python/. -->
# Source: how the CamStack YOLO builds are made
This folder is the **Corresponding Source** under the GNU AGPL-3.0 for the
Ultralytics-derived weights in this mirror (`objectDetection/yolo26`,
`objectDetection/yolov9`, `segmentation/yolo26-seg`).
The full source offer is in
[`THIRD_PARTY_MODELS.md`](../THIRD_PARTY_MODELS.md#source-offer-for-the-agpl-30-weights).
| File | What it is |
| --- | --- |
| `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. |
| `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. |
| `requirements.txt` | Python dependencies of `convert_lib.py`, with per-platform markers. |
| `export-models.py` | An older bulk exporter for YOLOv8 / YOLOv9 / YOLO11 ids. No file in this mirror needs it. |
## Rebuilding
```bash
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt ultralytics openvino nncf
python build-camstack-models.py --list # the recipes: one per hosted file stem
python build-camstack-models.py --check-hosted # every hosted file is a recipe output?
python build-camstack-models.py --variant camstack-yolo26n-320 # one hosted model, every format
python build-camstack-models.py --family yolov9 # one mirror folder
python build-camstack-models.py --all # the whole set
python build-camstack-models.py --all --formats onnx,openvino # some formats only
```
Results go to `./exported-models/<mirror path>`, for example
`exported-models/objectDetection/yolo26/openvino/camstack-yolo26n-320-int8.xml`.
Each build step runs in its own Python process: on macOS, CoreML, OpenVINO and
TensorFlow crash one another when they share one. CoreML needs macOS.
What the recipes encode (the hosted files record each of these):
| Folder | Files | ONNX | CoreML | OpenVINO IR |
| --- | --- | --- | --- | --- |
| `objectDetection/yolo26` | `camstack-yolo26{n,s,m,l,x}` @640 | static, opset 17 | static, batch 1, FP16 | FP16 as `<stem>.xml` and `<stem>-fp16.xml`; INT8 `<stem>-int8.xml` (128 images) |
| `objectDetection/yolo26` | `camstack-yolo26{n,s,m,l}-{320,256}` | dynamic axes, opset 17 | static, batch 1, FP16 | FP32 `<stem>.xml`; INT8 `<stem>-int8.xml` (128 images) |
| `objectDetection/yolov9` | `camstack-yolov9{t,s,m,c}-{320,640}` | static, opset 13 | static, batch 1, FP16 | FP16 `<stem>-fp16.xml`; INT8 `<stem>-int8.xml` (300 images) |
| `segmentation/yolo26-seg` | `camstack-yolo26{n,s,m}-seg` @640 | static, opset 17 | static, batch 1, FP16 | FP16 `<stem>.xml` |
Recorded tool versions (Ultralytics / PyTorch / coremltools / OpenVINO):
YOLO26 @640 and YOLO26-seg 8.4.24 / 2.10.0 / 9.0 / 2026.2.1; YOLO26 @320 and
@256 8.4.92 / 2.13.0 for ONNX, 2.5.1 for CoreML / 8.3.0 / 2026.2.1; YOLOv9
8.4.98 / 2.13.0 / 9.0 / 2026.2.1. The script warns when the installed Ultralytics differs.
With these versions a rebuild gives the hosted weights. Other versions give the
same weights in a possibly different graph. The @320 and @256 CoreML exports
use coremltools 8.3 with PyTorch 2.5.1, the macOS pins in `requirements.txt`.
The @320 and @256 CoreML packages were batch-flexible (1..16) until
2026-10-07. That package aborts any process that loads it on macOS 26.2, on
every compute unit, because Core ML's `gather_nd` shape pass fails on a
flexible batch. They are now exported with a static batch of 1 and were
replaced in place, under the same names. The script refuses a batch-flexible
CoreML recipe for a YOLO26 checkpoint.
Every CoreML package computes in FP16, the only precision the Apple Neural
Engine runs. An FP32 package runs on the GPU or the CPU instead, 2–4× slower.
The YOLOv9 packages were FP32 until 2026-10-07: Ultralytics 8.4.98 pins FP32
unless `half=True` is passed. They were re-exported FP16 and replaced in place,
under the same names. The YOLO26 @640 and YOLO26-seg packages record
`half: False` but are FP16, because Ultralytics 8.4.24 left Core ML's FP16
default in place; their recipes now pass `half=True` so a current Ultralytics
gives the same precision. The script refuses an FP32 CoreML recipe.
### INT8 calibration
The INT8 IRs are NNCF post-training quantisation of the ONNX
(`convert_lib.quantize_openvino_int8`), calibrated on COCO images. The
documented calibration set is COCO val2017:
```bash
curl -LO http://images.cocodataset.org/zips/val2017.zip && unzip -q val2017.zip
python build-camstack-models.py --family yolo26 --calib-dir ./val2017
```
The recipe takes the first N images in file-name order: 128 for YOLO26, 300
for YOLOv9, as recorded in each IR's `rt_info`. Each image is letterboxed to
the model's input size, CENTRED with grey (114) padding, and scaled to [0, 1],
the way CamStack feeds the model at runtime. `--calib-samples` overrides N. An
IR does not record WHICH images calibrated it, so a different set gives an
equivalent INT8 model, not a bit-identical one. Without calibration images the
INT8 files are skipped and the run says so.
Until 2026-10-08 the calibration padded TOP-LEFT. On that date every INT8 IR
was re-quantised centred from the hosted ONNX (`--reuse-onnx`, OpenVINO
2026.2.1 + NNCF 3.2.0) and measured against the old one. 13 replaced the old
IR, 8 measured worse and were kept. The replaced IRs are under
`archive/2026-10-08-int8-topleft-calibration/` at the mirror root.
[`archive/convert_lib-before-2026-10-08.py`](archive/convert_lib-before-2026-10-08.py)
reproduces the top-left ones; it rebuilds the hosted `yolov9m-640` INT8 IR bit
for bit.
### INT8 recipe (since 2026-10-09)
`convert_lib.quantize_openvino_int8` keeps the YOLO Detect head in float.
- **YOLOv9:** the head's own ops stay float: the DFL softmax and the box
decode (anchor ± distance, ×stride, class sigmoid). The conv branches stay
INT8.
- **YOLO26:** the whole end-to-end head stays float.
- **Every model:** NNCF preset MIXED and full bias correction.
This follows the Ultralytics OpenVINO INT8 exporter and NNCF's YOLO26 example.
The previous recipe quantised the decode, which put box coordinates on a
256-level grid.
**Result:**
- On all 8 YOLOv9 IRs, mAP50-95 went from 0.01–0.18 below float to within noise
of it, at NPU latency within 1 %.
- 14 IRs were replaced in place: every `yolov9*-int8`, `yolo26{n,s,l}-int8`,
`yolo26n-320-int8` and `yolo26{n,s}-256-int8`.
- The previous files are under `archive/2026-10-09-int8-before-head-float-recipe/`
at the mirror root.
Seven YOLO26 IRs were kept, because the float head cost 11–22 % latency on the
Intel iGPU:
- `yolo26{m,x}-int8` and `yolo26{m,l}-256-int8` (centred) are reproduced by
[`archive/convert_lib-before-2026-10-09.py`](archive/convert_lib-before-2026-10-09.py);
- `yolo26{s,m,l}-320-int8` (top-left) are reproduced by
`archive/convert_lib-before-2026-10-08.py`.
`build-camstack-models.py` lists which IR carries which recipe.
## Licences
- **These scripts** (`build-camstack-models.py`, `convert_lib.py`,
`export-models.py`, `requirements.txt`) are CamStack's own code and are
offered under the **MIT** licence.
- **The weights they produce keep their upstream licence.** The Ultralytics
checkpoints are AGPL-3.0 ([`LICENSES/AGPL-3.0.txt`](../LICENSES/AGPL-3.0.txt)),
and so is every file derived from them in this mirror. Nothing here is
relicensed; see [`LICENSE`](../LICENSE) and each folder's `README.md`.
|