Skip to main content

M2: The Court Doesn't Return Serve

Trevor McCormick
Data Product @ Disney+

That clip up top: the pipeline calling HIT and BOUNCE in real time, with every event pinned onto the court diagram as it happens.

M1 ended with a caveat that was really an assignment: the homography maps the ground plane, the ball is airborne most of the rally, and the mapped positions are only true at the moments the ball touches the court. So — find those moments. M2 is hit and bounce detection, and the kickoff post's premise ("every discontinuity is a hit or a bounce") finally meets the data.

The detector ended up right thirteen times out of thirteen. It got there by being wrong twice, and both failures taught me more physics than the successes did.

What the trajectory actually looks like

First move: differentiate the M0 track and stare at it.

Three stacked plots: image y-position, its velocity, and along-court shadow velocity across the rally's 300 frames

Position, vertical velocity, and along-court shadow velocity. 290 usable frames, 10 small tracking gaps.

Two traps announce themselves immediately. First: image-y is not height. It mixes physical height with court depth — a ball at the far baseline sits high in the frame even when it's low physically. Those beautiful M0 "parabolas" are projection mixtures. Second: the along-court shadow velocity spikes to ±200 m/s when the ball is high on the far side. No, the ball does not break the sound barrier — that's the projection amplifying airborne motion, M1's lesson wearing a different hat.

Version 1 and the smell test

Detector v1 did the obvious thing: find cusps in vertical velocity (ball stops falling), then classify by whether the direction of travel flips. It found nine events and called eight of them hits.

Eight hits, one bounce. Rallies do not work like that — nearly every groundstroke is preceded by a bounce. Something was systematically wrong, and the move that saved the milestone was refusing to debug the math and instead pulling the actual video frames at every detected event and looking at them.

Three broadcast frames around frame 44: the ball descending toward Zverev, at his feet, then rising as he winds up

Frame 44, flagged as a "hit." The ball is at Zverev's feet and his racket is nowhere near it. That's a bounce.

Eyes beat derivatives. Watching the strips produced a frame-verified ground truth for the whole rally and exposed two distinct failures.

Two failures, two lessons

Near-side bounces were classified as hits. After a bounce, the shadow velocity doesn't cleanly persist — it collapses into noise around -2 to -4 m/s, and its sign happened to flip, which fooled the direction test. The fix is to classify on outgoing speed instead: a real return leaves at 5+ m/s of shadow speed, often 40. A bounce's outgoing shadow speed collapses toward zero. The court doesn't return serve.

Far-side bounces were invisible — structurally. After a far bounce the ball rises and recedes from the camera, and both push its image position the same direction. There is no cusp. Not a threshold problem; the signal genuinely does not exist in that coordinate. What does exist: the shadow-speed collapse. Airborne, the shadow races at -30 to -50 m/s of amplified nonsense; the moment the ball is on the ground, the shadow moves at true ball speed, -5-ish. That collapse marks the bounce.

The false positive that taught the most

The collapse detector's first version promptly flagged frame 71 as a bounce. The video shows the ball at that moment crossing the net, a meter and a half in the air.

The lesson took a minute to sink in: the collapse isn't an instant that happens at ground contact. It unwinds gradually through the descent — as the ball loses height, the projection amplification deflates with it. Early collapse candidates are just the descent in progress. The bounce is the last collapse of a descent, not the first.

While verifying that fix, the frame strips delivered one more surprise: the rally wasn't over when I thought it was. My own ground truth had quietly stopped at frame 243, but the track runs to ~298 — one more far bounce, then Gasquet's final swing. The detector found them; my labels hadn't. Ground truth needs verifying too.

The rally trajectory with all thirteen events marked and labeled: red dots for hits, white for bounces

Detector v4: seven hits, six bounces.

The payoff

Thirteen events detected, thirteen matched against the frame-verified ground truth, thirteen classified correctly, zero false positives. And because M1 gave us the homography, every event lands on a real court:

Top-down court with all thirteen events placed: solid yellow bounce dots on the near side, hollow ones on the far side, red X marks for hits at both ends

The whole rally as thirteen events on a court. Solid bounces: trustworthy positions. Hollow: far-side, ±meters.

The near-side bounce depths come out at 19.4, 19.6, and 20.4 meters — about three and a half meters inside the baseline, which is exactly where professional groundstrokes land. Nobody told the pipeline that; it's just what the math says, and it agrees with how tennis works.

Read the event sequence out loud and something clicks: *hit, bounce, hit, bounce, hit... * — that's a rally, described shot by shot. It's crude, it has no shot types or directions yet, but it is unmistakably the skeleton of a chart. For the first time the pipeline's output looks like where this project is going, not just like computer vision.

The verdict

M2: achieved on this rally — with caveats owned in public. The thresholds are tuned to one clip and their generalization is untested. Far-side bounce positions are only good to a few meters: at the far baseline, one frame of timing error is meters of shadow, and the ball is already moving again by the next frame. Near-side positions look genuinely good.

Running total for three milestones: SAM 3 tracking, a homography, and a frame-verified event detector — for $0.60, all of it spent back in M0. M1 and M2 have been numpy all the way down.

Next, M3: turn event sequences into actual charting — rally segmentation, shot direction, Match Charting Project notation. The part where this stops being a vision project and starts being a tennis project.