"""spp スイープ追試(--set shapes6)の計算時間表と 6 形状のパネル図を作る(TEST_19 追試).
入力:
output/spp_noise_shapes6/summary.csv sweep_spp_noise.py --set shapes6 の集約
output/spp_noise_shapes6/<条件>_spp8192_s0/render.png
output/spp_noise_shapes6/geometry/<形状>.png sm-visualize --no-bake --style slide の撮影
出力:
spp_time_table_mean_sd.csv spp ごとの平均±標準偏差(n = 6 形状 × seed 2)
shapes6_geometry_panel.png 6 形状の 3D 配置図(2×3)
shapes6_render_panel.png 6 形状のレンダ結果(spp=8192,2×3)
実行方法:
uv run python scripts/build_spp_shapes6_summary.py
"""
from __future__ import annotations
from pathlib import Path
import matplotlib.pyplot as plt
import pandas as pd
ROOT = Path(__file__).resolve().parents[1]
BASE = ROOT / "output" / "spp_noise_shapes6"
# (条件名, 形状名, 表示ラベル).上段が球,下段が同じ内面積の立方体
SHAPES = [
("sphere_d270_th000", "sphere_d270", "球 φ270"),
("sphere_d340_th000", "sphere_d340", "球 φ340"),
("sphere_d500_th000", "sphere_d500", "球 φ500"),
("cube_L187_th000", "cube_L187", "立方体 L=187"),
("cube_L234_th000", "cube_L234", "立方体 L=234"),
("cube_L340_th000", "cube_L340", "立方体 L=340"),
]
def write_table() -> pd.DataFrame:
"""spp ごとの計算時間(平均±標準偏差)を CSV に書く."""
d = pd.read_csv(BASE / "summary.csv")
d["t_fixed"] = d.t_total - d.t_render
rows = []
for spp, x in d.groupby("spp"):
rows.append({
"spp": spp,
"n": len(x),
"合計時間[s]": f"{x.t_total.mean():.1f}±{x.t_total.std():.1f}",
"GPU処理[s]": f"{x.t_render.mean():.1f}±{x.t_render.std():.1f}",
"CPU処理[s]": f"{x.t_fixed.mean():.1f}±{x.t_fixed.std():.1f}",
"GPU処理の割合[%]": f"{100 * x.t_render.mean() / x.t_total.mean():.0f}",
"GPU SD/平均[%]": f"{100 * x.t_render.std() / x.t_render.mean():.0f}",
})
table = pd.DataFrame(rows)
table.to_csv(BASE / "spp_time_table_mean_sd.csv", index=False, encoding="utf-8-sig")
print(table.to_string(index=False))
print()
print(d.groupby(["condition", "spp"]).t_render.mean().unstack().round(2))
return table
def draw_panel(images: list[Path], out_path: Path, crop: bool) -> None:
"""6 枚の画像を 2×3 に並べてラベルを付ける."""
fig, axes = plt.subplots(2, 3, figsize=(10, 7))
for ax, image, (_, _, label) in zip(axes.ravel(), images, SHAPES, strict=True):
img = plt.imread(image)
if crop:
# sm-visualize のスクリーンショットは左右の余白が広いので切り詰める
h, w = img.shape[:2]
img = img[int(h * 0.05):int(h * 0.97), int(w * 0.15):int(w * 0.85)]
ax.imshow(img)
ax.set_title(label, fontsize=16)
ax.axis("off")
fig.tight_layout()
fig.savefig(out_path, dpi=200)
plt.close(fig)
print("saved", out_path)
def main() -> None:
"""表と 2 枚のパネル図を作る."""
plt.rcParams["font.family"] = ["Yu Gothic", "Meiryo", "sans-serif"]
write_table()
draw_panel([BASE / f"{cond}_spp8192_s0" / "render.png" for cond, _, _ in SHAPES],
BASE / "shapes6_render_panel.png", crop=False)
draw_panel([BASE / "geometry" / f"{shape}.png" for _, shape, _ in SHAPES],
BASE / "shapes6_geometry_panel.png", crop=True)
if __name__ == "__main__":
main()