Newer
Older
SmTIAS-LightSim / src / sm_tias_lightsim / evaluation / roi.py
@rintoHasegawa rintoHasegawa on 1 Jun 2 KB [add] S2 DNG ローダと新指標を MiniTias-Evaluation からコピー — evaluation/dng_loader.py 新規 (handoff_smtias_lightsim/src/dng_loader.py の完全コピー+ docstring 差替: rawpy で 10-bit BGGR Bayer 展開・output_color=raw で linear カラーマトリクス非適用・gamma=(1,1)+no_auto_bright+use_camera_wb=False+user_wb=[1,1,1,1] で linear raw 保持・output_format rgb_float32/luma_float32/rgb_uint8 切替・Rec.709 luma は output_color=raw 時の固定加重プロキシで既存 PNG 経路と互換).evaluation/radial.py に calc_radial_min_max_ratio をマージ追加 (MiniTias-Evaluation の src/analysis/spatial.py から.動径 min/max 比 = 20 点輪帯平均の min/max で 1.0 で均一.画素単位 max/min よりノイズロバスト.nan ビン除外を移植時補強).pyproject.toml + uv.lock に rawpy>=0.21.0 追加 (実 rawpy==0.27.0).新規テスト 32 件 (tests/evaluation/test_dng_loader.py 341 行) で仕様 A1-D 全項目カバー.既存 537 + 新規 32 = 569 件全通過 (slow 除外).検証 V4 完全合格: 実 DNG (reference_measurement/quantitative/MiniTIAS_QM_20260601_124500.dng, 16MB, 暗闇撮影) を LSC 順方向適用 + portrait 向き合わせ (rot90 -1) + 白板 ROI で評価し,CoV/std/max-min/mean/max/min 6 件が相対誤差 < 1e-6 で MiniTias-Evaluation 期待値完全一致.中心/周辺比 1.0911 vs 1.091143・勾配 8.35 vs 8.352987・動径 min/max 0.8600 vs 0.859962 も絶対誤差 < 0.003.Refactorer 判断: _apply_srgb_oetf は既存 evaluation/oetf.py:apply_srgb_oetf と実装重複だがコピー方針優先で放置 (3 箇所以上の違反基準未満・用途異なる).副次変更: roi.py / test_oetf.py / test_visualize.py の軽微な lint 修正.handoff_smtias_lightsim/ ディレクトリは S3 完了後に削除予定で本コミットには含めない.CLAUDE.md 実装済みモジュール一覧に dng_loader.py と radial.py の calc_radial_min_max_ratio 追記+進捗欄に S2 完了 (V4 完全一致含む) を追記
"""ROI(関心領域)抽出モジュール.

SPEC_02 の evaluation.roi 仕様に基づき,輝度画像または RGB 画像から
矩形 ROI を切り出す.現在は image_pixel 型のみ対応.
"""

from __future__ import annotations

import numpy

from sm_tias_lightsim.scene.schema import RoiConfig


def extract_roi(image: numpy.ndarray, roi: RoiConfig) -> numpy.ndarray:
    """画像から ROI(関心領域)を矩形クロップで抽出する.

    image_pixel 型の ROI 設定に従い,`roi.x`, `roi.y`, `roi.width`, `roi.height` で
    矩形領域を切り出す.2D(輝度)・3D(RGB)どちらの配列も受け付ける.

    Args:
        image: 輝度画像(H x W, float64)または RGB 画像(H x W x 3).
            先頭 2 軸が H, W であること.
        roi: ROI 設定(RoiConfig).type は "image_pixel" のみ対応.

    Returns:
        ROI でクロップされた配列.入力と同じ次元数を持つ.

    Raises:
        NotImplementedError: roi.type が "image_pixel" 以外の場合.
        ValueError: ROI が画像の範囲外にはみ出す場合.
    """
    # scene_face 等の将来型は NotImplementedError で明示的に未対応を伝える(SPEC_02 参照)
    if roi.type != "image_pixel":
        msg = f"ROI type '{roi.type}' は未対応です.現在は 'image_pixel' のみサポートしています."
        raise NotImplementedError(msg)

    img_height, img_width = image.shape[:2]

    x_end = roi.x + roi.width
    y_end = roi.y + roi.height

    if roi.x < 0 or roi.y < 0:
        msg = f"ROI の開始座標 (x={roi.x}, y={roi.y}) が負の値です.ROI 設定を確認してください."
        raise ValueError(msg)

    if x_end > img_width or y_end > img_height:
        msg = (
            f"ROI (x={roi.x}, y={roi.y}, width={roi.width}, height={roi.height}) が "
            f"画像サイズ (width={img_width}, height={img_height}) を超えています."
            "ROI 設定を確認してください."
        )
        raise ValueError(msg)

    return image[roi.y : y_end, roi.x : x_end]