Newer
Older
SmTIAS-Evaluation / scripts / compare_smtias_tias_radial.py
"""SmTIAS(DNG)と TIAS DFK23(PNG)の動径プロファイルを 1 枚に重ね描きする.

compare_smtias_tias_relative.png(輝度マップ 2 枚を共通相対スケールで並置した図)の
**動径プロファイル版**.いまは SmTIAS と TIAS で別々の radial_profile.png が 2 枚あるのを,
**各自のピーク=1.0 で正規化** して 1 枚に重ね,中心からの減衰カーブの形を直接比較できるようにする.

各自ピーク=1.0 で正規化する理由(共通スケールにしない):
- SmTIAS(DNG linear+LSC, 8bit→linear)と TIAS(PNG sRGB, DFK23)は
  ビット深度・色空間・センサが異なり,絶対輝度は比較できない.
- 各自の最内ピークを 1.0 に揃えると「中心からどれだけ落ちるか(減衰カーブの形)」だけが残り,
  谷の底がそのまま動径 min/max 比に一致する(compare_png_dng_slide.py 下段と同方式).

対象(compare_smtias_tias_relative.png と同一入力):
- SmTIAS : DNG (linear + LSC 補正)  SmTIAS_QM_20260601_124500  → 動径 min/max 0.860
- TIAS   : PNG (vignette 未補正)     WhiteBoard_0001            → 動径 min/max 0.982

使い方:
    python scripts/compare_smtias_tias_radial.py
"""

import sys
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np

PROJECT_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(PROJECT_ROOT))

from src.analysis.spatial import (  # noqa: E402
    calc_distance_map,
    calc_radial_min_max_ratio,
    calc_radial_profile,
)
from src.analysis.uniformity import (  # noqa: E402
    REC709_COEFF_B,
    REC709_COEFF_G,
    REC709_COEFF_R,
)
from src.config import load_roi_config  # noqa: E402
from src.io.dng_pipeline import dng_luminance  # noqa: E402
from src.io.loader import extract_roi, load_image  # noqa: E402

# compare_smtias_tias_relative.png と同一の入力
SMTIAS_PATH = PROJECT_ROOT / "data" / "smtias" / "quantitative" / "SmTIAS_QM_20260601_124500.dng"
TIAS_PATH = PROJECT_ROOT / "data" / "tias" / "whiteboard" / "WhiteBoard_0001.png"
ROI_CONFIG = PROJECT_ROOT / "config" / "roi_config.json"
SMTIAS_ROI_KEY = "smtias.whiteboard"
TIAS_ROI_KEY = "tias.whiteboard"
OUTPUT_PATH = PROJECT_ROOT / "output" / "compare" / "compare_smtias_tias_radial.png"


def _smtias_luminance(roi: dict) -> np.ndarray:
    """SmTIAS DNG を linear + LSC 補正で輝度化し ROI を切り出す."""
    luma = dng_luminance(str(SMTIAS_PATH), apply_lsc=True)
    return extract_roi(luma, roi["x"], roi["y"], roi["width"], roi["height"])


def _tias_luminance(roi: dict) -> np.ndarray:
    """TIAS PNG を Rec.709 で輝度化し ROI を切り出す(vignette 未補正)."""
    img = load_image(str(TIAS_PATH))
    luma = (
        REC709_COEFF_R * img[:, :, 0]
        + REC709_COEFF_G * img[:, :, 1]
        + REC709_COEFF_B * img[:, :, 2]
    )
    return extract_roi(luma, roi["x"], roi["y"], roi["width"], roi["height"])


def _radial(luminance: np.ndarray) -> tuple[np.ndarray, dict]:
    """ROI 輝度から動径プロファイルと min/max 統計を算出する."""
    distance_map = calc_distance_map(*luminance.shape)
    profile = calc_radial_profile(luminance, distance_map)
    radial = calc_radial_min_max_ratio(profile)
    return profile, radial


def main() -> None:
    """各自ピーク=1.0 で正規化した動径プロファイル重ね描きを生成して保存する."""
    smtias_roi = load_roi_config(str(ROI_CONFIG), SMTIAS_ROI_KEY)
    tias_roi = load_roi_config(str(ROI_CONFIG), TIAS_ROI_KEY)
    if smtias_roi is None or tias_roi is None:
        raise SystemExit("ROI が config に見つかりません")

    smtias_prof, smtias_r = _radial(_smtias_luminance(smtias_roi))
    tias_prof, tias_r = _radial(_tias_luminance(tias_roi))

    fig, ax = plt.subplots(figsize=(9, 6))

    # 各自の最内ピーク(radial_max)で正規化して形だけを重ねる
    sx, sy = smtias_prof[:, 0], smtias_prof[:, 1] / smtias_r["radial_max"]
    tx, ty = tias_prof[:, 0], tias_prof[:, 1] / tias_r["radial_max"]
    ax.plot(
        sx, sy, marker="o", ms=5, color="#1f3b73",
        label=f"SmTIAS   min/max = {smtias_r['radial_min_max_ratio']:.3f}",
    )
    ax.plot(
        tx, ty, marker="s", ms=5, color="#e08214",
        label=f"TIAS     min/max = {tias_r['radial_min_max_ratio']:.3f}",
    )

    # 谷の底(= 動径 min/max 比)を水平線で強調
    ax.axhline(smtias_r["radial_min_max_ratio"], color="#1f3b73", ls=":", alpha=0.5)
    ax.axhline(tias_r["radial_min_max_ratio"], color="#e08214", ls=":", alpha=0.5)

    ax.set_title("Radial profile: SmTIAS vs TIAS DFK23 (normalized, peak = 1.0)", fontsize=12)
    ax.set_xlabel("Normalized distance from center")
    ax.set_ylabel("Normalized mean luminance (own peak = 1.0)")
    ax.grid(True, linestyle="--", alpha=0.5)
    ax.legend(loc="lower left", fontsize=10)
    fig.tight_layout()

    OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
    OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
    fig.savefig(str(OUTPUT_PATH), dpi=150)
    plt.close(fig)
    print(f"動径プロファイル重ね描きを保存しました: {OUTPUT_PATH}")
    print(f"  SmTIAS (124500)        radial min/max = {smtias_r['radial_min_max_ratio']:.4f}")
    print(f"  TIAS   (WhiteBoard_0001) radial min/max = {tias_r['radial_min_max_ratio']:.4f}")


if __name__ == "__main__":
    main()