CandyEye 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- candyeye-0.1.0/CHANGELOG.md +54 -0
- candyeye-0.1.0/CandyEye.egg-info/PKG-INFO +343 -0
- candyeye-0.1.0/CandyEye.egg-info/SOURCES.txt +39 -0
- candyeye-0.1.0/CandyEye.egg-info/dependency_links.txt +1 -0
- candyeye-0.1.0/CandyEye.egg-info/entry_points.txt +2 -0
- candyeye-0.1.0/CandyEye.egg-info/requires.txt +27 -0
- candyeye-0.1.0/CandyEye.egg-info/top_level.txt +1 -0
- candyeye-0.1.0/LICENSE +675 -0
- candyeye-0.1.0/MANIFEST.in +14 -0
- candyeye-0.1.0/PKG-INFO +343 -0
- candyeye-0.1.0/README.md +295 -0
- candyeye-0.1.0/candyeye/__init__.py +57 -0
- candyeye-0.1.0/candyeye/configs/yolo11.yaml +37 -0
- candyeye-0.1.0/candyeye/configs/yolo11_exchange.yaml +38 -0
- candyeye-0.1.0/candyeye/core/__init__.py +13 -0
- candyeye-0.1.0/candyeye/core/backbone_mobilenet.py +82 -0
- candyeye-0.1.0/candyeye/core/candyeye.py +293 -0
- candyeye-0.1.0/candyeye/core/convert_yolo11.py +157 -0
- candyeye-0.1.0/candyeye/core/functions/__init__.py +0 -0
- candyeye-0.1.0/candyeye/core/functions/layer_utils.py +39 -0
- candyeye-0.1.0/candyeye/core/modules/__init__.py +0 -0
- candyeye-0.1.0/candyeye/core/modules/blocks.py +180 -0
- candyeye-0.1.0/candyeye/core/modules/conv.py +115 -0
- candyeye-0.1.0/candyeye/core/modules/detect.py +150 -0
- candyeye-0.1.0/candyeye/core/modules/exchange.py +151 -0
- candyeye-0.1.0/candyeye/data/__init__.py +0 -0
- candyeye-0.1.0/candyeye/data/transforms.py +66 -0
- candyeye-0.1.0/candyeye/data/voc.py +219 -0
- candyeye-0.1.0/candyeye/data/yolo.py +170 -0
- candyeye-0.1.0/candyeye/eval/__init__.py +0 -0
- candyeye-0.1.0/candyeye/eval/detection_metrics.py +241 -0
- candyeye-0.1.0/candyeye/eval/map.py +214 -0
- candyeye-0.1.0/candyeye/inference/__init__.py +0 -0
- candyeye-0.1.0/candyeye/inference/predict.py +240 -0
- candyeye-0.1.0/candyeye/paths.py +157 -0
- candyeye-0.1.0/candyeye/training/__init__.py +0 -0
- candyeye-0.1.0/candyeye/training/assigner.py +108 -0
- candyeye-0.1.0/candyeye/training/loss.py +140 -0
- candyeye-0.1.0/candyeye/training/trainer.py +726 -0
- candyeye-0.1.0/pyproject.toml +73 -0
- candyeye-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
|
|
6
|
+
## [0.1.0] — 2026-10-09
|
|
7
|
+
|
|
8
|
+
First public release: a lightweight, CPU-first object detector.
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Installable package** — code lives in `candyeye/` with the public API
|
|
13
|
+
`from candyeye import CandyEye, train, CandyEyePredictor`. CPU-first training
|
|
14
|
+
defaults (128×128 input, AdamW, warmup + cosine schedule, all logical cores).
|
|
15
|
+
- **Bundled architecture configs** (`candyeye/configs/yolo11.yaml`,
|
|
16
|
+
`yolo11_exchange.yaml`) resolved via `candyeye.paths` with no repo-root paths.
|
|
17
|
+
- **Adaptive cross-scale exchange neck** (`ScaleExchange`) with `none` / `static`
|
|
18
|
+
/ `dynamic` gates, selectable from YAML and as `neck: exchange` on the
|
|
19
|
+
MobileNet model.
|
|
20
|
+
- **Evaluation** — mAP@0.5, COCO-style mAP@0.5:0.95, and small/medium/large size
|
|
21
|
+
buckets; `scripts/evaluate.py` and `scripts/benchmark.py` (params, GFLOPs,
|
|
22
|
+
CPU latency).
|
|
23
|
+
- **Download-on-demand weights** — `candyeye.paths.download_default_weights`
|
|
24
|
+
fetches and converts the official `yolo11n` checkpoint into the per-user
|
|
25
|
+
cache; the distribution itself ships no weights.
|
|
26
|
+
- **CI** — `.github/workflows/ci.yml` (Python 3.10/3.12/3.14, CPU-only torch)
|
|
27
|
+
and an opt-in `.github/workflows/onnx-parity.yml`.
|
|
28
|
+
- **Publishing** — `.github/workflows/publish.yml` (TestPyPI + PyPI, Trusted
|
|
29
|
+
Publishing on `v*` tags).
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- Imports moved from top-level `core` / `training` / `data` / `eval` /
|
|
34
|
+
`inference` modules to the `candyeye.*` package.
|
|
35
|
+
- `pyproject.toml`: `requires-python >= 3.10`, SPDX `GPL-3.0-or-later`, version
|
|
36
|
+
single-sourced from `candyeye.__version__`.
|
|
37
|
+
|
|
38
|
+
### Notes
|
|
39
|
+
|
|
40
|
+
- Pretrained weights derive from Ultralytics' YOLO11 checkpoints (AGPL-3.0) and
|
|
41
|
+
are downloaded on first use rather than redistributed.
|
|
42
|
+
|
|
43
|
+
## [Unreleased] — Phase 0
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- `pyproject.toml` — package metadata (CandyEye 0.0.1, Python ≥ 3.14)
|
|
48
|
+
- `configs/default.yaml` — img_size 128, 20 classes, VOC07 trainval/test splits
|
|
49
|
+
- `scripts/download_voc.py` — VOC 2007 download, safe extract, line-count
|
|
50
|
+
verification (5011/4952), tar cleanup
|
|
51
|
+
- `data/voc.py` — `VOCDataset`: XML annotation parsing, class-id mapping
|
|
52
|
+
(0–19), coordinate clamping, degenerate-box drop, `difficult` flag
|
|
53
|
+
- `LICENSE` — GPL-3.0
|
|
54
|
+
- `README.md`, `ROADMAP.md`, `STATUS.md`, this changelog
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: CandyEye
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight, CPU-first object detector with an optional cross-scale exchange neck.
|
|
5
|
+
Author: Seventeen23
|
|
6
|
+
License-Expression: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/Seventeen23/CandyEye
|
|
8
|
+
Project-URL: Repository, https://github.com/Seventeen23/CandyEye
|
|
9
|
+
Project-URL: Issues, https://github.com/Seventeen23/CandyEye/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/Seventeen23/CandyEye/blob/master/CHANGELOG.md
|
|
11
|
+
Keywords: object-detection,computer-vision,yolo,cpu,edge-ai,pytorch
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Image Recognition
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: torch>=2.0
|
|
27
|
+
Requires-Dist: torchvision>=0.15
|
|
28
|
+
Requires-Dist: numpy>=1.24
|
|
29
|
+
Requires-Dist: opencv-python-headless>=4.8
|
|
30
|
+
Requires-Dist: pyyaml>=6.0
|
|
31
|
+
Provides-Extra: plots
|
|
32
|
+
Requires-Dist: matplotlib>=3.8; extra == "plots"
|
|
33
|
+
Provides-Extra: onnx
|
|
34
|
+
Requires-Dist: onnx>=1.16; extra == "onnx"
|
|
35
|
+
Provides-Extra: parity
|
|
36
|
+
Requires-Dist: onnx>=1.16; extra == "parity"
|
|
37
|
+
Requires-Dist: onnxruntime>=1.17; extra == "parity"
|
|
38
|
+
Provides-Extra: weights
|
|
39
|
+
Requires-Dist: ultralytics>=8.3; extra == "weights"
|
|
40
|
+
Provides-Extra: dev
|
|
41
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
42
|
+
Requires-Dist: matplotlib>=3.8; extra == "dev"
|
|
43
|
+
Requires-Dist: onnx>=1.16; extra == "dev"
|
|
44
|
+
Provides-Extra: release
|
|
45
|
+
Requires-Dist: build>=1.2; extra == "release"
|
|
46
|
+
Requires-Dist: twine>=5.0; extra == "release"
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
|
|
49
|
+
# CandyEye
|
|
50
|
+
|
|
51
|
+
[](https://pypi.org/project/CandyEye/)
|
|
52
|
+
[](https://github.com/Seventeen23/CandyEye/actions/workflows/ci.yml)
|
|
53
|
+
|
|
54
|
+
Lightweight, CPU-first object detector written from scratch — YOLOv11-style
|
|
55
|
+
architecture, trained on PASCAL VOC, exported to ONNX for fast CPU inference.
|
|
56
|
+
|
|
57
|
+
**Status:** Phase 0 complete → Phase 1 (model) in progress → [STATUS.md](STATUS.md) ·
|
|
58
|
+
Full plan → [ROADMAP.md](ROADMAP.md)
|
|
59
|
+
|
|
60
|
+
## Why "CandyEye"?
|
|
61
|
+
|
|
62
|
+
The name comes from sugar chemistry: **fructose** is a simple sugar — the
|
|
63
|
+
smallest, sweetest, quickest source of energy in nature. The detector aims
|
|
64
|
+
to be the same for vision: *small, sweet, fast*.
|
|
65
|
+
|
|
66
|
+
Three design ideas, one borrowed from each reference model:
|
|
67
|
+
|
|
68
|
+
| Idea | From | What it means here |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| **Detection** | YOLO | One forward glance — boxes come straight from conv features in a single stage, no region proposals |
|
|
71
|
+
| **Efficient & lightweight** | MobileNet | ~2.6M params, CPU-only training, 128×128 input — fast fuel, runs on the laptop it was built on |
|
|
72
|
+
| **Parallel** | YOLACT++ | Shared conv features feed multiple branches simultaneously: the FPN neck runs P3/P4/P5 in parallel, and each Detect-head scale splits into **box and classification branches computed in parallel** — no mask branch, bounding boxes only |
|
|
73
|
+
|
|
74
|
+
## Goals
|
|
75
|
+
|
|
76
|
+
| Metric | Target |
|
|
77
|
+
|---|---|
|
|
78
|
+
| Input size | 128×128 (divisible by 32) |
|
|
79
|
+
| Parameters | ~2.6M (YOLO11n-class, scale `n`: depth 0.50 / width 0.25) |
|
|
80
|
+
| FLOPs | ~0.3 G @ 128px (6.5 G @ 640px, scaled) |
|
|
81
|
+
| Training | CPU-only (laptop, 8 cores) |
|
|
82
|
+
| Inference | ONNX Runtime, CPU; INT8 quantization optional |
|
|
83
|
+
| Dataset | PASCAL VOC 2007 (20 classes): trainval 5011 / test 4952 |
|
|
84
|
+
| Latency | < 10 ms/image @ 128px (TBD in Phase 8) |
|
|
85
|
+
| Accuracy | mAP@0.5 baseline TBD after first training run |
|
|
86
|
+
|
|
87
|
+
## Experiments (planned)
|
|
88
|
+
|
|
89
|
+
Three configs share the same neck/head, differing only in how the backbone is
|
|
90
|
+
initialized:
|
|
91
|
+
|
|
92
|
+
| Config | Init | Purpose |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `tiny_scratch` | none | from-scratch baseline |
|
|
95
|
+
| `yolo11n_finetune` | YOLO11n COCO weights, detect head reinit | likely best accuracy |
|
|
96
|
+
| `mobilenetv3_small` | ImageNet-pretrained backbone, fresh neck/head | pretrained-backbone experiment |
|
|
97
|
+
|
|
98
|
+
### Adaptive cross-scale exchange neck
|
|
99
|
+
|
|
100
|
+
`candyeye/core/modules/exchange.py` adds an optional `ScaleExchange` neck that sits
|
|
101
|
+
between the existing FPN and the Detect head. Instead of only the fixed
|
|
102
|
+
top-down/bottom-up path, adjacent pyramid levels (P3↔P4, P4↔P5) pass a cheap
|
|
103
|
+
depthwise message whose admission is controlled by a gate:
|
|
104
|
+
|
|
105
|
+
| Gate | Behaviour |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `none` | fixed 0.5 mix — ablation control |
|
|
108
|
+
| `static` | `sigmoid(learnable per-channel weight + bias)` — BiFPN-like |
|
|
109
|
+
| `dynamic` | gate computed at runtime from the two feature maps — content-conditioned |
|
|
110
|
+
|
|
111
|
+
The `dynamic` gate is the intended contribution: unlike BiFPN's fixed learned
|
|
112
|
+
scalar weights, the amount of exchange adapts per input. Gates initialize
|
|
113
|
+
near-closed (`bias -4.0`), so an exchange model starts close to the plain
|
|
114
|
+
baseline. The neck is selectable through the architecture YAML
|
|
115
|
+
(`configs/yolo11_exchange.yaml`, layer 23) and as a `neck: exchange` option on
|
|
116
|
+
the MobileNet model, with experiment configs under `configs/experiments/`
|
|
117
|
+
(`isda_baseline`, `isda_exchange_{none,static,dynamic}`, and the
|
|
118
|
+
`isda_mobilenet_*` variants).
|
|
119
|
+
|
|
120
|
+
## Install
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pip install CandyEye # from PyPI (once published)
|
|
124
|
+
pip install -e . # or, from a checkout
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
For a CPU-only PyTorch wheel (recommended on laptops):
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
pip install --index-url https://download.pytorch.org/whl/cpu torch torchvision
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The wheel bundles the default `yolo11`/`yolo11_exchange` architecture configs, so
|
|
134
|
+
`CandyEye()` works with no checkout and no download. The clean `yolo11n` weights
|
|
135
|
+
are **not** redistributed (they derive from Ultralytics' AGPL-3.0 checkpoints):
|
|
136
|
+
the first `train(..., pretrained=True)` downloads the official checkpoint and
|
|
137
|
+
caches a clean state_dict under `~/.cache/candyeye/`. Install the optional
|
|
138
|
+
converter with `pip install CandyEye[weights]`, or point `$CANDYEYE_WEIGHTS` at
|
|
139
|
+
an existing `.pth` to stay offline.
|
|
140
|
+
|
|
141
|
+
## Training
|
|
142
|
+
|
|
143
|
+
Training is available through CandyEye's Python API. The model config defines
|
|
144
|
+
the architecture (bundled names such as `"yolo11"` or `"yolo11_exchange"`, a
|
|
145
|
+
YAML path, or a parsed dict); the data YAML points to your dataset and split
|
|
146
|
+
names.
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from candyeye import CandyEye
|
|
150
|
+
|
|
151
|
+
model = CandyEye() # bundled yolo11 architecture
|
|
152
|
+
results = model.train(
|
|
153
|
+
data="dataset.yaml",
|
|
154
|
+
epochs=100,
|
|
155
|
+
imgsz=128,
|
|
156
|
+
batch=16,
|
|
157
|
+
patience=20,
|
|
158
|
+
workers=0,
|
|
159
|
+
pretrained=True, # start from the bundled yolo11n weights
|
|
160
|
+
project="runs/train",
|
|
161
|
+
name="voc_yolo11",
|
|
162
|
+
)
|
|
163
|
+
print(results["best"])
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
This CPU-first trainer saves `best.pt`, `last.pt`, and `metrics.csv` in the
|
|
167
|
+
run directory. Each epoch reports train and validation total, box,
|
|
168
|
+
classification, and DFL losses, plus aggregate precision, recall, F1,
|
|
169
|
+
mAP@0.5, and mAP@0.5:0.95. Per-class precision, recall, F1, AP@0.5, and
|
|
170
|
+
COCO AP are stored in `metrics.csv` rather than printed every epoch, together
|
|
171
|
+
with a small/medium/large mAP breakdown. Precision/recall/F1 use confidence 0.25 and IoU 0.5; AP uses
|
|
172
|
+
confidence 0.001 and the IoU sweep 0.50:0.05:0.95. `best.pt` is selected by validation mAP@0.5, and
|
|
173
|
+
`patience` stops after that metric fails to improve. Resume with `resume=True`
|
|
174
|
+
or pass a checkpoint path. `imgsz` must be divisible by 32. Detection
|
|
175
|
+
“accuracy” is not a standard object-detection metric, so use precision, recall,
|
|
176
|
+
F1, and AP/mAP to assess the model.
|
|
177
|
+
|
|
178
|
+
> **Note on `val_split: test`:** by default the trainer validates (and selects
|
|
179
|
+
> `best.pt`) on the VOC **test** split, so model selection sees the test set and
|
|
180
|
+
> reported test numbers are mildly optimistic. This default is kept for
|
|
181
|
+
> convenience; for an unbiased benchmark set `val_split` to a held-out split
|
|
182
|
+
> (e.g. `train`) in your data config.
|
|
183
|
+
|
|
184
|
+
The console prints a compact summary for each epoch. At the end of training,
|
|
185
|
+
the run directory also contains `results.png` (total/component losses,
|
|
186
|
+
validation metrics, and
|
|
187
|
+
learning-rate curves), `confusion_matrix.png` (for the best checkpoint at
|
|
188
|
+
confidence 0.25 and IoU 0.5), and `confusion_matrix.csv`. The matrix includes a
|
|
189
|
+
background row and column to show false positives and missed objects. The CSV
|
|
190
|
+
history remains available as `metrics.csv`.
|
|
191
|
+
|
|
192
|
+
### Run a trained model on an image or video
|
|
193
|
+
|
|
194
|
+
Use the Python predictor with an image path or video path. It loads class names
|
|
195
|
+
from the dataset YAML and saves annotated output under `runs/predict/` by
|
|
196
|
+
default:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from candyeye import CandyEyePredictor
|
|
200
|
+
|
|
201
|
+
predictor = CandyEyePredictor(
|
|
202
|
+
weights="runs/train/fruit_smoke_test/best.pt",
|
|
203
|
+
data="data/test_data/fruits.v5i.yolov11/data.yaml",
|
|
204
|
+
imgsz=128,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Run one image
|
|
208
|
+
image_result = predictor.predict(
|
|
209
|
+
"data/test_data/fruits.v5i.yolov11/test/images/15_jpg.rf.bdcbfdcabfa19ea0ca4b42a986bcb604.jpg",
|
|
210
|
+
conf=0.25,
|
|
211
|
+
)
|
|
212
|
+
print(image_result["output"])
|
|
213
|
+
print(image_result["detections"])
|
|
214
|
+
|
|
215
|
+
# Or run a video instead
|
|
216
|
+
video_result = predictor.predict("/path/to/clip.mp4", conf=0.25)
|
|
217
|
+
print(video_result["output"], video_result["frames"])
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Pass `output="path/to/result.jpg"` (or an `.mp4` path for video) to choose a
|
|
221
|
+
specific output file.
|
|
222
|
+
|
|
223
|
+
For a function-style entry point:
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from candyeye import train
|
|
227
|
+
|
|
228
|
+
results = train(data="dataset.yaml", epochs=100, imgsz=128, batch=16,
|
|
229
|
+
patience=20, pretrained=True)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Evaluation and benchmarking
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# COCO-style mAP@0.5:0.95 (+ optional size buckets) for saved checkpoints
|
|
236
|
+
PYTHONPATH=. venv/bin/python scripts/evaluate.py \
|
|
237
|
+
--run configs/experiments/isda_exchange_dynamic.yaml \
|
|
238
|
+
runs/experiments/isda_exchange_dynamic/best.pt --size-buckets
|
|
239
|
+
|
|
240
|
+
# params, GFLOPs, and CPU latency at the configured image size
|
|
241
|
+
PYTHONPATH=. venv/bin/python scripts/benchmark.py \
|
|
242
|
+
--run configs/experiments/isda_baseline.yaml \
|
|
243
|
+
--run configs/experiments/isda_exchange_dynamic.yaml
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
`scripts/evaluate.py` accepts both VOC and `format: yolo_txt` configs and
|
|
247
|
+
writes a per-checkpoint AP table; `scripts/benchmark.py` uses PyTorch's
|
|
248
|
+
built-in FLOP counter (no extra dependency).
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
CandyEye accepts the common Roboflow image-folder and normalized `.txt` label
|
|
253
|
+
export. Its `data.yaml` can look like this (paths may be relative to the YAML):
|
|
254
|
+
|
|
255
|
+
```yaml
|
|
256
|
+
path: /datasets/my-export
|
|
257
|
+
train: train/images
|
|
258
|
+
valid: valid/images
|
|
259
|
+
test: test/images
|
|
260
|
+
nc: 2
|
|
261
|
+
names: [cat, dog]
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Each image needs a same-stem label file under the matching `labels/` folder;
|
|
265
|
+
each nonempty row is `class_id center_x center_y width height`, normalized to
|
|
266
|
+
the image dimensions. Pass the YAML path directly; CandyEye reads `nc` (or
|
|
267
|
+
counts `names`) and configures its class head automatically:
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
from candyeye import CandyEye
|
|
271
|
+
|
|
272
|
+
data_yaml = "/datasets/my-export/data.yaml"
|
|
273
|
+
model = CandyEye() # or CandyEye("yolo11_exchange") for the exchange neck
|
|
274
|
+
results = model.train(data=data_yaml, epochs=100, imgsz=128,
|
|
275
|
+
batch=16, patience=20)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
See [ROADMAP.md](ROADMAP.md) for current evaluation and export work.
|
|
279
|
+
|
|
280
|
+
> **Note:** package installation currently doesn't expose the modules outside
|
|
281
|
+
> the repo root — run scripts from the repo root (see
|
|
282
|
+
> [STATUS.md](STATUS.md#known-issues) for details).
|
|
283
|
+
|
|
284
|
+
## Project structure
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
CandyEye/
|
|
288
|
+
├── candyeye/ # pip-installable package (import candyeye)
|
|
289
|
+
│ ├── __init__.py # public API: CandyEye, train, CandyEyePredictor
|
|
290
|
+
│ ├── paths.py # packaged config + weight resolution
|
|
291
|
+
│ ├── configs/ # bundled architecture YAMLs (package data)
|
|
292
|
+
│ │ ├── yolo11.yaml
|
|
293
|
+
│ │ └── yolo11_exchange.yaml
|
|
294
|
+
│ ├── assets/yolo11n.pth # bundled clean weights (package data)
|
|
295
|
+
│ ├── core/ # model builder + nn.Module blocks
|
|
296
|
+
│ │ ├── functions/ # stateless helpers (autopad, make_divisible)
|
|
297
|
+
│ │ ├── modules/ # Conv, Bottleneck, C3k2, SPPF, C2PSA, DFL, Detect, ScaleExchange
|
|
298
|
+
│ │ ├── candyeye.py # YAML builder + forward graph
|
|
299
|
+
│ │ └── convert_yolo11.py # yolo11n.pt / ONNX → our state_dict
|
|
300
|
+
│ ├── data/ # VOC + YOLO-txt datasets, transforms
|
|
301
|
+
│ ├── training/ # assigner, loss, trainer
|
|
302
|
+
│ ├── eval/ # mAP (mAP@0.5, mAP@0.5:0.95, size buckets)
|
|
303
|
+
│ └── inference/ # predict.py (CandyEyePredictor)
|
|
304
|
+
├── configs/experiments/ # ablation configs (Isda 9-class)
|
|
305
|
+
├── data/ # datasets + VOCdevkit (gitignored)
|
|
306
|
+
├── export/ # ONNX export, benchmark, INT8 (Phase 8)
|
|
307
|
+
├── scripts/ # inspect_data, evaluate, benchmark, bootstrap_weights
|
|
308
|
+
├── runs/ # outputs (gitignored)
|
|
309
|
+
└── tests/ # shape, assigner, loss, mAP, exchange, ONNX parity
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## How it's built
|
|
313
|
+
|
|
314
|
+
- **Written from scratch** — no Ultralytics runtime dependency; the official
|
|
315
|
+
`yolo11n.pt` is used only as a weight source (see license note below).
|
|
316
|
+
- **Phase-driven** — each phase has a spec, an acceptance checklist, and a
|
|
317
|
+
code review before moving on. Current state always lives in
|
|
318
|
+
[STATUS.md](STATUS.md).
|
|
319
|
+
- **Architecture basis:** YOLOv11 (anchor-free head, DFL, Task-Aligned
|
|
320
|
+
assignment, C3k2/SPPF blocks), MobileNetV3 as the efficiency reference
|
|
321
|
+
(Experiment B), and a YOLACT-style *parallel* structure — neck scales and
|
|
322
|
+
the head's box/class branches compute over shared conv features
|
|
323
|
+
simultaneously. YOLACT's prototype-mask half is out of scope: bounding
|
|
324
|
+
boxes only (revisit only if segmentation is ever added).
|
|
325
|
+
|
|
326
|
+
## References
|
|
327
|
+
|
|
328
|
+
- [YOLO11](https://docs.ultralytics.com/models/yolo11/) — Ultralytics
|
|
329
|
+
- [YOLACT: Real-time Instance Segmentation](https://arxiv.org/abs/1904.02689) — Bolya et al. (parallel neck/head inspiration; mask branch out of scope)
|
|
330
|
+
- [Task-Aligned One-stage Object Detection (TOOD)](https://arxiv.org/abs/2108.07755) — assignment strategy
|
|
331
|
+
- [Generalized Focal Loss](https://arxiv.org/abs/2006.04388) — Distribution Focal Loss
|
|
332
|
+
- [PASCAL VOC](http://host.robots.ox.ac.uk/pascal/VOC/)
|
|
333
|
+
|
|
334
|
+
## License
|
|
335
|
+
|
|
336
|
+
[GPL-3.0](LICENSE) — Copyright (c) 2026 Seventeen23.
|
|
337
|
+
|
|
338
|
+
**Pretrained weights note:** YOLO11 weights are licensed AGPL-3.0 by
|
|
339
|
+
Ultralytics. They are used here for personal experimentation/fine-tuning only;
|
|
340
|
+
weights files are `.gitignore`d and are not redistributed with this repository.
|
|
341
|
+
Models trained from those weights may inherit AGPL obligations — check
|
|
342
|
+
[Ultralytics' license](https://ultralytics.com/license) before distributing
|
|
343
|
+
trained weights.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
CHANGELOG.md
|
|
2
|
+
LICENSE
|
|
3
|
+
MANIFEST.in
|
|
4
|
+
README.md
|
|
5
|
+
pyproject.toml
|
|
6
|
+
CandyEye.egg-info/PKG-INFO
|
|
7
|
+
CandyEye.egg-info/SOURCES.txt
|
|
8
|
+
CandyEye.egg-info/dependency_links.txt
|
|
9
|
+
CandyEye.egg-info/entry_points.txt
|
|
10
|
+
CandyEye.egg-info/requires.txt
|
|
11
|
+
CandyEye.egg-info/top_level.txt
|
|
12
|
+
candyeye/__init__.py
|
|
13
|
+
candyeye/paths.py
|
|
14
|
+
candyeye/configs/yolo11.yaml
|
|
15
|
+
candyeye/configs/yolo11_exchange.yaml
|
|
16
|
+
candyeye/core/__init__.py
|
|
17
|
+
candyeye/core/backbone_mobilenet.py
|
|
18
|
+
candyeye/core/candyeye.py
|
|
19
|
+
candyeye/core/convert_yolo11.py
|
|
20
|
+
candyeye/core/functions/__init__.py
|
|
21
|
+
candyeye/core/functions/layer_utils.py
|
|
22
|
+
candyeye/core/modules/__init__.py
|
|
23
|
+
candyeye/core/modules/blocks.py
|
|
24
|
+
candyeye/core/modules/conv.py
|
|
25
|
+
candyeye/core/modules/detect.py
|
|
26
|
+
candyeye/core/modules/exchange.py
|
|
27
|
+
candyeye/data/__init__.py
|
|
28
|
+
candyeye/data/transforms.py
|
|
29
|
+
candyeye/data/voc.py
|
|
30
|
+
candyeye/data/yolo.py
|
|
31
|
+
candyeye/eval/__init__.py
|
|
32
|
+
candyeye/eval/detection_metrics.py
|
|
33
|
+
candyeye/eval/map.py
|
|
34
|
+
candyeye/inference/__init__.py
|
|
35
|
+
candyeye/inference/predict.py
|
|
36
|
+
candyeye/training/__init__.py
|
|
37
|
+
candyeye/training/assigner.py
|
|
38
|
+
candyeye/training/loss.py
|
|
39
|
+
candyeye/training/trainer.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
torch>=2.0
|
|
2
|
+
torchvision>=0.15
|
|
3
|
+
numpy>=1.24
|
|
4
|
+
opencv-python-headless>=4.8
|
|
5
|
+
pyyaml>=6.0
|
|
6
|
+
|
|
7
|
+
[dev]
|
|
8
|
+
pytest>=7.4
|
|
9
|
+
matplotlib>=3.8
|
|
10
|
+
onnx>=1.16
|
|
11
|
+
|
|
12
|
+
[onnx]
|
|
13
|
+
onnx>=1.16
|
|
14
|
+
|
|
15
|
+
[parity]
|
|
16
|
+
onnx>=1.16
|
|
17
|
+
onnxruntime>=1.17
|
|
18
|
+
|
|
19
|
+
[plots]
|
|
20
|
+
matplotlib>=3.8
|
|
21
|
+
|
|
22
|
+
[release]
|
|
23
|
+
build>=1.2
|
|
24
|
+
twine>=5.0
|
|
25
|
+
|
|
26
|
+
[weights]
|
|
27
|
+
ultralytics>=8.3
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
candyeye
|