我们团队在做一个虚拟试衣产品,用户上传一张模特图和一张服装图,AI 生成模特穿上该服装的效果图。看起来很美好,但上线后遇到一个高频问题:
很多用户上传的"服装图"里面本身就包含模特。
这会导致什么问题?生图模型虽然提示词里写了"必须使用第一张图片的模特",但 AI 仍然会偶尔产生幻觉——把服装图里的模特当成目标模特,生成出完全错误的人脸。
用户反馈:为什么试衣结果的脸不是我?为什么换了个模特?
一开始我们的应对方式是让用户重试几次,但体验很差。根本原因是服装图里的人像信息在干扰生图模型。
既然问题出在服装图里的人像干扰,那最直接的方案就是:检测服装图是否包含人,如果包含就把脸部、脖子、头发等区域抹除掉,只保留服装本身的信息再传给试衣模型。
抹除后,生图模型只能从模特图获取人脸信息,幻觉问题基本消除。
我调研了几种主流方案:
方案 | CPU 速度 | 精度 | 模型大小 | 说明 |
|---|---|---|---|---|
SegFormer B0 人体解析 | ~2-3s/张 | 高 | ~50MB | 像素级语义分割,直接有 Face/Neck/Hair 标签 |
YOLO + MediaPipe | ~50ms | 中 | ~30MB | 脖子边界靠关键点估算,不精准 |
SAM2 | ~10-30s | 极高 | ~2GB | 太慢太重,CPU 不现实 |
RemBG + 人体解析 | ~5-6s | 中 | ~220MB | 两模型串联,延迟翻倍 |
最终选择 SegFormer B0 + ATR 数据集,理由:
抹除方式对比:
方式 | 效果 | 速度 | 推荐度 |
|---|---|---|---|
cv2 inpainting (Telea) | 边界自然,修复效果好 | ~10ms | ⭐⭐⭐ 推荐 |
马赛克 | 像素化打码 | ~5ms | ⭐⭐ 看场景 |
纯色填充 | 有明显色块 | ~1ms | ⭐ 不推荐 |
使用 mattmdjaga/segformer_b0_clothes 模型,通过 HuggingFace transformers 加载:
from transformers import SegformerForSemanticSegmentation, SegformerImageProcessor
model = SegformerForSemanticSegmentation.from_pretrained("mattmdjaga/segformer_b0_clothes")
processor = SegformerImageProcessor.from_pretrained("mattmdjaga/segformer_b0_clothes")模型输出 18 个类别的像素级分割图,其中我们需要抹除的类别:
ERASE_LABELS = {"Hat", "Hair", "Sunglasses", "Face", "Neck"}拿到分割图后,将需要抹除的类别合并为二值 mask,再做形态学膨胀确保边缘完整覆盖:
mask = np.isin(seg_map, list(ERASE_IDS)).astype(np.uint8) * 255
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (31, 31))
mask = cv2.dilate(mask, kernel, iterations=1)膨胀很重要——分割边界通常会比实际区域小几个像素,不膨胀的话抹除边缘会残留头发丝或脸部轮廓。
三种方式实现都很简单:
# Inpaint 修复(推荐)
result = cv2.inpaint(image, mask, inpaint_radius=10, cv2.INPAINT_TELEA)
# 马赛克
# 逐块计算 mask 区域内平均颜色,用平均色填充
# 纯色填充
result[mask == 255] = (128, 128, 128)用 FastAPI 封装成服务,接口设计为 base64 进 base64 出,方便虚拟试衣系统直接调用:
POST /api/erase
输入: image(base64) + method + 可选参数
输出: JSON { has_person, detected_parts, image(base64), format, elapsed_seconds }关键设计决策:
data:image/jpeg;base64, 前缀:前端传来的 base64 常带这个前缀,自动处理顺手做了一个单页应用,支持拖拽上传、三种抹除方式切换、参数调整、原图与结果左右对比、下载结果。方便给同事演示效果。
vtt-mask/
├── app/
│ ├── main.py # FastAPI 服务,路由定义
│ ├── parser.py # SegFormer B0 人体解析模型
│ ├── eraser.py # 抹除逻辑 (inpaint / mosaic / fill)
│ └── static/
│ └── index.html # 前端演示页面
├── requirements.txt
├── API_DOC.md # API 接口文档
└── README.mdimport requests, base64
with open("clothing.jpg", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
resp = requests.post("http://localhost:8787/api/erase", data={
"image": b64,
"method": "inpaint",
"output_format": "jpg",
})
result = resp.json()
if result["has_person"]:
print("检测到人像,已抹除:", result["detected_parts"])
else:
print("纯服装图,无需抹除")
# result["image"] 为抹除后图片 base64pip install "numpy<2"torch.onnx.export 导出时报错。最终放弃 ONNX 加速,直接用 PyTorch CPU 推理,2-3 秒也能接受decode → encode 一遍,导致 JPEG→PNG 格式变化和体积膨胀。改为直接回传原始字节流后解决这个方案的核心思路很简单:与其让 AI 不犯错,不如从输入端消除犯错的可能。把服装图中的人像信息抹除掉,试衣模型就只剩一个选择——用模特图的人脸。
SegFormer B0 模型轻量精准,CPU 即可运行,部署成本低。整个方案从调研到上线一天搞定,效果立竿见影。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。