资讯动态

基于深度学习的老照片修复实战:Python源码+Web页面,从部署到调参

发布时间:2026/9/28 15:21:39 来源:尧图企业网站定制
简介这是一份面向计算机、电子信息等专业学生与深度学习入门者的老照片修复实战源码基于深度学习模型实现图像上色与修复并自带简洁的Web交互页面可直接运行体验。资源包共20个文件约2.1MB其中7个Python脚本承担模型定义、推理服务与工具函数3个HTML模板与配套静态图片构成前端界面另有png、jpg示例图与说明文档整体结构清晰便于快速理解项目组织方式。项目涵盖模型加载、图像预处理、颜色转换与页面渲染等模块适合作为课程设计、期末大作业或毕设的参考案例也可在此基础上调试参数、替换模型以扩展功能。目前已有306人学习关注对于希望用较短时间跑通一个完整深度学习应用、并了解Web端部署流程的读者这份源码提供了可直接借鉴的实现路径与调试起点。1. 老照片修复这件事为什么值得用深度学习重做一遍家里翻出一张 1998 年的全家福扫描出来人脸糊成一团、划痕横七竖八、整体偏黄发灰——这是很多人做老照片修复的真实起点。传统做法是丢进 Photoshop 用修复画笔一点点抹一张图半小时起步遇到大面积破损基本没救。而基于深度学习的老照片修复本质是让模型学会「这张脸本来应该长什么样」把划痕、噪点、模糊、褪色当成可以被预测和重建的退化过程。这个标题里的关键词是「深度学习 Python 源码 自带 web 页面」翻译成落地语言就是一套能跑起来的模型推理代码外加一个浏览器里点几下就能出图的界面不需要你会训练模型装好环境就能用。它适合两类人一类是想快速验证老照片修复效果、拿去给家里人用的开发者另一类是刚学完 Python 和深度学习入门课想找一个「有界面、能演示、代码量可控」的完整项目练手的人。下面我按自己实际部署这类项目的顺序把选型、跑通、调参、踩坑一次讲清楚。2. 拆开这个压缩包老照片修复到底由哪几块拼成2.1 修复模型在做什么从退化建模到图像重建老照片的「坏」不是单一问题而是几种退化叠加高频划痕和噪点、低频的模糊和失焦、全局的色彩偏移和对比度衰减。深度学习修复模型通常把这几件事拆开处理或者用一个端到端网络一起学。常见的架构思路有三类一是基于 GAN 的生成式修复用生成器重建图像、判别器逼着结果逼近真实照片分布优点是细节丰富、人脸自然缺点是容易「脑补」出原图没有的内容二是基于 U-Net 类编码器-解码器结构配合跳跃连接保留结构信息训练稳定、推理快适合划痕去除和轻度模糊三是扩散模型路线生成质量高但推理慢对显存要求也高本地跑一张图可能要几十秒。这个项目标题强调「超级简单」说明它大概率走的是前两类的工程化封装模型权重已经训练好Python 端只负责加载权重、预处理、推理、后处理web 页面负责上传和展示。你不需要关心训练集怎么构造但要理解推理时的输入输出约定——输入通常是归一化到 [-1,1] 或 [0,1] 的 RGB 张量输出同尺寸图像中间可能经过 resize 到模型固定输入尺寸比如 256×256 或 512×512。理解这一点后面调参和排错才有方向。2.2 技术选型为什么是 Python 轻量 web 框架Python 是深度学习推理的事实标准PyTorch 和 ONNX Runtime 都能直接加载预训练权重生态成熟。web 层常见做法是 Flask 或 FastAPI 起一个本地服务前端用原生 HTML fetch 上传图片后端接收后调用模型推理返回修复后的图片。选 Flask 的理由是代码量少、依赖轻一个app.py加一个templates/index.html就能跑选 FastAPI 的理由是异步支持好、自带接口文档适合以后扩展成 API 服务。组件常见选择作用注意点推理框架PyTorch / ONNX Runtime加载模型、执行推理ONNX 跨平台更好PyTorch 调试更方便web 框架Flask / FastAPI提供上传和结果接口Flask 同步阻塞FastAPI 异步更适合并发图像处理Pillow / OpenCV读写、缩放、格式转换注意 RGB/BGR 通道顺序前端原生 HTML JS上传预览、结果展示不需要前端框架降低门槛我一般会先确认压缩包里的模型文件格式如果是.pth用 PyTorch 加载如果是.onnx用 ONNX Runtime后者在没装 CUDA 的机器上也能跑 CPU 推理只是慢一些。这一步决定了你requirements.txt里装什么别一上来就pip install torch装个几个 G 的包结果发现人家用的是 ONNX。2.3 最小可跑通路径从解压到浏览器出图假设你已经拿到压缩包并解压到一个目录下面是通用的跑通步骤。不同项目目录结构会有差异但核心环节一致。# 1. 进入项目目录先看结构确认入口文件和模型文件位置 cd old_photo_restore ls -la # 常见结构app.pyweb入口、models/权重、static/、templates/、requirements.txt # 2. 创建独立虚拟环境避免污染全局 Python python -m venv venv # Windows 激活venv\Scripts\activate source venv/bin/activate # 3. 安装依赖先看 requirements.txt 里有没有版本锁定 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 4. 启动 web 服务注意看控制台打印的端口号 python app.py # 常见输出Running on http://127.0.0.1:5000启动后浏览器打开http://127.0.0.1:5000上传一张老照片等待几秒看结果。如果页面能打开但上传报错先看后端控制台的异常堆栈八成是模型路径写死成了作者本机的绝对路径改成相对路径即可。如果上传成功但输出全黑或全白通常是预处理归一化参数和模型训练时不一致检查代码里有没有/255.0或mean/std的归一化步骤。提示第一次跑通不要急着换自己的照片先用项目自带的示例图验证链路排除是图片本身格式问题还是代码问题。3. 把 web 页面和推理代码接起来接口、预处理与参数3.1 后端接口怎么写一个上传接口的完整逻辑web 页面能出图靠的是后端一个接收文件、调用模型、返回图片的接口。下面是一个 Flask 版本的典型实现逻辑清晰、依赖少适合直接抄改。import io import torch from PIL import Image from flask import Flask, request, send_file, render_template app Flask(__name__) # 全局加载模型避免每次请求都重新加载这是性能关键 device torch.device(cuda if torch.cuda.is_available() else cpu) model torch.load(models/restore.pth, map_locationdevice) model.eval() # 切换到推理模式关闭 dropout 和 batchnorm 更新 def preprocess(image: Image.Image, size: int 256) - torch.Tensor: 把 PIL 图片转成模型输入张量 image image.convert(RGB) # 统一通道防止灰度图或 RGBA 报错 image image.resize((size, size)) # 缩放到模型固定输入尺寸 tensor torch.from_numpy( __import__(numpy).array(image) ).float() / 255.0 # 归一化到 [0,1] tensor tensor.permute(2, 0, 1) # HWC - CHW return tensor.unsqueeze(0).to(device) # 增加 batch 维度 app.route(/) def index(): return render_template(index.html) app.route(/restore, methods[POST]) def restore(): file request.files.get(photo) if not file: return {error: no file}, 400 image Image.open(io.BytesIO(file.read())) with torch.no_grad(): # 推理不需要梯度省显存 output model(preprocess(image)) output output.squeeze(0).permute(1, 2, 0).cpu().numpy() output (output.clamp(0, 1) * 255).astype(uint8) result Image.fromarray(output) buf io.BytesIO() result.save(buf, formatPNG) buf.seek(0) return send_file(buf, mimetypeimage/png) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)这段代码有几个关键点。model.eval()必须调用否则 BatchNorm 会拿单张图的统计量做归一化结果会明显偏色。torch.no_grad()关闭梯度计算显存占用能降一半左右。预处理里的resize尺寸必须和模型训练时一致如果模型是按 512 训练的你传 256 进去输出会糊上加糊。后处理clamp(0,1)是防止模型输出超出范围导致像素值溢出变成噪点。3.2 前端页面最小实现上传、预览、下载前端不需要框架一个 HTML 文件加几十行 JS 就够。核心是FormData上传和URL.createObjectURL预览。!DOCTYPE html html headmeta charsetutf-8title老照片修复/title/head body input typefile idfile acceptimage/* button onclickrestore()开始修复/button div styledisplay:flex;gap:20px;margin-top:20px divp原图/pimg idorigin stylemax-width:300px/div divp修复后/pimg idresult stylemax-width:300px/div /div script const fileInput document.getElementById(file); // 选择文件后立即预览原图 fileInput.onchange () { const file fileInput.files[0]; document.getElementById(origin).src URL.createObjectURL(file); }; async function restore() { const file fileInput.files[0]; if (!file) return alert(请先选择图片); const form new FormData(); form.append(photo, file); const res await fetch(/restore, { method: POST, body: form }); if (!res.ok) return alert(修复失败查看后端日志); const blob await res.blob(); document.getElementById(result).src URL.createObjectURL(blob); } /script /body /html前端这块最容易翻车的地方是跨域和超时。本地同源访问不会有跨域问题但如果你把后端部署到另一台机器浏览器会拦截请求需要在 Flask 里加 CORS 头。另外大图上传加推理可能超过浏览器默认等待时间建议在前端加个 loading 状态别让用户以为卡死了。3.3 三个必调参数尺寸、归一化、推理设备跑通之后想提升效果优先动这三个参数其他都是次要的。参数作用常见取值调错后果输入尺寸决定模型看到的细节量256 / 512太小丢细节太大爆显存归一化方式对齐训练分布[0,1] 或 [-1,1]不一致导致偏色、发灰推理设备CPU / CUDAcuda 优先CPU 慢 5-10 倍但兼容性好输入尺寸不是越大越好。很多修复模型是在 256×256 的 patch 上训练的你硬塞 1024×1024 进去模型没见过这个尺度输出反而崩。正确做法是按模型训练尺寸推理如果原图很大先缩放再推理或者分块推理后拼接。归一化方式必须看源码里训练部分的预处理/255.0和(x-0.5)/0.5是两种完全不同的分布搞错了输出就是一片灰。推理设备用torch.cuda.is_available()自动判断最省心没有 GPU 就老老实实 CPU别硬装 CUDA 驱动版本对不上能折腾一下午。4. 避坑与排查老照片修复项目最常见的五个翻车点4.1 模型加载报错路径、版本、权重格式三连坑现象启动时报FileNotFoundError或RuntimeError: Error(s) in loading state_dict。原因通常是模型路径写死成作者本机绝对路径或者 PyTorch 版本不匹配导致权重键名对不上。解决先把模型路径改成基于os.path.dirname(__file__)的相对路径如果是state_dict键名不匹配用model.load_state_dict(torch.load(...), strictFalse)跳过不匹配的层但要确认跳过的不是核心层否则输出没效果。ONNX 模型则检查onnxruntime版本和模型 opset 是否兼容。4.2 输出全黑或全白归一化和通道顺序在作怪现象上传成功返回的图片要么纯黑要么纯白。原因有两个一是预处理没归一化模型输入是 0-255 的大数值输出饱和二是通道顺序搞反OpenCV 读进来是 BGR直接喂给按 RGB 训练的模型颜色会诡异极端情况也会导致输出异常。解决统一用 Pillow 读图默认 RGB归一化步骤和训练代码逐行对齐输出后再乘回 255 并转 uint8。4.3 人脸修复过度GAN 模型的「脑补」风险现象划痕去掉了但人脸变得不像本人眼睛、牙齿出现奇怪的纹理。原因是 GAN 类模型在重建时会根据学到的先验「猜」内容遇到严重破损区域就自由发挥。解决对含人脸的图降低生成强度或者先用一个保守的回归模型做粗修复再用 GAN 做细节增强如果项目支持调低对抗损失的权重。实务中给家里人用的照片宁可保守一点也别修出个陌生人。4.4 大图推理爆显存分块与缩放策略现象小图正常一传手机拍的大图就CUDA out of memory。原因是模型输入尺寸固定但预处理时如果没缩放大图直接进网络显存瞬间打满。解决推理前强制 resize 到模型输入尺寸或者实现分块推理——把大图切成有重叠的 patch逐块推理后按重叠区域融合。分块时 patch 之间留 16-32 像素重叠避免拼接处出现明显接缝。4.5 web 服务部署后无法访问host 和端口绑定现象本机127.0.0.1:5000能访问换台机器或手机访问不了。原因是 Flask 默认只绑定127.0.0.1只接受本机请求。解决启动时改成app.run(host0.0.0.0, port5000)让服务监听所有网卡。同时检查防火墙有没有放行端口。注意debugTrue不要在生产环境开会暴露调试信息和代码执行风险。5. 进阶技巧用分块推理和批量处理把修复效果拉满跑通基础版本之后真正决定成品质量的是两个进阶操作分块推理和批量处理。分块推理解决的是「模型输入尺寸固定但原图很大」的矛盾。我的做法是把原图按 256×256 切块块间重叠 32 像素每块单独推理然后按重叠区域做加权平均融合。加权用高斯权重中心权重大、边缘权重小这样拼接缝几乎看不出来。代码上用一个滑动窗口循环维护一个和原图同尺寸的累加矩阵和权重矩阵最后相除即可。import numpy as np def tile_inference(model, image, tile256, overlap32, devicecpu): 分块推理重叠区域加权融合适合大图修复 h, w, c image.shape step tile - overlap output np.zeros((h, w, c), dtypenp.float32) weight np.zeros((h, w, c), dtypenp.float32) # 生成高斯权重窗中心高边缘低减少拼接痕迹 gauss np.outer( np.hanning(tile), np.hanning(tile) )[:, :, None] for y in range(0, h, step): for x in range(0, w, step): y2, x2 min(y tile, h), min(x tile, w) patch image[y:y2, x:x2] # 边缘不足一块的补零到 tile 尺寸 ph, pw patch.shape[:2] padded np.zeros((tile, tile, c), dtypenp.float32) padded[:ph, :pw] patch / 255.0 tensor torch.from_numpy(padded).permute(2, 0, 1).unsqueeze(0).to(device) with torch.no_grad(): pred model(tensor).squeeze(0).permute(1, 2, 0).cpu().numpy() output[y:y2, x:x2] pred[:ph, :pw] * gauss[:ph, :pw] weight[y:y2, x:x2] gauss[:ph, :pw] return (output / np.maximum(weight, 1e-6) * 255).astype(uint8)这段代码的关键参数是tile和overlap。tile必须和模型训练尺寸一致overlap一般取 tile 的 1/8 到 1/4太小接缝明显太大推理次数增加、速度变慢。高斯窗用np.hanning生成一维窗再外积比直接写二维高斯简单效果也够用。批量处理则是把目录下所有图片排队跑一遍输出到指定文件夹适合一次修复一整本相册。我自己的习惯是先拿三五张有代表性的图一张划痕多的、一张模糊的、一张偏色的调好参数确认效果稳定后再批量跑避免跑完几百张才发现参数不对那真是血泪经验。修复老照片这件事模型只是工具真正决定结果的是你对每张图退化类型的判断和参数取舍。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑