ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

深度学习老照片修复实战:基于VAE与GAN的退化建模与调优

深度学习老照片修复实战:基于VAE与GAN的退化建模与调优 简介一份基于Python的老照片修复深度学习工程适合图像处理开发者和AI实战学习者用于恢复划痕、模糊、破损等严重退化的旧照片。项目以卷积神经网络与生成对抗网络为核心完整覆盖数据预处理、模型训练、权重保存、推理部署和人脸增强等环节并配有可运行脚本能帮助理解图像恢复任务的完整落地流程。压缩包共81个文件大小约50.71MB。其中包括54个Python脚本分别承担网络构建、训练、测试、人脸检测与对齐等功能17张测试图片和效果对比图可用于质量评估4份PDF文档涵盖项目提案、演示文稿与最小可行产品说明另有GIF动态效果演示、MP4讲解录屏、依赖清单和使用指南目录结构清晰适合按需查找。目前已有350人学习/下载。通过该项目读者可获得一套可直接复现的老照片修复示例掌握卷积神经网络和生成对抗网络在图像增强中的调参与训练思路并借助附带的演示素材与说明文档快速用于课程设计、论文复现或历史影像数字化修复等场景。1. 拿到这个 zip 之前先想清楚老照片修复到底在解决什么问题一张 1940 年代的家庭合影问题从来不是单一的高斯模糊。放大看有胶片的颗粒噪点、有折痕划过人脸、有褪色导致的色偏、还有多次翻拍和压缩留下的 JPEG 块效应。这些退化叠加在一起退化函数完全未知所以传统去模糊、去噪、超分那一套按单一退化模型反解的思路在这里全部失效。这个项目标题说得很明白用深度学习修复「严重退化」的老照片。实现路径不是端到端硬train一发而是把问题拆成三件事先建模退化过程合成训练数据再用 VAE 和 GAN 分别处理全局退化和局部划痕最后在真实老照片上微调。你拿到的 zip 里装的就是这套完整流程的源码。对刚开始接触深度学习的新手这是一个能完整跑通的 GAN 项目对做了五六年算法工程师的人来说值得看的是合成数据策略和真实数据微调的配比这两处直接决定修复结果的上限和下限。2. 拿到 zip 后先别急着跑把 Python 和 PyTorch 环境配到能复现2.1 先拆包看目录搞清楚这个 zip 里各目录的职责这类修复项目一般会分成 Global 和 Local 两个模型目录外加若干脚本。先别双击 run.py先花两分钟把目录摸清楚。unzip old_photo_restore.zip -d photo_restore cd photo_restore tree -L 2常见的目录布局是Global/存放全局修补网络负责处理整张照片的纹理重建和色偏校正Local/存放局部修补网络专门处理划痕、污渍这类区域性的损伤run.py是统一推理入口requirements.txt是 Python 依赖清单。有的项目还会带face/子目录因为人脸区域的结构先验强单独训一个模型效果更好老照片修复尤其如此。先看requirements.txt里锁了哪些包版本再决定下一步。如果里面有torch1.7这种范围约束说明作者只保证在某个 torch 版本下跑通过换新版 torch 未必能直接复现推理结果。2.2 用 conda 隔离环境CUDA 版本和 torch 匹配这一步别省深度学习环境配置最大的坑不是装不上而是装上了 import 报错。最省事的做法是用 conda 建一个独立环境Python 版本选 3.8 或 3.9这两个版本对老代码的兼容性最好。conda create -n photo_restore python3.8 -y conda activate photo_restore pip install -r requirements.txt这里的核心是 PyTorch 的安装。如果机器有 NVIDIA 显卡先查驱动支持的 CUDA 版本再装对应版本的 PyTorch否则会出现torch.cuda.is_available()返回 False 的问题。没显卡的机器也能跑推理只是 CPU 上推理一张 512×512 的图可能要几分钟开发调试足够用了。装完依赖后先验证一下python -c import torch; print(torch.__version__, torch.cuda.is_available())如果输出True说明 CUDA 可用。这里有个常见的误用requirements.txt里写的 torch 版本是作者在他自己的环境里测过的如果你机器上的 CUDA 是 12.x可以把 torch 装成更新的 2.x 版本但要注意项目里如果用了老版 API 如torch.nn.functional.pad的某些写法升级后行为可能变化。2.3 跑通第一张图的最小推理命令环境就绪后先用一张测试图跑通全流程不要一上来就调参。这类项目的推理入口一般长这样python run.py --input_folder ./test_images --output_folder ./outputs --GPU 0参数含义--input_folder是输入图片目录脚本会遍历目录下所有.jpg/.png文件--output_folder指定修复结果的输出目录不存在会自动创建--GPU指定使用哪块 GPU从 0 开始编号CPU 机器填-1不少项目还会有一个--with_scratch开关如果你的老照片明确带划痕加上它会让局部修补网络参与推理否则默认只走全局修补修复后的划痕会残留。这点是使用该项目的关键。2.4 启动报错的几个高频原因及处理第一类ModuleNotFoundError: No module named torch说明依赖没装到位或环境没激活用conda activate photo_restore确认环境。第二类RuntimeError: CUDA out of memory显存不够。这类模型的全局网络在 4GB 显存上勉强能跑如果爆显存把输入图片预先缩到 512px 以内再喂进去或者换--GPU -1走 CPU 先验证流程。第三类ImportError涉及libGL.so.1之类这是 OpenCV 在纯净版容器里的老问题安装libgl1和libglib2.0-0即可不用升级任何 Python 包。3. 严重退化不是一种病是多种退化叠加模型得按退化类型分治3.1 退化模型怎么建模合成数据是这类项目能成立的地基真实老照片的退化是未知的但深度学习中有一个核心思路如果无法获得真实配对数据那就用已知的退化过程去合成近似数据让模型先学会「如何把退化图还原成干净图」这个能力迁移到真实老照片上效果取决于合成退化与真实退化的分布有多接近。形式上一个带划痕的老照片退化过程可以写成I_degraded compress( downscale( I_clean ⊗ k ) ) n s其中k是模糊核⊗是卷积操作downscale是下采样compress是 JPEG 压缩n是高斯或泊松噪声s是稀疏的划痕掩码叠加。这套项目在合成阶段做的事情就是按这个公式生产退化样本随机高斯核和多类运动模糊核模拟失焦和抖动随机下采样倍数模拟分辨率损失随机 JPEG 质量因子模拟多次压缩产生的块效应随机画直线、曲线模拟物理划痕合成数据的退化类型越全模型在真实数据上的泛化就越稳。训练时退化参数要随机化而不是固定一组否则模型会把某一种模糊核背下来。这给了一个很重要的启示在实际部署时如果发现某张真实照片的修复效果差先把它归因于哪类退化再针对性地往合成数据里补充同类退化比盲目调大 GAN 权重有效得多。比如真实扫描照片常有摩尔纹而合成数据里没加模型自然学不会消除摩尔纹。3.2 全局修补与局部修补VAE 编码器-解码器和 GAN 各管一段图像修复的通用难点是全局退化色偏、模糊、压缩伪影需要大感受野来理解整张图的语义而局部损伤划痕、斑点需要精细的像素级重建能力把这两种目标塞进同一个网络会让优化互相打架。这套方案把问题拆成两段全局网络用 VAE 结构做整图重建本质是一个编码器-解码器把退化图映射到隐空间再还原中间用自注意力机制扩大感受野。VAE 的作用不是生成全新的内容而是学习一个接近干净图像的隐空间分布再从中采样重建。编码器: I_degraded - z 解码器: z - I_global局部修补网络则用 patch-based GAN判别器只看局部图像块强迫生成器在划痕区域产生真实的纹理细节而不是平滑的模糊色块。GAN 在局部纹理生成上有天然优势因为判别器会盯着「这块区域像不像真实照片中该有的皮肤纹理或墙面颗粒」。另一个关键设计是激活函数的分布。这类网络的生成器里常用 ReLU 做中间层激活但在最后一层会用Tanh或Sigmoid把输出像素范围压到[0,1]这样在计算 L1 损失时能和归一化的图像像素对齐。如果你微调时改了输出层的激活函数损失曲线收敛了但输出图永远是灰的或过曝的基本就是这里出错了。3.3 划痕检测和三元组损失先定位损伤区再指导局部模型该重点修哪里局部修补的另一个前置问题是网络怎么知道哪里是损伤区、哪里是原始纹理区如果让网络无差别地重建整张图它会把完好的皮肤纹理也当成退化给「抹平」修复完的人脸像塑料。解决方案是加一个划痕检测网络输出每张图中划痕区域的掩码。这个掩码不参与最终生成而是用于指导局部网络在掩码覆盖区域内提高重建损失的权重在掩码外降低权重逼迫网络把修复能力集中在损伤区。为了让检测网络能区分「退化区域的特征」和「完好区域的特征」训练时强制拉大这两类特征在嵌入空间中的距离这就是三元组损失的作用。实现上的做法是取一个锚点样本一个正样本同一类损伤一个负样本完好区域损失函数让锚点与正样本更近、与负样本更远triplet_loss torch.nn.functional.triplet_margin_loss( anchor, positive, negative, margin1.0 )在推理阶段你可以把检测网络输出的 mask 可视化来看模型认为哪些区域是损伤区import cv2 mask detection_model(img) # 输出 0-1 的掩码 cv2.imwrite(scratch_mask.png, mask * 255)如果 mask 把完好的细节区域也标红了说明检测网络的阈值设得过于激进局部网络会被误导去「修复」本来完好的区域。此时要调整检测网络输出后处理的阈值而不是调生成器的损失权重。4. 训练参数怎么改效果才有可感知的变化预训练和微调两阶段区别对待4.1 预训练阶段的推荐参数batch size 和损失权重怎么配合成数据阶段的训练目标是让模型具备通用的退化消除能力。这个阶段用 Adam 优化器betas(0.9, 0.999)初始学习率1e-4batch size 看显存定通常 8 到 16。全局网络和局部网络分开训练不共享权重。下面是常见的训练启动命令python Global/train.py \ --name photo_restore_global \ --gpu_ids 0 \ --batch_size 16 \ --lr 1e-4 \ --niter 50 \ --niter_decay 50参数说明--niter是固定学习率的训练轮数--niter_decay是线性衰减学习率的轮数加起来 100 轮--niter_decay常见误用是设成 0导致模型一直在固定学习率下训练后期 loss 震荡不收敛。到训练后期学习率变小后模型才能在损失曲面底部稳定收窄这个衰减阶段是必需的训练时的总损失是三部分加权和L λ_l1 * L1_reconstruction λ_perceptual * L_perceptual λ_gan * L_gan比较稳的权重配置是L1权重 10感知损失权重 1GAN 损失权重 0.1。L1 权重要给得重它负责生成结果的像素级准确性GAN 权重只给 0.1让它负责提升纹理逼真度即可给太大会出现色彩漂移和伪纹理。参数一览表参数推荐值调大影响调小影响L1 权重10结果更平滑细节被压缩结构不稳定可能有断裂感知权重1语义保持更好但可能丢失高频颜色失真语义漂移GAN 权重0.1纹理更锐利但容易出伪影纹理平滑像过度磨皮学习率1e-4收敛快易震荡收敛慢效果好但耗时翻倍4.2 真实老照片微调少量样本 低学习率防止模型把噪声特征背下来合成数据和真实老照片的分布始终有差异真实扫描件的划痕有物理深度感、色彩有氧化褪色曲线。所以第二阶段需要在真实的老照片数据集上微调这一步是把合成阶段学到的基础能力迁移到真实分布上的关键。微调有两个铁律。第一学习率降到原来的十分之一到二十分之一也就是5e-6到1e-5微调不是从头学是在已有解附近做局部搜索学习率大了会直接破坏预训练权重。第二真实样本量不需要多几百张足够但 batch size 要相应减小因为真实数据量本来就不大batch 太大等于一个 batch 里全是同类照片梯度的多样性反而下降。python Global/train.py \ --name photo_restore_finetune \ --gpu_ids 0 \ --batch_size 4 \ --lr 5e-6 \ --niter 20 \ --load_pretrain ./checkpoints/photo_restore_global微调轮数控制在 20 轮以内。如果训练集里全是 40 年代的人像模型会把「人像老照片的高光偏黄」当成通用特征来学习导致修复现代数码照片时色调发黄。处理办法是训练时对照片做随机色偏增强打乱退化和内容的绑定关系。判断微调是否到位看两个曲线感知损失是否持续下降GAN 损失的判别器精度是否维持在 0.5 附近。如果感知损失下降但生成器输出出现局部伪影大概率是 GAN 权重要往下调。如果感知损失已经收敛但视觉效果依然模糊则该增大 L1 权重或减小感知权重因为感知网络一般是 VGG对高频纹理不敏感权重过大会让模型倾向生成在特征空间里接近、但像素层面模糊的结果。4.3 训练时看什么指标loss 曲线之外更值得看的是中间可视化训练期间每过 500 个 iteration把当前的生成结果和输入图并列保存肉眼看过比看 loss 值重要得多。一个常见的误判是L1 loss 已经降得很低结果看起来还行但其实只是输出偏向输入图的低频版本细节全部糊掉了。此时 perceptual loss 或 GAN loss 反而可能还在高位以这个为准不要只盯 L1。另一个实用技巧是在训练过程中记录「修复后图像的梯度幅值直方图」。修复结果的梯度如果大量集中在极低值区说明生成图过于平滑如果高幅值梯度大量出现且分布杂乱说明 GAN 在制造伪纹理。正常修复结果的梯度分布应该介于原始退化图和干净参考图之间这比 SSIM 对质量变化更敏感。5. 输出到底行不行量化验证和批量处理这两件事最后处理5.1 用 PSNR 和 SSIM 把「看起来不错」变成数字主观评价容易受单张图影响验证模型效果的最快路径是 PSNR 和 SSIM。对老照片修复来说没有原始干净图做参考常用的做法是在真实老照片上用人工方式模拟退化拿一张现代高清图按 3.1 节的退化模型合成退化图再用你的修复模型去修最后把修复结果和原始高清图计算指标。计算脚本用 scikit-image 实现from skimage.metrics import peak_signal_noise_ratio as psnr from skimage.metrics import structural_similarity as ssim import cv2 orig cv2.imread(clean.png) restored cv2.imread(restored.png) p psnr(orig, restored) s ssim(orig, restored, channel_axis2) print(fPSNR: {p:.2f} dB, SSIM: {s:.4f})PSNR 超过 30dB 说明整体重建在像素级已经比较接近SSIM 在 0.9 以上说明结构保持得很好。低于 25dB 就要回去检查参数了。注意较新版本的 scikit-image 中channel_axis用于指定 RGB 通道轴而这个参数在更早的版本里叫multichannel如果报 TypeError优先考虑升级 scikit-image 而不是改代码。5.2 批量修复真实老照片的完整流程真实场景下处理一批老照片通常不用一张张调用 run.py而是在脚本里循环目录for img in ./old_photos/*.jpg; do python run.py \ --input_folder $(dirname $img) \ --output_folder ./restored \ --GPU 0 \ --with_scratch done这个循环看起来简单但有一个易踩的坑run.py会处理--input_folder里的所有图片每张图都启动一次 Python 进程模型权重会被重复加载500 张图可能要多花一小时。正确的做法是只调用一次run.py把整个文件夹传进去项目内部会顺序处理然后检查输出目录里有没有为每张输入图片生成同名文件防止中间有图片因为格式不支持被静默跳过。另一个实用技巧是显存换速度在推理脚本中把网络切到半精度只对生成器生效不影响输出质量import torch model.half() with torch.no_grad(): output model(input_tensor.half())对大批量任务这样处理能把总耗时缩短近一半。跑完后按 5.1 节的脚本抽 10 到 20 张结果计算 PSNR/SSIM 的平均值和方差再抽样做主观查看修复工作的交付标准就立住了。最后补充一个对修复效果提升稳定的做法在把老照片送入模型前先做一步轻量预处理。扫描件用 3×3 中值滤波去一下颗粒噪点翻拍照先做透视校正这些预处理让模型把注意力集中在真正的严重退化上效果比调整 GAN 权重更明显。预处理和修复流程串起来就是一套能应对真实扫描件的完整方案。本文还有配套的精品资源点击获取
返回列表