ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

老照片修复源码解析:PyTorch迁移学习与Flask Web上色实战

老照片修复源码解析:PyTorch迁移学习与Flask Web上色实战 简介这是一份基于深度学习的老照片修复项目源码整体约2.1MB共20个文件包括7个Python脚本模型定义、颜色转换与后端服务、5张程序运行效果图、3个HTML页面上传、布局与预测界面、2张示例老照片和1份readme说明文档。项目采用colorizers等深度学习模块实现黑白或褪色照片的自动着色与修复并通过Python后端配合Web上传页面向用户提供可视化操作实际运行时较为简单适合计算机、电子信息等专业学生用于课程设计、期末大作业或毕业设计。源码目录划分清晰包含utils工具、静态图片资源、模板页面和核心模型脚本下载后即可直接运行体验。目前已有306人学习浏览具有一定的参考热度。对于希望快速上手深度学习图像修复流程并想看懂关键代码再做二次开发的读者来说是一份轻量实用的参考资料。1. 老照片修复不是滤镜这个 Python 源码包到底做了什么翻出一张上世纪的黑白合影想让它重新“活”过来变成彩色用这套基于深度学习的老照片修复 python 源码就能做到。它核心是一个 PyTorch 实现的老照片上色模型走的是 ImageNet 预训练 VGG16 迁移学习的路子灰度图进、彩色图出自带一个 Flask 写的 web 页面不用写一行前端代码就能在浏览器里上传、预览、下载。适合做课程设计、期末大作业也适合想搞懂彩色化原理而不是只套一层滤镜的开发者。zip 解压后代码结构很清爽主要坑集中在模型权重下载和 PyTorch 版本兼容上下面按我实际拆过这个包的步骤来讲。2. 解压先看目录五个文件决定你能不能跑起来2.1 zip 包结构哪些文件是核心哪些只是样例拿到手第一件事不是装环境而是把 zip 解开逐个看文件。这个包的根目录下有colorizers核心算法包、templatesWeb 模板、utils和uploads目录还有predict.py、service.py、color_transformer.py三个入口脚本以及imgs里的几张测试图和out目录下已经生成的输出样例。一眼能看出这是把“算法”和“Web 服务”分成两层colorizers管神经网络本身service.py管网页交互。我最关心下面这几个文件文件作用是否关键colorizers/models.py定义 VGG16 上色网络包含模型加载逻辑关键colorizers/util.py图片预处理、Lab 空间转换、后处理转 RGB关键predict.py命令行入口单张图片上色关键service.pyFlask 服务Web 页面入口关键templates/upload.html上传表单页面关键imgs/2.jpg、imgs/old_img.png测试图片用来验证安装是否成功辅助out1.png、out2.png、ui.png运行结果样例判断效果用辅助这里有个很容易被忽略的点colorizers/models.py里的load_model函数十有八九是通过torch.hub.load_state_dict_from_url去下载预训练权重的所以 zip 里大概率不直接带.pth文件。如果网络不通或者没提前下载权重直接跑predict.py会报文件找不到。后面避坑章我会专门讲这里先记住权重和代码是分家的这不是包有问题是设计如此。2.2 环境安装Python 版本与三个依赖坑这个项目诞生时间比较早对 Python 版本不算挑剔。我实际测试下来Python 3.8 到 3.10 都能稳定跑Python 3.11 以上偶发依赖编译问题。核心依赖是torch、torchvision、flask、numpy、scipy、Pillow、opencv-python。如果只是跑 Web 页面上色opencv不是必须的但predict.py里有的版本会用到cv2.resize建议直接装上省事。cd 老照片修复源码目录 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install flask numpy scipy pillow opencv-python装 CPU 版 PyTorch 够用上色这种任务单张图 CPU 推理两到五秒完全能接受。GPU 版要看 CUDA 版本别在这上面浪费太久。三个坑提前说第一scipy新版本把imresize删了如果util.py里引用了它得手动改成cv2.resize或者torch.nn.functional.interpolate第二Pillow 10 以后Image.resize的ANTIALIAS参数改名成LANCZOS报错的话去util.py里改一下第三别用 Anaconda 自带的旧 Python很多莫名报错都来自 Python 版本太老。2.3 首次启动浏览器打开上传页只要两步环境装完别急着改代码先按最朴素的流程把 Web 页面拉起来。在项目根目录执行python service.py看到类似Running on http://127.0.0.1:5000的输出说明 Flask 起来了。浏览器打开这个地址能看到上传页选一张黑白照片提交等两三秒结果页会展示上色后的效果。如果你第一次启动就报错八成是模型权重没下载成功先跳过 Web用命令行方式验证算法本身有没有装好python predict.py --img imgs/old_img.png --out out_test.png这条命令直接吃一张测试图输出上色结果到out_test.png。命令行跑通说明models.py和util.py里的核心链路没问题跑不通优先看错误信息里提到的路径和模型文件名。service 跑不起来但命令行能跑问题基本集中在 Flask 端口占用或模板目录没找到上。3. 上色原理与 predict.py 参数灰度图凭什么猜出颜色3.1 Lab 颜色空间模型只学 ab 通道L 通道直接复用老照片上色听起来玄学其实思路很朴素。彩色图片在 Lab 色彩空间里分成三部分L 表示亮度a 表示红绿方向b 表示黄蓝方向。黑白照片丢失的是 a 和 b 两个通道保留的是 L 通道。所以模型的任务不是把整张图从 1 通道“变”成 3 通道而是只预测缺失的 a、b 两个通道最后把预测结果和原图的 L 通道拼回去。# 推理阶段的通道拼接逻辑要点示意 import torch # gray_img: 1x1xHxW, 原图的 L 通道 # output_ab: 1x2xHxW, 模型预测的 a、b 通道 out_img torch.cat((gray_img, output_ab), dim1) # 拼回 1x3xHxWdim1是通道维度把 1 通道的亮度信息和 2 通道的颜色信息拼到一起得到完整的 Lab 图像。这一步决定了为什么输入黑白图也能得到彩色图——模型本质上是在做“条件生成”条件是图片的纹理、边缘、物体类别生成目标是合理的颜色分布。理解这个你就能懂为什么命名成color_transformer.py它管的就是 Lab 和 RGB 之间来回转换的活儿。3.2 VGG16 编码器与全局局部双路融合模型骨架用的是 ImageNet 上预训练过的 VGG16这部分我拆开说。全连接层被砍掉卷积层保留网络作用变成提取特征而不是分类。图片经过层层卷积池化后会产生不同尺度的特征图浅层保边缘纹理深层保语义信息。这个任务有意思的设计是“双路特征”一路是局部的直接取最后一个卷积层的特征图做上色预测另一路是全局的把特征图做全局平均池化压成一个描述“这张图大概是什么场景”的向量再广播回每个像素位置。两条路径拼起来再进解码层。为什么这么做因为颜色判断很多时候依赖整体语义。局部看一个像素它周围全是砖红色你会倾向涂成红色但如果全局信息告诉你这是张人脸你应该涂成肤色而不是砖红色。ImageNet 预训练在这里的价值非常大。VGG16 在 ImageNet 上学到的物体识别能力知道眼睛、草、天空长什么样这些知识迁移过来就直接够用所以即便训练数据量不大上色效果也相当自然。这也解释了为什么 zip 包只有几百 KB 代码却号称“深度学习修复”——真正干活的是那几十 MB 的预训练权重它才是大头。3.3 命令行跑通第一张图参数、输出和设备切换predict.py的典型调用方式是这样python predict.py --img imgs/old_img.png --out out/my_result.png --model siggraph17 --gpu 0参数逐一说--img指定输入图片路径支持 jpg、png--out指定输出路径注意目录要存在--model选择权重版本项目里常见两个选择eccv16是论文原始版本色彩偏激进siggraph17是后来针对老照片上色优化过的版本颜色更收敛、更自然老照片修复默认选 siggraph17--gpu指定 GPU 编号不传或者机器没显卡就自动跑 CPU。如果你打开predict.py源码会看到类似的逻辑加载模型 → 读图 → 前向推理 → 后处理保存。后处理那一步有个值得注意的细节输出 a、b 通道路过postprocess_tens时会对颜色范围做拉伸和截断防止出现过于饱和的荧光色。我实测一张 800×600 的图片CPU 推理大约三秒GPU 半秒内出结果。首次跑的时候如果提示下载权重会卡十几秒到几分钟取决于网速这一步不是你代码的问题是权重文件在下载。4. Web 页面背后upload 表单到 Flask 路由的完整链路4.1 三个模板页面的分工layout、upload 与 predicttemplates目录下三个 HTML 文件关系是继承式复用layout.html是骨架定义了公共的页面头部、样式引用和底部信息upload.html是首页撑起一个文件选择表单predict.html是结果页展示上色后的图片并提供下载链接。这三个文件里没有复杂的前端逻辑原生 HTML 加一点简单的内联样式。!-- upload.html 表单核心部分结构示意 -- form methodpost action/predict enctypemultipart/form-data input typefile namefile acceptimage/* required button typesubmit开始修复/button /formenctypemultipart/form-data这句很关键少了它 Flask 的request.files拿不到文件。action/predict指定提交到的路由。整个页面不需要任何 JavaScript后端把文件读完、处理完直接把结果塞进模板渲染返回给浏览器这是最传统的服务端渲染方式也是“超级简单”的底气来源。4.2 service.py 核心逻辑接收上传到返回结果service.py是 Web 端的核心我按它的职责拆成几段来看。整个流程是接收文件 → 保存到临时位置 → 调用算法处理 → 保存结果 → 渲染页面。下面是一个和源码包逻辑一致的简化版本from flask import Flask, request, render_template from colorizers import models, util import torch, os, uuid app Flask(__name__) app.config[UPLOAD_FOLDER] uploads model models.load_model() # 模型只加载一次全局复用 model.eval() app.route(/, methods[GET]) def home(): return render_template(upload.html) app.route(/predict, methods[POST]) def predict(): f request.files[file] # 拿到上传的文件对象 ext f.filename.rsplit(., 1)[-1] save_path os.path.join(app.config[UPLOAD_FOLDER], f{uuid.uuid4().hex}.{ext}) f.save(save_path) # 先落盘再喂给模型 img util.load_img(save_path) # 图片 → 1x1xHxW 张量 with torch.no_grad(): # 推理阶段关闭梯度省内存 out model(img) # 输出 1x2xHxW 的 ab 通道 rgb util.postprocess_tens(img, out) # L ab 合成并转 RGB result_path save_path.rsplit(., 1)[0] _out. ext util.save_img(rgb, result_path) return render_template(predict.html, resultresult_path) if __name__ __main__: app.run(debugFalse, host127.0.0.1, port5000)说几个关键设计。用uuid.uuid4().hex生成随机文件名能避免多用户同时上传时文件互相覆盖这是 Web 部署的基本功torch.no_grad()括号把推理包住关掉自动求导显存和内存占用能砍掉一大截模型在模块加载时就初始化而不是每次请求都重建否则每上传一张图都要等十几秒加载权重。postprocess_tens那行承担了颜色空间转换的全部工作它把网络输出的 ab 通道和原始灰度图的 L 通道拼起来从 Lab 转回 RGB再截断到合法的像素范围。这一段是源码里最容易踩坑的地方如果图片颜色全偏绿或者花屏问题基本都出在这里——最常见原因是输入图片本身不是灰度图模型拿到的 L 通道和预期的亮度分布对不上。4.3 再加一个 HTTP 接口脱离页面也能被程序调用服务跑起来以后Web 页面和命令行其实都只是“输入输出通道”。如果想让其他程序直接调用这个修复能力可以在service.py里加一个 JSON 接口接收图片路径或二进制流返回结果图片路径from flask import jsonify app.route(/api/colorize, methods[POST]) def api_colorize(): f request.files[file] ext f.filename.rsplit(., 1)[-1] save_path os.path.join(app.config[UPLOAD_FOLDER], fapi_{uuid.uuid4().hex}.{ext}) f.save(save_path) img util.load_img(save_path) with torch.no_grad(): out model(img) rgb util.postprocess_tens(img, out) result_path save_path.rsplit(., 1)[0] _out. ext util.save_img(rgb, result_path) return jsonify({status: ok, result: result_path})这样前端小程序、自动化脚本都可以通过 HTTP 方式调它。调用端用requests就行import requests resp requests.post( http://127.0.0.1:5000/api/colorize, files{file: open(old_photo.jpg, rb)} ) print(resp.json())files参数里键名必须和后端request.files[file]里的保持一致否则拿到的是空对象。这是个非常容易忽略的对接细节我在帮别人联调时见过好几次前端传的字段名和后端取的不一样后端不报错但拿不到内容。5. 避坑手册五条血泪经验解决九成报错5.1 下载的 zip 里没有模型权重一跑就报 FileNotFoundError现象执行python predict.py --img imgs/old_img.png --out t.png输出一堆信息后抛异常提示找不到siggraph17-df00044c.pth之类的文件。原因源码包只含代码预训练权重体积大没有一起打进 zip。load_model函数里写的是从网上下载网络一断就挂在半路。解决看colorizers/models.py里load_model函数找到权重下载 URL 或 GitHub 仓库地址手动下载对应的.pth文件放在checkpoints目录下然后修改load_model里的路径参数优先读本地文件。我习惯把下载好的权重放项目根目录的checkpoints文件夹里再在代码里加一个local_path判断能省下以后每次初始化的下载等待。5.2 PyTorch 2.6 以上加载旧模型直接报 weights_only 错误现象模型文件存在但torch.load报错提示Weights only load failed或 UnicodeDecodeError。原因新版 PyTorch 的torch.load默认weights_onlyTrue而这个项目的权重是旧格式里面除了张量还有非张量对象默认参数下加载直接被拒。解决在util.py或models.py里把torch.load调用改成torch.load(path, map_locationcpu, weights_onlyFalse)。注意map_location也要写不然 GPU 机器上加载 CPU 权重会报设备不匹配。5.3 中文文件名或中文路径导致上传后处理失败现象Web 页面上传名字带中文的图片Flask 显示上传成功但结果页打不开后台报UnicodeDecodeError或FileNotFoundError。原因Windows 下 Python 默认编码和前端传过来的 UTF-8 文件名不一致保存到本地后文件名乱码后续util.load_img照着乱码路径再读就崩了。解决上传时强制重命名别用原始文件名。最简单的做法就是用uuid或者时间戳生成新文件名彻底绕开中文编码问题。把f.save(save_path)之前加一行filename secure_filename(f.filename)也能处理一部分但中文会被滤空不如直接随机命名干净。5.4 CPU 跑大图内存吃满Web 页面卡死现象上传一张四五千像素宽的老照片浏览器一直转圈后台 CPU 100%几分钟没反应有时直接报RuntimeError: CUDA out of memory或内存溢出退出。原因网络计算量随分辨率平方增长VGG16 本身就不是轻量网络原图直接喂进去中间特征图能把内存撑爆。CPU 上跑一张 4000 像素的大图耗时按分钟算看起来就像死机。解决调用util.load_img前先主动缩图长边压到 800 到 1200 像素以内。老照片修复不是超分任务分辨率低一点不影响上色效果但速度能快一个数量级。如果自己写脚本可以在预处理里加一句img.thumbnail((1000, 1000))。另外确认推理代码包在torch.no_grad()里否则内存占用直接翻倍。5.5 Flask 默认端口被占用service.py 起不来现象执行python service.py报OSError: [Errno 98] Address already in use或者浏览器访问 5000 端口打开的是另一个不相干的页面。原因5000 是 macOS 和部分 Linux 发行版的系统预留端口之前跑过的 Flask 进程没关干净也占着它。解决改用其他端口把app.run那行的port5000改成port5001或 8000另外启动前用lsof -i:5000查一下谁占了端口kill掉残留的 Python 进程再重启。这里加一句service.py里app.run(debugTrue)如果开着改代码会自动重启调试方便但部署时要关掉否则每次请求都触发重载又慢又容易出怪问题。6. 进阶玩法批量修复文件夹与色调微调服务跑通、单张图的坑都踩完之后真正干活的时候到了。给一家照相馆批量修复几十张老照片不可能一张张上传再下载直接在命令行跑批处理脚本更靠谱。关键优化是模型只加载一次循环里只做推理from colorizers import models, util import torch, os, glob model models.load_model() model.eval() for path in glob.glob(imgs/*.png) glob.glob(imgs/*.jpg): img util.load_img(path) with torch.no_grad(): out model(img) rgb util.postprocess_tens(img, out) out_path os.path.join(out_folder, os.path.basename(path)) util.save_img(rgb, out_path) print(ffinished: {path})脚本说明glob用来匹配目录下所有图片模型在循环外加载几十张图只初始化一次网络每个文件独立处理单张失败不会影响其他图片输出目录先建好否则save_img会报路径错误。色调微调是另一个常被问到的需求。修复出来的颜色偏淡或偏艳不用重新训练模型直接在util.py的postprocess_tens里调参数——把 ab 通道的数值缩放一个系数。eccv16 版本的颜色本来就偏浓觉得辣眼睛就乘个 0.8siggraph17 偏保守想要更鲜艳就乘 1.2。这个系数就是颜色饱和度的手动开关比去改网络结构实在得多。验证上色效果别只看一眼顺不顺眼。我会把修复图和原图放在一起做局部对比人的皮肤是不是匀称的肤色而不是一块黄一块青天空是不是连续的蓝色而不是花花绿绿金属物的高光处颜色是否保持了灰度——这三个地方是上色模型最容易翻车的区域。旧照片扫描进来的噪点会让 ab 通道预测不稳定先做一次去噪再上色效果会比直接上色好很多。以前我跑这类开源项目习惯解压后直接python service.py结果不是权重下载失败就是版本不兼容折腾半天还以为是包的问题。从那以后我每次拿到这类源码 zip都强制走一遍“读 readme → 列目录 → 查权重 → 锁依赖版本 → 命令行验证 → 再开 Web”的流程至少能省出个把小时。这个包本身写得很规矩目录结构清晰算法和 Web 分层明确只要把模型权重这一关过了剩下的都顺理成章。希望帮到你。本文还有配套的精品资源点击获取
返回列表