InsightFace 本地人脸替换实践:贴回遮罩、身份增强与姿态保护

InsightFace 本地人脸替换实践:贴回遮罩、身份增强与姿态保护

技术笔记 · Windows / Python / InsightFace / ONNX Runtime

本文只讨论本地图像处理实现细节,不涉及任何违规用途。请合法合规使用相关技术。


写在前面

InsightFace 的 inswapper 系列模型,是目前本地做人脸区域替换较常见的方案之一。网上示例大多直接:

python 复制代码
result = swapper.get(target_img, target_face, source_face, paste_back=True)

能出图,但工程上常会碰到:

  1. 结果里残留目标人脸的妆容/唇色等局部颜色
  2. 「源脸身份」不够强,观感仍偏目标脸
  3. 为了更像源脸,又把源脸裁剪硬贴上去,角度不一致时出现「贴纸感」
  4. 桌面程序点击停止后界面假死(CUDA/ONNX 推理难以中途强杀)

下面按实现顺序记录一版可落地的处理思路与关键代码。完整工程资料可另行查阅公开站点文档(文末),正文以代码为主。


1. 最小推理骨架

python 复制代码
from insightface.app import FaceAnalysis
import insightface
import cv2

app = FaceAnalysis(
    name="buffalo_l",
    providers=["CUDAExecutionProvider", "CPUExecutionProvider"],
)
app.prepare(ctx_id=0, det_size=(640, 640))

swapper = insightface.model_zoo.get_model(
    "inswapper_128.onnx",
    providers=["CUDAExecutionProvider", "CPUExecutionProvider"],
)

src = cv2.imread("source.jpg")
tgt = cv2.imread("target.jpg")
sf = sorted(app.get(src), key=lambda f: f.bbox[0])[0]
tf = sorted(app.get(tgt), key=lambda f: f.bbox[0])[0]

# 建议先拿模型输出,自己决定贴回策略
bgr_fake, M = swapper.get(tgt, tf, sf, paste_back=False)

说明:

  • buffalo_l:检测 + 关键点 + 特征
  • inswapper_128.onnx:在对齐空间生成替换结果
  • 有 NVIDIA 显卡时优先 CUDA;CPU 也能跑,只是更慢
  • 模型建议放到项目本地目录,减少对系统缓存盘的依赖

2. 默认 paste_back=True 为什么会「漏颜色」

官方贴回逻辑大致是:把生成脸与对齐后的目标脸做差值,差值小的区域继续保留目标图像素。口红、腮红等局部颜色,如果与生成结果接近,就容易被当成「无需替换」留在最终图上。

更稳的做法是用几何脸罩(覆盖眼鼻嘴区域)贴回,而不是只信差值遮罩:

python 复制代码
import numpy as np
import cv2

def paste_face(target_img, bgr_fake, M):
    h, w = target_img.shape[:2]
    size = bgr_fake.shape[0]
    mask = np.zeros((size, size), np.float32)

    cv2.ellipse(
        mask,
        (size // 2, int(size * 0.46)),
        (int(size * 0.41), int(size * 0.50)),
        0, 0, 360, 1.0, -1,
    )

    # 下巴以下渐隐,减轻脖子接缝
    y_fade = np.ones(size, np.float32)
    cut = int(size * 0.82)
    if cut < size:
        y_fade[cut:] = np.linspace(1.0, 0.0, size - cut)
    mask *= y_fade[:, None]
    mask = cv2.GaussianBlur(mask, (7, 7), 0)

    IM = cv2.invertAffineTransform(M)
    fake_w = cv2.warpAffine(bgr_fake, IM, (w, h), borderValue=0.0)
    mask_w = cv2.warpAffine(mask, IM, (w, h), borderValue=0.0)[..., None]
    out = mask_w * fake_w + (1.0 - mask_w) * target_img.astype(np.float32)
    return np.clip(out, 0, 255).astype(np.uint8)

3. 增强源脸身份,同时保留目标姿态

inswapper 输出本身带有目标脸的姿态与光影。若把源脸 norm_crop 后以过高权重整块混入,证件正脸 × 目标侧脸时,很容易出现「正脸贴纸」。

更稳的策略:

  1. 用五官点估计左右偏转差,角度差大则降低几何混入上限
  2. LAB 色度(a/b)向源脸靠,亮度尽量保留模型输出
  3. 仅混入少量源脸高频细节,不整脸替换几何结构
python 复制代码
def yaw_proxy(kps):
    le, re, nose = kps[0], kps[1], kps[2]
    mid = (float(le[0]) + float(re[0])) * 0.5
    eye_w = abs(float(re[0]) - float(le[0])) + 1e-6
    return abs((float(nose[0]) - mid) / eye_w)

def pose_mismatch(src_face, tgt_face):
    return (
        abs(yaw_proxy(src_face.kps) - yaw_proxy(tgt_face.kps))
        + max(yaw_proxy(src_face.kps), yaw_proxy(tgt_face.kps)) * 0.35
    )

def boost_identity(bgr_fake, source_bgr, source_face, target_face, strength=0.75):
    from insightface.utils import face_align

    size = bgr_fake.shape[0]
    src_crop, _ = face_align.norm_crop2(
        source_bgr, np.asarray(source_face.kps, dtype=np.float32), size
    )

    # 角度差大 → 几何硬贴上限下降
    geom_cap = 0.42
    m = pose_mismatch(source_face, target_face)
    if m > 0.18:
        geom_cap = 0.22
    if m > 0.32:
        geom_cap = 0.12

    f_lab = cv2.cvtColor(bgr_fake, cv2.COLOR_BGR2LAB).astype(np.float32)
    s_lab = cv2.cvtColor(src_crop, cv2.COLOR_BGR2LAB).astype(np.float32)
    # 在五官权重区域内:a/b 向源脸统计量靠拢(具体 mask 可自建椭圆权重)

    detail = src_crop.astype(np.float32) - cv2.GaussianBlur(
        src_crop.astype(np.float32), (0, 0), 1.35
    )
    # base = base + detail * (face_mask * strength * 0.4)

    # 几何混入必须有硬顶:strength * geom_cap
    # return mix(src_adj, base, weight=face_mask * strength * geom_cap)

参数经验:

  • 身份强度常用 0.70~0.85,比直接拉满更稳
  • 「匹配目标亮度」类后处理默认可关;开太高容易重新偏向目标脸观感
  • 输入尽量双方正脸近景、角度接近,比反复堆后处理更有效

4. GUI 停止:用任务代数作废结果

ONNX/CUDA 单次推理通常无法优雅中断。如果界面要等当前步结束才解锁,体验会像卡死。

更友好的做法:

  • 点击停止时立刻 cancel_event.set(),并递增任务代数 _job_gen
  • UI 马上恢复可点
  • 后台线程结束后若代数已变,丢弃结果不回写界面
python 复制代码
import threading

self._job_gen = 0
self._cancel_event = threading.Event()
self._engine_lock = threading.Lock()

def stop_job(self):
    self._cancel_event.set()
    self._job_gen += 1
    self.set_busy(False)

def worker(self, job_id):
    with self._engine_lock:
        if self._cancel_event.is_set():
            return
        result = run_pipeline(cancel_event=self._cancel_event)
    if self._job_gen != job_id:
        return  # 已停止,丢弃
    self.ui_apply(result)

换脸主流程里仍建议在检测前后、贴回前后多次检查 cancel_event,便于尽快跳出 Python 层循环。


5. PyInstaller 打包后的资源路径

目录模式(onedir)打包后,资源可能在 exe 同级,也可能在 _internal。建议统一查找:

python 复制代码
from pathlib import Path
import sys

def resource_path(*parts: str) -> Path:
    if getattr(sys, "frozen", False):
        roots = [Path(sys.executable).resolve().parent]
        if hasattr(sys, "_MEIPASS"):
            roots.append(Path(sys._MEIPASS))
    else:
        roots = [Path(__file__).resolve().parent]

    for root in roots:
        p = root.joinpath(*parts)
        if p.exists():
            return p
    return roots[0].joinpath(*parts)

含模型与 CUDA 相关动态库时,体积会较大;发目录包比单文件更省事,也更利于排错。


6. 输入质量检查清单

情况 建议
双方正脸、光线接近 效果通常最好
正脸硬贴明显侧脸 降低几何混入,或换图
遮挡/墨镜/面具 先换清晰图
手机屏幕翻拍 尽量改用原图文件

后处理再强,也补不齐「输入角度差过大」带来的结构矛盾。


7. 小结

本地人脸区域替换可以拆成四层:

  1. 检测与对齐(InsightFace)
  2. 生成(InSwapper)
  3. 贴回策略(完整脸罩,减少颜色泄漏)
  4. 身份增强(保姿态、限几何、迁色度)

再补上取消任务与打包路径,就接近一个可日常使用的本地小工具了。


参考与延伸

  • InsightFace 官方仓库与模型说明
  • ONNX Runtime 的 CUDA / CPU Provider 文档
  • 若需对照更完整的工程组织方式,可在公开技术站点检索相关实践文档:zhixuanxingqiu.com

请仅将相关技术用于学习、研究与合法授权场景。

相关推荐
vivo互联网技术5 天前
扩散模型自引导新范式 SSG:直接交换Token就能变强! | CVPR 2026 Oral
图像识别·计算机图形学
weixin_4080996711 天前
2026 扑克牌识别 API 实战:识别牌值、花色、大小王(附 Python/Java/PHP 接入示例)
python·ocr·图像识别·api接口·接口开发·扑克牌识别·石榴智能
海途信息17 天前
非接触河道测流新技术:AI视频流速监测原理、算法体系与工程应用
图像识别·机器视觉·智慧水利·光流算法
谙忆10241 个月前
图片放大为什么会糊?从插值到 AI 超分,超分辨率技术是怎么演进的
图像识别
vivo互联网技术1 个月前
SIGGRAPH 2026 | VeraRetouch:多任务推理式照片修图框架
图像识别·计算机图形学
hhzz1 个月前
基于监控视频的水位尺自动识别技术方案与实现
python·opencv·yolo·图像识别·cv
Bigger2 个月前
我写了一个AI图像视频生成工具,免费API+本地部署,分享给大家
人工智能·图像识别·音视频开发
ZJPRENO2 个月前
创作者狂喜!Seedance 2.5 支持 50 份素材同时导入,做短剧广告爽翻
人工智能·ai编程·图像识别
Dxy12393102162 个月前
豆包九宫格验证码识别
网络爬虫·图像识别·验证码·抖音·验证码识别·豆包·九宫格验证码