☰
GAN图像修复源码详解:基于DCGAN的深度生成对抗修复实战
2026/9/28 1:31:38 网站建设 项目流程

简介:基于 Python 实现的深度生成对抗网络(GAN)图像修复模型项目,面向计算机专业毕业设计、期末大作业及需要项目实战练手的开发者。项目从生成器与判别器网络设计、损失计算、训练流程到图像补全推理均有完整工程实现,难度适中,经过本地编译调试,可运行复现。资源共 7 个文件,其中 6 个 Python 脚本分别承担模型定义、基础操作封装、数据加载与 GAN 训练、分布测试、缺失区域修复等功能,另含 1 份 Markdown 文档说明,压缩包仅 12KB,结构清晰,适合快速理解并扩展。已有 164 人学习下载。项目附带详细文档说明,并曾获导师指导认可、评审 98 分,可作为课程设计或论文实验的可靠参照;通过阅读源码与文档,可掌握 GAN 在图像修复任务上的建模思路与调试验证方法,快速上手二次开发。

1. GAN图像修复模型源码:零基础复现深度生成对抗修复流程

图像修复和图像超分不一样,超分是把低分辨率变成高分辨率,修复则是把缺失、破损、遮挡的区域“无中生有”补回来。传统插值算法补出来的图像在纹理上糊成一片,而用深度生成对抗网络GAN来做图像修复,核心思路是用生成器学习真实图像的分布,再把缺失区域作为条件,让生成器产出符合上下文逻辑的内容。这套基于Python实现的GAN图像修复模型源码,适合正在做毕业设计、期末大作业的计算机相关专业学生,也适合想快速上手GAN实战的开发者。资源里包含完整的模型定义、训练脚本和图像补全脚本,拿到后不需要从零推导网络结构,改改路径和参数就能跑通全流程。

2. 模型结构与文件映射:把源码包拆成看得懂的模块

2.1 从文件清单反推项目设计思路

拿到压缩包先别急着运行,先把文件结构和它们各自承担的角色理清楚。这个项目的文件组织方式很直白:model.py负责定义生成器和判别器的网络结构,ops.py封装了GAN常用的卷积、反卷积、批归一化等底层操作,utils.py是数据加载和预处理工具,train-dcgan.py是训练主入口,complete.py是图像补全脚本。还有一个master目录和simple-distributions.py,后者是对数据分布和潜在空间分布做可视化验证的小工具。从这套划分就能看出,作者刻意把网络结构、训练流程、推理修复拆成了三个独立层次,这在毕设答辩时也很容易讲清楚设计思路。

生成器部分采用的是DCGAN(Deep Convolutional GAN)的典型结构,全卷积网络配合转置卷积做上采样。判别器是卷积层加LeakyReLU激活,输出一个标量表示输入图像为真的概率。ops.py里封装的conv2d和deconv2d函数支持自定义卷积核大小、步长和是否使用批归一化,这意味着你可以不用频繁改动网络主体结构,只调参数就能控制每一层的特征图尺寸。utils.py里的图像加载函数会做缩放、中心裁剪和归一化,归一化范围映射到-1到1之间,这是GAN训练的标准做法,比0到1的归一化更适合生成器的输出层使用tanh激活函数。

2.2 生成器与判别器的关键技术参数

来看model.py里生成器的构造方式。典型DCGAN生成器接收一个100维的随机噪声向量,经过全连接层变换成4x4x1024的特征图,然后通过四层转置卷积逐步上采样,最终输出64x64x3的图像。每一层转置卷积都跟BN层和ReLU激活,最后一层用tanh把输出限制在-1到1。判别器则是四个卷积层逐步下采样,最后一层卷积后接一个全连接输出单个数值,激活函数用LeakyReLU,alpha值默认0.2,斜率太小会导致负区间的梯度消失,太大又会让判别器对真假样本的区分过于尖锐,实践中0.2是平衡得很好的取值。

在ops.py里需要特别注意卷积层初始化方式。作者用的是标准差为0.02的正态分布初始化,这个惯例来自DCGAN原论文,目的是让随机初始化状态下网络各层的输出方差一致,避免信号在深层网络中逐层放大或衰减。如果你自己改代码时发现训练一开始损失就剧烈震荡,优先检查初始化方式有没有被替换成默认的PyTorch初始化。另外,判别器最后一层没有用批归一化,这是有意为之的——BN在判别器最后一层会把真假样本的分布拉向同一个中心,削弱判别器的判别能力,这个细节在答辩时老师问到了你也能接得住。

数据加载部分还涉及一个容易被忽略的知识点:utils.py把训练图片一次性读入内存还是流式读取。如果是小规模数据集比如LFW人脸数据集或者自己收集的几百张图片,一次性读入内存反而更快,因为避免了每轮迭代的磁盘I/O开销。但如果数据量在几千张以上,建议换成ImageFolder延迟加载方案,否则16G内存的机器会在训练第20个epoch时直接OOM崩溃。

3. DCGAN训练全流程:从噪声分布到真实图像分布的拟合

3.1 训练循环与损失函数设计

训练过程遵循标准GAN交替训练框架。每一轮迭代中,先固定生成器,从真实数据集中采样一个batch的真实图片,同时从标准正态分布采样一个batch的噪声向量,生成器用噪声向量产出假图片。判别器分别接收真实图片和假图片计算两个损失,回传梯度更新判别器参数。随后固定判别器,重新采样噪声,让生成器生成假图片并通过判别器得到损失,回传梯度更新生成器参数。整个逻辑集中在train-dcgan.py里,下面是一个对照源码简化后的训练循环骨架:

for epoch in range(num_epochs): for batch_idx, real_imgs in enumerate(data_loader): current_batch_size = real_imgs.size(0) real_imgs = real_imgs.to(device) # ===== 训练判别器 ===== discriminator.zero_grad() z = torch.randn(current_batch_size, latent_dim, 1, 1, device=device) fake_imgs = generator(z) real_labels = torch.ones(current_batch_size, 1, device=device) fake_labels = torch.zeros(current_batch_size, 1, device=device) real_loss = criterion(discriminator(real_imgs), real_labels) fake_loss = criterion(discriminator(fake_imgs.detach()), fake_labels) d_loss = real_loss + fake_loss d_loss.backward() d_optimizer.step() # ===== 训练生成器 ===== generator.zero_grad() z = torch.randn(current_batch_size, latent_dim, 1, 1, device=device) fake_imgs = generator(z) g_loss = criterion(discriminator(fake_imgs), real_labels) g_loss.backward() g_optimizer.step()

判别器训练时传入的fake_imgs.detach()是非常关键的操作,它的作用是切断判别器反向传播到生成器参数的通路,让这一步梯度只更新判别器权重。如果不加detach(),生成器的参数会在这一步被意外更新,导致训练循环变成“判别器更新一次,生成器被连带更新一次”的错乱节奏。损失函数用的是BCEWithLogitsLoss,它把sigmoid激活和二元交叉熵合并在一起计算,数值上比分开用BCELoss更稳定,因为它在内部做了log-sum-exp的数值优化,避免了sigmoid输出接近0或1时梯度消失的问题。

3.2 优化器超参数与潜在空间设置

优化器方面,生成器和判别器分别用独立的Adam优化器,学习率都设置为0.0002,beta1取0.5而不是PyTorch默认的0.9。这里有个值得展开的知识点:Adam默认的beta1=0.9会保留过去梯度的很大的滑动平均,导致梯度更新过于平滑,而GAN训练需要快速响应判别器的动态变化,beta1=0.5让动量项衰减更快,使模型对当前batch的梯度更敏感。如果你用默认beta1=0.9训练DCGAN,常见的症状是生成图像在一段时间内毫无变化,突然从噪声变成清晰图像,然后又跳回噪声状态,这种震荡本质上是优化器动量滞后于对抗动态。

潜在空间维度latent_dim设为100,这个数字是DCGAN论文验证过的经验值。维度太高会让生成器参数学习难度增大并容易过拟合训练集的模式,维度太低则限制了生成器表达能力,无法覆盖真实图像分布的多样特征。simple-distributions.py的作用就是帮你在训练前验证潜在空间采样逻辑——它从标准正态分布采样10000个100维向量,然后分别画出任意两个维度的散点图分布。大多数人跑这个脚本会看到均匀的圆形散点云,这就说明你的随机采样部分没有问题。如果看到明显的条纹状或带状结构,说明随机种子设置有问题,后续训练出来的生成器会产生模式坍塌输出一堆相似图像。

训练过程中我一般每隔500个iteration保存一次生成器输出的假图像,把同一噪声向量在训练不同阶段产生的图像按时间序列排列,能直观看到从纯噪声逐步演化为结构化图像的过程。正常训练情况下,前1000个iteration图像还是一团模糊的色块,但能隐约看到整体色调与真实图像接近;到5000个iteration左右能辨认出物体轮廓和边缘结构;训练到20000个iteration时图像细节已经相当丰富。如果你发现某个阶段图像纹理层次不再改善甚至出现大量重复纹理,这就是模式坍塌的前兆,需要回退到之前的checkpoint并调整学习率或网络结构。

3.3 训练过程中的数据增强策略

数据增强在GAN训练里是一把双刃剑。随机水平翻转是安全的增强策略,它不会破坏图像内容的真实性,还能迫使生成器学习到对称不变特征。但像随机裁剪、随机旋转、颜色抖动这类增强策略要格外谨慎,因为增强后的图像分布和真实图像分布之间产生了偏差,判别器学会的“真”标准变得模糊,生成器也会被带偏。这个项目源码里没有内置数据增强策略,如果你训练出来的模型总是输出相似的构图或色调,可以在前处理阶段加上水平翻转和中心裁剪两个策略,注意中心裁剪要控制比例在80%到95%之间,这样既保留了主体结构,又带入了适当的位置扰动。

4. 图像修复实战:用complete.py跑通“缺失区域补全”流程

4.1 修复算法的核心逻辑

图像补全的逻辑和训练生成器完全不同。训练阶段是让生成器学会从随机噪声映射到真实图像空间,而补全阶段是固定生成器权重,通过梯度下降在潜在空间里搜索一个最优的z向量,让生成出的图像填充到缺失区域后整体视觉上合理。这个优化问题由两个损失项组成:上下文损失衡量填充区域和周围可见像素的差异程度,感知损失衡量生成图像和真实图像在高层语义特征上的接近程度。最后总损失是上下文损失和感知损失的加权和,权重参数在源码的complete.py里有明确的默认值。

由于源码里没有内置自己的感知模型,感知损失的做法通常可以选用一个预训练好的VGG16网络来提取特征——VGG16的特征对像素级改动十分敏感,比用GAN判别器特征更稳定。代码实现步骤如下:

import torch import torch.optim as optim from torchvision import models, transforms def vgg_loss(fake_img, real_img, vgg): fake_features = vgg(fake_img) real_features = vgg(real_img) return torch.mean((fake_features - real_features) ** 2) def contextual_loss(fake_img, real_img, mask): diff = fake_img - real_img diff = diff * mask return torch.sum(diff ** 2) / torch.sum(mask) def total_loss(z, generator, real_img, mask, vgg, ctx_weight, perc_weight): generated = generator(z.unsqueeze(0))[0] full_img = mask * real_img + (1 - mask) * generated ctx_loss = contextual_loss(full_img, real_img, mask) perc_loss = vgg_loss(full_img, real_img, vgg) return ctx_weight * ctx_loss + perc_weight * perc_loss

complete.py里的优化器配置通常是循环迭代500到2000次,学习率0.01,优化器用Adam。迭代次数太少的典型表现是填充区域边缘和已有图像之间有一条明显的分割线,因为生成内容还没有充分调整到和周围纹理衔接;迭代次数太多则容易出现过拟合现象,生成区域过度适应上下文颜色,丢失了应有的纹理细节。训练好的生成器在补全时可以直接从标准正态分布采样初始化z,但更稳妥的做法是用train-dcgan.py最后一次训练时保存的随机采样z作为起点,这个起步值已经有语义信息,比纯随机初始化收敛更快而且不容易陷入局部最优。

4.2 掩码设计与边界约束

掩码mask是决定修复效果的第二关键因素。掩码是一个和图像尺寸相同的0/1矩阵,1表示该像素是已知信息,0表示该像素需要修复。上下文损失计算时乘以掩码,是为了让优化过程只关注空白区域周围那些已知像素的约束,不在未知区域上施加强制一致性要求。掩码边缘的处理尤其重要——硬边界掩码会让优化过程在缺失区域边缘产生约束梯度不连续,修复结果容易出现锯齿边。常见做法是对掩码做高斯模糊处理,让约束权重从完全可信的1平滑过渡到0,这样补全区域和原始区域之间的交接更自然。

实际操作中要把修复区域挖成什么形状也很有讲究。修复矩形区域最容易,掩码生成简单,但矩形边缘的四个角点处的上下文约束强度最弱,优化结果往往在角落处出现微小色差。修复不规则形状区域在视觉上更真实,因为真实世界的破损大多是随机的,但掩码矩阵生成起来更麻烦——一种做法是用OpenCV画一个多边形并填充生成不规则掩码,另一种做法是准备一张黑白二值图直接加载。如果你的修复场景是抹除照片上的多余物体比如行人或电线杆,务必将掩码范围比物体实际轮廓向外扩展5到10个像素,否则优化算法倾向于让生成内容“刚好贴住”物体边界,留下的残余轮廓在视觉上特别明显。

4.3 修复在自定义照片上的操作流程

把训练好的生成器用在自定义照片上要按照以下步骤操作。先把输入图片统一缩放到64x64分辨率,再对缩放后的图片创建掩码。掩码的创建方式决定了修复的灵活性——如果在complete.py中看到类似np.zeros((image_size, image_size))的初始化代码,并配合cv2.rectangle或cv2.circle划定缺失区域,那便是矩形掩码和圆形掩码的处理方式。矩形掩码适合模拟裁剪损伤或删除小面积物体,圆形掩码更接近真实划痕的场景。创建完成后再执行python complete.py --input your_image.jpg --mask your_mask.png --checkpoint generator_epoch50.pth这样的命令,脚本就会输出修复后的图像。如果你传入的掩码是五颜六色的RGB图而不是二值图,脚本会自动做灰度转换再阈值化,但这一步有风险,如果原图某些区域本身就接近白色,可能被误判为掩码区域,所以最好自己先确认掩码图的二值性质。

5. 避坑与常见问题排查:DCGAN图像修复的五个典型翻车现场

5.1 训练不收敛:判别器损失归零,生成器输出全是噪声

现象:训练几千个iteration后,判别器对真实图像的损失已经降到接近0,但生成器输出的图像始终无法形成有效结构,看起来完全随机。这说明生成器并没有学会骗过判别器,而是判别器已经崩溃——它退化成了一个极端函数的判别器,对真实样本输出极高的置信度,对生成样本输出接近0,梯度为零导致生成器无法获得有效反传信号。

原因:判别器能力过强的原因在于网络参数量与生成器不匹配,或者判别器训练步数多于生成器训练步数。DCGAN原论文里生成器和判别器各更新一次,其中判别器先更新后生成器更新,这个顺序不能颠倒。另一个容易触发的原因是没有做标签平滑处理,真实标签被设为1.0,判别器学习到了一个极其尖锐的决策边界,对真实样本的置信度无限接近1。

解决:把真实标签从1.0改成0.9的随机抖动,即所谓的one-sided label smoothing,让判别器的决策边界变成软边界。具体来说对真实批次使用0.8到0.95之间的随机值作为标签。同时确认判别器和生成器的通道数是1:1对等关系,不要单独加宽判别器某一层卷积的通道数。如果出现判别器损失先降到0然后一直不动的情况,马上停掉训练,检查最近的checkpoint中生成器是否还有有效输出,如果有,回退到那个checkpoint并降低判别器学习率到0.0001。

5.2 模式坍塌:生成图像多样性不足,只输出两到三种模式

现象:每隔几百个iteration检查生成图像,发现训练集里有各种不同的目标,但生成器只学会了其中一两种类型的图像输出,比如人脸数据集里只生成正面脸部,从不生成侧脸或不同表情。多样性丢失的直接原因是生成器通过反复输出同一个模式骗过判别器,判别器没有能力对这种重复模式进行惩罚。

原因:模式坍塌的根本原因在于GAN对抗博弈的不稳定性。生成器发现输出某一类图像骗过判别器的成本最低,就会把潜在空间里的多个区域映射到同一个输出图像上,导致不同z向量生成同一张图。潜在空间维度不足时更容易出现这种现象,100维空间对小规模数据集本应足够,但如果数据集的图像背景极其单一比如全是同一种颜色背景的证件照,模式坍塌依然是高发问题。

解决:先尝试用更大的潜在空间维度,比如把latent_dim从100提升到200,看看生成图像的多样性是否有改善。如果不行,检查是否用了条件生成器结构,比如把图像类别标签作为额外输入注入生成器,让生成器被迫为不同类别生成不同结构的内容。最后的手段是改用WGAN-GP损失函数替代原始GAN的目标函数,WGAN使用Wasserstein距离度量真实分布和生成分布的差异,梯度比原始GAN的JS散度平滑得多,在众多实战案例中模式坍塌比例会明显下降,但需要把训练循环中生成器和判别器的更新次数改为“判别器每更新5次生成器更新1次”的比例。

5.3 补全结果内部有明显分界线或者模糊色块

现象:在complete.py修复一张缺失区域较大的图片时,修复区域内部看起来模糊一片,像是一团没有纹理的颜色,跟周围清晰区域格格不入。特别是修复区域超过图像面积30%的时候,这种情况出现频率很高。

原因:补全阶段只能通过上下文损失中的已知像素来指导生成,优化过程中感知损失对图像的约束范围有限,近端匹配难以精确重建小而密的纹理。本质上不是代码bug,而是GAN修复的固有限制——生成器对大规模缺失内容的推断能力不足。原因也可能是卷积感受野不够大,当前生成器只能看到局部纹理特征,无法推断远距离的结构关系。

解决:把输入图像先裁剪成多个小区域分开修复再拼接,每个小区域都在不完整区域内,这种情况生成器更容易推断出合理内容。另外可以调高感知损失的权重,具体参数是complete.py里的perc_weight,从默认的0.1调高到2.0,感知损失会强制生成图像在高层语义上更接近真实图像,对纹理恢复有明显帮助。

5.4 显存溢出或内存不足

现象:训练脚本启动后不久出现RuntimeError: CUDA out of memory退出,或者CPU训练模式下内存占用持续升高到系统卡死。

原因:一次加载的图片数量过多导致显存峰值过高,单个batch的图像尺寸放大时显存占用率急剧上升。接下来判别器和生成器的梯度同时保留在显存中,反向传播时峰值叠加会翻倍。

解决:执行以下步骤逐步缩小资源占用。先把batch_size从默认值逐步减半直到模型正常跑通,把图像从64x64缩放到32x32作为短期调试方案,这两步是最直接的显存释放手段。再检查utils.py里是否把原始分辨率的图片全部读入内存后才统一缩放,改成边加载边缩放或者调用torchvision.datasets.ImageFolder配合transforms.Resize流式读取。对于CPU训练的用户,确认没有在每一个epoch后把整个数据集的梯度保留下来,正确写法是在每次优化器step()后清空中间变量,具体做法是在每次迭代的batch输出后及时调用torch.cuda.empty_cache()或者删除不再使用的中间张量。如果显存确实只有4G,建议直接放弃64x64训练,用32x32跑通全流程再想办法换硬件。

5.5 修复结果颜色总体偏灰或偏暗

现象:修复出来的图片整体颜色比原图饱和度低很多,看起来像是罩上了一层灰纱。生成图像的所有区域包括已知区域都偏灰,而不仅仅是修复区域偏灰。

原因:生成图像的最后输出是tanh激活层,输出范围在-1到1之间,数据预处理时把它映射回0到255的像素值域,如果归一化时把像素值从0到255缩放到-1到1又改坏了数据对齐方式。修复区域颜色偏灰还有一个原因是在潜空间优化过程中上下文损失和感知损失的比例失衡,前三步梯度更新方向主要被感知损失主导,颜色分布被拉向VGG的统计均值方向。

解决:核查代码中utils.py的预处理函数,确认生成的图像像素值重新映射完成。验证方法是在修复流程中加入颜色直方图对比步骤,直接对比输入图像全图直方图、修复区域周围直方图、输出图像全图直方图的均值与标准差,如果差异超过25%说明颜色修正没有生效。也可以把complete.py输出图像的数据类型从float32强制转为uint8,防止在保存时被覆写为空值或溢出值。最后,把上下文损失的权重调高到感知损失的5到10倍,让整体色彩分布更贴近已知区域。

6. 从单张修复到批量评估:用指标筛选可用checkpoint

训练完epoch 50和epoch 200的checkpoint差距有多大?从生成器输出的fake图像上肉眼观察是一个维度的判断标准,但更可靠的筛选手段是用量化指标做批量评估。在补全任务里,两个最常用的指标是PSNR(峰值信噪比)和SSIM(结构相似性指标)——PSNR通过计算修复图像与原始图像之间像素级差异的均方误差来衡量失真程度,数值越高代表像素还原越逼真;SSIM则结合亮度、对比度和结构三个维度的信息来衡量整体感知质量,数值越接近1越好。但注意瑕疵——GAN修复一个突出的现象是像素级指标很高但视觉感受奇怪,这是因为GAN用对抗损失优化出来的结果更注重感知自然度而不是逐像素匹配,所以看到同一个修复结果出现PSNR值提升了但SSIM反而下降的组合时不要惊慌,这不是代码问题,是衡量指标和优化目标不完全一致导致的。

更可靠的评估流程是把整个测试集批量跑一遍修复,然后按区域划分统计指标。具体做法是准备30到50张测试图像,每张重新生成随机的掩码区域,保证每张图中缺失区域的面积和位置都有差异,然后统一在complete.py中执行修复并保存输出。完成后对输出图像和原始图像计算整体PSNR、SSIM、修复区域的INPSNR(区域峰值信噪比),以及修复区域与已知区域的均值色差。这组指标组合比单张图直观判断能更全面地反映模型对不同位置、不同面积缺失的适应能力,如果某个模型在整体PSNR上领先但在修复区域的INPSNR上落后,说明它更适合状态较好、背景干扰少的图像,不适合大面积破损的场景。

另外需要养成一个检查习惯:把训练过程中每个epoch保存的生成器权重文件名和对应时的loss值记录在训练日志里,用固定z向量在第5轮、第20轮、第100轮生成三张图像,批量放到一起看。如果第20轮到第100轮的图像差别已经很小,说明模型早已收敛,后面轮次的checkpoint是冗余的,可以直接删掉节省磁盘空间。如果你的模型此时出现反复震荡,比如第30轮生成的图像比第20轮生成的还模糊,就要提高警惕,可能是判别器在后期过于强势压制了生成器的学习。从那以后我每次做GAN训练都会强制走一遍这个流程:先用固定z取10个轮次生成图像序列观察趋势,再对最优checkpoint做30张批量修复评估,最后用PSNR和SSIM双指标定档。整个过程半小时就能跑完,却能避免把时间浪费在训练那些看似loss值光滑、实际上毫无实用价值的垃圾模型上,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询