ARTICLE DETAIL

资讯详情

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

基于Flask的老照片修复系统:深度学习模型集成与Web部署实战

基于Flask的老照片修复系统:深度学习模型集成与Web部署实战 简介本资源是一个基于Python Flask框架与深度学习技术实现的老照片修复Web应用源码包面向计算机、人工智能、自动化等专业学生及初学者解决老旧照片褪色、划痕、模糊等常见退化问题适用于课程设计、毕业设计及深度学习实践项目。压缩包共20个文件包含7个核心Python脚本如color_transformer.py、service.py、models.py、5张示例图像PNG/JPG格式用于测试与效果对比、3个HTML前端模板upload.html、predict.html等构成完整Web交互流程以及工具文档手册.docx和预编译pyc文件整体仅2.18MB轻量易部署。已有466人学习下载项目源自高分毕设答辩95分代码经完整调试验证可直接运行。用户可快速掌握Flask后端服务搭建、深度学习模型集成如图像着色与超分模块、前后端数据交互及Web界面开发全流程目录结构清晰模块职责分明具备强教学示范性与二次开发延展性。1. 背景与整体思路为什么选 Flask 做老照片修复老照片修复这个需求这两年其实一直很热。家里翻出一堆泛黄、缺角、模糊的老照片想修复却不知道找谁很多做老照片修复的工作室报价还不低。之所以选 Python Flask 搭一套老照片修复系统核心原因就是 Python 在深度学习领域的生态太完整了从图像处理到模型推理再到 Web 接口一套链路全都能在一个语言体系内解决。先说 Flask 和深度学习框架的关系。深度学习本身和 Web 框架没关系模型推理用 PyTorch 或者 TensorFlow 就行但要做成别人能用的服务就需要一个 Web 层。Flask 在 Python 的 Web 框架里属于轻量级选手没有 Django 那么多的默认约束写几个路由就能把模型封装成 HTTP 接口。这对于个人开发者或者小团队做工具类项目来说是最省事的选择。再说修复方案。当前老照片修复的主流技术路线基本都围绕生成对抗网络和自编码器展开。修复任务通常分三块去划痕和噪点、超分辨率重建、上色。这三块都有成熟的预训练模型可以直接用不需要从零训练。把 Flask 作为调度层请求进来后调用对应模型处理完返回结果整套逻辑并不复杂。这套系统的核心价值在于它不是一个只能在命令行里跑的实验脚本而是一个开箱可用的 Web 服务。用户上图片、点修复、下载结果整个过程不需要装 Python、不需要懂模型怎么加载只要浏览器能用就行。2. 核心技术选型与模型分析2.1 Flask 在项目里担当的角色Flask 的定位就是黏合层。它接收前端上传的老照片把图片临时存到服务器目录调用深度学习模型进行推理最后把修复后的图片返回给前端或提供下载路径。Flask 的优势在路由组织灵活。比如/api/repair处理修复请求/api/history查询处理记录/static/uploads提供静态文件中转。不需要额外配置只要实例化 Flask 应用、注册蓝本就可以跑起来。对于中小规模的个人工具站、私有部署场景Flask 够用且不至于杀鸡用牛刀。项目里用到的关键扩展主要是flask-cors用于解决前端跨域问题werkzeug自带的上传文件处理能力用于接收图片gevent或gunicorn用于部署时的并发支持。必要时要加个简单的验证机制比如flask-httpauth提供接口鉴权。2.2 深度学习模型部分如何选型老照片修复常见的模型路线有以下几类基于 GAN 的图像修复。典型代表是 DeOldify专门做老照片上色和划痕修复在开源社区使用广泛。DeOldify 的模型权重分三种Artistic和Stable两种渲染风格可以切换Artistic颜色更鲜艳但容易在某些肤色上出现偏色Stable效果更自然。实测下来人物照片用Stable更稳风景照用Artistic更容易出效果。超分辨率重建。典型模型是 ESRGAN、Real-ESRGAN。老照片普遍分辨率低、面部细节缺失直接用原始尺寸做修复回头放大了看还是一团糊。Real-ESRGAN 引入了更复杂的退化模型模拟对真实场景的低分辨率图像效果显著。这套系统里可以把超分环节放在修复链条的最后一环修正完细节后再放大输出观感最好。划痕检测与去除。这类模型通常基于掩码预测比如先检测划痕区域再通过图像补全网络填充。也可以直接用传统图像处理方法配合深度学习。但实际使用中对部分划痕效果不稳定所以项目里采用先检测掩码、再用 GAN 补全的混合方案。实际编码时模型加载是重点。PyTorch 默认将所有依赖的权重文件加载到 CPU 或 GPU 内存中如果不加限制服务会被超大模型拖垮。多模型共存时要设置合理的内存上限并注意模型推理是否占用同一块显存。如果只有一张显卡可能需要排队调度建议加一个简单的任务队列。2.3 预训练模型的选择策略对于这种工具型项目我强烈不建议从零训练模型。深度学习模型训练需要大规模成对数据集老照片修复的成对数据本身就难获取——你很难找到一张老照片对应的“完美原图”。业界通用的做法是使用开源的预训练权重。以 DeOldify 为例官方开源了在 ImageNet 上预训练并微调的权重文件直接下载就能用。需要做的只是将权重文件放入指定目录用 PyTorch 加载后调用推理函数。Real-ESRGAN 同样开源了多个版本的预训练权重包括针对人脸优化的人脸模型。选模型时还要注意输出尺寸限制。某些模型要求输入是 32 的整数倍否则会报错或者输出黑边。在 Flask 里需要做一个预处理先把图片等比缩放到合法尺寸再中心裁剪推理完成后再把边缘和原始尺寸做融合回填。这个细节如果处理不好用户上传一张特殊尺寸的图片就会看到输出图上有黑边或拉伸变形。3. 项目目录结构与核心实现3.1 源码目录设计拿到这个源码包先看目录结构就能摸清套路。合理的结构应该是photo_repair/ ├── app.py # Flask 主入口 ├── config.py # 配置文件 ├── models/ │ ├── deoldify.py # DeOldify 模型封装 │ ├── esrgan.py # 超分模型封装 │ └── utils.py # 图像预处理工具 ├── static/ │ ├── uploads/ # 上传原图 │ └── results/ # 修复输出 ├── templates/ │ └── index.html # 前端页面 ├── requirements.txt # 依赖清单 └── weights/ # 预训练权重目录把模型封装类单独放到 models 目录和 Flask 路由解耦。这样后续想换模型只要改 models 目录里的实现不影响接口层。weights 目录放权重文件避免把这个体积庞大的文件提交到源码仓库利用 README 说明下载地址即可。3.2 Flask 路由与请求处理以下是一个精简的 app.py 实现涵盖上传、修复状态查询和结果下载三个接口import os import uuid from flask import Flask, request, jsonify, send_from_directory from werkzeug.utils import secure_filename from models.deoldify import DeOldifyProcessor from models.esrgan import ESRGANProcessor app Flask(__name__) app.config.from_object(config.Config) ALLOWED_EXTENSIONS {png, jpg, jpeg, bmp, webp} UPLOAD_FOLDER app.config[UPLOAD_FOLDER] RESULT_FOLDER app.config[RESULT_FOLDER] deoldify_processor DeOldifyProcessor(app.config[DEOLDIFY_WEIGHTS]) esrgan_processor ESRGANProcessor(app.config[ESRGAN_WEIGHTS]) def allowed_file(filename): return . in filename and filename.rsplit(., 1)[1].lower() in ALLOWED_EXTENSIONS app.route(/api/repair, methods[POST]) def repair(): if file not in request.files: return jsonify({error: 没有文件上传}), 400 file request.files[file] if file.filename : return jsonify({error: 文件名为空}), 400 if not allowed_file(file.filename): return jsonify({error: 不支持的图片格式}), 400 ext file.filename.rsplit(., 1)[1].lower() upload_name f{uuid.uuid4().hex}.{ext} upload_path os.path.join(UPLOAD_FOLDER, upload_name) file.save(upload_path) mode request.form.get(mode, full) do_colorize mode in (full, colorize) do_enhance mode in (full, enhance) result_name f{uuid.uuid4().hex}.png result_path os.path.join(RESULT_FOLDER, result_name) try: # 第一步上色 去划痕DeOldify if do_colorize: processed deoldify_processor.process(upload_path) else: processed upload_path # 第二步超分辨率增强Real-ESRGAN if do_enhance: esrgan_processor.process(processed, result_path) else: shutil.copy(processed, result_path) except Exception as e: return jsonify({error: f修复失败: {str(e)}}), 500 return jsonify({ success: True, result_url: f/static/results/{result_name}, task_id: uuid.uuid4().hex }), 200 app.route(/static/results/filename) def result_file(filename): return send_from_directory(RESULT_FOLDER, filename) if __name__ __main__: app.run(host0.0.0.0, port5000, threadedTrue)这个实现里有个关键点是 model 实例化要放在路由外面也就是全局只加载一次权重。如果每次请求都重新加载模型服务器基本会被拖垮显存也会被反复分配释放最终导致 CUDA OOM。权重加载的耗时通常在几秒到几十秒这必须只做一次。3.3 DeOldify 模型封装细节import torch from PIL import Image import os class DeOldifyProcessor: def __init__(self, weights_path, deviceNone): self.device device or (cuda if torch.cuda.is_available() else cpu) from deoldify import device as deoldify_device deoldify_device.set(self.device) from deoldify.visualize import get_image_colorizer self.colorizer get_image_colorizer( artisticTrue, weights_pathweights_path, render_factor32, deviceself.device ) def process(self, input_path, output_pathNone, render_factor32): result self.colorizer.plot_transformed_image( pathinput_path, render_factorrender_factor, compareFalse, watermarkedFalse ) if output_path: result.save(output_path) return output_pathrender_factor 是 DeOldify 的核心参数控制渲染分辨率的上采样倍数。值越高颜色细节越丰富但处理时间也越长。经验值如下图片类型推荐 render_factor说明低分辨率人物照21~24颜色偏淡但稳定避免肤色失真一般生活照28~32默认范围大多数场景表现好风景照/高清大图32~40细节更丰富但耗时翻倍注意 DeOldify 的get_image_colorizer接收的权重路径需要注意版本适配不同的 DeOldify 版本对权重文件格式有兼容性要求一个是.pth格式一个是新版的.pkl。如果权重文件加载时报尺寸不匹配大概率是版本不一致导致。3.4 Real-ESRGAN 处理流程import cv2 class ESRGANProcessor: def __init__(self, weights_path, deviceNone): # 推荐使用 RealESRGANer 推理封装 from basicsr.archs.rrdbnet_arch import RRDBNet from realesrgan import RealESRGANer self.device device or (cuda if torch.cuda.is_available() else cpu) model RRDBNet(num_in_ch3, num_out_ch3, num_feat64, num_block23, num_grow_ch32, scale4) self.upsampler RealESRGANer( scale4, model_pathweights_path, modelmodel, tile256, tile_pad10, pre_pad0, halfFalse if self.device cpu else True, deviceself.device ) def process(self, input_path, output_path): img cv2.imread(input_path, cv2.IMREAD_COLOR) output, _ self.upsampler.enhance(img, outscale4) cv2.imwrite(output_path, output)这里有两个个容易踩的坑tile 参数。大分辨率图像直接输入模型会爆显存tile 设置成 256 表示把图切块处理每块 256x256 像素推理完成后再拼回去。tile_pad 是相邻块之间重叠的像素数用来消除拼接痕迹。如果输出图像在接缝处出现明显的色差多半是 tile_pad 设置太低。half 参数。在 GPU 推理时halfTrue使用半精度FP16计算显存占用减半且速度更快。但在 CPU 上不支持 FP16 推理必须显式设成 False。代码里已经通过self.device判断省得用户在 CPU 环境里直接报半精度不支持的错误。3.5 图像预处理的后坠路径处理中间文件管理是个容易被忽略的问题。如果用户先做了上色再超分辨率中间产物和最终产物都要写盘。临时文件最好统一放在一个临时目录并在请求处理完后清理。否则服务器跑几天磁盘空间就被撑满了。更稳妥的做法是生成一个临时目录task_id uuid.uuid4().hex然后把所有中间文件都写入这个目录最终再提取结果文件到结果目录。另一个容易被忽略的问题是原始图片的 EXIF 信息。手机或相机拍摄的图片可能带了旋转参数使用 OpenCV 读取时不会自动应用 EXIF 旋转导致修复结果出现方向不对的情况。要么在读取时用PIL.ImageOps.exif_transpose矫正要么用 OpenCV 的cv2.rotate手动处理。4. 前端界面与交互设计4.1 页面功能规划这个项目的目标是做一个可交互的工具所以前端不能只是一个空空的接口文档。要实现三个核心交互上传区域支持点击和拖拽上传前预览原图修复模式选择提供“全面修复”、“仅上色”、“仅增强”三个选项结果预览对比支持左右滑动前后对比下载按钮4.2 关键前端实现上传表单用FormData提交避免传统表单跳转刷新页面async function uploadAndRepair() { const fileInput document.getElementById(photoInput); const mode document.querySelector(input[namemode]:checked).value; if (!fileInput.files.length) { alert(请先选择图片); return; } const formData new FormData(); formData.append(file, fileInput.files[0]); formData.append(mode, mode); const resp await fetch(/api/repair, { method: POST, body: formData }); const data await resp.json(); if (data.success) { document.getElementById(resultImage).src data.result_url; document.getElementById(downloadBtn).href data.result_url; } else { alert(修复失败: data.error); } }前端值得留意的是图片预览的constraints比如限制上传图片体积不超过 20MB否则大尺寸图片会让模型处理时间超长。前端做一次校验后端也要做双重保险。对比预览可以用简单的 CSS 实现.compare-wrapper { position: relative; display: inline-block; } .compare-wrapper .after { position: absolute; top: 0; left: 0; clip-path: inset(0 0 0 50%); /* 通过滑块调整裁剪宽度 */ }这里用clip-path裁剪右侧图片展示范围配合滑块就能实现经典的左右拖动对比效果。5. 部署配置与性能优化5.1 依赖管理与环境搭建requirements.txt是部署时最关键的清单。由于 PyTorch 相关包的安装方式比较特殊建议在 README 里单独说明 CPU 版本和 GPU 版本的安装命令# 创建虚拟环境 conda create -n photo_repair python3.8 conda activate photo_repair # GPU 版本 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 基础依赖 pip install -r requirements.txtrequirements.txt 核心内容大致如下Flask2.2.5 flask-cors3.0.10 Pillow9.5.0 opencv-python4.8.0.74 numpy1.24.3 torch1.13.0 deoldify1.0.1 realesrgan0.3.0 basicsr1.4.2 gunicorn20.1.0需要注意版本号的兼容性。basicsr 和 realesrgan 有一些依赖交叠如果版本搭不对导入阶段就会报错。实际操作中直接使用 2023 年之后的版本匹配最不会出问题。5.2 本地与服务器部署的差异在本地开发时app.run(debugTrue)很方便但部署到服务器必须换生产级 WSGI 服务器。Flask 自带的开发服务器性能很差并发上来就卡。实测用 gunicorn 配合多 worker 模式gunicorn -w 2 -b 0.0.0.0:5000 app:app不过这里有一个坑如果模型是全局加载的gunicorn 开多个 worker 会导致模型被加载多份显存直接翻倍。2 个 worker 就需要 2 份模型显存。如果你只有一张显卡建议用 1 个 worker 配合线程模式gunicorn -w 1 --threads 4 -b 0.0.0.0:5000 app:app这样四个线程共享同一个模型实例不会重复加载权重又能一定程度上并发处理多个请求。不过 PyTorch 模型在多个线程中并行推理时会抢夺 GPU 资源线程数不宜设置过多实测 2 到 4 是最优区间。5.3 请求超时与任务队列深度学习推理耗时较长通常单张图片处理在 5 到 30 秒之间高峰期可能更长。HTTP 请求有超时限制如果处理时间超过 Nginx 或浏览器的超时时间用户会看到请求失败但后端仍在继续处理。这就需要用任务队列解耦。项目如果要做成正式服务建议引入 Celery 或 Redis 队列。请求进来直接返回task_id前端轮询/api/status/{task_id}查询处理状态。但这会增加部署复杂度个人项目可以先用同步模式但要向使用者说明超时风险。一个折中方案是设置 Flask 路由的 socket 超时和 Nginx 的 proxy_read_timeout 为 120 秒给模型推理留足时间。同时前端把请求超时时间也调长避免浏览器主动断开。6. 实际使用效果评估与调优记录6.1 测试样本与效果对比我拿了一组上世纪黑白人物照片、一张泛黄的风景胶片扫描件、一张严重划痕的证件照分别做了测试。黑白人物照经过 DeOldify 上色后肤色自然度在Stable模式下表现较好脸部和手部不会出现奇怪的青紫色。搭配 Real-ESRGAN 进行 4 倍超分后头发边缘细节有明显修复原来模糊的轮廓线更清晰了。风景胶片扫描件用Artistic模式上色效果最好天空偏蓝、树叶偏绿没有明显的颜色污染。唯一问题是部分高光区域出现轻微过曝和色斑这需要在模型参数里适当降低 render_factor让颜色不要过度饱和。严重划痕的证件照受限于模型能力大面积破损区域的修复效果一般。DeOldify 对细小划痕有效但对大块缺失区域的补全力不从心。如果要修这种重损伤图需要额外引入基于掩码的图像补全模型。6.2 参数调优经验表问题参数调整期望效果上色后肤色偏绿切换 Stable 模式肤色更自然修复后图片过曝降低 render_factor 到 24颜色温和超分后拼接痕迹明显增大 tile_pad 到 20接缝消失GPU 显存不足降低 tile 到 128显存占用降低CPU 推理太慢关闭 half画面尺寸限制在 1024 内速度提升明显7. 常见问题与排查技巧实录下面直接列出我在开发和部署过程中实际遇到的高频问题以及对应的排查路径。7.1 模型文件加载时报错现象运行后提示KeyError: xxx或size mismatch for xxx。原因及解决大概率是权重文件与模型代码版本不匹配。DeOldify 的权重文件在不同版本中 Key 名有变化要么换权重要么换库版本。建议把库锁定在一个稳定版本然后去对应的 release 页面下载匹配的权重文件。7.2 上传图片后报 413现象使用大尺寸图片上传时Flask 直接返回 413 Request Entity Too Large。解决在 Flask app 配置里加一行app.config[MAX_CONTENT_LENGTH] 20 * 1024 * 1024前端提示文件超限而不是直接让服务器报错。这个值根据实际使用场景调整限制在合理范围内可以有效防止内存被撑爆。7.3 CUDA Out of Memory现象并发请求两个以上时报 CUDA OOM。解决这类问题主要有三个原因每个请求都重置模型或重复加载代码里应该把模型设为全局单例输入图片尺寸过大超分模型 tile 参数未设置多个线程同时调用模型GPU 显存被峰值打满建议在调用模型前对图片做一次尺寸上限判断比如最长边超过 1600 先等比缩到 1600处理完再超分放大。7.4 输出图片是黑色的或者全灰现象模型返回的图片是一张纯黑或纯色图。原因输入图像色彩空间问题。OpenCV 读取图片默认是 BGR 格式但 PyTorch 模型训练时用的是 RGB。如果模型内部没做转换颜色通道错位会导致输出异常。处理方式是在模型推理前统一转换img_rgb cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)推理完成后再转回 BGRimg_bgr cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)7.5 Flask 处理请求时卡死无响应现象上传一张图后整个页面一直转圈最后无响应。原因可能是同步模型推理时间太长超出了浏览器或 Nginx 的默认超时时间。也可能是多线程下全局锁导致死锁。排查思路先用小尺寸图片测试确认推理流程正常查看服务端日志确认推理耗时把 Nginx 的proxy_read_timeout调大如果是单个 worker 多线程模式考虑逻辑线程锁问题如果并发量不大最简单的方式是用 1 个 worker 和 1 个线程配合任务队列。8. 源码优化与二次扩展建议拿到这套源码之后不要满足于能跑通还要思考怎么优化和扩展。8.1 代码结构的模块化改造目前的模型调用是硬编码在路由里的如果要扩展新的修复模型就需要改动 app.py。更好的做法是抽象一个修复器接口class BaseRepairer: def process(self, input_path, output_path, **kwargs): raise NotImplementedError class DeOldifyRepairer(BaseRepairer): pass class ESRGANRepairer(BaseRepairer): pass然后通过配置文件指定启用的模型链REPAIR_CHAIN [ {name: deoldify, order: 1}, {name: esrgan, order: 2} ]这样后续加新模型只需要实现 BaseRepairer 接口并注册即可。8.2 批处理能力扩展当前版本一次处理一张图片。如果用户手上有整本相册逐张上传会很痛苦。可以加一个/api/batch_repair接口接收多张图片后台用线程池逐张处理全部完成后统一打包下载。打包用zipfile实现不复杂但用户体验提升非常明显。8.3 引入模型缓存和预热首次加载权重需要几十秒如果服务器重启用户会感觉到明显的等待。可以在启动时增加预热逻辑app.before_first_request def warmup(): deoldify_processor.warmup() esrgan_processor.warmup()更优雅的方案是启动后异步预热用一个后台线程预先执行一次最小的推理任务把模型里的 lazy initialization 提前跑完。8.4 增加图片历史管理每次修复任务都保留原图、中间图和结果图记录时间戳和修复参数。用户可以根据历史任务回看效果也可以清理过期文件。这个功能只需要加一张 SQLite 表Flask 内置的sqlite3就能搞定不需要额外引入数据库服务。9. 部署上线要点与最终安全建议9.1 安全相关的关键细节任何涉及文件上传的服务都要警惕恶意文件。代码里有两个防线扩展名白名单只接受常见的图片格式重命名上传文件不用用户原始文件名防止路径穿越文件内容校验也值得做一下。虽然扩展名是.jpg但文件内容可能是脚本或恶意代码。用 PIL 打开一次如果无法解析则直接拒绝try: img Image.open(upload_path) img.verify() except Exception: os.remove(upload_path) return jsonify({error: 无效的图片文件}), 4009.2 服务器资源配置建议最低配置也要 4 核 CPU、8GB 内存最好带一块支持 CUDA 的 Nvidia 显卡显存 6GB 以上。没有 GPU 的环境跑 DeOldify 和 Real-ESRGAN 会很吃力单张图耗时可能超过 1 分钟。如果是个人体验CPU 环境就能跑但要接受速度慢和输出分辨率受限。部署在云服务器上时定期清理 uploads 目录的过期文件。使用celery的周期任务或 Crontab 脚本都可以。如果担心单机部署故障可以使用 Docker 容器化具体做法是将整个项目打包为镜像模型权重用 volume 挂载这样切换环境成本最低。9.3 模型的容错与回退生产环境里要处理模型推理失败的场景。例如输入图片分辨率异常、通道数不为 3、图片损坏等要捕获所有异常并返回中文提示。用户上传的图片格式多种多样不能假设每个文件都是干净的 RGB 三通道图片透明的 PNG 要先合成白底CMYK 模式的 JPEG 要先转成 RGB。我在实际操作中通常会加一个兜底策略如果超分模型失败就退回原图只返回上色结果确保用户经历了漫长的等待后至少能拿到一个可用的结果而不是一个冰冷的错误提示。这套基于 Flask 的老照片修复系统骨架并不复杂胜在思路清晰、模型选型成熟。核心就是将两个业界主流的开源视觉模型用轻量级 Web 框架串起来做成一个普通用户也能上手使用的小工具。只要把权重文件下载好、环境配置好整套项目跑起来并不难。后续想往产品方向走再加用户体系、任务队列、水印、历史管理能力边界可以不断扩展。希望这篇拆解能帮你把源码吃透做成自己真正能上手改进的项目。本文还有配套的精品资源点击获取
返回列表