ARTICLE DETAIL

资讯详情

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

基于深度学习的老旧照片修复与修补:从数据构造到部署实战

基于深度学习的老旧照片修复与修补:从数据构造到部署实战 简介这份源码资源面向具备一定Python与深度学习基础的开发者、研究人员及图像处理爱好者提供一套基于卷积神经网络的老旧照片修复与修补完整实现可用于破损照片恢复、图像降噪与超分辨率重建等场景。压缩包共95个文件约39.27MB以63个Python脚本为核心覆盖模型训练、预测、数据加载与GUI交互另含19个PNG、2个JPG示例图与结果图以及YAML、Dockerfile、权重下载脚本等配置部署文件目录按models、options、data、util等模块清晰划分。项目整合了人脸检测、对齐、pix2pixHD与特征映射等多种网络结构并支持容器化部署读者可借此理解从数据预处理、特征提取到模型评估与图像生成的完整流程也可直接下载预训练权重快速上手。目前已有455人学习适合作为深入研究和二次开发的基础。1. 老旧照片修复与修补从划痕到高清人脸的完整技术链路家里翻出一张二十年前的全家福扫描成电子版之后发现人脸模糊、折痕横穿画面、边角还有霉斑——这是很多人做老旧照片修复的真实起点。基于深度学习的老旧照片修复与修补设计源码本质上是一套把「退化图像」映射回「清晰图像」的工程方案核心包含两条技术路线一是修复Restoration处理划痕、噪点、模糊、褪色这类全局或局部退化二是修补Inpainting处理大面积缺失、破损、撕裂区域的内容重建。两者在模型结构、损失函数、数据构造上都有明显差异但落地时往往需要串联使用。这套方案适合谁如果你已经跑通过基础的 CNN 图像分类或分割任务想找一个有明确视觉效果、能直接展示成果的深度学习实战项目老旧照片修复是性价比很高的选择。它不依赖昂贵的标注数据退化图像可以通过算法合成训练周期可控推理结果肉眼可判。下面从数据构造、模型选型、训练调参到部署推理把整条链路拆开讲清楚。2. 数据从哪来退化合成与配对样本构造2.1 为什么不能直接用网上的老照片训练真实老照片的退化是复合的、不可控的同一张照片可能同时存在划痕、噪点、色偏、模糊而且没有对应的「清晰原图」作为监督信号。监督学习需要成对数据退化图清晰图所以行业里的常见做法是反向合成拿高质量清晰图像用算法人为施加退化构造出配对样本。这个思路的关键在于退化模型要尽量逼近真实老照片的退化分布。如果只加高斯噪声训出来的模型遇到划痕就废了。我一般会叠加以下几类退化退化类型模拟方式典型参数范围划痕随机线条掩膜 亮度偏移线宽 1-5px数量 3-20 条噪点高斯噪声 / 泊松噪声σ5-25模糊高斯核 / 运动模糊核核大小 3-15褪色色彩通道偏移 对比度衰减偏移量 ±20缺失随机矩形/不规则掩膜面积占比 5%-30%2.2 用 Python 构造退化配对数据集下面这段代码演示如何从一张清晰图生成退化图和对应的掩膜作为训练数据的基础单元。import cv2 import numpy as np import random def add_scratches(img, num10): 模拟划痕随机方向线条 局部亮度提升 h, w img.shape[:2] mask np.zeros((h, w), dtypenp.uint8) for _ in range(num): x1, y1 random.randint(0, w), random.randint(0, h) x2, y2 random.randint(0, w), random.randint(0, h) thickness random.randint(1, 4) cv2.line(mask, (x1, y1), (x2, y2), 255, thickness) # 划痕区域提亮模拟反光 scratched img.copy() scratched[mask 0] np.clip( scratched[mask 0].astype(int) 60, 0, 255 ).astype(np.uint8) return scratched, mask def add_blur(img, ksizeNone): 高斯模糊核大小随机 if ksize is None: ksize random.choice([3, 5, 7, 9, 11]) return cv2.GaussianBlur(img, (ksize, ksize), 0) def add_noise(img, sigmaNone): 高斯噪声 if sigma is None: sigma random.uniform(5, 25) noise np.random.normal(0, sigma, img.shape) noisy np.clip(img.astype(float) noise, 0, 255) return noisy.astype(np.uint8) def degrade_pipeline(clean_img): 串联退化流程返回退化图和划痕掩膜 img clean_img.copy() img, scratch_mask add_scratches(img, numrandom.randint(5, 15)) img add_blur(img) img add_noise(img) return img, scratch_mask逻辑说明degrade_pipeline按划痕→模糊→噪声的顺序串联顺序不能随意调换。如果先加噪声再画划痕划痕边缘会被噪声污染和真实老照片的退化层次不符。add_scratches返回的掩膜在后续训练中可以作为辅助监督信号让模型明确知道哪些区域是划痕而非真实内容。参数说明划痕数量控制在 5-15 条比较合理太少模型学不到划痕特征太多则图像几乎不可辨认。高斯噪声的 σ 建议从 5 起步超过 25 之后图像退化过于严重模型很难恢复出有意义的内容。模糊核大小不要超过 15否则整张图会糊成一团失去局部细节。2.3 数据集规模与划分策略合成数据的优势是可以无限生成但训练集不是越大越好。根据我的经验5000-10000 对训练样本足以让模型收敛到一个可用的水平。验证集单独用 500 对且验证集的退化参数分布要和训练集有区分度——比如训练集划痕偏多验证集就多加点模糊和缺失这样能检验模型的泛化能力。注意合成数据和真实老照片之间始终存在域差距domain gap。如果目标场景是修复真实的扫描老照片建议在合成数据训练之后用少量真实退化图像做微调哪怕只有几十对效果提升也很明显。3. 模型怎么选从 U-Net 到门控卷积的修补网络3.1 修复任务的主干网络选型老旧照片修复本质上是一个 image-to-image 的密集预测任务输入和输出都是同尺寸图像。这个领域最常用的主干网络是U-Net及其变体原因是它的编码器-解码器结构配合跳跃连接能同时保留全局语义和局部细节。但标准 U-Net 有一个问题普通卷积对所有像素一视同仁而破损区域的像素值是无效的这些无效信息会通过卷积核污染周围的有效区域。对于划痕修复这个问题还不算严重但对于大面积缺失的修补标准卷积的表现会明显下降。解决方案是使用部分卷积Partial Convolution或门控卷积Gated Convolution。部分卷积的核心思想是每次卷积只对有效像素做计算无效像素的权重置零并在多层传播中逐步缩小无效区域。门控卷积则更进一步让网络自己学习一个门控机制来决定哪些位置的信息应该保留。3.2 搭建一个带门控卷积的修补网络以下代码展示门控卷积层和简化版修补网络的核心结构。import torch import torch.nn as nn class GatedConv2d(nn.Module): 门控卷积feature 分支 gate 分支逐元素相乘 def __init__(self, in_ch, out_ch, kernel_size3, stride1, padding1): super().__init__() self.feature nn.Conv2d(in_ch, out_ch, kernel_size, stride, padding) self.gate nn.Conv2d(in_ch, out_ch, kernel_size, stride, padding) self.sigmoid nn.Sigmoid() self.bn nn.BatchNorm2d(out_ch) def forward(self, x): feat self.feature(x) gate self.sigmoid(self.gate(x)) return self.bn(feat * gate) class InpaintNet(nn.Module): 简化版修补网络编码-解码 跳跃连接 def __init__(self): super().__init__() # 编码器 self.enc1 GatedConv2d(4, 32) # 输入 4 通道RGB mask self.enc2 GatedConv2d(32, 64) self.enc3 GatedConv2d(64, 128) # 解码器 self.dec3 GatedConv2d(128, 64) self.dec2 GatedConv2d(64, 32) self.dec1 nn.Conv2d(32, 3, 3, 1, 1) # 输出 RGB self.relu nn.ReLU(inplaceTrue) self.pool nn.MaxPool2d(2) def forward(self, x, mask): # 拼接图像和掩膜作为输入 inp torch.cat([x, mask], dim1) e1 self.relu(self.enc1(inp)) e2 self.relu(self.enc2(self.pool(e1))) e3 self.relu(self.enc3(self.pool(e2))) d3 self.relu(self.dec3(e3)) d2 self.relu(self.dec2(d3 e2)) # 跳跃连接 out self.dec1(d2 e1) return torch.tanh(out)逻辑说明GatedConv2d把标准卷积拆成 feature 和 gate 两条支路gate 经过 Sigmoid 后值域在 0-1 之间相当于给每个空间位置学了一个软掩膜。输入通道是 4RGB 三通道 掩膜单通道掩膜告诉网络哪些区域需要修补。跳跃连接用加法而非拼接是为了减少显存占用在 256×256 输入下差别不大但 512×512 时加法能省不少显存。参数说明编码器通道数从 32 起步逐层翻倍到 128。如果显存充足可以加到 64→128→256效果会更好但训练时间翻倍。tanh输出值域是 [-1, 1]训练时目标图像也要归一化到 [-1, 1]推理时再映射回 [0, 255]。3.3 损失函数怎么配修补任务的损失函数不能只用 L1 或 L2否则输出会偏模糊。我一般用三项加权L1 损失保证像素级精度权重 1.0感知损失Perceptual Loss用预训练 VGG 提取特征比较特征空间的差异权重 0.05-0.1风格损失Style Loss计算 Gram 矩阵差异权重 50-100感知损失和风格损失的权重不能太大否则输出会出现纹理伪影。风格损失权重尤其敏感超过 200 之后画面会出现明显的棋盘格效应。4. 训练调参与避坑记录4.1 训练流程与关键参数训练修补网络的标准流程是前向传播 → 计算损失 → 反向传播 → 更新权重。但有几个细节直接决定成败。import torch.optim as optim from torch.utils.data import DataLoader # 假设 dataset 返回 (degraded, clean, mask) loader DataLoader(dataset, batch_size8, shuffleTrue, num_workers4) model InpaintNet().cuda() optimizer optim.Adam(model.parameters(), lr2e-4, betas(0.5, 0.999)) scheduler optim.lr_scheduler.StepLR(optimizer, step_size30, gamma0.5) for epoch in range(100): for degraded, clean, mask in loader: degraded degraded.cuda() clean clean.cuda() mask mask.cuda() output model(degraded, mask) loss_l1 nn.functional.l1_loss(output, clean) # 感知损失和风格损失此处省略具体实现 loss loss_l1 0.1 * perceptual_loss 50 * style_loss optimizer.zero_grad() loss.backward() optimizer.step() scheduler.step()逻辑说明Adam 优化器的 betas 设为 (0.5, 0.999) 而不是默认的 (0.9, 0.999)是因为图像生成任务中动量太大会导致训练不稳定输出出现颜色漂移。学习率从 2e-4 起步每 30 个 epoch 衰减一半100 个 epoch 基本能收敛。参数说明batch size 设为 8 是 8GB 显存的保守选择如果显存够可以加到 16。num_workers设为 4 是数据加载的并行进程数根据 CPU 核心数调整一般设为核心数的 1/4 到 1/2。4.2 避坑记录五个血泪教训现象一输出图像整体偏灰颜色发闷。原因L1 损失在训练后期梯度变小模型倾向于输出「平均色」来降低损失。解决在 L1 基础上加入对抗损失GAN Loss用 PatchGAN 判别器判断局部 patch 的真假能显著提升色彩饱和度。判别器学习率设为生成器的 1/5。现象二划痕区域修复后出现明显的方形边界。原因掩膜边缘太硬卷积核在边界处混入了无效像素。解决对掩膜做 3-5 像素的高斯模糊让边界过渡平滑。同时在损失函数中对边界区域加权权重设为内部区域的 2-3 倍。现象三训练 loss 正常下降但验证集效果很差。原因合成退化和真实退化分布不一致模型过拟合了合成数据的特定模式。解决在退化合成中增加随机性每次 epoch 重新生成退化参数而不是固定一套参数反复用。另外验证集要用不同退化参数生成。现象四大面积缺失区域修补后内容完全不合理。原因卷积的感受野有限当缺失区域超过 64×64 像素时网络无法获取足够的上下文信息。解决在编码器和解码器之间加入注意力模块或 Transformer 层让网络能建模长距离依赖。或者先用低分辨率修复整体结构再逐步上采样细化。现象五推理时显存溢出无法处理高分辨率图像。原因整图推理的显存占用随分辨率平方增长。解决采用滑窗推理把大图切成 256×256 的块块之间保留 32 像素重叠最后用加权平均融合。重叠区域的权重用高斯核避免拼接缝。提示训练过程中每隔 5 个 epoch 保存一次模型权重和对应的验证集输出图像。不要只看 loss 曲线肉眼观察修复效果才是最直接的判断依据。很多时候 loss 还在降但视觉效果已经不再提升这时候就该停了。5. 推理部署与效果验证的实用技巧5.1 滑窗推理的工程实现高分辨率老照片比如 2000×3000 的扫描件直接送入网络几乎必然爆显存。滑窗推理是标准解法但窗口大小和重叠像素的选择有讲究。def sliding_window_inference(model, img, mask, window256, overlap32): 滑窗推理高斯加权融合 h, w img.shape[:2] output np.zeros((h, w, 3), dtypenp.float32) weight np.zeros((h, w), dtypenp.float32) # 生成高斯权重核 gauss cv2.getGaussianKernel(window, window / 6) gauss_2d gauss gauss.T step window - overlap for y in range(0, h, step): for x in range(0, w, step): y2 min(y window, h) x2 min(x window, w) y1 max(y2 - window, 0) x1 max(x2 - window, 0) patch img[y1:y2, x1:x2] m_patch mask[y1:y2, x1:x2] # 送入模型推理 result model_infer(model, patch, m_patch) output[y1:y2, x1:x2] result * gauss_2d[:y2-y1, :x2-x1, None] weight[y1:y2, x1:x2] gauss_2d[:y2-y1, :x2-x1] return (output / weight[:, :, None]).astype(np.uint8)逻辑说明高斯权重核让窗口中心区域的预测结果权重更高边缘区域权重低这样重叠区域的融合更自然。step是滑动步长等于窗口大小减去重叠像素。重叠像素设为窗口的 1/8 到 1/4 比较合适太小会有拼接缝太大则推理时间成倍增加。参数说明窗口 256 是显存和效果的平衡点128 太快但上下文不足512 效果略好但显存翻四倍。高斯核的标准差设为窗口的 1/6这个值让权重从中心到边缘平滑衰减到接近零。5.2 效果验证除了 PSNR 和 SSIM 还要看什么PSNR 和 SSIM 是图像修复的标配指标但它们和人类视觉感知的相关性有限。一张 PSNR 很高的修复图可能看起来仍然很「假」。我一般会补充以下验证手段验证维度方法判断标准纹理自然度放大到 200% 观察修补区域无重复纹理、无模糊斑块色彩一致性对比修复区域和周围色差ΔE 5 肉眼基本无感结构完整性检查边缘和直线是否断裂无锯齿、无错位人脸区域单独裁剪人脸区域评估五官对称、无扭曲人脸区域是老旧照片修复中最敏感的部分。如果照片里有人脸建议单独用人脸检测模型裁剪出人脸区域放大对比修复前后的效果。很多时候整体指标很好但人脸区域出现了「塑料感」——皮肤过于平滑、失去纹理细节。这是感知损失权重过高的典型症状把感知损失从 0.1 降到 0.05 通常能缓解。5.3 一个容易被忽略的技巧分阶段修复把修复和修补拆成两个阶段比用一个模型同时处理效果更好。第一阶段用修复网络处理划痕、噪点、模糊输出一张相对干净的图第二阶段用修补网络处理缺失区域。两个阶段之间加一个掩膜生成步骤自动检测第一阶段输出中仍然异常的区域比如颜色突变、纹理断裂作为第二阶段的输入掩膜。这个分阶段策略的好处是每个模型只需要关注一类退化训练难度降低最终效果反而比端到端模型好。代价是推理时间翻倍但对于离线处理场景完全可以接受。我自己做这个方向踩过最大的坑是一开始贪心想用一个模型搞定所有退化结果训了两周效果还不如分阶段训三天的。后来养成习惯先拆问题再合方案。老旧照片修复这个方向数据构造的质量比模型结构重要分阶段策略比端到端省心验证时肉眼比指标可靠。希望帮到你。本文还有配套的精品资源点击获取
返回列表