批量测试:仅非高清(20脸×5发型=100张)+ 报告原图大图并排对比
- batch_grow_v2.py: HR_OPTIONS 仅保留非高清档,去掉高清 - gen_report_hairline_v2.py: 每张脸 grid 改为「原图大图 + 5发型」6列并排, 原图不再是缩略图;去掉高清/非高清行标签和失败表高清列
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""批量调用接口12(/api/v1/hairline/grow_v2)生成对比素材。
|
||||
|
||||
20 张女生照片 × 5 种发际线发型 × {高清, 非高清} = 200 张输出。
|
||||
并发 4,失败的跳过并记录原因。结果图落盘到 static/report_hairline_v2/img/,
|
||||
20 张女生照片 × 5 种发际线发型(仅非高清)= 100 张输出。
|
||||
并发 2,失败的跳过并记录原因。结果图落盘到 static/report_hairline_v2/img/,
|
||||
元数据落盘 static/report_hairline_v2/results.json,供生成报告用。
|
||||
|
||||
用法: python scripts/batch_grow_v2.py
|
||||
@@ -31,7 +31,7 @@ HAIRSTYLES = [
|
||||
("chang_xinxing", "心形"),
|
||||
("chang_huaban", "花瓣"),
|
||||
]
|
||||
HR_OPTIONS = [(True, "hr"), (False, "nohr")]
|
||||
HR_OPTIONS = [(False, "nohr")] # 仅非高清
|
||||
|
||||
|
||||
def list_inputs():
|
||||
|
||||
@@ -78,12 +78,11 @@ def main():
|
||||
.face-head h2 {{ margin:0; font-size:18px; }}
|
||||
.face-head .orig-thumb {{ width:64px; height:64px; object-fit:cover;
|
||||
border-radius:8px; border:1px solid var(--border); }}
|
||||
.grid {{ display:grid; grid-template-columns:140px repeat({len(hairstyles)*2}, 1fr);
|
||||
gap:8px; align-items:start; }}
|
||||
.grid {{ display:grid; grid-template-columns:repeat({len(hairstyles)+1}, 1fr);
|
||||
gap:10px; align-items:start; }}
|
||||
.col-hdr {{ font-size:12px; color:var(--muted); text-align:center; padding:6px 4px;
|
||||
border-bottom:1px solid var(--border); }}
|
||||
.row-lbl {{ font-size:13px; color:var(--accent); padding:8px 4px;
|
||||
border-right:1px solid var(--border); }}
|
||||
.col-hdr.orig {{ color:var(--accent); }}
|
||||
.cell {{ position:relative; }}
|
||||
.cell img {{ width:100%; border-radius:6px; display:block;
|
||||
border:1px solid var(--border); }}
|
||||
@@ -117,7 +116,7 @@ def main():
|
||||
<body>
|
||||
<header>
|
||||
<h1>发际线生发对比报告 <span class="tag">接口12 · grow_v2</span></h1>
|
||||
<div class="sub">固定参数:blend_method=multiband · mb_levels=5 · erode_cm=0.6 · 生成后端=swaphair</div>
|
||||
<div class="sub">固定:pushed 遮罩 + multiband 融合 · mb_levels=5 · erode_cm=0.6 · 非高清</div>
|
||||
<div class="sub">生成时间:{html.escape(meta['generated_at'])} · 并发 {meta['concurrency']} · 耗时 {meta['elapsed_sec']}s</div>
|
||||
<div class="stats">
|
||||
<div class="stat"><div class="n">{meta['total']}</div><div class="l">总调用</div></div>
|
||||
@@ -131,45 +130,39 @@ def main():
|
||||
""")
|
||||
|
||||
# 图例
|
||||
parts.append('<div class="legend">每张脸横向为 5 种发际线发型,每种发型分 '
|
||||
'<span class="tag">高清 is_hr=true</span>'
|
||||
'<span class="tag no">非高清 is_hr=false</span> 两列。</div>')
|
||||
parts.append('<div class="legend">每张脸:第一列为原图,其余 5 列为发际线生发结果(非高清)。点击任意图片可放大。</div>')
|
||||
|
||||
# 每张脸一个区块
|
||||
# 每张脸一个区块:原图 + 5 发型横向并排
|
||||
for stem in faces:
|
||||
orig_rel = f"orig/{stem}.jpg"
|
||||
parts.append('<div class="face-block">')
|
||||
parts.append(f' <div class="face-head">'
|
||||
f'<img class="orig-thumb zoomable" src="{url(orig_rel)}" '
|
||||
f'data-full="{url(orig_rel)}" alt="原图 {html.escape(stem)}">'
|
||||
f'<h2>{html.escape(stem)}</h2>'
|
||||
f'</div>')
|
||||
# 网格表头
|
||||
parts.append(f' <div class="face-head"><h2>{html.escape(stem)}</h2></div>')
|
||||
# 网格:第一列原图,其余 5 列发型结果
|
||||
parts.append('<div class="grid">')
|
||||
parts.append('<div class="col-hdr"></div>')
|
||||
# 表头
|
||||
parts.append('<div class="col-hdr orig">原图</div>')
|
||||
for h in hairstyles:
|
||||
parts.append(f'<div class="col-hdr" style="grid-column:span 2;">{html.escape(h["cn"])}<br><span style="opacity:.6">{html.escape(h["id"])}</span></div>')
|
||||
# 行:原图占满左侧第一列的高度由第一行承载;每行 = 一个 hr 选项
|
||||
for hro in hr_opts:
|
||||
hr_tag = hro["tag"]
|
||||
is_hr = hro["is_hr"]
|
||||
tag_cls = "" if is_hr else " no"
|
||||
tag_txt = "高清" if is_hr else "非高清"
|
||||
parts.append(f'<div class="row-lbl">{tag_txt}<span class="tag{tag_cls}">{hr_tag}</span></div>')
|
||||
for h in hairstyles:
|
||||
r = idx.get((stem, h["id"], hr_tag))
|
||||
if r and r["ok"]:
|
||||
ms = r["ms"]
|
||||
cap = f"{html.escape(h['cn'])} · {tag_txt} · {ms}ms"
|
||||
parts.append(
|
||||
f'<div class="cell"><img class="zoomable" src="{url(r["out"])}" '
|
||||
f'data-full="{url(r["out"])}" loading="lazy" '
|
||||
f'alt="{html.escape(stem)} {html.escape(h["cn"])} {hr_tag}">'
|
||||
f'<div class="cap">{ms}ms</div></div>')
|
||||
else:
|
||||
err = (r or {}).get("error", "未执行")
|
||||
parts.append(
|
||||
f'<div class="cell fail"><div class="failbox">{html.escape(err)}</div></div>')
|
||||
parts.append(f'<div class="col-hdr">{html.escape(h["cn"])}<br><span style="opacity:.6">{html.escape(h["id"])}</span></div>')
|
||||
# 第一列:原图大图(可点击放大)
|
||||
parts.append(
|
||||
f'<div class="cell"><img class="zoomable" src="{url(orig_rel)}" '
|
||||
f'data-full="{url(orig_rel)}" loading="lazy" alt="原图 {html.escape(stem)}">'
|
||||
f'<div class="cap">原图</div></div>')
|
||||
# 5 发型结果(hr_opts 现在只有 nohr 一档,直接取)
|
||||
hr_tag = hr_opts[0]["tag"] if hr_opts else "nohr"
|
||||
for h in hairstyles:
|
||||
r = idx.get((stem, h["id"], hr_tag))
|
||||
if r and r["ok"]:
|
||||
ms = r["ms"]
|
||||
parts.append(
|
||||
f'<div class="cell"><img class="zoomable" src="{url(r["out"])}" '
|
||||
f'data-full="{url(r["out"])}" loading="lazy" '
|
||||
f'alt="{html.escape(stem)} {html.escape(h["cn"])}">'
|
||||
f'<div class="cap">{ms}ms</div></div>')
|
||||
else:
|
||||
err = (r or {}).get("error", "未执行")
|
||||
parts.append(
|
||||
f'<div class="cell fail"><div class="failbox">{html.escape(err)}</div></div>')
|
||||
parts.append('</div>') # grid
|
||||
parts.append('</div>') # face-block
|
||||
|
||||
@@ -179,21 +172,19 @@ def main():
|
||||
parts.append('<div class="face-block">')
|
||||
parts.append(f'<h2>失败用例({len(fails)})</h2>')
|
||||
parts.append('<table class="fail-table"><thead><tr>'
|
||||
'<th>人脸</th><th>发型</th><th>高清</th><th>耗时(ms)</th><th>原因</th>'
|
||||
'<th>人脸</th><th>发型</th><th>耗时(ms)</th><th>原因</th>'
|
||||
'</tr></thead><tbody>')
|
||||
for r in fails:
|
||||
parts.append(
|
||||
f'<tr><td>{html.escape(r["stem"])}</td>'
|
||||
f'<td>{html.escape(r["hair_cn"])}</td>'
|
||||
f'<td>{r["hr_tag"]}</td>'
|
||||
f'<td>{r["ms"]}</td>'
|
||||
f'<td>{html.escape(r["error"] or "")}</td></tr>')
|
||||
parts.append('</tbody></table></div>')
|
||||
|
||||
parts.append(f"""
|
||||
<footer>
|
||||
接口:<code>POST /api/v1/hairline/grow_v2</code> ·
|
||||
固定 multiband / mb_levels=5 / erode_cm=0.6 ·
|
||||
接口:<code>POST /api/v1/hairline/grow_v2</code> · 固定 pushed 遮罩 + multiband 融合 ·
|
||||
数据源 results.json · 点击任意图片可放大查看
|
||||
</footer>
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user