基于Python深度学习的GFPGAN图片修复:从源码到实战

发布时间:2026/10/5 4:53:49
基于Python深度学习的GFPGAN图片修复:从源码到实战 简介本资源为基于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 图片修复从一张糊脸到可交付源码的完整路径手里有一批老照片人脸模糊、噪点重、还有压缩块效应用传统锐化降噪调半天脸还是像蒙了一层雾。GFPGANGenerative 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×512v1.4 建议 8GB 起。2.2 从权重加载到推理输出的最小命令先装环境。Python 3.8 是官方测试过的版本PyTorch 选 1.13 或 2.0 都行但注意 CUDA 版本要和驱动匹配。下面这套命令我一般在 Linux 和 Windows WSL 下都用conda create -n gfpgan python3.8 -y conda activate gfpgan pip install torch2.0.1 torchvision0.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那是版本冲突降级到basicsr1.4.2通常能解决。推理脚本最小化写法import cv2 from gfpgan import GFPGANer # 初始化修复器model_path 指向下载好的权重 restorer GFPGANer( model_pathexperiments/pretrained_models/GFPGANv1.4.pth, upscale2, # 输出放大倍数2 表示长宽各乘 2 archclean, # 使用 clean 架构对应 v1.4 channel_multiplier2, bg_upscale2 # 背景也做超分避免人脸清晰背景糊 ) img cv2.imread(old_photo.jpg) # 第三个参数控制是否对齐人脸True 会先检测再修复 _, _, output restorer.enhance(img, has_alignedFalse, only_center_faceFalse, paste_backTrue) 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_factor2, face_size512, crop_ratio(1, 1), det_modelretinaface_resnet50, save_extpng, use_parseTrue, # 启用人脸解析分离五官区域 devicecuda )face_size512是 GFPGAN 训练时的输入尺寸改成 256 会掉点改成 1024 显存翻倍但收益很小。use_parseTrue会多跑一个解析网络显存紧张时可以关但嘴角和眼睛的修复质量会降。检测阈值在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_range0.1): # img 是 RGB 浮点数组范围 0-1 shift np.random.uniform(-shift_range, shift_range, size3) 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_node2 --master_port4321 gfpgan/train.py \ -opt options/gfpgan_v1.yml \ --launcher pytorchnproc_per_node是 GPU 数单卡就写 1。options/gfpgan_v1.yml里batch_size_per_gpu默认 4512×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_alignedTrue先做对齐。如果还是换人检查输入分辨率低于 64×64 的人脸不要指望 GFPGAN先做超分再修复。4.3 输出图像有网格状伪影现象修复结果上有一格一格的纹理。原因StyleGAN2 的channel_multiplier和训练时不一致或者upscale设了 4 但模型只见过 2 倍退化。解决推理参数和训练配置对齐channel_multiplier用 2upscale用 2。如果还有在enhance后加一层双边滤波output cv2.bilateralFilter(output, d5, sigmaColor20, sigmaSpace20)d5是邻域直径sigmaColor和sigmaSpace控制颜色和空间平滑度别调太大否则人脸变糊。4.4 背景比人脸还糊现象人脸清晰了背景像油画。原因bg_upscale没开或者背景超分用的 Real-ESRGAN 权重没加载。解决确认bg_upscale2并检查realesrgan包是否安装。如果背景是纯色可以设bg_upscale1省显存。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_size512, overlap256): h, w img.shape[:2] output np.zeros_like(img, dtypenp.float32) weight np.zeros((h, w, 1), dtypenp.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_alignedFalse, paste_backTrue) # 边缘权重衰减避免拼接缝 mask np.ones((y2-y, x2-x, 1), dtypenp.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_range1.0) lpips piq.LPIPS()(restored_tensor, gt_tensor)没有 GT 图时用facexlib的解析结果看五官区域是否对齐对齐了基本不会太差。最后说个习惯我每次改完退化管道或损失权重都会先跑 100 张验证集看 PSNR 和 LPIPS 的分布而不是盯着一张图调。GFPGAN 的玄学在于生成先验的随机性单张图好不代表批量好。希望帮到你。本文还有配套的精品资源点击获取