dieCamera dice readers
Four ONNX models that read physical polyhedral dice from a webcam frame: which dice are on the table, and what each one landed on. They run offline on CPU or any DX12 GPU (DirectML) in ~50β100 ms a frame, and are the local reader inside dieCamera.
Trained on G-G-Games/diecamera-dice.
| file | job | size |
|---|---|---|
dice-shape.onnx |
YOLOv8 detector β finds each die, classifies its type | 43MB |
dice-value-glyph.onnx |
YOLOv8 detector β finds the up-face numeral on each die | 43MB |
dice-value.onnx |
ConvNeXt classifier β reads that numeral, 21-way "0".."20" |
106MB |
dice-value-cls.onnx |
YOLOv8-cls β reads a whole-die crop directly, 60-way | 20MB |
Each ships with a .classes.json giving its output order. The class list is not
optional β the indices mean nothing without it.
Two ways to read a face
dice-shape finds and types the dice in both pipelines. What differs is how the numeral
is read:
Three-pass (dice-shape β dice-value-glyph β dice-value) localises the numeral on
the full frame and classifies a tight crop of it. Argmax is gated to the die type's legal
range, so a "15" can never come back for a d8, and the returned confidence is a softmax
over the in-range classes only.
Two-pass (dice-shape β dice-value-cls) skips localisation and classifies the die
crop whole, as one of 60 (type, face) classes. A fifth the size and one less model call.
Both are shipped. Which is better is genuinely unsettled β see below.
Accuracy, honestly
Reported by their authors, on their own held-out splits:
dice-shapeβ mAP50 0.886 on a shared three-domain validation set (v2). Its predecessor scored 0.995 on a single-domain split and 0.752 here; the higher number was an easier test set, not a better model.dice-value(three-pass) β cross-validated per type: d8 0.88 Β· d10 0.72 Β· d12 0.75 Β· d20 0.55.dice-value-cls(two-pass) β face top-1 0.411 on held-out crops. Most of that came from training at 224px instead of downscaling crops to 128, which was worth +0.12 by itself.
Measured head-to-head on identical frames from the training corpus:
| frames | three-pass | two-pass |
|---|---|---|
| whole corpus (563 dice) | 0.490 | 0.480 |
| 30 newest, sharp 5MP (113 dice) | 0.575 | 0.699 |
Do not read that table as a verdict. Both models were trained on this corpus with splits that were not recorded, and the two-pass model was retrained after most of those newest frames existed β so its 0.699 is partly recall. Only five frames in the corpus postdate its training. There is no clean shared holdout yet, and the per-author numbers above are better evidence than anything measured after the fact.
Die-type detection is identical either way (0.961 on the same frames) because both start
from dice-shape.
What they are not
These are rig specialists, not a general dice reader. They were trained on a handful of webcams over a small tray, and accuracy falls off sharply on unfamiliar lighting, backgrounds, or dice sets. The best single predictor of whether they will work for you is how many pixels land on a die face: the reference rig reads well at ~270px across a die and poorly at ~140px.
The known failure is 6 vs 9 on a d10, which is ambiguous on plenty of real dice sets and is not fully solvable from pixels alone.
Consuming application note: dieCamera never posts a face without showing it to a human first. A fast, correctable guess is more useful there than a slow accurate one β these models are tuned for that trade, not for autonomous use.
Usage
import json, onnxruntime as ort
sess = ort.InferenceSession("dice-shape.onnx")
classes = json.load(open("dice-shape.classes.json"))["classes"]
# 640x640 letterboxed RGB, NCHW, 0-1 -> standard YOLOv8 output; NMS, then classes[class_id]
The full pipeline β letterboxing, NMS, glyph-to-die matching, the range gate, and the
ImageNet normalisation the classifier expects β is implemented in TypeScript in
src/readers/local/.
The crop recipe must match training exactly (detector box + 0.1 margin, stretch-resized to
224Γ224, ImageNet-normalised); drifting from it reintroduced a domain-shift bug that once
took a value model from 97% on web images to 17.5% on the real rig.
Credits
dice-value.onnxanddice-value-glyph.onnxβ the three-pass value reader, and its runtime, by Triveni Gandhi (@trivenigandhi), contributed in dieCamera PR #6. She also contributed capture frames to the dataset.dice-shape.onnxanddice-value-cls.onnxβ @eschatus.
License
AGPL-3.0. Use, modify, or redistribute these weights β including as part of a hosted or network-accessible service β under AGPL-3.0, which requires releasing corresponding source for your derivative under the same terms. A separate commercial license, without the AGPL's copyleft/source-disclosure obligations, is available from G-G-Games for closed commercial use; contact us for terms. The dieCamera application itself is licensed separately (see its own repo). The training data is CC BY-SA 4.0 and published separately.
(Provisional β this replaces an earlier MIT license on this card. Not yet cleared by counsel; do not rely on the enforceability of the commercial-license carve-out until a lawyer has reviewed it.)