"""sm-evaluate コマンドラインエントリポイント.
PNG または luminance.npy を入力として均一性評価を実行し,
metrics.csv と可視化 PNG を出力ディレクトリに保存する.
処理本体は `evaluation/pipeline.py` の `evaluate_image()` にある(スイープ等から
プロセス内で直接呼べるようにするため).本モジュールは引数のパースと,
例外 → 「エラー: …」表示 + 終了コード 1 への変換だけを担う.
matplotlib のバックエンドは Agg を強制する(headless 環境対応).
evaluate は Mitsuba を使わないため,render.py と異なり遅延 import は不要.
ただし Agg バックエンドの設定順序を保つため,matplotlib.use() は pyplot import より前に置く.
"""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
import matplotlib
# headless 環境(サーバ・CI 等)でも動くよう Agg バックエンドを強制する.
# pipeline(→ visualize.py)より先に設定することで,pyplot import 時に Agg が有効になる.
matplotlib.use("Agg")
# evaluate_image / EvaluationStepError 以外は,補助関数が本モジュールにあった頃の
# 参照互換のための再エクスポート(テスト・スクリプトが cli.evaluate.<名前> を参照している).
from sm_tias_lightsim.evaluation.pipeline import ( # noqa: E402, F401
_FIGURES_DIRNAME,
_HISTOGRAM_FILENAME,
_LUMINANCE_MAP_FILENAME,
_METRICS_FILENAME,
_RADIAL_PROFILE_FILENAME,
_ZONE_MAP_FILENAME,
EvaluationStepError,
_apply_normalization_to_luminance,
_apply_oetf_to_luminance,
_load_eval_config,
_load_luminance,
_load_reference_luminance,
_print_summary,
_run_fullimage_min_max,
_run_radial_min_max,
_run_radial_profile,
_run_shape_metrics,
_run_zone_analysis,
_save_figures,
_save_metrics_csv,
evaluate_image,
)
from sm_tias_lightsim.evaluation.radial import ( # noqa: E402, F401
calc_radial_min_max_ratio,
calc_radial_profile,
)
def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
"""コマンドライン引数をパースして返す.
Args:
argv: 引数リスト.None のとき sys.argv[1:] を使用.
Returns:
パース済み Namespace.
"""
parser = argparse.ArgumentParser(
prog="sm-evaluate",
description=(
"PNG または luminance.npy を読み込み,均一性指標 CSV と可視化 PNG を出力する."
),
)
parser.add_argument(
"--image",
"-i",
required=True,
type=Path,
metavar="IMAGE_PATH",
help="評価対象の画像ファイル(PNG または .npy).",
)
parser.add_argument(
"--config",
"-c",
required=True,
type=Path,
metavar="YAML_PATH",
help="評価設定 YAML ファイルのパス(SceneConfig または EvaluationConfig 互換).",
)
parser.add_argument(
"--output",
"-o",
required=True,
type=Path,
metavar="OUTPUT_DIR",
help="出力先ディレクトリ(存在しない場合は親ごと作成する).",
)
parser.add_argument(
"--no-figures",
action="store_true",
help="figures/ の可視化 PNG を作らない(指標だけ欲しいスイープ用.描画分だけ速い).",
)
return parser.parse_args(argv)
def main(argv: list[str] | None = None) -> int:
"""sm-evaluate コマンドのメインエントリポイント.
引数をパースして `evaluate_image()` を呼び,段階別のエラーを終了コードに変換する.
評価そのものの処理フローは `evaluation/pipeline.py` の `evaluate_image()` を参照.
Args:
argv: 引数リスト.None のとき sys.argv[1:] を使用.
Returns:
終了コード(成功時 0,失敗時 1).
"""
# argparse 自体のエラー(必須引数欠落等)は ArgumentParser が直接 sys.exit するため
# ここでは ValueError / FileNotFoundError 等の実行時エラーのみ捕捉する
args = _parse_args(argv)
try:
evaluate_image(
args.image.resolve(),
args.config.resolve(),
args.output.resolve(),
save_figures=not args.no_figures,
)
except EvaluationStepError as exc:
print(f"エラー: {exc}", file=sys.stderr)
return 1
return 0