"""動径輝度プロファイルを「min/max 比注記・凡例なし」で出力する一回限りのスクリプト.
既存の動径プロファイル図(plot_radial_profile)から,右上の min/max ratio 注記と
左下の凡例を除いたクリーン版 PNG を出力する.データは run_uniformity_dng.py と同じ
DNG パイプライン(LSC 補正あり・portrait・linear)で再計算する.
使い方:
python scripts/plot_radial_clean.py \\
--image data/smtias/quantitative/SmTIAS_QM_20260601_124500.dng
"""
import argparse
import sys
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt # noqa: E402
import numpy as np # noqa: E402
PROJECT_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(PROJECT_ROOT))
from src.analysis.spatial import calc_spatial_uniformity # noqa: E402
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
def plot_radial_profile_clean(radial_profile: np.ndarray, output_path: str) -> None:
"""動径プロファイルを min/max 比注記・凡例なしで保存する.
plot_radial_profile と同一の見た目だが,右上の min/max ratio テキストと
左下の凡例(Mean Luminance / Max / Min)を描画しない.
最大点(緑)・最小点(赤)のマーカーは残す.
Args:
radial_profile: 放射状プロファイルの NumPy 配列(N x 2, float64).
列 0 = 正規化距離, 列 1 = 平均輝度.
output_path: 出力先ファイルパス(PNG).
"""
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
distances = radial_profile[:, 0]
luminances = radial_profile[:, 1]
min_idx = int(np.argmin(luminances))
max_idx = int(np.argmax(luminances))
lmin = float(luminances[min_idx])
lmax = float(luminances[max_idx])
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(distances, luminances, marker="o", color="steelblue")
ax.scatter(distances[max_idx], lmax, color="green", s=80, zorder=5)
ax.scatter(distances[min_idx], lmin, color="red", s=80, zorder=5)
ax.set_title("Radial Luminance Profile")
ax.set_xlabel("Normalized Distance from Center")
ax.set_ylabel("Mean Luminance")
ax.grid(True, linestyle="--", alpha=0.5)
# min/max ratio 注記・凡例ともに描画しない
fig.tight_layout()
fig.savefig(output_path, dpi=150)
plt.close(fig)
print(f"クリーン版 動径プロファイルを保存しました: {output_path}")
def main() -> None:
"""エントリーポイント."""
parser = argparse.ArgumentParser(
description="動径輝度プロファイルを min/max 比注記・凡例なしで出力する."
)
parser.add_argument("--image", required=True, help="解析対象の DNG ファイルパス")
parser.add_argument(
"--config",
default=str(PROJECT_ROOT / "config" / "roi_config.json"),
help="ROI 設定ファイルのパス(デフォルト: config/roi_config.json)",
)
parser.add_argument(
"--roi",
default="smtias.whiteboard",
help="ROI のキー(ドット区切り,デフォルト: smtias.whiteboard)",
)
parser.add_argument(
"--output",
default=str(PROJECT_ROOT / "output"),
help="出力ディレクトリのパス(デフォルト: output/)",
)
parser.add_argument(
"--no-lsc",
action="store_true",
help="LSC(レンズシェーディング)補正を無効化する(既定は補正あり)",
)
args = parser.parse_args()
roi = None
if args.config:
try:
roi = load_roi_config(args.config, args.roi)
except FileNotFoundError as e:
print(f"警告: {e} → 画像全体を解析します")
luma_full = dng_luminance(args.image, apply_lsc=not args.no_lsc)
if roi is not None:
luminance = extract_roi(
luma_full, roi["x"], roi["y"], roi["width"], roi["height"]
)
else:
luminance = luma_full
spatial = calc_spatial_uniformity(luminance)
stem = Path(args.image).stem
output_path = (
Path(args.output) / "figures" / f"{stem}_radial_profile_clean.png"
)
plot_radial_profile_clean(spatial["radial_profile"], str(output_path))
if __name__ == "__main__":
main()