"""セグメンテーションの評価の図(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)