状态:草案(2026-10-13),已反映实测数据。 准确率均为从 12 场 OpenTTGames 实际比赛(275 个已知发球方的拉力赛)中直接测得的值,每次迭代均附带了日期和条件。由于学习型事件检测和拉力赛结束判定重新设计的实验仍在进行中,故暂不记录结果。TT4D 和 PathCRF 的数值为论文中的值。 编写规则:以实测数据为基础,未能测得的数据标记为“未实测”。
目录
- 1. 从轨迹到事件
- 2. 裁判:不看“谁击的球”
- 3. 从 66.9% 到 93.1%:每次迭代的改动与代价
- 4. 误差发生在第几层:代入正确答案看看
- 5. 未来展望:3D 极值规则与约束 Viterbi
- 参考资料
1. 从轨迹到事件
第一层给出的每个帧是一个 (시각, x, y, 신뢰도) 。为了打分,必须将其转换为事件序列。引擎会产生四个事件。
| 事件 | 定义(侧面摄像机) |
|---|---|
| bounce(弹跳) | 在球台顶面条带内,球的屏幕 y 坐标先变大(下降)然后开始变小(上升)的帧。前提是保持横向方向不变 |
| net(网球/触网) | 球在网附近从球台高度上方通过,且横向速度降至 0.7 倍以下或发生反转 |
| out(出界) | 弹跳后球飞出球台边缘并落到球台高度以下 |
| end(结束) | 球连续 1 秒不可见,或最后一次弹跳后 1.5 秒内没有新的弹跳 |
直觉:弹跳是 V 字形,击球也是 V 字形
屏幕的 y 轴向下增大。当球落在球台上并弹起时,y 先增大后减小。这个“V”字形是弹跳的基本形态。问题在于 在球台上用球拍击打的球也会绘制出 V 字形。因此,我们需要观察另一个特征。落在球台上的球会继续朝同一方向飞行,而击中球拍的球则会折返。在 12 场实际比赛中,真正的弹跳有 97.9% 保持了横向方向,而击球前后 3 帧内的假弹跳有 73.8% 发生了横向方向反转。
# 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)))
“如果相同的 y 持续存在,则与前面的其他 y 进行比较”这一简单的代码背后也有其原因。由于热图坐标是整数像素,在运动缓慢的区间内会出现相同的值,如果把“相同”当作“下降”,就会在正在上升的球中产生弹跳。
每个常数都有其依据
规则引擎的一个弱点是数字很容易凭感觉设定。因此,代码注释中记录了每个常数的实测依据。
| 常数 | 值 | 依据(12 场实际比赛,2026-10-11) |
|---|---|---|
END_AFTER_BOUNCE_S |
1.5秒 | 拉力赛中弹跳间隔的 98%(1,087 个中有 1,066 个)在 1.5 秒以内 |
REVERSAL_MIN_PX |
2px | 真实弹跳的 97.9% 保持方向不变,击球附近的假弹跳有 73.8% 方向反转 |
NET_SLOW_RATIO |
0.7 | 触网的球(Extended 中 83 个 net 结束)的前后横向速度比为 −0.44…−0.03,直接从网上方通过的球(OpenTTGames 中 1,268 个 net)为 0.75…1.01 |
GAP_ZERO_S |
0.25秒 | 如果未填补的空白达到这个长度,置信度为 0。超过填补极限(6 帧)则无法相信“没有漏掉弹跳” |
与最后一行相配的是 fill_gaps 。如果球被身体遮挡而不可见的时间不超过 6 帧(在 60fps 下为 0.1 秒),则用直线填补,并将填补点的置信度削减为两端的一半。这是为了让该区间的弹跳自动被标记为“需人工确认”。
2. 裁判:不看“谁击的球”
裁判引擎(ttscore/referee.py) 的核心思想正如导言中的一句话: 不直接从视频中观察是谁击球,而是从弹跳顺序中进行推断。 如果球在 S 方一侧弹跳,那么接下来 S 方的运动员必须击球并让球在对方一侧落台一次。如果未能做到这一点(在同一侧再次弹跳、未弹跳直接出界、无缘无故结束),则 S 方运动员失分。发球是个例外,球必须先在发球方一侧落台,然后在接发球方一侧落台。
通过这种方式,我们可以在没有“球拍接触”这一视频中最难检测的事件的情况下进行计分。相反,如果漏掉一个弹跳或夹杂一个假弹跳,“轮到谁击球”就会完全错乱。因此,裁判的其余规则大多是 过滤假弹跳的装置。
横向速度这一单一数字
弹跳事件中包含弹跳刚结束时的横向速度 vx(球台长度/秒)。由于它是除以球台长度而不是屏幕像素的值,因此与摄像机距离和分辨率无关。裁判通过这个数字来区分三件事。
- 发球的开始。 必须是记分牌发球方一侧的弹跳,且球必须以 0.7 个球台/秒或以上的速度向球网方向移动,才能被当作发球。实际 271 个发球的第一次弹跳都在 1.07 以上,而拉力赛之外的发球方一侧的球(发球前在自己一侧弹跳、传给对手的球),除了被误认为发球的 4 个之外,都在 0.61 以下。
- 拉力赛中的假弹跳。 在拉力赛中,S 方的真实弹跳是由对方击过来的球,因此会朝着 S 方底线方向、即远离球网的方向运动。朝着球网方向运动的“弹跳”会被当作误读自网空通过的球或击球动作而丢弃。仅凭这一条规则,实际拉力赛胜者判定正确的数量就从 235 增加到了 241。
- 传球。 如果接发球方将球抛给发球方,球会从接发球方一侧朝球网方向弹跳并落在发球方一侧。它看起来“像发球”,但由于它始于记分牌发球方的反侧,因此不予接受。同时接受双方的变体使得实际 5 场比赛的错误判定从 3 个增加到了 16 个。
推迟快速二次弹跳的判定
如果同一侧的两次弹跳间隔小于 0.25 秒,则无法区分这确实是两次弹跳,还是将弹跳刚结束时的击球误读为了弹跳。在实测中,此类 39 个判定中有 16 个(41%)是错误的。其余判定的错误率为 8%。因此,此类判定将被 推迟到下一个事件发生时再处理。 如果下一次弹跳在对方一侧(回球落地),则说明第二个弹跳是假的,应予舍弃并继续拉力赛。通过这条规则,判定发生改变的 9 个实际拉力赛全部从错误变为正确。付出的代价是时间。该判定最多会延迟 1.5 秒得出。
置信度与“需人工确认”
拉力赛的置信度是该拉力赛各事件置信度中的 最小值。事件的置信度取检测置信度与前面空白处置信度两者中的较小值。如果低于 0.6,记分牌就会弹出“需人工确认”。判定结果会照常提交,但会要求人工检查一次。
在这里需要区分两个数字:
- 需人工确认比例:需要人工审核的拉力赛比例。如果太高会很麻烦。
- 未经确认即出错:没有显示“需人工确认”却判定错误的比例。由于人工会直接相信并跳过,因此这是 实际造成成本的错误。
如果保守地扣减置信度,未经确认即出错就会减少,但需人工确认会增加,反之亦然。下面的历史演变也是这两个数字之间的一场拉锯战。
3. 从 66.9% 到 93.1%:每次迭代的改动与代价
条件(所有行相同):12 场 OpenTTGames 实际比赛,Extended OpenTTGames 拉力赛结束标签,275 个已知发球方的拉力赛,BlurBall 坐标,录像评分。日期均为 2026-10-11–12。
| 版本 | 改动内容 | 胜者 | 需人工确认 | 未经确认即出错 | 无判定 | 弹跳精确度 / 召回率 |
|---|---|---|---|---|---|---|
| 最初 | 球坐标 → 填补空白 → 事件 → 裁判 | 66.9% (184) | 76.4% | 2.9% | 10.2% | 0.789 / 0.971 |
| 2 | 击球≠弹跳(仅在保持横向方向时弹跳),最后一次弹跳后 1.5 秒即结束 | 79.6% (219) | 85.1% | 1.8% | 1.5% | 0.831 / 0.958 |
| 3 | 允许扩大边界 “8px + 球半径”(数据集掩码边缘) | 82.5% (227) | 85.5% | 1.5% | 0.7% | 0.825 / 0.965 |
| 4 | SAM 2.1 边缘(3 场) | 84.7% (233) | 85.8% | 1.5% | 0.0% | 0.823 / 0.965 |
| 5 | 引入发球规则,丢弃拉力赛中朝向网的“弹跳”,重新设计需人工确认 | 88.7% (244) | 41.5% | 5.1% | 0.4% | 0.823 / 0.965 |
| 6 | 网判定(发球触网),推迟快速二次弹跳 | 93.1% (256) | 41.1% | 3.6% | — | 0.832 / 0.963 |
记录每次迭代付出的代价如下。
- 版本 2。 在最初的版本中出错的 91 个案例中,有 65 个是“将击球误读为弹跳”和“拉力赛未结束”。修复这两点后,无判定比例从 10.2% 降至 1.5%。代价是 需人工确认从 76.4% 增加到 85.1% ,并且 1.5 秒结束规则可能会在长球较多的防守型比赛中过早判定得分(1.5 秒是 98% 的分位数,意味着有 2% 比这更长)。
- 版本 3。 由于弹跳瞬间球心比接触点高出一个半径,边界附近的弹跳跑到了条带外面。扩大条带后胜者准确率上升,但弹跳精确度从 0.831 略微下降至 0.825。条带宽了,混入的假弹跳也就更多了。
- 版本 4。 将边缘更换为 SAM 2.1。代价是在比赛开始时需要运行一次分割模型并进行两次人工点击。
- 版本 5。 性质在此发生了变化。如果“需人工确认”高达 85%,这就不是自动计分,而是“供审核的草案”。通过引入发球规则和过滤假弹跳来提高判定准确率,并重新设计了置信度扣减逻辑,从而 将需人工确认的比例从 85.8% 减少了一半以上,降至 41.5%。 代价是 未经确认即出错从 1.5%(4 个)增加到 5.1%(14 个)。 人工查看的工作量大大减少,但被人工信任并跳过的错误却增加了三倍。
- 版本 6。 通过网判定捕获了发球触网并进入的擦网球(Let),并推迟了快速二次弹跳。胜者准确率上升至 93.1%,未经确认即出错从 5.1% 再次降至 3.6%。代价有三个:快速二次弹跳判定最多延迟 1.5 秒得出,一个实际标签发球被误读为擦网(显示为需人工确认),并且合成测试的部分噪声条件变差了(从 83.8% 降至 83.2%)。
以及 2026-10-13 基准线
版本 6 的 93.1% 是使用 SAM 边缘测得的,但该边缘文件已经不存在了。随着研究重新开始,我们使用数据集掩码边缘和最新引擎重新设定了基准线,这一次我们评估了 实际部署的实时路径(采用每隔一段时间重新回溯最近 2.5 秒的方式)进行了测量。
| 路径 | 胜者 | 未经确认即判定错误 | 需要确认 | 无判定 | 弹跳精度 / 召回率 |
|---|---|---|---|---|---|
| 录像评分 | 91.3% (251) | 13 | 108 | 3 | 0.835 / 0.965 |
| 部署实时路径 | 90.2% (248) | 15 | 129 | 2 | 0.800 / 0.970 |
使用相同的引擎和相同的球坐标,仅仅改变路径,3 个回合的结果就发生了变化。这是因为录像是在看完全部回合后才做出判断,而实时模式则仅查看最近 2.5 秒并即时做出判断。 录像评分会夸大实时性能。 因此,接下来的所有实验都会将这两种路径一并记录。
4. 误差出现在哪一层:代入正确答案进行测试
由于划分了层级,我们可以用三种不同的输入来运行相同的评分。分别是模型检测到的球、数据集的标准球坐标,以及数据集的标准弹跳。如果代入了标准球仍然出错,那么该误差就不是由找球导致的。如果代入了标准弹跳仍然出错,则也不是由事件提取导致的。
| 引擎版本 | 模型球 | 标准球 | 标准弹跳 | 范围 |
|---|---|---|---|---|
| 2.0 版(79.6% 版) | 79.6% | 85.8% | 90.2% | 模型球为 275 个回合,标准输入为包含标准答案的回合 |
| 5.0 版(88.7% 版) | 88.7% | 90.2% | 93.5% | 模型球为 275 个回合,标准输入为包含标准答案的 246 个回合 |
| 2026-10-13 基准线 | 93.5% | 89.0% | 90.2% | 三者均为相同的 246 个回合 |
在 2.0 版和 5.0 版中,输入的正确答案越多,准确率就越高。因此,我们得出结论:「剩余的大部分误差并非源于找球,而是源于 事件提取」,事实上,在 5.0 版的 25 起假弹跳中,有 11 起将击球误读为了弹跳,5 起在网边颠倒了方向,4 起漏掉了弹跳。
然而在最新版本的引擎中, 模型球的表现优于标准球和标准弹跳。 这就是为什么不能将这些数字解读为「上限」的原因所在。
- 标准球仅存在于事件附近。 OpenTTGames 的球标记仅存在于事件发生前 4 帧到后 12 帧之间(第 7 部分)。由于其余部分为空,因此无法仅凭标准球来正确观察回合的结束(球消失)。
- 标准弹跳没有横向速度。 引擎确定的发球和回球规则会使用弹跳的横向速度。如果仅代入标准弹跳,就无法使用该规则。
也就是说,标准输入并非「完美的输入」,而是「不同的输入」。由于引擎已经针对模型球的特性(延伸至事件之外的坐标、横向速度)进行了微调,因此与标准输入的比较仅可用作区分各层误差的工具,而不能用作上限。
当前剩余的误差
这是在 2026-10-13 基准线(录像评分,275 个回合)中出错或无判定的 27 个案例的分类。
| 类型 | 数量 |
|---|---|
| 假弹跳 | 7 |
| 漏掉弹跳 | 4 |
| 结束判定方向偏移 | 4 |
| 未出现结束(end) | 3 |
| 将击球误读为弹跳 | 2 |
| 方向颠倒·过早判定·网边方向颠倒·出界误判 | 各 1 |
事件(弹跳)方向有 27 起中的 14 起,回合结束方向有 8 起。按失分原因来看,出界占 92.8%(138 个中的 128 个),触网占 92.6%(81 个中的 75 个),制胜分占 84.4%(32 个中的 27 个),两次弹跳占 75.0%(4 个中的 3 个)。
恶劣条件下的表现
对 7 场比赛(57 个回合)施加画质恶化后进行了测量(录像评分,胜者匹配数)。
| 条件 | 正确 / 57 | 未经确认即判定错误 |
|---|---|---|
| 清晰 | 50 | 3 |
| 黑暗 | 35 | 2 |
| 30fps(引擎也为 30fps) | 41 | 9 |
| 低分辨率 320×180 | 42 | 2 |
| 长曝光 | 46 | 2 |
| 手机 360p 60fps 500kbps 压缩 | 50 | 1 |
尽管 30fps 下找球准确率几乎没有下降(第 2 部分),但得分却大幅下降,并且未经确认即判错的情况最多。这是因为弹跳瞬间的时间分辨率减半了。 该引擎崩溃时往往会掩盖错误答案。 因此,拍摄指南建议使用 60fps。
5. 未来展望:3D 极值规则与约束维特比(Viterbi)
TT4D 式 3D 极值规则
目前的规则是在 2D 画面中寻找「V 字」。由于画面中的 y 坐标混合了球的高度和与摄像机的距离,因此当视点改变时,规则就会动摇(第 3 部分末尾的摄像机问题)。
TT4D(arXiv 2605.01234, ACM MM 2026)将单摄像机转播视频中的球轨迹提升到 3D 空间 后寻找事件。其规则简单得令人惊叹。
- 弹跳 = 球高度 z 的 极小值.
- 击球 = 球台长度方向 x 的 极值(球折返的位置)。
- 同类极值之间间隔 0.2 秒以上,极值位置距离球网 0.3 米以上。
在具有 3D 标准答案的数据(合成数据与 TT3D)中,报告称 ±2 帧标准下的弹跳 F1 值为 0.92,击球 F1 值为 0.83(论文中无单独的精度和召回率)。关于击球数值较低的原因,作者认为是因为在运动员附近很难看清球。
与我们引擎的 2D 规则相比,这是同一思路的 3D 版本。「V 字」即 z 的极小值,而「横向方向颠倒即为击球」则是 x 的极值。升维到 3D 后,无论视点如何规则都保持不变,将空中之球错误映射到平面的问题也随之消失。取而代之的是,需要新增从单摄像机恢复 3D 的步骤。能否在我们侧面的手机视频中实现这种恢复,是下一个实验要探讨的问题(正在进行中,未实测)。由于同系列的部分公开实现采用 GPL 协议,我们不打算直接拿来代码,而是计划自行实现其方法。
约束维特比:将规则转化为「允许的转移」
目前的引擎是逐个接收事件并当即做出决定。一旦混入一个假弹跳,就会导致「接下来该谁击球」的顺序颠倒,从而使后续的所有判断全部出错。人类在遇到这种情况时会回顾过去并修正当前:「如果这个弹跳是真的,那么随后的顺序在规则上是不可能的。因此,这个弹跳一定是假的。」
将这种思路转化为计算的就是 约束维特比解码。
- 我们会对每个时间点的 状态候选(如「发球方一侧的弹跳」、「接发球方一侧的弹跳」、「击球」、「无事发生」)进行评分。这些分数来自球、声音、姿态等信号。
- 我们将乒乓球规则编写为 允许的转移。例如:发球 → 发球方一侧的弹跳 → 接发球方一侧的弹跳 → 击球 → 发球方一侧的弹跳……是允许的,而在同一侧连续两次弹跳后继续回合则是禁止的(必须产生得分)。我们将被禁止的转移的分数设得极低(实际上为 −∞)。
- 利用维特比算法寻找 分数字和最大、且符合规则的唯一路径。由于采用动态规划,其运行时间与回合长度成正比。
- 如果最佳路径与第二佳路径的分数差很小,则该回合属于「需要确认」。这比现有的「最低置信度」更有依据。
在足球领域展现出相同思路的 PathCRF(arXiv 2602.12080, KDD 2026) 在保持模型不变的情况下仅改变解码方式 ,就将事件 F1 值从 65.34% 提升至 73.14%,并将规则违规率从 2.80% 降至 0%(论文数据)。由于乒乓球的规则比足球严格得多,我们期望(推测)其效果会更加显著。由于 PathCRF 代码采用 MPL-2.0 协议,我们不打算直接套用,而是试图根据我们的引擎重新实现该方法。
正在进行的实验
由于以下实验尚无结果,故暂不记录(进行中)。
- 使用观察球轨迹短窗口(过去 16 帧,未来最多 200ms)的小型 1D 卷积模型(约 1.8 万个参数)来输出弹跳、击球和网球事件,并过滤或补充规则弹跳。通过改变前瞻长度来权衡准确度与延迟。
- 更改回合结束判定的「球消失」阈值和固定延迟确认,以减少判定延迟。
- 将上述两点与约束维特比相结合。
实验原则已经确定。超参数仅在未用于训练的验证比赛中进行选择,7 场测试比赛只在最后查看一次。此外,由于规则引擎是在查看测试比赛的过程中不断完善的,因此在与新方法进行比较时,我们会一并说明 规则基准线的测试得分偏向乐观这一事实。
乒乓球小贴士。 裁判不看球拍。仅凭数清球落在了哪里就能猜对大半。由于一个假弹跳就会彻底打乱顺序,因此绝大部分规则都用在了过滤假弹跳上。
参考
- TT4D: https://arxiv.org/abs/2605.01234
- PathCRF: https://arxiv.org/abs/2602.12080
- Extended OpenTTGames(回合结束原因标签): https://arxiv.org/abs/2512.19327
- 我们的测量:代码库
docs/tt/PERFORMANCE.md2.2·2.6·2.7,实验台账 E00(2026-10-13),代码ttscore/events.py·referee.py·track.py