简介:本资源为基于Python深度学习框架的GFPGAN图片修复算法实现源码,面向具备一定Python编程与深度学习基础、关注图像修复与生成对抗网络应用的开发者与研究者,可用于老旧照片修复、面部图像增强及数字取证等场景。压缩包共62个文件,约6.22MB,其中26个py源码文件承载算法核心实现与训练推理逻辑,8个yml与2个yaml配置文件负责参数与实验设置,5个md文档提供说明与常见问题,另有png、jpg示例图、mdb数据集、pth权重及json、cfg等辅助文件,目录涵盖archs、models、utils、options、experiments、tests等模块,结构清晰。已有430人学习下载。通过该源码可深入理解GFPGAN生成器与判别器的协作机制、StyleGAN2与ArcFace等网络结构,并借助训练脚本、推理脚本与测试用例完成模型复现、效果对比与二次开发,是图像修复方向较为完整的实践参考。
1. GFPGAN 图片修复:从一张糊脸到可交付源码的完整路径
手里有一批老照片,人脸模糊、噪点重、还有压缩块效应,用传统锐化+降噪调半天,脸还是像蒙了一层雾。GFPGAN(Generative Facial Prior GAN)就是冲着这个场景来的:它把 StyleGAN 学到的「人脸先验」塞进修复网络里,让模型在补细节时知道人脸该长什么样,而不是瞎猜。标题里的「基于 Python 深度学习」不是装饰——整套推理和微调都跑在 PyTorch 上,源码结构清晰,适合拿来改。这篇笔记面向两类人:想跑通 GFPGAN 图片修复的新手,和想把它接进自己业务流程、需要知道参数边界和踩坑点的熟手。下面从环境、推理、训练、避坑一路写到进阶技巧,代码都能直接抄。
2. GFPGAN 源码拆解:三个网络各干什么活
2.1 退化去除模块与生成先验的分工
GFPGAN 的源码里,核心是三个部分:退化去除模块(Degradation Removal Module)、生成先验模块(Generative Facial Prior)和隐藏特征解码器。退化去除模块负责把低质输入往「干净」方向拉,它不直接输出图像,而是输出一组特征;生成先验模块基于 StyleGAN2 的架构,把随机噪声和退化特征一起映射成潜码,再解码出人脸细节;最后隐藏特征解码器把生成先验的输出和退化去除模块的特征融合,重建出高清图。
为什么这么设计?如果只用普通超分网络,模型没见过的人脸结构它补不出来,只能靠像素邻域插值,结果就是「塑料脸」。GFPGAN 的生成先验相当于一个「人脸记忆库」,在潜空间里找最接近的合理人脸,再往输入上贴。源码里GFPGANv1.3.py和GFPGANv1.4.py的区别主要在通道数和残差块数量,v1.4 更重但细节更好。选版本时看你的显存:v1.3 在 6GB 显存上能跑 512×512,v1.4 建议 8GB 起。
2.2 从权重加载到推理输出的最小命令
先装环境。Python 3.8 是官方测试过的版本,PyTorch 选 1.13 或 2.0 都行,但注意 CUDA 版本要和驱动匹配。下面这套命令我一般在 Linux 和 Windows WSL 下都用:
conda create -n gfpgan python=3.8 -y conda activate gfpgan pip install torch==2.0.1 torchvision==0.15.2 --index-url https://download.pytorch.org/whl/cu118 pip install basicsr facexlib gfpgan pip install opencv-python numpy pillowbasicsr是 GFPGAN 依赖的基础超分库,facexlib提供人脸检测和对齐。装完后如果basicsr报ImportError: cannot import name 'degradations',那是版本冲突,降级到basicsr==1.4.2通常能解决。
推理脚本最小化写法:
import cv2 from gfpgan import GFPGANer # 初始化修复器,model_path 指向下载好的权重 restorer = GFPGANer( model_path='experiments/pretrained_models/GFPGANv1.4.pth', upscale=2, # 输出放大倍数,2 表示长宽各乘 2 arch='clean', # 使用 clean 架构,对应 v1.4 channel_multiplier=2, bg_upscale=2 # 背景也做超分,避免人脸清晰背景糊 ) img = cv2.imread('old_photo.jpg') # 第三个参数控制是否对齐人脸,True 会先检测再修复 _, _, output = restorer.enhance(img, has_aligned=False, only_center_face=False, paste_back=True) cv2.imwrite('restored.jpg', output)upscale不是越大越好,2 倍在多数老照片上够用,4 倍会放大生成先验的伪影。bg_upscale建议和upscale一致,否则人脸和背景清晰度割裂。only_center_face在合影场景要设 False,否则只修中间那张脸。
2.3 人脸检测与对齐的隐藏参数
facexlib的人脸检测器默认用 RetinaFace,它有个confidence_threshold藏在FaceRestoreHelper初始化里,默认 0.5。如果照片里人脸小或者侧脸多,检测不到就不会修复。我一般会在restorer.enhance之前手动调一下:
from facexlib.utils.face_restoration_helper import FaceRestoreHelper helper = FaceRestoreHelper( upscale_factor=2, face_size=512, crop_ratio=(1, 1), det_model='retinaface_resnet50', save_ext='png', use_parse=True, # 启用人脸解析,分离五官区域 device='cuda' )face_size=512是 GFPGAN 训练时的输入尺寸,改成 256 会掉点,改成 1024 显存翻倍但收益很小。use_parse=True会多跑一个解析网络,显存紧张时可以关,但嘴角和眼睛的修复质量会降。检测阈值在helper.face_det里,可以设helper.face_det.det_thresh = 0.3来召回更多小脸。
3. 训练自己的 GFPGAN:数据、损失与显存控制
3.1 退化管道怎么造才像真实老照片
GFPGAN 官方训练用的是 FFHQ 数据集,但你要修的是自己的老照片,就得构造匹配的退化管道。源码里basicsr/data/degradations.py提供了random_mixed_kernels和add_jpeg_compression,但默认参数偏「合成退化」,和真实老照片的泛黄、划痕、噪点分布不一样。我一般会改三处:
第一,模糊核范围从[0.2, 3.0]扩到[0.2, 5.0],老照片的失焦更严重。第二,JPEG 压缩质量从[30, 95]改成[20, 80],因为很多老照片是扫描件,压缩痕迹重。第三,加一层颜色偏移,模拟泛黄:
import numpy as np def add_color_shift(img, shift_range=0.1): # img 是 RGB 浮点数组,范围 0-1 shift = np.random.uniform(-shift_range, shift_range, size=3) return np.clip(img + shift, 0, 1)这个函数放在退化管道最后一步,shift_range设 0.1 对应轻微泛黄,设 0.2 会偏蓝或偏红,看你的数据分布。注意别加高斯噪声后再加颜色偏移,顺序反了噪声会被颜色变换放大。
3.2 损失函数里哪几项不能关
GFPGAN 的损失由 L1 重建损失、感知损失(VGG 特征)、GAN 对抗损失和 ID 损失(人脸身份保持)组成。源码gfpgan/models/gfpgan_model.py里l_g_pix、l_g_percep、l_g_gan、l_g_id四个权重默认是 1.0、1.0、0.1、1.0。我的经验:ID 损失不能关,关了人脸会「换人」;GAN 损失权重超过 0.2 会出纹理伪影,低于 0.05 则细节不够锐。感知损失用 VGG19 的conv4_4层,别换成conv5_4,后者太高层,修复结果会偏「概念化」。
训练命令示例:
python -m torch.distributed.launch --nproc_per_node=2 --master_port=4321 gfpgan/train.py \ -opt options/gfpgan_v1.yml \ --launcher pytorchnproc_per_node是 GPU 数,单卡就写 1。options/gfpgan_v1.yml里batch_size_per_gpu默认 4,512×512 输入下 8GB 显存跑 2 比较稳。如果 OOM,先降batch_size,再降num_worker,别急着改网络通道数。
3.3 微调时冻结哪些层省显存
如果你只有几张到几十张老照片,全量微调会过拟合。常见做法是冻结生成先验模块的前半部分,只训退化去除模块和解码器。在gfpgan_model.py的optimize_parameters里加一行:
# 冻结 StyleGAN2 的前 6 个 style block for name, param in self.gfpgan.generator.named_parameters(): if 'style' in name and int(name.split('.')[1]) < 6: param.requires_grad = False这样可训练参数从 80M 降到 30M 左右,显存省 40%,训练时间减半。代价是生成先验的多样性下降,适合「只修某一类老照片」的场景。如果数据超过 500 张,还是全量微调效果好。
4. 避坑与排查:GFPGAN 跑不通的五个血泪现场
4.1 报错CUDA out of memory但显存明明够
现象:8GB 显存跑 512×512 推理,提示 OOM,但nvidia-smi显示只用了 3GB。原因:PyTorch 默认缓存分配器会预留显存,加上facexlib的人脸解析模型也占一份。解决:在推理前加torch.cuda.empty_cache(),并把bg_upscale设为 1,或者用--upscale 1先跑通再调大。
4.2 修复后的人脸「换人」了
现象:输入是张三,输出像李四。原因:ID 损失权重太低,或者输入人脸角度太大,生成先验「猜」了一个正脸。解决:把l_g_id从 1.0 提到 2.0,并在推理时设has_aligned=True先做对齐。如果还是换人,检查输入分辨率,低于 64×64 的人脸不要指望 GFPGAN,先做超分再修复。
4.3 输出图像有网格状伪影
现象:修复结果上有一格一格的纹理。原因:StyleGAN2 的channel_multiplier和训练时不一致,或者upscale设了 4 但模型只见过 2 倍退化。解决:推理参数和训练配置对齐,channel_multiplier用 2,upscale用 2。如果还有,在enhance后加一层双边滤波:
output = cv2.bilateralFilter(output, d=5, sigmaColor=20, sigmaSpace=20)d=5是邻域直径,sigmaColor和sigmaSpace控制颜色和空间平滑度,别调太大,否则人脸变糊。
4.4 背景比人脸还糊
现象:人脸清晰了,背景像油画。原因:bg_upscale没开,或者背景超分用的 Real-ESRGAN 权重没加载。解决:确认bg_upscale=2,并检查realesrgan包是否安装。如果背景是纯色,可以设bg_upscale=1省显存。
4.5 训练 loss 不降反升
现象:训练几个 epoch 后 L1 损失从 0.1 涨到 0.3。原因:学习率太大,或者 GAN 损失和重建损失打架。解决:把生成器学习率从1e-4降到5e-5,判别器从1e-4降到2e-5。如果还不行,先关 GAN 损失训 10 个 epoch,再打开做联合训练。
5. 进阶技巧:用分块推理修 4K 大图与效果验证
GFPGAN 直接跑 4K 图会 OOM,分块推理是常见解法。思路是把大图切成有重叠的小块,逐块修复再拼接。重叠区域取face_size的一半,即 256 像素,拼接时用加权平均消除接缝。
import numpy as np def tile_inference(restorer, img, tile_size=512, overlap=256): h, w = img.shape[:2] output = np.zeros_like(img, dtype=np.float32) weight = np.zeros((h, w, 1), dtype=np.float32) for y in range(0, h, tile_size - overlap): for x in range(0, w, tile_size - overlap): y2 = min(y + tile_size, h) x2 = min(x + tile_size, w) tile = img[y:y2, x:x2] _, _, restored = restorer.enhance(tile, has_aligned=False, paste_back=True) # 边缘权重衰减,避免拼接缝 mask = np.ones((y2-y, x2-x, 1), dtype=np.float32) if y > 0: mask[:overlap] *= np.linspace(0, 1, overlap)[:, None, None] if x > 0: mask[:, :overlap] *= np.linspace(0, 1, overlap)[None, :, None] output[y:y2, x:x2] += restored * mask weight[y:y2, x:x2] += mask return (output / np.maximum(weight, 1e-6)).astype(np.uint8)tile_size设 512 和模型输入一致,overlap设 256 保证人脸不会被切一半。如果图里人脸跨块,先用人脸检测框把图裁出来单独修,再贴回去,比纯分块稳。
效果验证别只看肉眼。我一般算两个指标:PSNR 和 LPIPS。PSNR 高于 28dB 算合格,LPIPS 低于 0.15 说明感知质量好。用piq库两行搞定:
import piq psnr = piq.psnr(restored_tensor, gt_tensor, data_range=1.0) lpips = piq.LPIPS()(restored_tensor, gt_tensor)没有 GT 图时,用facexlib的解析结果看五官区域是否对齐,对齐了基本不会太差。
最后说个习惯:我每次改完退化管道或损失权重,都会先跑 100 张验证集,看 PSNR 和 LPIPS 的分布,而不是盯着一张图调。GFPGAN 的玄学在于生成先验的随机性,单张图好不代表批量好。希望帮到你。
本文还有配套的精品资源,点击获取