lgd-cards-gen4 β€” playing-card corner-pip detector (spread-recall fine-tune)

4th-generation card detector for Live Game Defender, an on-prem computer-vision integrity monitor for live casino table games. Detects the 52 rank+suit corner index pips (AS, 10H, KD, …) on an overhead table camera β€” one card shows up to two pip boxes; whole-card assembly happens downstream in the tracker, not here.

License / provenance. AGPL-3.0. This is a fine-tune of Ultralytics YOLO11s (COCO pre-trained), so the weights are a YOLO derivative β€” not "ours". Downstream use inherits AGPL.

⚠️ Not casino accuracy. All numbers below are on our own PoC / Czech Croupier Academy footage with LLM-verified labels, not human casino ground truth. Treat as PoC metrics only.

Status: spread specialist β€” NOT the production model

gen4 was activated on 2026-07-20 and reverted the same day: its gameplay-precision trade broke the ante-integrity pillar on gameplay footage. Two measured issues (KAS-35 E2E, uth_player3_ante_paid.mp4): (a) hot nested double-reads β€” it read a 7H pip's bare rank digit as "7C" at 73% confidence, minting a phantom board pair that faked "dealer qualifies"; (b) it missed two players' hole cards outright. lgd-cards-gen3 is the production/served model. gen4 remains the best deck-spread specialist (45/52 live deck check vs fewer under gen3); the planned gen5 warm-starts from gen3's weights + this model's spread data and must beat both the spread gate and the gameplay E2E.

Why gen4

gen4 targets the one thing prior gens were weak at: recall on a fully spread deck (the dealer fanning all 52 cards face-up β€” the KAS-52 "cards missing / only 1 card" report, and the input to the KAS-36 deck-completeness check). It is the same recipe as gen3 with one added dataset: day-3 deck-spread video (lgd-cards-video-day3).

Metrics (frozen held-out real video, eval_real.py, serve-style 6Γ—4 tiling @ 4K, threshold 50)

Scored against the previous served model (gen3 = day-2) in one run, both on the same expanded 318-frame holdout (225 prior frames + 93 newly-held-out deck-spread frames β€” never trained on):

Recall (cards found) Precision-proxy
gen4 (this model) 0.786 0.752
gen3 (day-2, prior served) 0.750 0.774

On the 93 deck-spread-only frames β€” the target scenario:

Recall count-of-52 unique codes (mean) best frame
gen4 0.665 26.2 / 52 32 / 52
gen3 0.574 23.0 / 52 24 / 52

+9 points of spread recall. Normal-gameplay recall is held (~0.848 β†’ 0.853). gen4 does not reach a full 52, and its precision-proxy dips β€” partly a genuine trade (it detects more aggressively) and partly an artifact: the LLM-made holdout GT is not exhaustive, so real cards gen4 recovers that the GT lacks score as false positives. It fails the strict "beat the previous model on both axes" gate; it was activated for the recall win on 2026-07-20 and reverted the same day (see Status above). Full numbers in metrics.json.

Numbering note: gen3's headline figure elsewhere (0.85) was on an older 225-frame holdout; on the same 318-frame holdout used here gen3 scores 0.750, which is the fair comparison. Naming was unified 2026-07-20: genN means the same model on HF, in the project's generations/genN-* archive (this one: generations/gen4-spread) and in every doc.

Files

  • model.onnx β€” deployable ONNX (dynamic batch) β€” the exact export served during its brief 2026-07-20 activation (not currently deployed; see Status).
  • model.classes.json β€” the 52 class names, in model output order.
  • best.pt β€” Ultralytics checkpoint (for resuming / re-export).
  • metrics.json β€” full eval incl. per-label recall and the reference-model comparison.

Usage (onnxruntime, no Ultralytics at runtime)

import onnxruntime as ort, numpy as np, json
sess = ort.InferenceSession("model.onnx", providers=["CUDAExecutionProvider", "CPUExecutionProvider"])
names = json.load(open("model.classes.json"))
# letterbox your BGR frame to 640Γ—640, NCHW float32 /255; run sess.run(None, {input: x});
# decode YOLO11 output [1,56,8400] -> boxes+52 class scores, NMS. For 4K, tile (serve uses 6Γ—4).

The engine runs this tiled (full frame + a 6Γ—4 overlap grid at 4K) at a confidence threshold of 50.

Training

YOLO11s (COCO) β†’ fine-tuned 40 epochs (patience 15, batch 16, imgsz 640, seed 7) on the Roboflow ow27d base set (oversampled, not redistributed here β€” get it from Roboflow Universe) mixed with lgd-cards-video-day1 + -day2 + -day3 (the new deck-spread tiles). Labels are detector-proposed, OpenAI-gpt-5-mini-verified against the closed 52-code vocabulary.

Family

Cards: gen1 Β· gen2 Β· gen3 Β· gen4 (this) Β· Chips: gen1 Β· gen2

Downloads last month
2
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support