状態: ドラフト(2026-10-13)、実測値を反映。 正解率はすべてOpenTTGamesの実際の試合12編(サーバーが分かるラリー275個)で直接測定した値であり、バージョンごとに日付と条件を付記しています。学習型イベント検出とラリー終了判定の再設計実験は進行中のため結果を記載していません。TT4D・PathCRFの数値は論文の値です。 作成ルール: 実測データが基本であり、測定できなかったものは「未実測」と表示します。
目次
- 1. 軌跡からイベントへ
- 2. 審判: 「誰が打ったか」は見ない
- 3. 66.9%から93.1%まで: バージョンごとに変更したことと代償にしたこと
- 4. 誤差はどのレイヤーで発生するのか: 正解を入力してみる
- 5. 今後: 3D極値ルールと制約Viterbi
- 参考
1. 軌跡からイベントへ
第1レイヤーが提供するのはフレームごとの (시각, x, y, 신뢰도) がひとつです。スコアをつけるにはこれをイベントの列に変換しなければなりません。エンジンが出力するイベントは4つです。
| イベント | 定義(側面カメラ) |
|---|---|
| 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と比較」という1行にも理由があります。ヒートマップ座標は整数ピクセルであるため、動きの遅い区間では同じ値が続き、「等しい」を「下降」としてカウントすると、上昇するボールでバウンドが発生してしまいました。
定数にはそれぞれ根拠がある
ルールエンジンの弱点は、数値を感覚で決めやすいことです。そのため、定数ごとに実測の根拠をコードのコメントに記載しました。
| 定数 | 値 | 根拠(実際の試合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のnet終了83個)の前後水平速度比は−0.44…−0.03、ネットの上をそのまま通過したボール(OpenTTGamesのnet 1,268個)は0.75…1.01 |
GAP_ZERO_S |
0.25秒 | 埋められなかった空白がこれだけあれば信頼度0。埋める限界(6フレーム)を超えると「見落としたバウンドはない」と信じることはできない |
最後の行と対をなすのが fill_gaps です。ボールが身体に遮られて6フレーム(60fpsで0.1秒)以下の間見えない場合、直線で補間し、補間された点の信頼度を両端の半分に引き下げます。その区間で発生したバウンドを自動的に「要確認」へ誘導するためです。
2. シンパン: 「ダレが打ったか」は見ない
審判エンジン(ttscore/referee.py)の核心となる考え方は、序文のひとつの文章にあります。 誰が打ったかは映像から直接見ず、バウンドの順序から推論します。 ボールがS側にバウンドした場合、次はS側の選手が打って反対側に1回落とさなければなりません。そうできなければ(同じ側に再びバウンド、バウンドせずに外に出る、何事もなく終了する)、S側の選手が失点します。サーブのみ例外で、サーバー側に1回、そしてレシーバー側に1回落ちなければなりません。
このようにすると、「ラケットの接触」という、映像から見つけるのが最も困難なイベントなしでスコアをつけることができます。その代わり、バウンドをひとつ見落としたり偽のバウンドがひとつ混ざったりすると、「どちらが打つ番か」が丸ごと狂ってしまいます。そのため、審判の残りのルールのほとんどは 偽のバウンドを排除する装置です。
水平速度というひとつの数値
バウンドイベントには、バウンド直後の水平速度 vx(テーブルの長さ/秒)が含まれています。画面のピクセルではなくテーブルの長さで割った値であるため、カメラの距離や解像度に関係ありません。審判はこの数値で3つのものを区別します。
- サーブの開始。 スコアボードのサーバー側のバウンドであり、かつボールがネット側へ0.7テーブル/秒以上移動していなければサーブとして受け付けません。実際のサーブ271個の最初のバウンドはすべて1.07以上であり、ラリー外のサーバー側のボール(サーブ前の自分の側でのバウンド、相手に渡すボール)は、サーブのように見えた4個を除いて0.61以下でした。
- ラリー中の偽のバウンド。 ラリー中のS側の本物のバウンドは相手が打ったボールであるため、S側のエンドに向かって、すなわちネットから離れながら進みます。ネット側へ向かう「バウンド」は、ネットの上を通過するボールや打球を誤読したものであるため破棄します。このルールひとつで実際のラリーの勝者が235から241に増加しました。
- ボールを返す行為。 レシーバーがサーバーにボールを投げ返すと、レシーバー側からネット側へ弾んでサーバー側に落ちます。「サーブのように」見えますが、スコアボードのサーバーの反対側から始まるため受け付けません。両方を受け付ける変形は、実際の5試合の誤判定を3から16に増加させました。
速いツーバウンドは後回しにする
同じ側の2つのバウンドの間隔が0.25秒よりも短い場合、本物のツーバウンドなのか、バウンド直後の打球をバウンドとして読み取ったものなのかを区別できません。実測において、このような判定39件のうち16件(41%)が誤っていました。残りの判定の誤答率は8%でした。そのため、この判定は 次のイベントまで保留します。 次のバウンドが反対側であれば(リターンが落ちる)、2番目のバウンドは偽物だったので破棄してラリーを続行します。このルールにより判定が変更された実際のラリー9件は、すべて「不正解」から「正解」になりました。代償は時間です。その判定は最大1.5秒遅れて出力されます。
信頼度と「要確認」
ラリーの信頼度は、そのラリーのイベントたちの信頼度の中での 最小値です。イベントの信頼度は、検出の信頼度と、直前の空白の信頼度のうち小さい方です。0.6を下回るとスコアボードに「要確認」が表示されます。判定はそのまま反映しつつ、人間に一度確認を促すというものです。
ここで2つの数値を区別しなければなりません。
- 要確認の割合: 人間が確認しなければならないラリーの割合。高いと面倒である。
- 確認なしの誤り: 「要確認」の表示なしに誤った判定。人間が信じてそのまま通過させるため、 実際にコストとなるエラーである。
信頼度を保守的に引き下げれば確認なしの誤りは減るものの要確認が増え、逆にすればその逆になります。以下の歴史は、これら2つの数値の間の綱引きでもあります。
3. 66.9%から93.1%まで: バージョンごとに変更したことと代償にしたこと
条件(すべての行で同一): OpenTTGames実際の試合12編、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件が、「打球をバウンドとして読み取ったこと」と「ラリーが終了しなかったこと」でした。この2つを修正したところ、判定なしが10.2%から1.5%に減少しました。代償は 要確認が76.4%から85.1%に 増加したことと、1.5秒終了ルールによりロブが多い守備型の試合でポイントが早く決まってしまう可能性がある点です(1.5秒は98パーセンタイルであるため、2%はそれよりも長いです)。
- バージョン3. バウンド瞬間のボールの中心は接触した点よりも半径分だけ上にあるため、遠方の境界付近のバウンドが帯の外側に出てしまいました。帯を広げたところ勝者は上昇しましたが、バウンドの精度は0.831から0.825へと少し低下しました。帯が広いと偽物もより多く入り込みます。
- バージョン4. 境界をSAM 2.1に変更しました。代償は試合開始時のセグメンテーションモデルの実行1回と人間の2回のタップです。
- バージョン5. ここで性質が変わります。「要確認」が85%であれば自動スコアではなく「レビュー用のドラフト」です。サーブ認定ルールと偽のバウンドの排除により判定の正解率を高め、信頼度の減点を再設計することで、 要確認を85.8%から41.5%へと半分以上に削減しました。 代償として 確認なしの誤りが1.5%(4件)から5.1%(14件)に増加しました。 人間が確認する作業は大幅に減少しましたが、人間が信じてスルーした誤答は3倍になりました。
- バージョン6. サーブがネットに当たって入ったレットをネット判定として捉え、速いツーバウンドを保留しました。勝者が93.1%に上昇し、確認なしの誤りが5.1%から3.6%に再び減少しました。代償は3つあります。速いツーバウンドの判定が最大1.5秒遅れて出力され、実際のラベルのサーブ1つがレットと誤読され(要確認として表示される)、合成テストのノイズ条件の一部が少し悪化しました(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. 誤差はどのレイヤーで発生するのか:正解を入力してみる
レイヤーを分けたおかげで、同じ採点を3種類の入力で実行できます。モデルが検出したボール、データセットの正解ボール座標、データセットの正解バウンドです。正解のボールを入力しても外れる場合、その誤差はボール検出のせいではありません。正解のバウンドを入力しても外れる場合、イベント抽出のせいでもありません。
| エンジン版 | モデルのボール | 正解のボール | 正解のバウンド | 範囲 |
|---|---|---|---|---|
| 2版(79.6%版) | 79.6% | 85.8% | 90.2% | モデルのボールは275ラリー、正解の入力は正解が存在するラリー |
| 5版(88.7%版) | 88.7% | 90.2% | 93.5% | モデルのボールは275ラリー、正解の入力は正解が存在する246ラリー |
| 2026-10-13のベースライン | 93.5% | 89.0% | 90.2% | 3つすべて同じ246ラリー |
第2版と第5版では、正解を入力するほどスコアが上昇しました。このことから、「残りの誤差の大部分はボール検出ではなく イベント抽出で発生している」と読み解くことができ、実際、第5版の偽バウンド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.3m以上。
3Dの正解データ(合成およびTT3D)において、±2フレーム基準でバウンドのF1が0.92、打球のF1が0.83と報告されています(論文、適合率・再現率は別途記載なし)。打球の数値が低い理由として、著者らは選手の近くでボールが見えにくくなることを挙げています。
我々のエンジンの2Dルールと比較すると、同じ考え方の3D版と言えます。「V字」は z の極小値であり、「水平方向が反転すれば打球」は x の極値です。3Dに拡張すれば視点に関わらず同じルールになり、高さを把握することで空中のボールを平面に誤って投影してしまう問題も解消されます。その代わり、単一カメラから3Dを復元するステップが新た必要になります。この復元を、我々のサイドカメラのスマホ映像で行えるかどうかが次の実験の疑問点です(進行中、未計測)。同系統のオープンソース実装の一部はGPLライセンスであるためコードは流用せず、手法のみを実装する予定です。
制約付きViterbi:ルールを「許可された遷移」として定義する
現在のエンジンはイベントを1つずつ受け取り、その都度判定を下しています。1つの偽バウンドが入力されると、「次はどちらが打つ番か」が逆転し、それ以降の判断がすべて狂ってしまいます。人間はこのような場合、後を振り返って前を修正します。「このバウンドが本物だとすれば、その後の順序がルール上あり得ない。したがって、このバウンドは偽物だったはずだ。」
この考え方を計算に落とし込んだものが 制約付きViterbiデコーディングです。
- 時点ごとに、「サーバー側のバウンド」、「レシーバー側のバウンド」、「打球」、「何もなし」といった 状態候補にスコアを割り当てます。スコアはボール、音声、姿勢などのシグナルから得られます。
- 卓球のルールを 許可された遷移として記述します。サーブ → サーバー側のバウンド → レシーバー側のバウンド → 打球 → サーバー側のバウンド … は許可、同じ側のバウンドが2回続いた後のラリー継続は禁止(得点が発生するはず)といった具合です。禁止された遷移のスコアは非常に低く(実質的に −∞)設定します。
- Viterbiアルゴリズムを使用して、 スコアの合計が最も大きい、ルール上可能な1つのパスを探索します。動的計画法であるため、ラリーの長さに比例する時間で処理が完了します。
- 最適なパスと2番目のパスのスコア差が小さい場合、そのラリーは「確認が必要」となります。これは現在の「信頼度の最小値」よりも根拠が明確な信頼度となります。
サッカーにおいて同様の考え方を示したPathCRF(arXiv 2602.12080, KDD 2026)は、 モデルはそのままにしてデコーディングのみを変更し、 イベントのF1スコアを65.34%から73.14%に向上させ、ルール違反を2.80%から0%に削減しました(論文)。卓球はサッカーよりもルールがはるかに厳格であるため、さらに大きな効果が期待できると推測されます。PathCRFのコードはMPL-2.0であるため流用せず、その手法を我々のエンジンに合わせて独自に実装する予定です。
進行中の実験
次の実験はまだ結果が出ていないため記載しません(進行中)。
- ボール軌跡の短いウィンドウ(過去16フレーム、未来最大200ms)を処理する小規模な1D畳み込みモデル(パラメータ数約1.8万)により、バウンド・打球・ネットを出力し、ルールベースのバウンドをフィルタリングまたは補完する。未来予測の長さを変更しながら、精度と遅延のバランスを計測する。
- ラリー終了判定における「ボール消失」の閾値と固定遅延の確定方法を変更し、判定の遅延を短縮する。
- 上記2点と制約付きViterbiを組み合わせる。
実験の原則はすでに定めています。ハイパーパラメータは学習に使用していない検証用試合でのみ選定し、テスト用の7試合は最後に1回だけ参照します。また、ルールエンジンはテスト用の試合を確認しながら調整されたものであるため、 ルールベースラインのテストスコアは楽観的なものになっているという点を、新しい手法と比較する際に併記します。
ティティの一言: 審判はラケットを見ていません。ボールがどこに落ちたのかを数えるだけでも、ほとんどすべて正解できます。ただし、1つの偽バウンドによって順序がまるごと狂ってしまうため、ルールの大部分は偽物を取り除くために使われます。
参考
- 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