Skip to main content

M3: The Static Camera Isn't

Trevor McCormick
Data Product @ Disney+

That clip up top: every serve the pipeline is confident about across the sixty point clips — server end and deuce/ad called on each, SERVE flashed at the detected toss.

Sixty clips are waiting to be charted, and charting each one starts with the players. SAM does that beautifully for about $0.15 a clip — $9 per full pass over the reel, forever, every rerun. But M1 left a free alternative lying around: the temporal median erases players, which means frame minus median IS a player detector. This session cashed that in, and the pursuit of free player tracking knocked over three assumptions on the way.

Finding 1: the static camera isn't

M1 measured the camera static across the 16-second M0 rally and concluded one homography serves all frames. Align every frame of a 60-second clip against its first frame (ECC, translation-only) and a different story appears:

Line plot of camera x and y shift across a point clip, showing a spike to 32 pixels mid-point

point_29: the broadcast camera, measured. A 32-pixel pan in the middle of the point, plus slow drift throughout.

The camera operator follows the play. Of course they do — it's their job. The pan is invisible watching the footage and fatal to everything downstream: it smears the median plate, and at the far baseline 30 pixels is meters of court-coordinate error. M1's assumption survived only because the M0 clip was short. Every frame now gets stabilized to its clip's first frame before anything else — plate, subtraction, or homography — touches it.

Finding 2: the plate keeps a ghost

With stabilization in, the near player tracked at 100% coverage immediately. The far player: 35%. The plate had eaten him.

He's small, and between shots he barely moves — so the median over a short clip contains a faint standing copy of him, and wherever he stands still, frame-minus-plate reads zero. The near player never suffers this because he's big and restless.

The fix borrows the medicine that already worked twice: don't build the plate from one clip; build it from five. Each clip's plate is median-merged with its four neighbors' plates (ECC-aligned). Players stand in different spots from point to point, so their ghosts never line up, and the median votes them out. Far-player coverage: 35% → 96%, near stays 100%. Zero API calls.

Finding 3: everyone looks like a tennis player

First serve-detection run, several clips reported the far player standing at court x = 13.5 meters. The court is 10.97 meters wide.

Those blobs were the far-corner ballkids — crouched just outside the doubles lines, inside my generous tracking region, and moving just enough to out-diff a motionless pre-serve server. Third impostor of the project, after M0's spare-ball holders and the self-erasing line judge: to a difference image, everyone near a tennis court looks like a tennis player. The far half's region is now cut tight to the court; the near half stays generous.

Point anatomy: who serves, and when

With tracks in hand, the serve should be geometry. The server hugs the center mark; the receiver waits wide. The toss stretches the server's silhouette — arm and racket at full reach — so the blob's height peaks. Which side of the mark they stand on gives deuce or ad for free.

Version 1 asked only "who's closer to the center mark?" and the frame strips ate it alive: the reel's cold open starts mid-rally, zoom-tail fragments produced garbage blobs, and — the expensive lesson — returners often stand a meter off the center hash, where a return swing reads as a toss to a height detector.

Five-frame strip of a detected serve: toss rising, full reach at the marked frame, racket drop after

When it's right, it's textbook: point_33, toss at full stretch on the detected frame.

Five-frame strip showing a player swinging a return, wrongly flagged as a serve

When it's wrong, it's instructive: point_40's "server" is the returner, one meter off the hash, mid-return-swing. Four gates passed on the wrong man.

Version 2 stops guessing. Every candidate must clear four gates — early track coverage, distance to the center mark, distance to their own baseline, and a real height peak — or the clip gets an explicit no_confident_serve with its reasons attached. The yield: 40 of 60 clips produce a confident serve call; the other 20 are reasoned refusals, and reading the refusal log is diagnosis for free (the cold open, mid-point rejoins, and the two long clips whose early far tracks are too sparse — the 49-shot rally among them).

Frame-verifying a sample of the confident calls: six of eight server ends right, five of eight serve moments usable. Honest scorecard. Geometry-only anatomy tops out about here — the structural fix is the ball. A serve is the ball leaving the server's hand and crossing the net; when ball tracking runs over these clips, the ball adjudicates everything the silhouette can only suggest.

The verdict

Player tracking for all sixty points, serve anatomy on two-thirds of them, three assumptions corrected — for $0.00. Project total: still about seventy-five cents, all of it spent back in M0. SAM is now needed for exactly one thing: the ball.

Which is the next session: the charting loop. Ball tracking over point clips, M2's detector with fps finally a parameter, and the pieces — serve, hits, bounces, letters — assembling into per-point MCP strings. The string from cv-05 was one rally's middle. The next one should have a beginning.