diff --git a/outputs/poster_content.json b/outputs/poster_content.json index 505d224..f05de6b 100644 --- a/outputs/poster_content.json +++ b/outputs/poster_content.json @@ -4,13 +4,13 @@ "color": "#000000" }, "layer6_title_content": { - "content": "世界读书日", + "content": "国庆盛典", "font_name": "FZLanTingHei-ExtraBold-GB", "color": "#000000" }, "layer7_subtitle_content": { - "content": "阅古今中外,启智慧之门", - "font_name": "Adobe Song Std L", - "color": "#4F4F4F" + "content": "共庆华诞,同绘未来", + "font_name": "SimHei", + "color": "#555555" } } \ No newline at end of file diff --git a/outputs/prompts.yaml b/outputs/prompts.yaml new file mode 100644 index 0000000..57fed3f --- /dev/null +++ b/outputs/prompts.yaml @@ -0,0 +1,5 @@ +description: "Generated prompt based on user input: \u7AEF\u5348\u8282\u6D77\u62A5\ + \uFF0C\u5305\u542B\u80CC\u666F\u3001\u6D3B\u52A8\u4EAE\u70B9\u548C\u56FE\u6807" +generated_at: 03:05 PM HKT on Monday, June 09, 2025 +user_prompt: "\u7AEF\u5348\u8282\u6D77\u62A5\uFF0C\u5305\u542B\u80CC\u666F\u3001\u6D3B\ + \u52A8\u4EAE\u70B9\u548C\u56FE\u6807" diff --git a/requirements.txt b/requirements.txt index 2cc43e7..af1d048 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,9 @@ -# 基础依赖 -python-dotenv # 用于加载.env文件中的环境变量 -pyyaml # 用于解析YAML配置文件 - -# API客户端 -openai == 1.82.1 # 用于调用OpenAI/Moonshot/DeepSeek API - -# 图像处理 -Pillow # PIL库,用于基础图像处理 -psd-tools # 用于PSD文件的创建和操作 - +fastapi>=0.68.0 +uvicorn>=0.18.0 +python-dotenv>=0.21.0 +pyyaml>=6.0 +websocket-client>=1.5.0 +requests>=2.28.0 +pillow>=9.0.0 +psd-tools>=1.9.0 +openai>=1.0.0 diff --git a/scripts/__pycache__/generate_layout.cpython-311.pyc b/scripts/__pycache__/generate_layout.cpython-311.pyc new file mode 100644 index 0000000..28ceef0 Binary files /dev/null and b/scripts/__pycache__/generate_layout.cpython-311.pyc differ diff --git a/scripts/__pycache__/generate_text.cpython-311.pyc b/scripts/__pycache__/generate_text.cpython-311.pyc new file mode 100644 index 0000000..673a4b3 Binary files /dev/null and b/scripts/__pycache__/generate_text.cpython-311.pyc differ diff --git a/scripts/generate_layout.py b/scripts/generate_layout.py index 8bea5d3..2c1fa2c 100644 --- a/scripts/generate_layout.py +++ b/scripts/generate_layout.py @@ -98,6 +98,8 @@ def call_deepseek( raise raise Exception("达到最大重试次数,API 调用失败") + + def generate_vue_code(prompt=None): prompt = ( "生成一个Vue组件代码,用于端午节活动海报,包含以下部分并指定排版位置:" @@ -123,6 +125,5 @@ def save_code(code, file_path="../outputs/generated_code.vue"): f.write(code) if __name__ == "__main__": - vue_code = generate_vue_code() - save_code(vue_code) + save_code(generate_vue_code()) print("Vue组件代码已生成并保存到outputs/generated_code.vue") \ No newline at end of file diff --git a/scripts/generate_text.py b/scripts/generate_text.py index 2f6be0b..e9b344f 100644 --- a/scripts/generate_text.py +++ b/scripts/generate_text.py @@ -249,7 +249,7 @@ if __name__ == "__main__": # user_input_example2 = "我要一个端午节的海报,中国风,深色背景。" # user_input_example3 = "世界读书日海报,简约风格,元素包含打开的书和思考的人。" - load_config_from_file("./configs/font.yaml") + load_config_from_file("../configs/font.yaml") user_input = input("请输入您的海报需求:") @@ -260,7 +260,7 @@ if __name__ == "__main__": if suggestions: # print("\n--- 最终输出给调用者的内容 ---") # print(json.dumps(suggestions, indent=2, ensure_ascii=False)) - output_folder = "outputs" + output_folder = "../outputs" output_file = os.path.join(output_folder, "poster_content.json") try: with open(output_file, "w", encoding="utf-8") as file: diff --git a/scripts/run_pipeline.py b/scripts/run_pipeline.py new file mode 100644 index 0000000..bd64b77 --- /dev/null +++ b/scripts/run_pipeline.py @@ -0,0 +1,175 @@ +import os +from dotenv import load_dotenv +import yaml +from generate_layout import call_deepseek, generate_vue_code, save_code +from generate_text import load_config_from_file, get_poster_content_suggestions +from fastapi import FastAPI, HTTPException +from fastapi.responses import FileResponse +from ComfyUI.flux_con import comfyui_img_info # 导入已实现的图像生成函数 + +# 配置路径 +config_paths = { + "font": "../configs/font.yaml", + "output_folder": "../outputs/", +} + +# 加载环境变量和配置 +load_dotenv() +app = FastAPI() + +with open(config_paths["font"], "r", encoding="utf-8") as f: + fonts_config = yaml.safe_load(f) + + +# 假设的 DeepSeek 提示词分析接口 +def llm_user_analysis(user_input): + # 占位符实现,模拟 DeepSeek 分析 + if not user_input: + user_input = "端午节海报,包含背景和活动亮点" + return { + "analyzed_prompt": user_input, + "keywords": ["端午节", "背景", "活动亮点"], + "width": 1080, + "height": 1920, + "batch_size": 2 + } + + +# 假设的 PSD 合成接口(占位符) +def create_psd_from_images(img_list, vue_layout_path, output_path): + print(f"Generating PSD from {img_list} using layout {vue_layout_path} to {output_path}") + return None + + +# 动态生成 prompts.yaml 基于用户输入 +def generate_prompts_yaml(user_input): + prompts_data = { + "user_prompt": user_input, + "generated_at": "02:50 PM HKT on Monday, June 09, 2025", + "description": f"Generated prompt based on user input: {user_input}" + } + prompts_yaml_path = os.path.join(config_paths["output_folder"], "prompts.yaml") + with open(prompts_yaml_path, "w", encoding="utf-8") as f: + yaml.dump(prompts_data, f) + return prompts_yaml_path + + +# 生成动态Vue排版Prompt,支持多张图片 +def generate_layout_prompt(user_input_analysis_result, parse_imglist): + width = user_input_analysis_result["width"] + height = user_input_analysis_result["height"] + + # 构造图片信息字符串 + images_info = "\n".join( + [f"- {img['picture_name']} ({img['picture_description']})" for img in parse_imglist] + ) + + # 调用DeepSeek生成动态排版Prompt + system_prompt = "你是一个擅长前端开发的AI,专注于生成Vue.js代码。给定组件尺寸和多张图片信息,自动分析并建议合理的排版位置,使用absolute定位,仅关注位置,不包含样式描述。" + prompt = ( + f"根据组件尺寸 {width}x{height}px 和以下图片信息,生成一个Vue组件代码的Prompt,包含以下部分并指定动态排版位置:\n" + f"图片信息:\n{images_info}\n" + f"要求:\n" + f"1. 背景图层:使用第一张图片,占据整个组件区域。" + f"2. 主体图层:包含标题和副标题,位于合适位置,居中。" + f"3. 活动亮点:使用剩余图片,位于合适位置,居中,使用网格或分层布局展示所有图片。" + f"4. 页脚:包含主办单位信息和logo图片,位于合适位置,居中。" + f"组件尺寸为{width}x{height}px,布局使用absolute定位,仅关注排版位置。" + f"返回一个格式化的Prompt字符串,供生成Vue代码使用。" + ) + + result, _ = call_deepseek(prompt=prompt, system_prompt=system_prompt, temperature=0.4) + return result + + +# 一键执行流程 +def run_pipeline(user_input=None): + """ + 自动执行海报生成流程: + 1. 使用 llm_user_analysis 分析用户提示词得到 user_input_analysis_result。 + 2. 使用 comfyui_img_info 生成 parse_imglist 列表。 + 3. 根据 user_input_analysis_result 生成文案 suggestions。 + 4. 结合 parse_imglist 和动态生成的 prompts.yaml 生成 Vue 排版文件。 + 5. 将 parse_imglist 组装成 img_list,调用 create_psd_from_images 合成 PSD。 + :param user_input: 用户提供的提示词(可选,默认使用占位符) + :return: 合成海报的路径 + """ + # 步骤 1: 加载配置 + load_config_from_file(generate_prompts_yaml(user_input)) # 动态生成 prompts.yaml + + # 步骤 2: 分析用户提示词 + user_input_analysis_result = llm_user_analysis(user_input) + print(f"Analyzed result: {user_input_analysis_result}") + + # 步骤 3: 生成图片信息 + system_prompt = user_input_analysis_result["analyzed_prompt"] + parse_imglist = comfyui_img_info(user_input_analysis_result, system_prompt) + print(f"Generated image list: {parse_imglist}") + + # 步骤 4: 生成文案 + suggestions = get_poster_content_suggestions(user_input_analysis_result) + print(f"Generated suggestions: {suggestions}") + + # 步骤 5: 生成 Vue 排版(动态Prompt,支持多张图片) + dynamic_prompt = generate_layout_prompt(user_input_analysis_result, parse_imglist) + print(f"Generated dynamic layout prompt: {dynamic_prompt}") + vue_code = generate_vue_code(dynamic_prompt) + save_code(vue_code, file_path=os.path.join(config_paths["output_folder"], "generated_code.vue")) + print("Generated Vue layout") + + # 步骤 6: 合成 PSD + img_list = [(pic["picture_name"], pic["picture_type"]) for pic in parse_imglist] + create_psd_from_images( + img_list=img_list, + vue_layout_path=os.path.join(config_paths["output_folder"], "generated_code.vue"), + output_path=os.path.join(config_paths["output_folder"], "final_poster.psd") + ) + print("Generated PSD file") + + return os.path.join(config_paths["output_folder"], "final_poster.png") # 假设 PNG 作为最终输出 + + +# 本地运行函数 +def run_local_pipeline(user_input=None): + """ + 本地运行整个管道流程,输出结果到控制台和文件系统。 + :param user_input: 用户提供的提示词(可选,默认使用占位符) + :return: None,打印运行结果 + """ + print(f"Starting local pipeline with input: {user_input}") + output_path = run_pipeline(user_input) + print(f"Pipeline completed. Results saved to:") + print(f"- Vue layout: {os.path.join(config_paths['output_folder'], 'generated_code.vue')}") + print(f"- PSD file: {os.path.join(config_paths['output_folder'], 'final_poster.psd')}") + print(f"- Final poster (placeholder): {output_path}") + print("Check the outputs/ directory for generated files.") + + +# API 端点 +@app.get("/generate_poster") +async def generate_poster(user_input: str = None): + output_path = run_pipeline(user_input) + return FileResponse(output_path, media_type="image/png") + + +# 可选 API 端点 +@app.get("/generate_poster_psd") +async def generate_poster_psd(user_input: str = None): + try: + output_path = run_pipeline(user_input) + return { + "image_url": output_path, + "font_info": fonts_config, + "psd_path": os.path.join(config_paths["output_folder"], "final_poster.psd") + } + except Exception as e: + raise HTTPException(status_code=500, detail=f"PSD generation failed: {str(e)}") + + +if __name__ == "__main__": + import uvicorn + + # 启动本地运行(可选) + run_local_pipeline("端午节海报,包含背景、活动亮点和图标") + # 或者启动API服务器 + # uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/scripts/run_pipline.md b/scripts/run_pipline.md new file mode 100644 index 0000000..4bb854b --- /dev/null +++ b/scripts/run_pipline.md @@ -0,0 +1,208 @@ +# 自动执行流程说明文档 + +本流程通过多个脚本与配置文件协作,自动生成海报的排版、文案、图像并输出 PSD 文件,最终通过 API 提供服务。 + +--- + +## 一、自动执行流程的步骤 + +### 步骤 1: 配置加载 + +**目标**:加载必要的配置信息(如 API 密钥、字体规则、Prompt 模板)。 + +**涉及文件**: +- `.env`:加载 DeepSeek API 密钥等环境变量。 +- `configs/fonts.yaml`:加载字体与配色规则。 +- `configs/prompts.json`:加载图像生成 Prompt 模板。 + +**过程**: +- 使用 `dotenv` 加载 `.env` 中的 `DEEPSEEK_API_KEY`。 +- 使用 `yaml` 或 `json` 库加载 `fonts.yaml` 和 `prompts.json`。 + +**输出**:配置对象(字典或类),供后续步骤使用。 + +--- + +### 步骤 2: 排版生成 + +**目标**:使用 LLM 生成海报的排版结构(Vue 组件)。 + +**涉及文件**: +- `scripts/generate_layout.py`:调用 DeepSeek API 生成 Vue 组件代码。 + +**过程**: +- 调用 `generate_layout.py` 中的 `generate_vue_code` 函数。 +- 传入从 `prompts.json` 动态读取的 Prompt,结合字体规则生成 Vue 代码。 + +**输出**:Vue 组件代码,保存为 `outputs/generated_code.vue`。 + +--- + +### 步骤 3: 文案生成 + +**目标**:生成海报文案内容(标题、副标题、活动描述等)。 + +**涉及文件**: +- `scripts/generate_text.py`:调用 LLM 生成文案。 + +**过程**: +- 调用脚本生成端午节相关文案(如“端午安康”、“包粽子比赛”)。 +- 文案可按 `prompts.json` 中模板动态调整。 + +**输出**:文案内容(字符串或字典),供后续步骤使用。 + +--- + +### 步骤 4: 图像生成 + +**目标**:使用 ComfyUI 生成海报图层。 + +**涉及文件**: +- `comfyui_flows/layer_generation.json`:定义图层生成流程。 +- `scripts/generate_images.py`:调用 ComfyUI API 生成图层。 +- `images/`:存储背景、前景、文字图层。 + +**过程**: +- 运行 `generate_images.py`,加载 `layer_generation.json`。 +- 根据排版与文案生成: + - `background.png` + - `foreground.png` + - `text_overlay.png` + +**输出**:图像文件保存到 `images/` 目录。 + +--- + +### 步骤 5: 合成海报 + +**目标**:将图层合成为最终海报。 + +**涉及文件**: +- `scripts/compose_poster.py`:合成图层脚本。 +- `outputs/final_poster.png`:合成结果。 + +**过程**: +- 读取 `generated_code.vue` 中的排版位置信息。 +- 使用 `image_utils.py` 对图层进行合成。 + +**输出**:`final_poster.png` + +--- + +### 步骤 6: 导出 PSD 文件 + +**目标**:导出 PSD 文件以便后期修改。 + +**涉及文件**: +- `scripts/export_psd.py`:PSD 导出脚本。 +- `outputs/final_poster.psd`:导出的 PSD 文件。 + +**过程**: +- 将 `final_poster.png` 转换为 PSD。 +- 保留图层信息。 + +**输出**:`final_poster.psd` + +--- + +### 步骤 7: 提供前端接口 + +**目标**:将结果通过 API 提供给前端调用。 + +**涉及文件**: +- `run_pipeline.py`:协调整体流程并定义 API 接口。 + +**过程**: +- 使用 `FastAPI` 或 `Flask` 创建端点(如 `/generate_poster`)。 +- 接收前端请求,依次调用上述所有步骤。 +- 返回最终文件的 URL 或文件流。 + +**输出**:HTTP 响应,包含 `final_poster.png` 链接或文件流。 + +--- + +## 二、如何实现自动执行流程 + +### 环境准备 + +- **依赖安装**:确保 `requirements.txt` 包含以下依赖: + - `openai` + - `python-dotenv` + - `PyYAML` + - `requests`(用于 ComfyUI API 调用) + - `Pillow`(图像处理) + - `psd-tools`(PSD 导出) + +- **API 密钥配置**:在 `.env` 文件中添加以下字段: + ```env + DEEPSEEK_API_KEY=your_api_key + COMFYUI_API_KEY=your_comfyui_key + +```python +import os +from dotenv import load_dotenv +from scripts.generate_layout import generate_vue_code +from scripts.generate_text import generate_text # 假设存在 +from scripts.generate_images import generate_images # 假设存在 +from scripts.compose_poster import compose_poster # 假设存在 +from scripts.export_psd import export_psd # 假设存在 +import yaml +import json +from fastapi import FastAPI, HTTPException +from fastapi.responses import FileResponse + +# 加载环境变量和配置 +load_dotenv() +app = FastAPI() + +with open("configs/fonts.yaml", "r", encoding="utf-8") as f: + fonts_config = yaml.safe_load(f) +with open("configs/prompts.json", "r", encoding="utf-8") as f: + prompts_config = json.load(f) + +# 一键执行流程 +def run_pipeline(): + try: + # 步骤 1: 排版生成 + layout_code = generate_vue_code() + with open("../outputs/generated_code.vue", "w", encoding="utf-8") as f: + f.write(layout_code) + + # 步骤 2: 文案生成 + text_content = generate_text(prompts_config.get("text_prompt", "")) + + # 步骤 3: 图像生成 + generate_images( + workflow_path="comfyui_flows/layer_generation.json", + text_content=text_content, + output_dir="images/" + ) + + # 步骤 4: 合成海报 + compose_poster( + layout_path="../outputs/generated_code.vue", + image_dir="images/", + output_path="../outputs/final_poster.png" + ) + + # 步骤 5: 导出 PSD + export_psd( + input_path="../outputs/final_poster.png", + output_path="../outputs/final_poster.psd" + ) + + return "../outputs/final_poster.png" + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +# API 端点 +@app.get("/generate_poster") +async def generate_poster(): + output_path = run_pipeline() + return FileResponse(output_path, media_type="image/png") + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) + +``` \ No newline at end of file