台州市网站建设_网站建设公司_腾讯云_seo优化
2025/12/28 23:17:19 网站建设 项目流程

第4天:图片生成功能实现

📋 第4天概述

在第3天完成文字生成功能的基础上,第4天将重点实现图片生成功能的完整业务逻辑,包括图像生成服务、图片处理工具、文件管理以及用户界面集成。

🎯 第4天目标

主要任务:构建完整的图片生成系统,实现智能图像生成、图片处理和文件管理
核心需求:开发稳定可靠的图像生成服务,支持多种风格和尺寸定制

🏗️ 架构设计

服务层架构

class ImageGenerationService:"""图片生成服务类"""def __init__(self, aliyun_client):self.client = aliyun_clientself.image_templates = self._load_image_templates()self.image_storage = ImageStorage()# 核心生成方法def generate_image(self, prompt, style, size, **kwargs)def _build_image_prompt(self, base_prompt, style)def _process_image_response(self, response)# 图片管理def save_image(self, image_data, metadata)def get_image_history(self, limit=10)def delete_image(self, image_id)# 工具方法def resize_image(self, image_path, target_size)def convert_format(self, image_path, target_format)def add_watermark(self, image_path, watermark_text)

🔧 具体实施步骤

步骤1:创建图片生成服务类

创建 src/services/image_service.py

import os
import base64
import json
import time
from datetime import datetime
from typing import Dict, List, Optional, Any, Tuple
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
import ioclass ImageGenerationService:"""图片生成服务类负责图像生成、图片处理和文件管理"""def __init__(self, aliyun_client, upload_folder: str = "./uploads"):self.client = aliyun_clientself.upload_folder = Path(upload_folder)self.image_templates = self._load_image_templates()self.image_history = []self.max_history_size = 50# 确保上传目录存在self.upload_folder.mkdir(exist_ok=True)# 创建图片子目录(self.upload_folder / "images").mkdir(exist_ok=True)(self.upload_folder / "thumbnails").mkdir(exist_ok=True)def _load_image_templates(self) -> Dict[str, Dict[str, Any]]:"""加载图片生成模板"""templates = {"卡通风格": {"prompt_suffix": ",卡通风格,色彩鲜艳,线条简洁","styles": ["可爱", "搞笑", "简约", "日系", "美式"],"size_options": ["512x512", "768x768", "1024x1024", "1024x768", "768x1024"]},"写实风格": {"prompt_suffix": ",写实风格,细节丰富,光影真实","styles": ["超写实", "油画", "水彩", "素描", "摄影"],"size_options": ["512x512", "768x768", "1024x1024", "1024x768", "768x1024"]},"奇幻风格": {"prompt_suffix": ",奇幻风格,充满想象力,神秘氛围","styles": ["魔法", "神话", "科幻", "童话", "梦境"],"size_options": ["512x512", "768x768", "1024x1024", "1024x768", "768x1024"]},"教育风格": {"prompt_suffix": ",教育风格,清晰易懂,适合儿童","styles": ["科普", "数学", "语言", "艺术", "历史"],"size_options": ["512x512", "768x768", "1024x1024", "1024x768", "768x1024"]}}return templatesdef get_available_styles(self) -> List[str]:"""获取可用的图片风格"""return list(self.image_templates.keys())def get_style_options(self, style: str) -> List[str]:"""获取指定风格的子风格选项"""if style in self.image_templates:return self.image_templates[style]["styles"]return []def get_size_options(self, style: str) -> List[str]:"""获取指定风格的尺寸选项"""if style in self.image_templates:return self.image_templates[style]["size_options"]return []def _build_image_prompt(self, base_prompt: str, style: str, sub_style: str = "") -> str:"""构建图片生成提示词"""if style in self.image_templates:prompt_suffix = self.image_templates[style]["prompt_suffix"]if sub_style:prompt_suffix = prompt_suffix.replace("风格", f"{sub_style}风格")final_prompt = base_prompt + prompt_suffixelse:final_prompt = base_prompt# 添加质量要求quality_requirements = """请确保图片具有以下特点:1. 构图合理,主体突出2. 色彩协调,视觉效果良好3. 细节丰富,避免模糊4. 符合儿童审美,避免恐怖内容5. 具有艺术性和创意性"""return final_prompt + quality_requirementsdef generate_image(self, prompt: str, style: str, size: str = "1024x1024", sub_style: str = "", model: str = "wan2.5-t2i-preview", n: int = 1, **kwargs) -> Dict[str, Any]:"""生成图片Args:prompt: 提示词style: 图片风格size: 图片尺寸sub_style: 子风格model: 模型名称n: 生成数量**kwargs: 其他参数Returns:dict: 生成结果"""# 构建完整提示词full_prompt = self._build_image_prompt(prompt, style, sub_style)print(f"🎨 开始生成图片...")print(f"提示词: {prompt}")print(f"风格: {style} - {sub_style}")print(f"尺寸: {size}")print(f"完整提示词长度: {len(full_prompt)} 字符")try:# 调用API生成图片start_time = time.time()response = self.client.generate_image(prompt=full_prompt,model=model,size=size,style=sub_style or style,n=n,**kwargs)generation_time = time.time() - start_time# 处理响应processed_result = self._process_image_response(response)# 构建结果result = {"success": True,"prompt": prompt,"style": style,"sub_style": sub_style,"size": size,"generation_time": round(generation_time, 2),"timestamp": datetime.now().isoformat(),"model_used": model,**processed_result}# 保存图片文件if processed_result.get("image_url"):saved_path = self._save_image_from_url(processed_result["image_url"], result)result["local_path"] = str(saved_path)# 生成缩略图thumbnail_path = self._create_thumbnail(saved_path)result["thumbnail_path"] = str(thumbnail_path)# 保存到历史记录self._save_to_history(result)print(f"✅ 图片生成成功!")print(f"生成时间: {generation_time:.2f}秒")print(f"图片尺寸: {size}")return resultexcept Exception as e:error_result = {"success": False,"error": str(e),"prompt": prompt,"style": style,"size": size,"timestamp": datetime.now().isoformat()}print(f"❌ 图片生成失败: {e}")return error_resultdef _process_image_response(self, response: Dict[str, Any]) -> Dict[str, Any]:"""处理图片生成响应"""# 阿里云文生图API返回格式处理if "output" in response and "task_id" in response["output"]:# 异步任务模式,返回任务IDreturn {"task_id": response["output"]["task_id"],"task_status": "pending"}elif "data" in response and len(response["data"]) > 0:# 直接返回图片URLimage_data = response["data"][0]return {"image_url": image_data.get("url", ""),"task_status": "completed"}else:raise ValueError("无法从响应中提取图片信息")def _save_image_from_url(self, image_url: str, metadata: Dict[str, Any]) -> Path:"""从URL下载并保存图片"""import requeststry:# 下载图片response = requests.get(image_url, timeout=30)response.raise_for_status()# 生成文件名timestamp = int(time.time())filename = f"image_{timestamp}_{metadata['style']}.png"filepath = self.upload_folder / "images" / filename# 保存图片with open(filepath, 'wb') as f:f.write(response.content)# 验证图片有效性with Image.open(filepath) as img:img.verify()return filepathexcept Exception as e:raise Exception(f"图片保存失败: {e}")def _create_thumbnail(self, image_path: Path, size: Tuple[int, int] = (200, 200)) -> Path:"""创建缩略图"""try:with Image.open(image_path) as img:# 保持宽高比生成缩略图img.thumbnail(size, Image.Resampling.LANCZOS)# 生成缩略图文件名thumbnail_name = f"thumb_{image_path.stem}.png"thumbnail_path = self.upload_folder / "thumbnails" / thumbnail_name# 保存缩略图img.save(thumbnail_path, "PNG")return thumbnail_pathexcept Exception as e:raise Exception(f"缩略图创建失败: {e}")def _save_to_history(self, image_data: Dict[str, Any]):"""保存图片到历史记录"""if image_data["success"]:# 为图片分配IDimage_id = f"image_{len(self.image_history) + 1}_{int(time.time())}"image_data["id"] = image_id# 添加到历史记录self.image_history.append(image_data)# 限制历史记录大小if len(self.image_history) > self.max_history_size:self.image_history = self.image_history[-self.max_history_size:]def get_image_history(self, limit: int = 10) -> List[Dict[str, Any]]:"""获取图片历史记录"""return self.image_history[-limit:]def get_image_by_id(self, image_id: str) -> Optional[Dict[str, Any]]:"""根据ID获取图片"""for image in self.image_history:if image.get("id") == image_id:return imagereturn Nonedef delete_image(self, image_id: str) -> bool:"""删除指定图片"""for i, image in enumerate(self.image_history):if image.get("id") == image_id:# 删除本地文件if "local_path" in image:try:Path(image["local_path"]).unlink(missing_ok=True)except:passif "thumbnail_path" in image:try:Path(image["thumbnail_path"]).unlink(missing_ok=True)except:pass# 从历史记录中移除self.image_history.pop(i)return Truereturn Falsedef resize_image(self, image_path: str, target_size: Tuple[int, int]) -> Path:"""调整图片尺寸"""try:with Image.open(image_path) as img:# 调整尺寸resized_img = img.resize(target_size, Image.Resampling.LANCZOS)# 生成新文件名original_path = Path(image_path)new_name = f"{original_path.stem}_{target_size[0]}x{target_size[1]}{original_path.suffix}"new_path = original_path.parent / new_name# 保存调整后的图片resized_img.save(new_path)return new_pathexcept Exception as e:raise Exception(f"图片尺寸调整失败: {e}")def convert_format(self, image_path: str, target_format: str) -> Path:"""转换图片格式"""try:with Image.open(image_path) as img:# 生成新文件名original_path = Path(image_path)new_name = f"{original_path.stem}.{target_format.lower()}"new_path = original_path.parent / new_name# 转换格式并保存if target_format.upper() == "JPG":# 转换为RGB模式(JPG不支持透明度)if img.mode in ("RGBA", "P"):img = img.convert("RGB")img.save(new_path, target_format.upper())return new_pathexcept Exception as e:raise Exception(f"图片格式转换失败: {e}")def add_watermark(self, image_path: str, watermark_text: str, position: str = "bottom-right", opacity: int = 50) -> Path:"""添加水印"""try:with Image.open(image_path) as img:# 转换为RGBA模式以支持透明度if img.mode != "RGBA":img = img.convert("RGBA")# 创建水印图层watermark = Image.new("RGBA", img.size, (0, 0, 0, 0))draw = ImageDraw.Draw(watermark)# 尝试加载字体,失败则使用默认字体try:font = ImageFont.truetype("arial.ttf", 24)except:font = ImageFont.load_default()# 计算水印位置bbox = draw.textbbox((0, 0), watermark_text, font=font)text_width = bbox[2] - bbox[0]text_height = bbox[3] - bbox[1]if position == "bottom-right":x = img.width - text_width - 10y = img.height - text_height - 10elif position == "top-left":x = 10y = 10elif position == "center":x = (img.width - text_width) // 2y = (img.height - text_height) // 2else:x = 10y = img.height - text_height - 10# 绘制水印draw.text((x, y), watermark_text, font=font, fill=(255, 255, 255, int(255 * opacity / 100)))# 合并图片和水印watermarked = Image.alpha_composite(img, watermark)# 生成新文件名original_path = Path(image_path)new_name = f"{original_path.stem}_watermarked{original_path.suffix}"new_path = original_path.parent / new_name# 保存带水印的图片watermarked.save(new_path)return new_pathexcept Exception as e:raise Exception(f"水印添加失败: {e}")

步骤2:创建图片生成Web路由

创建 src/web/routes/image_routes.py

from flask import Blueprint, request, jsonify, send_file
from src.services.image_service import ImageGenerationService
import os
from pathlib import Path# 创建蓝图
image_bp = Blueprint('image', __name__)def init_image_routes(app, aliyun_client, upload_folder: str):"""初始化图片生成路由"""image_service = ImageGenerationService(aliyun_client, upload_folder)@image_bp.route('/api/generate_image', methods=['POST'])def generate_image():"""生成图片API"""try:data = request.get_json()# 验证必需参数required_fields = ['prompt', 'style']for field in required_fields:if field not in data:return jsonify({'success': False,'error': f'缺少必需参数: {field}'}), 400# 调用服务生成图片result = image_service.generate_image(prompt=data['prompt'],style=data['style'],size=data.get('size', '1024x1024'),sub_style=data.get('sub_style', ''),model=data.get('model', 'wan2.5-t2i-preview'),n=int(data.get('n', 1)))return jsonify(result)except Exception as e:return jsonify({'success': False,'error': f'生成图片失败: {str(e)}'}), 500@image_bp.route('/api/image_history', methods=['GET'])def get_image_history():"""获取图片历史记录"""try:limit = int(request.args.get('limit', 10))history = image_service.get_image_history(limit)return jsonify({'success': True,'history': history})except Exception as e:return jsonify({'success': False,'error': str(e)}), 500@image_bp.route('/api/image_templates', methods=['GET'])def get_image_templates():"""获取图片模板信息"""try:styles = image_service.get_available_styles()templates = {}for style in styles:templates[style] = {'sub_styles': image_service.get_style_options(style),'size_options': image_service.get_size_options(style)}return jsonify({'success': True,'templates': templates})except Exception as e:return jsonify({'success': False,'error': str(e)}), 500@image_bp.route('/api/image/<path:filename>')def serve_image(filename):"""提供图片文件服务"""try:filepath = Path(upload_folder) / "images" / filenameif not filepath.exists() or not filepath.is_file():return jsonify({'error': '图片不存在'}), 404return send_file(filepath)except Exception as e:return jsonify({'error': str(e)}), 500@image_bp.route('/api/thumbnail/<path:filename>')def serve_thumbnail(filename):"""提供缩略图服务"""try:filepath = Path(upload_folder) / "thumbnails" / filenameif not filepath.exists() or not filepath.is_file():return jsonify({'error': '缩略图不存在'}), 404return send_file(filepath)except Exception as e:return jsonify({'error': str(e)}), 500@image_bp.route('/api/process_image', methods=['POST'])def process_image():"""图片处理API"""try:data = request.get_json()if 'image_id' not in data or 'action' not in data:return jsonify({'error': '缺少必需参数'}), 400image = image_service.get_image_by_id(data['image_id'])if not image or 'local_path' not in image:return jsonify({'error': '图片不存在'}), 404result = {}if data['action'] == 'resize':size = tuple(map(int, data['size'].split('x')))new_path = image_service.resize_image(image['local_path'], size)result['new_path'] = str(new_path)elif data['action'] == 'convert':new_path = image_service.convert_format(image['local_path'], data['format'])result['new_path'] = str(new_path)elif data['action'] == 'watermark':new_path = image_service.add_watermark(image['local_path'], data['text'],data.get('position', 'bottom-right'),int(data.get('opacity', 50)))result['new_path'] = str(new_path)return jsonify({'success': True,'result': result})except Exception as e:return jsonify({'success': False,'error': str(e)}), 500# 注册蓝图app.register_blueprint(image_bp, url_prefix='/image')

步骤3:创建图片生成前端界面

创建 src/web/templates/image_generation.html

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>AI图片生成器</title><link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"><style>.image-generator {max-width: 1000px;margin: 0 auto;padding: 20px;}.form-container {background: white;padding: 30px;border-radius: 15px;box-shadow: 0 4px 15px rgba(0,0,0,0.1);margin-bottom: 30px;}.form-group {margin-bottom: 25px;}.form-group label {display: block;margin-bottom: 8px;font-weight: 600;color: #333;}.form-control {width: 100%;padding: 12px;border: 2px solid #e0e0e0;border-radius: 8px;font-size: 16px;transition: border-color 0.3s;}.form-control:focus {outline: none;border-color: #4CAF50;}.btn-generate {background: linear-gradient(45deg, #FF6B6B, #4ECDC4);color: white;border: none;padding: 15px 40px;border-radius: 25px;font-size: 18px;font-weight: 600;cursor: pointer;transition: all 0.3s;box-shadow: 0 4px 15px rgba(0,0,0,0.2);}.btn-generate:hover {transform: translateY(-2px);box-shadow: 0 6px 20px rgba(0,0,0,0.3);}.btn-generate:disabled {background: #ccc;cursor: not-allowed;transform: none;}.image-result {margin-top: 30px;text-align: center;display: none;}.generated-image {max-width: 100%;max-height: 600px;border-radius: 10px;box-shadow: 0 4px 15px rgba(0,0,0,0.2);margin-bottom: 20px;}.image-actions {display: flex;gap: 15px;justify-content: center;flex-wrap: wrap;}.btn-action {background: #2196F3;color: white;border: none;padding: 10px 20px;border-radius: 20px;cursor: pointer;transition: background 0.3s;}.btn-action:hover {background: #1976D2;}.loading {text-align: center;padding: 40px;display: none;}.spinner {border: 4px solid #f3f3f3;border-top: 4px solid #3498db;border-radius: 50%;width: 40px;height: 40px;animation: spin 2s linear infinite;margin: 0 auto 20px;}@keyframes spin {0% { transform: rotate(0deg); }100% { transform: rotate(360deg); }}.error-message {background: #ffebee;color: #c62828;padding: 15px;border-radius: 8px;margin: 20px 0;display: none;}.history-section {margin-top: 50px;}.history-grid {display: grid;grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));gap: 20px;margin-top: 20px;}.history-item {background: white;border-radius: 10px;padding: 15px;box-shadow: 0 2px 10px rgba(0,0,0,0.1);text-align: center;cursor: pointer;transition: transform 0.3s;}.history-item:hover {transform: translateY(-5px);}.history-thumbnail {width: 100%;height: 120px;object-fit: cover;border-radius: 5px;margin-bottom: 10px;}</style>
</head>
<body><div class="image-generator"><h1>🎨 AI图片生成器</h1><div class="form-container"><div class="form-group"><label for="prompt">图片描述:</label><textarea id="prompt" class="form-control" rows="3" placeholder="请输入您想要生成的图片描述,例如:一只可爱的小猫在花园里玩耍..."></textarea></div><div class="form-row"><div class="form-group" style="flex: 1;"><label for="style">主要风格:</label><select id="style" class="form-control"><option value="">请选择风格...</option></select></div><div class="form-group" style="flex: 1;"><label for="subStyle">子风格:</label><select id="subStyle" class="form-control" disabled><option value="">请先选择主要风格</option></select></div><div class="form-group" style="flex: 1;"><label for="size">图片尺寸:</label><select id="size" class="form-control" disabled><option value="">请先选择风格</option></select></div></div><button id="generateBtn" class="btn-generate">✨ 生成图片</button></div><div id="loading" class="loading"><div class="spinner"></div><p>AI正在创作中,这可能需要一些时间...</p></div><div id="errorMessage" class="error-message"></div><div id="imageResult" class="image-result"><h3>🖼️ 生成的图片</h3><img id="generatedImage" class="generated-image" src="" alt="生成的图片"><div class="image-meta"><p><strong>生成时间:</strong> <span id="generationTime"></span>秒</p><p><strong>图片尺寸:</strong> <span id="imageSize"></span></p></div><div class="image-actions"><button id="downloadBtn" class="btn-action">💾 下载图片</button><button id="copyPromptBtn" class="btn-action">📋 复制提示词</button><button id="regenerateBtn" class="btn-action">🔄 重新生成</button></div></div><div class="history-section"><h3>📚 生成历史</h3><div id="historyGrid" class="history-grid"></div></div></div><script src="{{ url_for('static', filename='js/image_generation.js') }}"></script>
</body>
</html>

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询