"""再現性評価モジュール."""
import numpy as np
from itertools import combinations
from skimage.metrics import structural_similarity as ssim
def calc_pairwise_ssim(luminance_maps: list[np.ndarray]) -> dict:
"""複数の輝度マップ間のペアワイズ SSIM を算出する.
Args:
luminance_maps: 輝度マップのリスト(各要素は同一サイズの 2D numpy 配列).
Returns:
{
"pairwise_scores": list[dict] — 各ペアの SSIM スコア
[{"image_a": 0, "image_b": 1, "ssim": 0.998}, ...],
"mean_ssim": float — 全ペアの平均 SSIM,
"min_ssim": float — 最小 SSIM,
"max_ssim": float — 最大 SSIM,
"std_ssim": float — SSIM の標準偏差,
"n_pairs": int — ペア数,
}
Raises:
ValueError: 輝度マップが 2 枚未満の場合.
"""
if len(luminance_maps) < 2:
raise ValueError(
f"輝度マップは 2 枚以上必要です(現在: {len(luminance_maps)} 枚)."
)
# Rec.709 輝度(uint8 RGB → float64)の取りうる範囲は 0.0〜255.0
data_range = 255.0
pairwise_scores: list[dict] = []
for idx_a, idx_b in combinations(range(len(luminance_maps)), 2):
score = float(
ssim(
luminance_maps[idx_a],
luminance_maps[idx_b],
data_range=data_range,
)
)
pairwise_scores.append({"image_a": idx_a, "image_b": idx_b, "ssim": score})
scores = np.array([p["ssim"] for p in pairwise_scores])
return {
"pairwise_scores": pairwise_scores,
"mean_ssim": float(np.mean(scores)),
"min_ssim": float(np.min(scores)),
"max_ssim": float(np.max(scores)),
"std_ssim": float(np.std(scores, ddof=0)),
"n_pairs": len(pairwise_scores),
}