"""SmTIAS 定量モード DNG の照明均一性評価スクリプト(PNG 版 run_uniformity.py の DNG 対応版).
PNG 版と同じ出力一式(輝度マップ / ヒストグラム / 動径プロファイル / ゾーンマップ /
均一性 CSV / コンソール指標)を,DNG に対して同じ ROI・同じ向き(portrait)で出力する.
PNG 版との違い:
- 読み込みを load_image_dng(linear, output_color=raw)に置換
- DNG は RAW_SENSOR で未補正のため,meta.json の lscMap でレンズシェーディングを順適用(--no-lsc で無効化可)
- DNG はセンサ native が landscape のため,PNG と同じ portrait へ rot90(-1) で向き合わせ
使い方:
python scripts/run_uniformity_dng.py --image data/smtias/quantitative/xxx.dng
"""
import argparse
import sys
from pathlib import Path
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.analysis.uniformity import calc_uniformity # noqa: E402
from src.config import load_roi_config # noqa: E402
from src.export.exporter import ensure_output_dirs, export_results # noqa: E402
from src.io.dng_pipeline import dng_luminance # noqa: E402
from src.io.loader import extract_roi # noqa: E402
from src.visualization.plotter import ( # noqa: E402
plot_histogram,
plot_luminance_map,
plot_radial_profile,
plot_zone_map,
)
def analyze_single_dng(
image_path: str,
roi: dict | None,
output_base: Path,
apply_lsc: bool,
) -> None:
"""DNG 1 枚を解析し,PNG 版と同じ出力一式を生成する.
Args:
image_path: 解析対象 DNG ファイルのパス.
roi: ROI の辞書(x, y, width, height).None の場合は画像全体を解析.
output_base: 出力ルートディレクトリのパス.
apply_lsc: True の場合,LSC(レンズシェーディング)補正を適用する.
"""
print(f"解析開始: {image_path}")
luma_full = dng_luminance(image_path, apply_lsc)
print(f"画像サイズ: {luma_full.shape[1]} x {luma_full.shape[0]} px(portrait)")
print(f"LSC 補正: {'あり' if apply_lsc else 'なし'} / ドメイン: linear(output_color=raw)")
if roi is not None:
print(
f"ROI を適用します: x={roi['x']}, y={roi['y']}, "
f"width={roi['width']}, height={roi['height']}"
)
luminance = extract_roi(luma_full, roi["x"], roi["y"], roi["width"], roi["height"])
else:
print("ROI 未指定: 画像全体を解析します")
luminance = luma_full
results = calc_uniformity(luminance)
print("--- 均一性指標(輝度は 0〜255 linear スケール)---")
print(f" 平均輝度 : {results['mean']:.2f}")
print(f" 標準偏差 : {results['std']:.2f}")
print(f" CoV : {results['cov']:.4f}")
print(f" 最大/最小比 : {results['max_min_ratio']:.4f}")
print(f" 最大輝度 : {results['max']:.2f}")
print(f" 最小輝度 : {results['min']:.2f}")
spatial = calc_spatial_uniformity(luminance)
zone_stats = spatial["zone_stats"]
print("--- 空間解析指標 ---")
print(f" 中心平均輝度 : {zone_stats['center_mean']:.2f}")
print(f" 中間平均輝度 : {zone_stats['middle_mean']:.2f}")
print(f" 周辺平均輝度 : {zone_stats['periphery_mean']:.2f}")
print(f" 中心/周辺比 : {zone_stats['center_periphery_ratio']:.4f}")
print(f" 勾配量 (%) : {zone_stats['gradient_magnitude']:.2f}")
print(f" 動径min/max比 : {spatial['radial_min_max_ratio']:.4f}")
stem = Path(image_path).stem # 例: SmTIAS_QM_20260601_102753
figures = output_base / "figures"
plot_luminance_map(luminance, str(figures / f"{stem}_luminance_map.png"))
plot_histogram(luminance, str(figures / f"{stem}_histogram.png"))
plot_radial_profile(spatial["radial_profile"], str(figures / f"{stem}_radial_profile.png"))
plot_zone_map(luminance, spatial["zone_map"], str(figures / f"{stem}_zone_map.png"))
export_results(results, str(output_base / "results" / f"{stem}_uniformity.csv"))
print(f"解析完了: {image_path}")
def main() -> None:
"""エントリーポイント."""
parser = argparse.ArgumentParser(
description="SmTIAS 定量モード DNG の照明均一性を評価する(PNG 版と同じ出力一式)."
)
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()
output_base = Path(args.output)
ensure_output_dirs(args.output)
roi = None
if args.config:
try:
roi = load_roi_config(args.config, args.roi)
except FileNotFoundError as e:
print(f"警告: {e} → 画像全体を解析します")
analyze_single_dng(args.image, roi, output_base, apply_lsc=not args.no_lsc)
if __name__ == "__main__":
main()