Downloads · 30 days
0
kjfk/tennis-bounce-detector
tennis-bounce-detector is a machine learning model from kjfk. Use it for the machine learning task on the model card, and read the license before you ship it in a product. The card lists the license as mit.
Decides whether the ball touched the court, from the shape of its trajectory. HistGradientBoostingClassifier over 16 scale-free trajectory features.
Downloads · 30 days
0
Access
Public
Updated Aug 18, 2026
Repo size
736 KB
Likes
0
Public
Click a slice to open those files.
.joblib736 KB · 99%
From the Hugging Face model README
Decides whether the ball touched the court, from the shape of its trajectory.
HistGradientBoostingClassifier over 16 scale-free trajectory features.
Trained on the TrackNet tennis dataset: 95 clips, 10 professional matches, 920 labelled bounces.
| split | precision | recall | F1 | PR-AUC |
|---|---|---|---|---|
| held-out clips | 0.888 | 0.944 | 0.915 | 0.956 |
| leave-one-match-out | 0.951 |
Leave-one-match-out is the honest number: clips from one match share a court, a camera and a pair of players, so a clip-level split lets the model see the same camera during training. 0.951 across ten unseen matches says it transfers.
The geometric rule this replaces used a single prominence threshold on the ball's height signal. Swept against ground truth, no value of that threshold found every point - so the parameter had no correct setting and the model was wrong, not the tuning.
The candidate proposal stage originally offered only frames where the ball's projected court y was a local maximum. Against 136 real labelled bounces that is true 2% of the time: a bounce is a corner in the trajectory, not a peak, because the height signal also carries down-court travel and that term usually dominates. Proposal recall was 34.6%. Proposing on the upward kink in image y instead - normalised by the segment's median speed, so it is resolution independent - took it to 99.3%. No classifier can recover an event that was never proposed.
16 features per candidate, in this order:
image_kink, height_prominence_left, height_prominence_right, height_curvature, reversal_sharpness, speed_before, speed_after, speed_ratio, direction_change_deg, vertical_velocity_before, vertical_velocity_after, vertical_velocity_flip, court_y_at_candidate, distance_inside_court_m, interpolated_share, mean_confidence
Court-space features are in metres and need a court homography.
import joblib
model = joblib.load("bounce_model.joblib")
probability = model.predict_proba(features)[:, 1]
Requires scikit-learn 1.9+ (pickled there; older versions load with a warning and may give different answers).
TrackNet tennis dataset - 81 broadcast clips, 10 matches, 30 fps, per-frame ball
coordinates with a status column where 2 marks ground contact.