# TIASjudge 引き渡し仕様 (TIASjudge Handoff)

TIASjudge に「撮影画像 → 舌のセグメンテーション → 舌色 5 分類」の判定モードを組み込むために，本リポジトリが引き渡すものと，TIASjudge 側で守るべき処理の手順を定める（本リポジトリの Issue #9．TIASjudge 側は TIASjudge の Issue #13）．検証結果は [TEST_07](../06_TEST/TEST_07_セグメンテーションの再現と判定モデルの書き出し.md)．

## 全体の流れ (Pipeline)

```text
撮影画像（TIAS・色補正なし．cv2.imread の BGR）
  → 1. 舌の検出（Detection_Normal.onnx）: 舌の枠
  → 2. 舌の分割（ResUNet.onnx）: 枠の中の舌マスク → 元の画像サイズのマスク
  → 3. マスク外を黒 (0,0,0) にした RGB 画像
  → 4. 色特徴（107 次元）→ ロジスティック回帰（color_model.json）: 5 クラスの確率
```

1〜2 は DeepTIAS 2.7 の「画像のみ」モードの手順を再現したもの（DeepTIAS の実行ファイルの逆コンパイルで確認）．3 は DeepTIAS の `2-segmentation/5-extraction` と同じ形で，判定モデルの学習データもこの形である．

## 入力の前提 (Input Requirements)

- **TIAS の機材で撮影した，色補正をしていない画像**に限る．判定モデルは補正なしの RGB で学習しているため，TIASjudge の変換行列（ROI 診断で使う多項式変換）を通した画像や，他の機材で撮った画像には使えない．画面にこの前提を明示する
- 既存のルールベース診断（ROI クリック → 変換行列 → SVM）は置き換えず，**別の判定モード**として追加する（TIASjudge の「診断結果は旧実装と一致させる」ルールのため）

## 引き渡すもの (Deliverables)

| もの | 内容 | 入手先 |
| --- | --- | --- |
| `color_model.json` | 判定モデル（特徴の定義・標準化の値・係数・切片・クラス・学習条件・来歴）．約 28 KB．**ややあり除外のモデル**（下記） | `tias-train color-export --config configs/export/color_logistic_without_mild.yaml` の出力（サーバで実行し `tias-remote pull` で回収．[ENV_04](../02_ENV/ENV_04_開発コマンド.md)）．2026-09-28 の書き出しは実験 ID `20260928-232605_color_logistic_without_mild_405ab01-dirty` |
| `deploy/color_model.py` | 判定モデルの参照実装（NumPy と標準ライブラリのみ．単一ファイル） | `src/tias_judge_model/deploy/color_model.py` をそのままコピーする |
| `segmentation/`（`detection.py`・`resunet.py`・`pipeline.py`） | セグメンテーションの参照実装（NumPy・OpenCV・onnxruntime のみ．パッケージ内では相互にだけ依存） | `src/tias_judge_model/segmentation/` から 3 ファイルをコピーし，import 文のパッケージ名を合わせる |
| `ResUNet.onnx` | 舌の分割モデル（約 71 MB．入力 `input.1` (1,3,256,256) float32，出力 (1,2,256,256)） | DeepTIAS 2.7 の `models/` に同梱のもの（開発元のファイルとハッシュが一致） |
| `Detection_Normal.onnx` | 舌の検出モデル（約 12 MB） | DeepTIAS 2.7 の `models/Detection_Normal.pb` を `scripts/convert_detection_to_onnx.py` で変換したもの（サーバの `~/models/TongueSegmentation/` にある） |

- モデルファイル（`.onnx`・`color_model.json`）は本リポジトリに入れない（臨床データから学習したもの）
- **DeepTIAS のモデル（`ResUNet.onnx`・`Detection_Normal.onnx`）は TIASjudge の配布物に含めてよい**（2026-09-29 にユーザーが確認）
- TIASjudge 側に必要な依存: `numpy`・`opencv-python`（既存）・`onnxruntime`（追加）．TensorFlow・PyTorch・scikit-learn は不要

### 渡す判定モデル (Selected Model)

**ややあり除外のモデル**（`configs/export/color_logistic_without_mild.yaml`．学習 754 枚・C = 0.1）を渡す（2026-09-29 のユーザー判断）．

- 「あり」と淡紅の画像での成績が込みのモデルよりやや良い（common macro F1 0.644 対 0.630．[TEST_05](../06_TEST/TEST_05_色特徴分類器の実験.md)）
- ややありの画像を淡紅と判定しやすい（約 55 %．込みは約 33 %）．臨床で「ややあり」を淡紅と判断することが多い点と整合する
- 学習にややありを含まないため，ややありの画像では「あり」の色か淡紅のどちらかに寄った判定になる．境界例であることは確率の値（最大の確率が低い）で示す

## 1. 舌の検出 (Detection)

`segmentation/detection.py` の `OnnxDetector.detect(bgr)`．

- 入力: 画像を **RGB に並べ替えた** uint8 の `(1, H, W, 3)`（入力名 `image_tensor:0`．TF の物体検出 API の既定．DeepTIAS も Bitmap 経由で RGB を渡している）．画像サイズは任意
- 出力: `detection_boxes`（正規化座標 [ymin, xmin, ymax, xmax]）・`detection_scores`
- スコアが **0.5 を超える**枠のうち最高の 1 つを採用する（0.5 ちょうどは不採用．同点は先の枠）
- 正規化座標に画像の高さ・幅を掛けて **切り捨て**で画素の枠にし，画像の内側に収める
- 枠が無い（0.5 を超えない）場合は「舌を検出できなかった」として判定を行わず，利用者に撮り直し等を促す

## 2. 舌の分割 (Segmentation)

`segmentation/pipeline.py` の `segment_in_rect(bgr, rect, runner, params)`（`runner` は `resunet.py` の `ResUNetRunner`）．

1. 枠で切り出した画像を `cv2.resize`（線形補間）で 256×256 にする
2. **BGR のまま** 255 で割り，CHW の float32 にして ResUNet に入れる（学習時の `cv2.imread` の BGR /255 と同じ）
3. 出力の**チャンネル 0**（舌）にシグモイドを掛け，×255 を切り捨てて uint8 にする
4. 大津の方法で 2 値化する
5. 輪郭（Canny → 外側の輪郭）が 0 個または 2 個を超える場合だけ，面積 500 画素未満の前景の島を除き，次に同じく小さい背景の穴を埋める（8 近傍）
6. 枠のサイズに線形補間で戻し，127 を超える画素を舌とする
7. 元の画像サイズのマスクに配置する（枠の外は背景）

設定値（`SegmentationParams`）: `foreground_channel=0`・`min_area=500`・`mask_threshold=127`．[TEST_07](../06_TEST/TEST_07_セグメンテーションの再現と判定モデルの書き出し.md) の評価（IoU 0.958）はこの値で行った．

- マスクが空（舌の画素が 0）なら判定を行わず，分割に失敗したことを示す
- 結果の確認のため，マスクの輪郭を撮影画像に重ねて表示する（TIASjudge の Issue #13）

## 3. マスクの適用 (Masking)

- 撮影画像（BGR）を **RGB に並べ替え**，マスクの外を (0, 0, 0) にする
- 判定モデルは「RGB がすべて 0 の画素」を背景とみなす．舌の中にもともと (0, 0, 0) の画素があれば背景扱いになるが，学習データも同じ扱いなので問題ない

## 4. 舌色の判定 (Classification)

`deploy/color_model.py`．

```python
model = load_color_model("color_model.json")
probs = predict_proba(rgb_masked, model)  # (5,) の確率．並びは model の classes の順
```

- 長辺が `max_side`（512）を超える画像は最近傍法で縮小してから特徴を取る（学習時と同じ．`predict_proba` の中で行う）
- クラスの順は `color_model.json` の `classes`（0 淡白・1 淡紅・2 紅・3 暗赤紅・4 紫）に従う．最大の確率のクラスを判定結果とし，5 クラスの確率も表示する
- 参照実装は学習側と同じ予測を返す（特徴はビット一致，確率の差 1e-12 以下．本リポジトリのテストで確認）．TIASjudge 側では，同じ画像に対して参照実装と同じ確率が出ることをテストで確かめる

## 性能の目安 (Expected Performance)

| 段階 | 指標 | 値 | 出典 |
| --- | --- | --- | --- |
| セグメンテーション | 撮影画像からの舌の IoU（検出＋ResUNet．170 枚） | 平均 0.958・最小 0.864 | TEST_07 |
| 舌色の判定 | 「あり」と淡紅での macro F1（5-fold × 3 seed） | 0.630（ややあり込み）〜0.644（除外） | [TEST_05](../06_TEST/TEST_05_色特徴分類器の実験.md) |
| 舌色の判定 | 同・正解率 | 0.72〜0.75 | TEST_05 |

判定の性能は医師のラベルとの一致がまだ 7 割程度であり，**当面は参考表示とする**．ルールベース診断との比較（[PLAN_02](../03_PLAN/PLAN_02_開発ステップ.md) Step 6）の結果を見て扱いを決める．

## 未決事項 (Open Items)

- 配布サイズ（`ResUNet.onnx` 約 71 MB で単体 exe が大きくなる）
- 学習データ（舌色データセット）のマスクと，本実装のマスクがビット単位で一致するかは未確認（元の撮影画像が無いため．TEST_07「注意」）
