TT Lab
Blog

한국어 English 日本語 中文

Get started
Blog

Blog

Events and Referee: Extracting Bounds from Trajectories and Scoring by Rules, From 66.9% to 93.1%

Status: Draft (2026-10-13), reflecting actual measurements. All accuracies are values measured directly from 12 actual OpenTTGames matches (275 rallies with known servers), with dates and conditions attached to each version. Experiments on learned event detection and rally-end determination redesign are ongoing, so their results are not listed. TT4D and PathCRF figures are from the paper. Writing rule: Measured data is the default, and anything unmeasured is marked as "Unmeasured."

Table of Contents

1. From Trajectory to Event

What the first layer gives you per frame is (시각, x, y, 신뢰도) alone. To score a match, this must be converted into a sequence of events. The engine outputs four events:

Event Definition (Side Camera)
bounce The frame where the ball's screen y increases (descending) and then starts to decrease (ascending) within the table top bounding band, provided it maintains its horizontal direction.
net The ball passes near the net above the table height, and its horizontal speed drops below 0.7x or reverses.
out After a bounce, the ball goes beyond the table edge and falls below the table height.
end The ball is invisible for 1 second, or no new bounce occurs for 1.5 seconds after the last bounce.

Intuition: Bounds are Vs, and Shots are Also Vs

Screen y increases downward. When a ball drops onto the table and bounces back up, y increases and then decreases. This "V" shape is the basic pattern of a bounce. The problem is that balls hit with a racket on the table also form Vs. Therefore, we check one more thing. A ball that bounces on the table continues flying in the same direction, whereas a ball hit by a racket reverses direction. Across 12 actual matches, 97.9% of true bounds maintained their horizontal direction, while 73.8% of fake bounds within 3 frames before and after a shot reversed their horizontal direction.

# ttscore/events.py detect_events() 에서 줄임
going_down = before.x is not None and c.y > before.y      # 같은 y 가 이어지면 그 앞의 다른 y 와 비교
going_up = n.y < c.y
if (going_down and going_up and table.on_surface(c.x, c.y)
        and c.t - last_bounce_t >= MIN_BOUNCE_GAP_S         # 0.12초 — 같은 바운드를 두 번 세지 않게
        and keeps_direction(i)                              # 앞뒤 가로 이동이 둘 다 2px 이상이고 반대면 타구
        and not near_net_touch(i)):                         # 네트에 걸린 공의 V 는 버린다
    emit(Event(c.t, "bounce", table.ball_side(c.x, c.y), conf=min(p.conf, c.conf, n.conf),
               vx=vx_at(i), vx_in=vx_at(i, after=False)))

The single line "if the same y continues, compare with a different y preceding it" also has a backstory. Heatmap coordinates are integer pixels, so identical values persist in slow sections; counting "sameness" as "descending" generated false bounds on ascending balls.

Every Constant Has a Basis

A weakness of rule engines is that constants are easily set by intuition. Therefore, empirical bases for each constant are documented in the code comments.

Constant Value Basis (12 actual matches, 2026-10-11)
END_AFTER_BOUNCE_S 1.5s 98% of bounce intervals within rallies (1,066 out of 1,087) are under 1.5 seconds.
REVERSAL_MIN_PX 2px 97.9% of true bounds maintain direction; 73.8% of fake bounds near shots reverse direction.
NET_SLOW_RATIO 0.7 The pre- and post-horizontal speed ratio for balls caught in the net (83 net endings in Extended) is −0.44…−0.03, while balls passing cleanly over the net (1,268 nets in OpenTTGames) range from 0.75…1.01.
GAP_ZERO_S 0.25s If an unfilled gap reaches this size, confidence is 0. Beyond the filling limit (6 frames), we cannot trust that "no bounds were missed."

Paired with the last line is fill_gaps . If the ball is obscured by the body and goes unseen for 6 frames (0.1 seconds at 60fps) or less, it is interpolated with a straight line, and the confidence of the interpolated points is halved relative to both endpoints. The goal is to automatically route bounds occurring in that segment to "Needs Review."

2. Referee: Does Not Watch "Who Hit the Ball"

서브 대기 서브처럼 보이는 공만 받음 서버 쪽 바운드 + 네트 쪽 속도 ≥ 0.7 서브 진행 네트 닿음 = 렛 후보 리시버 쪽 바운드 1초 안 서버 쪽에 또 · 바운드 없이 끝 랠리 기대하는 쪽: S S 에 바운드 → 다음은 반대쪽 서브 실패 리시버 득점 S 쪽 선수가 진다 같은 쪽에 또 바운드 → 투 바운드 테이블 밖으로 떨어짐 → 아웃 아무 일 없이 끝남 → 못 받음 「누가 쳤나」는 보지 않는다. 바운드가 떨어진 쪽의 선수가 받아 넘겨야 할 차례다. 0.25초 안의 같은 쪽 두 바운드는 다음 이벤트까지 판정을 미룬다(가짜일 수 있다).
Figure: Referee State Transitions

The core idea of the referee engine (ttscore/referee.py) is summarized in a single sentence in the introduction: Who hit the ball is not directly observed in the video but inferred from the sequence of bounds. If the ball bounced on side S, the player on side S must hit it next and make it land once on the opposite side. Failing to do so (another bounce on the same side, going out without a bounce, or ending without an event) means side S loses the point. The serve is the only exception, requiring one bounce on the server's side followed by one on the receiver's side.

This allows scoring without "racket contact," which is the hardest event to find in video. Instead, if a single bounce is missed or a fake bounce sneaks in, "whose turn it is to hit" gets completely out of sync. Therefore, most of the referee's remaining rules are mechanisms to filter out fake bounds.

A Single Number Called Horizontal Speed

Bounce events carry the horizontal speed immediately following the bounce vx(table lengths/second). Because it is divided by table length rather than screen pixels, it is independent of camera distance and resolution. The referee uses this number to distinguish three things:

  1. Start of a serve. Recognized as a serve if it is a bounce on the scoreboard server's side and the ball moves toward the net at 0.7 table lengths/second or faster. The first bounds of all 271 actual serves were 1.07 or higher, whereas balls on the server's side outside of rallies (bouncing on one's own side before serving, passing the ball to the opponent) were 0.61 or lower, excluding 4 that looked like serves.
  2. Fake bounds during rallies. True bounds for side S during a rally are shots hit by the opponent and thus head toward side S's end—away from the net. A "bounce" heading toward the net is discarded as a misread ball passing over the net or a shot. This single rule increased the number of correct rally winners from 235 to 241.
  3. Returning the ball. When the receiver tosses the ball back to the server, it bounces from the receiver's side toward the net and lands on the server's side. It looks "like a serve" but starts on the side opposite the scoreboard server, so it is not accepted. A variant accepting both sides increased fake judgments across 5 matches from 3 to 16.

Postponing Fast Double Bounds

If two bounces on the same side are closer than 0.25 seconds, it is impossible to tell whether they are true double bounces or a shot immediately following a bounce misread as a bounce. In actual measurements, 16 out of 39 such judgments (41%) were incorrect. The error rate for remaining judgments was 8%. Therefore, this judgment is postponed until the next event. If the next bounce is on the opposite side (a return lands), the second bounce was fake, so it is discarded and the rally continues. All 9 actual rallies whose judgments changed due to this rule went from incorrect to correct. The trade-off is time: that judgment comes out up to 1.5 seconds later.

Confidence and "Needs Review"

The confidence of a rally is the minimumamong the confidences of its constituent events. An event's confidence is the lesser of its detection confidence and preceding gap confidence. If it falls below 0.6, the scoreboard displays "Needs Review." This means the judgment is output as-is, but flagged for human inspection.

Here, two numbers must be distinguished:

Conservatively lowering confidence reduces errors without review while increasing needs review, and vice versa. The history below is also a tug-of-war between these two numbers.

3. From 66.9% to 93.1%: What Each Version Changed and Paid

Conditions (all rows identical): 12 actual OpenTTGames matches, Extended OpenTTGames rally end labels, 275 rallies with known servers, BlurBall coordinates, scored from recordings. All dates are 2026-10-11–12.

Version Changed Winner Needs Review Incorrect Without Review No Judgment Bounce Precision / Recall
Initial Ball coordinates → Gap filling → Events → Referee 66.9% (184) 76.4% 2.9% 10.2% 0.789 / 0.971
2 Shot ≠ bounce (bounce only when maintaining horizontal direction), end if 1.5s after last bounce 79.6% (219) 85.1% 1.8% 1.5% 0.831 / 0.958
3 Allowing distant borders "8px + ball radius" (dataset mask corners) 82.5% (227) 85.5% 1.5% 0.7% 0.825 / 0.965
4 SAM 2.1 corners (3 matches) 84.7% (233) 85.8% 1.5% 0.0% 0.823 / 0.965
5 Serve recognition rules, discarding net-ward "bounds" during rallies, redesigning needs review 88.7% (244) 41.5% 5.1% 0.4% 0.823 / 0.965
6 Net judgment (service let), postponing fast double bounds 93.1% (256) 41.1% 3.6% — 0.832 / 0.963

Listing the trade-offs paid per version yields the following.

And the 2026-10-13 Baseline

Version 6's 93.1% was measured using SAM corners, but those corner files no longer exist. Starting research anew, we reset the baseline with dataset mask corners and the latest engine, this time utilizing actually deployed real-time paths.(Measured by replaying the recent 2.5 seconds per sample).

Pipeline Winner Incorrect w/o review Requires review No decision Bounce Precision / Recall
Recorded video evaluation 91.3% (251) 13 108 3 0.835 / 0.965
Serving real-time pipeline 90.2% (248) 15 129 2 0.800 / 0.970

Using the exact same engine and ball coordinates, changing only the pipeline resulted in 3 different rallies. This is because recorded video evaluation makes judgments after watching the entire rally, whereas real-time evaluation looks at only the last 2.5 seconds and makes decisions on the fly. Recorded video evaluation inflates real-time performance. Therefore, all future experiments will report both pipelines side by side.

4. Where do the errors occur?: Feeding ground truth

Thanks to the separation of layers, we can run the same evaluation with three different inputs: the model's ball detection, the dataset's ground truth ball coordinates, and the dataset's ground truth bounces. If it still fails when given the ground truth ball, the error is not due to ball detection. If it still fails when given the ground truth bounces, the error is not due to event extraction either.

Engine version Model ball Ground truth ball Ground truth bounce Scope
v2 (79.6% version) 79.6% 85.8% 90.2% Model ball uses 275 rallies; ground truth inputs use rallies with ground truth available
v5 (88.7% version) 88.7% 90.2% 93.5% Model ball uses 275 rallies; ground truth inputs use 246 rallies with ground truth available
2026-10-13 baseline 93.5% 89.0% 90.2% All three use the same 246 rallies

In v2 and v5, performance improved as ground truth was fed into the system. Therefore, we interpreted that "most of the remaining errors stem from event extraction, rather than ball detection." Indeed, out of 25 false bounces in v5, 11 were hits misread as bounces, 5 were side flips near the net, and 4 were missed bounces.

However, in the latest engine, the model ball outperforms both the ground truth ball and ground truth bounces. This reveals why these numbers should not be interpreted as an "upper bound."

In other words, ground truth inputs are not "perfect inputs" but rather "different inputs." Because the engine is tuned to the properties of the model ball (such as coordinates extending beyond events and horizontal velocity), comparisons with ground truth inputs should be used solely as a tool to isolate layer-specific errors, not as an upper bound.

Remaining errors

These are the categories of the 27 cases that resulted in errors or no decisions in the 2026-10-13 baseline (recorded video, 275 rallies).

Category Count
False bounce 7
Missed bounce 4
Side mismatch at end decision 4
End not triggered 3
Hit misread as bounce 2
Side flip · Premature decision · Side flip near net · Incorrect out call 1 each

Events (bounces) account for 14 out of the 27 cases, and rally ends account for 8. Broken down by cause of point ending: outs were 92.8% (128 out of 138), nets 92.6% (75 out of 81), winners 84.4% (27 out of 32), and double bounces 75.0% (3 out of 4).

Under degraded conditions

We measured performance by introducing degradation across 7 test matches (57 rallies) (recorded video evaluation, correct winner count).

Condition Correct / 57 Incorrect w/o review
Clean 50 3
Dark 35 2
30fps (engine also at 30fps) 41 9
Low resolution 320x180 42 2
Long exposure 46 2
Mobile 360p 60fps 500kbps compression 50 1

Even though 30fps barely degrades ball detection (Part 2), it caused a significant drop in score, with incorrect decisions without review being the most frequent. This is because the temporal resolution at the moment of the bounce is halved. This engine tends to fail in ways that conceal incorrect answers. Therefore, we recommend 60fps for recording.

5. Looking ahead: 3D extremum rules and constrained Viterbi

TT4D-style 3D extremum rules

Current rules look for a "V-shape" in the 2D image. Because the y-axis in the image mixes ball height and distance from the camera, changes in viewpoint disrupt the rules (end-of-video camera issue in Part 3).

TT4D (arXiv 2605.01234, ACM MM 2026) lifts ball trajectories into 3D space before detecting events. The rules are surprisingly simple:

On data with 3D ground truth (synthetic and TT3D), they reported a bounce F1 of 0.92 and hit F1 of 0.83 within a ±2 frame tolerance (paper does not separately report precision and recall). The authors attribute the lower hit score to the ball being frequently occluded near players.

Compared to our 2D rules, this is the 3D counterpart of the same idea. The "V-shape" corresponds to the local minimum of z, and the "horizontal direction reversal equals a hit" corresponds to the extremum of x. Lifting to 3D makes the rules invariant to viewpoint changes, eliminating the distortion caused by projecting airborne balls onto a 2D plane. However, this introduces a new requirement to reconstruct 3D coordinates from a single camera. Whether this reconstruction can be achieved using our side-view mobile footage is the question for our next experiment (in progress, unmeasured). Since some open-source implementations of this family are licensed under GPL, we plan not to borrow their code but to implement the methodology ourselves.

Constrained Viterbi: Turning rules into "allowed transitions"

Currently, the engine receives events one by one and makes decisions on the fly. When a single false bounce is introduced, it flips "whose turn it is to hit," throwing off all subsequent judgments. Humans look back and correct themselves in such situations: "If this bounce were real, the subsequent sequence would violate the rules. Therefore, this bounce must have been false."

Translating this thought process into computation gives us constrained Viterbi decoding.

  1. At each timestamp, we score candidate statessuch as "server-side bounce," "receiver-side bounce," "hit," and "no event." These scores are derived from signals such as ball tracking, audio, and player pose.
  2. Table tennis rules are formulated as allowed transitions. Examples include: serve → server-side bounce → receiver-side bounce → hit → server-side bounce … is allowed, whereas two consecutive bounces on the same side without a rally continuation is forbidden (a point must have been scored). Forbidden transitions are assigned very low scores (effectively −∞).
  3. Using the Viterbi algorithm, we find a single rule-compliant path with the highest total score. Because it is dynamic programming, execution time is proportional to the length of the rally.
  4. If the score difference between the best path and the second-best path is small, that rally is marked as "requires review." This provides a more rigorous confidence metric than our current "minimum confidence threshold."

PathCRF (arXiv 2602.12080, KDD 2026), which applied a similar concept to soccer, kept the model unchanged and only modified the decoding to improve event F1 from 65.34% to 73.14% and rule violations from 2.80% to 0% (paper). We expect an even greater effect in table tennis because its rules are much stricter than soccer's (estimated). Since the PathCRF code is licensed under MPL-2.0, we will not import it, but rather re-implement the method tailored to our engine.

Ongoing experiments

The next experiments do not have results yet, so they are omitted here (in progress).

We have established our experimental principles: hyperparameters are selected exclusively from validation matches not used in training, and the 7 test matches are evaluated only once at the very end. Furthermore, because the rule engine was refined while observing the test matches, we note when comparing against new methods that the rule baseline's test scores are optimistic.

A word from TT. Umpires don't watch the racket. You can get almost everything right just by counting where the ball lands. However, since a single false bounce disrupts the entire sequence, most of the rules are dedicated to filtering out false positives.

References

Sign in to like

Comments

No comments yet.

Sign in to leave a comment