Volleyball jersey-number reader (ResNet-18)

Reads the jersey number on a person crop from volleyball footage, or says the number is not readable. Companion to grdesignbuild/volleyball-person-ball-detector: the detector finds the people, this model reads their numbers, and together they attach moments and stats to a player.

How the output works

The model has 23 logits in three groups, so it can read numbers it never saw in training:

logits meaning
0โ€“9, 10 tens digit 0โ€“9, or blank (single-digit number)
11โ€“20 ones digit 0โ€“9
21, 22 unreadable / readable

Decode: if readable wins, the number is tens + ones ("7" when tens is blank, "07" when tens is 0). read_number.py in this repo does this and also cuts the right crop.

Input

Feed the chest of a person box: from 10 % to 60 % of the box height (skipping the head), widened 10 % per side, resized to 224ร—224. That is exactly what the training crops were, and the processor config here passes a square 224 image through untouched.

from transformers import AutoImageProcessor, AutoModelForImageClassification
from PIL import Image
import torch

repo = "grdesignbuild/volleyball-jersey-number-reader"
processor = AutoImageProcessor.from_pretrained(repo)
model = AutoModelForImageClassification.from_pretrained(repo).eval()

crop = Image.open("torso.jpg").convert("RGB")            # see read_number.torso() for the crop rule
with torch.no_grad():
    logits = model(**processor(images=crop, return_tensors="pt")).logits[0]
tens, ones, readable = logits[:11].softmax(-1), logits[11:21].softmax(-1), logits[21:].softmax(-1)
if readable[1] > 0.5:
    t, o = int(tens.argmax()), int(ones.argmax())
    print(str(o) if t == 10 else f"{t}{o}")
else:
    print("not readable")

Training data

  • Person crops from 2,025 4K keyframes of youth club matches filmed from the referee stand (indoor halls, beach and grass), located by the companion detector.
  • Labels are pseudo-labels from Apple's Vision text recognizer run on tiles of the full frame (the same recipe the Vision XI app uses), kept only when the read sits inside a person box, in its upper 70 %, and is not static signage across frames. Crops with no clean read are labelled unreadable, capped at twice the readable count.
  • 1,435 readable crops over 66 distinct numbers, split by recording so validation venues are unseen.
  • The images contain minors and are not published.

Evaluation

Against the teacher's reads on held-out recordings:

metric value
number accuracy on readable crops 0.671
precision of predicted numbers 0.588
readable precision / recall 0.866 / 0.988
precision of numbers at confidence โ‰ฅ 0.85 0.79 (recall 0.49)

The teacher's own held-out labels are about one in ten wrong on inspection, so true accuracy is somewhat higher than these figures. In practice, read every frame of a tracked player and keep numbers with confidence โ‰ฅ 0.85; two or three agreeing reads identify the player reliably.

Limitations

  • Numbers are only readable a fraction of the time (back turned, occluded, far away). Expect "unreadable" often; carry identity across frames with tracking rather than reading every frame.
  • Trained on youth club jerseys; unusual fonts, sponsor text near the number, and numbers over 99 are out of scope.
  • Two-digit numbers with a leading zero (07) are rare in the data.

License

Apache-2.0. Built with ๐Ÿค— Transformers.

Downloads last month
27
Safetensors
Model size
11.2M params
Tensor type
F32
ยท
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for grdesignbuild/volleyball-jersey-number-reader

Quantized
(6)
this model