Faceswap Manual Editor 手册:从 CLI 参数到五大编辑器与对齐文件全流程解析
本文基于 docs/full/tools/manual.rst 这份 tools.manual 包的 API 文档,结合 tools/manual/ 下的约 8000 行源码,完整解析 Faceswap 中 Manual(手动对齐)工具的架构与用法:如何从命令行启动、三个对齐器与四种归一化模式如何协同工作、五大编辑器与五种过滤模式各自解决什么问题、以及全部键盘快捷键与对齐文件的保存/回退/缩略图缓存机制。读完之后,你可以独立完成一次视频源帧的对齐文件精修,并能读懂该工具每一处行为背后的源码依据。
Manual 工具的定位:对齐文件的"可视化精修器"
tools/manual/cli.py 中对该工具的描述是:"A tool to perform various actions on frames, faces and alignments files using visual tools"。也就是说,Manual 工具是 Faceswap Tools 套件中唯一的 GUI 工具,它围绕 alignments(对齐)文件 工作:读取视频或帧文件夹,把其中每张人脸的检测框、68 点 landmarks、mask 等数据以 DetectedFace 对象形式加载进内存,让用户在图形界面上逐帧修正,再把结果写回 .json 对齐文件。
该工具只接受"源视频/源帧"作为输入,不接受 extract 步骤输出的"已抽取人脸文件夹"。tools/manual/manual.py 中的 _validate_non_faces 会在启动时随机抽一张 PNG 检查其 iTXt 元数据中是否带有 alignments 头,一旦发现输入是抽取后的人脸文件夹就直接报错退出:
The input folder 'xxx' contains extracted faces.
The Manual Tool works with source frames or a video file, not extracted faces.
这一校验决定了典型工作流:extract 生成对齐文件 → 用 Manual 精修对齐文件 → 再回到 extract 使用修正后的结果。
命令行启动方式与全部参数
Manual 工具的标准入口是 python tools.py manual(见 manual.py 类文档),CLI 参数定义在 cli.py 的 ManualArgs.get_argument_list() 中,共有四个参数:
| 参数 | 选项 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| 对齐文件 | -a / --alignments |
FileFullPaths(filetypes: alignments) |
否 | 指向输入对应的 alignments 文件路径(当它不在默认位置时) |
| 帧源 | -f / --frames |
DirOrFileFullPaths(filetypes: video) |
是 | 源视频文件,或包含源帧的目录(人脸最初即从该视频/帧抽取) |
| 强制重建缩略图 | -t / --thumb-regen |
store_true |
否 | 强制重新生成缓存在对齐文件中的低分辨率 jpg 缩略图 |
| 单进程建缩略图 | -s / --single-process |
store_true |
否 | 禁用并行线程从视频抽缩略图,改用更慢但更稳定的单线程(用于某些视频会导致缓存过程挂起时) |
最小可运行示例:
python tools.py manual -f /path/to/video.mp4
-f 既可以是视频文件也可以是图片文件夹;输入类型由 globals.py 的 TkGlobals._check_input 判定——目录按帧文件夹处理,扩展名在 lib.video 的 VIDEO_EXTENSIONS 中的按视频处理,否则直接 sys.exit(1)。
整体架构:API 文档映射到的模块树
docs/full/tools/manual.rst 用 automodapi 指令按包生成了 API 参考,它列出的五个模块与源码目录一一对应:
| API 文档中的模块 | 源码文件 | 职责 |
|---|---|---|
manual 包主体 |
manual.py | 主入口 Manual 类(tkinter 应用)、右侧选项面板 _Options、对齐管线 Aligner、帧加载器 FrameLoader |
manual.cli |
cli.py | 命令行参数定义 |
manual.detected_faces |
detected_faces.py | DetectedFaces 及磁盘 IO、面更新、过滤三大子系统 |
manual.globals |
globals.py | 全局 tkinter 变量 TkGlobals 与当前帧状态 CurrentFrame |
manual.thumbnails |
thumbnails.py | 对齐文件缩略图缓存生成器 ThumbsCreator |
manual.face_viewer.frame |
face_viewer/frame.py | 下方"人脸视图"画布 FacesFrame |
manual.face_viewer.interact |
face_viewer/interact.py | 人脸视图的鼠标悬停与活动帧交互 |
manual.face_viewer.viewport |
face_viewer/viewport.py | 人脸视图可视区域/视口处理 |
manual.frame_viewer.control |
frame_viewer/control.py | 上方"帧视图"的导航 Navigation 与背景图 |
manual.frame_viewer.frame |
frame_viewer/frame.py | 上方主显示区 DisplayFrame 与动作切换 |
manual.frame_viewer.editor.* |
editor/ 下 bounding_box.py / extract_box.py / landmarks.py / mask.py |
五个编辑器的具体实现 |
从源码结构看,整个 GUI 是一个垂直 ttk.PanedWindow:上半部分是帧视图(DisplayFrame),下半部分是人视图(FacesFrame),右侧再挂一个随编辑器切换的控制面板(manual.py 的 _create_containers)。窗口初始几何为 940×600 并尝试全屏(manual.py),标题为 "Faceswap.py - Visual Alignments"。
启动流程:两条后台线程 + 对齐器管线
Manual.__init__(manual.py)的初始化顺序揭示了该工具的并发设计:
- 输入校验:
_validate_non_faces排除人脸文件夹输入; - tkinter 初始化:解绑
TButton/TCheckbutton/TRadiobutton的<Key-space>(防止空格触发按钮而不是播放控制),设置窗口几何与标题; - 并行启动两条后台线程:
Aligner:加载三个对齐插件并初始化推理管线(通常最耗时,因此最先启动);FrameLoader:用lib.image.SingleFrameLoader打开视频或帧文件夹。若输入是文件夹,会把"不在对齐文件中的帧"加入 skip 列表(manual.py),只遍历对齐文件覆盖到的帧;
- 加载人脸:
DetectedFaces.load_faces()将 alignments JSON 反序列化为DetectedFace对象列表(detected_faces.py); - 主线程等待:
_wait_for_threads每 1 秒轮询两条线程的is_initialized,全部就绪后把DetectedFaces挂到 Aligner 上,并在视频元数据缺失时把pts_time/keyframes写回对齐文件,避免下次再全量扫描视频(manual.py); - 生成缩略图:
ThumbsCreator检查对齐文件是否已有缩略图缓存,没有(或传了-t)则后台多线程重建(manual.py)。
三个对齐器与归一化
Aligner(manual.py)在后台线程中依次初始化三个插件:
for plugin in ("cv2-dnn", "FAN", "HRNet"):
self._aligners[plugin] = Align(plugin, normalization="hist")()
- cv2-dnn:OpenCV DNN 的人脸检测(快,用于快速加框);
- FAN / HRNet:68 点 landmark 对齐模型,用户可在 GUI 中切换当前使用哪一个。
Aligner.get_landmarks 的调用链是:取 TkGlobals.current_frame 的当前帧图像 → 用指定对齐器对该帧上某个人脸的检测框执行推理(passthrough=True)→ 返回 68 点 landmarks_xy(manual.py)。这正是"用户手动画框 → 自动算 landmarks"功能背后的实现。
归一化方法可在 GUI 调整,set_normalization_method 会同步更新所有三个对齐器的 handler(manual.py)。editor/bounding_box.py 帮助文本列出了全部四种取值:
hist(直方图均衡,默认)mean(均值归一化)clahe(自适应直方图均衡)none(不做归一化)
对低质量、光照不均的视频,切换归一化方式能明显改善自动 landmarks 的稳定性。
上方帧视图:五个动作(Editor)
DisplayFrame(frame_viewer/frame.py)左上是主画面。动作列表硬编码为五个(frame_viewer/frame.py):
self._actions = ("View", "BoundingBox", "ExtractBox", "Landmarks", "Mask")
它们分别映射到 F1–F5 快捷键,切换后右侧面板(_Options)会随之换成对应编辑器的控制项(manual.py)。编辑器实现都继承自 editor/_base.py 的 Editor 基类,并在 editor/init.py 统一导出,其中还包含 Mesh(landmarks 网格查看器)与 View:
from ._base import View
from .bounding_box import BoundingBox
from .extract_box import ExtractBox
from .landmarks import Landmarks, Mesh
from .mask import Mask
- View / Mesh:只读查看帧;
Mesh会把 68 点 landmarks 以连线网格叠加显示,便于肉眼检查对齐质量(数据来自lib.align的LANDMARK_PARTS,见 editor/landmarks.py); - BoundingBox(editor/bounding_box.py):鼠标拖拽修改/添加人脸检测框;框选完成后由
Aligner自动重算该框的 landmarks。面板中同时暴露归一化下拉框(四种模式见上节); - ExtractBox(editor/extract_box.py):绘制自定义框,用于"抽脸"预览——即模拟 extract 步骤对该框的裁剪/对齐效果,确认抽取区域是否符合预期;
- Landmarks(editor/landmarks.py):拖动 68 个关键点中的某一个来精修 landmark,右侧面板提供按
LandmarkType分组的点选择; - Mask(editor/mask.py):画笔式编辑人脸 mask,支持
B(Brush)、D(Drag)、E(Erase)操作与Z(Zoom)缩放,[/]调整画笔粗细——这些是源码注释中明确保留给 editor 模块的按键(manual.py)。
导航条(播放/进度条)由 frame_viewer/control.py 的 Navigation 类管理:进度条的 to 值随过滤模式动态变化(nav_scale_callback),播放状态用独立的 tk.BooleanVar 跟踪。
下方人视图:缩略图画布与交互
FacesFrame(face_viewer/frame.py,约 800 行)是下方的网格画布,把当前过滤条件下每一帧的每个人脸缩略图排成网格:
- 缩略图不是每次运行时现场解码视频,而是缓存在对齐文件里的低分辨率 jpg(
-t强制重建的对象)。ThumbsCreator的线程策略见 thumbnails.py:CPU 核数大于 2 时取cpu_count - 2,并向上取到至少 32 线程;视频输入加--single-process时强制单线程——这正是 CLI 中-s选项存在的原因; - 缩略图尺寸由右侧面板的 "Face Size" 下拉控制,取值为
Tiny / Small / Medium / Large / Extra Large,默认Medium(manual.py); - face_viewer/viewport.py 负责把画布可视区域(视口)内的帧正确渲染出来,避免全量重绘;
- face_viewer/interact.py 处理鼠标悬停预览与"活动帧"——点击网格中的某个脸即可在上方帧视图定位到对应帧。
帧过滤模式:快速定位"问题帧"
DetectedFaces 内建的 Filter 子系统(detected_faces.py)提供五种导航过滤模式,按 F 键循环切换:
| 过滤模式 | 判据(源码) |
|---|---|
| All Frames | 不过滤 |
| No Faces | 该帧没有任何检测到的人脸 |
| Has Face(s) | len(frame_faces) > 0 |
| Multiple Faces | len(frame_faces) > 1 |
| Misaligned Faces | 任一 face.aligned.average_distance > 阈值 |
其中 Misaligned Faces 最实用:它基于对齐后的平均残差距离筛出"对齐得不像"的帧。距离阈值由全局变量 filter_distance 控制,默认 10(globals.py 中 tk.IntVar(value=10)),在过滤模式下会显示一个专门的阈值滑块(Navigation.pack_threshold_slider,见 frame_viewer/control.py)。过滤条件改变后,进度条总帧数、人视图网格会随之刷新。
键盘快捷键全表
Manual._handle_key_press(manual.py)集中定义了全局快捷键,其中数字键支持小键盘(KP_ 前缀自动剥离):
| 键 | 功能 |
|---|---|
z / x |
上一帧 / 下一帧 |
space |
播放/暂停 |
home / end |
跳到第一帧 / 最后一帧 |
up / down |
人视图画布上/下滚动 |
prior / next(PageUp/PageDown) |
人视图画布翻页 |
f |
循环切换过滤模式 |
F1–F5 |
切换 View / BoundingBox / ExtractBox / Landmarks / Mask |
F9 / F10 |
切换人视图上的标注显示 |
c / v |
把当前帧的人脸复制(copy)到上一帧 / 下一帧 |
ctrl+s |
保存对齐文件 |
r |
把当前帧回退到已保存版本 |
Delete、[ / ]、B/D/E/M |
保留给编辑器:删除人脸 / 调画笔粗细 / 画笔操作 |
源码特别注明 Windows 下 Alt 修饰键不可靠,因此所有组合键只用 shift / ctrl(manual.py)。
对齐文件的数据流:加载、更新、保存、回退
DetectedFaces(detected_faces.py)是数据中枢,内部分三个子系统:
_DiskIO:load_faces()从.json反序列化出每帧的DetectedFace列表;save()(Ctrl+S 触发)把内存中所有编辑写回文件;revert_to_saved(frame_index)(r键)把某帧恢复到上次保存的状态;extract()支持从工具内直接调用抽取流程;FaceUpdate:GUI 中任何一次编辑(移动框、拖动 landmark、涂 mask、Delete删脸、c/v跨帧复制)都会更新对应DetectedFace,并把帧索引记入_updated_frame_indices(is_frame_updated可查询);Filter:前文所述的五种过滤模式,按TkGlobals中当前filter_mode字符串分派。
三个 tkinter 布尔变量驱动界面状态(detected_faces.py):tk_unsaved(自上次保存后有修改)、tk_edited(需要触发 GUI 重绘)、tk_face_count_changed(人脸数量变化,需要重画网格)。标题栏/状态区的"未保存"提示即由 tk_unsaved 驱动。
此外 DetectedFaces.available_masks 会统计对齐文件中各 mask 类型覆盖的人脸数,用于 mask 编辑器面板中列出可选 mask;save_video_meta_data 则把视频的 pts_time 与关键帧列表持久化进对齐文件(detected_faces.py),保证二次打开同一视频时无需重新扫描。
小结
docs/full/tools/manual.rst 给出的 API 目录其实就是 Manual 工具的全部骨架:manual.py 负责入口、容器与 Aligner/FrameLoader 两条后台线程;detected_faces.py 负责对齐文件 IO、编辑与过滤;globals.py 承载跨组件的 tkinter 变量;thumbnails.py 负责缩略图缓存;face_viewer/ 三件套负责下方人脸网格;frame_viewer/ 的 control、frame 与 editor/ 四个编辑器模块负责上方帧画面与五种编辑动作。掌握 python tools.py manual -f <video> 的四个参数、F1–F5 编辑器切换、f 循环过滤(尤其 Misaligned Faces + 距离阈值)以及 Ctrl+S / r 的保存与回退,就足以完成一次完整的对齐文件精修;而当自动 landmarks 不理想时,记得先检查 BoundingBox 面板里的归一化选项,它直接决定 Aligner 推理输入的质量。
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 StartedRust0624
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