DeOldify Flask集成教程:将上色能力嵌入自有Web系统完整示例
DeOldify Flask集成教程:将上色能力嵌入自有Web系统完整示例
1. 项目概述
你是不是遇到过这样的场景:手头有一堆黑白老照片,想要让它们重焕光彩,但又不想学习复杂的深度学习技术?或者你正在开发一个Web应用,想要集成图片上色功能,却被模型部署和API对接搞得头大?
现在有了DeOldify图像上色服务,这些问题都能轻松解决。基于U-Net深度学习模型,这个服务能够智能地将黑白图片转换为彩色图片,而且最重要的是——你不需要懂任何深度学习知识,就能快速集成到自己的系统中。
本文将手把手教你如何将DeOldify图像上色能力集成到Flask Web应用中,从环境准备到完整代码实现,让你快速拥有一个专业的图片上色工具。
2. 环境准备与快速部署
2.1 系统要求
在开始之前,确保你的系统满足以下要求:
- Python 3.8或更高版本
- 至少4GB内存(处理大图片建议8GB以上)
- 网络连接(用于下载模型和依赖包)
2.2 安装必要依赖
首先创建并激活虚拟环境:
# 创建项目目录 mkdir deoldify-flask-app cd deoldify-flask-app # 创建虚拟环境 python -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows # 安装核心依赖 pip install flask requests pillow 2.3 验证DeOldify服务
确保DeOldify服务正在运行并可以访问:
# 检查服务健康状态 curl http://localhost:7860/health 如果服务正常,你会看到类似这样的响应:
{ "service": "cv_unet_image-colorization", "status": "healthy", "model_loaded": true } 3. Flask应用基础框架搭建
3.1 创建Flask应用结构
让我们先建立项目的基本文件结构:
deoldify-flask-app/ ├── app.py # 主应用文件 ├── static/ # 静态文件目录 │ ├── css/ │ ├── js/ │ └── images/ ├── templates/ # 模板目录 │ └── index.html ├── uploads/ # 上传文件临时目录 └── requirements.txt # 依赖列表 3.2 基础Flask应用代码
创建主应用文件 app.py:
from flask import Flask, render_template, request, send_file, jsonify import os from werkzeug.utils import secure_filename app = Flask(__name__) app.config['SECRET_KEY'] = 'your-secret-key-here' app.config['UPLOAD_FOLDER'] = 'uploads' app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024 # 50MB限制 # 确保上传目录存在 os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) # DeOldify服务配置 DEOLDIFY_API = "http://localhost:7860" @app.route('/') def index(): """显示主页面""" return render_template('index.html') if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5000) 3.3 创建基础HTML模板
创建 templates/index.html:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DeOldify图片上色工具</title> <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> </head> <body> <div> <h1>🎨 黑白图片上色工具</h1> <p>使用DeOldify AI技术为你的黑白照片添加色彩</p> <div> <h2>上传图片</h2> <form enctype="multipart/form-data"> <div> <input type="file" accept="image/*" hidden> <label for="imageInput"> <span>📁 选择图片或拖拽到此处</span> <p>支持 JPG, PNG, BMP 格式 (最大50MB)</p> </label> </div> <button type="submit">开始上色</button> </form> </div> <div> <h2>处理结果</h2> <div> <div> <h3>原始图片</h3> <img alt="原始图片"> </div> <div> <h3>上色结果</h3> <img alt="上色结果"> <div> <button onclick="downloadImage()">⬇️ 下载图片</button> </div> </div> </div> </div> <div> <div></div> <p>AI正在为图片上色,请稍候...</p> </div> </div> <script src="{{ url_for('static', filename='js/app.js') }}"></script> </body> </html> 4. 集成DeOldify上色功能
4.1 创建图片上色工具函数
在 app.py 中添加核心的上色功能:
import requests import base64 from io import BytesIO from PIL import Image import time def colorize_image(image_file): """ 调用DeOldify服务为图片上色 """ try: # 准备请求数据 files = {'image': (image_file.filename, image_file.stream, image_file.mimetype)} # 调用API start_time = time.time() response = requests.post(f"{DEOLDIFY_API}/colorize", files=files) processing_time = time.time() - start_time if response.status_code == 200: result = response.json() if result.get('success'): return { 'success': True, 'image_data': result['output_img_base64'], 'format': result.get('format', 'png'), 'processing_time': round(processing_time, 2) } return { 'success': False, 'error': '上色处理失败', 'details': response.text } except requests.exceptions.ConnectionError: return { 'success': False, 'error': '无法连接到上色服务', 'details': '请确保DeOldify服务正在运行' } except Exception as e: return { 'success': False, 'error': '处理过程中发生错误', 'details': str(e) } 4.2 添加上传和处理路由
在 app.py 中添加处理路由:
@app.route('/api/colorize', methods=['POST']) def api_colorize(): """API接口:处理图片上色""" if 'image' not in request.files: return jsonify({'success': False, 'error': '没有上传图片'}) image_file = request.files['image'] if image_file.filename == '': return jsonify({'success': False, 'error': '没有选择文件'}) # 验证文件类型 allowed_extensions = {'jpg', 'jpeg', 'png', 'bmp', 'tiff', 'webp'} if '.' not in image_file.filename or \ image_file.filename.rsplit('.', 1)[1].lower() not in allowed_extensions: return jsonify({'success': False, 'error': '不支持的图片格式'}) # 保存原始图片用于显示 filename = secure_filename(image_file.filename) original_path = os.path.join(app.config['UPLOAD_FOLDER'], f"original_{filename}") image_file.save(original_path) # 重置文件指针并调用上色服务 image_file.stream.seek(0) result = colorize_image(image_file) if result['success']: # 保存处理结果 colored_path = os.path.join(app.config['UPLOAD_FOLDER'], f"colored_{filename}") img_data = base64.b64decode(result['image_data']) with open(colored_path, 'wb') as f: f.write(img_data) result['original_filename'] = filename result['original_path'] = original_path result['colored_path'] = colored_path return jsonify(result) @app.route('/download/<filename>') def download_file(filename): """下载处理后的图片""" safe_filename = secure_filename(filename) file_path = os.path.join(app.config['UPLOAD_FOLDER'], safe_filename) if os.path.exists(file_path) and safe_filename.startswith('colored_'): return send_file(file_path, as_attachment=True) else: return '文件不存在', 404 5. 前端交互功能实现
5.1 创建CSS样式文件
创建 static/css/style.css:
* { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; } .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 15px; padding: 30px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); } h1 { text-align: center; color: #333; margin-bottom: 10px; font-size: 2.5em; } p { text-align: center; color: #666; margin-bottom: 30px; } .upload-section { background: #f8f9fa; padding: 30px; border-radius: 10px; margin-bottom: 30px; } .file-upload { margin-bottom: 20px; } .upload-label { display: block; padding: 60px; border: 3px dashed #ccc; border-radius: 10px; text-align: center; cursor: pointer; transition: all 0.3s ease; } .upload-label:hover { border-color: #667eea; background: #f0f4ff; } .upload-label span { font-size: 1.2em; color: #667eea; display: block; margin-bottom: 10px; } .upload-label p { color: #999; margin: 0; } .btn-primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 15px 30px; border-radius: 8px; font-size: 1.1em; cursor: pointer; width: 100%; transition: transform 0.2s ease; } .btn-primary:hover { transform: translateY(-2px); } .btn-primary:disabled { opacity: 0.6; cursor: not-allowed; transform: none; } .result-section { margin-top: 30px; } .comparison { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; margin-top: 20px; } .image-box { text-align: center; } .image-box h3 { margin-bottom: 15px; color: #333; } .image-box img { max-width: 100%; height: auto; border-radius: 8px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); } .download-btn { margin-top: 15px; } .download-btn button { background: #28a745; color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; } .loading { text-align: center; padding: 40px; } .spinner { width: 50px; height: 50px; border: 5px solid #f3f3f3; border-top: 5px solid #667eea; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 20px; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .error { background: #ffe6e6; color: #d63384; padding: 15px; border-radius: 8px; margin: 20px 0; text-align: center; } .success { background: #e6f7e6; color: #28a745; padding: 15px; border-radius: 8px; margin: 20px 0; text-align: center; } @media (max-width: 768px) { .comparison { grid-template-columns: 1fr; } .container { padding: 15px; } } 5.2 创建JavaScript交互逻辑
创建 static/js/app.js:
document.addEventListener('DOMContentLoaded', function() { const form = document.getElementById('uploadForm'); const fileInput = document.getElementById('imageInput'); const uploadLabel = document.querySelector('.upload-label'); const resultSection = document.querySelector('.result-section'); const loading = document.querySelector('.loading'); // 拖拽上传功能 uploadLabel.addEventListener('dragover', function(e) { e.preventDefault(); this.style.borderColor = '#667eea'; this.style.background = '#f0f4ff'; }); uploadLabel.addEventListener('dragleave', function(e) { e.preventDefault(); this.style.borderColor = '#ccc'; this.style.background = 'transparent'; }); uploadLabel.addEventListener('drop', function(e) { e.preventDefault(); this.style.borderColor = '#ccc'; this.style.background = 'transparent'; const files = e.dataTransfer.files; if (files.length > 0) { fileInput.files = files; updateFileName(files[0].name); } }); // 文件选择变化 fileInput.addEventListener('change', function() { if (this.files.length > 0) { updateFileName(this.files[0].name); } }); // 表单提交 form.addEventListener('submit', async function(e) { e.preventDefault(); if (!fileInput.files.length) { alert('请选择要上传的图片'); return; } const formData = new FormData(); formData.append('image', fileInput.files[0]); // 显示加载中 showLoading(); hideResult(); hideError(); try { const response = await fetch('/api/colorize', { method: 'POST', body: formData }); const result = await response.json(); if (result.success) { showResult(result); } else { showError(result.error, result.details); } } catch (error) { showError('网络错误', '无法连接到服务器'); } finally { hideLoading(); } }); function updateFileName(filename) { const span = uploadLabel.querySelector('span'); span.textContent = `📁 已选择: ${filename}`; } function showLoading() { loading.style.display = 'block'; form.querySelector('button').disabled = true; } function hideLoading() { loading.style.display = 'none'; form.querySelector('button').disabled = false; } function showResult(result) { // 显示原始图片 const originalImage = document.getElementById('originalImage'); originalImage.src = URL.createObjectURL(fileInput.files[0]); // 显示上色结果 const coloredImage = document.getElementById('coloredImage'); coloredImage.src = `data:image/${result.format};base64,${result.image_data}`; // 存储文件名用于下载 coloredImage.dataset.filename = result.original_filename; // 显示处理时间 const timeElement = document.createElement('div'); timeElement.className = 'success'; timeElement.innerHTML = `✅ 上色完成!处理时间: ${result.processing_time}秒`; resultSection.prepend(timeElement); resultSection.style.display = 'block'; } function hideResult() { resultSection.style.display = 'none'; } function showError(title, message) { const errorDiv = document.createElement('div'); errorDiv.className = 'error'; errorDiv.innerHTML = ` <strong>❌ ${title}</strong> <p>${message}</p> `; const container = document.querySelector('.container'); const uploadSection = document.querySelector('.upload-section'); container.insertBefore(errorDiv, uploadSection.nextSibling); // 5秒后自动隐藏错误信息 setTimeout(() => { errorDiv.remove(); }, 5000); } function hideError() { const errorDiv = document.querySelector('.error'); if (errorDiv) { errorDiv.remove(); } } }); // 下载图片功能 function downloadImage() { const coloredImage = document.getElementById('coloredImage'); const filename = coloredImage.dataset.filename; if (filename) { const link = document.createElement('a'); link.href = coloredImage.src; link.download = `colored_${filename}`; link.click(); } } 6. 完整应用测试与部署
6.1 测试应用功能
现在启动你的Flask应用进行测试:
python app.py 打开浏览器访问 http://localhost:5000,你应该能看到图片上色工具界面。尝试上传一张黑白图片,等待几秒钟后就能看到上色效果。
6.2 添加错误处理和日志记录
为了完善应用,添加错误处理和日志记录:
import logging from datetime import datetime # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('app.log'), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) @app.errorhandler(413) def too_large(e): return jsonify({'success': False, 'error': '文件太大', 'details': '请上传小于50MB的图片'}), 413 @app.errorhandler(500) def internal_error(e): logger.error(f'服务器内部错误: {str(e)}') return jsonify({'success': False, 'error': '服务器内部错误'}), 500 # 在colorize_image函数中添加日志记录 def colorize_image(image_file): try: logger.info(f'开始处理图片: {image_file.filename}') # ... 原有代码 ... if result['success']: logger.info(f'图片处理成功: {image_file.filename}, 耗时: {processing_time}秒') else: logger.error(f'图片处理失败: {image_file.filename}, 错误: {result.get("error")}') return result except Exception as e: logger.error(f'处理图片时发生错误: {str(e)}') return { 'success': False, 'error': '处理过程中发生错误', 'details': str(e) } 6.3 生产环境部署建议
对于生产环境,建议使用Gunicorn和Nginx:
- 安装Gunicorn:
pip install gunicorn - 创建Gunicorn配置文件
gunicorn_conf.py:
bind = "0.0.0.0:5000" workers = 4 worker_class = "sync" timeout = 120 - 使用Gunicorn启动应用:
gunicorn -c gunicorn_conf.py app:app - 配置Nginx反向代理(可选但推荐)
7. 总结
通过本教程,你已经成功将DeOldify图像上色能力集成到了Flask Web应用中。现在你拥有一个功能完整的图片上色工具,支持:
- 🖼️ 拖拽上传和文件选择两种方式
- 🎨 调用DeOldify AI服务进行智能上色
- 📱 响应式设计,支持移动端访问
- ⬇️ 一键下载上色结果
- ⚡ 实时进度显示和错误处理
这个示例展示了如何将复杂的AI能力以简单的方式集成到Web应用中,即使你没有深度学习背景也能轻松实现。你可以在此基础上进一步扩展功能,比如添加批量处理、历史记录、用户管理等功能。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 ZEEKLOG星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。