Compare commits

...

2 Commits

Author SHA1 Message Date
764540c432 保持了左侧区域的对齐 2025-07-10 10:09:16 +08:00
3ddc04caff reinit, vibrant_model 可用 2025-07-10 10:08:03 +08:00
121 changed files with 1918 additions and 1 deletions

View File

@ -1,6 +1,5 @@
/result/ /result/
*.zip *.zip
/hotel_img/
/output/ /output/
/log/ /log/
/__pycache__/ /__pycache__/

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@ -0,0 +1,98 @@
import os
import sys
from PIL import Image
# 将项目根目录添加到 Python 路径中,以便导入 poster 模块
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from utils.poster.templates.vibrant_template import VibrantTemplate
from utils.poster.templates.business_template import BusinessTemplate
def test_templates():
"""
测试 VibrantTemplate BusinessTemplate 的海报生成功能
"""
# 定义资源和输出目录
image_dir = '/root/autodl-tmp/TCC_RESTRUCT/resource/data/images/天津冒险湾'
output_dir = 'tests/output'
os.makedirs(output_dir, exist_ok=True)
# --- 1. 测试 VibrantTemplate ---
print("正在测试 VibrantTemplate...")
try:
vibrant_config = {
'image_path': os.path.join(image_dir, '微信图片_20250703104552.jpg'),
'content': {
"title": "探索天津冒险湾",
"slogan": "奇幻水上乐园,家庭欢乐首选!",
"price": "199",
"ticket_type": "夜场票",
"content_button": "套餐内容",
"content_items": [
"体验所有水上滑梯",
"享受家庭欢乐时光",
"品尝美味海滨小吃",
"参与精彩互动表演"
],
"remarks": [
"工作日可直接入园",
"周末请提前1天预约"
],
"tag": "#夏日特惠"
}
}
vibrant_template = VibrantTemplate()
vibrant_poster = vibrant_template.generate(
image_path=vibrant_config['image_path'],
content=vibrant_config['content']
)
vibrant_output_path = os.path.join(output_dir, 'vibrant_poster_test.png')
vibrant_poster.save(vibrant_output_path)
print(f"VibrantTemplate 测试成功,海报已保存至: {vibrant_output_path}")
except Exception as e:
print(f"VibrantTemplate 测试失败: {e}")
# --- 2. 测试 BusinessTemplate ---
print("\n正在测试 BusinessTemplate...")
try:
# 保持与原始文件一致的 "hotel_info" 结构
hotel_info = {
"name": "天津冒险湾:商业合作新机遇",
"feature": "携手共创文旅新篇章",
"slogan": "打造顶级水上文旅品牌",
"price": "洽谈",
"info_list": [
{'title': '战略合作', 'desc': '与顶级品牌联合,提升影响力。'},
{'title': '市场推广', 'desc': '覆盖全媒体渠道,精准触达目标客户。'},
{'title': '活动承办', 'desc': '承接各类大型商业活动,设施齐全。'},
{'title': '投资前景', 'desc': '高回报率的文旅投资项目,前景广阔。'}
],
"footer": [
"联系我们partner@adventurebay.com",
"地址:天津市滨海新区"
]
}
business_config = {
'top_image_path': os.path.join(image_dir, '微信图片_20250703104600.jpg'),
'bottom_image_path': os.path.join(image_dir, '微信图片_20250703104605.jpg'),
'small_image_paths': [os.path.join(image_dir, '微信图片_20250703104609.jpg')],
'content': hotel_info # 传递完整的 "hotel_info"
}
business_template = BusinessTemplate()
business_poster = business_template.generate(**business_config)
# 将 RGBA 模式转换为 RGB 模式以便保存为 JPG
if business_poster.mode == 'RGBA':
business_poster = business_poster.convert('RGB')
business_output_path = os.path.join(output_dir, 'business_poster_test.png')
business_poster.save(business_output_path)
print(f"BusinessTemplate 测试成功,海报已保存至: {business_output_path}")
except Exception as e:
print(f"BusinessTemplate 测试失败: {e}")
if __name__ == "__main__":
test_templates()

24
utils/poster/__init__.py Normal file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
海报生成模块主入口
"""
from .poster_generator import PosterGenerator
from .templates.base_template import BaseTemplate
from .templates.vibrant_template import VibrantTemplate
from .templates.business_template import BusinessTemplate
from .templates.collage_template import CollageTemplate
from .utils import ImageProcessor, TextRenderer, ColorExtractor
__all__ = [
"PosterGenerator",
"BaseTemplate",
"VibrantTemplate",
"BusinessTemplate",
"CollageTemplate",
"ImageProcessor",
"TextRenderer",
"ColorExtractor"
]

Binary file not shown.

Binary file not shown.

Binary file not shown.

16
utils/poster/job.py Normal file
View File

@ -0,0 +1,16 @@
from typing import Dict, Any, List, Optional
from pydantic import BaseModel, Field
class TextGenerationParams(BaseModel):
"""文本生成特定参数"""
system_prompt_path: Optional[str] = None
user_prompt_path: Optional[str] = None
content_data: Dict[str, Any] = Field(default_factory=dict)
class PosterJob(BaseModel):
"""定义单个海报生成任务的配置"""
template: str = "vibrant"
size: List[int] = Field(default_factory=lambda: [1080, 1080])
params: Dict[str, Any] = Field(default_factory=dict)
generate_text: bool = False
text_generation_params: TextGenerationParams = Field(default_factory=TextGenerationParams)

View File

@ -0,0 +1,132 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
海报生成流程总协调器
"""
import logging
import os
from typing import Dict, Any, Optional
import sys
from PIL import Image
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# 核心模块依赖
from core.ai import AIAgent
from core.config import PosterConfig
from utils.file_io import OutputManager, ResourceLoader
# 本地模块依赖
from .job import PosterJob
from .templates.vibrant_template import VibrantTemplate
from .templates.business_template import BusinessTemplate
from .templates.collage_template import CollageTemplate
from .text_generator import PosterContentGenerator
from .utils import ImageProcessor
logger = logging.getLogger(__name__)
class PosterGenerator:
"""
负责根据配置选择和调用不同的海报模板并协调整个生成流程
"""
def __init__(self, config: PosterConfig, ai_agent: AIAgent, output_manager: OutputManager):
"""
初始化海报生成器
Args:
config (PosterConfig): 海报生成相关的配置模型
ai_agent (AIAgent): AI代理实例
output_manager (OutputManager): 文件输出处理器
"""
self.config = config
self.ai_agent = ai_agent
self.output_manager = output_manager
self.content_generator = PosterContentGenerator(ai_agent)
self.logger = logging.getLogger(__name__)
# 模板注册表
self.templates = {
"vibrant": VibrantTemplate,
"business": BusinessTemplate,
"collage": CollageTemplate,
}
async def generate(self, run_id: str, topic_index: int, variant_index: int, job: PosterJob) -> Optional[str]:
"""
执行单个海报生成任务
Args:
run_id (str): 当前运行的ID
topic_index (int): 主题索引
variant_index (int): 变体索引
job (PosterJob): 包含任务详情的配置对象
Returns:
Optional[str]: 成功则返回生成海报的路径否则返回None
"""
self.logger.info(f"开始处理海报任务: template={job.template}, topic={topic_index}, variant={variant_index}")
# 1. 选择模板
template_class = self.templates.get(job.template)
if not template_class:
self.logger.error(f"未知的海报模板: {job.template}")
return None
template_instance = template_class(size=job.size)
# 2. 准备生成参数
generation_params = job.params.copy()
# 3. (可选) AI生成文本
if job.generate_text:
text_params = job.text_generation_params
system_prompt = ResourceLoader.load_text_file(text_params.system_prompt_path or self.config.poster_system_prompt)
user_prompt = ResourceLoader.load_text_file(text_params.user_prompt_path or self.config.poster_user_prompt)
if not system_prompt or not user_prompt:
self.logger.error("AI文本生成失败无法加载系统或用户提示词。")
return None
generated_content = await self.content_generator.generate_text_for_poster(
system_prompt=system_prompt,
user_prompt=user_prompt,
context_data={"content_data": text_params.content_data},
temperature=self.config.model.temperature,
top_p=self.config.model.top_p
)
if not generated_content:
self.logger.error("AI未能生成有效的文本内容。")
return None
# 将AI生成的内容合并到参数中
generation_params.update(generated_content)
# 4. 执行模板生成
try:
self.logger.info(f"使用模板 '{job.template}' 生成图像...")
poster_image = template_instance.generate(**generation_params)
if poster_image:
# 5. (可选) 应用哈希干扰
if self.config.anti_duplicate_hash:
self.logger.info(f"为海报应用哈希干扰...")
poster_image = ImageProcessor.apply_strategic_hash_disruption(poster_image)
# 6. 保存最终图像
output_filename = f"poster_{topic_index}_{variant_index}_{job.template}.png"
# 使用OutputManager来获取正确的保存路径
variant_dir = self.output_manager.get_variant_dir(topic_index, variant_index)
output_path = variant_dir / output_filename
ImageProcessor.save_image(poster_image, str(output_path))
self.logger.info(f"成功生成并保存海报: {output_path}")
return str(output_path)
else:
self.logger.error(f"模板 '{job.template}' 未能生成图像。")
return None
except Exception as e:
self.logger.critical(f"海报生成过程中发生意外错误: {e}", exc_info=True)
return None

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
海报模板包
"""
from .base_template import BaseTemplate
from .vibrant_template import VibrantTemplate
from .business_template import BusinessTemplate
from .collage_template import CollageTemplate
__all__ = [
"BaseTemplate",
"VibrantTemplate",
"BusinessTemplate",
"CollageTemplate"
]

Some files were not shown because too many files have changed in this diff Show More