Newer
Older
SmTIAS-LightSim / src / sm_tias_lightsim / evaluation / normalize.py
"""linear ドメイン正規化モジュール.

シミュの生 luminance 配列に対して,mean を指定値(target_mean)に揃える
線形ゲインを適用する.

線形ゲインは形状指標(CoV / 勾配 / C/P 比 / max-min 比)に厳密に不変であり,
絶対値スケールだけを合わせる操作である.これにより複数設計のシミュ結果を
linear ドメインで公平比較できる.

OETF 順方向適用(oetf.py の露出アンカー)と同じ思想を linear モードに
対称に展開したものであり,OETF との併用は禁止する(EvaluationConfig の
model_validator で検証する).
"""

from __future__ import annotations

import numpy


def apply_mean_anchor(
    values: numpy.ndarray,
    target_mean: float,
) -> tuple[numpy.ndarray, float]:
    """入力配列の mean を target_mean に揃える線形ゲインを適用する.

    $\\alpha = \\text{target\\_mean} / \\text{values.mean()}$
    として,``values * alpha`` を返す.

    CoV = std / mean,max-min 比,勾配,C/P 比などの形状指標はスカラー倍で
    不変なので,絶対値スケールだけが揃う(線形ゲインの性質).

    Args:
        values: 入力 ROI 配列(任意形状・任意次元,float64 推奨).
        target_mean: 目標 mean 値(uint8 スケール [0, 255] 想定だが任意 float 可).

    Returns:
        (normalized, alpha) のタプル.
        normalized: ``values * alpha`` の配列(dtype は values と同じ).
        alpha: 適用した線形ゲイン(target_mean / values.mean()).

    Raises:
        ValueError: values.mean() が 0 の場合(ゼロ除算を防ぐ).
    """
    src_mean = float(numpy.mean(values))
    if src_mean == 0.0:
        msg = "values の mean が 0 のため,mean anchor 正規化を適用できません."
        raise ValueError(msg)
    alpha = target_mean / src_mean
    return values * alpha, alpha