From 9dcfff077eaa8bbe0d7d496e00a3b97273bcc78d Mon Sep 17 00:00:00 2001 From: 13315423919 <13315423919@qq.com> Date: Fri, 7 Nov 2025 09:05:13 +0800 Subject: [PATCH] Add File --- .../image/adapters/ppt_prompt_adapter.py | 345 ++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 src/landppt/services/image/adapters/ppt_prompt_adapter.py diff --git a/src/landppt/services/image/adapters/ppt_prompt_adapter.py b/src/landppt/services/image/adapters/ppt_prompt_adapter.py new file mode 100644 index 0000000..9443170 --- /dev/null +++ b/src/landppt/services/image/adapters/ppt_prompt_adapter.py @@ -0,0 +1,345 @@ +""" +PPT内容到图片生成提示词的适配器 +将PPT幻灯片内容转换为适合AI图片生成的提示词 +""" + +import logging +import re +from typing import Dict, Any, List, Optional, Tuple +from dataclasses import dataclass + +from ..models import ImageGenerationRequest, ImageProvider + +logger = logging.getLogger(__name__) + + +@dataclass +class PPTSlideContext: + """PPT幻灯片上下文信息""" + title: str + content: str + scenario: str + topic: str + page_number: int + total_pages: int + slide_type: str = "content" + language: str = "zh" + + +class PPTPromptAdapter: + """PPT内容到图片生成提示词的适配器""" + + def __init__(self, config: Dict[str, Any] = None): + self.config = config or {} + + # 场景到视觉风格的映射 + self.scenario_styles = { + "general": "professional, clean, modern", + "business": "corporate, professional, sleek, business meeting", + "education": "educational, academic, classroom, learning", + "technology": "futuristic, digital, high-tech, innovation", + "tourism": "scenic, beautiful, travel, destination", + "analysis": "data visualization, charts, analytics, research", + "history": "historical, vintage, cultural, heritage", + "medical": "medical, healthcare, clinical, scientific", + "finance": "financial, banking, investment, economic", + "marketing": "creative, colorful, engaging, promotional" + } + + # 内容类型到视觉元素的映射 + self.content_type_visuals = { + "title": "title slide, presentation cover, professional header", + "overview": "overview diagram, roadmap, structure visualization", + "content": "content slide, information display, bullet points", + "conclusion": "conclusion slide, summary, final thoughts", + "data": "data visualization, charts, graphs, statistics", + "process": "process flow, workflow, step-by-step diagram", + "comparison": "comparison chart, versus layout, side-by-side", + "timeline": "timeline, chronological order, historical progression" + } + + # 语言特定的提示词模板 + self.prompt_templates = { + "zh": { + "base": "专业的PPT幻灯片背景图片,{style_desc},{content_desc},高质量,商务风格", + "title": "PPT标题页背景,{topic}主题,{style_desc},专业设计", + "content": "PPT内容页背景,支持{content_type}展示,{style_desc},简洁明了", + "data": "数据可视化背景,适合图表展示,{style_desc},专业分析风格", + "conclusion": "PPT结论页背景,总结性设计,{style_desc},完整收尾" + }, + "en": { + "base": "Professional PPT slide background, {style_desc}, {content_desc}, high quality, business style", + "title": "PPT title slide background, {topic} theme, {style_desc}, professional design", + "content": "PPT content slide background, supports {content_type} display, {style_desc}, clean and clear", + "data": "Data visualization background, suitable for charts, {style_desc}, professional analysis style", + "conclusion": "PPT conclusion slide background, summary design, {style_desc}, complete ending" + } + } + + async def generate_image_prompt(self, slide_context: PPTSlideContext) -> str: + """为PPT幻灯片生成图片提示词""" + try: + # 分析幻灯片内容 + content_analysis = self._analyze_slide_content(slide_context) + + # 获取场景风格描述 + style_desc = self._get_scenario_style(slide_context.scenario) + + # 获取内容类型描述 + content_desc = self._get_content_description(slide_context, content_analysis) + + # 选择合适的提示词模板 + template_key = self._select_template_key(slide_context, content_analysis) + template = self.prompt_templates[slide_context.language][template_key] + + # 构建提示词 + prompt = template.format( + style_desc=style_desc, + content_desc=content_desc, + topic=slide_context.topic, + content_type=content_analysis["type"] + ) + + # 添加质量和风格修饰符 + prompt = self._enhance_prompt_quality(prompt, slide_context) + + # 添加负面提示词建议 + negative_prompt = self._generate_negative_prompt(slide_context) + + logger.info(f"Generated image prompt for slide {slide_context.page_number}: {prompt[:100]}...") + + return prompt + + except Exception as e: + logger.error(f"Failed to generate image prompt: {e}") + # 返回基础提示词 + return self._get_fallback_prompt(slide_context) + + async def create_generation_request(self, + slide_context: PPTSlideContext, + provider: ImageProvider = ImageProvider.DALLE, + **kwargs) -> ImageGenerationRequest: + """创建图片生成请求""" + # 生成主提示词 + prompt = await self.generate_image_prompt(slide_context) + + # 生成负面提示词 + negative_prompt = self._generate_negative_prompt(slide_context) + + # 根据幻灯片类型调整参数 + generation_params = self._get_generation_params(slide_context, provider) + generation_params.update(kwargs) + + return ImageGenerationRequest( + prompt=prompt, + negative_prompt=negative_prompt, + provider=provider, + **generation_params + ) + + def _analyze_slide_content(self, slide_context: PPTSlideContext) -> Dict[str, Any]: + """分析幻灯片内容""" + content = slide_context.content.lower() + title = slide_context.title.lower() + + analysis = { + "type": "content", + "keywords": [], + "themes": [], + "visual_elements": [], + "complexity": "medium" + } + + # 识别内容类型 + if slide_context.page_number == 1: + analysis["type"] = "title" + elif slide_context.page_number == slide_context.total_pages: + analysis["type"] = "conclusion" + elif any(word in content for word in ["数据", "统计", "图表", "分析", "data", "chart", "graph"]): + analysis["type"] = "data" + elif any(word in content for word in ["流程", "步骤", "过程", "process", "workflow", "step"]): + analysis["type"] = "process" + elif any(word in content for word in ["对比", "比较", "versus", "comparison", "vs"]): + analysis["type"] = "comparison" + elif any(word in content for word in ["时间", "历史", "发展", "timeline", "history", "evolution"]): + analysis["type"] = "timeline" + elif any(word in content for word in ["概述", "总览", "overview", "summary", "outline"]): + analysis["type"] = "overview" + + # 提取关键词 + keywords = self._extract_content_keywords(content + " " + title) + analysis["keywords"] = keywords[:10] # 最多10个关键词 + + # 识别主题 + themes = self._identify_content_themes(content + " " + title) + analysis["themes"] = themes + + # 评估复杂度 + if len(content.split()) > 100: + analysis["complexity"] = "high" + elif len(content.split()) < 30: + analysis["complexity"] = "low" + + return analysis + + def _extract_content_keywords(self, text: str) -> List[str]: + """从内容中提取关键词""" + # 移除标点符号和特殊字符 + text = re.sub(r'[^\w\s]', ' ', text) + words = text.split() + + # 停用词列表 + stop_words = { + '的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '上', '也', '很', '到', + 'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by' + } + + # 过滤停用词和短词 + keywords = [word for word in words if word not in stop_words and len(word) > 2] + + # 统计词频并返回最常见的词 + from collections import Counter + word_counts = Counter(keywords) + + return [word for word, count in word_counts.most_common(15)] + + def _identify_content_themes(self, text: str) -> List[str]: + """识别内容主题""" + themes = [] + text_lower = text.lower() + + theme_keywords = { + "success": ["成功", "成就", "胜利", "优秀", "success", "achievement", "victory", "excellent"], + "innovation": ["创新", "创意", "新颖", "突破", "innovation", "creative", "novel", "breakthrough"], + "growth": ["增长", "发展", "提升", "进步", "growth", "development", "improvement", "progress"], + "teamwork": ["团队", "合作", "协作", "配合", "team", "cooperation", "collaboration", "partnership"], + "technology": ["技术", "科技", "数字", "智能", "technology", "digital", "smart", "tech"], + "business": ["商业", "业务", "市场", "经济", "business", "market", "economic", "commercial"], + "education": ["教育", "学习", "培训", "知识", "education", "learning", "training", "knowledge"], + "future": ["未来", "前景", "展望", "趋势", "future", "prospect", "outlook", "trend"] + } + + for theme, keywords in theme_keywords.items(): + if any(keyword in text_lower for keyword in keywords): + themes.append(theme) + + return themes[:3] # 最多3个主题 + + def _get_scenario_style(self, scenario: str) -> str: + """获取场景对应的视觉风格""" + return self.scenario_styles.get(scenario, self.scenario_styles["general"]) + + def _get_content_description(self, slide_context: PPTSlideContext, content_analysis: Dict[str, Any]) -> str: + """获取内容描述""" + content_type = content_analysis["type"] + base_desc = self.content_type_visuals.get(content_type, "professional presentation slide") + + # 添加关键词增强 + if content_analysis["keywords"]: + keywords_str = ", ".join(content_analysis["keywords"][:3]) + base_desc += f", related to {keywords_str}" + + # 添加主题增强 + if content_analysis["themes"]: + themes_str = ", ".join(content_analysis["themes"]) + base_desc += f", {themes_str} theme" + + return base_desc + + def _select_template_key(self, slide_context: PPTSlideContext, content_analysis: Dict[str, Any]) -> str: + """选择合适的提示词模板""" + content_type = content_analysis["type"] + + if content_type == "title": + return "title" + elif content_type == "data": + return "data" + elif content_type == "conclusion": + return "conclusion" + else: + return "content" + + def _enhance_prompt_quality(self, prompt: str, slide_context: PPTSlideContext) -> str: + """增强提示词质量""" + # 添加质量修饰符 + quality_modifiers = [ + "high resolution", + "professional quality", + "clean design", + "modern aesthetic", + "suitable for presentation" + ] + + # 根据场景添加特定修饰符 + if slide_context.scenario == "business": + quality_modifiers.extend(["corporate style", "executive presentation"]) + elif slide_context.scenario == "technology": + quality_modifiers.extend(["futuristic design", "digital aesthetic"]) + elif slide_context.scenario == "education": + quality_modifiers.extend(["educational design", "academic style"]) + + enhanced_prompt = prompt + ", " + ", ".join(quality_modifiers[:3]) + + return enhanced_prompt + + def _generate_negative_prompt(self, slide_context: PPTSlideContext) -> str: + """生成负面提示词""" + negative_elements = [ + "cluttered", + "messy", + "unprofessional", + "low quality", + "blurry", + "distorted", + "inappropriate", + "distracting elements", + "poor composition", + "amateur design" + ] + + # 根据场景添加特定的负面元素 + if slide_context.scenario == "business": + negative_elements.extend(["casual", "informal", "playful"]) + elif slide_context.scenario == "education": + negative_elements.extend(["complex", "overwhelming", "confusing"]) + + return ", ".join(negative_elements[:8]) + + def _get_generation_params(self, slide_context: PPTSlideContext, provider: ImageProvider) -> Dict[str, Any]: + """获取生成参数""" + params = { + "width": 1920, # PPT标准宽度 + "height": 1080, # PPT标准高度 + "quality": "standard" + } + + # 根据提供者调整参数 + if provider == ImageProvider.DALLE: + params.update({ + "size": "1792x1024", # DALL-E 3支持的最接近16:9的尺寸 + "quality": "standard", + "style": "natural" # 更适合PPT背景 + }) + elif provider == ImageProvider.STABLE_DIFFUSION: + params.update({ + "width": 1024, + "height": 576, # 16:9比例 + "steps": 30, + "cfg_scale": 7.0, + "sampler": "K_DPM_2_ANCESTRAL" + }) + + # 根据幻灯片类型调整 + if slide_context.page_number == 1: # 标题页 + params["quality"] = "hd" if provider == ImageProvider.DALLE else params.get("quality") + + return params + + def _get_fallback_prompt(self, slide_context: PPTSlideContext) -> str: + """获取回退提示词""" + scenario_style = self._get_scenario_style(slide_context.scenario) + + if slide_context.language == "zh": + return f"专业的PPT幻灯片背景,{scenario_style}风格,适合{slide_context.topic}主题,高质量,商务设计" + else: + return f"Professional PPT slide background, {scenario_style} style, suitable for {slide_context.topic} theme, high quality, business design"