feat(接口2): ComfyUI重绘分辨率默认改为1024 + wave测试套件 + 报告统一部署
- hairline/service.py: _REDRAW_MAX_SIDE 默认 896→1024 逻辑: 输入图长边>1024 才缩到1024; ≤1024 原图分辨率直送(不放大) male路径_GROW_B_MAX_SIDE 已是1024,行为一致 - static/test_interface2.html: 分辨率下拉选项标签同步(默认1024/>1024才缩) - image/wave_test/: wave发型5档分辨率对比测试(21图×5档=105次,全成功) batch_test.py/gen_report.py/report-server-wave.service - image/reports/: 报告统一HTTP服务(单端口8850,路径区分/wave /v2 /v1) 含索引页index.html + report-server.service + 三报告软链接 - .gitignore: 补充 wave_test/out/ 及运行期文件忽略规则
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env python3
|
||||
"""生成 v3 wave发型 分辨率对比报告:每行=原图+5档,21行;含各档耗时统计。"""
|
||||
import json, os, html
|
||||
|
||||
OUT = "/home/ubuntu/hair/image/wave_test/out"
|
||||
REPORT = os.path.join(OUT, "report.html")
|
||||
results = json.load(open(os.path.join(OUT, "report.json")))
|
||||
|
||||
SIDES = [("origin", "原图直送", 0), ("s1024", "1024", 1024),
|
||||
("s896", "896", 896), ("s768", "768", 768), ("s640", "640", 640)]
|
||||
|
||||
# 图片顺序(与测试脚本一致):girl_img 排序 + asdf + qwer
|
||||
IMG_DIR = "/home/ubuntu/hair/image"
|
||||
IMAGES = []
|
||||
for f in sorted(os.listdir(os.path.join(IMG_DIR, "girl_img"))):
|
||||
if f.lower().endswith((".jpg", ".jpeg", ".png")):
|
||||
IMAGES.append(os.path.splitext(f)[0])
|
||||
IMAGES.append("asdf")
|
||||
IMAGES.append("qwer")
|
||||
|
||||
def get(img, side):
|
||||
for r in results:
|
||||
if r["img"] == img and r["side"] == side:
|
||||
return r
|
||||
return None
|
||||
|
||||
# 统计
|
||||
total = len(results)
|
||||
ok = sum(1 for r in results if r["ok"])
|
||||
|
||||
# 各档统计
|
||||
def avg(side):
|
||||
ts = [r["elapsed"] for r in results if r["side"] == side and r["ok"]]
|
||||
return sum(ts)/len(ts) if ts else 0
|
||||
avgs = {s[0]: avg(s[0]) for s in SIDES}
|
||||
|
||||
# 速度色阶
|
||||
all_t = sorted(r["elapsed"] for r in results if r["ok"])
|
||||
tmin, tmax = all_t[0], all_t[-1]
|
||||
def speed_color(t):
|
||||
if tmax == tmin: return "#16a34a"
|
||||
ratio = (t - tmin) / (tmax - tmin)
|
||||
if ratio < 0.33: return "#16a34a"
|
||||
elif ratio < 0.66: return "#f59e0b"
|
||||
else: return "#dc2626"
|
||||
|
||||
# 表格行
|
||||
rows_html = []
|
||||
for img in IMAGES:
|
||||
orig_cell = (f'<td class="cell orig">'
|
||||
f'<div class="thumb"><img loading="lazy" src="orig_{img}.jpg" '
|
||||
f'onclick="openImg(this.src)" alt="原图"></div>'
|
||||
f'<div class="meta">📷 原图</div></td>')
|
||||
side_cells = []
|
||||
for side_name, side_lbl, side_val in SIDES:
|
||||
r = get(img, side_name)
|
||||
if r and r["ok"]:
|
||||
col = speed_color(r["elapsed"])
|
||||
cell = (f'<td class="cell">'
|
||||
f'<div class="thumb"><img loading="lazy" src="{r["key"]}.jpg" '
|
||||
f'onclick="openImg(this.src)" alt="{html.escape(r["key"])}"></div>'
|
||||
f'<div class="meta"><b style="color:{col}">{r["elapsed"]}s</b></div></td>')
|
||||
elif r:
|
||||
cell = (f'<td class="cell fail"><div class="thumb noimg">❌</div>'
|
||||
f'<div class="meta err">{html.escape(r["err"][:30])}</div></td>')
|
||||
else:
|
||||
cell = '<td class="cell fail"><div class="thumb noimg">—</div></td>'
|
||||
side_cells.append(cell)
|
||||
rows_html.append("<tr>" + orig_cell + "".join(side_cells) + "</tr>")
|
||||
|
||||
# 各档统计卡
|
||||
def stat_card(lbl, val, col, rng=""):
|
||||
return (f'<div class="stat" style="border-left:3px solid {col}">'
|
||||
f'<div class="num" style="color:{col}">{val:.1f}s</div>'
|
||||
f'<div class="lbl">{lbl}{rng}</div></div>')
|
||||
|
||||
stat_cards = "".join([
|
||||
stat_card("原图直送(0)", avgs["origin"], "#dc2626"),
|
||||
stat_card("1024", avgs["s1024"], "#f59e0b"),
|
||||
stat_card("896", avgs["s896"], "#2563eb"),
|
||||
stat_card("768", avgs["s768"], "#16a34a"),
|
||||
stat_card("640", avgs["s640"], "#0d9488"),
|
||||
])
|
||||
|
||||
html_doc = f"""<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>wave发型 5档分辨率对比</title>
|
||||
<style>
|
||||
* {{ box-sizing: border-box; margin: 0; padding: 0; }}
|
||||
body {{ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Microsoft YaHei", sans-serif; background: #f3f4f6; color: #1f2937; line-height: 1.5; padding: 16px; }}
|
||||
.wrap {{ max-width: 100%; margin: 0 auto; }}
|
||||
h1 {{ font-size: 22px; margin-bottom: 4px; }}
|
||||
.sub {{ color: #6b7280; font-size: 12px; margin-bottom: 16px; }}
|
||||
.stats {{ display: flex; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; }}
|
||||
.stat {{ background: #fff; border-radius: 8px; padding: 12px 14px; box-shadow: 0 1px 3px rgba(0,0,0,.06); flex: 1; min-width: 110px; }}
|
||||
.stat .num {{ font-size: 22px; font-weight: 700; }}
|
||||
.stat .lbl {{ font-size: 11px; color: #6b7280; margin-top: 2px; }}
|
||||
.summary-bar {{ background: #fff; border-radius: 8px; padding: 12px 16px; margin-bottom: 16px; box-shadow: 0 1px 3px rgba(0,0,0,.06); font-size: 13px; }}
|
||||
.summary-bar b {{ color: #dc2626; }}
|
||||
.legend {{ display: inline-flex; gap: 12px; font-size: 11px; color: #6b7280; margin-left: 12px; }}
|
||||
.legend span {{ display: inline-flex; align-items: center; gap: 4px; }}
|
||||
.legend i {{ width: 10px; height: 10px; border-radius: 2px; display: inline-block; }}
|
||||
.table-wrap {{ overflow-x: auto; background: #fff; border-radius: 10px; box-shadow: 0 1px 4px rgba(0,0,0,.07); }}
|
||||
table {{ border-collapse: collapse; min-width: 100%; }}
|
||||
th, td {{ vertical-align: top; }}
|
||||
thead th {{ position: sticky; top: 0; background: #f9fafb; z-index: 2; padding: 10px 8px; font-size: 12px; color: #374151; border-bottom: 2px solid #e5e7eb; text-align: center; }}
|
||||
thead th.orig-h {{ background: #fef3c7; }}
|
||||
tbody td {{ border-bottom: 1px solid #f3f4f6; padding: 8px; }}
|
||||
tbody tr:hover {{ background: #f9fafb; }}
|
||||
.cell {{ width: 200px; min-width: 200px; text-align: center; }}
|
||||
.cell.orig {{ background: #fffbeb; }}
|
||||
.thumb {{ background: #1f2937; border-radius: 6px; overflow: hidden; margin-bottom: 4px; cursor: zoom-in; }}
|
||||
.thumb img {{ width: 100%; height: 260px; object-fit: contain; display: block; }}
|
||||
.thumb.noimg {{ color: #d1d5db; font-size: 16px; padding: 110px 0; text-align: center; }}
|
||||
.meta {{ font-size: 11px; color: #6b7280; }}
|
||||
.meta.err {{ color: #dc2626; }}
|
||||
.overlay {{ display: none; position: fixed; inset: 0; background: rgba(0,0,0,.92); z-index: 999; justify-content: center; align-items: center; cursor: zoom-out; padding: 24px; }}
|
||||
.overlay.active {{ display: flex; }}
|
||||
.overlay img {{ max-width: 96%; max-height: 96%; object-fit: contain; border-radius: 4px; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<h1>💇 wave发型 · 5档分辨率对比报告</h1>
|
||||
<p class="sub">POST /api/v1/hair/grow · female · wave(波浪) · 21 图 × 5 档分辨率 = 105 次 · 串行 · 4090 (24G)</p>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat" style="border-left:3px solid #16a34a"><div class="num" style="color:#16a34a">{ok}/{total}</div><div class="lbl">成功 / 总数</div></div>
|
||||
{stat_cards}
|
||||
</div>
|
||||
|
||||
<div class="summary-bar">
|
||||
📊 <b>结论:</b>耗时随分辨率单调下降。<b>大图(asdf 1666/qwer 1678) 原图直送需 24~26s,是 1024 档(11s) 的 2.3 倍</b>;
|
||||
中小图(≤1024) 各档差异较小(6~13s),因小图本身不触发缩图。
|
||||
<b>4090 24G 全程无 OOM</b>,105/105 成功。<b>画质对比</b>见下表(横向滑动),点击任意图可放大。
|
||||
<span class="legend">
|
||||
<span><i style="background:#16a34a"></i>快(<{(tmin+(tmax-tmin)*0.33):.0f}s)</span>
|
||||
<span><i style="background:#f59e0b"></i>中等</span>
|
||||
<span><i style="background:#dc2626"></i>慢(>{(tmin+(tmax-tmin)*0.66):.0f}s)</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="orig-h">📷 原图</th>
|
||||
<th>原图直送 (0)<br><span class="dim">不缩放</span></th>
|
||||
<th>1024</th>
|
||||
<th>896<br><span class="dim">默认</span></th>
|
||||
<th>768</th>
|
||||
<th>640</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{"".join(rows_html)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay" id="overlay" onclick="this.classList.remove('active')"><img id="overlayImg" src=""></div>
|
||||
<script>
|
||||
function openImg(src) {{
|
||||
document.getElementById('overlayImg').src = src;
|
||||
document.getElementById('overlay').classList.add('active');
|
||||
}}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
with open(REPORT, "w") as f:
|
||||
f.write(html_doc)
|
||||
print(f"已生成: {REPORT}")
|
||||
Reference in New Issue
Block a user