From bddb36f484765dd9098db5037163330dd0af7e33 Mon Sep 17 00:00:00 2001 From: jinye_huang Date: Tue, 22 Apr 2025 17:25:13 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E5=BA=95=E6=9D=BF?= =?UTF-8?q?=E7=AD=89=E6=B5=B7=E6=8A=A5=E5=90=88=E6=88=90=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E7=9A=84=E8=AF=BB=E5=8F=96=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=BA=95=E6=9D=BF=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E6=96=87=E4=BB=B6=E5=A4=B9=EF=BC=9B=E5=B0=86descripti?= =?UTF-8?q?on=E6=94=B9=E4=B8=BA=E5=92=8Cobject=E4=B8=80=E6=A0=B7=E7=9A=84?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 ++- SelectPrompt/userPrompt.txt | 2 +- core/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 438 bytes core/__pycache__/ai_agent.cpython-310.pyc | Bin 0 -> 7413 bytes core/__pycache__/posterGen.cpython-312.pyc | Bin 31283 -> 31625 bytes core/posterGen.py | 8 +- example_config.json | 28 +++- examples/test_pipeline_steps.py | 128 ++++++++++++++++++ poster_gen_config.json | 29 +++- .../tweet_generator.cpython-312.pyc | Bin 27341 -> 28259 bytes utils/tweet_generator.py | 61 ++++++--- 11 files changed, 239 insertions(+), 35 deletions(-) create mode 100644 core/__pycache__/__init__.cpython-310.pyc create mode 100644 core/__pycache__/ai_agent.cpython-310.pyc create mode 100644 examples/test_pipeline_steps.py diff --git a/README.md b/README.md index 8857131..7bb8043 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/SelectPrompt/userPrompt.txt b/SelectPrompt/userPrompt.txt index e7aa0f5..8bb216d 100644 --- a/SelectPrompt/userPrompt.txt +++ b/SelectPrompt/userPrompt.txt @@ -1,4 +1,4 @@ - 选题日期注意点:不论选题数量多少,只能在我规定的时间进行选题,即你只需要罗列4月5日当天的选题,不要出现4月6日,或者5月1日,这样的日期 + 选题日期注意点:不论选题数量多少,只能在我规定的时间进行选题,即如果我给你的时间范围是4月5日,你只需要罗列4月5日当天的选题,不要出现4月6日,或者5月1日,这样的日期 选定产品:根据产品特性,随机选取我给你的产品信息,进行选题策划,但是不能自己创造不存在的信息和产品 选题风格:根据选题标题,根据我给你的各种文案提示词风格,按用户需求有逻辑的选择不同的风格和面对的用户人群即可,注意只可以在给你的资料中选择,并严谨摘取文件名称,不要自己创作 输出注意点: diff --git a/core/__pycache__/__init__.cpython-310.pyc b/core/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d0194e8b52a4295d2d703b9ad2a86e2b1038038 GIT binary patch literal 438 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 PH-p5GR^fWkw^RKe48?J; literal 0 HcmV?d00001 diff --git a/core/__pycache__/posterGen.cpython-312.pyc b/core/__pycache__/posterGen.cpython-312.pyc index 83c8a4f9a19bb64ff2ff22426a0d67d7e171666f..8ae523a6d7be31742de5e079a56872870af5e1c8 100644 GIT binary patch delta 3247 zcmZ`*X>b(B6`r0wwU<^GLb8NL2qabrfjMIg2oSn#BSax6POBDXw2WBoik{I*TBOK0 znA=C_I7WcWg>zIWY!xsjh>*i4F*b=+F_pvwU6sm$2S1`vC)Twj2O1 zUCxoy;Tx2Lz-Gu9fSGcZoC#c34+)K9!@i;%2bi8Z2je)`X*-N)4rPYP!*h7GPl^6%(b_9TbHB(e7%o~kv~_6xaGg9{sC=5)I_zux z{dj4w{h0|kYyU9l5 zcwx59xej&KvkTI-0$j#)MB`lw9pfejlrCZCil=$`2G(GI1N*W#O!gWpC;mnB#*wjr zHB7!herPyKzhNWoY*DCXVJi+s5F`Z48cKXWc9cpHIuN!aPy`J@XJ%+RNia{jMot;u zEI;FNwj-Zpb7$_Z*azZ$dxg}qknRVu(+N-^{c-ST3+aK;{>aJ03+X}Q=FI(s=uDeE zV>S1v4m7w&rb~$>R2q+&wuqwBM&w7Gtidk#{{K4Jiz`lo2v)$a4?-QdJ_Z=YAu7z-^cuh;?{l>tVeoCS&(S!74{VBPiPt}6N*^lls(z;>dF?qU7Q zeo*uRa3=`GH6zy0XH}6_-fmUV9jv-0N*Fs;lS5u)7i-EhbWo&tF`M>fG~7j-7^$64 zUSbc1)m1vJ0I=8V4v^EVvi?mn(YRWF+vz@!rU?CjU0E@2-NWrShSKOZw1Wjf ze~Exc%{p{NC`kwpoeSA4XSf@cFr?W4BuSz{)O&}?O(n!I_BKUr{iFWz#O^@_^lrq1 z5@yA;>G2NwJa&JAfI&8$R!u~Hbs5{$D*9s_It1__LvK|<8DRJ*4FSKBt#R5O z@>>2~6@*-YVvSKBf>l~5-C+Bhcapaa-|D|R`mJCA9ohKeyMLRq@yjZD0hM+maEJel z(hh{5B489L-xzmzKXl<*;(m-q@IzF(ghT0;Hk;kr$(BZ&jmF%U7&o9<9cZXgX*(7EvfJ z7z&PYgL}Y_&S-uNocI>P=K!W>GrR;XTjEi83LLW1p>L&s0B6sH7nAqc-EajdF{I5& zk^Gz;m1jV`UzUr>2ke$Sy_D}|)GlsWa{)ksV6%$keO9eZB~39?Xq3WK7&wWQ_TZa;%t8nyb_ITSovX5zP%llU%(T(s10>3DAl&t#( zr7sY$RA?>2w-6)*EN#;TZ(R)D1-b&MB?$Eh3IczH@X9sqS~#ItXQK&6DgZ)0x;Py> zod_QRnBI7&-qESIaEtt&^Y91Ny5){?l~y$tj<%@nVaU=o5>3XrI*M!>H(dQ$WLA^e0k-?Sg)SnzZ@5BKzI`2DFl9t z@Eq{i@4wEg;)de`5Ot*};5?CDqP6m;irpUA;Ehjk28ffbqy)*>}wLkoSL;kBD;M zn61w)r?Zz5MP73eKmt#?b!2UH5aqK7=h)7!O{JLakjtvXSCHnJE$rN_6r@Fi=jXe5S i0r3va$|1|i?MlJ!+nfB2E$=hC`?=cNHo>0HQ}JIjrzmUy delta 2934 zcmZ{meNa@_6~Nzp`vL41p9?4-y95KSKrk4kk|3Z6Xd(uML>-vz%6kh7%Yt_oWMLh2 zLNpqU)iV{7n3+r_ooOr4n5VTLag3(1G1ZwS?LXb78Ev(OnbxM!)T!-Ed(M4)m`rnL z_V>;`=e&E*z31HfcJDo=-DQqn+wB$!+nHqv-FHS$Iri8d&rq!6Y>Q2?jZ0hXiXG`t zoQeZ=$x5b@jL#G$1+}S4Dl$#UP|{GB9%Y^^_>IlwGU9Ls{N2e%PDn8ple{#AXb~Bb zH|N5Ij0^}a%*IL*-u&23~jA!J3?8j7dGX+~hoqZT4l@X>@}7>BJNqz%i?pdR*jZPkBI7 zNE&C_j3z?cC0b7rTrYP*l`*|sNtjHWSrU_!Bpftj#!azc&pFeJN*-%73QXf=NU#X+jlQuoah=c@VMx&GAg zqt$$$_V{D_7~^nh>EbpK(Q5P%5ycbK&`^N)hvJ4VHNu}F{alc%s4JrX)*wg7WfY!V zRx&SwV%*jjP?VtR4f>+Ej}!?`e{fkbV^_4Zl}BweOK7X_$O5!_jAHMm?xQ?aN$!1c zxqgpWo>&;)M@ky#7XGkZtKqxx(X~XhKiBXe#8xjV^`VY$MNBc0=pyJE%{b9`ePq0b zKZm_rue?m{M>YI;_+<4D*nSvp$YW7B(h$r#g!Vx?du8q$=MLODccRKYT+I)_yv9YT z9MzmIiny_!`?m9DXl<-w00$eFx(}1>h_HnNBkDJk?1vkTtI{UO=_tapkSCKIf#ppF z=57)v;k!+Xt&~fBu^PPE^e{UE7n&Y1>kEgknqFilVb_|sSiV-i_6u|3SsIG*_o29@ za^>u@n>|T9K;uv$@DB*31X0BqSr&hPDB>OPMY>OF ze`pCB#^yp3xqP0+punaqBvB&bhDiSaf04RBCG01iSucaGt5>LfTEkD1>3fLjq@1q7 z%+tQZ@r1!pz~9gNJXSrlxFluaZjlq^X1bb4x(R=5+r{41ywCj8G&WlxUF`=i zezmau?=}2qXA~} zioI&~$Z-?*g}PKu(|J-J2tv$HZ%)rc(ka3_gzJd7WovLy^>+3L`+3}?r~{F1{(B77 z)LF|eKtpFKb7|qu5t+RUx7Ee$0J!`G?0u;7m#2u8%w@eBcKV&L91rWH-^rfRF8U7} ztMuRl;|56zyp5L_*Y72oop!mEKY_#oYsbfZ(<40tY8P)IYdgHuTgmporQUU8>q#+0 z_%Q*8!7GoTUnwO0i9n^n>j}FEZUR+tJOMvUAzz=$*O97*&_L)UY$VXF8aIZ0L#loZ znn_uT@Yr~*IErD?d`NEA{=vw=V8km%6jzx=eEsx`>}BEN4}^R{Z=lbIS-A|K1Ou#9 zd!p}*(Mk*DCWdH%|7_dD&O#gCy<`Jv=b{lOv4V8JB+wc})bYgdKrj$dha=npj&Kh9 z5Eg~=JgsPtTg7BvED$wJ8N|%aX}E|!2?oNgY@hZ)_%f3{GL#H29BbxuJUy~FaSiws zH5VjZ22aNa&mho8pQjS&d(9gOYY1xz;x-Wp5*shFBr-ym1^l#e+u=lE@ zm}3Sdfn+6dT9c$$6zeg=tlsu@eV44*j~R9t6~_)!41CF%Wu}-Bos*~CQeKmfW4%&i z(*8Q5gnx8Wo8zViUofolmw*kIuv#b^&W66>s*1m$n>dG9#p1Z#Bn2IJ{p1?>B*_}# z3}R#hzFGwT9j>rbEqD^Rn@rz_713u*#o_!`Tqku!^+VB diff --git a/core/posterGen.py b/core/posterGen.py index dde2be7..ab26f5b 100644 --- a/core/posterGen.py +++ b/core/posterGen.py @@ -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) diff --git a/example_config.json b/example_config.json index 7c4436c..98a3b23 100644 --- a/example_config.json +++ b/example_config.json @@ -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 } \ No newline at end of file diff --git a/examples/test_pipeline_steps.py b/examples/test_pipeline_steps.py new file mode 100644 index 0000000..2c95c45 --- /dev/null +++ b/examples/test_pipeline_steps.py @@ -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() \ No newline at end of file diff --git a/poster_gen_config.json b/poster_gen_config.json index 8d44d25..862f45c 100644 --- a/poster_gen_config.json +++ b/poster_gen_config.json @@ -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 diff --git a/utils/__pycache__/tweet_generator.cpython-312.pyc b/utils/__pycache__/tweet_generator.cpython-312.pyc index e68f878f2afbeb198a158b0673a01a4396bda278..fdd70effcd5e3b569e7d19e8afb275e376d6b5c1 100644 GIT binary patch delta 3373 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 bCp187ersu+^M0k(zj^d22edxXPc!}x${AlU diff --git a/utils/tweet_generator.py b/utils/tweet_generator.py index 0d0c3b5..5c0e722 100644 --- a/utils/tweet_generator.py +++ b/utils/tweet_generator.py @@ -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 - 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") - + # 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) 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 = [] - if os.path.exists(description_file_path): - info_directory = [description_file_path] - print(f" Using description file: {description_file_path}") - else: - print(f" Description file not found: '{description_file_path}'. Using generated content for poster text.") + 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] # Pass the found path + print(f" Found and using description file from config: {description_file_path}") + found_description = True + else: + 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: