基于深度学习的模糊人脸图像增强:U-Net实现与工程实践 📅 发布时间:2026/9/16 16:18:54 👁 浏览次数: 简介基于深度学习的模糊人脸图像增强系统是一份高分毕业设计项目源码主要面向计算机相关专业正在准备毕业设计的学生以及需要实战练习的深度学习爱好者。项目包含完整源码、训练数据、模型文件与说明文档经过严格调试可直接运行适用于人脸修复、图像增强等研究场景。压缩包内共20个文件以9个Python源码文件为主体涵盖模型定义、输入处理和测试流程辅以预编译pyc文件、项目配置xml、图片示例、日志及README说明整体仅363KB轻量便捷。目前已有335人学习下载具有较好的参考热度。通过该资源学习者可直观理解基于深度学习的模糊人脸增强实现思路掌握从数据预处理到模型推理的完整流程并能基于现有代码快速改造迁移至自己的毕业设计或课程项目中。1. 一张模糊人脸为什么值得用深度学习去做增强监控抓拍里截出来的人脸往往只有几十像素还叠加了运动模糊和压缩噪声老照片里的人脸则常因扫描或对焦问题变得轮廓发虚。传统锐化、直方图均衡这类方法对边缘和纹理的恢复能力有限稍一用力就会出现伪影。基于深度学习的模糊人脸图像增强把问题建模成端到端的图像到图像翻译输入退化图输出清晰图。这个项目就是一个典型的毕设级实现Python 编写核心网络命名为 UNetLike目录中同时包含 src、data、model、resource 等工程模块还有训练日志和可运行的 Test.py。适合正在做毕业设计的学生也适合想完整跑通一个视觉增强项目的开发者。2. 从源码目录看模糊人脸增强系统的模块边界拿到压缩包后我的习惯是先不急着跑代码而是把目录当文档读。这个项目的顶层结构很有代表性包含主目录、src、data、model以及几个关键 Python 文件和资源文件。梳理清楚谁负责什么后面改代码和写论文都会顺手很多。2.1 文件与目录的职责边界按照常见工程约定这个压缩包的目录可以映射成一张职责表路径/文件职责src/主代码目录包含训练、推理入口与工具函数data/训练与测试数据清晰人脸和模糊人脸配对或原始图像model/保存训练好的权重文件供 Test.py 加载FaceEnhance.py网络模型定义UNetLike 网络主体FaceInput.py输入侧数据读取、预处理与 Dataset 封装Test.py推理脚本读图、预测、保存增强结果log.txt训练过程 loss 记录帮助判断收敛状态resource/项目文档、训练曲线或界面相关素材test1.png一张测试图像用于验证流程能否跑通UNetLike.png网络结构图答辩时可直接放入论文或 PPTREADME.md说明文档环境依赖和运行方式通常写在这里这种布局符合一个深度学习图像增强项目的规范数据和模型分离训练与推理入口独立。特别值得注意的是 log.txt 和 resource/前者能让你判断训练有没有真的收敛后者常常藏着环境配置说明或网络结构图很多开发者会忽略但答辩时非常有用。2.2 FaceInput 与 FaceEnhance 的分工从文件名能看出两个核心角色FaceInput.py 负责把图片变成模型能吃的张量FaceEnhance.py 负责网络前向计算。很多初学者会把预处理和网络定义写在同一个文件里结果换数据集时越改越乱。这个项目把输入侧和网络侧拆开本身就是一种可维护性的示范。FaceInput.py 中比较常见的做法是定义一个 Dataset在__getitem__里完成读图、缩放、归一化、加模糊或加载配对图。FaceEnhance.py 里的 UNetLike 类继承nn.Moduleforward实现从 3 通道输入到 3 通道增强图的映射。两个文件之间没有直接依赖完全靠 Test.py 或训练脚本组装。这意味着可以在不修改网络的前提下替换人脸数据或者把 FaceEnhance 换成其他模型做对比实验。2.3 从图片到结果的调用链按这套设计一次完整推理的调用顺序是读取 test1.png → FaceInput 预处理成(1,3,H,W)Tensor → 加载 model 里的权重 → FaceEnhance 前向推理 → 把输出 Tensor 转回 HWC 图像 → 保存结果。下面是一个符合项目结构的推理代码import torch from PIL import Image from torchvision import transforms from FaceInput import GeneralFaceLoader # 输入侧封装 from FaceEnhance import UNetLike # 网络定义 def run_inference(image_path, model_path, output_path): # 1. 加载并预处理输出 [0,1] 范围的张量 loader GeneralFaceLoader(size(256, 256)) input_tensor loader.load(image_path) # shape: [1, 3, 256, 256] # 2. 初始化网络并加载权重 model UNetLike(in_channels3, out_channels3) model.load_state_dict(torch.load(model_path, map_locationcpu)) model.eval() # 3. 关闭梯度前向推理得到增强结果 with torch.no_grad(): enh_tensor model(input_tensor) # shape: [1, 3, 256, 256] # 4. 后处理裁剪到 [0,1] 并保存 enh_img enh_tensor.squeeze(0).clamp(0, 1) enh_img transforms.ToPILImage()(enh_img) enh_img.save(output_path) if __name__ __main__: run_inference(test1.png, model/enhance.pth, result.png)load方法要负责把任意尺寸图片缩放到 256×256并转成 NCHW 格式。clamp(0,1)是因为网络输出层一般接 Sigmoid如果用的是 Tanh就要先把输出归一化到[0,1]再保存。map_locationcpu在无 GPU 环境下也能跑只是速度会慢一些。这段代码说明即使没有训练环境只要有权重文件推理也能独立运行。3. 清晰模糊人脸对是怎么造出来的预处理与数据加载模糊人脸图像增强本质上是监督学习输入是退化图标签是清晰图。但现实里很难直接采集到同一张脸的成对模糊/清晰图像所以常见做法在清晰人脸数据上人工添加模糊退化构造训练对。这个项目的 data 目录保留了这种思路训练时通过随机模糊模拟真实拍摄中的散焦和手抖让网络学习逆过程。3.1 训练数据从哪来清晰图人工加模糊退化方式的选择直接影响网络泛化能力。高斯模糊适合模拟景深不足或轻微对焦不准运动模糊适合模拟手抖或目标移动真实监控场景往往是两者叠加。如果只用固定 sigma 的高斯模糊网络只会记住一种退化强度换到真实低质量图片时增强效果会明显变差。比较好的做法是每个迭代随机生成一个模糊核等价于做无限数据扩充。模糊核尺寸通常取 11 到 21。太小模拟不了手抖带来的长拖尾太大会让人脸五官结构完全消失网络学到的是“凭空想象”而不是增强。sigma 取 1.0 到 2.5 覆盖轻度到中度模糊如果论文里需要做鲁棒性实验可以把范围放宽到 3.0但训练收敛时间也会变长。3.2 预处理参数与归一化策略图像增强任务中输入和输出的像素范围必须保持一致。我通常推荐直接用 0-1 缩放理由有三点实现简单不需要反标准化训练稳定不会因为 ImageNet 均值和方差引入额外偏置方便各种损失函数直接计算。归一化方式像素范围网络输出层适用场景0-1 缩放[0,1]Sigmoid通用图像到图像增强-1 到 1[-1,1]Tanh与 GAN 训练配合ImageNet 标准化均值0方差1需反标准化使用预训练分类网络对于人脸增强这种不需要预训练权重的任务0-1 缩放是最稳的。输入模糊图和标签清晰图都做相同缩放否则 loss 一开始就会剧烈震荡训练日志里会看到 loss 曲线长时间下不去。3.3 DataSet 与数据增强实现FaceInput.py 中与数据加载相关的逻辑可以用一个标准的 PyTorch Dataset 来描述。它读取一张清晰图动态添加随机运动模糊然后返回(模糊图, 清晰图)对import cv2 import numpy as np import torch from torch.utils.data import Dataset import os class FaceEnhanceDataset(Dataset): def __init__(self, root_dir, image_size256): self.paths [os.path.join(root_dir, f) for f in os.listdir(root_dir) if f.endswith((.jpg, .png))] self.size image_size def _random_motion_blur(self, img, kernel_size15): kernel np.zeros((kernel_size, kernel_size), dtypenp.float32) angle np.random.randint(0, 180) center (kernel_size - 1) / 2 angle_rad np.deg2rad(angle) # 沿直线方向给核内像素置 1模拟运动轨迹 for i in range(kernel_size): offset i - center x int(center offset * np.cos(angle_rad)) y int(center offset * np.sin(angle_rad)) if 0 x kernel_size and 0 y kernel_size: kernel[y, x] 1 kernel / kernel.sum() return cv2.filter2D(img, -1, kernel) def __len__(self): return len(self.paths) def __getitem__(self, idx): img cv2.imread(self.paths[idx]) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (self.size, self.size)) # 随机选择高斯模糊或运动模糊 if np.random.rand() 0.5: sigma np.random.uniform(1.0, 2.5) blur_img cv2.GaussianBlur(img, (0, 0), sigma) else: blur_img self._random_motion_blur(img, kernel_size15) img_t torch.from_numpy(img.transpose(2, 0, 1)).float() / 255.0 blur_t torch.from_numpy(blur_img.transpose(2, 0, 1)).float() / 255.0 return blur_t, img_t运动模糊核构造的逻辑是先生成全零方阵按随机角度画一条经过中心的直线再归一化让所有元素之和为 1这样卷积结果不会改变图像亮度。kernel_size15是折中值既能模拟中等强度手抖又不会过度破坏五官结构。训练时还可以在__getitem__里加随机水平翻转和 90 度旋转人脸增强对翻转变换不敏感能提升数据多样性。注意训练 Dataset 返回的是两张图而 Test.py 推理时只需要加载真实模糊图不需要动态加模糊。很多同学会在测试时也走训练预处理结果把测试图又模糊了一遍导致评估结果失真。4. UNetLike 网络编码器-解码器如何恢复五官边缘模糊人脸增强有两个基本诉求一是保留肤色和五官结构二是修复边缘纹理。U-Net 的编码器逐步下采样提取高层语义解码器逐步恢复分辨率中间的跳跃连接把编码器的浅层细节直接传给解码器正好缓解高频信息丢失的问题。UNetLike 就是在这个思路上做的轻量化版本通道数和深度都更适合 256×256 的人脸图像。4.1 为什么选择 U-Net 形态纯卷积网络堆叠足够深之后感受野能覆盖整张脸但下采样过程中的池化操作会丢失边缘位置信息。人脸增强要求输出与输入像素对齐因此需要保留空间位置。U-Net 的编码器-解码器结构天然适合这种同尺寸映射任务跳跃连接能在解码每个尺度时补充对应的纹理细节让输出在锐利度和语义正确性之间取得平衡。这也是人脸增强、去雨、去模糊等任务普遍采用 U-Net 变体的原因。4.2 简化版 UNetLike 模型代码FaceEnhance.py 中的 UNetLike 可以用下面这段代码作为骨架包含 DoubleConv、下采样和上采样模块共 4 层编码器、4 层解码器import torch import torch.nn as nn class DoubleConv(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.conv nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), nn.Conv2d(out_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), ) def forward(self, x): return self.conv(x) class UNetLike(nn.Module): def __init__(self, in_channels3, out_channels3, base_channels64): super().__init__() # 编码器 self.down1 DoubleConv(in_channels, base_channels) self.down2 DoubleConv(base_channels, base_channels * 2) self.down3 DoubleConv(base_channels * 2, base_channels * 4) self.down4 DoubleConv(base_channels * 4, base_channels * 8) self.pool nn.MaxPool2d(2) # 桥接层 self.bridge DoubleConv(base_channels * 8, base_channels * 16) # 解码器 self.up4 nn.ConvTranspose2d(base_channels * 16, base_channels * 8, 2, stride2) self.conv4 DoubleConv(base_channels * 16, base_channels * 8) self.up3 nn.ConvTranspose2d(base_channels * 8, base_channels * 4, 2, stride2) self.conv3 DoubleConv(base_channels * 8, base_channels * 4) self.up2 nn.ConvTranspose2d(base_channels * 4, base_channels * 2, 2, stride2) self.conv2 DoubleConv(base_channels * 2, base_channels * 2) self.up1 nn.ConvTranspose2d(base_channels * 2, base_channels, 2, stride2) self.conv1 DoubleConv(base_channels * 2, base_channels) # 输出层1x1 卷积 Sigmoid 映射到 [0,1] self.out_conv nn.Conv2d(base_channels, out_channels, 1) self.sigmoid nn.Sigmoid() def forward(self, x): d1 self.down1(x) d2 self.down2(self.pool(d1)) d3 self.down3(self.pool(d2)) d4 self.down4(self.pool(d3)) b self.bridge(self.pool(d4)) u4 self.up4(b) u4 torch.cat([u4, d4], dim1) u4 self.conv4(u4) u3 self.up3(u4) u3 torch.cat([u3, d3], dim1) u3 self.conv3(u3) u2 self.up2(u3) u2 torch.cat([u2, d2], dim1) u2 self.conv2(u2) u1 self.up1(u2) u1 torch.cat([u1, d1], dim1) u1 self.conv1(u1) return self.sigmoid(self.out_conv(u1))上采样使用ConvTranspose2d同时把同尺度的编码器输出拼接上来让解码器既能看到高层语义也能看到浅层细节。base_channels默认 64如果显存不足改成 48 或 32 即可。输入输出都是 3 通道对应 RGB 人脸图像。torch.cat要求两侧特征图空间尺寸一致因此输入尺寸需要能被 2 的 4 次方整除256×256 是最稳妥的选择。4.3 损失函数与训练配置只用 L2 损失训练出来的模型输出图像偏平滑人脸看起来像蒙了一层雾。常见做法是 L1 损失加 SSIM 损失的加权组合L1 稳定像素误差SSIM 约束局部结构一致性import torch.nn.functional as F def hybrid_loss(pred, target): l1 F.l1_loss(pred, target) # pytorch_msssim 库提供 ssim 函数返回 0-1 之间越大越相似 ssim_value ssim(pred, target) # 这里简写实际需要导入对应模块 return l1 0.5 * (1 - ssim_value)log.txt 里的 loss 如果从 0.05 缓慢下降到 0.02 附近并产生小幅度波动属于正常收敛。Adam 优化器初始学习率建议 1e-4每 20 个 epoch 衰减一半batch size 在 8 到 16 之间。训练过程每几个 epoch 保存一次权重并顺手用验证集计算 PSNR方便对比不同 epoch 的增强效果。5. 复现 UNetLike 并用注意力与边缘监督改进指标源码不是跑通就算结束尤其作为毕业设计需要体现自己的思考。这里分享三个我实际用过的改进方式改动量都不大但能在答辩时给出清楚的技术增量。5.1 先跑通 Test.py 并计算基础指标第一步先直接运行推理脚本确认环境无误python Test.py --input test1.png --weight model/enhance.pth --output result.pngweight参数指定模型权重路径如果输出图像比原图清晰且肤色自然说明权重和预处理对齐。接下来用 skimage 计算 PSNR 和 SSIM再比较输入与输出之间的 LPIPS。PSNR 反映像素差异SSIM 反映结构相似性LPIPS 更接近人眼感知三个指标一起看才不会被单一数字误导。5.2 改进一在跳跃连接中插入通道注意力原始 U-Net 的跳跃连接会把编码器特征直接拼到解码器但这些特征不一定全是人脸细节也可能包含模糊噪声。可以在跳跃连接后加一个 SE Block对通道做重标定让网络更关注对边缘和五官有贡献的特征。改动集中在DoubleConv之后不改变模型整体结构训练时也不会明显增加显存占用。5.3 改进二边缘监督让轮廓更利落很多增强结果 PSNR 高但视觉上偏“塑料感”原因是损失函数只约束了像素级差异没有直接约束边缘。可以用 Sobel 算子提取清晰图的边缘图同时对网络输出施加边缘 L1 损失edge_out sobel(pred) edge_target sobel(target) loss 0.1 * F.l1_loss(edge_out, edge_target)边缘损失权重 0.1 是比较安全的起点过大会让网络过度强化边缘产生白边。人脸图像中眼睑、嘴唇和下颌线都是边缘监督的重点区域这样处理后增强结果的主观清晰度通常会有明显提升。5.4 让数据更贴近真实监控场景如果时间允许把数据增强 pipeline 从单纯高斯模糊升级为随机退化组合高斯模糊、运动模糊、JPEG 重压缩和亮度抖动叠在一起模拟真实监控设备的成像链路。具体实现可以在FaceInput.py中增加一个degrade开关每次迭代随机采样退化参数并把退化强度写入 log.txt方便后期分析模型是否因为退化过强而发散。这样得到的系统不再只对合成模糊有效处理真实低质量人脸时也有可用的结果。本文还有配套的精品资源点击获取