Newer
Older
TIASJudgeModel / src / tias_judge_model / evaluation / segmentation_figures.py
"""セグメンテーションの評価の図(IoU の分布).

`figures/` は回収対象であるため,集計値の図だけを描き,画像(入力・マスク・重ね合わせ)は
描かない(SPEC_01「実験の出力」).`evaluation/figures.py` と同じく pyplot を使わず `Figure` +
`FigureCanvasAgg` で描き,ラベルは英字で書く.
"""

from __future__ import annotations

from collections.abc import Mapping, Sequence
from pathlib import Path

import numpy as np
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.figure import Figure

_DPI = 120
# IoU のヒストグラムのビン(0〜1 を 0.02 刻み).
_IOU_BINS = np.linspace(0.0, 1.0, 51)


def iou_histogram_filename(set_name: str) -> str:
    """評価セットの IoU のヒストグラムのファイル名を返す.

    Args:
        set_name: 評価セットの名前.

    Returns:
        ファイル名(`seg_iou_hist_<set_name>.png`).
    """
    return f"seg_iou_hist_{set_name}.png"


def plot_iou_histograms(
    ious_by_method: Mapping[str, Sequence[float]], title: str, path: Path
) -> None:
    """方式ごとの IoU のヒストグラムを縦に並べた図を保存する.

    Args:
        ious_by_method: 方式名 → 画像ごとの IoU(空の方式は描かない).
        title: 図のタイトル.
        path: 保存先(親ディレクトリは作る).
    """
    methods = [m for m, v in ious_by_method.items() if len(v) > 0]
    n = max(1, len(methods))
    fig = Figure(figsize=(6.0, 2.2 * n + 0.6))
    FigureCanvasAgg(fig)
    axes = fig.subplots(n, 1, squeeze=False, sharex=True)[:, 0]
    for ax, method in zip(axes, methods, strict=False):
        values = np.asarray(ious_by_method[method], dtype=np.float64)
        ax.hist(np.clip(values, 0.0, 1.0), bins=_IOU_BINS, color="tab:blue")
        ax.set_ylabel("count")
        ax.set_title(f"{method} (n={values.size}, mean={values.mean():.3f})", fontsize=9)
    axes[-1].set_xlabel("IoU (foreground)")
    axes[-1].set_xlim(0.0, 1.0)
    fig.suptitle(title, fontsize=10)
    fig.tight_layout()
    path.parent.mkdir(parents=True, exist_ok=True)
    fig.savefig(path, dpi=_DPI)