#!/usr/bin/env python3 # -*- coding: utf-8 -*- """把 benchmark_out/matrix/results.json 生成 HTML 报告。 每个组合一行:原图 + 3次生发图 + 耗时/显存。 图片用 base64 内嵌(自包含单文件,便于部署)。 """ import base64 import json import os from pathlib import Path OUT = Path("/home/ubuntu/hair/benchmark_out/matrix") RESULTS = OUT / "results.json" HTML = OUT / "report.html" RES_LABEL = {"orig": "原图", "640": "640", "896": "896(默认)"} MODEL_LABEL = { "4b-fp8": "4B fp8 (3.8G)", "9b-fp8": "9B fp8 (8.8G)", "9b-Q5": "9B Q5_K_M (6.6G)", "9b-Q4": "9B Q4_K_M (5.6G)", } MODEL_ORDER = ["4b-fp8", "9b-Q4", "9b-Q5", "9b-fp8"] def img_b64(path, max_w=240): """读图片转 base64 data URI。""" if not path or not os.path.isfile(path): return None with open(path, "rb") as f: b = base64.b64encode(f.read()).decode() return f"data:image/jpeg;base64,{b}" def thumb(data_uri, alt="", cls=""): if not data_uri: return f'
⚠ 失败
' return f'{alt}' def main(): data = json.load(open(RESULTS, encoding="utf-8")) # 预读原图 base64(3 张图) orig_b64 = {} for c in data: ip = c["img_path"] if c["img"] not in orig_b64: orig_b64[c["img"]] = img_b64(ip, 200) # 统计:每个模型的平均耗时、平均峰值显存 stats = {} for c in data: m = c["model"] stats.setdefault(m, {"times": [], "peaks": []}) for r in c["runs"]: if not r["error"]: stats[m]["times"].append(r["elapsed"]) stats[m]["peaks"].append(r["gpu_peak"]) rows_html = [] # 按模型顺序、分辨率顺序、图片顺序排列 for m in MODEL_ORDER: mdata = [c for c in data if c["model"] == m] for rlabel in ["orig", "640", "896"]: for ilabel in ["asdf", "qwer", "girl5"]: c = next((x for x in mdata if x["res"] == rlabel and x["img"] == ilabel), None) if not c: continue # 3 次结果图 run_cells = [] for i, r in enumerate(c["runs"]): uri = img_b64(r["grown_path"]) if not r["error"] else None if uri: run_cells.append( f'
第{i+1}次 · {r["elapsed"]:.1f}s
' f'{thumb(uri, f"r{i+1}", "result-img")}
' ) else: run_cells.append( f'
第{i+1}次 · 失败
' f'
⚠ {r["error"][:30] if r["error"] else ""}
' ) orig = orig_b64.get(c["img"]) rows_html.append(f'''
{MODEL_LABEL.get(m, m)}
res={RES_LABEL.get(rlabel, rlabel)}
{thumb(orig, "原图", "orig-img")}
{ilabel}
{"".join(run_cells)}
''') # 模型对比汇总 summary_rows = [] for m in MODEL_ORDER: s = stats.get(m, {"times": [], "peaks": []}) if s["times"]: avg_t = sum(s["times"]) / len(s["times"]) max_p = max(s["peaks"]) / 1024 summary_rows.append( f"{MODEL_LABEL.get(m,m)}{avg_t:.1f}s" f"{max_p:.1f} GB{len(s['times'])} 成功" ) html = f""" Flux 模型矩阵测试报告 — 接口2女性花瓣形

💇 Flux 模型矩阵测试报告

接口2女性 · 花瓣形发型 · 4模型 × 3分辨率 × 3图 × 3次 = 108 次 · RTX 3090 24GB

📊 模型对比汇总

{"".join(summary_rows)}
模型平均耗时峰值显存成功次数

🖼️ 各组合对比(每行:原图 + 3次生发结果)

{"".join(rows_html)} """ with open(HTML, "w", encoding="utf-8") as f: f.write(html) print(f"✓ 报告已生成: {HTML} ({HTML.stat().st_size//1024} KB)") if __name__ == "__main__": main()