资讯详情

GFPGAN老照片修复实战:从原理到Python批量处理

📅 2026/10/11 13:27:55 | 华诺云谱 👁 阅读
GFPGAN老照片修复实战:从原理到Python批量处理
简介基于GFPGAN算法的Python老照片修复设计源码面向图像处理开发者与有老照片修复需求的个人用户解决老照片模糊、人脸细节丢失、画质退化等问题GFPGAN作为泛用性人脸先验引导的修复网络结合生成对抗网络与人脸先验知识在修复时保持人脸特征清晰真实配合OpenCV、NumPy等库可形成完整修复流程。包内共51个文件压缩包约6.09MB以Python脚本、YAML配置、PNG/JPG样例图、Markdown文档为主。Python脚本覆盖推理、训练与工具函数YAML配置运行参数图片提供修复前后对比MD文档写明安装与使用方式另有预训练权重与测试数据结构清晰便于二次开发。目前已有491人学习浏览该项目可直接运行体验GFPGAN修复效果也能作为自建照片修复工具或相关实验的代码参考与文档支撑。1. 老照片修复为什么偏偏是GFPGAN拿一张十年前拍糊了的脸丢给普通锐化工具出来的效果不是“更清晰”而是“更脏”——边缘出现白边皮肤变成塑料噪点被放大成麻点。老照片修复的真正难点不是提分辨率而是要在放大两倍、四倍的同时把丢失的面部结构“补”回来并且补得自然。GFPGANGenerative Facial Prior GAN做的正是这件事它不是靠插值算法去猜像素而是用预训练的人脸先验知识去“重绘”一张脸所以修复结果在观感上会明显好于传统超分方案。这篇内容写给两类人一是手头有一批老照片、想本地批量修复的从业者二是想做图像修复产品原型、需要选型和落地的开发者。我会从GFPGAN的选型原理讲起给出能直接跑通的完整Python代码再把参数调试、批量处理、资源占用这些坑逐个拆开。整个过程不需要你有深度学习的理论底子但需要你习惯用命令行并且愿意折腾一两次环境。2. GFPGAN是什么从“超分辨率”到“人脸先验重绘”2.1 为什么传统超分模型在老照片上翻车老照片修复最常被拿来对比的模型是Real-ESRGAN它做的是通用的图像超分把低分辨率输入映射到高分辨率输出训练时用大量的退化模拟模糊、噪声、压缩痕迹。问题是老照片的退化不是单一的扫描噪点、化学药水污渍、冲印时的对焦不准这些退化叠加在一起Real-ESRGAN这类通用模型会倾向于“顺着纹理猜”结果是把噪点也当成纹理一起放大了。GFPGAN走了另一条路。它引入了一个关键概念人脸先验Facial Prior。模型在训练时不仅学“低清到高清”的映射还学“一张正常的人脸应该长什么样”。具体来说它用StyleGAN的预训练生成器作为先验知识库修复时先把输入图像编码到StyleGAN的隐空间latent space再在这个空间里找到最接近输入的人脸向量最后用生成器重建出高清人脸。这就像警察画嫌犯肖像不是把模糊照片硬放大而是根据轮廓特征“画”出一张符合人脸规律的高清画像。这个设计的直接好处是对于人脸区域修复结果不会出现结构性畸形比如眼睛错位、嘴巴歪掉坏处也很明显——如果你要修复的是风景照、文字文档或者没有人脸的合影局外区域GFPGAN的人脸先验反而可能帮倒忙。所以它在架构上做了区域控制face_enhancement模块只作用于检测到的人脸框背景部分交给另一个轻量级超分网络处理。2.2 GFPGAN v1.4与v1.3该选哪个权重GFPGAN官方仓库维护了两个常用的发布版本权重v1.3和v1.4我建议直接选v1.4。v1.3是早期版本对应的训练数据和生成器是StyleGAN2修复后的肤色偏暖、皮肤纹理偏“油画感”对极端模糊的旧照片反而有奇效因为它的生成器先验更强。v1.4换用了更新的人脸生成器并且加入了更多真实退化数据的训练整体效果更自然皮肤毛孔和头发丝的处理接近真实照片但在输入人脸特别模糊时它“脑补”的幅度会收着一点不会像v1.3那样大胆。实操建议如果你的照片是民国时期那种严重颗粒化、面部结构都快看不清的先试v1.3如果是九十年代家庭合影、有轻微磨损但五官轮廓尚存直接上v1.4。我一般会在批量脚本里同时下载两个权重遇到效果不满意的单张照片再切换对比这个成本只是多占1GB磁盘空间。2.3 为什么当前推荐用Python调用而非ComfyUI节点GFPGAN在图像生成社区里经常被封装成ComfyUI节点或WebUI插件点几下鼠标就能出图。但我依然推荐用Python直接调官方repo理由有两条第一插件节点为了通用性把很多参数固化在了界面里比如只暴露denoise_strength一个滑杆而实际影响修复风格的还有bg_upsampler、padding_ratio等参数用代码才能完整控制第二插件节点属于第三方维护GFPGAN官方更新权重后节点未必同步更新直接调官方repo能第一时间拿到新模型和bug修复。3. 本地跑通最小修复流程环境搭建与首个推理脚本3.1 环境搭建的版本选择与CUDA踩坑GFPGAN依赖PyTorch和基础的图像处理库。这里有一个常见的翻车点PyTorch的CUDA版本必须和显卡驱动版本匹配否则推理时会报出“CUDA error: no kernel image is available for execution on the device”这类玄学错误。我的建议是先确定你的显卡驱动支持的最高CUDA版本再去PyTorch官网选对应的安装命令。如果只是做推理不追求训练装CPU版也不是不行——一张512×512的图CPU推理耗时在30秒到1分钟批量处理十几张还能忍上百张就相当折磨了。下面是完整的安装流程假设你用conda管理环境# 创建独立环境Python版本锁定3.10 conda create -n gfpgan_env python3.10 -y conda activate gfpgan_env # 安装PyTorch这里以CUDA 11.8为例如果你是N卡且驱动支持更高版本可以换成对应的cu121 pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 # 克隆GFPGAN官方仓库并安装依赖 git clone https://github.com/TencentARC/GFPGAN.git cd GFPGAN pip install basicsr facexlib pip install -r requirements.txt # 安装训练依赖仅推理可跳过 python setup.py develop这段命令里需要注意两点basicsr和facexlib是GFPGAN的两个核心依赖前者提供了超分网络的基础模块后者负责人脸检测和对齐python setup.py develop会把当前目录注册为Python包方便后面直接import gfpgan。如果跳过这一步在别的目录下写脚本会报ModuleNotFoundError。3.2 跑通第一张照片修复最小推理脚本装好环境后先别急着上批量脚本。我建议先用官方提供的inference_gfpgan.py跑单张图确认整个链路是通的再写自己的封装。把一张照片放到inputs目录下执行# 注意先把权重文件下载到 experiments/pretrained_models/ 下 python inference_gfpgan.py --input inputs/old_photo.jpg --output results/ --version v1.4 --scale 2参数说明--scale 2表示把图片放大两倍老照片一般建议2倍放大4倍容易出现面部虚焦感--version v1.4决定加载哪个权重文件仓库里的脚本会自动拼出GFPGANv1.4.pth这样的文件名。如果出现“pretrained model not found”错误说明权重文件位置不对需要放在experiments/pretrained_models/目录下。运行结束后在results目录下会得到对比图和单独的修复图。如果输入图里有多张人脸GFPGAN会逐一检测并修复然后在对比图里用红框标出每个检测到的人脸区域。这其实也是一次隐式的“人脸检测成功率”测试——如果某张侧脸没被框出来说明它没被修复后面批量时要回去找它。3.3 封装成自定义函数输入路径、输出路径与参数分离官方脚本适合验证但不适合批量。我的做法是封装一个函数把输入路径、输出路径、scale、denoise_strength都作为参数暴露出来这样既能单张调用也能在循环里批量处理import cv2 import torch from gfpgan import GFPGANer def restore_photo(input_path, output_path, versionv1.4, scale2, denoise0.75): # 初始化修复器bg_upsampler设置为None表示背景不做额外超分 bg_upsampler None # 创建GFPGAN实例 restorer GFPGANer( model_pathfexperiments/pretrained_models/GFPGAN{version}.pth, upscalescale, archclean, channel_multiplier2, bg_upsamplerbg_upsampler ) # 读取图片GFPGAN内部会自动处理BGR/RGB转换 img cv2.imread(input_path, cv2.IMREAD_COLOR) if img is None: raise ValueError(f无法读取图片: {input_path}) # 执行修复返回值分别是修复后的图、修复后的人脸框、修复前的人脸框 _, _, _ restorer.enhance( img, has_alignedFalse, only_center_faceFalse, paste_backTrue, weightdenoise ) # 保存结果 cv2.imwrite(output_path, img) print(f[OK] {input_path} - {output_path})这段代码里有几个参数值得重点说明。archclean是固定的对应v1.4权重如果换用v1.3权重arch要改回originalchannel_multiplier也要从2改成1两个版本是两套结构定义。weightdenoise是整套修复里最值得调的参数它控制生成器先验与输入图像的融合比例——数值越大修复结果越依赖“人脸先验”对于严重模糊的照片效果更好但可能丢失原图的真实细节数值越小越保留原图信息适合本身清晰度尚可的照片。has_alignedFalse表示输入图是自然图像而不是已经对齐过的人脸GFPGAN会先做一次人脸检测。only_center_faceFalse表示修复所有检测到的人脸而不是只修画面中心的那张。paste_backTrue表示把修复后的人脸贴回原图而不是只输出裁剪后的人脸区域。4. 修复质量的四个关键参数与效果验证方法4.1 denoise_strength先验与真实感的博弈denoise_strength是GFPGAN里对结果影响最大的参数没有之一。它的本质是生成器重建时的“去噪权重”——具体到推理脚本里这个值会作为StyleGAN生成器输入latent code的截断阈值truncation psi和融合权重。我自己的经验区间是0.5到0.9照片是那种带明显胶片颗粒的旧图想要数字化之后的干净质感用0.8以上照片本身是九十年代彩色照片失真不严重只是分辨率低用0.5到0.6保留更多原始纹理黑白合影照人物多、每张脸占的面积小用0.75作为默认值既保证小脸区域能被“脑补”完整又不至于让肤色变化太突兀。调参时不要全凭肉眼感受有一个能量化的判断方法修复输出和原图做差分计算差分图的峰值信噪比PSNR。如果PSNR高于28dB说明修复结果和原图差异很小这时候denoise_strength可以再加一点如果低于20dB说明已经改得面目全非了需要往回降。4.2 scale的选择2倍与4倍不是越高越好很多人拿到老照片第一反应是放大4倍实际上这是最常见的翻车点。老照片的原始分辨率往往只有几百像素人脸区域更是只有几十像素见方。放大4倍后GFPGAN需要在这么小的区域里凭空生成细节结果就是五官位置正确但脸型被“风格化”——变得过于光滑像CG渲染出来的人物。我的经验是扫描件原图长边超过1200像素的放心用scale2长边只有500到800像素的老照片扫描件先做一次scale4但修复完再用cv2.resize缩回2倍尺寸利用多出来的像素冗余做降噪效果通常比直接2倍更干净。这是我在处理大量馆藏老照片时学会的办法本质上是用超分模型当降噪器用。4.3 批量修复的目录设计与断点续跑实际工作流里你几乎不可能只是一张张手动跑。批量处理时要设计好目录结构我的习惯是restore_project/ ├── inputs/ │ ├── album_1/ │ └── album_2/ ├── outputs/ │ ├── album_1/ │ └── album_2/ ├── failed.txt └── batch_restore.py批量脚本里必须写失败日志和断点续跑逻辑。原因很简单几百张图里总有那么几张因为人脸检测失败或图片本身损坏导致程序抛异常如果不做容错跑了一小时直接整体中断前面的工作全白费。我的做法是把单张修复包进try/except里失败时把文件路径写进failed.txt用continue跳过同时修完的文件会写入一个done.txt下次运行时先读取done.txt跳过已经处理过的图片。4.4 验证修复质量不能只看“变清晰了”修复完成后的验证新手看“清晰度”熟手看三个细节眼睛瞳孔是否出现不自然的对称高光、牙齿是否糊成一片白色、耳廓边缘是否有锯齿状伪影。GFPGAN生成的伪影非常典型——它会在瞳孔里加一个理想的圆形反光点这在真实老照片里几乎不可能存在。如果你发现所有修复的人脸瞳孔都带一个位置几乎相同的高光点说明denoise_strength开太高了。另一个需要警惕的问题是“人脸克隆”当一张照片里有多个人脸且都比较小时GFPGAN偶尔会把所有人脸都修复成同一张脸的变体——因为它们编码到的latent code太接近。这时候需要把only_center_face设为True只修主体人脸其他侧脸放弃或者回到原图重新调整对齐后再修。5. GFPGAN老照片修复的常见问题与排查记录5.1 修复后人脸出现“塑料肌”皮肤毫无纹理这个现象在彩色照片上尤其明显修复结果像游戏CG人物皮肤光滑到反光。原因是denoise_strength设置过高生成器先验完全压过了输入图像的真实纹理。GFPGAN的生成器从StyleGAN继承了一个特点它生成的人脸皮肤是“干净”的没有真实皮肤那种微观肌理。解决方法是降低weight参数到0.5以下同时把bg_upsampler换成RealESRGAN让背景和皮肤区域的纹理由超分网络贡献而不是由人脸生成器完全接管。5.2 检测不到人脸整张图原样输出输入一张侧面45度角的老照片修复后结果和原图几乎一样没有任何变化。原因是GFPGAN默认使用retinaface_resnet50做人脸检测它对大角度侧脸的召回率不够检测不到人脸框修复流程直接跳过。解决方法是先对图像做一次人脸检测预处理如果检测不到就把图像旋转15到30度再试或者换用facexlib里的其他检测模型——不过我实际用下来最稳妥的还是旋转后重试因为换模型意味着要重新下载权重并处理对齐逻辑成本更高。5.3 修复后整张图的颜色偏灰饱和度下降输出的人脸修复得很自然但整张图看起来像蒙了一层灰色彩发闷。这是因为老照片原图本身偏黄偏暗GFPGAN对人脸区域重绘时的色彩分布以训练数据现代数码照片的统计均值为基准修复后的人脸颜色自然、但和原图的复古色调不匹配。解决方法是修复后做一次色彩迁移用OpenCV计算原图与修复图的亮度均值将原图的色偏信息叠加回去。代码上就是cv2.addWeighted把原图和修复图按1:1混合再提升饱和度。5.4 黑白照片修复后肤色像涂了一层灰紫色老照片翻拍的黑白图像在修复后经常出现肤色蜡黄或灰紫的情况这是因为原图已经丢失了色彩信息GFPGAN的生成器却强行给它分配了一套“合理肤色”然后又叠加了灰度图的亮度分布。处理这类照片前需要先做一个预处理把灰度图复制到三个通道做一次自动白平衡让亮度分布更均匀再进GFPGAN。修复完成后如果想保持黑白质感就手动cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)转回灰度。这也是我处理馆藏老照片时最常用的流程。5.5 显存不足程序崩溃或者OOM后残留进程占显存处理多人合影时每个人脸框都会单独送入生成器如果一张照片检测出几十个人脸显存占用会瞬间拉满程序直接报torch.cuda.OutOfMemoryError。解决思路有两个一是手动限制每个人脸框的大小在enhance前把检测框的最大边长缩到512像素以内GFPGAN内部会自动缩放但小框对显存的占用会指数下降二是在批量脚本里量力而行设置torch.cuda.empty_cache()在每张图处理后回收缓存。需要注意OOM崩溃后显存不会自动释放再次运行前要检查进程管理器里是否残留了Python进程有就杀掉否则下一轮还会继续崩溃。6. 把修复流程固化成可交付脚本进阶技巧与量化验证6.1 一个更接近生产环境的修复脚本结构我最终沉淀下来的脚本不是裸调GFPGAN而是加了三个前置处理自动白平衡、灰度图分支处理、人脸检测失败旋转重试。这三个处理加起来不到60行代码但能把修复成功率从70%提升到90%以上。def preprocess_for_gfpgan(img): 修复前预处理 1. 灰度图转三通道并做白平衡 2. 长边超过1600像素时先缩小防止小脸被忽略 if len(img.shape) 2: img cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) # 白平衡灰度世界假设 avg_b np.mean(img[:, :, 0]) avg_g np.mean(img[:, :, 1]) avg_r np.mean(img[:, :, 2]) avg (avg_b avg_g avg_r) / 3.0 img[:, :, 0] np.clip(img[:, :, 0] * (avg / avg_b), 0, 255) img[:, :, 1] np.clip(img[:, :, 1] * (avg / avg_g), 0, 255) img[:, :, 2] np.clip(img[:, :, 2] * (avg / avg_r), 0, 255) h, w img.shape[:2] max_side max(h, w) if max_side 1600: scale_factor 1600 / max_side img cv2.resize(img, (int(w * scale_factor), int(h * scale_factor)), interpolationcv2.INTER_AREA) return img这段预处理的核心逻辑是灰度世界假设把偏色拉回中性缩小长边是为了让检测模型不至于在过大的画幅里忽略小尺寸人脸因为retinaface对大于一定尺寸的人脸框会直接跳过。6.2 用差分图定位修复伪影的三步法修复结果的验收不能只靠肉眼看我习惯用差分图做量化定位把修复图和原图缩到同一尺寸做绝对差分然后高斯模糊再用固定阈值二值化。伪影会以高亮区域的形式显示出来。看差分图时重点观察三个位置瞳孔、牙齿和发际线。如果高亮区域集中在这几个位置且成片出现说明生成器先验主导了修复这时要么降低denoise_strength要么考虑改用v1.3权重。def locate_artifacts(orig, restored, threshold50): 定位修复伪影 返回与restored同尺寸的二值图白色区域即改动较大的位置 h, w restored.shape[:2] orig_resized cv2.resize(orig, (w, h), interpolationcv2.INTER_AREA) diff cv2.absdiff(orig_resized, restored) diff_gray cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) diff_blur cv2.GaussianBlur(diff_gray, (5, 5), 0) _, mask cv2.threshold(diff_blur, threshold, 255, cv2.THRESH_BINARY) return maskthreshold设置成50时基本只有结构性改动眼睛、嘴、轮廓重绘会被标出来如果改成20会把颜色调整、亮度变化也标出来适合检查整体风格迁移程度。我会在批量跑完后随机抽5%的结果图做这个检查确认没有“千人一面”的问题。6.3 关于GFPGAN的一个清醒认知它不是万能的用了大半年GFPGAN之后我最大的一个教训是它擅长修脸不擅长修照片。如果一张老照片的核心问题是划痕、霉斑、折痕GFPGAN几乎帮不上忙——它的目标函数里没有“去除划痕”这一项划痕经过超分后反而变得更粗更明显。正确的工作流应该是先用传统的去划痕工具如OpenCV的inpaint或者Photoshop手修处理物理损伤再交给GFPGAN做人脸增强最后统一做降噪和调色。还有一个经常被忽略的点GFPGAN不适合修复包含文字的旧书页或证件扫描件。文字经过生成器重建后会“被写字”——笔画变得圆润模糊甚至缺笔少画。如果你要修的是带文字的档案照片记得把paste_back设为False只取人脸区域修复后再靠外部脚本贴回去别让文字区域碰生成器。最后分享一个我自己的习惯处理任何一批老照片前先拿三张不同年代、不同清晰度的样图跑一遍完整流程分别检查人脸检测成功率、伪影情况和处理耗时。这三张样图的通过率基本能代表整批照片的下限——样图全部合格再开批量能省掉大量返工时间。希望这些经验能帮你少踩几个坑也期待看到你修出来的老照片比我修得更好。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑