"""輝度マップ・ヒストグラム・空間分析可視化モジュール."""
import math
from pathlib import Path
import matplotlib.pyplot as plt
import numpy
def _ensure_parent(output_path: str) -> None:
"""出力ファイルの親ディレクトリを作成する.
Args:
output_path: 出力先ファイルパス.
"""
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
def plot_luminance_map(luminance: numpy.ndarray, output_path: str) -> None:
"""輝度画像をヒートマップとして保存する.
Args:
luminance: 輝度画像の NumPy 配列(H x W, float64).
output_path: 出力先ファイルパス(PNG).
"""
_ensure_parent(output_path)
fig, ax = plt.subplots(figsize=(8, 6))
im = ax.imshow(luminance, cmap="hot", interpolation="nearest")
fig.colorbar(im, ax=ax, label="Luminance")
ax.set_title("Luminance Map")
ax.set_xlabel("X (px)")
ax.set_ylabel("Y (px)")
fig.tight_layout()
fig.savefig(output_path, dpi=150)
plt.close(fig)
print(f"輝度マップを保存しました: {output_path}")
def plot_histogram(luminance: numpy.ndarray, output_path: str) -> None:
"""輝度ヒストグラムを生成して保存する.
Args:
luminance: 輝度画像の NumPy 配列(H x W, float64).
output_path: 出力先ファイルパス(PNG).
"""
_ensure_parent(output_path)
fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(
luminance.ravel(), bins=256, range=(0, 255), color="steelblue", edgecolor="none"
)
ax.set_title("Luminance Histogram")
ax.set_xlabel("Luminance")
ax.set_ylabel("Pixel Count")
fig.tight_layout()
fig.savefig(output_path, dpi=150)
plt.close(fig)
print(f"輝度ヒストグラムを保存しました: {output_path}")
def plot_radial_profile(radial_profile: numpy.ndarray, output_path: str) -> None:
"""放射状輝度プロファイルを折れ線グラフとして保存する.
最小輝度点(赤マーカー)と最大輝度点(緑マーカー)を強調表示し,
動径 min/max 比をグラフ右上にアノテーションとして表示する.
Args:
radial_profile: 放射状プロファイルの NumPy 配列(N x 2, float64).
列 0 = 正規化距離, 列 1 = 平均輝度.
output_path: 出力先ファイルパス(PNG).
"""
_ensure_parent(output_path)
distances = radial_profile[:, 0]
luminances = radial_profile[:, 1]
# min/max 点のインデックスを特定する
min_idx = int(numpy.argmin(luminances))
max_idx = int(numpy.argmax(luminances))
lmin = float(luminances[min_idx])
lmax = float(luminances[max_idx])
ratio = lmin / lmax if lmax != 0.0 else float("inf")
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(distances, luminances, marker="o", color="steelblue", label="Mean Luminance")
# 最大点(緑)・最小点(赤)を強調マーカーで重ねる
ax.scatter(
distances[max_idx], lmax,
color="green", s=80, zorder=5, label=f"Max: {lmax:.2f}",
)
ax.scatter(
distances[min_idx], lmin,
color="red", s=80, zorder=5, label=f"Min: {lmin:.2f}",
)
# 動径 min/max 比をグラフ右上にテキスト注記する
ratio_text = "min/max ratio: inf" if math.isinf(ratio) else f"min/max ratio: {ratio:.4f}"
ax.text(
0.98, 0.97, ratio_text,
transform=ax.transAxes,
ha="right", va="top",
fontsize=10,
bbox={"boxstyle": "round,pad=0.3", "facecolor": "white", "alpha": 0.8},
)
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)
ax.legend(loc="lower left")
fig.tight_layout()
fig.savefig(output_path, dpi=150)
plt.close(fig)
print(f"放射状プロファイルを保存しました: {output_path}")
def plot_zone_map(
luminance: numpy.ndarray,
zone_map: numpy.ndarray,
output_path: str,
) -> None:
"""輝度マップにゾーン境界をオーバーレイして保存する.
"hot" カラーマップの輝度ヒートマップ上に、3 ゾーンの境界を
contour で描画する.
Args:
luminance: 輝度画像の NumPy 配列(H x W, float64).
zone_map: ゾーンマップの NumPy 配列(H x W, uint8).
0=center, 1=middle, 2=periphery.
output_path: 出力先ファイルパス(PNG).
"""
_ensure_parent(output_path)
fig, ax = plt.subplots(figsize=(8, 6))
# 輝度ヒートマップ
im = ax.imshow(luminance, cmap="hot", interpolation="nearest")
fig.colorbar(im, ax=ax, label="Luminance")
# ゾーン境界をコンター線で描画(境界値は 0.5, 1.5 でゾーン間を区切る)
ax.contour(zone_map, levels=[0.5, 1.5], colors=["cyan", "lime"], linewidths=1.5)
ax.set_title("Zone Map (center / middle / periphery)")
ax.set_xlabel("X (px)")
ax.set_ylabel("Y (px)")
fig.tight_layout()
fig.savefig(output_path, dpi=150)
plt.close(fig)
print(f"ゾーンマップを保存しました: {output_path}")