
简介面向本科毕业设计与深度学习初学者的模糊人脸图像增强系统源码包聚焦人脸去模糊任务兼顾全局超分辨率与局部修复适合作为图像增强方向的项目参考。资源共20个文件以Python源码为主包含9个py脚本涉及数据输入、模型搭建、测试等环节另有6个pyc编译文件、2张PNG图片含UNetLike网络结构示意以及xml、txt、md等说明文档压缩包仅362KB结构紧凑。目前已有74人学习下载。包内提供完整工程实现包括FaceInput.py、FaceEnhance.py、Test.py等核心脚本附带训练日志与README可帮助读者快速理解基于CNN的模糊人脸增强流程掌握数据预处理、模型训练与指标评估的完整思路便于直接运行或在此基础上扩展改进。1. 为什么把“模糊人脸图像增强”做成本科毕设是可行的门禁系统抓拍到的人脸经常是糊的夜间监控截图里人脸只有一片虚影主观题试卷扫描件里的人脸照也带着运动模糊。这些场景落到技术上就是一个统一问题输入一张模糊人脸输出一张清晰可辨认的正面人脸。这个任务既不算“简单到没东西写”也不会“复杂到本科生做完毕业设计”是个天然适合深度学习毕设的中间地带——有明确的输入输出、有公开数据集可以表达、有评价指标可以量化。而且它有一个科研以外的优势答辩时随便找一个模糊的人脸照片往系统里一放出图效果肉眼可见评审老师不用看指标就能 get 到做了什么。哪怕最终效果达不到商用级别结合深度学习的链路完整性也足够支撑一篇合格的本科毕业论文。关键是别贪大把“单一模糊类型、单一模型、有限的增强效果”这几个边界在开题时就定住系统就能在三个月内跑通。2. 问题定型与数据准备先分清“去模糊”和“超分”再动手2.1 三个常见任务边界别混成一锅粥模糊人脸增强这个标题下其实藏了三种常见任务。图像去模糊是把运动模糊、散焦模糊恢复成清晰图超分辨率是把小图放大并补细节通用增强是调亮度、对比度、去噪。毕设最怕的是把这三者搅在一起最后说不清楚系统到底干了什么。我的建议是锁定“去模糊”作为核心任务因为它的退化模型好描述、数据集好造、效果也直观。如果开题时老师问“你的增强指什么”就答“以去运动模糊为主兼顾轻度散焦模糊”然后在论文里把退化模型写清楚——模糊图可以近似看作清晰图与模糊核的卷积再加噪声用公式表达系统就是在做这个逆过程。2.2 数据集方案公开数据集与自造数据两条腿走路人脸去模糊绕不开公开数据集。常见的选择是 GoPro 数据集和 HIDE 数据集它们包含真实运动模糊和对应的清晰图是领域内的基准。但这两个数据集有个现实问题体积大、下载慢、人脸占比不一定适合做人脸增强任务。本科毕设如果算力有限我建议只用它们的子集或者干脆换另一条更可控的路线——用高清人脸数据集加人工模糊核来合成。这里给出一个具体的合成方案从 FFHQ 或 CelebA-HQ 里抽取清晰人脸图用 Python 生成随机运动模糊核对清晰图做卷积得到模糊图。这样 train、val、test 三个集合完全可控还能在测试时精确知道每一张图的“清晰版本”评价指标算起来没有任何争议。下面这段代码是合成模糊图的核心逻辑import numpy as np import cv2 def generate_motion_kernel(kernel_size15, angle_deg30): 生成指定角度和长度的运动模糊核。 kernel_size 越大模糊越重angle_deg 控制运动方向。 kernel np.zeros((kernel_size, kernel_size), dtypenp.float32) center kernel_size // 2 rad np.deg2rad(angle_deg) dx, dy np.cos(rad), np.sin(rad) for i in range(kernel_size): x int(round(center (i - center) * dx)) y int(round(center (i - center) * dy)) if 0 x kernel_size and 0 y kernel_size: kernel[y, x] 1.0 kernel / kernel.sum() return kernel def blur_face(sharp_img, kernel): 对清晰人脸图做卷积模糊并叠加轻度高斯噪声。 返回 0~255 范围的模糊图类型为 uint8。 blurred cv2.filter2D(sharp_img, -1, kernel) noise np.random.normal(0, 2, blurred.shape).astype(np.float32) blurred np.clip(blurred noise, 0, 255).astype(np.uint8) return blurred这段代码里有两个参数直接影响训练效果。第一个是kernel_size一般取 11 到 21 之间太小模糊程度不够模型轻轻松松就能恢复体现不出增强的价值太大则信息丢失过多模型根本学不回来。第二个是angle_deg训练时应该随机取 0 到 180 度让模型见过各种方向的运动模糊避免只在水平模糊上有效。实际使用时每张清晰图随机生成 1 到 3 个模糊核选其中一个来做增强相当于做了数据扩增。值得说明的是合成模糊和真实模糊之间存在 domain gap所以训练后期可以用少量 GoPro 的真实模糊图做微调。不需要多几百张即可目的是让模型在真实场景下不至于断崖式失效。2.3 数据清洗与文件组织一张坏图毁掉整个训练集数据集整理的坑往往不在合成而在清洗。我曾经遇到过一个情况从网上下载的人脸数据集里混入了几十张卡通头像和脸部被遮挡严重的照片模型训练完在真实人脸上出现奇怪的纹理伪影。后来加了一步筛选用 OpenCV 的 Haar Cascade 人脸检测器或 MTCNN 对每张图片做前置检测检测不到的图直接剔除。文件组织建议直接按 PyTorch 的ImageFolder风格设计清晰图和模糊图同名存放训练时用__getitem__按索引配对读取。目录结构如下dataset/ ├── train/ │ ├── sharp/ # 清晰人脸原图 │ └── blur/ # 对应模糊图文件名与 sharp 中一致 ├── val/ │ ├── sharp/ │ └── blur/ └── test/ ├── sharp/ └── blur/3. 模型选型与训练代码GAN 路线是本科毕设的最优解3.1 三条技术路线为什么我推荐 GAN模糊人脸图像增强有三大类模型可以选。第一类是 CNN 回归模型比如直接让网络拟合清晰图用 L2 或 L1 损失训练代表是 NAFNet、Restormer优点是训练稳定、指标高缺点是输出偏“平滑”细节不够锐利。第二类是 GAN 模型如 DeblurGAN-v2判别器逼着生成器产出视觉上更真实的结果人脸皮肤纹理和五官轮廓会更自然。第三类是扩散模型效果最好但训练成本太高一张卡训一周是常态不适合毕设节奏。本科毕设我倾向 GAN 路线。原因是效果上GAN 生成的人脸在答辩时一眼看上去更“像照片”工作量上生成器和判别器的对抗训练写进论文里内容更丰满调参上虽然 GAN 不稳定但正因为不稳定才有话可讲。常见做法是拿 DeblurGAN-v2 的结构做 baseline把主干换成轻量级人脸增强网络这样论文里既有对比又有自己的改动。3.2 核心训练代码生成器、判别器与损失函数以下是一个最小可运行的 PyTorch 训练循环核心片段生成器采用 UNet 结构的简化版判别器采用 PatchGANimport torch import torch.nn as nn from torchvision.models import vgg16 # 感知损失用 VGG16 中间层特征做比较网络更关注结构而非像素 class PerceptualLoss(nn.Module): def __init__(self): super().__init__() vgg vgg16(pretrainedTrue).features.eval() self.layers nn.Sequential(*list(vgg[:16])) # 取到 relu3_3 for p in self.layers.parameters(): p.requires_grad False def forward(self, pred, target): return nn.functional.l1_loss(self.layers(pred), self.layers(target)) def train_one_step(generator, discriminator, blur_img, sharp_img, opt_g, opt_d, l1_weight50.0): # 1) 生成器前向 fake generator(blur_img) # 2) 判别器梯度更新 pred_fake discriminator(fake.detach()) pred_real discriminator(sharp_img) loss_d nn.functional.binary_cross_entropy_with_logits(pred_fake, torch.zeros_like(pred_fake)) \ nn.functional.binary_cross_entropy_with_logits(pred_real, torch.ones_like(pred_real)) opt_d.zero_grad() loss_d.backward() opt_d.step() # 3) 生成器梯度更新 pred_fake discriminator(fake) adv_loss nn.functional.binary_cross_entropy_with_logits(pred_fake, torch.ones_like(pred_fake)) l1_loss nn.functional.l1_loss(fake, sharp_img) perc_loss PerceptualLoss()(fake, sharp_img) loss_g l1_weight * l1_loss 0.1 * perc_loss 0.01 * adv_loss opt_g.zero_grad() loss_g.backward() opt_g.step() return loss_d.item(), loss_g.item()这个训练循环里有几个关键点值得注意。第一是l1_weight设为 50这个值决定了生成器输出和清晰图之间的“保真度”太高会让输出偏向模糊图的平滑版本太低则容易产生幻觉细节。第二是感知损失之前要把像素值归一化到 VGG 预训练时的区间否则特征分布错位损失函数起不到监督作用。第三是生成器和判别器必须交替更新但判别器不能训练太强——如果 loss_d 一直趋近于 0说明判别器碾压生成器生成器梯度会被磨平图学不出来。PatchGAN 的判别器感受野决定了输出图像的局部真实性一般用 70×70 的感受野设置对人脸这种局部纹理丰富的对象比较合适。3.3 训练策略的先后顺序先 L1 后对抗真正训练时不推荐一上来就完整跑上面的流程。常见做法是第一阶段只训练生成器用 L1 损失跑 20 个 epoch让网络先学会大体的去模糊映射第二阶段再把判别器接进来加载第一阶段的生成器权重整体训练 10 到 15 个 epoch。这样做的好处是生成器初始输出不至于太差判别器不会在一开始就学到“永远判假”这种无意义的决策边界。这个策略用代码表达就是把上面的训练循环拆成两个脚本。第一个脚本只执行opt_g.zero_grad(); loss_g.backward(); opt_g.step()且loss_g l1_weight * l1_loss第二个脚本才引入对抗损失。两个脚本之间用保存的.pth权重文件衔接。4. 训练与调参的关键节点损失变化、学习率与验证策略4.1 损失值看什么不看绝对值看三者的相对趋势很多带着自己的毕设实验来找我的同学都会问同一个问题“我的 loss 降到 0.003 了为什么图还是糊的”答案很简单不同损失函数的数值尺度完全不同。L1 损失在 0 到 1 之间感知损失也在 0 到 1 之间对抗损失是 BCE 输出数值在 0 到 1 之间但它们各自含义不同。正确的观察方式是这样的第一阶段只看 L1 loss 和验证集的 PSNR当 PSNR 连续 5 个 epoch 不涨时停下第二阶段关注 GAN 的训练动态只要生成器 loss 在缓慢下降、判别器 loss 在 0.5 到 0.8 之间震荡说明对抗训练是健康的。如果判别器 loss 掉到 0.1 以下按 3.2 节说的要降低判别器学习率或者增加生成器的 L1 权重来压住生成器输出。学习率建议使用 Adam 优化器初始学习率设为 1e-4每 10 个 epoch 乘以 0.5。这个衰减策略是为 GAN 准备的生成器和判别器同步衰减稳定度远好于固定学习率。4.2 验证集上必须做的一件事裁剪对齐再算指标人脸图像的指标计算有一个隐藏的坑预测图和真实清晰图之间存在像素级别的偏移。如果直接把网络输出和原图大小对比PSNR 和 SSIM 会偏低但视觉上其实很好。这是因为卷积网络对输入的平移不敏感输出可能在几个像素内存在位移。避坑做法是计算指标前先做一次图像配准。最简单的方案是取图像中心区域裁剪比如 256×256 的图裁中间 128×128 的块分别从预测图和清晰图中裁出来再对比。这样既保证了评价区域的语义对应又避免了背景差异对指标的干扰。论文里把这段描述写清楚评审老师会觉得你懂评价指标的本质。4.3 人脸增强的专项评估识别率比 PSNR 更有说服力很多做图像增强的毕设答辩被问到“PSNR 高代表什么”时答不上来。我建议在验证阶段额外加一个指标用现成的人脸识别模型如 ArcFace分别对模糊图和增强图做人脸特征提取计算识别准确率或特征余弦相似度。整个毕设评估部分可以设计成三列指标——PSNR、SSIM 和识别率提升其中识别率提升用来证明“增强结果对下游任务有实际帮助”。这里有一个实际经验有时 PSNR 下降了 0.2但人脸识别率反而提高了原因是感知质量改善了关键五官区域。这个现象写在论文讨论部分会是很加分的亮点说明了指标与真实需求之间的差异。5. 常见问题避坑5 个高频率翻车现场5.1 训练了很久 loss 不降输出还是原样模糊现象生成器 loss 在某个数值附近震荡输出图像和输入几乎没有差别。原因最常见的是学习率太大导致震荡或者生成器网络太浅感受野不够覆盖模糊核的范围。还有一个容易被忽略的原因是输入图像的归一化方式不对——如果输入是 0~255 而标签是 0~1模型学出来的结果看起来就像蒙了一层灰。解决先检查数据预处理是否统一再降低学习率到 5e-5最后检查生成器第一层卷积的 stride。当模糊核 size 是 21 时第一层用 stride1 的卷积保留更多空间信息不要一上来就用 stride2 压缩分辨率。5.2 训练集效果惊艳测试集一塌糊涂现象验证集上 PSNR 刚过 30 就很满意了结果拿手机随手拍的一张模糊人脸测试输出图出现伪影或五官扭曲。原因合成模糊核太单一模型只见过线性运动模糊对真实复杂的抖动模糊和人像散焦没有泛化能力。解决在数据扩增阶段加入两种扰动一是随机模糊核叠加一个运动模糊核加一个高斯模糊核二是对合成模糊图做随机 gamma 变换。这两种扰动成本很低但能显著扩展退化空间。5.3 显存不足batch size 调到 1 还是 OOM现象把 batch size 降到 1图像尺寸从 256 降到 128依然报 CUDA out of memory。原因训练脚本里可能同时加载了 VGG16 的预训练权重用于感知损失而 VGG16 特征提取器在前向过程中也会占用显存另外判别器如果用了多尺度结构会额外消耗显存。解决感知损失网络可以提前把 L1 计算放在 CPU 上或者只在特定 step 计算感知损失比如每 4 个 step 算一次。另一个实用技巧是打开torch.backends.cudnn.benchmark True配合固定输入尺寸能明显减少显存峰值。5.4 增强后人脸的身份特征变了同一张脸变成另一张脸现象输出图确实清晰了但和原清晰图对比眼睛大小、脸型发生肉眼可见的改变人脸识别模型判定为不同的人。原因对抗损失权重过大生成器为了骗过判别器产生了“幻觉”五官细节在模糊严重的人脸区域自由发挥。解决把对抗损失的权重从 0.01 降到 0.002同时提高感知损失的权重到 0.5。感知损失对图像特征结构有约束能力能有效压住五官漂移。另外可以在训练集中加入身份保持损失用预训练人脸识别模型的 embedding 做 L2 约束。5.5 论文里放了模型结构图但训练代码和论文不一致现象论文画的是 DeblurGAN-v2 结构训练代码里用的却是另一个开源网络答辩时被老师问到参数就露馅了。原因写论文时对着参考文献画图训练时又图省事直接改了别人的开源代码两边没对齐。解决论文第 4 章写“系统实现”时直接用训练代码里实际定义的模块画图哪怕结构简单。把网络层的名称、通道数、kernel size 从代码里抄到图注里保持一致。这个细节经常是答辩评分的关键。6. 用 Gradio 做演示界面让毕设从“跑通”变“成品”毕设做完模型只是第一步系统总要有一个能演示的样子。这里我给你留一个最省事的方案用 Gradio 搭一个 Web 界面本地跑起来后浏览器上传模糊人脸图系统返回增强图并显示 PSNR 提升数值。import gradio as gr import torch def enhance_face(image): # image 为 numpy 数组先转 tensor再送进生成器 model.eval() with torch.no_grad(): tensor torch.from_numpy(image).permute(2, 0, 1).unsqueeze(0).float() / 255.0 output model(tensor) return output.squeeze(0).permute(1, 2, 0).numpy() * 255.0 gr.Interface( fnenhance_face, inputsgr.Image(typenumpy), outputsgr.Image(typenumpy), title模糊人脸图像增强系统, description上传人脸模糊图输出增强后的清晰图, ).launch(server_name0.0.0.0, server_port7860)这段代码里的enhance_face函数要注意输入输出都在 CPU 上处理避免演示机器没 GPU 时直接崩溃。模型推理时可以加torch.no_grad()和model.eval()别忘了在启动脚本里加载训练好的权重。最后给你一句过来人的忠告毕设阶段不要追刷榜一样死磕 PSNR真正会让答辩老师和评审印象深刻的是你把“系统”这两个字撑起来了——有数据、有训练、有评估、有界面、有失败分析。我当年就是靠一个简单的 Gradio 界面和一份诚实的失败案例记录把原本只值 75 分的项目拉到 90 分的。希望这个方案能帮到你。本文还有配套的精品资源点击获取