问题:接口2 与接口3/5 乱序调用时耗时抖动(最差 15~22s)。两个根因: 1. GPU 24G 常驻 21.4G,Flux-2(3.9G) 无法完全驻留显存,每次采样动态换页, 速度随空闲显存波动(2s~8s); 2. ComfyUI 单队列 FIFO,接口2 排在接口3/5 批量任务后面。 改动: - hairline/comfyui.py: run() 新增 front 参数,/prompt 带 "front": true 插队到队列最前; redraw.py 透传;service.py 接口2 三处调用(女重绘 + 男有/无遮罩)传 front=True, 接口3/5 仍走普通队列。 - add_hair.json / 0716add-hair-api.json: 节点61 CLIPLoader device default→cpu。 qwen CLIP(4G) 不再占显存(文本条件缓存常年命中),ComfyUI 显存 8.8G→4.5G, Flux-2 完全驻留,采样稳定 ~3-5s。代价:换 prompt 后首次请求 CPU 编码 ~11s(一次性)。 - 提示词全局统一为「填充遮罩区域的头发,皮肤加一点磨皮,再加一点美颜」: app.py 4处默认值、service.py _REDRAW_PROMPT、redraw.py _DEFAULT_PROMPT、 4个工作流节点60内置文案、测试页(test_interface2/3/7/12/12_final)、local_test。 任何两个不同 prompt 交替提交都会打爆 CLIP 编码缓存(--cache-classic 只存最近一次), 之前测试页旧文案与服务端不一致导致交替测试每次 +11s。 - app.py: 接口7 /api/v1/hair/grow-v2 下线(业务弃用;add_hair2.json 的 Klein-9b 会把常驻 Klein-4b 挤出显存)。保留 stub 返回 1007 明确报错,避免裸 404。 实测(1024 档):接口2女 8.5~10s、接口2男 ~5s、接口3 ~7-10s,交替混跑无尖刺。 Co-authored-by: Cursor <cursoragent@cursor.com> (cherry-picked from ubuntu3090 e7b62f2;已适配 main 分支代码结构:main 无 _REDRAW_PROMPT/_REDRAW_MAX_SIDE 缩图逻辑,front=True 直接加在 _call_local_redraw / generate_grow_results 的调用点;另把 main 独有的 benchmark_*.py 里的 prompt 一并统一) Co-authored-by: Cursor <cursoragent@cursor.com>
70 lines
2.7 KiB
Python
70 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate result images at different step counts for quality comparison."""
|
|
import io, time
|
|
import requests
|
|
import numpy as np
|
|
from PIL import Image, ImageFilter
|
|
import app as A
|
|
|
|
COMFY = "http://127.0.0.1:8188"
|
|
MODEL = "flux2.0/flux-2-klein-9b-fp8.safetensors"
|
|
DTYPE = "fp8_e4m3fn_fast"
|
|
OUT = "output"
|
|
|
|
image = Image.open("用来重绘.jpg").convert("RGB")
|
|
mask_img = Image.open("用来重绘.png").convert("RGBA")
|
|
mask_data = np.max(np.array(mask_img), axis=2)
|
|
m = Image.fromarray(mask_data, mode="L")
|
|
if m.size != image.size:
|
|
m = m.resize(image.size, Image.LANCZOS)
|
|
m = m.filter(ImageFilter.GaussianBlur(radius=4))
|
|
alpha = Image.eval(m, lambda x: 255 - x)
|
|
r, g, b = image.split()
|
|
rgba = Image.merge("RGBA", (r, g, b, alpha))
|
|
buf = io.BytesIO(); rgba.save(buf, format="PNG"); buf.seek(0)
|
|
fname = requests.post(f"{COMFY}/upload/image",
|
|
files={"image": ("hair_input.png", buf, "image/png")}).json()["name"]
|
|
|
|
# fixed seed for fair comparison
|
|
SEED = 123456789
|
|
imgs = []
|
|
labels = []
|
|
for steps in [2, 3, 4, 6]:
|
|
wf = A.build_workflow(fname, "填充遮罩区域的头发,皮肤加一点磨皮,再加一点美颜", seed=SEED)
|
|
wf["16"]["inputs"]["unet_name"] = MODEL
|
|
wf["16"]["inputs"]["weight_dtype"] = DTYPE
|
|
wf["1"]["inputs"]["steps"] = steps
|
|
pid = requests.post(f"{COMFY}/prompt", json={"prompt": wf}).json()["prompt_id"]
|
|
t0 = time.time()
|
|
while True:
|
|
time.sleep(0.1)
|
|
h = requests.get(f"{COMFY}/history/{pid}").json()
|
|
if pid in h and "17" in h[pid].get("outputs", {}):
|
|
ts = {mm[0]: mm[1].get("timestamp") for mm in h[pid]["status"]["messages"]}
|
|
dur = (ts["execution_success"] - ts["execution_start"]) / 1000.0
|
|
info = h[pid]["outputs"]["17"]["images"][0]
|
|
data = requests.get(f"{COMFY}/view", params={
|
|
"filename": info["filename"], "subfolder": info.get("subfolder", ""),
|
|
"type": info.get("type", "output")}).content
|
|
im = Image.open(io.BytesIO(data)).convert("RGB")
|
|
imgs.append(im)
|
|
labels.append(f"steps={steps} {dur:.2f}s")
|
|
print(f"steps={steps}: {dur:.2f}s", flush=True)
|
|
break
|
|
|
|
# build side-by-side contact sheet
|
|
from PIL import ImageDraw
|
|
h0 = imgs[0].height
|
|
w0 = imgs[0].width
|
|
pad = 10
|
|
bar = 28
|
|
sheet = Image.new("RGB", (w0 * len(imgs) + pad * (len(imgs) + 1),
|
|
h0 + bar + pad * 2), (26, 26, 46))
|
|
d = ImageDraw.Draw(sheet)
|
|
for i, (im, lb) in enumerate(zip(imgs, labels)):
|
|
x = pad + i * (w0 + pad)
|
|
sheet.paste(im, (x, bar + pad))
|
|
d.text((x + 4, 6), lb, fill=(233, 69, 96))
|
|
sheet.save(f"{OUT}/compare_steps.png")
|
|
print("saved:", f"{OUT}/compare_steps.png", flush=True)
|