SplatAtlas / tools /build_fig1_separate.py
KCBtheone's picture
Upload SplatAtlas benchmark pipeline code
23e73f9 verified
Raw
History Blame Contribute Delete
7.75 kB
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw, ImageFont
import os
import glob
import json
import numpy as np
from pdf2image import convert_from_path
# =========================================================================
# 配置项
# =========================================================================
SCENE = "bonsai"
VIEW = "00000.png"
OUTPUTS_BASE = "/root/autodl-tmp/SplatAtlas/outputs"
METHODS = ["vanilla_3dgs", "pgsr", "ges", "opti3dgs", "steepgs"]
LEFT_PDF_PATH = "/root/autodl-tmp/SplatAtlas/outputs/phase5b/fig1_left_panel.pdf"
OUTPUT_DIR = "/root/autodl-tmp/SplatAtlas/tex/figures"
# =========================================================================
# 辅助函数 (构建右图 6宫格 用)
# =========================================================================
def get_valid_dir(method, scene):
for suffix in ["", "_bak"]:
d = f"{OUTPUTS_BASE}/{method}_{scene}{suffix}"
if os.path.exists(d) and len(glob.glob(f"{d}/renders_test*")) > 0:
return d
return None
def get_metrics(cell_dir):
try:
metric_files = sorted(glob.glob(f"{cell_dir}/metrics_test_iter*.json"))
if not metric_files: return None
with open(metric_files[-1], 'r') as f:
data = json.load(f)
geo = data.get("geometric", {})
return {
"opacity": geo.get("alpha_mean", 0.0),
"aniso": geo.get("gamma_median", 0.0),
"psnr": data.get("photometric", {}).get("PSNR", 0.0)
}
except Exception:
return None
def add_label(img, title, psnr, opacity=None, aniso=None, is_gt=False):
"""在图片上绘制带有半透明黑底的白字标签"""
draw = ImageDraw.Draw(img, 'RGBA')
w, h = img.size
title_text = title.upper()
font_size = max(16, int(h * 0.04)) # 字号稍微调大一点点
try:
font = ImageFont.truetype("DejaVuSans-Bold.ttf", font_size)
except:
font = ImageFont.load_default()
padding = 5
title_bbox = draw.textbbox((0, 0), title_text, font=font)
tw, th = title_bbox[2] - title_bbox[0], title_bbox[3] - title_bbox[1]
title_x = (w - tw) // 2
title_y = 10
draw.rectangle([title_x - padding, title_y - padding,
title_x + tw + padding, title_y + th + padding],
fill=(0, 0, 0, 180))
title_color = (255, 255, 255) if is_gt else (100, 255, 100)
draw.text((title_x, title_y), title_text, font=font, fill=title_color)
if psnr:
psnr_text = f"PSNR: {psnr:.2f} dB" if not is_gt else "Reference"
p_bbox = draw.textbbox((0, 0), psnr_text, font=font)
pw, ph = p_bbox[2] - p_bbox[0], p_bbox[3] - p_bbox[1]
px = w - pw - 10
py = h - ph - 45 # 留出底栏空间
draw.rectangle([px - padding, py - padding,
px + pw + padding, py + ph + padding],
fill=(0, 0, 0, 180))
draw.text((px, py), psnr_text, font=font, fill=(255,255,255))
if opacity is not None and aniso is not None:
bar_h = 35
draw.rectangle([0, h - bar_h, w, h], fill=(30, 30, 30, 255))
stat_text = f"opacity μ: {opacity:.3f} | anisotropy μ: {aniso:.3f}" if not is_gt else "opacity μ: 1.000 | anisotropy μ: 1.000"
s_bbox = draw.textbbox((0, 0), stat_text, font=font)
sw, sh = s_bbox[2] - s_bbox[0], s_bbox[3] - s_bbox[1]
draw.text(((w-sw)//2, h - bar_h + (bar_h-sh)//2), stat_text, font=font, fill=(200,200,200))
return img
os.makedirs(OUTPUT_DIR, exist_ok=True)
# =========================================================================
# 处理 Fig 1a: 左图 (Rank Instability) 单独输出
# =========================================================================
print("正在处理 Fig 1a (左图)...")
if not os.path.exists(LEFT_PDF_PATH):
print(f"❌ 找不到左图 (PDF): {LEFT_PDF_PATH}")
else:
try:
pages = convert_from_path(LEFT_PDF_PATH, dpi=300)
img_left = pages[0]
# 为左图单独创建一个更宽的画板
fig_a, ax_a = plt.subplots(figsize=(12, 6))
ax_a.imshow(img_left)
ax_a.axis('off')
ax_a.set_title("(a) Per-seed PSNR Rank Instability (BONSAI & LEGO)",
fontsize=20, pad=15, loc='center', fontweight='bold')
out_a_pdf = os.path.join(OUTPUT_DIR, "fig1a_rank_instability.pdf")
out_a_png = os.path.join(OUTPUT_DIR, "fig1a_rank_instability.png")
fig_a.savefig(out_a_pdf, dpi=300, bbox_inches='tight', format='pdf', transparent=True)
fig_a.savefig(out_a_png, dpi=300, bbox_inches='tight', format='png', transparent=False)
print(f"✅ Fig 1a 保存成功: {out_a_pdf}")
plt.close(fig_a)
except Exception as e:
print(f"❌ 左图处理失败: {e}")
# =========================================================================
# 处理 Fig 1b: 右图 (6宫格 Render vs Rep) 单独输出
# =========================================================================
print("\n正在处理 Fig 1b (右图 6宫格)...")
images = []
labels = ["GT"] + METHODS
# 1. 提取 GT
gt_dir = get_valid_dir("vanilla_3dgs", SCENE)
if gt_dir:
gt_path = os.path.join(gt_dir, "gt_test_30000", VIEW)
if not os.path.exists(gt_path):
gt_path = os.path.join(gt_dir, "gt_test", VIEW)
if os.path.exists(gt_path):
img_gt = Image.open(gt_path).convert("RGBA")
img_gt = add_label(img_gt, "GT", None, opacity=1.0, aniso=1.0, is_gt=True)
images.append(img_gt)
else:
print(f"❌ 找不到 GT 图像: {gt_path}"); exit(1)
else:
print("❌ 找不到 vanilla_3dgs 目录以提取 GT"); exit(1)
# 2. 提取 Methods
for method in METHODS:
cell_dir = get_valid_dir(method, SCENE)
if not cell_dir:
images.append(Image.new("RGBA", images[0].size, (50,50,50,255)))
continue
render_dirs = sorted(glob.glob(f"{cell_dir}/renders_test*"))
if not render_dirs:
images.append(Image.new("RGBA", images[0].size, (50,50,50,255)))
continue
img_path = os.path.join(render_dirs[-1], VIEW)
if not os.path.exists(img_path):
images.append(Image.new("RGBA", images[0].size, (50,50,50,255)))
continue
img_m = Image.open(img_path).convert("RGBA")
metrics = get_metrics(cell_dir)
if metrics:
img_m = add_label(img_m, method, metrics['psnr'], metrics['opacity'], metrics['aniso'])
else:
img_m = add_label(img_m, method, 0.0)
images.append(img_m)
# 3. 拼接 6 宫格
iw, ih = images[0].size
padding = 15
grid_w = iw * 3 + padding * 2
grid_h = ih * 2 + padding
img_right = Image.new("RGBA", (grid_w, grid_h), (255,255,255,0)) # 使用透明底色
positions = [
(0, 0), (iw + padding, 0), ((iw + padding) * 2, 0),
(0, ih + padding), (iw + padding, ih + padding), ((iw + padding) * 2, ih + padding)
]
for img, pos in zip(images, positions):
img_right.paste(img, pos)
# 4. 单独制图保存
fig_b, ax_b = plt.subplots(figsize=(14, 8)) # 适合 3x2 的宽幅比例
ax_b.imshow(img_right)
ax_b.axis('off')
ax_b.set_title(f"(b) Saturated Cluster ({SCENE.upper()}): Render vs. Representation",
fontsize=20, pad=15, loc='center', fontweight='bold')
out_b_pdf = os.path.join(OUTPUT_DIR, "fig1b_saturation_renders.pdf")
out_b_png = os.path.join(OUTPUT_DIR, "fig1b_saturation_renders.png")
fig_b.savefig(out_b_pdf, dpi=300, bbox_inches='tight', format='pdf', transparent=True)
fig_b.savefig(out_b_png, dpi=300, bbox_inches='tight', format='png', transparent=False)
print(f"✅ Fig 1b 保存成功: {out_b_pdf}")
plt.close(fig_b)
print("\n=== 图片分离处理完成!===")