搭建 pipeline framework

This commit is contained in:
Wang Xiuqiang 2025-06-09 16:08:14 +08:00
parent 5eec48a889
commit 90fe8dbd11
9 changed files with 406 additions and 19 deletions

View File

@ -4,13 +4,13 @@
"color": "#000000" "color": "#000000"
}, },
"layer6_title_content": { "layer6_title_content": {
"content": "世界读书日", "content": "国庆盛典",
"font_name": "FZLanTingHei-ExtraBold-GB", "font_name": "FZLanTingHei-ExtraBold-GB",
"color": "#000000" "color": "#000000"
}, },
"layer7_subtitle_content": { "layer7_subtitle_content": {
"content": "阅古今中外,启智慧之门", "content": "共庆华诞,同绘未来",
"font_name": "Adobe Song Std L", "font_name": "SimHei",
"color": "#4F4F4F" "color": "#555555"
} }
} }

5
outputs/prompts.yaml Normal file
View File

@ -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"

View File

@ -1,11 +1,9 @@
# 基础依赖 fastapi>=0.68.0
python-dotenv # 用于加载.env文件中的环境变量 uvicorn>=0.18.0
pyyaml # 用于解析YAML配置文件 python-dotenv>=0.21.0
pyyaml>=6.0
# API客户端 websocket-client>=1.5.0
openai == 1.82.1 # 用于调用OpenAI/Moonshot/DeepSeek API requests>=2.28.0
pillow>=9.0.0
# 图像处理 psd-tools>=1.9.0
Pillow # PIL库用于基础图像处理 openai>=1.0.0
psd-tools # 用于PSD文件的创建和操作

Binary file not shown.

Binary file not shown.

View File

@ -98,6 +98,8 @@ def call_deepseek(
raise raise
raise Exception("达到最大重试次数API 调用失败") raise Exception("达到最大重试次数API 调用失败")
def generate_vue_code(prompt=None): def generate_vue_code(prompt=None):
prompt = ( prompt = (
"生成一个Vue组件代码用于端午节活动海报包含以下部分并指定排版位置" "生成一个Vue组件代码用于端午节活动海报包含以下部分并指定排版位置"
@ -123,6 +125,5 @@ def save_code(code, file_path="../outputs/generated_code.vue"):
f.write(code) f.write(code)
if __name__ == "__main__": if __name__ == "__main__":
vue_code = generate_vue_code() save_code(generate_vue_code())
save_code(vue_code)
print("Vue组件代码已生成并保存到outputs/generated_code.vue") print("Vue组件代码已生成并保存到outputs/generated_code.vue")

View File

@ -249,7 +249,7 @@ if __name__ == "__main__":
# user_input_example2 = "我要一个端午节的海报,中国风,深色背景。" # user_input_example2 = "我要一个端午节的海报,中国风,深色背景。"
# user_input_example3 = "世界读书日海报,简约风格,元素包含打开的书和思考的人。" # user_input_example3 = "世界读书日海报,简约风格,元素包含打开的书和思考的人。"
load_config_from_file("./configs/font.yaml") load_config_from_file("../configs/font.yaml")
user_input = input("请输入您的海报需求:") user_input = input("请输入您的海报需求:")
@ -260,7 +260,7 @@ if __name__ == "__main__":
if suggestions: if suggestions:
# print("\n--- 最终输出给调用者的内容 ---") # print("\n--- 最终输出给调用者的内容 ---")
# print(json.dumps(suggestions, indent=2, ensure_ascii=False)) # 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") output_file = os.path.join(output_folder, "poster_content.json")
try: try:
with open(output_file, "w", encoding="utf-8") as file: with open(output_file, "w", encoding="utf-8") as file:

175
scripts/run_pipeline.py Normal file
View File

@ -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)

208
scripts/run_pipline.md Normal file
View File

@ -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)
```