问题:接口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>
发型补全服务 API 文档
服务概述
本服务提供基于 ComfyUI 的发型补全(局部重绘)能力。通过传入人物图片和遮罩图片,调用 ComfyUI 工作流(0716add-hair.json)生成补全后的图片。
技术栈
- 框架: Flask
- 依赖: requests, Pillow, numpy
- 后端: ComfyUI (http://127.0.0.1:8188)
服务地址
- HTTP:
http://127.0.0.1:8899 - 前端页面:
http://127.0.0.1:8899/ - API接口:
http://127.0.0.1:8899/api/generate
启动方式
使用脚本(推荐)
# 启动服务
cd /home/ubuntu/hair/local_test
./start.sh
# 停止服务
./stop.sh
直接运行
cd /home/ubuntu/hair/local_test
/home/ubuntu/ComfyUI/venv/bin/python app.py
API 接口
POST /api/generate
调用 ComfyUI 工作流,传入图片和遮罩,返回生成结果。
请求参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| image | File | 是 | 人物图片(支持 jpg, png 等常见格式) |
| mask | File | 是 | 遮罩图片(支持 jpg, png,遮罩区域可用红色/白色/alpha 通道标识) |
| prompt | String | 否 | 提示词,默认值:"填充遮罩区域的头发,皮肤加一点磨皮" |
遮罩图片格式说明
服务支持多种遮罩格式,自动提取遮罩区域:
| 格式类型 | 示例 | 遮罩区域标识 |
|---|---|---|
| 红色遮罩 | 红色画笔绘制 | R=255 的像素 |
| 白色遮罩 | 白色画笔绘制 | R=G=B=255 的像素 |
| Alpha 遮罩 | 透明背景 | A=255 的像素 |
服务会取所有通道的最大值作为遮罩强度,因此以上格式均可混用。
注意: 遮罩区域表示需要重绘的部分,非遮罩区域保持原图不变。
请求示例(curl)
curl -X POST http://127.0.0.1:8899/api/generate \
-F "image=@/path/to/person.jpg" \
-F "mask=@/path/to/mask.png" \
-F "prompt=填充遮罩区域的头发,皮肤加一点磨皮" \
--output result.png
请求示例(Python)
import requests
url = "http://127.0.0.1:8899/api/generate"
files = {
"image": open("person.jpg", "rb"),
"mask": open("mask.png", "rb"),
}
data = {
"prompt": "填充遮罩区域的头发,皮肤加一点磨皮"
}
resp = requests.post(url, files=files, data=data, timeout=600)
if resp.status_code == 200:
with open("result.png", "wb") as f:
f.write(resp.content)
else:
print(f"Error: {resp.json()}")
响应
成功 (HTTP 200):
返回 PNG 图片二进制数据,Content-Type: image/png。
失败 (HTTP 4xx/5xx):
返回 JSON 格式错误信息:
{
"error": "错误描述"
}
错误码
| 状态码 | 说明 |
|---|---|
| 500 | 内部错误(文件处理失败、ComfyUI 返回错误等) |
| 503 | 无法连接到 ComfyUI(服务未启动或端口错误) |
| 500 | 超时(工作流执行超过 5 分钟) |
工作流说明
服务使用的工作流 0716add-hair.json 包含以下处理步骤:
- 加载模型: Flux 2 Klein 9B (FP8) + Qwen 3.8B CLIP
- 图片上传: 将原图与遮罩合成为 RGBA 格式上传至 ComfyUI
- 遮罩处理: 填充孔洞 → 转换为图像 → 缩放 → 转换回遮罩
- 图像缩放: 按比例缩放至合适尺寸(最大边长 1024,8 的倍数)
- VAE 编码: 将图像编码为 latent
- 采样生成: 使用 Flux 模型 + ReferenceLatent 进行局部重绘
- VAE 解码: 将 latent 解码为图像
- 颜色匹配: 使用 ColorMatch 保持颜色一致
- 保存结果: 返回生成的图片
前置依赖
启动服务前需确保:
- ComfyUI 已启动:
http://127.0.0.1:8188可访问 - 模型文件存在:
models/unet/flux2.0/flux-2-klein-9b-fp8.safetensorsmodels/vae/flux2-vae.safetensorsmodels/clip/qwen_3_8b_fp8mixed.safetensors
- 虚拟环境已激活: 使用
/home/ubuntu/ComfyUI/venv/bin/python
文件结构
/home/ubuntu/hair/local_test/
├── app.py # Flask 后端服务
├── index.html # 前端测试页面
├── test_api.py # API 测试脚本
├── README.md # 本文档
├── output/ # 测试结果输出目录
├── 用来重绘.jpg # 测试人物图片
└── 用来重绘.png # 测试遮罩图片
使用流程
- 启动 ComfyUI(
python main.py --listen) - 启动本服务(
python app.py) - 调用 API 或访问前端页面上传图片和遮罩
- 等待生成完成(通常 30-60 秒)
- 获取返回的 PNG 图片
注意事项
- 请求超时时间为 5 分钟,生成复杂图片可能需要较长时间
- 遮罩图片尺寸需与人物图片一致,服务会自动缩放对齐
- 建议使用红色或白色绘制遮罩,确保遮罩强度足够
- 服务会自动对遮罩边缘进行高斯模糊(radius=4),避免硬边