"""CSV・PNG 出力モジュール."""
import csv
from pathlib import Path
import numpy as np
SUMMARY_FIELDNAMES = ["image_name", "mean", "std", "cov", "max_min_ratio", "max", "min"]
SPATIAL_FIELDNAMES = [
"image_name",
"center_mean",
"middle_mean",
"periphery_mean",
"center_periphery_ratio",
"gradient_magnitude",
"radial_min_max_ratio",
"radial_min_distance",
]
def _ensure_parent_dir(output_path: str) -> None:
"""出力ファイルの親ディレクトリを作成する.
Args:
output_path: 出力先ファイルパス.
"""
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
def ensure_output_dirs(base_path: str) -> None:
"""output/figures/, output/results/ ディレクトリを作成する.
Args:
base_path: 出力ルートディレクトリのパス.
"""
base = Path(base_path)
(base / "figures").mkdir(parents=True, exist_ok=True)
(base / "results").mkdir(parents=True, exist_ok=True)
print(f"出力ディレクトリを確認しました: {base}")
def export_results(results: dict, output_path: str) -> None:
"""均一性指標を CSV ファイルに出力する.
CSV の形式は 1 行目がヘッダー,2 行目が値.
Args:
results: 均一性指標の辞書(calc_uniformity の戻り値).
output_path: 出力先ファイルパス(CSV).
"""
_ensure_parent_dir(output_path)
with Path(output_path).open("w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=list(results.keys()))
writer.writeheader()
writer.writerow(results)
print(f"結果を保存しました: {output_path}")
def export_summary(all_results: list[dict], output_path: str) -> None:
"""バッチ解析の集約結果を CSV ファイルに出力する.
CSV の形式は 1 行目がヘッダー,2 行目以降が各画像の値.
Args:
all_results: 均一性指標の辞書リスト.各要素は
{"image_name": "xxx", "mean": ..., "std": ..., "cov": ...,
"max_min_ratio": ..., "max": ..., "min": ...} の形式.
output_path: 出力先ファイルパス(CSV).
"""
_ensure_parent_dir(output_path)
with Path(output_path).open("w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=SUMMARY_FIELDNAMES, extrasaction="ignore")
writer.writeheader()
writer.writerows(all_results)
print(f"まとめ CSV を保存しました: {output_path}")
def export_spatial_summary(all_results: list[dict], output_path: str) -> None:
"""空間解析の集約結果を CSV ファイルに出力する.
CSV の形式は 1 行目がヘッダー,2 行目以降が各画像の値.
Args:
all_results: 空間解析結果を含む辞書リスト.各要素は
{"image_name": "xxx", "center_mean": ..., "middle_mean": ...,
"periphery_mean": ..., "center_periphery_ratio": ...,
"gradient_magnitude": ...} を含む形式.
output_path: 出力先ファイルパス(CSV).
"""
_ensure_parent_dir(output_path)
with Path(output_path).open("w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=SPATIAL_FIELDNAMES, extrasaction="ignore")
writer.writeheader()
writer.writerows(all_results)
print(f"空間解析まとめ CSV を保存しました: {output_path}")
def calc_batch_statistics(all_results: list[dict]) -> dict:
"""バッチ解析結果から画像間の集計統計を算出する.
各指標について mean, std, min, max, cv(画像間変動係数)を計算する.
Args:
all_results: 均一性指標と空間解析結果を含む辞書リスト.
Returns:
{"uniformity": {...}, "spatial": {...}} の形式.
各値は {指標名: {"mean": ..., "std": ..., "min": ..., "max": ..., "cv": ...}}
の辞書.
"""
uniformity_metrics = ["mean", "std", "cov", "max_min_ratio"]
spatial_metrics = [
"center_mean",
"middle_mean",
"periphery_mean",
"center_periphery_ratio",
"gradient_magnitude",
"radial_min_max_ratio",
"radial_min_distance",
]
def _calc_metric_stats(metric_name: str) -> dict:
values = np.array([r[metric_name] for r in all_results if metric_name in r])
m = float(np.mean(values))
s = float(np.std(values, ddof=0))
cv = s / m if m != 0 else 0.0
return {
"mean": m,
"std": s,
"min": float(np.min(values)),
"max": float(np.max(values)),
"cv": cv,
}
uniformity_stats = {
metric: _calc_metric_stats(metric) for metric in uniformity_metrics
}
spatial_stats = {
metric: _calc_metric_stats(metric) for metric in spatial_metrics
}
return {"uniformity": uniformity_stats, "spatial": spatial_stats}
def export_ssim_summary(
ssim_results: dict, image_names: list[str], output_path: str
) -> None:
"""SSIM のペアワイズ結果をサマリーとして CSV に出力する.
位置合わせ付き SSIM(calc_pairwise_ssim_registered)の戻り値にも対応する.
位置合わせ情報が含まれない場合でも後方互換を保ちエラーにならない.
出力 CSV の形式:
- 1 行目: 集計統計(mean_ssim, min_ssim, max_ssim, std_ssim,
mean_ssim_registered, min_ssim_registered, max_ssim_registered,
std_ssim_registered, mean_shift_magnitude, max_shift_magnitude, n_pairs)
- 空行
- ペアワイズスコア(image_a, image_b, ssim,
ssim_registered, dx, dy, shift_magnitude)
Args:
ssim_results: calc_pairwise_ssim または calc_pairwise_ssim_registered の戻り値.
image_names: 画像名のリスト(インデックスと対応).
output_path: 出力先ファイルパス(CSV).
"""
_ensure_parent_dir(output_path)
with Path(output_path).open("w", newline="", encoding="utf-8") as f:
# 1 行目: 集計統計(後方互換のため dict.get でキーなし時は空文字)
summary_fieldnames = [
"mean_ssim",
"min_ssim",
"max_ssim",
"std_ssim",
"mean_ssim_registered",
"min_ssim_registered",
"max_ssim_registered",
"std_ssim_registered",
"mean_shift_magnitude",
"max_shift_magnitude",
"n_pairs",
]
summary_writer = csv.DictWriter(f, fieldnames=summary_fieldnames)
summary_writer.writeheader()
summary_writer.writerow(
{
"mean_ssim": ssim_results["mean_ssim"],
"min_ssim": ssim_results["min_ssim"],
"max_ssim": ssim_results["max_ssim"],
"std_ssim": ssim_results["std_ssim"],
"mean_ssim_registered": ssim_results.get("mean_ssim_registered", ""),
"min_ssim_registered": ssim_results.get("min_ssim_registered", ""),
"max_ssim_registered": ssim_results.get("max_ssim_registered", ""),
"std_ssim_registered": ssim_results.get("std_ssim_registered", ""),
"mean_shift_magnitude": ssim_results.get("mean_shift_magnitude", ""),
"max_shift_magnitude": ssim_results.get("max_shift_magnitude", ""),
"n_pairs": ssim_results["n_pairs"],
}
)
# 空行
f.write("\n")
# ペアワイズスコア(後方互換のため dict.get でキーなし時は空文字)
pair_fieldnames = [
"image_a",
"image_b",
"ssim",
"ssim_registered",
"dx",
"dy",
"shift_magnitude",
]
pair_writer = csv.DictWriter(f, fieldnames=pair_fieldnames)
pair_writer.writeheader()
for pair in ssim_results["pairwise_scores"]:
idx_a = pair["image_a"]
idx_b = pair["image_b"]
name_a = image_names[idx_a] if idx_a < len(image_names) else str(idx_a)
name_b = image_names[idx_b] if idx_b < len(image_names) else str(idx_b)
pair_writer.writerow(
{
"image_a": name_a,
"image_b": name_b,
"ssim": pair["ssim"],
"ssim_registered": pair.get("ssim_registered", ""),
"dx": pair.get("dx", ""),
"dy": pair.get("dy", ""),
"shift_magnitude": pair.get("shift_magnitude", ""),
}
)
print(f"SSIM サマリー CSV を保存しました: {output_path}")
def export_batch_statistics(stats: dict, output_path: str) -> None:
"""集計統計を CSV ファイルに出力する.
行: 各指標名,列: statistic, mean, std, min, max, cv
Args:
stats: calc_batch_statistics の戻り値.
output_path: 出力先ファイルパス(CSV).
"""
_ensure_parent_dir(output_path)
fieldnames = ["statistic", "mean", "std", "min", "max", "cv"]
rows = []
for category in ("uniformity", "spatial"):
for metric_name, values in stats[category].items():
rows.append({
"statistic": metric_name,
"mean": values["mean"],
"std": values["std"],
"min": values["min"],
"max": values["max"],
"cv": values["cv"],
})
with Path(output_path).open("w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(rows)
print(f"集計統計 CSV を保存しました: {output_path}")