initial release: 15-class MobileNetV3-small GUI element classifier
Browse files- MANIFEST.md +42 -0
- README.md +138 -0
- classes.json +21 -0
- inference_example.py +105 -0
- mobilenetv3_small.onnx +3 -0
- mobilenetv3_small.pth +3 -0
MANIFEST.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Artifact Manifest — GUI Element Classifier
|
| 2 |
+
|
| 3 |
+
Standalone bundle of a 15-class MobileNetV3-small GUI element type classifier, prepared for HuggingFace Hub release.
|
| 4 |
+
|
| 5 |
+
## Files
|
| 6 |
+
|
| 7 |
+
| File | sha256 | Purpose |
|
| 8 |
+
|---|---|---|
|
| 9 |
+
| `mobilenetv3_small.onnx` | `5e17d4c43de2927ca3f6ab56e46edf44d2d5ef991fa0afc5d1892bbbf65fbf05` | ONNX export, fixed batch=1, primary inference artifact |
|
| 10 |
+
| `mobilenetv3_small.pth` | `a1176d43ce666e819d712892b0c8e47bdff99616f094c3e5e58d2ff886535292` | PyTorch state_dict source for the same weights — useful for fine-tuning further or re-exporting with dynamic batch axis |
|
| 11 |
+
| `classes.json` | (small) | 15-class taxonomy + index ordering |
|
| 12 |
+
| `inference_example.py` | (small) | Self-contained 100-line inference demo |
|
| 13 |
+
| `README.md` | (small) | Model card |
|
| 14 |
+
|
| 15 |
+
## Architecture & training
|
| 16 |
+
|
| 17 |
+
- **Architecture:** MobileNetV3-small, 15-class output head.
|
| 18 |
+
- **Training data:** A curated set of GUI element crops drawn primarily from Linux desktop UIs (~7000 crops across the 15 classes after consolidation).
|
| 19 |
+
- **Training framework:** PyTorch with `torchvision.models.mobilenet_v3_small` backbone + cross-entropy loss on the 15-class head; ImageNet-pretrained backbone fine-tuned end-to-end.
|
| 20 |
+
- **Held-out test set:** 1449 examples sampled from the training distribution (not strictly out-of-distribution).
|
| 21 |
+
- **Export:** ONNX opset 17, fixed batch_size=1.
|
| 22 |
+
- **Training-time metrics:** accuracy ≈ 0.72, macro-F1 ≈ 0.58, weighted-F1 ≈ 0.76. Per-class F1 details and the limits of these metrics are spelled out in `README.md`.
|
| 23 |
+
|
| 24 |
+
## Framework versions used during this export
|
| 25 |
+
|
| 26 |
+
- Python 3.11
|
| 27 |
+
- ONNX Runtime 1.25.x (validated on the inference path)
|
| 28 |
+
- PIL (Pillow) 10.4+
|
| 29 |
+
- NumPy 1.26+
|
| 30 |
+
|
| 31 |
+
## Build date
|
| 32 |
+
|
| 33 |
+
- Weights frozen / exported: 2026-03 (training run). Bundle assembled for HF release: 2026-05-02.
|
| 34 |
+
|
| 35 |
+
## Verification
|
| 36 |
+
|
| 37 |
+
After download, verify the ONNX file hash matches:
|
| 38 |
+
|
| 39 |
+
```bash
|
| 40 |
+
sha256sum mobilenetv3_small.onnx
|
| 41 |
+
# expected: 5e17d4c43de2927ca3f6ab56e46edf44d2d5ef991fa0afc5d1892bbbf65fbf05
|
| 42 |
+
```
|
README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
library_name: onnx
|
| 6 |
+
tags:
|
| 7 |
+
- gui-element-classification
|
| 8 |
+
- ui-classification
|
| 9 |
+
- desktop-agents
|
| 10 |
+
- agentic-ai
|
| 11 |
+
- mobilenet-v3
|
| 12 |
+
- computer-use
|
| 13 |
+
- onnx
|
| 14 |
+
pipeline_tag: image-classification
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
# GUI Element Classifier — MobileNetV3-small (15 classes)
|
| 18 |
+
|
| 19 |
+
Lightweight (~6 MB) MobileNetV3-small ONNX classifier for **15 GUI element types**. CPU-friendly (~5 ms per crop, ONNX Runtime), designed as a deterministic preprocessing step before VLM-based GUI agent pipelines. No GPU required, no external dependencies beyond `onnxruntime`, `numpy`, and `pillow`.
|
| 20 |
+
|
| 21 |
+
## What it's for
|
| 22 |
+
|
| 23 |
+
You have a screenshot, a list of detected element bounding boxes (from any detector — YOLOv8, OWL-ViT, SAM-then-filter, accessibility tree, anything else), and you need cheap, deterministic per-element type labels (`button` vs `text_input` vs `slider` vs …) before passing the structured layout to a reasoning LLM. Drop this classifier into the pipeline as the typing layer:
|
| 24 |
+
|
| 25 |
+
```
|
| 26 |
+
[Screenshot]
|
| 27 |
+
↓
|
| 28 |
+
[Your detector] → list of bboxes
|
| 29 |
+
↓
|
| 30 |
+
[This classifier] → per-bbox type label + confidence
|
| 31 |
+
↓
|
| 32 |
+
[Your reasoning / action LLM] → reasons over typed elements, not pixels
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
The classifier is not a replacement for VLM captioning — it's the cheap deterministic layer that adds structure to your prompt so the LLM doesn't have to look at every region just to figure out what it is.
|
| 36 |
+
|
| 37 |
+
## Classes (15)
|
| 38 |
+
|
| 39 |
+
`button`, `checkbox`, `container`, `dropdown`, `icon_button`, `image`, `label`, `link`, `menu_item`, `scrollbar`, `slider`, `tab`, `text_input`, `toggle`, `unknown`
|
| 40 |
+
|
| 41 |
+
The class indices in the model output (0..14) match the alphabetical ordering above. See [`classes.json`](classes.json) for the canonical list.
|
| 42 |
+
|
| 43 |
+
## Files
|
| 44 |
+
|
| 45 |
+
| File | Purpose |
|
| 46 |
+
|---|---|
|
| 47 |
+
| `mobilenetv3_small.onnx` | ONNX export (fixed batch=1). Primary inference artifact. |
|
| 48 |
+
| `mobilenetv3_small.pth` | PyTorch state_dict for those who want to fine-tune further or re-export with dynamic axes. |
|
| 49 |
+
| `classes.json` | Class names + ordering. |
|
| 50 |
+
| `inference_example.py` | 100-line self-contained demo. `pip install onnxruntime numpy pillow` then `python inference_example.py crop.png`. |
|
| 51 |
+
|
| 52 |
+
## Quick start
|
| 53 |
+
|
| 54 |
+
```python
|
| 55 |
+
from PIL import Image
|
| 56 |
+
import numpy as np
|
| 57 |
+
import onnxruntime as ort
|
| 58 |
+
|
| 59 |
+
CLASSES = ['button','checkbox','container','dropdown','icon_button',
|
| 60 |
+
'image','label','link','menu_item','scrollbar',
|
| 61 |
+
'slider','tab','text_input','toggle','unknown']
|
| 62 |
+
|
| 63 |
+
MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32)
|
| 64 |
+
STD = np.array([0.229, 0.224, 0.225], dtype=np.float32)
|
| 65 |
+
|
| 66 |
+
def preprocess(img: Image.Image) -> np.ndarray:
|
| 67 |
+
img = img.convert("RGB")
|
| 68 |
+
w, h = img.size
|
| 69 |
+
m = max(w, h)
|
| 70 |
+
pad = Image.new("RGB", (m, m), (128, 128, 128))
|
| 71 |
+
pad.paste(img, ((m - w) // 2, (m - h) // 2))
|
| 72 |
+
arr = np.array(pad.resize((224, 224), Image.BILINEAR), dtype=np.float32) / 255.0
|
| 73 |
+
arr = (arr - MEAN) / STD
|
| 74 |
+
return arr.transpose(2, 0, 1)[None, :, :, :].astype(np.float32)
|
| 75 |
+
|
| 76 |
+
sess = ort.InferenceSession("mobilenetv3_small.onnx",
|
| 77 |
+
providers=["CPUExecutionProvider"])
|
| 78 |
+
crop = Image.open("button.png")
|
| 79 |
+
logits = sess.run(None, {sess.get_inputs()[0].name: preprocess(crop)})[0]
|
| 80 |
+
probs = np.exp(logits - logits.max()) / np.exp(logits - logits.max()).sum()
|
| 81 |
+
idx = int(probs.argmax())
|
| 82 |
+
print(CLASSES[idx], float(probs[0, idx]))
|
| 83 |
+
```
|
| 84 |
+
|
| 85 |
+
## Preprocessing (must-match-or-quality-degrades)
|
| 86 |
+
|
| 87 |
+
1. **PadToSquare with gray (128, 128, 128)** on the shorter axis.
|
| 88 |
+
2. **Resize to 224x224** with `Image.BILINEAR`.
|
| 89 |
+
3. `array / 255.0` → float32 in [0, 1].
|
| 90 |
+
4. **ImageNet normalize**: `mean=[0.485, 0.456, 0.406]`, `std=[0.229, 0.224, 0.225]`.
|
| 91 |
+
5. Transpose HWC → CHW. Add batch dim.
|
| 92 |
+
|
| 93 |
+
The model is sensitive to all five steps — wrong pad colour, BICUBIC instead of BILINEAR, or skipping the ImageNet stats will degrade accuracy noticeably.
|
| 94 |
+
|
| 95 |
+
## Performance
|
| 96 |
+
|
| 97 |
+
Aggregate metrics from the training-time held-out evaluation:
|
| 98 |
+
|
| 99 |
+
| Metric | Value |
|
| 100 |
+
|---|---|
|
| 101 |
+
| Test set | 1449 examples (sampled from the training distribution, **not strictly out-of-distribution** — see Limitations) |
|
| 102 |
+
| Accuracy | ~0.72 |
|
| 103 |
+
| Macro F1 | ~0.58 |
|
| 104 |
+
| Weighted F1 | ~0.76 |
|
| 105 |
+
|
| 106 |
+
Weighted F1 is dominated by the `icon_button` class (~50% of test support, F1=0.78). Per-class F1s vary widely — `tab` and `text_input` clear 0.95+, `container` and `slider` lag at 0.15-0.25. **If your domain skews heavily to specific classes, expect class-imbalance effects.**
|
| 107 |
+
|
| 108 |
+
**Latency:** ~5 ms per crop on a modern x86 laptop CPU (Intel i7-12th gen, single thread). The shipped ONNX is fixed batch_size=1 for the simplest possible drop-in; if you're processing >100 crops per screenshot, re-export with dynamic axes from the included `.pth` for batching.
|
| 109 |
+
|
| 110 |
+
## Limitations & honest scope
|
| 111 |
+
|
| 112 |
+
- **Training-time test-set metrics are not a tight estimate of accuracy on your domain.** The aggregate numbers above (acc ~0.72, weighted F1 ~0.76) come from a single held-out split **sampled from the training distribution — this is not strictly out-of-distribution evaluation**. On a domain that differs from the training mix (web vs desktop, different OS look-and-feel, dark mode vs light mode, custom design systems), expect a meaningful gap from these numbers. Validate on your own labelled crops before depending on the figures.
|
| 113 |
+
- **Per-class F1 varies widely.** `tab` and `text_input` clear 0.95+, `icon_button` sits around 0.78 (and dominates weighted F1 because of class imbalance — ~50% of test support), while `container` and `slider` lag at 0.15-0.25. The aggregate numbers (acc / macro / weighted F1) hide this variance — read the per-class story above before trusting the headline.
|
| 114 |
+
- **Linux desktop bias.** Training data skews toward Linux desktop UIs (XFCE / GTK toolkits / Firefox / Mousepad / Thunar / terminal). Web pages, macOS, Windows 11, and mobile UIs are likely under-represented and may need domain adaptation.
|
| 115 |
+
- **`unknown` is a real class.** When the classifier produces `unknown` with high confidence, the input is genuinely ambiguous (small icon with no clear visual identity); don't paper over it with `argmax-but-skip-unknown` logic.
|
| 116 |
+
- **`container` and `slider` underperform** at training-time evaluation. Consider using bbox geometry as a sanity check (containers are large, sliders are wide-and-thin) alongside the model rather than trusting it alone for those two classes.
|
| 117 |
+
- **Single-label argmax.** No multi-class output. If a region could legitimately be both `tab` and `button` (some apps style tabs as buttons), the model picks one.
|
| 118 |
+
- **Fixed batch_size=1 in the shipped ONNX.** For high-throughput scenarios, re-export from the included `.pth` with dynamic axes (`torch.onnx.export(..., dynamic_axes={'input': {0: 'batch'}})`).
|
| 119 |
+
- **ImageNet preprocessing is assumed.** The model was trained against the standard ImageNet mean/std + PadToSquare-with-gray. Substituting different normalization will silently degrade results.
|
| 120 |
+
|
| 121 |
+
## License
|
| 122 |
+
|
| 123 |
+
Apache-2.0. The MobileNetV3-small architecture itself was originally introduced by Google Research; this export uses the open architecture and re-trained weights.
|
| 124 |
+
|
| 125 |
+
## Citation
|
| 126 |
+
|
| 127 |
+
```
|
| 128 |
+
@misc{gui_element_classifier_mobilenetv3_2026,
|
| 129 |
+
author = {Diogo Neno},
|
| 130 |
+
title = {15-class MobileNetV3-small GUI Element Classifier},
|
| 131 |
+
year = {2026},
|
| 132 |
+
url = {https://huggingface.co/diogoneno/gui-element-classifier},
|
| 133 |
+
}
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
## Changelog
|
| 137 |
+
|
| 138 |
+
- `2026-05-02` — Initial public release.
|
classes.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"classes": [
|
| 3 |
+
"button",
|
| 4 |
+
"checkbox",
|
| 5 |
+
"container",
|
| 6 |
+
"dropdown",
|
| 7 |
+
"icon_button",
|
| 8 |
+
"image",
|
| 9 |
+
"label",
|
| 10 |
+
"link",
|
| 11 |
+
"menu_item",
|
| 12 |
+
"scrollbar",
|
| 13 |
+
"slider",
|
| 14 |
+
"tab",
|
| 15 |
+
"text_input",
|
| 16 |
+
"toggle",
|
| 17 |
+
"unknown"
|
| 18 |
+
],
|
| 19 |
+
"n_classes": 15,
|
| 20 |
+
"ordering": "alphabetical (model output index 0..14 corresponds to this list in order)"
|
| 21 |
+
}
|
inference_example.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Self-contained inference example for the 15-class UI-element classifier.
|
| 2 |
+
|
| 3 |
+
Run:
|
| 4 |
+
pip install onnxruntime numpy pillow
|
| 5 |
+
python inference_example.py path/to/element_crop.png
|
| 6 |
+
|
| 7 |
+
Designed to drop into an LLM-orchestration loop where you have a screenshot,
|
| 8 |
+
a list of detected element bounding boxes (from any detector — YOLOv8, OWL-ViT,
|
| 9 |
+
SAM-then-filter, accessibility tree, etc.), and you need cheap, deterministic
|
| 10 |
+
per-element type labels before passing them to a reasoning LLM.
|
| 11 |
+
|
| 12 |
+
Inference is CPU-friendly (~5 ms per crop on a modern x86 laptop). Use it as a
|
| 13 |
+
'helper' that adds structure to the orchestrator's prompt — e.g., 'click the
|
| 14 |
+
text_input near label "Username"' — instead of paying VLM tokens to look at
|
| 15 |
+
every crop.
|
| 16 |
+
"""
|
| 17 |
+
from __future__ import annotations
|
| 18 |
+
|
| 19 |
+
import json
|
| 20 |
+
import sys
|
| 21 |
+
from pathlib import Path
|
| 22 |
+
|
| 23 |
+
import numpy as np
|
| 24 |
+
import onnxruntime as ort
|
| 25 |
+
from PIL import Image
|
| 26 |
+
|
| 27 |
+
HERE = Path(__file__).parent
|
| 28 |
+
ONNX_PATH = HERE / "mobilenetv3_small.onnx"
|
| 29 |
+
CLASSES = json.loads((HERE / "classes.json").read_text())["classes"]
|
| 30 |
+
|
| 31 |
+
IMAGENET_MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32)
|
| 32 |
+
IMAGENET_STD = np.array([0.229, 0.224, 0.225], dtype=np.float32)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def pad_to_square(img: Image.Image) -> Image.Image:
|
| 36 |
+
"""Pad shorter side with gray (128, 128, 128) — must match training transform."""
|
| 37 |
+
w, h = img.size
|
| 38 |
+
m = max(w, h)
|
| 39 |
+
out = Image.new("RGB", (m, m), (128, 128, 128))
|
| 40 |
+
out.paste(img, ((m - w) // 2, (m - h) // 2))
|
| 41 |
+
return out
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def preprocess(img: Image.Image) -> np.ndarray:
|
| 45 |
+
"""PadToSquare -> Resize 224x224 BILINEAR -> /255 -> ImageNet normalize -> CHW."""
|
| 46 |
+
img = pad_to_square(img.convert("RGB"))
|
| 47 |
+
img = img.resize((224, 224), Image.BILINEAR)
|
| 48 |
+
arr = np.array(img, dtype=np.float32) / 255.0
|
| 49 |
+
arr = (arr - IMAGENET_MEAN) / IMAGENET_STD
|
| 50 |
+
arr = arr.transpose(2, 0, 1)
|
| 51 |
+
return arr[None, :, :, :].astype(np.float32) # (1, 3, 224, 224)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def softmax(x: np.ndarray) -> np.ndarray:
|
| 55 |
+
e = np.exp(x - np.max(x, axis=1, keepdims=True))
|
| 56 |
+
return e / np.sum(e, axis=1, keepdims=True)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def classify(crop_path: str | Path) -> dict:
|
| 60 |
+
"""Classify a single element crop. Returns label, confidence, full score map."""
|
| 61 |
+
so = ort.SessionOptions()
|
| 62 |
+
so.intra_op_num_threads = 4
|
| 63 |
+
sess = ort.InferenceSession(str(ONNX_PATH), sess_options=so, providers=["CPUExecutionProvider"])
|
| 64 |
+
img = Image.open(crop_path)
|
| 65 |
+
batch = preprocess(img)
|
| 66 |
+
logits = sess.run(None, {sess.get_inputs()[0].name: batch})[0]
|
| 67 |
+
probs = softmax(logits)[0]
|
| 68 |
+
idx = int(np.argmax(probs))
|
| 69 |
+
return {
|
| 70 |
+
"label": CLASSES[idx],
|
| 71 |
+
"confidence": float(probs[idx]),
|
| 72 |
+
"scores": {c: float(probs[i]) for i, c in enumerate(CLASSES)},
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def classify_batch(crop_paths: list[str | Path]) -> list[dict]:
|
| 77 |
+
"""Convenience: per-crop loop. The shipped ONNX is fixed batch_size=1.
|
| 78 |
+
|
| 79 |
+
For higher throughput on large batches, re-export with dynamic axes and
|
| 80 |
+
run a single batched session.run() — kept simple here for clarity.
|
| 81 |
+
"""
|
| 82 |
+
so = ort.SessionOptions()
|
| 83 |
+
so.intra_op_num_threads = 4
|
| 84 |
+
sess = ort.InferenceSession(str(ONNX_PATH), sess_options=so, providers=["CPUExecutionProvider"])
|
| 85 |
+
results = []
|
| 86 |
+
for p in crop_paths:
|
| 87 |
+
img = Image.open(p)
|
| 88 |
+
batch = preprocess(img)
|
| 89 |
+
logits = sess.run(None, {sess.get_inputs()[0].name: batch})[0]
|
| 90 |
+
probs = softmax(logits)[0]
|
| 91 |
+
idx = int(np.argmax(probs))
|
| 92 |
+
results.append({
|
| 93 |
+
"label": CLASSES[idx],
|
| 94 |
+
"confidence": float(probs[idx]),
|
| 95 |
+
})
|
| 96 |
+
return results
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
if __name__ == "__main__":
|
| 100 |
+
if len(sys.argv) < 2:
|
| 101 |
+
print("usage: python inference_example.py <crop.png> [<crop2.png> ...]")
|
| 102 |
+
sys.exit(1)
|
| 103 |
+
for path in sys.argv[1:]:
|
| 104 |
+
result = classify(path)
|
| 105 |
+
print(f"{path}: {result['label']} (confidence={result['confidence']:.3f})")
|
mobilenetv3_small.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5e17d4c43de2927ca3f6ab56e46edf44d2d5ef991fa0afc5d1892bbbf65fbf05
|
| 3 |
+
size 6142536
|
mobilenetv3_small.pth
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a1176d43ce666e819d712892b0c8e47bdff99616f094c3e5e58d2ff886535292
|
| 3 |
+
size 6266042
|