本プロジェクトの最終ゴールは、麻雀の対局映像から 「牌の認識 → 立/倒 (姿勢) 判定 → アガリ判定 → 役・点数計算」 を 一気通貫で自動化し、プレイヤーの認知負荷を下げる 補助システム を構築することです。
[音声 + 映像 + RFID]
↓
[牌認識] → [立/倒 (姿勢) 判定] → [プレイヤー領域分離] → [アガリ判定]
↓
[手牌+和了牌+ドラ 分離] → [役判定 (mahjong lib)] → [翻・符・点数]
↓
[視覚オーバーレイ + 点棒移動アニメ]
| 区分 | 入力 | 出力 |
|---|---|---|
| 映像 | 📷 4K天井カメラ / M-League ダイジェスト | 🖥 Web ダッシュボード + 動画オーバーレイ |
| 音声 | 🎤 マイクアレイ (ツモ・ロン・ポン・チー) | 💾 牌譜 DB (SQLite + 天鳳形式 export) |
| 物理 | 🎴 RFID 点棒トレイ (各プレイヤー残高) | 🎯 点棒移動の物理連動 |
設計当初は「動画フレームを ML 推論で常時認識して、牌種・配置・姿勢をリアルタイムで取り、役判定まで自動で繋ぐ」絵を描いていました。 実際にパイプラインを組み立てた結果、以下の 5 つの構造的ボトルネック が顕在化しました。 各ボトルネックは「症状 → 根本原因 → 影響範囲 → 採用解決策」の順で整理します。
test-upsgd/mahjong-tiles-oc9zz/2 は M-League 非特化。
訓練データに「緑フェルト = negative」が不足し、フェルト上の白いハイライト・反射を牌と誤認識する。mleague/dense-frames/ にキャッシュして再評価コストを 0 にする。scripts/sparse-data-smoke-test.py を回すと、19 GT イベント全てが False Negative。precision 算出不能、recall = 0。detect_agari_transitions) は「直前 ~1.5 秒の立群 → 倒群」を捉える時系列遷移検出器なので、立群フレームが 0 枚だと構造上検出不能。scripts/evaluate-agari-transition-detector.py + 連続フレーム検出 JSON (50 frames/event) で評価。mleague/frames-pre-agari/ に 4 枚抽出済み = pre-agari 用 PoC)。src/cv/calibration.py) で内部行列 + 卓平面外部姿勢を取得 → 3D 座標で領域判定。src/scoring/hand_structure.py の 13/10/7/4/1 鳴き枚数ルール で構造的不整合を検出。tools/calibrate-oblique-view.py) で内部+外部パラメータを取得。src/scoring/pose_3d.py で 7 orientation クラス → (pitch, yaw, roll) を割当て、立/倒 二値判定を pitch から導出。| # | ボトルネック | 症状 | 影響レイヤ | 状態 |
|---|---|---|---|---|
| B1 | 公開モデル精度 | false positive 多発 | 検出 | 対処中 |
| B2 | 推論コスト | 無料枠 4 動画で枯渇 | 推論密度 | 対処済 |
| B3 | データ密度 | 遷移検出 recall = 0 | アガリ判定 | 枠組み済 / dense 取得待ち |
| B4 | 領域分離 | Y-band 過分節 | 鳴き判定 | 設計済 / 未実装 |
| B5 | 3D 姿勢 | 固定スキューの誤魔化し | 視覚整合 | キャリブ基盤済 / OBB 未学習 |
test-upsgd/mahjong-tiles-oc9zz/2 の検出結果
M-League sample01 / event1 (t=81s, アガリ peak) のフレームに対して、現行の公開モデルで推論した結果です。
緑 = confidence 90% 以上、黄 = 70〜89%、赤 = 70% 未満。
緑フェルト・空きスペース・卓周辺パーツに大量の false positive bbox が出ている ことがわかります。
同じフレームの「上位 9 false positive」をズームで切り出した検査画像です。緑フェルトの空きスペース・牌と牌の隙間・反射ハイライトに小さな bbox が出ています。
tile 検出が走っている (緑色の bbox)。M-League の卓特有の白いハイライトを牌の角と誤認識している。現状の検出結果に立 / 倒 (色分け) + プレイヤー領域 (太枠) を重ねた、クラウドファンディング想定のデモ描画です。 一見もっともらしいですが、よく見ると 緑フェルト上の架空 bbox や 選手の腕に出た誤検出 が含まれています。
比較として、初期 PoC で実機向けに作ったリアルタイム検出 + 役表示のモックです。 M-League 専用ファインチューン後は、この水準の「取りこぼし・誤検出ほぼゼロ」を動画でも目指します。
実は 自前モデル訓練の前段 として、動画から 499 フレームを抽出し、機械的に「麻雀卓が映っている 201 枚」に絞り込みました。 緑(KEEP) と 赤(DISCARD) のラベルで監査しています。
5 つのボトルネックに対し、現時点で採用している解決アプローチを採用判定とその理由付きで整理します。
| 項目 | 採用 | 不採用案 | 採用理由 |
|---|---|---|---|
| 検出モデル基盤 | Roboflow (Hosted Inference + Auto-Label) | CVAT セルフホスト, ローカル Ultralytics 直訓練 | Auto-Label による初期 bbox 提示でアノテーション工数が約 70% 削減できる。Starter Plan $60/月 で訓練+ホストまで一括。 |
| 初期クラス設計 | MVP v1 = 1クラス (tile) → MVP v2 = 34クラス |
最初から 34 クラスでフル訓練 | v1 で 位置の正解 を確立、v2 で 牌種の正解 を上乗せ。失敗時の手戻りを最小化する 2 段ロケット。 |
| bbox 形式 | YOLOv8m + axis-aligned bbox (現行) | OBB (Oriented Bounding Box) ヘッド | v1/v2 では axis-aligned で開始。OBB はアノテーション工数 + ラベリングツール選定の問題で v3 以降に持ち越し。 |
| 検出フレーム選択 | 音声トリガーで peak ±5s だけ推論 + dense キャッシュ | 30fps 全フレーム連続推論 | B2 のコスト制約 → 音声で時刻を絞ってから 5 FPS dense。1 動画あたり 950 推論 (無料枠内)。 |
| 項目 | 採用 | 不採用案 | 採用理由 |
|---|---|---|---|
| orientation 分類 | 7-クラス (h/w aspect ratio 閾値) | 専用 ML ヘッド | 計算ゼロ・データ要件ゼロで開始可能。standing/standing_oblique/lying_v/lying_v_oblique/lying_top/lying_h/ambiguous。 |
| pose_3d 推定 | orientation → 固定 (pitch, yaw, roll) テーブル参照 | 連続回帰 ML | 初手は固定テーブルで 立/倒 二値判定 ≥ 0.92 を狙う。OBB ヘッドが入ったら 6DoF 回帰に置換。 |
| 立/倒 判定 | pitch ≥ 60° 立 / pitch ≤ 30° 倒 / それ以外 不確定 | aspect ratio 直接 3 値 | pitch 経由にすることで、将来 OBB / 6DoF に置き換えても判定ロジックが不変。 |
| 撮影設置 | 斜め上 (oblique-overhead) 設置 | 天井真下 (overhead) | 2026-04-29 方針転換。理由: M-League ダイジェスト中継の主視点が斜め上であり、データ取得コストが圧倒的に低い。詳細 |
| キャリブレーション | チェスボード + cv2.calibrateCamera + solvePnP |
ChArUco / ArUco / Bundle Adjustment | 1 度きりの手動セットアップで十分。マーカー無くす運用リスクが低い。ChArUco は将来差し替え予定。 |
| 項目 | 採用 | 不採用案 | 採用理由 |
|---|---|---|---|
| アガリ遷移検出 | detect_agari_transitions = 立群 → 倒群 時系列遷移 |
静止 1 フレーム + ML 分類 | 静止 1 枚は「全倒し終わった後」しか映らず、立て手牌の証拠が消える。時間軸の使用が本質的に必要。 |
| 音声トリガー | faster-whisper (small) で日本語 ASR + 正規表現 (ツモ/ロン/積も/論/和了) | 音声検出をやめて視覚単独 | 視覚単独だと推論密度が必要 (B2 と衝突)。音声は recall 限界があるが、視覚と マージで補完 できる (scripts/merge-events.py)。 |
| 視覚×音声マージ | peak_timestamp_sec と voice start_sec の差が 15 秒以内なら同一 | 音声単独 / 視覚単独 | 視覚に見落としがあっても音声でカバー。逆も成立。source: "both" なら confidence 高い。 |
| プレイヤー領域分離 | Y-band 1次元クラスタリング (暫定) → ROI 6 分割 (将来) | 領域分離をしない全卓検出 | 領域分離なしだと「立 13 = 面前」のような構造的整合性チェックが回らず、誤検出を弾けない。 |
| 鳴き枚数ルール | 立て枚数 ∈ {13, 10, 7, 4, 1} → 鳴き数 {0, 1, 2, 3, 4} | 鳴きを直接 ML で分類 | 麻雀ルールから演繹できる構造制約。zero-shot で適用でき、検出誤りの自動補正に使える。 |
| 項目 | 採用 | 不採用案 | 採用理由 |
|---|---|---|---|
| 役判定エンジン | PyPI mahjong 2.0.0 (純 Python) |
自前役判定 / Web API | 標準 30 役対応・36/36 単体テスト PASS・ライセンス問題なし。オフライン動作可。 |
| 手牌+和了牌+ドラ分離 | src/scoring/agari_parser.py (13+1 + ドラ) |
1 リストで渡す | mahjong ライブラリ API が手牌・和了牌・ドラを別引数で要求するため、視覚検出から分離する責務が必要。 |
| 暗槓 / 明槓 符 | 5/5 PASS で検証済 (暗槓 50 符 vs 明槓 30 符) | — | 役判定ライブラリの符計算限界の事前検証。 |
| 項目 | 採用 | 不採用案 | 採用理由 |
|---|---|---|---|
| 点棒管理 | RFID 点棒トレイ (PN5180 + ESP32) + CV | CV 単独 / 手動入力 | 点棒は 視覚で読みにくく数を間違える 領域。物理 RFID で正解を取り、CV は牌に集中させる役割分担。 |
| 推論実行場所 | (現在) Roboflow Hosted → (将来) Cloudflare Workers AI / Jetson Orin Nano | 恒久的に Roboflow 依存 | Roboflow Hosted は MVP 検証に最適だが、本番運用では BYOM (Bring Your Own Model) でレイテンシ・コスト最適化。 |
| スマートグラス連携 (将来) | INMO Air 3 ベース (クラウドファンディング) | — | Mahjong Vision プロダクトの本命。検出ロジックは PC で確立 → グラスへポート。 |
役・点数まわりで採用している麻雀固有のロジック・ルールを、ファイル単位で整理します。
| ファイル | 担当 | 主なロジック |
|---|---|---|
src/scoring/agari_parser.py |
あがり画面パーサー | 7-クラス orientation 分類 (h/w aspect ratio) + 13+1 (手牌13 + 和了牌1) + ドラの構造分離 |
src/scoring/pose_3d.py |
3D 姿勢推定 | orientation → (pitch, yaw, roll) 固定テーブル / 立 (pitch ≥ 60°) / 倒 (pitch ≤ 30°) / 不確定 二値判定 |
src/scoring/hand_structure.py |
鳴き構造ルール | 立て枚数 ∈ {13, 10, 7, 4, 1} → 鳴き数 {0, 1, 2, 3, 4} の VALID 集合判定 (tolerance ±1) |
src/scoring/agari_transition_detector.py |
アガリ遷移検出 | 直前 ~1.5 秒の立群 → 倒群 への遷移を時系列で検出 (window_seconds=1.5) |
scripts/extract-mleague-events.py |
音声トリガー | faster-whisper (small) + 正規表現 ツモ|つも|積も|ロン|ろん|栄和|和了|あがり |
scripts/extract-agari-visual.py |
視覚トリガー | 等間隔 3 秒サンプリング → 検出牌数 ≥ 12 を「あがり候補」 → 10 秒ウィンドウでクラスタリング → peak frame 採用 |
scripts/merge-events.py |
視覚×音声マージ | 視覚 peak と音声 start の差 ≤ 15 秒で同一あがり判定。マージ済の render_timestamp_sec は視覚優先 (より正確) |
PyPI mahjong 2.0.0 |
役・翻・符・点数計算 | 標準 30 役・暗槓/明槓 符の正確計算。36/36 単体テスト PASS |
src/cv/calibration.py + tools/calibrate-oblique-view.py |
カメラキャリブ | チェスボード画像から内部行列 + 卓平面の外部姿勢を cv2.calibrateCamera + solvePnP で導出 |
本プロジェクトは様々な代替案を検討・棄却・将来採用候補に分類してきました。 凡例: 採用 部分採用 将来採用 不採用
| 代替案 | 判定 | 理由・備考 |
|---|---|---|
| Roboflow Universe 公開モデル継続使用 | 不採用 | B1: false positive 多発。M-League 専用ファインチューンに移行。 |
| M-League 専用 YOLOv8m ファインチューン | 採用 | 本タスクの主軸。ai-brushup-plans/plans/01-mleague-yolo-finetune.md |
| YOLOv9c / RT-DETR / Roboflow 3.0 | 将来 | v1 は YOLOv8m (Fast) で速度優先。精度が必要なら段階的に上位モデルへ。 |
| OBB (Oriented Bounding Box) ヘッド | 将来 | 横倒し牌の精度向上に有効。アノテーション工数 +30% でラベリングツール選定が課題 (v3 以降)。 |
| 34 クラス (1m..7z) を最初からフル学習 | 不採用 | MVP v1 で 1 クラスから開始し、v2 で 34 クラスに拡張する 2 段戦略。 |
| 136 クラス (34牌種 × 4状態) | 不採用 | 配置と姿勢を 1 軸に押し込む設計。立/倒 二値判定や 6DoF 接続が悪い。直交多軸+マルチヘッドへ。 |
| CVAT セルフホスト | 将来 | 30,000 枚アノテーションを内製化する場合に検討。MVP は Roboflow Auto-Label で十分。 |
| Roboflow 外注アノテーション ($0.05/件) | 将来 | 500 × 30 = 15,000 bbox ≒ $750。納期 1-2 週間。MVP は自前で対応。 |
| 代替案 | 判定 | 理由・備考 |
|---|---|---|
| 音声トリガーで時刻絞り込み + 連続 5 FPS | 採用 | B2 への解。1 動画 950 推論で無料枠内。 |
| 30fps 全フレーム連続推論 | 不採用 | 無料枠 4 動画で枯渇。本番運用でもエッジ推論なしでは非現実的。 |
| Roboflow Pay-as-you-go ($数/1000 推論) | 将来 | 評価フェーズで複数動画を回す場合に課金。 |
| ローカル GPU (RTX4090) でオフライン推論 | 将来 | Cloudflare Tunnel 経由で運用。本番 dense キャッシュ生成に使用予定。 |
| Cloudflare Workers AI (BYOM) | 将来 | 本番デプロイ候補。Workers AI 対応 ONNX をエクスポート。 |
| Jetson Orin Nano (エッジ推論) | 将来 | 実機・スマートグラス連携時の推論先候補。 |
検出結果 JSON キャッシュ (mleague/dense-frames/) | 採用 | 評価のたびに推論しないで済む。Git 追跡外。 |
| 代替案 | 判定 | 理由・備考 |
|---|---|---|
| 斜め上 (oblique-overhead) 設置 | 採用 | 2026-04-29 方針転換。M-League の主視点と一致 → データ取得コスト最小。 |
| 天井真下 (overhead) 単一固定アングル | 不採用 | ハード未着 + 単一固定アングル前提のモデルは設計拘束として禁止 (将来スマートグラス対応のため)。 |
| 多視点 (寄り・引き・横) を学習データに混入 | 採用 | M-League 中継のカメラスイッチングを活かして汎化性を確保。 |
| 4K カメラ実機 | 将来 | ハード未着 (BOM 推奨 ¥34,780)。クラファンで調達。 |
| カメラキャリブ (チェスボード) | 採用 | 1 度きりの手動セットアップ。tools/calibrate-oblique-view.py |
| ChArUco / ArUco マーカー | 将来 | マーカー紛失リスクとトレードオフ。多視点統合時に再検討。 |
| 代替案 | 判定 | 理由・備考 |
|---|---|---|
| 視覚ファースト + 音声マージ (現行) | 採用 | 視覚 peak が正確、音声で見落とし補完。source: "both" なら信頼度最高。 |
| 音声単独検出 | 不採用 | 表記揺れで recall 低下。sample02 で「ツモ → 積も」「ロン → 論」の漢字変換ミスで recall 0% 実例。 |
| 視覚単独 (連続全フレーム) | 不採用 | B2 の推論コスト制約。 |
| Y-band 1 次元クラスタリング (暫定) | 暫定 | 過分節するが現状はこれで動作。ROI 6 分割で置換予定。 |
| ROI 6 分割推論 (東/南/西/北/王牌/中央) | 将来 | B4 への本命解。カメラキャリブ完了後に実装。 |
| 13/10/7/4/1 鳴き枚数ルール | 採用 | 麻雀ルール由来の構造的制約。zero-shot 適用可。 |
| 静止 1 フレーム + ML 分類 | 不採用 | 「立 → 倒」遷移瞬間の証拠が消える。時系列が本質的に必要。 |
連続フレーム時系列遷移検出 (detect_agari_transitions) | 採用 | 立群 → 倒群を ±1.5 秒で検出。dense 検出 JSON が前提。 |
| 代替案 | 判定 | 理由・備考 |
|---|---|---|
| RFID 点棒トレイ + CV (ハイブリッド) | 採用 | 点棒は CV で読みにくいので物理 RFID で取り、CV は牌に集中。 |
| CV 単独で点棒も認識 | 不採用 | 点棒の数を 100% 正確に読むのは過剰な負担。物理に逃がす。 |
| 手動入力 | 不採用 | 「補助系」の意義が薄れる。 |
| INMO Air 3 ベース スマートグラス | 将来 | Mahjong Vision プロダクトの本命。クラウドファンディング (Makuake) 経由で量産。 |
| FastAPI Orchestrator + SQLite 牌譜 DB | 採用 | 音声/視覚/RFID を Orchestrator が統合。SQLite で天鳳形式 export 対応。 |
| 代替案 | 判定 | 理由・備考 |
|---|---|---|
| Auto-Label (汎用モデル) → 人手修正 | 採用 | 初期 bbox 提示で工数約 70% 削減。MVP v1 は自前修正 500 枚で開始。 |
| 手動アノテーション (CVAT) | 将来 | 30,000 枚スケールで内製化する場合に検討。 |
| クラウドソーシング外注 | 将来 | $0.05/件、500 × 30 = $750 ≒ ¥110,000。納期 1-2 週間。 |
| テロップ OCR で自動ラベル抽出 (EasyOCR PoC) | 採用 | 「ロン xx,xxx」「ツモ xx,xxx」を 100% 抽出成功 (コミット 95c2736)。半自動ラベリングの基盤。 |
| テロップなしで音声のみ | 不採用 | 表記揺れで recall 低下 (前述)。 |
Roboflow セットアップ → 自前モデル訓練の準備段階として、以下が完了しています。
| 項目 | 状態 | 定量結果 |
|---|---|---|
| 動画から訓練フレーム抽出 | ✅ | sample01/02 から 499 枚 (mleague/frames-roboflow/) |
| 自動フィルタ (麻雀卓 vs 非卓) | ✅ | 201 keep / 298 discard |
| フィルタ品質監査 | ✅ | false positive / false negative ともに 0 件 (本日確認) |
| クラス定義 (MVP v1 / v2) | ✅ | v1: 1 クラス (tile), v2: 34 クラス |
| Roboflow Workspace 作成 | 🔴 | 未着手 (次工程) |
| 画像アップロード | 🔴 | 未着手 (次工程) |
| Auto-Label 実行 | 🔴 | 未着手 |
| 人手アノテーション | 🔴 | 未着手 (半日〜1 日想定) |
| 訓練 + デプロイ | 🔴 | 未着手 (2-4 時間自動) |
| 既存スクリプト切替 | 🔴 | 未着手 (deploy URL 受領後 1 時間) |
docs/roboflow-setup-guide.md — 322 行・Roboflow セットアップ全手順docs/current-detection-and-rendering-spec.md — 308 行・現状の検出/描画仕様docs/agari-transition-eval-data-needs.md — 163 行・アガリ遷移評価データ要件ai-brushup-plans/plans/01-mleague-yolo-finetune.md — M-League ファインチューン計画詳細scripts/extract-roboflow-frames.py — フレーム抽出scripts/filter-roboflow-frames.py — フィルタscripts/audit-roboflow-filter.py — フィルタ品質監査Roboflow 自前モデル訓練を本格再開する前に、以下の意思決定が必要です。
mleague/dense-frames/ にキャッシュ → evaluate-agari-transition-detector.py で評価