Files
hair/image/res_test/batch_test.py
xsl 5101bb5f6b feat(接口2): 测试页暴露重绘分辨率选项 + 分辨率对比测试套件
- test_interface2.html: 新增「重绘分辨率」下拉(默认896/1024/768/640/不缩图0),
  仅 female 生效,append redraw_max_side 字段;male 时自动隐藏
- 新增分辨率对比测试工具 (image/compare_test, image/res_test):
  - batch_test.py: 串行批量测试脚本,支持断点续跑
  - gen_report.py: 生成自包含 HTML 对比报告(速度色阶+缩略图+点击放大)
- 两轮测试结果:
  - v1: 4图×5发型×2档=40次 (compare_test)
  - v2: 5图×3发型×5档=75次 (res_test)
  - 结论: 大图(长边>1600)原图直送比896档慢~3倍; 中小图各档差异小
- .gitignore: 忽略测试结果图/原图副本/运行日志(out/, *.log, progress.json)
2026-07-26 22:06:27 +08:00

149 lines
5.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""接口2 female 分辨率对比测试 v2: 5图 × 3发型 × 5档 = 75次。
串行执行,记录耗时与成败,结果图按 图_发型_档位 命名保存。
支持断点续跑(progress.json)。
"""
import base64, csv, json, os, sys, time, io
import requests
from PIL import Image
API = "http://127.0.0.1:8187/api/v1/hair/grow"
TOKEN = "dev-shared-secret-2026"
TIMEOUT = 600
OUT = "/home/ubuntu/hair/image/res_test/out"
PROGRESS = "/home/ubuntu/hair/image/res_test/progress.json"
IMG_DIR = "/home/ubuntu/hair/image"
IMAGES = [
("girl2", f"{IMG_DIR}/girl_img/girl2.jpg"),
("girl5", f"{IMG_DIR}/girl_img/girl5.jpg"),
("qwer", f"{IMG_DIR}/qwer.jpg"),
("asdf", f"{IMG_DIR}/asdf.jpg"),
("girl7", f"{IMG_DIR}/girl_img/girl7.jpg"),
]
# female: 2=flower(花瓣) 3=heart(心形) 5=wave(波浪)
STYLES = [(2, "flower"), (3, "heart"), (5, "wave")]
# 5档: 原图(0) / 1024 / 896 / 768 / 640
SIDES = [
("origin", 0),
("s1024", 1024),
("s896", 896),
("s768", 768),
("s640", 640),
]
def load_progress():
if os.path.exists(PROGRESS):
try:
return json.load(open(PROGRESS))
except Exception:
pass
return {"done": [], "results": []}
def save_progress(prog):
json.dump(prog, open(PROGRESS, "w"), ensure_ascii=False, indent=1)
def run_one(img_name, img_path, style_idx, style_key, side_name, side_val):
key = f"{img_name}_{style_key}_{side_name}"
with open(img_path, "rb") as f:
img_b64 = base64.b64encode(f.read()).decode()
data = {
"image_base64": "data:image/jpeg;base64," + img_b64,
"gender": "female",
"hair_style": str(style_idx),
"redraw_max_side": str(side_val), # 含原图档=0
}
t0 = time.time()
rec = {"key": key, "img": img_name, "style": style_key, "style_idx": style_idx,
"side": side_name, "side_val": side_val, "ok": False,
"elapsed": 0.0, "err": "", "out_w": 0, "out_h": 0, "bytes": 0}
try:
r = requests.post(API, data=data, headers={"X-Internal-Token": TOKEN}, timeout=TIMEOUT)
rec["elapsed"] = round(time.time() - t0, 1)
d = r.json()
if d.get("code") != 0:
rec["err"] = f"code={d.get('code')} {d.get('message','')}"[:200]
return rec
results = (d.get("data") or {}).get("results") or []
if not results:
rec["err"] = "空结果"
return rec
it = results[0]
grown = it.get("grown_image_base64")
if not grown:
rec["err"] = "无生发图(重绘失败/OOM?)"
return rec
raw = base64.b64decode(grown)
im = Image.open(io.BytesIO(raw))
rec["out_w"], rec["out_h"] = im.size
out_path = f"{OUT}/{key}.jpg"
with open(out_path, "wb") as fo:
fo.write(raw)
rec["ok"] = True
rec["bytes"] = len(raw)
except requests.exceptions.Timeout:
rec["elapsed"] = round(time.time() - t0, 1)
rec["err"] = f"超时(>{TIMEOUT}s)"
except Exception as e:
rec["elapsed"] = round(time.time() - t0, 1)
rec["err"] = f"{type(e).__name__}: {str(e)[:180]}"
return rec
def main():
prog = load_progress()
done_keys = set(prog["done"])
total = len(IMAGES) * len(STYLES) * len(SIDES)
print(f"=== 接口2 female 分辨率对比测试 v2 ===")
print(f"矩阵: {len(IMAGES)}× {len(STYLES)}发型 × {len(SIDES)}档 = {total} 次")
print(f"已跳过 {len(done_keys)} 个已完成项\n")
idx = 0
for img_name, img_path in IMAGES:
for style_idx, style_key in STYLES:
for side_name, side_val in SIDES:
idx += 1
key = f"{img_name}_{style_key}_{side_name}"
if key in done_keys:
print(f"[{idx}/{total}] ⏭ 跳过 {key}")
continue
print(f"[{idx}/{total}] ▶ {key} (side={side_val})...", end=" ", flush=True)
rec = run_one(img_name, img_path, style_idx, style_key, side_name, side_val)
prog["results"].append(rec)
prog["done"].append(key)
save_progress(prog)
if rec["ok"]:
print(f"✅ {rec['elapsed']}s {rec['out_w']}x{rec['out_h']} ({rec['bytes']}B)")
else:
print(f"❌ {rec['elapsed']}s {rec['err']}")
time.sleep(2)
print("\n" + "=" * 70)
print("汇总")
print("=" * 70)
write_report(prog["results"])
print(f"\n结果图: {OUT}/")
print(f"CSV: {OUT}/report.csv JSON: {OUT}/report.json")
def write_report(results):
with open(f"{OUT}/report.csv", "w", newline="") as f:
w = csv.writer(f)
w.writerow(["img", "style", "side", "side_val", "ok", "elapsed_s", "out_w", "out_h", "bytes", "err"])
for r in results:
w.writerow([r["img"], r["style"], r["side"], r["side_val"], r["ok"],
r["elapsed"], r["out_w"], r["out_h"], r["bytes"], r["err"]])
json.dump(results, open(f"{OUT}/report.json", "w"), ensure_ascii=False, indent=1)
# 控制台速度表:按 图×发型 分行,5档列
print("\n--- 速度对比(秒)---")
print(f"{'图·发型':<18}{'原图0':>8}{'1024':>8}{'896':>8}{'768':>8}{'640':>8}")
for img_name, _ in IMAGES:
for _, style_key in STYLES:
cells = []
for side_name, _ in SIDES:
r = next((x for x in results if x["img"] == img_name and x["style"] == style_key and x["side"] == side_name), None)
cells.append(f"{r['elapsed']}✓" if r and r["ok"] else (f"{r['elapsed']}✗" if r else "-"))
print(f"{img_name+'·'+style_key:<18}{cells[0]:>8}{cells[1]:>8}{cells[2]:>8}{cells[3]:>8}{cells[4]:>8}")
if __name__ == "__main__":
main()