修改了底板等海报合成相关信息的读取方式,增加了底板内容文件夹;将description改为和object一样的读取方式

This commit is contained in:
jinye_huang 2025-04-22 17:25:13 +08:00
parent 9457a665c8
commit bddb36f484
11 changed files with 239 additions and 35 deletions

View File

@ -108,10 +108,17 @@ pip install numpy pandas opencv-python pillow openai
- `topic_system_prompt`: 选题生成系统提示词文件路径 (应为要求JSON输出的版本)
- `topic_user_prompt`: 选题生成基础用户提示词文件路径
- `content_system_prompt`: 内容生成系统提示词文件路径
- `resource_dir`: 包含景点等资源文件信息的列表 (结构见 `example_config.json`)
- `resource_dir`: 包含**资源文件信息**的列表。列表中的每个元素是一个字典,包含:
- `type`: 资源类型,目前支持 `"Object"` (景点/对象信息), `"Description"` (对应的描述文件), `"Product"` (关联产品信息)。
- `file_path`: 一个包含该类型所有资源文件**完整路径**的列表。
- 对于 `"Object"` 类型,程序会根据选题中的对象名称在此列表中查找匹配的文件。
- 对于 `"Description"` 类型,程序会根据选题中的对象名称在此列表中查找对应的描述文件 (文件名应包含对象名以便匹配)。
- 对于 `"Product"` 类型,程序会根据选题中的产品名称在此列表中查找匹配的文件。
- `num`: (可选,目前似乎未使用) 文件数量。
- `prompts_dir`: 存放 Demand/Style/Refer 等提示词片段的目录路径
- `output_dir`: 输出结果保存目录路径
- `image_base_dir`: **图片资源根目录绝对路径或相对路径**
- `image_base_dir`: **图片资源根目录绝对路径或相对路径** (用于查找源图片)
- `poster_assets_base_dir`: **海报素材根目录绝对路径或相对路径** (用于查找字体、边框、贴纸、文本背景等)
- `num`: (选题阶段)生成选题数量
- `variants`: (内容生成阶段)每个选题生成的变体数量
@ -122,8 +129,11 @@ pip install numpy pandas opencv-python pillow openai
- `content_temperature`, `content_top_p`, `content_presence_penalty`: 内容生成 API 相关参数 (默认为 0.3, 0.4, 1.5)
- `request_timeout`: AI API 请求的超时时间(秒,默认 30
- `max_retries`: 请求超时或可重试网络错误时的最大重试次数(默认 3
- `camera_image_subdir`: 存放原始照片和描述文件的子目录名(相对于 `image_base_dir`,默认"相机"
- `modify_image_subdir`: 存放处理后/用于拼贴的图片的子目录名(相对于 `image_base_dir`,默认"modify"
- `camera_image_subdir`: 存放原始照片的子目录名(相对于 `image_base_dir`,默认 "相机" - **注意:此项不再用于查找描述文件。**
- `modify_image_subdir`: 存放处理后/用于拼贴的图片的子目录名(相对于 `image_base_dir`,默认 "modify"
- `output_collage_subdir`: 在每个变体输出目录中存放拼贴图的子目录名(默认 "collage_img"
- `output_poster_subdir`: 在每个变体输出目录中存放最终海报的子目录名(默认 "poster"
- `output_poster_filename`: 输出的最终海报文件名(默认 "poster.jpg"
- `poster_target_size`: 海报目标尺寸 `[宽, 高]`(默认 `[900, 1200]`
- `text_possibility`: 海报中第二段附加文字出现的概率 (默认 0.3)

View File

@ -1,4 +1,4 @@
选题日期注意点不论选题数量多少只能在我规定的时间进行选题即你只需要罗列4月5日当天的选题不要出现4月6日或者5月1日这样的日期
选题日期注意点:不论选题数量多少,只能在我规定的时间进行选题,即如果我给你的时间范围是4月5日你只需要罗列4月5日当天的选题不要出现4月6日或者5月1日这样的日期
选定产品:根据产品特性,随机选取我给你的产品信息,进行选题策划,但是不能自己创造不存在的信息和产品
选题风格:根据选题标题,根据我给你的各种文案提示词风格,按用户需求有逻辑的选择不同的风格和面对的用户人群即可,注意只可以在给你的资料中选择,并严谨摘取文件名称,不要自己创作
输出注意点:

Binary file not shown.

Binary file not shown.

View File

@ -55,9 +55,13 @@ class PosterConfig:
return self.config[index]
class PosterGenerator:
def __init__(self,base_dir="/root/autodl-tmp/poster_baseboard_0403", output_dir=None):
def __init__(self, base_dir, output_dir=None):
# 基础路径设置
if not base_dir or not os.path.isdir(base_dir):
# Handle error: Base directory is essential
raise ValueError(f"Poster assets base directory '{base_dir}' is invalid or not provided.")
self.base_dir = base_dir
print(f"Initializing PosterGenerator with asset base: {self.base_dir}") # Log the used base_dir
self.font_dir = os.path.join(self.base_dir, "font")
self.frame_dir = os.path.join(self.base_dir, "frames") # 边框素材目录
self.sticker_dir = os.path.join(self.base_dir, "stickers") # 贴纸素材目录
@ -689,7 +693,7 @@ def main():
print(f"设置环境变量USE_TEXT_BG为: {os.environ['USE_TEXT_BG']}")
# 创建生成器实例
generator = PosterGenerator()
generator = PosterGenerator("/root/autodl-tmp/poster_baseboard_0403")
poster_config_path = "/root/autodl-tmp/poster_generate_result/2025-04-16_20-49-32.json"
poster_config = PosterConfig(poster_config_path)

View File

@ -1,6 +1,6 @@
{
"date": "5月15日",
"num": 5,
"num": 3,
"model": "qwenQWQ",
"api_url": "http://localhost:8000/v1/",
"api_key": "EMPTY",
@ -10,11 +10,21 @@
"resource_dir": [
{
"type": "Object",
"num": 3,
"num": 4,
"file_path": [
"./resource/Object/景点信息-泰宁古城.txt",
"./resource/Object/景点信息-尚书第.txt",
"./resource/Object/景点信息-明清园.txt"
"./resource/Object/景点信息-明清园.txt",
"./resource/Object/景点信息-泰宁古城.txt",
"./resource/Object/景点信息-甘露寺.txt"
]
},
{
"type": "Description",
"file_path": [
"./resource/Object/description-尚书第.txt",
"./resource/Object/description-明清园.txt",
"./resource/Object/description-泰宁古城.txt",
"./resource/Object/description-甘露寺.txt"
]
},
{
@ -25,18 +35,22 @@
],
"prompts_dir": "./genPrompts",
"output_dir": "./result",
"image_base_dir": "/path/to/your/image/directory",
"image_base_dir": "/root/autodl-tmp/sanming_img",
"poster_assets_base_dir": "/root/autodl-tmp/poster_baseboard_0403",
"camera_image_subdir": "相机",
"modify_image_subdir": "modify",
"variants": 3,
"variants": 2,
"topic_temperature": 0.2,
"topic_top_p": 0.3,
"topic_top_p": 0.5,
"topic_presence_penalty": 1.5,
"content_temperature": 0.3,
"content_top_p": 0.4,
"content_presence_penalty": 1.5,
"request_timeout": 30,
"max_retries": 3,
"output_collage_subdir": "collage_img",
"output_poster_subdir": "poster",
"output_poster_filename": "poster.jpg",
"poster_target_size": [900, 1200],
"text_possibility": 0.3
}

View File

@ -0,0 +1,128 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import json
import traceback
# Add project root to the Python path
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, project_root)
from core.ai_agent import AI_Agent
from utils.prompt_manager import PromptManager
from utils.tweet_generator import (
run_topic_generation_pipeline,
generate_content_for_topic,
generate_posters_for_topic
)
def load_config(config_path="/root/autodl-tmp/TravelContentCreator/poster_gen_config.json"):
"""Loads configuration relative to the script."""
if not os.path.exists(config_path):
print(f"Error: Config file '{config_path}' not found.")
sys.exit(1)
try:
with open(config_path, 'r', encoding='utf-8') as f:
config = json.load(f)
print("Configuration loaded successfully.")
return config
except Exception as e:
print(f"Error loading configuration: {e}")
sys.exit(1)
def main_test():
print("--- Starting Pipeline Step Test ---")
config = load_config()
# --- Override config for faster testing ---
config['num'] = 1 # Generate only 1 topic
config['variants'] = 1 # Generate only 1 content/poster variant
print(f"Config overridden for testing: num={config['num']}, variants={config['variants']}")
run_id = None
tweet_topic_record = None
ai_agent_content = None # Separate agent instance for content/poster
try:
# --- Step 1: Test Topic Generation ---
print("\n--- Testing Topic Generation ---")
run_id, tweet_topic_record = run_topic_generation_pipeline(config) # run_id generated inside if not passed
if not run_id or not tweet_topic_record or not tweet_topic_record.topics_list:
print("Topic generation failed or produced no topics. Exiting test.")
return
print(f"Topic generation successful. Run ID: {run_id}")
print(f"Generated {len(tweet_topic_record.topics_list)} topic(s).")
test_topic = tweet_topic_record.topics_list[0] # Get the first topic for testing
print("Test Topic Data:", json.dumps(test_topic, ensure_ascii=False, indent=2))
# --- Step 2: Test Content Generation (for the first topic) ---
print("\n--- Testing Content Generation ---")
# Initialize resources needed for content generation
prompt_manager = PromptManager(config)
print("Initializing AI Agent for content...")
request_timeout = config.get("request_timeout", 30)
max_retries = config.get("max_retries", 3)
ai_agent_content = AI_Agent(
config["api_url"],
config["model"],
config["api_key"],
timeout=request_timeout,
max_retries=max_retries
)
base_output_dir = config["output_dir"]
topic_index = 1 # Testing the first topic (1-based index)
tweet_content_list = generate_content_for_topic(
ai_agent_content, prompt_manager, config, test_topic,
base_output_dir, run_id, topic_index
)
if not tweet_content_list:
print("Content generation failed or produced no content. Exiting test.")
return
print(f"Content generation successful. Generated {len(tweet_content_list)} variant(s).")
print("Generated Content Data (first variant):", json.dumps(tweet_content_list[0], ensure_ascii=False, indent=2))
# --- Step 3: Test Poster Generation (for the first topic/content) ---
print("\n--- Testing Poster Generation ---")
# Poster generation uses its own internal ContentGenerator and PosterGenerator instances
# We just need to call the function
success = generate_posters_for_topic(
config,
test_topic,
tweet_content_list, # Pass the list generated above
base_output_dir,
run_id,
topic_index
)
if success:
print("Poster generation function executed (check output directory for results).")
else:
print("Poster generation function reported failure or skipped execution.")
except Exception as e:
print(f"\n--- An error occurred during testing ---")
print(f"Error: {e}")
traceback.print_exc()
finally:
# Clean up the content generation AI agent if it was created
if ai_agent_content:
print("\nClosing content generation AI Agent...")
ai_agent_content.close()
print("\n--- Test Finished ---")
if __name__ == "__main__":
main_test()

View File

@ -1,6 +1,6 @@
{
"date": "5月15日",
"num": 10,
"num": 2,
"model": "qwenQWQ",
"api_url": "http://localhost:8000/v1/",
"api_key": "EMPTY",
@ -10,11 +10,21 @@
"resource_dir": [
{
"type": "Object",
"num": 3,
"num": 4,
"file_path": [
"./resource/Object/景点信息-泰宁古城.txt",
"./resource/Object/景点信息-尚书第.txt",
"./resource/Object/景点信息-明清园.txt"
"./resource/Object/景点信息-明清园.txt",
"./resource/Object/景点信息-甘露寺.txt"
]
},
{
"type": "Description",
"file_path": [
"./resource/Object/景点信息-泰宁古城.txt",
"./resource/Object/景点信息-尚书第.txt",
"./resource/Object/景点信息-明清园.txt",
"./resource/Object/景点信息-甘露寺.txt"
]
},
{
@ -26,11 +36,22 @@
"prompts_dir": "./genPrompts",
"output_dir": "./result",
"image_base_dir": "/root/autodl-tmp/sanming_img",
"poster_assets_base_dir": "/root/autodl-tmp/poster_baseboard_0403",
"camera_image_subdir": "相机",
"modify_image_subdir": "modify",
"variants": 5,
"variants": 2,
"topic_temperature": 0.2,
"topic_top_p": 0.3,
"topic_presence_penalty": 1.5,
"content_temperature": 0.3,
"content_top_p": 0.4,
"content_presence_penalty": 1.5,
"request_timeout": 30,
"max_retries": 3,
"description_filename": "description.txt",
"output_collage_subdir": "collage_img",
"output_poster_subdir": "poster",
"output_poster_filename": "poster.jpg",
"poster_target_size": [
900,
1200

View File

@ -446,7 +446,14 @@ def generate_posters_for_topic(config, topic_item, tweet_content_list, output_di
# Alternatively, pass initialized instances if they hold state or are expensive
try:
content_gen_instance = core_contentGen.ContentGenerator()
poster_gen_instance = core_posterGen.PosterGenerator()
# poster_gen_instance = core_posterGen.PosterGenerator()
# --- Read poster assets base dir from config ---
poster_assets_base_dir = config.get("poster_assets_base_dir")
if not poster_assets_base_dir:
print("Error: 'poster_assets_base_dir' not found in configuration. Cannot generate posters.")
return False # Cannot proceed without assets base dir
# --- Initialize PosterGenerator with the base dir ---
poster_gen_instance = core_posterGen.PosterGenerator(base_dir=poster_assets_base_dir)
except Exception as e:
print(f" Error initializing generators for poster creation: {e}")
return False
@ -470,26 +477,42 @@ def generate_posters_for_topic(config, topic_item, tweet_content_list, output_di
if not object_name_cleaned:
print(f" Warning: Object name '{object_name}' resulted in empty string after cleaning. Skipping posters.")
return False
object_name = object_name_cleaned
object_name = object_name_cleaned # Use the cleaned name for searching
except Exception as e:
print(f" Warning: Could not fully clean object name '{object_name}': {e}. Skipping posters.")
return False
# Construct and check image paths
# Construct and check INPUT image paths (still needed for collage)
input_img_dir_path = os.path.join(image_base_dir, modify_image_subdir, object_name)
camera_img_dir_path = os.path.join(image_base_dir, camera_image_subdir, object_name)
description_file_path = os.path.join(camera_img_dir_path, "description.txt")
if not os.path.exists(input_img_dir_path) or not os.path.isdir(input_img_dir_path):
print(f" Image directory not found or not a directory: '{input_img_dir_path}'. Skipping posters for this topic.")
print(f" Modify Image directory not found or not a directory: '{input_img_dir_path}'. Skipping posters for this topic.")
return False
# --- NEW: Locate Description File using resource_dir type "Description" ---
info_directory = []
description_file_path = None
resource_dir_config = config.get("resource_dir", [])
found_description = False
for dir_info in resource_dir_config:
if dir_info.get("type") == "Description":
for file_path in dir_info.get("file_path", []):
# Match description file based on object name containment
if object_name in os.path.basename(file_path):
description_file_path = file_path
if os.path.exists(description_file_path):
info_directory = [description_file_path]
print(f" Using description file: {description_file_path}")
info_directory = [description_file_path] # Pass the found path
print(f" Found and using description file from config: {description_file_path}")
found_description = True
else:
print(f" Description file not found: '{description_file_path}'. Using generated content for poster text.")
print(f" Warning: Description file specified in config not found: {description_file_path}")
break # Found the matching entry in this list
if found_description: # Stop searching resource_dir if found
break
if not found_description:
print(f" No matching description file found for object '{object_name}' in config resource_dir (type='Description').")
# --- End NEW Description File Logic ---
# --- Generate Text Configurations for All Variants ---
try:
@ -522,8 +545,10 @@ def generate_posters_for_topic(config, topic_item, tweet_content_list, output_di
# Define output directories for this specific variant
run_output_dir = os.path.join(output_dir, run_id) # Base dir for the run
variant_output_dir = os.path.join(run_output_dir, f"{topic_index}_{variant_index}")
collage_output_dir = os.path.join(variant_output_dir, "collage_img")
poster_output_dir = os.path.join(variant_output_dir, "poster")
output_collage_subdir = config.get("output_collage_subdir", "collage_img")
output_poster_subdir = config.get("output_poster_subdir", "poster")
collage_output_dir = os.path.join(variant_output_dir, output_collage_subdir)
poster_output_dir = os.path.join(variant_output_dir, output_poster_subdir)
os.makedirs(collage_output_dir, exist_ok=True)
os.makedirs(poster_output_dir, exist_ok=True)
@ -555,8 +580,10 @@ def generate_posters_for_topic(config, topic_item, tweet_content_list, output_di
if len(texts) > 1 and random.random() < text_possibility: # Use variable from config
text_data["additional_texts"].append({"text": texts[1], "position": "bottom", "size_factor": 0.5})
final_poster_path = os.path.join(poster_output_dir, "poster.jpg")
result_path = poster_gen_instance.create_poster(collage_img_path, text_data, final_poster_path)
# final_poster_path = os.path.join(poster_output_dir, "poster.jpg") # Filename "poster.jpg" is hardcoded
output_poster_filename = config.get("output_poster_filename", "poster.jpg")
final_poster_path = os.path.join(poster_output_dir, output_poster_filename)
result_path = poster_gen_instance.create_poster(collage_img_path, text_data, final_poster_path) # Uses hardcoded output filename
if result_path:
print(f" Successfully generated poster: {result_path}")
else: