Real-time playing card recognition from a webcam. A YOLO11s detector, fine-tuned on a fully synthetic dataset generated from photos of one physical deck, reads the corner index (rank + suit) of cards on the table and emits JSON events.

No hand-labeled data: bounding boxes come for free because the dataset generator composites card images with known geometry.
capture.py → extract_cards.py → generate_dataset.py → train.py → reader.py
(webcam (contour detect, (composite cards (fine-tune (live
footage of warp each card onto backgrounds YOLO11s) recognition)
each card) to 500×700 PNG) with analytic labels)
capture.py — film each of the 52 cards and a few background shotsextract_cards.py — finds the card contour in each frame and warps itcards/<CARD>/.generate_dataset.py — builds a YOLO detection dataset (4,000 train /dataset/preview/ before training — boxes must sit ontrain.py — fine-tunes COCO-pretrained YOLO11s for 60 epochs. Runsreader.py — webcam loop. Both corners of a card vote (confidence-CARD_RECOGNIZED JSON event is printed, with a cooldown against repeats.python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python reader.py # needs models/playing_cards_custom.pt, see below
With several cameras attached, the reader window gets a "Camera" dropdown (top-right) — click it to switch feeds live. --camera 2 sets the starting camera. capture.py uses an interactive picker instead (SPACE = next, ENTER = select), same --camera flag to skip it.
dataset.zip, ~370 MB): attached to the GitHub Release —data=dataset/data.yaml at it. Note: path: insidedata.yaml is absolute; fix it for your machine (e.g. /content/datasetmodels/playing_cards_custom.pt (YOLO11s, 52 classes) is incards/) and background shotsbackgrounds/) are included, so you can regenerate the dataset and retrain!pip install ultralytics
!unzip -q dataset.zip
!sed -i 's|^path: .*|path: /content/dataset|' dataset/data.yaml
!yolo detect train model=yolo11s.pt data=dataset/data.yaml epochs=60 imgsz=640
Download runs/detect/train/weights/best.pt, copy it to models/playing_cards_custom.pt. Tip: mount Google Drive and add project=/content/drive/MyDrive/card-reader so checkpoints survive runtime disconnects (resume with yolo detect train resume model=.../weights/last.pt).
The model is only as general as the synthetic data. It performs well in conditions matching the training distribution (this deck, dark table surface, roughly top-down camera, cards 25–65 % of frame height) and degrades on unseen surfaces, shallow/oblique camera angles, and small/distant cards. Tens are the weakest rank (two-glyph index). To adapt: capture new backgrounds and sharper card frames, widen the augmentation ranges in generate_dataset.py, regenerate, retrain.
Code and dataset: MIT (see LICENSE).