Faceswap Convert 插件体系全解析:颜色校正、Mask 融合、锐化与输出编码
本指南以 convert 包 API 参考文档为主线,系统梳理 Faceswap 在“转换(convert)”阶段提供的全部插件:颜色校正(colour)、Mask 融合(mask)、锐化缩放(scaling)与结果写出(writer)。读完本文,你将掌握每个插件的算法原理、全部可配置参数的含义与取值范围,以及它们在 plugins/convert 目录中的实现位置,能够独立调优一次完整的换脸输出流程。
一、convert 包在 Faceswap 中的地位
docs/full/plugins/convert.rst 用一句话概括了这个包的核心职责:
The Convert Package handles the various plugins available for performing conversion in Faceswap.
也就是说,换脸模型推理完成后,从“新面孔(new_face)”到“无缝合成进原始帧”这一整条后期处理流水线,都由 plugins/convert 之下的插件完成。这些插件被划分为四个子包:
| 子包 | 职责 | 包含插件(模块) |
|---|---|---|
colour |
让换脸结果在色彩上贴近目标帧 | avg_color、color_transfer、manual_balance、match_hist、seamless_clone |
mask |
融合换脸区域与背景的边界 | mask_blend |
scaling |
合成后对画面进行锐化等后处理 | sharpen |
writer |
决定转换结果如何写出(视频/图像序列/GIF) | ffmpeg、gif、opencv、patch、pillow |
该 RST 文件是 Sphinx 自动生成 API 文档的“节点”:通过 automodapi 指令把上述模块的 docstring 与类、方法、参数文档抽取到渲染页面中。因此,理解这篇文档的真正入口是 plugins/convert 目录下的源码与每个插件配套的 <plugin_name>_defaults.py 配置文件——后者正是渲染页面上所有配置项说明的来源。
一个对理解全文至关重要的机制:每个插件默认都带有一个 <plugin_name>_defaults.py 文件(如 color_transfer_defaults.py)。defaults 文件头部说明明确指出,其中定义的 ConfigItem 会自动写入 faceswap/config 目录下对应的 .ini 配置文件,并自动出现在 GUI 的设置页面中,无需手工维护配置表。加载这些默认配置的入口是 convert_config.py:其 _Config 类调用 _defaults_from_plugin(os.path.dirname(__file__)),扫描本目录下所有插件的 defaults 文件完成注册,且整个进程内配置单例(_CONFIG)只加载一次。
二、colour 子包:五种颜色/合成校正插件
colour 子包中的所有插件共享同一个父类 color/_base.py 的 Adjustment。理解父类即可理解全部颜色插件的调用约定。
2.1 统一的处理管线(Adjustment 基类)
颜色插件只须实现 process(old_face, new_face, raw_mask) 这一个纯算法方法,父类的 run()(见 _base.py 第 24-41 行)负责统一调度:
- 若输入面片带第 4 个通道(RGBA),先剥离 Alpha 通道保存,仅对 RGB 三通道做颜色处理;
- 调用子类实现的
process(); - 结果统一执行
np.clip(new_face, 0.0, 1.0)把像素钳制到合法区间; - 若此前剥离了 Alpha,则把原 mask 通道重新拼接回去。
由于每个插件的构造方法都会执行 convert_config.load_config(...),所有颜色插件的参数都来自同一份 convert 配置,通过 defaults 中的模块级 ConfigItem 对象取值(例如 cfg.clip())。
下面是五个插件的逐一剖析。
2.2 Average Colour(avg_color)
avg_color.py 的 Color.process() 实现最直观的均值对齐:反复两次计算 old_face 与 new_face 之间的逐通道差 diff;当存在有效 mask 时,用 raw_mask 加权求出平均差 avg_diff(即 sum(diff*mask)/sum(mask)),再把该平均差整体加回 new_face。其效果是让两个面片各通道的均值趋同,属于最基础、开销最低的快速校色手段,但只对齐一阶统计量,无法纠正饱和度、对比度的差异。
2.3 Color Transfer(基于 Reinhard 等人的 L*a*b* 颜色迁移)
color_transfer.py 移植自 Adrian Rosebrock 的 color_transfer 实现(源码头部保留了 MIT 许可证声明),其算法“松散地”基于 Reinhard et al., 2001 的论文 Color Transfer between Images:把源(原始帧面片)与目标(换脸面片)都转换到 L*a*b* 色彩空间,用均值和标准差统计各通道分布,再对目标做“去均值 → 按标准差缩放 → 加源均值”的分布匹配,最后转回 BGR。实现细节还包括:
- 源图与目标图在统计前都会乘上
raw_mask并缩放到 0–255 的 uint8,保证统计只针对真实人脸区域; - 越界像素可通过
np.clip(保持亮度贴近原图)或 min-max 缩放(避免 clip 造成发白区域)两种方式处理; - 完成后用
new_face * (1 - raw_mask)把非人脸背景重新叠回。
该插件两个配置项定义于 color_transfer_defaults.py:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
clip |
bool | True |
L*a*b* 分量在转回 BGR 前是否用 np.clip 缩放。为 False 时改用 min-max 缩放,可避免裁剪导致的发白区域,但会整体改变亮度观感 |
preserve_paper |
bool | True |
是否严格遵循论文的缩放方法。为 False 时改用论文建议系数的倒数(l_std_src/l_std_tar),实践上能产生更一致、更美观的结果 |
2.4 Manual Balance(手动调色)
manual_balance.py 允许用户像修图软件一样手动拖动滑杆调整换脸面片的色彩。其 process() 首先按所选色彩空间把图像从 BGR 转换过去,再对三个通道分别做增益:adj>=0 时 ch = (1-ch)*adj + ch(向白/纯色方向推),adj<0 时 ch = ch*(1+adj)(向黑方向压),最后转回 BGR 并叠加对比度/亮度调整。注意其对比度实现使用 contrast*1.27、brightness*1.27 的放大系数映射到 OpenCV 风格的 alpha/beta 公式。
配置项定义于 manual_balance_defaults.py:
| 配置项 | 类型 | 默认值 | 范围 | 说明 |
|---|---|---|---|---|
colorspace |
str | "HSV" |
RGB/HSV/LAB/YCrCb |
调色所用色彩空间。RGB 三个通道同时编码亮度,易相互影响;HSV 用单一 Hue 通道描述颜色最直观;LAB 的 L 只编码亮度;YCrCb 把亮度(Y)与色度(Cr/Cb)分离 |
balance_1 |
float | 0.0 |
-100 ~ 100 | 通道 1:RGB 中为 Red,HSV 中为 Hue,LAB 中为 Lightness,YCrCb 中为 Luma |
balance_2 |
float | 0.0 |
-100 ~ 100 | 通道 2:Green / Saturation / Green→Magenta / Cr |
balance_3 |
float | 0.0 |
-100 ~ 100 | 通道 3:Blue / Intensity / Blue→Yellow / Cb |
contrast |
float | 0.0 |
-100 ~ 100 | 对比度量(按 1.27 系数换算到内部公式) |
brightness |
float | 0.0 |
-100 ~ 100 | 亮度调整量 |
GUI 中配置按 group 分组:前四项归入 “color balance”,后两项归入 “brightness contrast”。
2.5 Match Hist(直方图匹配)
match_hist_defaults.py 的目标是“匹配源与目标面片的直方图”。其唯一配置项 threshold 用于过滤直方图两端的极端颜色,避免极端色彩渗入结果:
| 配置项 | 类型 | 默认值 | 范围 | 说明 |
|---|---|---|---|---|
threshold |
float | 99.0 |
90.0 ~ 100.0 | 直方图匹配阈值。调低可剔除直方图最末端的极端颜色,从而抑制过冲色斑 |
2.6 Seamless Clone(无缝克隆)
seamless_clone.py 借助 OpenCV 的 cv2.seamlessClone(..., cv2.NORMAL_CLONE) 把换脸结果“泊松融合”进原始帧:先用 np.nonzero(raw_mask) 求 mask 的包围盒与几何中心,把待插入内容裁剪出来、对背景做等量 padding 后交给 seamlessClone,最后再裁掉 padding。值得注意的是源码在类 docstring 中自我评价“doesn't work well”,并说明它之所以放在 colour 包中只是因为“不是一个颜色调整、也没有更合适的归属”,并且它是由 lib/convert.py 作为额外插件单独调用的(见模块头部注释)。这也提示使用者:在追求高真实感的场景下,seamless clone 未必优于 mask_blend 方案。
三、mask 子包:Mask Blend 融合
mask_blend.py(配置见 mask_blend_defaults.py)负责对换脸边界做融合,是决定“接缝是否可见”的关键插件。其 HELPTEXT 描述为“blending the edges between the mask and the background image”。配置项非常丰富,全部定义如下:
| 配置项 | 类型 | 默认值 | 范围/选项 | 说明 |
|---|---|---|---|---|
type |
str | "normalized" |
gaussian/normalized/none |
融合算法:gaussian 用高斯滤波器,较慢但通常更好;normalized 用归一化 box 滤波器,速度更快;none 不融合 |
kernel_size |
int | 3 |
1 ~ 9 | 融合核直径(以 128px mask 为基准计算)。决定融合量,越大边界越柔和;偶数会自动取整为下一个奇数 |
passes |
int | 4 |
1 ~ 8 | 融合算法迭代次数。额外迭代可改善平滑度但增加耗时,且收益指数级递减,不建议设过高 |
threshold |
int | 4 |
0 ~ 50 | 把接近纯白的像素置白、接近纯黑的像素置黑;0 关闭 |
erosion |
float | 0.0 |
-100.0 ~ 100.0 | 对整个面片 mask 腐蚀/膨胀,量纲为 mask 半径的百分比。正值收缩换脸区域,负值(膨胀)扩大换脸区域 |
erosion_top / erosion_bottom / erosion_left / erosion_right |
float | 0.0 |
-100.0 ~ 100.0 | 仅对 mask 的对应边缘做腐蚀(正值向中心收缩)或膨胀(负值向外扩张),用于单独修正发际线、下颌等局部边界 |
如果你只希望调节“边缘是否生硬”,核心只需关注 type 与 kernel_size;erosion* 系列适合处理换脸区域边缘残留(如发丝、面部轮廓错位)的精细修复。
四、scaling 子包:Sharpen 锐化
scaling 子包目前只提供 sharpen.py 一个插件(父类位于 scaling/_base.py),作用是在人脸合成进画面后对整体结果做锐化补偿(因为模型输出普遍偏软)。其配置在 sharpen_defaults.py:
| 配置项 | 类型 | 默认值 | 范围/选项 | 说明 |
|---|---|---|---|---|
method |
str | "none" |
none/box/gaussian/unsharp_mask |
锐化算法。box 最快但最弱(box 滤波器提取边缘);gaussian 较慢但更好(高斯滤波器提取边缘);unsharp_mask 最慢但可调项最多 |
amount |
int | 150 |
100 ~ 500 | 锐化强度百分比:控制边缘两侧过冲(overshoot)的明暗幅度,也可理解为在边缘处附加的对比度;不影响边缘宽度 |
radius |
float | 0.3 |
0.1 ~ 5.0 | 以最终帧宽度的百分比表示的边缘尺寸(四舍五入到像素)。例如 1280 宽帧取 0.6% 约得 8px。过大的 radius 会在边缘产生光晕,细微纹理需要更小半径;radius 与 amount 相互制约 |
threshold |
float | 5.0 |
1.0 ~ 10.0 | 仅 unsharp_mask 生效:参与锐化所需的最小亮度变化量。越高越只锐化强边缘、保护平滑区域免于噪点化 |
五、writer 子包:五种输出写出插件
writer 子包决定转换帧最终以什么形态落盘。所有写出插件都继承 writer/_base.py 的 Output 父类,父类集中实现了三类通用能力,值得先理解:
- 输出文件命名:
get_output_filename()把输入帧名去扩展名后拼接目标扩展名;若开启separate_mask,mask 会写到输出目录下自动创建的masks子目录。 - 按序写出与缓存:流式 writer(GIF、FFmpeg)通过
_set_frame_order()构造待写帧队列,并由cache_frame()依据帧文件名尾部数字(正则(\d+)(?=\.\w+$))把乱序到达的帧缓存起来,保证写出顺序与画面顺序一致;父类据此提供is_stream属性(子类有_frame_order即为流式)。pre_encode()说明文档指出:由于合成是多线程、写出是单线程,支持预编码的 writer 可在 lib/convert.py 的合成阶段提前完成图像编码,显著提速。
5.1 FFMPEG(视频编码,含音频混流)
ffmpeg.py 使用 pyAV 编码视频,并借助 lib/video.py 的 VideoMux 从源视频抽取 FPS 与音频轨混入成品。输出文件名规则在 _get_output_filename()(第 60-88 行)中定义:取源视频名加 _converted 后缀、扩展名随 container 配置,若文件已存在则自动追加 _1、_2…… 迭代至不冲突为止。
全部配置项(ffmpeg_defaults.py)如下:
| 配置项 | 类型 | 默认值 | 选项/范围 | 说明 |
|---|---|---|---|---|
container |
str | "mp4" |
avi/flv/mkv/mov/mp4/mpeg/webm | 视频封装容器 |
codec |
str | "libx264" |
libx264(H.264)、libx265(H.265/HEVC) | 视频编码器 |
crf |
int | 23 |
0 ~ 51 | 恒定质量因子。0 无损、51 最差,常用合理区间 17–28,17–18 视为视觉无损。该值呈指数效应:+6 约使码率/体积减半,-6 约翻倍 |
preset |
str | "medium" |
ultrafast…veryslow(9 档) | 编码速度与压缩率权衡,越慢压缩越好,建议用“有耐心等的最慢档” |
tune |
str | "none" |
none/film/animation/grain/stillimage/fastdecode/zerolatency | 针对输入微调。film/animation/stillimage 仅 H.264 可用(见 ffmpeg.py 中 _valid_tunes,libx265 仅支持 grain/fastdecode/zerolatency) |
profile |
str | "auto" |
baseline/main/high/high10/high422/high444 | 仅 H.264。目标设备不支持时才需修改 |
level |
str | "auto" |
auto、1~6.2 各级 | 仅 H.264。编码等级,同理仅旧设备需要指定 |
skip_mux |
bool | False |
True/False | 跳过音频混流,得到无声视频 |
两个重要的行为约束(均在源码中体现):其一,仅当 skip_mux 为真时关闭音频;其二,限制帧范围转换时无法混流音频——_should_mux_audio() 会打印 warning 并返回 False,此时产出的视频需要你之后手动混音。
5.2 GIF(动画输出)
gif_defaults.py 明确指出一个关键限制:GIF 需要把所有帧载入内存,因此只适合短视频片段。配置项:
| 配置项 | 类型 | 默认值 | 范围/选项 | 说明 |
|---|---|---|---|---|
fps |
int | 25 |
1 ~ 60 | 帧率 |
loop |
int | 0 |
0 ~ 100 | 循环次数,0 表示无限循环 |
palette_size |
str | "256" |
2/4/8/16/32/64/128/256 | 量化到该数量的颜色(会自动就近取 2 的幂) |
dithering |
bool | False |
True/False | 抖动处理。改善渐变但引入噪点:适合自然图像,不适合线条锐利的画面 |
5.3 OpenCV 与 Patch
除上述两个流式 writer 外,writer 子包还提供两个图像写出类插件:
- opencv.py:基于 OpenCV 输出图像序列,适合需要把逐帧 PNG/JPG 交给外部工具继续处理的工作流;
- patch.py:与“写回整帧”不同,patch 插件面向仅需要换脸图块(patch)本身的场景(如自行后期合成或批量实验)。
(以上两个插件与其各自 _defaults.py 同样遵循第五节的自动注册机制,参数会出现在 convert 配置与 GUI 中。)
5.4 Pillow(功能更丰富的图像序列输出)
writer/pillow_defaults.py 自述“Pillow 比 OpenCV 功能更丰富但可能更慢”,支持 7 种格式与 Alpha 透明输出。这是唯一支持把换脸结果单独画在透明图层上的图像 writer,非常适合制作贴纸类素材:
| 配置项 | 类型 | 默认值 | 范围/选项 | 说明 |
|---|---|---|---|---|
format |
str | "png" |
bmp/gif/jpg/jp2/png/ppm/tif | 输出图像格式(gif 为静态帧,非动画) |
draw_transparent |
bool | False |
True/False | 把换脸面片画在透明层上而非原始帧上。仅 png/tif 兼容,选了不兼容格式会被强制存为 png |
separate_mask |
bool | False |
True/False | 仅在开启 draw_transparent 时有效:开启则 RGB 图存入输出目录、mask 单独存入 masks 子目录;关闭则 mask 写入 RGBA 的 Alpha 通道 |
optimize |
bool | False |
True/False | 仅 gif/jpg/png:让编码器额外扫描一遍以选取最优编码设置 |
gif_interlace |
bool | True |
True/False | 仅 gif:是否隔行扫描保存 |
jpg_quality |
int | 75 |
1 ~ 95 | 仅 jpg:质量越高体积越大 |
png_compress_level |
int | 3 |
0 ~ 9 | 仅 png:zlib 压缩级别,1 最快、9 压缩率最高、0 不压缩;开启 optimize 后该值被强制为 9 |
tif_compression |
str | "tiff_deflate" |
none/tiff_ccitt/group3/group4/tiff_jpeg/tiff_adobe_deflate/tiff_thunderscan/tiff_deflate/tiff_sgilog/tiff_sgilog24/tiff_raw_16 | 仅 tif:TIFF 压缩算法 |
六、配置注入路径:从 defaults 到 convert.ini
贯穿全文的一个机制值得单独说明:为什么改动插件效果只需调 GUI/ini,而不用改代码? 链条如下:
- 每个插件目录内都有一个
<plugin_name>_defaults.py,用ConfigItem(...)声明配置项,并声明模块级HELPTEXT作为插件简介; - 启动时 convert_config.py 的
_Config调用self._defaults_from_plugin(os.path.dirname(__file__)),自动扫描plugins/convert下所有插件的 defaults 文件; - 这些项被写入/比对
faceswap/config下的 convert.ini配置文件,并自动呈现到 GUI 的对应设置页; - 插件运行期通过
cfg.<config_item>()(如cfg.crf()、cfg.clip())读取当前生效值——可参考 color_transfer.py 第 69-70 行与 ffmpeg.py 第 97-110 行的取值方式。
由于插件配置被设计成进程级单例,同一轮转换中每个插件的参数只会被解析一次。若需要完全脱离默认配置运行,各插件的构造函数(Adjustment.__init__、Output.__init__)都接受可选的 config_file 参数,允许传入自定义 ini 路径覆盖全局配置。
七、排障与调优速查
结合上文各插件源码,给出几条实用的排查/调优方向:
- 换脸区域颜色发灰、发白:优先尝试
color_transfer(把clip设为False可避免发白)或调高match_hist的threshold排除极端色。 - 接缝/边缘生硬:把
mask_blend的type从normalized换为gaussian,或增大kernel_size、passes;仅局部不对齐时用erosion_top/bottom/left/right单独收缩对应边界。 - 画面偏软、缺乏细节:在 scaling 段启用
sharpen,先用gaussian+ 默认amount=150起步;出现光晕说明radius过大。 - FFmpeg 输出没有声音:检查是否限制了帧范围(源码已确认该场景自动跳过音频混流),或误开了
skip_mux。 - GIF 内存溢出:GIF 需要整段载入 RAM,务必使用短视频序列,必要时降低
palette_size与fps。 - 需要透明背景素材:writer 选 Pillow,开启
draw_transparent,并配合separate_mask决定 mask 走 Alpha 通道还是独立masks子目录。
本文所有结论均可在 docs/full/plugins/convert.rst 所列各模块的源码(plugins/convert/color、plugins/convert/mask、plugins/convert/scaling、plugins/convert/writer)及其配套 defaults 文件中逐一验证。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00