简介:GFPGAN 旧照片修复算法的完整工程包,面向对人工智能图像修复感兴趣的开发者、研究者及有老照片还原需求的用户。工程基于 VS2019 构建,已集成必要的依赖与模型权重,下载解压后即可打开运行,省去繁琐的环境配置。压缩包共 445 个文件,约 255.35MB,主要包含 hpp/h 头文件、cpp 源文件、lib/obj 等编译产物、dll 运行库以及 param/bin 等模型参数文件,结构清晰,便于二次开发与学习。已有 1470 人浏览学习。通过该工程,用户可直接体验人脸修复、超分辨率重建等典型流程,也可基于源码深入理解 GAN 在图像增强中的实现细节,是实践深度学习落地应用的优质参考。
1. GFPGAN.rar 整个工程:一次拆包开始的实战课
拿到一个GFPGAN.rar 整个工程,说是“整个工程”,其实打开之后往往是一堆.py文件、几个.yaml配置、一个weights文件夹,里面躺着 200MB 以上的 pth 权重,还有个 README。这套东西在 AI 绘画圈子里几乎是人脸修复的默认答案:GAN 生成器负责把人脸从模糊、马赛克、低分辨率拉回清晰状态,同时保留身份特征不漂移。常见用途是给老照片去糊、给 AI 生成的人脸二次精修、给视频抽帧后的人脸批量提清晰度。
但真正动手跑过的人都知道,这个“整个工程”比想象中更容易卡住:不是缺库就是版本冲突,明明照着 README 写了三行代码却报RuntimeError: CUDA out of memory,或者输出图片一片漆黑。这篇博文不做背诵式介绍,按一线工程师拆解开源工程的习惯,从压缩包落地、依赖还原、推理链路验证、参数边界、批量应用和排错这几个层次,把GFPGAN.rar 整个工程背后那套东西讲透。目标是:你拿到任意一份 GFPGAN 源码包,能在半小时内让它跑出第一张图,并且知道它为什么能跑。
2. 先拆包再谈跑通:rar 工程压缩包的结构与校验
2.1 rar 解压不只是右键解压:检查压缩包完整性与目录结构
常见做法是拿到GFPGAN.rar直接右键解压到当前文件夹,但这在工程场景下不够严谨。rar 在传输过程中可能截断,GFPGAN 的weights目录里放着GFPGANv1.4.pth(约 348MB)这类大文件,如果 rar 分卷或者从网盘下载时有丢包,解压时可能报 CRC 错误,也可能静默解出损坏的权重文件。后者更危险——文件在,但模型加载后推理结果全是噪声。
我一般习惯在解压前先验证完整性,Windows 下可以用 WinRAR 的“测试压缩文件”功能,命令行则走unrar t。Linux 下如果安装了unar,也可以快速校验:
# 测试压缩包完整性,不解压 unrar t GFPGAN.rar # 如果包内目录层级混乱,先列出内容再决定解压方式 unrar lb GFPGAN.rar | head -50unrar t会逐个文件进行 CRC 校验,输出All OK才说明压缩包本体没问题。unrar lb是只列出文件路径不实际解压,适合先看一眼包内是不是套了一层GFPGAN-master/之类的父目录。如果套了一层,直接解压后所有路径前面都会多一个父目录,影响后续工作目录设置。
解压本身用unrar x会保留压缩包内的目录结构,unrar e则会把所有文件平铺到当前目录,工程场景下不要用e模式,因为 GFPGAN 有inference_gfpgan.py与gfpgan/包目录同名的风险,平铺会导致 import 失败。
2.2 压缩包内整个工程的标准骨架
解压后先别急着跑代码,先对着目录结构确认这是完整工程还是被裁剪过的“伪完整”。一份标准的 GFPGAN 工程应该包含以下部分:
GFPGAN/ ├── gfpgan/ # 核心 Python 包 │ ├── archs/ │ │ └── gfpganv1_arch.py # 生成器模型结构 │ ├── models/ │ ├── utils.py │ └── __init__.py ├── inference_gfpgan.py # 推理入口脚本 ├── options/ │ └── inference_gfpgan.yml # 推理参数配置 ├── weights/ # 预训练权重目录 │ ├── GFPGANv1.4.pth │ ├── GFPGANv1.3.pth │ └── detection_Resnet50_Final.pth ├── experiments/ │ └── pretrained_models/ ├── inputs/ # 默认输入图片目录 ├── results/ # 默认输出目录 ├── requirements.txt ├── setup.py └── README.md最关键的三个文件是inference_gfpgan.py、gfpgan/archs/gfpganv1_arch.py和weights/下的权重。inference_gfpgan.py是推理入口,它定义了命令行参数如何映射到模型加载和推理流程。gfpganv1_arch.py定义了GFPGANv1这个 PyTorch 网络的层结构,包括退化清除分支(U-Net 风格)与先验分支(基于人脸解析图)怎么融合。如果包内没有gfpgan/目录而是直接散落.py文件,那说明这个 rar 是从别处拷贝的源码而非官方打包,需要额外确认依赖版本。
权重文件的大小也是判断完整度的直观指标:GFPGANv1.4.pth一般在 300~400MB 之间,如果解压出来只有几 MB,多半是源文件被损坏,或者是经过哈希截断的残次品,不用继续尝试。
3. 跑通 GFPGAN 推理链路:环境、模型与第一张修复图
3.1 环境搭建:依赖版本踩过的三个深坑
GFPGAN 的依赖不多,requirements.txt里核心是torch、torchvision、opencv-python、basicsr、facexlib、gfpgan自身。但版本组合不对,跑起来就各种诡异报错。我自己实践下来,最容易出问题的三个点是:
PyTorch 与 CUDA 版本匹配。如果你机器上有 CUDA 11.8,安装 torch 时如果直接pip install torch默认拿到的是 CPU 版本(在部分镜像源下),推理会慢得让人失去耐心。正确做法是走 PyTorch 官方索引:
pip install torch==2.0.1 torchvision==0.15.2 --index-url https://download.pytorch.org/whl/cu1182.0.1搭配cu118是与basicsr兼容性较好的组合,新版本 torch 2.1+ 也可以跑,但部分环境里会遇到basicsr内部torchvision.transforms.functional_tensor被移除导致的 import 错误,不建议在新手上路阶段给自己加难度。
basicsr和facexlib必须使用源码安装。GFPGAN 官方 README 里明确建议pip install basicsr -i https://pypi.org/simple和pip install facexlib -i https://pypi.org/simple,但实际执行时,由于依赖链中torchvision的版本约束松散,可能装到与 torch 2.0 不兼容的版本。我一般会指定 GitHub 源码:
pip install git+https://github.com/xinntao/BasicSR.git pip install git+https://github.com/xinntao/facexlib.git这两者的源码安装会实时拉取最新代码,规避 PyPI 上老版本对torchvision.models里AlexNet权重加载方式变更的适配问题。
FaceXLib 的权重下载被墙或超时。第一次跑人脸检测时,facexlib会尝试从 GitHub Release 下载detection_Resnet50_Final.pth(约 108MB)。这个下载不是通过weights/目录走,而是默认缓存在~/.cache/facexlib/。如果下载失败,后续会报FileNotFoundError,但报错信息里不会告诉你它在下载。所以工程里如果把facexlib的权重也一并打包放进 rar,就非常省事,只是需要手动指定权重路径。我通常先把权重解压后放到项目根下的weights/,然后设置环境变量让 FaceXLib 优先读这里:
export FACE_XLIB_CACHE_DIR=/path/to/GFPGAN/weights/facexlib这个变量名是 facexlib 源码里实际读取的缓存根目录,指向了包含detection_Resnet50_Final.pth的目录后,推理时就不会再去外网下载。
3.2 最小推理命令:一张图从输入到输出的全流程
环境就绪后,跑通整个工程的最小命令如下:
python inference_gfpgan.py \ --upscale 2 \ --version 1.4 \ --source inputs/old_photo.jpg \ --bg_upsampler realesrgan \ --bg_tile 400 \ --suffix old_photo_restored \ --output results逐项拆解这些参数的实际作用:
--upscale 2:超分辨率放大倍数。GFPGAN 内部默认会先做人脸检测与对齐,人脸区域裁剪后送入生成器,输出的高清人脸贴回原图时需要按这个倍率缩放。设为 1 时人脸区域基本只做修复不做放大,背景保持原始分辨率。--version 1.4:指定使用GFPGANv1.4.pth权重。v1.2、v1.3、v1.4 三版权重在训练数据与网络结构上有细微差别,v1.4 是官方推荐版本,对真实老照片的泛化更好;v1.3 更偏合成降质数据。--bg_upsampler realesrgan:背景增强器。传入realesrgan时,代码会用 Real-ESRGAN 对背景(非人脸区域)做放大增强,这对整张照片效果提升非常明显,但显存消耗也会上升。如果显存紧张,可以设为None,表示只修脸不改背景。--bg_tile 400:背景增强时 Real-ESRGAN 的分块大小。背景分辨率高时,直接整图推理会爆显存,分块(tile)把图片切成 400x400 的小块逐步推理再拼回,防止 OOM。调小这个值(如 200)能进一步降低显存峰值,但拼接痕迹会略微明显。--suffix:输出文件名的后缀,便于区分不同参数下的结果。--output:输出目录。默认是results,如果目录不存在,脚本会自动创建。
执行过程中,STDOUT 会打印face detect的日志,说明人脸检测模型已经成功加载,随后打印当前检测到的人脸数量与置信度。如果输入图里没有人脸,脚本会直接跳过人脸修复,只对背景做超分(在--bg_upsampler启用时),或者直接原样输出。看到输出图里人脸没变化,第一反应不应该是代码坏了,而是确认原图里是否真的有人脸、人脸分辨率是否低于 GFPGAN 的可检测下限(一般小于 16x16 像素的人脸检测不到)。
3.3 weights 何时缺失:哪些情况下可以自己下载
工程压缩包里如果没有weights/GFPGANv1.4.pth,运行时会有非常醒目的报错:FileNotFoundError: No such file or directory: 'weights/GFPGANv1.4.pth'。这种缺失不用慌,因为权重文件本身是通过开源协议发布的,可以从官方 GitHub Release 自行下载,然后把 pth 文件放进weights/目录即可。注意版本要与--version参数一致,v1.4与v1.2的模型结构不完全一致,混用会在load_state_dict时报Missing key(s) in state_dict。
4. GFPGAN 的代码边界:哪些参数值得动,哪些是误区
4.1--only_center_face、--aligned和--extensions:人脸修复的控制开关
inference_gfpgan.py提供了多个针对人脸处理方式的开关,参数文档往往只有一句话,但实际使用差别很大。
| 参数 | 可选值 | 行为差异 | 适用场景 |
|---|---|---|---|
--only_center_face | False/True | False时修复图中所有检测到的人脸;True时只修复最靠近画面中心的那张脸 | 多人合照但只想修主体人物 |
--aligned | False/True | True时跳过人脸检测,假设输入图已经是裁剪对齐后的人脸图 | 配合人脸裁剪脚本做批处理时使用 |
--extensions | 字符串列表,如['jpg', 'png'] | 指定输入目录下要处理的图片扩展名 | 目录中混有.jpeg、.bmp时按需扩展 |
以--only_center_face为例,源码内部是对检测出的人脸框计算几何中心到图像中心的欧氏距离,取最小值那张脸进行修复。多人合照中画面边缘的人脸即使再模糊也不会被处理。这个开关在旧照片修复场景下容易踩坑:如果主体人脸不在画面中心,输出结果会让人觉得“该修的脸没修”。
--aligned的语义更微妙。正常推理链路中,GFPGAN 会对检测到的人脸做仿射变换校准——将人脸关键点对齐到模板位置,再送入网络,最后把修复结果做逆变换贴回原图。--aligned=True跳过了检测与对齐两个环节,直接把输入当作对齐好的标准人脸图。所以如果你拿一张完整的老照片(含背景)且设置--aligned,输出会是变形的、不受控的结果。
4.2--bg_upsampler与--face_enhance的功能边界
很多人分不清--bg_upsampler和--face_enhance的作用范围,这两者的命名确实容易混淆。
--bg_upsampler:作用于背景(非人脸区域)。可传realesrgan、realersrnet或None。传None时背景不做任何处理,只把人脸修复结果贴回原分辨率背景上。--face_enhance:没有参数值,写了就启用。它表示人脸区域先由 GFPGAN 修复,再额外由一个 face enhancement 模型(GPEN 或 RestoreFormer)进行二次增强。
从执行流程看,--face_enhance生效时,推理会变成两个阶段:GFPGAN 修复 -> 贴回原图 -> 再次裁剪该人脸 -> 送入增强模型 -> 再次贴回。这会让单张图的处理时间翻倍甚至更久,但对人脸细节(毛孔、眉毛纹理)有肉眼可见的提升。对于 5 年以上经验的人来说,值得注意的细节是--face_enhance与--bg_upsampler realesrgan同时启用时,两个模型都会占用显存,一张 1920x1080 的图在 8GB 显存下容易 OOM,解决办法是先把--bg_tile调低到 200 左右,或者干脆先关掉--face_enhance分两步执行。
4.3 常见误用:把 GFPGAN 当通用超分模型用
这是 GFPGAN 最有辨识度的边界。GFPGAN 不是通用超分模型,它只负责把人脸区域从“有降质的人脸”变为“清晰的人脸”,背景的超分来自--bg_upsampler背后的 Real-ESRGAN。如果只传--bg_upsampler None,一张风景图的输出基本是原图直接复制回来。不少新手把老照片整张丢进去,发现背景糊得跟输入一样,就判定模型无效,实际上是把两个模块的职责范围搞混了。
同样,GFPGAN 对输入人脸的分辨率也有限制。内部实现中,检测到的人脸会先被缩放到 512x512 再送入网络。所以一张 1920x1080 的照片中,人脸区域只有 40x40 像素,放大到 512 后本身的底层信息已经不足以支撑高质量修复,输出只能算是“脑补”,细节会偏平滑。真正适合 GFPGAN 的输入人脸,在原始图像中至少要有 100x100 以上像素。低于这个阈值,正常做法是先走一遍 Real-ESRGAN 整体超分,把脸放大到可修复范围,再交给 GFPGAN。
5. 批量推理与高清化配合:从单张图片到真实工作流
5.1 用脚本改写inference_gfpgan.py:目录级批处理
官方inference_gfpgan.py的--source参数虽然支持传入目录,但处理逻辑较为基础:遍历目录下匹配--extensions的文件,逐张推理。这里有一个隐藏的限制——它不支持递归子目录。如果素材按train/class_a/xxx.jpg到train/class_z/xxx.jpg这种分类目录存放,直接传--source train只能处理train/第一层的图片。常见做法是写一个小的 shell 脚本,配合find展开文件列表:
find /path/to/inputs -type f \( -name "*.jpg" -o -name "*.png" \) | while read img; do python inference_gfpgan.py \ --upscale 2 \ --version 1.4 \ --source "$img" \ --bg_upsampler realesrgan \ --bg_tile 400 \ --suffix restored \ --output /path/to/results echo "done: $img" done逐张调用 Python 进程虽然慢,但好在每张图有独立的日志输出,哪张图失败能精确定位。如果要追求吞吐,可以改为在 Python 内部用torch.no_grad()包住整个循环,一次加载模型处理多张图,这需要直接改inference_gfpgan.py的逻辑,把--source目录下所有图片路径预处理后传给循环。
批处理场景下最容易忽视的一点是--suffix重名覆盖。不同子目录下可能都存在photo.jpg,输出到同一个results目录时会互相覆盖。解决方式是在脚本里为每个子目录单独建输出子目录,或利用--suffix拼接输入文件名中的唯一特征。
5.2 先超分再修复:把 GFPGAN 放进两级流水线
真实老照片的修复工作流中,GFPGAN 很少单枪匹马出场。我一般是这样设计流水线的:
- 第一步用 Real-ESRGAN 做 2x 或 4x 整体超分,把背景和前脸上采样到足够分辨率。
- 第二步用 GFPGAN 只做人脸修复(
--bg_upsampler None --upscale 1),避免二次超分带来的重复处理成本。 - 第三步如果需要,导入图像编辑软件里做色彩校正与瑕疵手工清理。
这里的思路是:GFPGAN 的修复能力在人脸区域,而背景细节的恢复交给专门的超分模型,两者分工。如果反过来先 GFPGAN 再超分,人脸区域会被超分模型进一步涂抹,GFPGAN 刚修好的脸部纹理反而被破坏。先后顺序不是随意的,是一个 pipeline 顺序决定最终画质的典型例子。
GPU 资源允许时,--bg_upsampler realesrgan可以在一步内完成超分与修复,但这要求人脸区域的原始信息占比较大。人脸在画面中占比较小时,整图超分出来的背景不会特别自然。
5.3 视频抽帧人脸修复的注意点
视频场景下,GFPGAN 的用法是:ffmpeg抽帧 -> 批处理 GFPGAN ->ffmpeg重组视频。但这里有个实际项目里会反复出现的问题——相邻帧人脸修复结果不一致导致闪烁。GFPGAN 是单帧模型,不引入时间维度的约束,因此同一张脸在不同帧里可能得到略微不同的修复纹理。常见解法是把视频先做场景切分,每个场景内取关键帧修复,再用ffmpeg的minterpolate做光流插帧,减少闪烁感。或者直接接受轻微闪烁,因为对大部分档案视频,人脸区域的微小闪烁远没有分辨率提升带来的观感收益明显。
6. 遇到黑图、灰度图和爆显存时的定位路径
6.1 RGB 顺序问题导致输出发绿发紫
有人用cv2.imread自行写脚本读入图片后送进 GFPGAN,输出图颜色整体偏绿或偏紫。这几乎一定是 BGR 与 RGB 通道顺序错乱导致的。inference_gfpgan.py内部使用cv2.imread读图,并用cv2.cvtColor(img, cv2.COLOR_BGR2RGB)做转换,生成器内部处理的是 RGB,保存时再转回 BGR。如果你自己写脚本直接读图(尤其用imageio或matplotlib读入),拿到的就是 RGB 顺序,但直接用 GFPGAN 的enhance接口,最终输出会以 BGR 顺序保存,颜色自然错乱。
6.2 全黑输出的根源:归一化方式不匹配
GFPGAN 内部对人脸区域的归一化遵循 Real-ESGAN 系列的标准:像素值除以 255 后映射到[-1, 1],即x = x * 2 - 1。如果外部调用时只做x / 255.0而不做2x - 1的线性映射,网络输入的分布与训练时不一致,输出大概率是噪声或全黑图。这不是模型损坏,是预处理管道问题。检查顺序是:先确认输入张量归一化范围和值域分布,其次才去怀疑权重文件损坏。
6.3 黑屏与 OOM 的显存排查
CUDA out of memory出现时,先看--bg_tile。背景超分是显存消耗大户,一张 4K 图直接送 Real-ESRGAN 会瞬间吃掉 6~8GB 显存。调低--bg_tile到 200 或 100,峰值显存会显著下降。另外一个较少人注意的配置是torch.backends.cudnn.benchmark,GFPGAN 推理脚本里没有显式设置它,但 PyTorch 默认在输入尺寸变化时会重新选择卷积算法,batch 处理时多张不同分辨率的图交替输入会造成显存碎片化。可以用环境变量PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True缓解,实测对 GFPGAN 这种交替加载多个模型(检测模型、生成器、背景超分)的场景有效。
6.4 验证推理结果的简单手段:把中间特征图导出来
当输出结果看起来没有达到预期时,不要只盯最终输出图,可以把archs/gfpganv1_arch.py中生成器的中间层输出导出为 numpy 数组,快速判断人脸特征是否被有效提取。具体做法是在forward方法里把self.face_generator某一层的输出x用x.detach().cpu().numpy()存下来,再去看它的数值分布。如果中间特征图全为常数或方差极小,说明输入的人脸质量过低或归一化有问题;如果特征图有明显的边缘激活,说明网络前向计算正常,问题出在后处理或贴回原图阶段。这比反复调整超参数更能快速定位方向。
本文还有配套的精品资源,点击获取