"""暗闇 vs 室内(外乱光)の動径プロファイル重ね描きスライドを生成する.
REPORT_03(外乱光影響評価)に対応.同一 DNG 定量モード(linear + LSC 補正,露出/ISO 固定)で
撮った 2 枚を,動径プロファイルを **絶対輝度のまま** 1 枚に重ねて比較する.
軸を絶対輝度にする理由(正規化しない):
- 両者は同じ linear 単位・同じ露出/ISO・同じ LSC 補正なので絶対値が正当に比較可能.
- 「外光を足したのに暗い」(暗闇 raw mean 441.2 > 室内 431.4)という逆転は絶対値でしか見えない.
各自の最大で正規化するとこの"レベルも含めてほぼ同一"という情報が消える.
- 2 本がほぼ重なること自体が「外乱光にロバスト」というメッセージになる.
対象(REPORT_03 の条件 A/B):
- 暗闇 (lights OFF) : SmTIAS_QM_20260601_124500 → min/max 0.860
- 室内 (lights ON) : SmTIAS_QM_20260601_102753 → min/max 0.853
使い方:
python scripts/compare_ambient_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.config import load_roi_config # noqa: E402
from src.io.dng_pipeline import dng_luminance # noqa: E402
from src.io.loader import extract_roi # noqa: E402
# REPORT_03 の条件 A(暗闇=室内灯OFF)/ 条件 B(室内=室内灯ON)
DARK_PATH = PROJECT_ROOT / "data" / "smtias" / "quantitative" / "SmTIAS_QM_20260601_124500.dng"
INDOOR_PATH = PROJECT_ROOT / "data" / "smtias" / "quantitative" / "SmTIAS_QM_20260601_102753.dng"
ROI_CONFIG = PROJECT_ROOT / "config" / "roi_config.json"
ROI_KEY = "smtias.whiteboard"
OUTPUT_PATH = PROJECT_ROOT / "output" / "figures" / "compare_ambient_radial.png"
def _radial(path: Path, roi: dict) -> tuple[np.ndarray, dict]:
"""DNG(linear + LSC)の ROI 動径プロファイルと min/max 統計を返す."""
luma = dng_luminance(str(path), apply_lsc=True)
luma = extract_roi(luma, roi["x"], roi["y"], roi["width"], roi["height"])
distance_map = calc_distance_map(*luma.shape)
profile = calc_radial_profile(luma, distance_map)
radial = calc_radial_min_max_ratio(profile)
return profile, radial
def main() -> None:
"""重ね描きスライドを生成して保存する."""
roi = load_roi_config(str(ROI_CONFIG), ROI_KEY)
if roi is None:
raise SystemExit(f"ROI '{ROI_KEY}' が config に見つかりません")
dark_prof, dark_r = _radial(DARK_PATH, roi)
indoor_prof, indoor_r = _radial(INDOOR_PATH, roi)
fig, ax = plt.subplots(figsize=(9, 6))
ax.plot(
dark_prof[:, 0], dark_prof[:, 1],
marker="o", ms=5, color="#1f3b73",
label=f"Dark (lights OFF) min/max = {dark_r['radial_min_max_ratio']:.3f}",
)
ax.plot(
indoor_prof[:, 0], indoor_prof[:, 1],
marker="s", ms=5, color="#e08214",
label=f"Indoor (lights ON) min/max = {indoor_r['radial_min_max_ratio']:.3f}",
)
ax.set_title("Ambient light effect on radial profile (DNG, linear + LSC)", fontsize=12)
ax.set_xlabel("Normalized distance from center")
ax.set_ylabel("Mean luminance (absolute, linear 0-255 scale)")
ax.grid(True, linestyle="--", alpha=0.5)
ax.legend(loc="upper right", fontsize=10)
fig.tight_layout()
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" Dark (124500) min/max = {dark_r['radial_min_max_ratio']:.4f}")
print(f" Indoor (102753) min/max = {indoor_r['radial_min_max_ratio']:.4f}")
if __name__ == "__main__":
main()