- 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)
149 lines
5.8 KiB
Python
149 lines
5.8 KiB
Python
#!/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()
|