状态:草稿(2026-10-13),已反映实测结果。 我们的数值是 2026-10-11 至 12 在服务器 GPU(24GB 级别)上,直接加载 BlurBall 预训练权重且不经微调测得的值。论文数值单独标记为“论文”。引用的源码标注了 commit 并在简短化后引用。 编写规则:以实测数据为准,未测得的数据标记为“未实测”。
目录
- 1. 难点所在
- 2. 直觉:不猜坐标,绘制地图
- 3. 谱系:TrackNet → TTNet → TrackNetV2 → WASB → BlurBall
- 4. HRNet:将分辨率保持到底
- 5. 在我们的代码中
- 6. 实测:能找得多么准确
- 7. 为什么选择 BlurBall 以及未来展望
- 参考资料
1. 难点所在
乒乓球的直径为 4cm。在侧面拍摄、能看到整个球台的 1080p 实验室视频(TT3D)中,测量得到的球直径在中心为 11–12px,为了输入模型而缩放到 512×288 时,就只剩下大约 3px 了。此外,它的速度极快。以 70km/h(约 20m/s)飞行的球在 1/120 秒的曝光时间内会移动 17cm,在 1/60 秒内会移动 33cm。这意味着会留下 模糊轨迹。不仅如此,运动员的手和球拍会遮挡球,天花板的荧光灯和白色的鞋子也看起来很像球。
因此,如果直接使用通用的目标检测器(寻找人或汽车的边界框检测器),效果往往不好。该领域有专门寻找球类的谱系,TT Score 使用的 BlurBall 正是这一谱系的顶峰。
2. 直觉:不猜坐标,绘制地图
找球模型不会直接输出“球在 (x, y)”这样的两个数字。相反,它会绘制一张与输入尺寸相同的黑白图像,即 热力图。这是一张用 0–1 为每个像素涂上“此处是球的概率”的地图。标签也是在球的位置画出一个小型高斯山丘的地图,并与每个像素进行比较。
这样做有三个原因。
- 按像素学习位置误差。 如果只猜两个坐标,代表“错得有多离谱”的信号只有两个数字,而地图上的数万个像素各自都能提供学习信号。目标越小,这种差异就越明显。
- “无球”状态更自然。 如果整张地图都很暗,就说明没有球。坐标回归在没有球的时候也会指向某个地方。
- 可能存在多个候选。 如果球和荧光灯都被涂得很亮,可以把两者都留作候选,然后在后面挑选能连成轨迹的那一个。
第二个思路是 同时输入多个帧。只看单张图,根本分不清白色的点是球还是荧光灯。连续看 3 张图就能区分什么是运动的、什么是静止的。这就像人在视频中比在静态截图中更容易找到球一样。
3. 谱系:TrackNet → TTNet → TrackNetV2 → WASB → BlurBall
TrackNet(2019):热力图与多帧
这是为了在网球转播中寻找球而提出的模型(arXiv 1907.03698)。它使用 VGG-16 的前 13 层提取特征,并通过反卷积将其放大,从而生成 640×360 的热力图。它对比了输入单张的方案(Model I)和输入连续 3 张的方案(Model II),其精度和召回率从 95.7% / 89.6% 提升到了 99.8% / 96.6%(论文,将同一场比赛按 7:3 划分进行评估)。这是首次用数字展示多帧效果的案例。
解码很简单。通过在 128(0–255 之间)处对热力图进行阈值处理以将其转换为黑白图像,并使用圆检测(Hough)来寻找圆, 只有在精确匹配到一个时 ,才将其中心输出为球。在同一篇论文中,未混淆比赛的 10 折交叉验证的召回率为 75.7%。这已经暴露出该领域反复出现的教训:在一个比赛中表现良好的模型,在从未见过的比赛中性能会显著下降。
标签规则也值得记住。TrackNet 在球发生模糊时,会在痕迹的**最新位置(最前端)**打上点。而 BlurBall 正是对这一规则提出了质疑。
TTNet(2020):乒乓球、120fps 以及 OpenTTGames
这是一个为了实时分析乒乓球比赛而开发的模型(arXiv 2004.09927),我们用于评估的 OpenTTGames 数据集正是与该论文一同公开的了。在 120fps 全高清视频中,将连续 9 帧堆叠成通道(输入 27 通道)。采用两阶段结构:先将整个画面缩小到 320×128 大致定位,再从原图中裁剪该区域进行精确查找。
输出很有趣。它不是 2D 热力图,而是 两个长度分别为 320 和 128 的 1D 向量(x 轴、y 轴)。将 2D 地图拆分为两个轴的 1D 分布可以减少计算量。第四篇中的 RTMPose (SimCC) 也采用了相同的思路。论文报告称:事件识别率 97.0%,球检测率 97.5%,整个流水线耗时 6.0ms(单张桌面级 GPU)(见论文)。在我们 60fps 的手机视频上尚未进行实测。
TrackNetV2 (2020):输入 3 帧,输出 3 帧
这是针对羽毛球改进的版本。通过将其更改为 U-Net 结构,采用 输入 3 帧并同时输出 3 张热力图的(MIMO) 方式,将速度从 2.7 FPS 提升至 31.8 FPS(见论文摘要)。其输入大小 288×512 和“输入 3 帧并输出 3 帧”的做法被后来的 WASB 和 BlurBall 直接继承。
WASB (2023):小型 HRNet 与连通域后处理
这是一个旨在广泛用于足球、网球、羽毛球、排球和篮球这五项运动的基准模型(BMVC 2023,arXiv 2311.05237,代码采用 MIT 协议)。乒乓球并不在那五项之中。其骨干网络是小型 HRNet(见下文第 4 节),拥有 150 万个参数。输入是 (B, 9, 288, 512),即 RGB 3 通道 × 3 帧,输出是经过 Sigmoid 的 3 张热力图。
后处理不使用 TrackNet 的圆检测,而是使用 连通域查找。以下是原代码库中的相关部分。
# nttcom/WASB-SBDT src/detectors/postprocessor.py @923462ca, 49–59행(MIT, NTT Communications)
if np.max(hm) > self._score_threshold:
th, hm_th = cv2.threshold(hm, self._score_threshold, 1, cv2.THRESH_BINARY)
n_labels, labels = cv2.connectedComponents(hm_th.astype(np.uint8))
for m in range(1,n_labels):
ys, xs = np.where( labels==m )
ws = hm[ys, xs]
if self._use_hm_weight:
score = ws.sum()
x = np.sum( np.array(xs) * ws ) / np.sum(ws)
y = np.sum( np.array(ys) * ws ) / np.sum(ws)
在 0.5 处进行截断,将每个连通域按热力图值加权得到的中心作为位置,并将值的 和作为分数。使用求和意味着大而深的连通域会胜出。论文中解释称,连接帧之间的追踪器会通过“位置 + 速度 + 加速度/2”来预测下一个位置,但公开的代码并没有使用预测,而是 在上一个位置 300px(基于原图)范围内的候选者中选择分数最高的一个。当论文与代码不一致时,以代码的实际动作为准。
BlurBall (2025–2026):模糊的中心
这是针对乒乓球对 WASB 进行修改后的模型(arXiv 2509.18387,CVPRW 2026,代码采用 MIT 协议)。改变的主要是 标签。
- 如果球是模糊的,就在运动轨迹痕迹的 中心打上一个点。端点(TrackNet 的方式)会根据快门关闭的瞬间前后晃动,但中心点是曝光的中点时间,因此是恒定的。由于每一帧都指向相同的“相同时刻的球”,轨迹会变得更加平滑。
- 模糊的 方向 θ 和半长度 l 也作为标签一同标注。正确的热力图不是单个点,而是覆盖整个拖尾痕迹的细长丘状区域。
- 骨干网络的每个块中都加入了通道注意力机制(SE 块),并将损失函数更改为了 Quality Focal Loss。
输入和输出的形状与 WASB 相同((B, 9, 288, 512) → 3 张热力图)。模糊的长度和角度不是通过单独的输出头,而是通过 热力图连通域的形状在后处理中读取。
# cogsys-tuebingen/BlurBall src/detectors/blurball_postprocessor.py @2f0f5496, 48–50·60–71행에서 줄임(MIT)
angle = 0.5 * np.arctan2(2 * moments["mu11"], moments["mu20"] - moments["mu02"])
mean, eigenvectors = cv2.PCACompute(coords.astype(np.float32), mean=None)
principal_axis = eigenvectors[0] # 가장 긴 축
projected = np.dot(coords - mean, principal_axis)
l = projected.max() - projected.min() # 79행에서 l / 2 를 반길이로 저장
这里同样存在两个论文与代码的差异。论文解释说连通域的置信度是热力图的 平均值,但代码中和 WASB 一样是 求和。关于角度,论文通过主轴向量计算,而代码通过图像矩计算。
论文中的数值(自定义乒乓球测试集,288×512,桌面级 GPU)为 F1 96.52、79 FPS,这些数据经常被一起引用,但 这两个数字来自不同的设置。96.52 是以单帧为步长滑动的 1-step 推理,79 FPS 是以三帧为步长的 3-step 推理(3-step 的 F1 为 96.16)。论文表格中没有给出 1-step 的 FPS。
4. HRNet:完整保留分辨率
WASB 和 BlurBall 的骨干网络属于 HRNet(CVPR 2019,arXiv 1902.09212)系列。普通的分类模型会将图像缩小到 1/2、1/4、… 1/32 来压缩“这是什么”。一个 3px 大小的球哪怕只缩小到 1/8,也会比单个像素还要小。即使再放大,位置也会变得模糊不清。
HRNet 自始至终保持原始分辨率的分支,同时在旁边逐个添加低分辨率分支并行运行,并在分支之间反复融合信息。 低分辨率分支负责“附近有人且有桌子”的宏观上下文,高分辨率分支则负责“精确到这个像素”。我们使用的配置是 4 个分支,通道数为 16、32、64、128,并且将初始主干(stem)的步长设为 1,直接将输入分辨率送入分支中(在我方代码的 "STEM": {"STRIDES": [1, 1]}).
5. 在我方代码中
模型代码(MIT)并未放入代码库中,而是存放在服务器的存储空间中读取。因为如果引入他人的代码,我们就必须跟着修改其副本。代码库中只有其周边部分(live/blurball.py, batch/model_blurball.py).
权重验证。 下载的权重在比对 sha256 后,仅以 torch.load(weights_only=True) 的方式读取。由于 PyTorch 检查点是 pickle 格式,因此可能会执行任意代码。如果比对错误或 weights_only 无法读取时将停止启动。
预处理。 RGB,512×288,使用 ImageNet 均值和标准差进行归一化。因为浏览器已经将图像缩小到 512×288,所以服务器不会更改大小。我曾因此吃过一次大亏。将解码后的数组仅通过 transpose 交换轴进行堆叠后,张量变成了 channels_last 布局,导致 cuDNN 走了一条与预热时不同的计算路径。预热后的第一个实际帧耗时 12.5秒 。 np.ascontiguousarray 修改了一行代码就解决了。
后处理。 为了找到与原始仓库相同的连通块,额外生成了一个用于屏幕显示的值。
# live/blurball.py blobs() 에서 줄임
_, th = cv2.threshold(hm, SCORE_T, 1, cv2.THRESH_BINARY) # SCORE_T = 0.5
n, labels = cv2.connectedComponents(th.astype(np.uint8))
for m in range(1, n):
ys, xs = np.where(labels == m); ws = hm[ys, xs]; sw = float(ws.sum())
x, y = float((xs * ws).sum() / sw), float((ys * ws).sum() / sw)
pts = np.stack([xs, ys], 1).astype(np.float64); pts -= pts.mean(0)
vals, vecs = np.linalg.eigh(np.cov(pts.T)) # 주축 = 번짐 방향
pm = pts @ vecs[:, 1]; length = float(pm.max() - pm.min()) / 2.0
out.append({"x": x, "y": y, "score": sw, "peak": float(ws.max()), "len": length, ...})
score(和)用于跟踪器选择候选时, peak(最大值,0–1)用于在屏幕上显示置信度。由于“和”会根据连通块的大小超过 1,因此不适合作为展示给人类看的值。与原始仓库不同,这里的角度是通过协方差的主轴计算得出的。
实时与录像的区别。 实时模式下,每个新帧都会滚动最近 3 张图像的窗口,并且 只使用最后的那个热力图 (OUT_INDEX = 2)。录像模式使用不重叠的 3 张图像窗口,使用全部三张图像。因此,实时模式每帧的 GPU 工作量是三倍(第 1 篇第 3 节)。
6. 实测:能找到多少
基准线(2026-10-11)
条件:OpenTTGames 测试 7 场比赛,仅使用原始 120fps 的偶数帧(60fps),仅使用带有标记的 3,483 帧,在 512×288 下与正确答案相差 5px 以内即视为正确。权重保持预训练时的状态。
| 指标 | 值 |
|---|---|
| 精确率 / 召回率 | 0.9817 / 0.9785 |
| 位置误差(RMSE) | 0.594px(以 512 为基准),以原始 1920 为基准时为 2.23px |
| 边界前后 ±2 帧的召回率 | 0.988 |
| 时间(单个窗口,录像模式) | 每帧 3.42ms(293fps),8 个窗口组合 2.81ms |
| 时间(单个窗口,实时模式,空卡) | 10.5ms(fp32),8.0ms(fp16) |
| GPU 内存(torch 最大预留量) | fp32 单个窗口 0.35GB,8 个 2.6GB,16 个 5.2GB。fp16 单个窗口 0.22GB |
| 模型 | 权重 5.8MB,149 万个参数 |
弱点有三个。
| 球的状态 | 召回率 |
|---|---|
| 几乎静止的球(60fps 帧之间原始基准小于 10px) | 0.790 |
| 与运动员重叠的球(与数据集中的人体掩码重叠) | 0.863 |
| 极快的球(80px 以上) | 0.855 |
静止的球大多是发球前拿在手里的球,对得分影响很小。重叠的球和极快的球才是真正痛点所在。
test_7 的精确率低至 0.882,但这并不是模型的问题。 这场比赛中有 104 处标记为“无球”,其中 41 处实际上能看到球(经目视确认 6 帧)。模型正确找到的球被判定为了“找到了原本不存在的球”。扣除这 41 处后,精确率为 0.9937。关于数据集的内容将在第 7 篇中详细介绍。
与其他模型对比(2026-10-11–12)
使用 BlurBall 作者公开的对比模型权重在相同条件下进行了测量。
| 模型 | 精确率 / 召回率 | 重叠的球 | 静止的球 | 极快的球 |
|---|---|---|---|---|
| BlurBall(阈值 0.5) | 0.982 / 0.979 | 0.863 | 0.790 | 0.855 |
| BlurBall(阈值 0.3) | 0.980 / 0.985 | 0.903 | 0.862 | 0.868 |
| WASB,使用中心标签训练 | 0.985 / 0.964 | 0.879 | 0.707 | 0.855 |
| WASB,使用端点标签训练 | 0.976 / 0.934 | 0.774 | 0.696 | 0.513 |
| MonoTrack | 0.983 / 0.949 | 0.798 | 0.646 | 0.829 |
| TrackNetV2 | 0.992 / 0.822 | 0.669 | 0.398 | 0.566 |
| ResTrackNetV2 | 0.993 / 0.712 | 0.532 | 0.160 | 0.237 |
| BlurBall + WASB,通过热力图最大值合并(阈值 0.3) | 0.980 / 0.989 | 0.935 | 0.895 | 0.882 |
有三点值得一读。
- 即使同为 WASB,使用中心标签训练的模型也比使用端点标签的模型更好。 在极快的球上为 0.855 对 0.513。这是模糊中心标签有效的直接证据(OpenTTGames 的标记更偏向中心也是原因之一)。
- 性能提升的大部分原因不是因为叠加了模型,而是因为调低了阈值。 即使单独使用 BlurBall,只要将阈值降至 0.3,召回率就会从 0.979 升至 0.985。叠加 WASB 带来的纯收益是召回率 +0.35%p。
- 在干净的视频中,寻找更多的球并没有提高分数。 在 7 场比赛 57 个多拍测试中,BlurBall 猜对胜者的次数为 53,阈值 0.3 为 52,两个模型结合也是 52。因为降低阈值也会增加虚假边界。
在恶劣条件下 (2026-10-11–12)
我们对同一个测试视频施加了决定性的退化进行测量(球检测召回率)。
| 条件 | BlurBall 单独 | BlurBall + WASB 结合 (0.3) |
|---|---|---|
| 干净 | 0.979 | 0.989 |
| 黑暗 + 传感器噪声 | 0.685 | 0.820 |
| 低分辨率(缩小到 320×180 后放大) | 0.898 | 0.957 |
| 长曝光(1/60 秒模糊) | 0.841 | 0.869 |
| 球被遮挡(肤色椭圆) | 0.842 | 0.868 |
| 形似球的干扰物 | 0.969 | 0.927 |
| 30fps | 0.984 | 0.985 |
在昏暗的地方,结合两个模型有很大帮助(57个多拍测试中的胜者从 36 升至 45),但在形似球的干扰物面前,WASB 产生了大量虚假检测,情况反而变糟了。 不存在永远稳操胜券的组合。 而且结合后 GPU 时间翻倍(单窗口 10.5 → 20.0ms)。因此我们目前一直仅常态化运行 BlurBall,并正在试验“仅在球信号较弱时开启第二个模型”的方法(进行中)。
在 OpenTTGames 之外 (2026-10-11)
| 视频 | 精确率 / 召回率 | 错在哪里 |
|---|---|---|
| OpenTTGames 测试(基准) | 0.982 / 0.979 | — |
| TT3D 实验室,侧面 50fps | 0.929 / 0.860 | 错过的大多是发球前拿在手里的球 |
| TT3D 实验室,斜向 60fps | 0.894 / 0.820 | 20 个远距离错误中有 15 个是 天花板荧光灯. 除去画面上方的条纹,精确率为 0.956 |
| LATTE-MV 国际比赛转播,30fps 640×360 | 0.815 / 0.722(一致度) | 40 个不一致中有 25 个是指向了 同一模糊痕迹的另一端处的点 |
最后一行完美地展示了这篇文章的主题。LATTE-MV 的球坐标不是人工标注,而是其数据管道的追踪结果,并指向痕迹的一端。BlurBall 指向中心。被评判为“不同”的两者中有 62% 实际上是同一个球。如果标注规则不同,即使是同一个模型,评分也会不同。
7. 为什么选择 BlurBall,以及下一步
- 适合 60fps 手机视频的标注。 快门越长,模糊越长,此时中心标注的优势就越大。
- 小巧且快速。 149 万参数,单窗口 10.5ms。在同一张显卡上有容纳多个会话的余地。
- 代码采用 MIT 协议。 不过权重和训练数据没有标注许可证。论文附录中提到数据是用“采用知识共享许可证的公开在线视频”制作的,由于未公开训练视频列表,因此不清楚是否与 OpenTTGames 重叠。所以我们的实际测量结果 不作为泛化性能来解读。
作为下一步,我们正在调查和实验抗遮挡模型(仅在遮挡帧开启的条件模型)以及使用手机视频的微调。结果出来后会补充到这篇文章中(进行中)。目前最大的课题是手机视频还没有人工标注的真值集。
Titi 的一言。 同样的 WASB 模型,通过教导它拍摄模糊球的“中心”而不是“边缘”,快球召回率从 0.51 提高到了 0.86。这就是一个标注约定的力量。
阅读更多原理。 完整保持热力图和分辨率的结构整理在 视觉模型阅读第 1、3 篇:U-Net 与 HRNet中,热力图损失与训练技巧整理在 第 4 篇:损失函数与训练技巧中,并附带了原始论文。
参考
- TrackNet: https://arxiv.org/abs/1907.03698
- TTNet: https://arxiv.org/abs/2004.09927
- WASB: https://arxiv.org/abs/2311.05237, https://github.com/nttcom/WASB-SBDT (MIT)
- BlurBall: https://arxiv.org/abs/2509.18387, https://github.com/cogsys-tuebingen/BlurBall (代码 MIT,权重与数据未标注许可证)
- HRNet: https://arxiv.org/abs/1902.09212
- 我们的测量:仓库
docs/tt/PERFORMANCE.md2.1·2.8·2.9,实时演示设计文档第 9 节