基于GAN的图像修复实战:生成对抗网络原理与PyTorch实现 简介基于Python编程语言实现深度生成对抗网络图像修复模型的完整工程项目源码与项目文档齐备主要面向毕业设计、课程设计与项目开发场景也适合具备一定深度学习基础的学习者作为实践参考。压缩包共包含七个文件其中六个为Python脚本一个为Markdown说明文档整体体积仅12KB脚本覆盖模型定义、网络操作、数据工具、训练与图像补全等核心环节目录精简且职责清晰。源码已经过严格测试可直接运行并在此基础上延展配套文档详细介绍了项目结构、运行方式与使用细节便于快速理解生成对抗网络的构建逻辑和图像修复实现思路。目前已有253人学习/下载读者可借助这份紧凑而完整的代码骨架快速跑通图像修复实验并针对自身课题进行调整与二次开发适合毕业设计、课程设计或项目起步阶段参考。1. 项目概述与技术选型思路1.1 这个项目解决什么问题图像修复Image Inpainting是计算机视觉里的一个常青话题说人话就是把图片中缺失、被遮挡、被损坏的区域补回来并且让观感上看不出修补痕迹。修复的对象可能是老照片上的划痕、被物体遮挡的背景、因为裁剪或擦除造成的空洞甚至是有害信息遮挡后的内容重建。这个需求在文物保护、影像后期、医疗影像、自动驾驶去除遮挡物等领域都有直接应用场景。我这次做的项目是基于深度生成对抗网络GAN的图像修复模型语言用Python框架选PyTorch整体从数据准备、模型搭建到训练推理全部手写实现并配套完整源码和项目文档。之所以选这个题目是因为图像修复本身是一个特别适合做毕设或课程设计的场景它比图像分类、目标检测这种“标准任务”更有视觉冲击力演示效果好同时技术栈覆盖全面——从卷积神经网络、注意力机制、对抗训练到损失函数设计几乎把深度学习里的核心知识点都串起来了。做完一个项目基本相当于把生成模型这条线吃透了。这个项目适合的人群包括正在选毕设题目的本科生、需要交课程项目的研究生、想从分类任务转向生成任务练手的开发者以及秋招想往AIGC算法方向走的同学。难度上属于中等偏上但我会把每一步拆开讲清楚跟着做完全来得及。1.2 为什么用GAN而不是传统方法影像修复这个方向其实很早就有了早期方法大致有两类一类是基于扩散的Diffusion-based从空洞边缘一圈一圈往里填充效果像用图章工具一点一点补慢且只能处理小区域另一类是基于Patch匹配的Exemplar-based在图像其他区域找相似纹理块贴到空洞里对于背景纹理规整的图效果尚可但遇到复杂结构或者语义信息比如人脸的五官就完全不行了。这两类方法的本质缺陷是它们都在做“像素级别的相似度匹配”不理解图像的高层语义。说白了就是不懂“这里应该有一张桌子”“这里应该是眼睛”。修复结果经常出现纹理错乱、结构扭曲的情况。而GAN恰好能解决这个问题——生成器在对抗训练的过程中会被迫学习真实图像的分布也就是说它知道“合理的图像长什么样”。有了这种先验知识面对一个大空洞时它生成的不再是一堆纹理的堆砌而是符合语义逻辑的内容。当然现在也有基于扩散模型的修复方案效果确实更强但计算资源要求高训练周期长对毕设来说不够友好。GAN在这个场景下的平衡点最好训练可控、显存要求适中、效果可视化明显而且源码和论文资料积累多踩坑容易找到参考。1.3 项目整体架构与工作流程整个项目的处理流程可以用一条线串起来原始图片 → 人为生成掩码模拟损坏区域→ 将图片和掩码输入生成器 → 生成器输出修复结果 → 判别器判断修复结果是否真实 → 计算多种损失函数 → 反向传播更新参数 → 循环迭代直到收敛。模型的总体结构采用“双网络对抗”框架生成器Generator负责根据残缺图像和掩码生成完整的、看起来合理的图像。判别器Discriminator负责区分输入图像是“真实原图”还是“生成器修复出来的图”。两个网络互相博弈生成器想骗过判别器判别器想抓住生成器的破绽。这种对抗训练的方式使得生成器输出的图像越来越逼真。这是GAN系列方法的通用训练范式具体到图像修复任务生成器的设计会更复杂——它不仅要生成逼真的纹理还要保证生成区域和周围已有像素在颜色、光照、结构上完全协调任务难度比“从噪声生成图像”的传统GAN更高。2. 核心原理与关键技术解析2.1 生成器网络的结构设计生成器是整个修复模型的重头戏我采用的是类似U-Net的编码器-解码器结构并在此基础上加入了部分卷积Partial Convolution和注意力机制Attention。先解释为什么用U-Net结构图像修复要求输出和输入是同一尺寸的大图U-Net通过跳跃连接Skip Connection把编码器各层的特征直接送到解码器对应层这样保留了大量空间细节信息避免深层网络丢失边缘、纹理等细节。编码器部分用步长为2的卷积逐层下采样每下一层特征图的尺寸减半、通道数翻倍。这样做的目的是扩大感受野让网络能“看到”更大范围内的上下文信息。想象一下如果空洞区域很大而你只盯着空洞旁边几十个像素根本猜不出这里该填什么必须看到更大范围的场景结构才能做出合理的推断。解码器部分用转置卷积或者最近邻上采样把特征图逐步放大回原图尺寸。这里有一个关键改进点部分卷积Partial Convolution。普通的卷积操作在处理带掩码的图像时有一个严重问题——空洞区域的像素值通常是0或噪声会被当成有效信息参与卷积计算导致生成结果出现明显的色斑或模糊边界。部分卷积的处理方式是卷积的时候只对有效区域的像素做计算同时自动更新掩码随着层数加深原本无效的区域逐渐变成有效区域。这套机制是NVIDIA在2018年提出的Partial Convolutions for Image Inpainting效果比普通卷积好一大截。2.2 判别器与局部判别机制判别器我采用的是PatchGAN结构不是输出一个0到1的全局真伪概率而是输出一个N×N的矩阵每个元素对应原图的一个小区域是真还是假。这个设计的动机很实际图像修复好不好关键看局部细节是否逼真——你可能整体看觉得没问题但放大看某个小区域纹理、边缘完全对不上。PatchGAN把注意力放在局部块上强制生成器在每个局部区域都生成足够真实的内容。PatchGAN的另一个优势是参数少、计算快因为它没有全连接层全部是卷积操作输入也不需要固定尺寸灵活性很高。实际使用中我会把判别器的感受野设置为70×70对应每个输出节点的感知区域这个数值在原始论文和大量复现中被验证是有效的平衡点。此外为了让判别器也能关注到空洞区域本身我实现了全局判别和局部判别两个分支全局判别器看整张修复后的图像局部判别器只裁剪空洞区域及其周边一小块进行判断。两个分支的损失相加作为最终判别损失。这样生成器就必须同时保证“整体和谐”和“局部逼真”。2.3 损失函数的设计思路这是整个项目里最需要花心思的地方。图像修复的损失函数不能只用GAN那套对抗损失否则模型训练不稳定容易输出带有伪影的图像。我采用了一个复合损失函数由以下几部分组成L1损失像素重建损失计算生成图像和真实图像在像素级别的绝对误差。L1损失相比L2MSE损失的优点是它对离群点不敏感生成的图像边缘更锐利不会像L2那样把结果平均得过于平滑。感知损失Perceptual Loss把生成图像和真实图像分别送入预训练的VGG16网络取中间若干层的特征图做L1距离。这个损失衡量的是“语义层面的差异”而非像素层面的差异。举例来说两张图像素值可能有差异但人眼看都是“一只猫”它们的高层特征就应该是接近的。这个损失能显著改善修复结果的语义合理性。风格损失Style Loss计算特征图的Gram矩阵差异用来约束生成图像的纹理风格与全局一致。在修复背景纹理草地、墙壁、水面时效果特别明显。对抗损失Adversarial Loss使用LSGAN的损失形式最小二乘损失用判别器输出的特征计算。选择LSGAN而不是原始GAN的交叉熵损失是因为LSGAN的梯度更平滑训练更稳定生成的图像质量更高。空洞区域加权损失在计算L1损失时对空洞区域内的像素施加更大的权重因为修复任务的核心就是把空洞区域填好网络应该把主要“精力”放在这里。这几种损失叠加在一起如果比例不合适会出现互相拉扯的情况比如像素损失过大导致图像模糊对抗损失过大会导致训练震荡。我最终使用的比例是L1损失权重1.0感知损失权重0.1风格损失权重120因为Gram矩阵数值很大对抗损失权重0.01空洞区域权重放大为普通区域的10倍。这个比例是通过多次实验调出来的可作为初值参考具体任务还需微调。3. 环境配置与数据集准备3.1 Python环境与依赖安装这个项目我建议使用Python 3.8或3.9版本。PyTorch建议安装1.10以上版本Linux和Windows均可运行不过训练推荐用Linux NVIDIA显卡Windows上做推理和调试也没问题。如果只是做课程设计CPU也能跑起来只是训练速度会慢很多建议用预训练模型直接做推理演示。创建虚拟环境并安装依赖的步骤如下conda create -n inpainting python3.8 conda activate inpainting pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install opencv-python numpy pillow tqdm tensorboard matplotlib需要说明的是CUDA版本的PyTorch安装要结合自己机器上的显卡驱动版本来选。可以用nvidia-smi查看驱动支持的CUDA版本选择对应的安装命令。如果驱动版本较老就装cpu版本先跑通流程后续再去申请带GPU的服务器训练。3.2 数据集选择与掩码生成训练数据我优先推荐 Places2 数据集这是图像修复领域最常用的基准数据集包含超过800万张场景图片涵盖各种室内外场景很适合训练泛化能力强的修复模型。如果你的机器资源有限也可以先用COCO数据集的一个子集或者干脆用torchvision.datasets.Place365接口加载一部分数据来跑通全流程。我当时初学的时候最早是拿几百张自然风光图片训练效果其实也还过得去关键是流程先跑通。数据预处理中有一环特别重要就是随机掩码的生成。掩码是一个与输入图像同尺寸的二值矩阵取值为1的位置表示缺失区域取值为0的位置表示有效区域。掩码形状需要考虑多样性矩形块、不规则多边形、细长条、多个小区域混合这样才能让模型见到丰富的损坏场景防止过拟合到某种固定形状。我写了一个掩码生成器每次训练迭代时随机生成不同类型的掩码并限制空洞占比在10%到40%之间。占比太小模型学不到东西占比太大上下文信息不足生成难度过高训练也不稳定。def generate_mask(shape, max_vertex8, max_angle4): mask np.zeros(shape, dtypenp.uint8) num_polygons random.randint(1, 4) for _ in range(num_polygons): vertex_count random.randint(3, max_vertex) points [] for _ in range(vertex_count): x random.randint(0, shape[1]) y random.randint(0, shape[0]) points.append((x, y)) cv2.fillPoly(mask, [np.array(points)], 1) if random.random() 0.5: mask np.maximum(mask, generate_rectangle(shape)) return mask训练时原图和掩码合在一起作为生成器输入掩码单独用于损失函数计算时区分有效和无效区域。注意别忘记在数据增强阶段保持掩码和图像的同步变换比如随机翻转、缩放时图像变换了掩码也必须做一模一样的变换否则掩码标注的位置就和实际损坏位置对不上了。4. 模型实现与训练流程4.1 项目目录结构一个规范的工程目录可以让整个项目加分不少尤其毕设要交文档和代码结构清晰很重要。我采用下面的组织方式GAN-Inpainting/ ├── config.py # 配置文件集中管理超参数 ├── datasets.py # 数据加载和掩码生成 ├── models/ │ ├── generator.py # 生成器网络定义 │ ├── discriminator.py # 判别器网络定义 │ └── losses.py # 损失函数定义 ├── train.py # 训练主脚本 ├── test.py # 推理脚本 ├── utils.py # 工具函数可视化、保存模型等 ├── checkpoints/ # 模型权重保存位置 └── results/ # 修复结果输出配置文件单独拆出来的好处是调参不用改源码所有超参数集中在一个文件里一目了然。我在config.py里配置了batch size、学习率、训练轮数、损失权重、GPU编号等参数这也是很多参考项目不太注意的细节但对用户使用体验来说影响却很大。4.2 生成器实现细节生成器分为两个阶段。第一阶段是粗修复网络先生成一个模糊但结构正确的初步结果第二阶段是精修复网络输入粗修复结果和掩码再输出一个细节更丰富的最终结果。这种两阶段设计来自PatchMatch等经典工作的启发也被证明能显著提升修复质量。粗修复网络结构相对简单编码器使用6层卷积解码器使用最近邻上采样加卷积中间输出特征图尺寸是32×32。精修复网络在这个基础上增加了注意力层。注意力层的核心作用是让模型在特征层面“搜索”与空洞区域相似的特征块并用相似块的信息来填充空洞对应的特征位置。打个比方如果图像里有一块天空被抠掉了注意力层会在图像其他部位找到同样是蓝色的、纹理接近的天空区域把那个区域的特征“搬运”到空洞位置。虽然注意力机制不是完美的但确实能有效减少修复时常见的重复纹理和结构错乱。以下是精修复网络中注意力层的关键实现简化版class AttentionModule(nn.Module): def __init__(self, in_channels): super().__init__() self.conv_f nn.Conv2d(in_channels, in_channels // 2, kernel_size1) self.conv_g nn.Conv2d(in_channels, in_channels // 2, kernel_size1) self.conv_v nn.Conv2d(in_channels, in_channels, kernel_size1) self.softmax nn.Softmax(dim-1) def forward(self, x): B, C, H, W x.shape f self.conv_f(x).view(B, -1, H * W).permute(0, 2, 1) # 关键点query g self.conv_g(x).view(B, -1, H * W) # 关键点key v self.conv_v(x).view(B, -1, H * W) # 用于聚合的value attn self.softmax(torch.bmm(f, g)) # 注意力权重 out torch.bmm(v, attn.permute(0, 2, 1)) return out.view(B, C, H, W)这里注意注意力机制的数值稳定性靠softmax保证但实际训练时注意力权重容易出现极端分布建议在后面加一层LayerNorm或者残差连接来稳定训练。这是我踩过的一个坑后面会细说。4.3 训练循环与损失计算训练循环采用的是“先训判别器再训生成器”交替进行的策略这也是GAN训练的标准范式。每一步迭代中先固定生成器参数更新判别器然后固定判别器参数更新生成器。两个优化器都使用Adam学习率设为0.0002beta1设为0.5GAN训练中Adam的beta1通常不使用默认的0.9因为0.5能让训练更稳定。判别器的输入有两类一类是真实图像拼接对应区域的掩码标签设为1真另一类是生成器输出的修复图拼接掩码标签设为0假。判别损失使用最小二乘形式LSGAN# 判别器损失希望真实图像的判别结果接近1生成图像的判别结果接近0 real_loss torch.mean((d_real - 1) ** 2) fake_loss torch.mean(d_fake ** 2) d_loss 0.5 * (real_loss fake_loss)生成器损失包括前面讲的四种损失总和但注意对抗损失的反向传播生成器要尽量让判别器把生成图像判为真实所以对抗损失里生成图像对应的标签设为1这部分的梯度会流回生成器迫使生成器生成更真实的图像。我开始学的时候经常在这搞混需要特别留意。4.4 训练参数配置训练参数我建议从小的开始试不要一上来就跑大数据集。下面是验证阶段效果不错的配置参数推荐值说明输入图像大小256×256兼顾显存和效果太小细节丢失严重Batch Size8单卡显存不够就降到4或2初始学习率0.0002Adam优化器默认配置常用训练轮数100视数据集规模和Loss收敛情况调整损失权重见上文根据任务微调学习率衰减每30轮×0.5让后期训练更稳定如果显存只有8G输入尺寸降到128×128batch size设为4也能训练只是修复细节会打折。我建议课程设计用256×256最多一个晚上能跑完一个小数据集毕设如果时间充裕可以完整在Places2的子集上训练3到5天。5. 推理流程与效果评估5.1 推理脚本的设计训练完成之后推理部分反而简单。加载训练好的生成器权重输入一张图像和掩码前向传播一次就得到修复结果。但有几个实用细节需要注意掩码的预处理掩码需要和训练时保持一致的数据处理方式包括归一化到0到1之间、resize到相同尺寸。推理时常用的是把掩码区域像素值置为0或127灰度值我在推理代码中提供了一个可选参数可以把输入图像中损坏区用均值填充。因为网络训练时看到的是带掩码的输入推理时保持相同模式效果才会一致。重叠区域的平滑如果用户输入的掩码区域不规则或者边界特别生硬修复结果可能会有边缘痕迹。可以在推理后对修复区域和原图进行边界融合推荐使用OpenCV的seamlessClone做泊松融合或者简单地在空洞边界做一个高斯模糊的alpha blending。5.2 模型效果评估指标除了肉眼观察还需要用客观指标评估模型效果。常用的两个指标是PSNR峰值信噪比和SSIM结构相似性这两个指标都是将修复结果与真实原图对比PSNR越大越好一般25dB以上算不错30dB以上就是比较理想了它衡量的是像素级别的误差大小。SSIM越接近1越好它衡量的是结构信息的保持程度也就是人眼感知上的相似性。但这两个指标都有局限它们对纹理细节的评判并不准确。有时PSNR很高的修复图像人眼看起来却怪怪的因为结构合理但纹理过于平滑这是L1/L2损失的常见问题。所以我的经验是指标作为参考最终效果还是要靠人眼判断——把修复结果放大到100%看纹理细节看边缘是否锐利看空洞区域和周围光照是不是协调。5.3 结果可视化与演示给毕设答辩做演示时我强烈建议做一个直观的对比图展示一行展示原图一行展示带掩码的残缺图一行展示修复结果。最好能挑一组“修复前后对比”和“真实原图对比”同时出现在一张图上的样例这种效果比口头解释强太多。给个参考代码用matplotlib可以快速生成对比图import matplotlib.pyplot as plt def visualize(original, masked, output, save_path): fig, axes plt.subplots(1, 3, figsize(15, 5)) titles [Original, Masked, Output] images [original, masked, output] for ax, img, title in zip(axes, images, titles): ax.imshow(img) ax.set_title(title) ax.axis(off) plt.tight_layout() plt.savefig(save_path, dpi150, bbox_inchestight)如果想让演示更“抓眼球”可以录制一个动态过程输入一张损坏图把模型在多个迭代步比如用不同训练轮数的checkpoint推理或者使用GAN的插值特性生成一系列渐进修复的结果的输出做成GIF展示模型从一团模糊到细节清晰的变化。答辩现场放这个视频基本能让评委眼前一亮。6. 常见问题与排查技巧实录这一部分是我在实际跑项目时真正遇到过的坑有些问题甚至花了好几天才定位。写出来供大家参考Q1训练不收敛Loss值振荡剧烈常见原因有三个学习率过高、batch size过小、判别器能力太强生成器跟不上。排查思路是先把学习率降到0.0001或更低试试看Loss曲线是否趋于平滑如果波动仍然很大就在训练循环里降低判别器的更新频率比如每训练2次判别器才训练1次生成器用这种“不平衡训练”来稳定对抗过程。还有一个容易被忽视的细节训练开始时最好用较大的L1损失权重让生成器先学会“填对内容”后期再逐步加大对抗损失的权重这样生成器不容易在前期就学崩。Q2生成结果出现棋盘格伪影棋盘格伪影通常是不合理的上采样方式导致的。我之前用转置卷积做上采样时生成图像总是有一块一块的方格状纹理非常难消除。查了一圈发现是转置卷积的“不均匀重叠”问题——卷积核在滑动时某些位置重叠次数多某些位置少导致像素强度不均匀。解决办法有两个一是改用最近邻上采样或双线性插值先放大再普通卷积二是用PixelShuffle操作。我建议直接换成最近邻上采样普通卷积的组合省事且效果稳定。Q3修复结果边缘模糊边界过渡不自然这个问题的核心是掩码边界信息没有被网络有效利用。我当时的解决方案是在输入层面把掩码做一次高斯模糊模拟边界渐变让网络知道边缘附近有过渡区域在损失层面对空洞边界附近像素的L1损失权重进一步加大让网络更关注边界的生成质量。Q4Attention层导致显存溢出注意力机制的计算复杂度是O(H²W²)特征图越大显存占用越高。我在一张512×512的图上训练时直接OOM了。解决办法带注意力层之前先下采样在32×32或更小分辨率上做注意力计算再把结果上采样回原尺寸或者用稀疏注意力/窗口注意力来替代全局注意力。对图像修复来说在低分辨率上做全局注意力、在高分辨率上用卷积细化效果就已经足够好了。Q5生成的图像色调偏灰或整体偏暗这通常是因为训练数据没有做合适的归一化或者生成器最后一层用了Sigmoid激活而数据范围没有映射对。我当时犯的错误是数据归一化到[0,1]但生成器最后用Tanh输出范围[-1,1]导致结果整体偏暗。要保证数据预处理和网络输出激活函数匹配一致如果是Tanh数据就要归一化到[-1,1]如果是Sigmoid数据范围应该是[0,1]。Q6模型在验证集上效果不错但测试集上很差这是典型的过拟合。图像修复模型对训练数据分布很敏感如果训练集里全是室内场景拿到户外照片上就崩。缓解办法是数据增强随机旋转、色彩抖动、水平翻转和加大数据多样性。另外可以试试在预训练VGG特征上做微调把感知损失的权重适当调高因为感知损失本身能提供一定的泛化能力——它让模型学到的不是“记住像素”而是“生成语义合理的结构”。7. 项目文档与后续扩展建议7.1 项目文档应包含哪些内容这个标题是“源码项目文档”所以文档部分也需要用心。一份完整的项目文档至少应包含这些内容项目背景和意义、国内外研究现状可引用几篇经典论文如Partial Convolution、EdgeConnect、GAN Inpainting、技术路线图、核心算法的推导过程、实验环境配置说明、数据集描述、模型结构图和参数量表、训练过程记录Loss曲线和评估指标变化、结果展示修复前后对比图、创新点的总结、以及可行的改进方向。文档里的图表很重要架构图、流程图、结果对比图每个都值得认真画。这些不只是为了“凑篇幅”而是老师或评审判断你是否真正理解项目的直接依据。7.2 后续可以扩展的方向如果这个项目做完还有余力有几个很容易出彩的扩展方向从单一掩码到任意掩码增加掩码生成器的复杂度训练模型支持用户手动涂鸦式掩码做成一个交互式修复工具演示效果会非常好。从图像修复到视频修复把单帧修复扩展到视频序列关键是保持时序一致性这又可以作为研究生级别的研究方向。从CNN到Transformer尝试把生成器的骨干网络替换为Swin Transformer或者结合ViT的结构看看对长距离依赖关系的建模能力能否带来进一步的提升。结合文字引导修复加入CLIP等跨模态模型让用户用文字描述“这里应该是什么”模型根据语义描述来修复空洞。这个方向目前是研究热点做出简单版demo并不难但足够展示技术深度。根据我个人经验最值得投入精力的扩展方向是第一个——交互式修复工具。因为它的演示效果最好也最能体现“从模型到产品”的完整链路。其他方向在答辩时列在“未来展望”里就够有分量了。最后再说一个小的实用建议训练过程中定期用TensorBoard记录损失曲线和生成结果不仅方便调试后续写文档写实验报告时直接截图就是现成的素材省去补实验的时间。这些积累最后都会变成项目里最亮眼的部分。本文还有配套的精品资源点击获取