Compare commits
2 Commits
65857475b1
...
764540c432
| Author | SHA1 | Date | |
|---|---|---|---|
| 764540c432 | |||
| 3ddc04caff |
1
TWEET/.gitignore → .gitignore
vendored
1
TWEET/.gitignore → .gitignore
vendored
@ -1,6 +1,5 @@
|
|||||||
/result/
|
/result/
|
||||||
*.zip
|
*.zip
|
||||||
/hotel_img/
|
|
||||||
/output/
|
/output/
|
||||||
/log/
|
/log/
|
||||||
/__pycache__/
|
/__pycache__/
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tests/output/business_poster_test.png
Normal file
BIN
tests/output/business_poster_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
BIN
tests/output/vibrant_poster_test.png
Normal file
BIN
tests/output/vibrant_poster_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 MiB |
98
tests/test_poster_generation.py
Normal file
98
tests/test_poster_generation.py
Normal 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()
|
||||||
Binary file not shown.
24
utils/poster/__init__.py
Normal file
24
utils/poster/__init__.py
Normal 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"
|
||||||
|
]
|
||||||
BIN
utils/poster/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
utils/poster/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
utils/poster/__pycache__/job.cpython-312.pyc
Normal file
BIN
utils/poster/__pycache__/job.cpython-312.pyc
Normal file
Binary file not shown.
BIN
utils/poster/__pycache__/poster_generator.cpython-312.pyc
Normal file
BIN
utils/poster/__pycache__/poster_generator.cpython-312.pyc
Normal file
Binary file not shown.
BIN
utils/poster/__pycache__/text_generator.cpython-312.pyc
Normal file
BIN
utils/poster/__pycache__/text_generator.cpython-312.pyc
Normal file
Binary file not shown.
BIN
utils/poster/__pycache__/utils.cpython-312.pyc
Normal file
BIN
utils/poster/__pycache__/utils.cpython-312.pyc
Normal file
Binary file not shown.
16
utils/poster/job.py
Normal file
16
utils/poster/job.py
Normal 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)
|
||||||
132
utils/poster/poster_generator.py
Normal file
132
utils/poster/poster_generator.py
Normal 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
|
||||||
18
utils/poster/templates/__init__.py
Normal file
18
utils/poster/templates/__init__.py
Normal 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"
|
||||||
|
]
|
||||||
BIN
utils/poster/templates/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
utils/poster/templates/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user