
Image Service
- 82 installs
- 263 repo stars
- Updated July 22, 2026
- zrt-ai-lab/opencode-skills
Helps with ai & agent building tasks during AI-assisted development.
About
image-service is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- image-service
- AI & Agent Building
- AI-coding skill
Image Service by the numbers
- 82 all-time installs (skills.sh)
- Ranked #5,101 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/zrt-ai-lab/opencode-skills --skill image-serviceAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 82 |
|---|---|
| repo stars | ★ 263 |
| Last updated | July 22, 2026 |
| Repository | zrt-ai-lab/opencode-skills ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
zlab 图像处理技能
配置
| 配置项 | 值 |
|---|---|
| API | 配置文件中设置 |
| 生图模型 | 配置文件中设置 |
| 视觉模型 | 配置文件中设置 |
| 配置文件 | config/settings.json |
执行规范
- 脚本使用绝对路径调用,图片保存到当前工作目录
- 提示词必须使用中文,图中标题标签必须中文
- 含中文文字时,追加
guides/text-rendering.md中的文字清晰后缀 - 默认宽高比 16:9,竖版场景(小红书/漫画)默认 3:4
🔀 场景路由(铁律:收到需求后第一步)
收到生图需求
│
├─ 信息图/数据可视化/架构图/流程图/对比图/鱼骨图/思维导图/金字塔/漏斗/韦恩图/冰山图
│ → 📊 信息图 → scenes/infographic/
│
├─ 封面/头图/banner/公众号封面/文章封面
│ → 🖼️ 封面图 → scenes/cover/
│
├─ 长图/微信长图/多屏/从上到下/竖版长图/分段/多个段落
│ → 📜 长图 → scenes/long-image/
│
├─ 幻灯片/PPT图/slides/演示文稿
│ → 📑 幻灯片 → scenes/slide-deck/
│
├─ 漫画/连环画/分镜/绘本/知识漫画/故事图
│ → 📖 漫画 → scenes/comic/
│
├─ 文章插图/自动配图/智能插图/给文章配图
│ → ✏️ 文章插图 → scenes/article-illust/
│
├─ 小红书/笔记配图/种草图/卡片系列
│ → 📱 小红书图 → scenes/xhs/
│
├─ 海报/主图/电商/促销/九宫格/产品图/营销/物料
│ → 🛍️ 营销物料 → scenes/marketing/
│
├─ 改图/编辑图片/风格转换/基于这张图
│ → 🔄 图生图 → core/image_to_image.py(直接执行)
│
├─ 分析图片/OCR/识别文字/描述图片/图生文
│ → 👁️ 图生文 → core/image_to_text.py(直接执行)
│
└─ 其他单图需求(一张图,无特殊场景)
→ 🎨 单图 → core/text_to_image.py(直接执行)路由到场景后 → 加载该场景的 README.md 获取详细参数和用法 → 按下方执行流程操作。
---
📋 通用执行流程(所有场景共用)
第一步:分析需求
- 明确要生什么(主题、内容要点)
- 确定场景类型(已由路由判断)
- 确定数量(几张图)
- 确定比例(16:9 / 3:4 / 1:1 等)
第二步:规划方案(多图场景必须)
单图:可跳过直接生成。 多图场景(长图/幻灯片/漫画/小红书/文章插图):必须先输出规划,等用户确认后再执行。
规划输出格式:
| 序号 | 内容概要 | 类型/布局 |
|-----|---------|----------|
| 1 | xxx | xxx |
| 2 | xxx | xxx |
**场景**:xx | **风格**:xx | **比例**:xx | **预计**:N张第三步:执行生成
| 场景 | 执行方式 | 规则 |
|---|---|---|
| 📊 信息图 | 场景脚本直接调用 | 单张可并发 |
| 🖼️ 封面图 | 场景脚本直接调用 | 单张 |
| 📜 长图 | core引擎串行 | 铁律:必须串行! 首屏text_to_image,后续image_to_image参考上一屏,最后merge拼接 |
| 📑 幻灯片 | 场景脚本逐页 | 串行逐页生成 |
| 📖 漫画 | 场景脚本逐页 | 串行! 首页text_to_image定调,后续image_to_image参考上一页 |
| ✏️ 文章插图 | 场景脚本批量 | 各章节独立,可并发(≤8张) |
| 📱 小红书 | 场景脚本逐张 | 串行! 首张定调,后续参考上一张保风格一致 |
| 🛍️ 营销物料 | core引擎+模板 | 同风格可并发(≤8张) |
| 🔄 图生图 | core直接调用 | 单张 |
| 🎨 单图 | core直接调用 | 单张 |
第四步:质量校验
1. 生成完成后,含中文文字的图用 core/image_to_text.py -m ocr 校验文字是否清晰 2. OCR结果与预期不符 → 用 core/image_to_image.py 迭代修复(参考 guides/text-rendering.md) 3. 多图场景完成后,逐张展示给用户确认,不满意的单张重新生成
---
🛠️ 脚本速查
场景脚本
# 📊 信息图 — 20布局×17风格=340种
python3 scenes/infographic/zlab_infographic.py -l {布局} -s {风格} -n "标题" -c "内容" -o out.png
python3 scenes/infographic/zlab_infographic.py --list # 查看所有选项
# 🖼️ 封面图 — 五维定制=3888种
python3 scenes/cover/zlab_cover.py -n "标题" --type {类型} --palette {配色} --rendering {渲染} --text {文字} --mood {氛围} -o out.png
# 📑 幻灯片 — 16种预设
python3 scenes/slide-deck/zlab_slide_deck.py article.md --style {预设} -o slides/
python3 scenes/slide-deck/zlab_slide_deck.py article.md --outline-only # 仅看大纲
# 📖 漫画 — 5画风×7基调×6布局=210种
python3 scenes/comic/zlab_comic.py source.md --art {画风} --tone {基调} --layout {布局} -o comic/
# ✏️ 文章插图 — 6类型×8风格=48种
python3 scenes/article-illust/zlab_article_illustrator.py article.md --style {风格} -o images/
# 📱 小红书 — 9风格×6布局=54种
python3 scenes/xhs/zlab_xhs_images.py content.md --style {风格} --layout {布局} -o xhs/所有场景脚本支持 --list 查看可用选项。
核心引擎
# 文生图
python3 core/text_to_image.py "中文描述" -r 16:9 -o output.png
python3 core/text_to_image.py "描述" -r 3:4 --ref ref.png -o output.png # 参考图风格
# 图生图
python3 core/image_to_image.py input.png "编辑描述" -r 3:4 -o output.png
# 图生文
python3 core/image_to_text.py image.jpg -m describe|ocr|chart|fashion|product|scene
# 长图拼接
python3 core/merge_long_image.py img1.png img2.png -o output.png --blend 20
python3 core/merge_long_image.py -p "*.png" -o output.png --sort name支持比例:1:1 2:3 3:2 3:4 4:3 4:5 5:4 9:16 16:9 21:9
---
📚 通用指南(按需加载)
| 触发条件 | 文件 | 何时加载 |
|---|---|---|
| 图片含中文文字 | guides/text-rendering.md | 提示词要求含中文标题/标签时必须加载 |
| 为PPT/文档配图 | guides/color-sync.md | 需要与其他载体配色协同时加载 |
| 需要优化生图效果 | guides/prompt-guide.md | 生图效果不理想需要改进时加载 |
| API接口问题 | guides/api-reference.md | 调试底层接口时加载 |
| 生成多屏长图 | scenes/long-image/README.md | 路由到长图场景时必须加载 |
| 营销物料模板 | scenes/marketing/templates.md | 路由到营销场景时必须加载 |
{
"image_api": {
"key": "your_image_api_key",
"base_url": "https://api.openai.com/v1",
"model": "dall-e-3"
},
"vision_api": {
"key": "your_vision_api_key",
"base_url": "https://api.openai.com/v1",
"model": "gpt-4o"
},
"defaults": {
"text_to_image": {
"size": "1792x1024",
"response_format": "b64_json"
},
"image_to_image": {
"size": "1792x1024",
"response_format": "b64_json"
},
"image_to_text": {
"max_tokens": 2000,
"temperature": 0.7,
"mode": "describe"
}
},
"limits": {
"max_file_size_mb": 4,
"supported_formats": ["png", "jpg", "jpeg", "webp", "gif"],
"max_prompt_length": 1000,
"timeout_seconds": {
"text_to_image": 180,
"image_to_image": 180,
"image_to_text": 120
}
},
"retry": {
"max_attempts": 3,
"backoff_multiplier": 2,
"initial_delay_seconds": 1
}
}
#!/usr/bin/env python3
"""
图生图脚本 (Image-to-Image)
基于参考图片和中文指令进行图片编辑(OpenAI Images API 兼容)
Author: 翟星人
"""
import httpx
import base64
import json
import os
from typing import Dict, Any, Optional, Union
from pathlib import Path
VALID_ASPECT_RATIOS = [
"1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"
]
VALID_SIZES = [
"1024x1024",
"1536x1024", "1792x1024", "1344x768", "1248x832", "1184x864", "1152x896", "1536x672",
"1024x1536", "1024x1792", "768x1344", "832x1248", "864x1184", "896x1152"
]
RATIO_TO_SIZE = {
"1:1": "1024x1024",
"2:3": "832x1248",
"3:2": "1248x832",
"3:4": "1024x1536",
"4:3": "1536x1024",
"4:5": "864x1184",
"5:4": "1184x864",
"9:16": "1024x1792",
"16:9": "1792x1024",
"21:9": "1536x672"
}
class ImageToImageEditor:
"""图生图编辑器"""
def __init__(self, config: Optional[Dict[str, str]] = None):
"""
初始化编辑器
Args:
config: 配置字典,包含 api_key, base_url, model
如果不传则从环境变量或配置文件读取
"""
if config is None:
config = self._load_config()
self.api_key = config.get('api_key') or config.get('IMAGE_API_KEY')
self.base_url = config.get('base_url') or config.get('IMAGE_API_BASE_URL')
self.model = config.get('model') or config.get('IMAGE_MODEL') or 'dall-e-3'
if not self.api_key or not self.base_url:
raise ValueError("缺少必要的 API 配置:api_key 和 base_url")
def _load_config(self) -> Dict[str, str]:
"""从配置文件或环境变量加载配置"""
config = {}
# 尝试从配置文件加载
config_path = Path(__file__).parent.parent / 'config' / 'settings.json'
if config_path.exists():
with open(config_path, 'r', encoding='utf-8') as f:
settings = json.load(f)
api_config = settings.get('image_api', {})
config['api_key'] = api_config.get('key')
config['base_url'] = api_config.get('base_url')
config['model'] = api_config.get('model')
# 环境变量优先级更高
config['api_key'] = os.getenv('IMAGE_API_KEY', config.get('api_key'))
config['base_url'] = os.getenv('IMAGE_API_BASE_URL', config.get('base_url'))
config['model'] = os.getenv('IMAGE_MODEL', config.get('model'))
return config
@staticmethod
def image_to_base64(image_path: str, with_prefix: bool = True) -> str:
"""
将图片文件转换为 base64 编码
Args:
image_path: 图片文件路径
with_prefix: 是否添加 data URL 前缀
Returns:
base64 编码字符串
"""
path = Path(image_path)
if not path.exists():
raise FileNotFoundError(f"图片文件不存在: {image_path}")
# 获取 MIME 类型
suffix = path.suffix.lower()
mime_types = {
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
'.gif': 'image/gif',
'.webp': 'image/webp'
}
mime_type = mime_types.get(suffix, 'image/png')
with open(image_path, 'rb') as f:
b64_str = base64.b64encode(f.read()).decode('utf-8')
if with_prefix:
return f"data:{mime_type};base64,{b64_str}"
return b64_str
def edit(
self,
image: Union[str, bytes],
prompt: str,
aspect_ratio: Optional[str] = None,
size: Optional[str] = None,
output_path: Optional[str] = None,
response_format: str = "b64_json"
) -> Dict[str, Any]:
"""
编辑图片
Args:
image: 图片路径或 base64 字符串
prompt: 中文编辑指令
aspect_ratio: 宽高比 (如 3:4, 16:9)
size: 传统尺寸 (如 1024x1792)
output_path: 输出文件路径
response_format: 响应格式
Returns:
包含编辑结果的字典
"""
# 处理图片输入
if isinstance(image, str):
if os.path.isfile(image):
image_b64 = self.image_to_base64(image)
elif image.startswith('data:'):
image_b64 = image
else:
# 假设是纯 base64 字符串
image_b64 = f"data:image/png;base64,{image}"
else:
image_b64 = f"data:image/png;base64,{base64.b64encode(image).decode('utf-8')}"
payload: Dict[str, Any] = {
"model": self.model,
"prompt": prompt,
"image": image_b64,
"response_format": response_format
}
# 确定尺寸:优先用 aspect_ratio 映射,其次用 size
if aspect_ratio:
payload["size"] = RATIO_TO_SIZE.get(aspect_ratio, "1024x1536")
elif size:
payload["size"] = size
else:
payload["size"] = "1024x1536" # 默认 3:4
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
try:
with httpx.Client(timeout=180.0) as client:
response = client.post(
f"{self.base_url}/images/edits",
headers=headers,
json=payload
)
response.raise_for_status()
result = response.json()
# 如果指定了输出路径,保存图片
if output_path and result.get("data"):
b64_data = result["data"][0].get("b64_json")
if b64_data:
self._save_image(b64_data, output_path)
result["saved_path"] = output_path
return {
"success": True,
"data": result,
"saved_path": output_path if output_path else None
}
except httpx.HTTPStatusError as e:
return {
"success": False,
"error": f"HTTP 错误: {e.response.status_code}",
"detail": str(e)
}
except Exception as e:
return {
"success": False,
"error": "编辑失败",
"detail": str(e)
}
def _save_image(self, b64_data: str, output_path: str) -> None:
"""保存 base64 图片到文件"""
image_data = base64.b64decode(b64_data)
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
with open(output_path, 'wb') as f:
f.write(image_data)
def main():
"""命令行入口"""
import argparse
import time
parser = argparse.ArgumentParser(
description='图生图编辑工具',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=f'''
尺寸参数说明:
-r/--ratio 宽高比(推荐),支持: {", ".join(VALID_ASPECT_RATIOS)}
-s/--size 传统尺寸,支持: {", ".join(VALID_SIZES[:4])}...
示例:
python image_to_image.py input.png "编辑描述" -r 3:4
python image_to_image.py input.png "编辑描述" -s 1024x1536
'''
)
parser.add_argument('image', help='输入图片路径')
parser.add_argument('prompt', help='中文编辑指令')
parser.add_argument('-o', '--output', help='输出文件路径(默认保存到当前目录)')
parser.add_argument('-r', '--ratio', help=f'宽高比(推荐)。可选: {", ".join(VALID_ASPECT_RATIOS)}')
parser.add_argument('-s', '--size', help='传统尺寸,如 1024x1536')
args = parser.parse_args()
if args.ratio and args.ratio not in VALID_ASPECT_RATIOS:
print(f"错误: 不支持的宽高比 '{args.ratio}'")
print(f"支持的宽高比: {', '.join(VALID_ASPECT_RATIOS)}")
return
if args.size and args.size not in VALID_SIZES:
print(f"警告: 尺寸 '{args.size}' 可能不被支持")
output_path = args.output
if not output_path:
timestamp = time.strftime("%Y%m%d_%H%M%S")
output_path = f"edited_{timestamp}.png"
editor = ImageToImageEditor()
result = editor.edit(
image=args.image,
prompt=args.prompt,
aspect_ratio=args.ratio,
size=args.size,
output_path=output_path
)
if result["success"]:
print(f"编辑成功!")
if result.get("saved_path"):
print(f"图片已保存到: {result['saved_path']}")
else:
print(f"编辑失败: {result['error']}")
print(f"详情: {result.get('detail', 'N/A')}")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
图生文脚本 (Image-to-Text) - 视觉识别
使用 Qwen2.5-VL 模型分析图片内容并生成文字描述
Author: 翟星人
"""
import httpx
import base64
import json
import os
from typing import Dict, Any, Optional, Union, List
from pathlib import Path
class ImageToTextAnalyzer:
"""图生文分析器 - 视觉识别"""
# 预定义的分析模式
ANALYSIS_MODES = {
"describe": "请详细描述这张图片的内容,包括:人物、场景、物品、颜色、布局等所有细节。",
"ocr": "请仔细识别这张图片中的所有文字内容,按照文字在图片中的位置顺序输出。如果是中文,请保持原文输出。",
"chart": "请分析这张图表的内容,包括:图表类型、数据趋势、关键数据点、标题标签、以及数据的结论或洞察。",
"fashion": "请分析这张图片中人物的穿搭,包括:服装款式、颜色搭配、配饰、整体风格等。",
"product": "请分析这张产品图片,包括:产品类型、外观特征、功能特点、品牌信息等。",
"scene": "请描述这张图片的场景,包括:地点、环境、氛围、时间(白天/夜晚)等。"
}
def __init__(self, config: Optional[Dict[str, str]] = None):
"""
初始化分析器
Args:
config: 配置字典,包含 api_key, base_url, model
如果不传则从环境变量或配置文件读取
"""
if config is None:
config = self._load_config()
self.api_key = config.get('api_key') or config.get('VISION_API_KEY') or config.get('IMAGE_API_KEY')
self.base_url = config.get('base_url') or config.get('VISION_API_BASE_URL') or config.get('IMAGE_API_BASE_URL')
self.model = config.get('model') or config.get('VISION_MODEL') or 'qwen2.5-vl-72b-instruct'
if not self.api_key or not self.base_url:
raise ValueError("缺少必要的 API 配置:api_key 和 base_url")
def _load_config(self) -> Dict[str, str]:
"""从配置文件或环境变量加载配置"""
config = {}
# 尝试从配置文件加载
config_path = Path(__file__).parent.parent / 'config' / 'settings.json'
if config_path.exists():
with open(config_path, 'r', encoding='utf-8') as f:
settings = json.load(f)
# 优先使用 vision_api 配置
vision_config = settings.get('vision_api', {})
if vision_config:
config['api_key'] = vision_config.get('key')
config['base_url'] = vision_config.get('base_url')
config['model'] = vision_config.get('model')
else:
# 回退到 image_api 配置
api_config = settings.get('image_api', {})
config['api_key'] = api_config.get('key')
config['base_url'] = api_config.get('base_url')
# 环境变量优先级更高
config['api_key'] = os.getenv('VISION_API_KEY', os.getenv('IMAGE_API_KEY', config.get('api_key')))
config['base_url'] = os.getenv('VISION_API_BASE_URL', os.getenv('IMAGE_API_BASE_URL', config.get('base_url')))
config['model'] = os.getenv('VISION_MODEL', config.get('model', 'qwen2.5-vl-72b-instruct'))
return config
@staticmethod
def image_to_base64(image_path: str) -> str:
"""
将图片文件转换为 base64 编码(带 data URL 前缀)
Args:
image_path: 图片文件路径
Returns:
base64 编码字符串(含 data URL 前缀)
"""
path = Path(image_path)
if not path.exists():
raise FileNotFoundError(f"图片文件不存在: {image_path}")
# 获取 MIME 类型
suffix = path.suffix.lower()
mime_types = {
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
'.gif': 'image/gif',
'.webp': 'image/webp'
}
mime_type = mime_types.get(suffix, 'image/png')
with open(image_path, 'rb') as f:
b64_str = base64.b64encode(f.read()).decode('utf-8')
return f"data:{mime_type};base64,{b64_str}"
def analyze(
self,
image: Union[str, bytes],
prompt: Optional[str] = None,
mode: str = "describe",
max_tokens: int = 2000,
temperature: float = 0.7
) -> Dict[str, Any]:
"""
分析图片并生成文字描述
Args:
image: 图片路径、URL 或 base64 字符串
prompt: 自定义分析提示词(如果提供则忽略 mode)
mode: 分析模式 (describe/ocr/chart/fashion/product/scene)
max_tokens: 最大输出 token 数
temperature: 温度参数
Returns:
包含分析结果的字典
"""
# 确定使用的提示词
if prompt is None:
prompt = self.ANALYSIS_MODES.get(mode, self.ANALYSIS_MODES["describe"])
# 处理图片输入
if isinstance(image, str):
if os.path.isfile(image):
image_url = self.image_to_base64(image)
elif image.startswith('data:') or image.startswith('http'):
image_url = image
else:
# 假设是纯 base64 字符串
image_url = f"data:image/png;base64,{image}"
else:
image_url = f"data:image/png;base64,{base64.b64encode(image).decode('utf-8')}"
# 构建请求
payload = {
"model": self.model,
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": prompt
},
{
"type": "image_url",
"image_url": {
"url": image_url
}
}
]
}
],
"max_tokens": max_tokens,
"temperature": temperature
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
try:
with httpx.Client(timeout=120.0) as client:
response = client.post(
f"{self.base_url}/chat/completions",
headers=headers,
json=payload
)
response.raise_for_status()
result = response.json()
# 提取文本内容
content = result.get("choices", [{}])[0].get("message", {}).get("content", "")
return {
"success": True,
"content": content,
"mode": mode,
"usage": result.get("usage", {})
}
except httpx.HTTPStatusError as e:
return {
"success": False,
"error": f"HTTP 错误: {e.response.status_code}",
"detail": str(e)
}
except Exception as e:
return {
"success": False,
"error": "分析失败",
"detail": str(e)
}
def describe(self, image: Union[str, bytes]) -> Dict[str, Any]:
"""通用图片描述"""
return self.analyze(image, mode="describe")
def ocr(self, image: Union[str, bytes]) -> Dict[str, Any]:
"""文字识别 (OCR)"""
return self.analyze(image, mode="ocr")
def analyze_chart(self, image: Union[str, bytes]) -> Dict[str, Any]:
"""图表分析"""
return self.analyze(image, mode="chart")
def analyze_fashion(self, image: Union[str, bytes]) -> Dict[str, Any]:
"""穿搭分析"""
return self.analyze(image, mode="fashion")
def analyze_product(self, image: Union[str, bytes]) -> Dict[str, Any]:
"""产品分析"""
return self.analyze(image, mode="product")
def analyze_scene(self, image: Union[str, bytes]) -> Dict[str, Any]:
"""场景分析"""
return self.analyze(image, mode="scene")
def batch_analyze(
self,
images: List[str],
mode: str = "describe"
) -> List[Dict[str, Any]]:
"""
批量分析多张图片
Args:
images: 图片路径列表
mode: 分析模式
Returns:
分析结果列表
"""
results = []
for image in images:
result = self.analyze(image, mode=mode)
result["image"] = image
results.append(result)
return results
def main():
"""命令行入口"""
import argparse
parser = argparse.ArgumentParser(description='图生文分析工具(视觉识别)')
parser.add_argument('image', help='输入图片路径')
parser.add_argument('-m', '--mode', default='describe',
choices=['describe', 'ocr', 'chart', 'fashion', 'product', 'scene'],
help='分析模式')
parser.add_argument('-p', '--prompt', help='自定义分析提示词')
parser.add_argument('--max-tokens', type=int, default=2000, help='最大输出 token 数')
args = parser.parse_args()
analyzer = ImageToTextAnalyzer()
result = analyzer.analyze(
image=args.image,
prompt=args.prompt,
mode=args.mode,
max_tokens=args.max_tokens
)
if result["success"]:
print(f"\n=== 分析结果 ({result['mode']}) ===\n")
print(result["content"])
print(f"\n=== Token 使用 ===")
print(f"输入: {result['usage'].get('prompt_tokens', 'N/A')}")
print(f"输出: {result['usage'].get('completion_tokens', 'N/A')}")
else:
print(f"分析失败: {result['error']}")
print(f"详情: {result.get('detail', 'N/A')}")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
长图拼接脚本 (Merge Long Image)
将多张图片按顺序垂直拼接成一张微信长图
Author: 翟星人
"""
import argparse
import os
import glob as glob_module
from pathlib import Path
from typing import List, Optional, Dict, Any
from PIL import Image
import numpy as np
class LongImageMerger:
"""长图拼接器"""
def __init__(self, target_width: int = 1080):
"""
初始化拼接器
Args:
target_width: 目标宽度,默认1080(微信推荐宽度)
"""
self.target_width = target_width
def _blend_images(self, img_top: Image.Image, img_bottom: Image.Image, blend_height: int) -> Image.Image:
"""
在两张图的接缝处创建渐变融合过渡
Args:
img_top: 上方图片
img_bottom: 下方图片
blend_height: 融合区域高度(像素)
Returns:
融合后的下方图片(顶部已与上方图片底部融合)
"""
blend_height = min(blend_height, img_top.height // 4, img_bottom.height // 4)
top_region = img_top.crop((0, img_top.height - blend_height, img_top.width, img_top.height))
bottom_region = img_bottom.crop((0, 0, img_bottom.width, blend_height))
top_array = np.array(top_region, dtype=np.float32)
bottom_array = np.array(bottom_region, dtype=np.float32)
alpha = np.linspace(1, 0, blend_height).reshape(-1, 1, 1)
blended_array = top_array * alpha + bottom_array * (1 - alpha)
blended_array = np.clip(blended_array, 0, 255).astype(np.uint8)
blended_region = Image.fromarray(blended_array)
result = img_bottom.copy()
result.paste(blended_region, (0, 0))
return result
def merge(
self,
image_paths: List[str],
output_path: str,
gap: int = 0,
background_color: str = "white",
blend: int = 0
) -> Dict[str, Any]:
"""
拼接多张图片为长图
Args:
image_paths: 图片路径列表,按顺序拼接
output_path: 输出文件路径
gap: 图片之间的间隔像素,默认0
background_color: 背景颜色,默认白色
blend: 接缝融合过渡区域高度(像素),默认0不融合,推荐30-50
Returns:
包含拼接结果的字典
"""
if not image_paths:
return {"success": False, "error": "没有提供图片路径"}
valid_paths = []
for p in image_paths:
if os.path.exists(p):
valid_paths.append(p)
else:
print(f"警告: 文件不存在,跳过 - {p}")
if not valid_paths:
return {"success": False, "error": "没有有效的图片文件"}
try:
imgs = [Image.open(p) for p in valid_paths]
resized_imgs = []
for img in imgs:
if img.mode in ('RGBA', 'P'):
img = img.convert('RGB')
ratio = self.target_width / img.width
new_height = int(img.height * ratio)
resized = img.resize((self.target_width, new_height), Image.Resampling.LANCZOS)
resized_imgs.append(resized)
if blend > 0 and len(resized_imgs) > 1:
for i in range(1, len(resized_imgs)):
resized_imgs[i] = self._blend_images(resized_imgs[i-1], resized_imgs[i], blend)
total_height = sum(img.height for img in resized_imgs) + gap * (len(resized_imgs) - 1)
long_image = Image.new('RGB', (self.target_width, total_height), background_color)
y_offset = 0
for img in resized_imgs:
long_image.paste(img, (0, y_offset))
y_offset += img.height + gap
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
long_image.save(output_path, quality=95)
for img in imgs:
img.close()
for img in resized_imgs:
img.close()
return {
"success": True,
"saved_path": output_path,
"width": self.target_width,
"height": total_height,
"image_count": len(resized_imgs)
}
except Exception as e:
return {"success": False, "error": str(e)}
def merge_from_pattern(
self,
pattern: str,
output_path: str,
sort_by: str = "name",
gap: int = 0,
background_color: str = "white",
blend: int = 0
) -> Dict[str, Any]:
"""
通过 glob 模式匹配图片并拼接
Args:
pattern: glob 模式,如 "*.png" 或 "generated_*.png"
output_path: 输出文件路径
sort_by: 排序方式 - "name"(文件名) / "time"(修改时间) / "none"(不排序)
gap: 图片间隔
background_color: 背景颜色
blend: 接缝融合过渡高度
Returns:
包含拼接结果的字典
"""
image_paths = glob_module.glob(pattern)
if not image_paths:
return {"success": False, "error": f"没有找到匹配 '{pattern}' 的图片"}
if sort_by == "name":
image_paths.sort()
elif sort_by == "time":
image_paths.sort(key=lambda x: os.path.getmtime(x))
print(f"找到 {len(image_paths)} 张图片:")
for i, p in enumerate(image_paths, 1):
print(f" {i}. {os.path.basename(p)}")
return self.merge(image_paths, output_path, gap, background_color, blend)
def main():
"""命令行入口"""
parser = argparse.ArgumentParser(
description='长图拼接工具 - 将多张图片垂直拼接成微信长图',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例用法:
# 拼接指定图片
python merge_long_image.py img1.png img2.png img3.png -o output.png
# 使用通配符匹配
python merge_long_image.py -p "generated_*.png" -o long_image.png
# 指定宽度和间隔
python merge_long_image.py -p "*.png" -o out.png -w 750 -g 20
# 按修改时间排序
python merge_long_image.py -p "*.png" -o out.png --sort time
# 启用接缝融合过渡(推荐40px)
python merge_long_image.py img1.png img2.png -o out.png --blend 40
"""
)
parser.add_argument('images', nargs='*', help='要拼接的图片路径列表')
parser.add_argument('-p', '--pattern', help='glob 模式匹配图片,如 "*.png"')
parser.add_argument('-o', '--output', required=True, help='输出文件路径')
parser.add_argument('-w', '--width', type=int, default=1080, help='目标宽度,默认1080')
parser.add_argument('-g', '--gap', type=int, default=0, help='图片间隔像素,默认0')
parser.add_argument('--sort', choices=['name', 'time', 'none'], default='name',
help='排序方式:name(文件名)/time(修改时间)/none')
parser.add_argument('--bg', default='white', help='背景颜色,默认 white')
parser.add_argument('--blend', type=int, default=0,
help='接缝融合过渡高度(像素),推荐30-50,默认0不融合')
args = parser.parse_args()
if not args.images and not args.pattern:
parser.error("请提供图片路径列表或使用 -p 指定匹配模式")
merger = LongImageMerger(target_width=args.width)
if args.pattern:
result = merger.merge_from_pattern(
pattern=args.pattern,
output_path=args.output,
sort_by=args.sort,
gap=args.gap,
background_color=args.bg,
blend=args.blend
)
else:
result = merger.merge(
image_paths=args.images,
output_path=args.output,
gap=args.gap,
background_color=args.bg,
blend=args.blend
)
if result["success"]:
print(f"\n拼接成功!")
print(f"输出文件: {result['saved_path']}")
print(f"尺寸: {result['width']} x {result['height']}")
print(f"共 {result['image_count']} 张图片")
else:
print(f"\n拼接失败: {result['error']}")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
文生图脚本 (Text-to-Image)
根据中文文本描述生成图片(OpenAI Images API 兼容)
支持参考图风格生成
Author: 翟星人
"""
import httpx
import base64
import json
import os
from typing import Dict, Any, Optional, Union
from pathlib import Path
VALID_ASPECT_RATIOS = [
"1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"
]
VALID_SIZES = [
"1024x1024",
"1536x1024", "1792x1024", "1344x768", "1248x832", "1184x864", "1152x896", "1536x672",
"1024x1536", "1024x1792", "768x1344", "832x1248", "864x1184", "896x1152"
]
RATIO_TO_SIZE = {
"1:1": "1024x1024",
"2:3": "832x1248",
"3:2": "1248x832",
"3:4": "1024x1536",
"4:3": "1536x1024",
"4:5": "864x1184",
"5:4": "1184x864",
"9:16": "1024x1792",
"16:9": "1792x1024",
"21:9": "1536x672"
}
class TextToImageGenerator:
"""文生图生成器"""
def __init__(self, config: Optional[Dict[str, str]] = None):
"""
初始化生成器
Args:
config: 配置字典,包含 api_key, base_url, model
如果不传则从环境变量或配置文件读取
"""
if config is None:
config = self._load_config()
self.api_key = config.get('api_key') or config.get('IMAGE_API_KEY')
self.base_url = config.get('base_url') or config.get('IMAGE_API_BASE_URL')
self.model = config.get('model') or config.get('IMAGE_MODEL') or 'dall-e-3'
if not self.api_key or not self.base_url:
raise ValueError("缺少必要的 API 配置:api_key 和 base_url")
def _load_config(self) -> Dict[str, str]:
"""从配置文件或环境变量加载配置"""
config = {}
config_path = Path(__file__).parent.parent / 'config' / 'settings.json'
if config_path.exists():
with open(config_path, 'r', encoding='utf-8') as f:
settings = json.load(f)
api_config = settings.get('image_api', {})
config['api_key'] = api_config.get('key')
config['base_url'] = api_config.get('base_url')
config['model'] = api_config.get('model')
config['api_key'] = os.getenv('IMAGE_API_KEY', config.get('api_key'))
config['base_url'] = os.getenv('IMAGE_API_BASE_URL', config.get('base_url'))
config['model'] = os.getenv('IMAGE_MODEL', config.get('model'))
return config
@staticmethod
def image_to_base64(image_path: str, with_prefix: bool = True) -> str:
"""将图片文件转换为 base64 编码"""
path = Path(image_path)
if not path.exists():
raise FileNotFoundError(f"图片文件不存在: {image_path}")
suffix = path.suffix.lower()
mime_types = {
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
'.gif': 'image/gif',
'.webp': 'image/webp'
}
mime_type = mime_types.get(suffix, 'image/png')
with open(image_path, 'rb') as f:
b64_str = base64.b64encode(f.read()).decode('utf-8')
if with_prefix:
return f"data:{mime_type};base64,{b64_str}"
return b64_str
def generate(
self,
prompt: str,
size: Optional[str] = None,
aspect_ratio: Optional[str] = None,
image_size: Optional[str] = None,
output_path: Optional[str] = None,
response_format: str = "b64_json",
ref_image: Optional[str] = None
) -> Dict[str, Any]:
"""
生成图片
Args:
prompt: 中文图像描述提示词
size: 图片尺寸 (如 1792x1024),与 aspect_ratio 二选一
aspect_ratio: 宽高比 (如 16:9, 3:4),推荐使用
image_size: 分辨率 (1K/2K/4K),仅 gemini-3.0-pro-image-preview 支持
output_path: 输出文件路径,如果提供则保存图片
response_format: 响应格式,默认 b64_json
ref_image: 参考图片路径,用于风格参考
Returns:
包含生成结果的字典
"""
if ref_image:
return self._generate_with_reference(
prompt=prompt,
ref_image=ref_image,
aspect_ratio=aspect_ratio,
size=size,
output_path=output_path,
response_format=response_format
)
payload: Dict[str, Any] = {
"model": self.model,
"prompt": prompt,
"response_format": response_format
}
# 确定尺寸:优先用 aspect_ratio 映射,其次用 size
if aspect_ratio:
payload["size"] = RATIO_TO_SIZE.get(aspect_ratio, "1024x1024")
elif size:
payload["size"] = size
else:
payload["size"] = "1792x1024" # 默认 16:9
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
try:
with httpx.Client(timeout=180.0) as client:
response = client.post(
f"{self.base_url}/images/generations",
headers=headers,
json=payload
)
response.raise_for_status()
result = response.json()
if output_path and result.get("data"):
b64_data = result["data"][0].get("b64_json")
if b64_data:
self._save_image(b64_data, output_path)
result["saved_path"] = output_path
return {
"success": True,
"data": result,
"saved_path": output_path if output_path else None
}
except httpx.HTTPStatusError as e:
return {
"success": False,
"error": f"HTTP 错误: {e.response.status_code}",
"detail": str(e)
}
except Exception as e:
return {
"success": False,
"error": "生成失败",
"detail": str(e)
}
def _generate_with_reference(
self,
prompt: str,
ref_image: str,
aspect_ratio: Optional[str] = None,
size: Optional[str] = None,
output_path: Optional[str] = None,
response_format: str = "b64_json"
) -> Dict[str, Any]:
"""
参考图片风格生成新图
Args:
prompt: 新图内容描述
ref_image: 参考图片路径
aspect_ratio: 宽高比
size: 尺寸
output_path: 输出路径
response_format: 响应格式
"""
image_b64 = self.image_to_base64(ref_image)
enhanced_prompt = f"参考这张图片的背景风格、配色方案和视觉设计,保持完全一致的风格,生成新内容:{prompt}"
# 确定尺寸:优先用 aspect_ratio 映射,其次用 size
if size is None:
size = RATIO_TO_SIZE.get(aspect_ratio, "1024x1792") if aspect_ratio else "1024x1792"
payload = {
"model": self.model,
"prompt": enhanced_prompt,
"image": image_b64,
"size": size,
"response_format": response_format
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
try:
with httpx.Client(timeout=180.0) as client:
response = client.post(
f"{self.base_url}/images/edits",
headers=headers,
json=payload
)
response.raise_for_status()
result = response.json()
if output_path and result.get("data"):
b64_data = result["data"][0].get("b64_json")
if b64_data:
self._save_image(b64_data, output_path)
result["saved_path"] = output_path
return {
"success": True,
"data": result,
"saved_path": output_path if output_path else None
}
except httpx.HTTPStatusError as e:
return {
"success": False,
"error": f"HTTP 错误: {e.response.status_code}",
"detail": str(e)
}
except Exception as e:
return {
"success": False,
"error": "生成失败",
"detail": str(e)
}
def _save_image(self, b64_data: str, output_path: str) -> None:
"""保存 base64 图片到文件"""
image_data = base64.b64decode(b64_data)
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
with open(output_path, 'wb') as f:
f.write(image_data)
def main():
"""命令行入口"""
import argparse
import time
parser = argparse.ArgumentParser(
description='文生图工具',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=f'''
尺寸参数说明:
-r/--ratio 推荐使用,支持: {", ".join(VALID_ASPECT_RATIOS)}
-s/--size 传统尺寸,支持: {", ".join(VALID_SIZES[:4])}...
--resolution 分辨率(1K/2K/4K),仅 gemini-3.0-pro-image-preview 支持
--ref 参考图片路径,后续图片将参考首图风格生成
示例:
python text_to_image.py "描述" -r 3:4 # 竖版 3:4
python text_to_image.py "描述" -r 9:16 -o out.png # 竖屏 9:16
python text_to_image.py "描述" -s 1024x1792 # 传统尺寸
# 长图场景:首图定调,后续参考首图风格
python text_to_image.py "首屏内容" -r 3:4 -o 01.png
python text_to_image.py "第二屏内容" -r 3:4 --ref 01.png -o 02.png
'''
)
parser.add_argument('prompt', help='中文图像描述提示词')
parser.add_argument('-o', '--output', help='输出文件路径(默认保存到当前目录)')
parser.add_argument('-r', '--ratio', help=f'宽高比,推荐使用。可选: {", ".join(VALID_ASPECT_RATIOS)}')
parser.add_argument('-s', '--size', help='图片尺寸 (如 1792x1024)')
parser.add_argument('--resolution', help='分辨率 (1K/2K/4K),仅部分模型支持')
parser.add_argument('--ref', help='参考图片路径,用于风格参考(长图场景)')
args = parser.parse_args()
if args.ratio and args.ratio not in VALID_ASPECT_RATIOS:
print(f"错误: 不支持的宽高比 '{args.ratio}'")
print(f"支持的宽高比: {', '.join(VALID_ASPECT_RATIOS)}")
return
if args.size and args.size not in VALID_SIZES:
print(f"警告: 尺寸 '{args.size}' 可能不被支持")
print(f"推荐使用 -r/--ratio 参数指定宽高比")
if args.ref and not os.path.exists(args.ref):
print(f"错误: 参考图片不存在: {args.ref}")
return
output_path = args.output
if not output_path:
timestamp = time.strftime("%Y%m%d_%H%M%S")
output_path = f"generated_{timestamp}.png"
generator = TextToImageGenerator()
result = generator.generate(
prompt=args.prompt,
size=args.size,
aspect_ratio=args.ratio,
image_size=args.resolution,
output_path=output_path,
ref_image=args.ref
)
if result["success"]:
print(f"生成成功!")
if result.get("saved_path"):
print(f"图片已保存到: {result['saved_path']}")
else:
print(f"生成失败: {result['error']}")
print(f"详情: {result.get('detail', 'N/A')}")
if __name__ == "__main__":
main()
API 参考文档
概述
本技能使用两套 API: 1. Images API - 用于图像生成和编辑(文生图、图生图) 2. Qwen2.5-VL API - 用于视觉识别(图生文)
---
一、Images API(图像生成)
1.1 基础配置
| 配置项 | 值 |
|---|---|
| Base URL | 配置文件中的 image_api.base_url |
| Model | 配置文件中的 image_api.model |
| 认证方式 | Bearer Token |
1.2 文生图接口
端点
POST /images/generations请求头
{
"Content-Type": "application/json",
"Authorization": "Bearer ${IMAGE_API_KEY}"
}请求体
{
"model": "dall-e-3",
"prompt": "中文图像描述",
"size": "1792x1024",
"response_format": "b64_json"
}参数说明
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| model | string | 是 | 配置文件中的 image_api.model |
| prompt | string | 是 | 中文图像生成提示词 |
| size | string | 否 | 图片尺寸,默认 1792x1024 |
| response_format | string | 否 | 响应格式,推荐 b64_json |
响应体
{
"created": 1641234567,
"data": [
{
"b64_json": "base64编码的图片数据"
}
]
}1.3 图生图接口
端点
POST /images/edits请求体
{
"model": "dall-e-3",
"prompt": "中文编辑指令",
"image": "data:image/png;base64,{base64数据}",
"size": "1792x1024",
"response_format": "b64_json"
}参数说明
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| model | string | 是 | 配置文件中的 image_api.model |
| prompt | string | 是 | 中文图片编辑指令 |
| image | string | 是 | Base64 编码的参考图片(含 data URL 前缀) |
| size | string | 否 | 输出尺寸 |
| response_format | string | 否 | 响应格式 |
响应体
{
"data": [
{
"b64_json": "base64编码的生成图片"
}
]
}---
二、Qwen2.5-VL API(视觉识别)
2.1 基础配置
| 配置项 | 值 |
|---|---|
| Base URL | 配置文件中的 vision_api.base_url |
| Model | 配置文件中的 vision_api.model |
| 认证方式 | Bearer Token |
2.2 图生文接口
端点
POST /chat/completions请求头
{
"Content-Type": "application/json",
"Authorization": "Bearer ${VISION_API_KEY}"
}请求体
{
"model": "qwen2.5-vl-72b-instruct",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "请描述这张图片"
},
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64,{base64数据}"
}
}
]
}
],
"max_tokens": 2000,
"temperature": 0.7
}参数说明
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| model | string | 是 | 视觉模型名称 |
| messages | array | 是 | 消息列表,包含文本和图片 |
| max_tokens | int | 否 | 最大输出 token 数 |
| temperature | float | 否 | 温度参数(0-1) |
响应体
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"created": 1641234567,
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "这是一张..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 100,
"completion_tokens": 50,
"total_tokens": 150
}
}---
三、错误码说明
| 状态码 | 说明 | 处理建议 |
|---|---|---|
| 400 | 请求参数错误 | 检查请求体格式和参数 |
| 401 | API 密钥无效 | 检查 API Key 是否正确 |
| 403 | 权限不足 | 检查 API Key 权限 |
| 429 | 请求频率限制 | 等待后重试 |
| 500 | 服务器内部错误 | 稍后重试 |
| 503 | 服务不可用 | 稍后重试 |
---
四、最佳实践
4.1 超时设置
- 文生图:建议 120-180 秒
- 图生图:建议 180-300 秒
- 图生文:建议 60-120 秒
4.2 重试策略
建议实现指数退避重试: 1. 首次重试:等待 1 秒 2. 第二次重试:等待 2 秒 3. 第三次重试:等待 4 秒
4.3 图片格式
- 支持格式:PNG、JPG、JPEG、WebP、GIF
- 推荐格式:PNG(无损)或 JPEG(有损但体积小)
- 最大文件大小:建议不超过 4MB
4.4 Base64 编码
图片必须使用完整的 Data URL 格式:
data:image/png;base64,iVBORw0KGgo...配色协同机制
当 image-service 与其他 skill 配合使用时(如 pptx、docx、obsidian 等),必须感知上下文配色方案并自动适配,确保生成的图片与目标载体风格统一。
协同原则
1. 主动感知:生成配图前,先确认目标载体的配色方案 2. 自动适配:将配色信息融入图片生成提示词 3. 风格统一:背景色、主色调、强调色保持一致
配色来源优先级
| 优先级 | 来源 | 说明 |
|---|---|---|
| 1 | 用户明确指定 | 用户直接提供的颜色值 |
| 2 | 当前任务上下文 | 正在制作的 PPT/文档的配色方案 |
| 3 | 项目配置文件 | .design/palette.json 或类似配置 |
| 4 | 默认风格 | 手绘白底风格(无特殊要求时) |
与 PPTX 协同
制作 PPT 配图时,从 pptx skill 的设计方案中提取配色:
# 示例:PPT 配色方案
- 背景色:#181B24(深蓝黑)
- 主色:#B165FB(紫色)
- 辅助色:#40695B(翡翠绿)
- 文字色:#FFFFFF / #AAAAAA生成图片时,将配色融入提示词:
# 错误示例(不考虑配色)
python scripts/text_to_image.py "流程图,用户路径变化" -r 16:9
# 正确示例(融入配色)
python scripts/text_to_image.py "信息图风格,深色背景#181B24,科技感流程图。用紫色#B165FB和翡翠绿#40695B作为强调色,展示用户路径变化,发光线条风格,中文标签" -r 16:9与其他 Skill 协同
| 目标载体 | 配色来源 | 适配要点 |
|---|---|---|
| PPTX | HTML slides 的 CSS 配色 | 背景色、强调色、文字色统一 |
| DOCX | 文档主题色或用户指定 | 配合文档正式/活泼风格 |
| Obsidian | Vault 主题(深色/浅色) | 适配笔记阅读体验 |
| 小红书 | 品牌色或内容调性 | 竖版 3:4,吸睛配色 |
| 调研报告 | 统一手绘风格 | 使用 research_image.py 预设 |
配色提示词模板
信息图风格,{背景描述}背景{背景色},{风格描述}。
使用{主色}作为主色调,{辅助色}作为辅助色。
{内容描述},{视觉风格},中文标签。示例:
信息图风格,深色背景#181B24,科技感对比图。
使用紫色#B165FB作为主色调,翡翠绿#40695B作为辅助色。
左侧展示SEO特点,右侧展示GEO特点,发光连接线风格,中文标签。Agent 执行规范
1. 识别协同场景:检测是否在为其他 skill 生成配图 2. 提取配色方案:从上下文/HTML/配置中获取颜色值 3. 构建适配提示词:将配色信息自然融入生成描述 4. 验证风格一致:生成后确认与目标载体视觉协调
协同执行流程
1. 确认目标载体 → 2. 提取配色方案 → 3. 融入提示词 → 4. 生成适配图片
提示词指南
概述
本指南提供文生图、图生图和图生文三种场景的提示词编写规范和最佳实践。
---
一、文生图提示词
1.1 基本规则
1. 必须使用中文撰写提示词 2. 图片中的标题、说明、标签必须为中文 3. 默认尺寸为 16:9(1792x1024) 4. 结构化描述效果更好
1.2 标准模板
[风格类型],[艺术效果],[分辨率]。
标题:[中文标题]。
视觉元素:[主体对象、结构、场景描述]。
配色:[主色调方案]。
类型:[具体类型]。1.3 推荐风格
| 风格 | 适用场景 |
|---|---|
| 信息图风格 | 数据展示、流程说明 |
| 数据可视化 | 图表、统计数据 |
| 手绘文字风格 | 笔记、教程 |
| 科技插画风 | 技术文章配图 |
| 扁平化设计 | UI/UX 展示 |
| 3D 渲染风格 | 产品展示 |
1.4 示例
信息图类
信息图风格插图,手绘文字风格,高清16:9。
标题:AI技术发展趋势。
视觉元素:中央AI芯片图标,周围连接云计算、大数据、机器学习图标。
配色:科技蓝和白色。
类型:信息图。数据可视化类
数据可视化风格,中文标注,高清16:9。
标题:2026年AI投资趋势。
视觉元素:柱状图、增长箭头、美元符号。
配色:金色和科技蓝。
类型:数据可视化。产品展示类
3D产品渲染风格,光影效果,高清16:9。
标题:智能手表新品发布。
视觉元素:手表主体居中,周围展示核心功能图标。
配色:深空灰和玫瑰金。
类型:产品展示。---
二、图生图提示词
2.1 基本规则
1. 明确指出保留什么和修改什么 2. 描述目标风格和期望效果 3. 提供具体的细节要求
2.2 标准模板
基于原图进行编辑,[编辑描述]。
保持:[需要保留的元素]。
修改:[需要修改的部分]。
风格:[目标风格]。
细节:[具体的细节要求]。2.3 编辑类型
| 类型 | 说明 | 示例 |
|---|---|---|
| 风格迁移 | 改变整体风格 | 转为油画风格 |
| 背景替换 | 更换背景 | 将背景改为海滩 |
| 元素添加 | 添加新元素 | 添加文字标题 |
| 元素删除 | 移除元素 | 删除背景人物 |
| 色调调整 | 改变颜色 | 转为暖色调 |
| 质量增强 | 提升质量 | 增加细节和清晰度 |
2.4 示例
风格迁移
基于原图进行编辑,将整体风格改为科技蓝色调的信息图。
保持:主体元素和构图。
修改:所有文字替换为中文标注,背景改为深蓝渐变。
风格:现代科技感信息图。
细节:添加数据流动效果和光点装饰。人物编辑
基于原图进行编辑,将人物转换为3D科幻风格。
保持:人物姿态和面部特征。
修改:服装改为未来感战斗服,增加全息UI界面。
风格:类似钢铁侠贾维斯系统。
细节:添加蓝色全息光效和数据面板。背景替换
基于原图进行编辑,替换背景为深色科技空间。
保持:原图主体比例和清晰度。
修改:背景完全替换,添加中文标题与数据标签。
风格:深色科技风格。
细节:背景添加星空和网格线条。---
三、图生文提示词
3.1 分析模式
| 模式 | 用途 | 提示词 |
|---|---|---|
| describe | 通用描述 | 详细描述图片内容 |
| ocr | 文字识别 | 识别图片中的所有文字 |
| chart | 图表分析 | 分析图表数据和趋势 |
| fashion | 穿搭分析 | 分析人物服装搭配 |
| product | 产品分析 | 分析产品特征 |
| scene | 场景分析 | 描述场景环境 |
3.2 自定义提示词示例
详细描述
请详细描述这张图片的内容,包括:
1. 人物特征和表情
2. 服装样式和颜色
3. 画面布局和构图
4. 艺术风格或摄影风格
5. 任何文字标注或说明
6. 背景环境和其他细节OCR识别
请仔细识别这张图片中的所有文字内容,包括:
1. 标题和副标题
2. 正文内容
3. 图表标签
4. 按钮文字
5. 其他任何可见的文字
请按照文字在图片中的位置顺序,以清晰的格式输出识别结果。图表分析
请分析这张图表的内容,包括:
1. 图表类型(柱状图、折线图、饼图等)
2. 主要数据趋势
3. 关键数据点
4. 图表标题和标签
5. 数据的结论或洞察
请用中文详细描述图表传达的信息。穿搭分析
请分析这张图片中人物的穿搭,包括:
1. 上装:款式、颜色、材质
2. 下装:款式、颜色、材质
3. 鞋履:类型、颜色
4. 配饰:包包、帽子、眼镜、饰品等
5. 整体风格:休闲/商务/运动/时尚等
6. 搭配建议和点评---
四、最佳实践
4.1 提示词优化技巧
1. 具体明确:避免模糊描述,使用具体词汇 2. 结构清晰:使用分点或模板结构 3. 重点突出:将最重要的要求放在前面 4. 适度详细:提供足够细节但不要过于冗长
4.2 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 生成结果与描述不符 | 提示词不够具体 | 添加更多细节描述 |
| 中文显示异常 | 未强调中文要求 | 明确指定"中文标注" |
| 风格不统一 | 风格描述模糊 | 使用具体的风格参考 |
| 元素缺失 | 未明确列出元素 | 逐一列出所需元素 |
4.3 提示词长度建议
- 文生图:100-300 字
- 图生图:50-200 字
- 图生文:50-150 字
文字清晰规范
生成包含中文文字的图片时,必须在 prompt 末尾追加文字清晰指令,确保文字可读、无乱码。
文字清晰后缀(必加)
【文字渲染要求】
- 所有中文文字必须清晰可读,笔画完整,无模糊、无乱码、无伪文字
- 文字边缘锐利,呈现印刷级清晰度,彻底消除压缩噪点与边缘溢色
- 字体风格统一,字距适中,排版规整
- 严禁出现无法阅读的乱码字符或残缺笔画完整 Prompt 结构
{风格描述}。{内容描述}。{布局描述}。
【文字渲染要求】
- 所有中文文字必须清晰可读,笔画完整,无模糊、无乱码、无伪文字
- 文字边缘锐利,呈现印刷级清晰度
- 字体风格统一,排版规整生成后校验流程
1. 生成图片后,用 image_to_text.py -m ocr 校验文字是否清晰 2. 如果 OCR 识别结果与预期文字不符,使用图生图迭代修复 3. 修复 prompt 使用以下模板
文字修复 Prompt(图生图迭代修复用)
执行语意级图像重构。针对图中模糊或乱码的文字区域进行修复:
1. 保持原图的版面配置、物体座标、配色风格完全不变
2. 将模糊文字修复为清晰的简体中文:{预期文字内容}
3. 文字笔画必须呈现印刷级清晰度,边缘锐利,无压缩噪点
4. 严禁产生无法阅读的伪文字或乱码
直接输出修复后的图像。{
"palettes": {
"tech-blue": {
"name": "科技蓝",
"primary": "#4A90D9",
"secondary": "#81C784",
"accent": "#FFB74D",
"background": "#F5F7FA",
"text": "#333333"
},
"dark-purple": {
"name": "暗紫科技",
"primary": "#B165FB",
"secondary": "#40695B",
"accent": "#00B4D8",
"background": "#181B24",
"text": "#FFFFFF"
},
"warm-earth": {
"name": "暖色大地",
"primary": "#D4845A",
"secondary": "#8B7355",
"accent": "#E8C170",
"background": "#FFF8F0",
"text": "#4A3728"
},
"minimal-gray": {
"name": "极简灰",
"primary": "#333333",
"secondary": "#666666",
"accent": "#0066FF",
"background": "#FFFFFF",
"text": "#1A1A1A"
},
"neon-cyber": {
"name": "霓虹赛博",
"primary": "#00FFE0",
"secondary": "#FF00FF",
"accent": "#FFE500",
"background": "#0A0A1A",
"text": "#FFFFFF"
}
}
}
{
"categories": {
"tech": ["现代极简科技风格", "深色渐变背景", "霓虹蓝紫光效", "金属质感", "高级产品摄影", "8K超高清"],
"life": ["温暖自然生活美学", "柔和自然光线", "浅色干净背景", "日系清新摄影", "高级感留白"],
"fashion": ["潮流街头风格", "大胆撞色", "动态构图", "霓虹灯牌", "时尚杂志级", "高对比度"],
"food": ["美食摄影风格", "暖色调打光", "微距特写质感", "蒸汽烟雾效果", "高级餐厅氛围"],
"education": ["柔和温馨插画风格", "马卡龙色系", "圆润可爱造型", "安全信任感", "干净清爽"]
}
}
image-service
多模态图像处理技能,支持 8 大生图场景 + 图生文 + 长图拼接。
场景能力
| 场景 | 脚本 | 组合数 |
|---|---|---|
| 📊 信息图 | scenes/infographic/zlab_infographic.py | 20布局×17风格=340 |
| 🖼️ 封面图 | scenes/cover/zlab_cover.py | 6×9×6×4×3=3888 |
| 📜 长图 | core引擎串行生成+拼接 | — |
| 📑 幻灯片 | scenes/slide-deck/zlab_slide_deck.py | 16种预设 |
| 📖 漫画 | scenes/comic/zlab_comic.py | 5×7×6=210 |
| ✏️ 文章插图 | scenes/article-illust/zlab_article_illustrator.py | 6×8=48 |
| 📱 小红书图 | scenes/xhs/zlab_xhs_images.py | 9×6=54 |
| 🛍️ 营销物料 | scenes/marketing/templates.md | 模板库 |
核心引擎
| 引擎 | 脚本 | 说明 |
|---|---|---|
| 文生图 | core/text_to_image.py | 中文描述→图片,支持10种比例 |
| 图生图 | core/image_to_image.py | 基于参考图编辑 |
| 图生文 | core/image_to_text.py | 图片分析(describe/ocr/chart等6种模式) |
| 长图拼接 | core/merge_long_image.py | 多图垂直拼接,支持融合过渡 |
快速开始
1. 配置 API
编辑 config/settings.json,填入你的 API 地址和密钥。支持任何 OpenAI 兼容的图像生成 API。
2. 使用示例
# 单图
python core/text_to_image.py "信息图风格,AI技术趋势" -r 16:9 -o out.png
# 信息图(金字塔+手绘风)
python scenes/infographic/zlab_infographic.py -l pyramid -s craft-handmade -n "AI技术栈" -c "顶层AGI,中层大模型,底层算力" -o ai.png
# 封面图(五维定制)
python scenes/cover/zlab_cover.py -n "深入AI Agent" --type conceptual --palette dark -o cover.png
# 漫画(日漫+温暖)
python scenes/comic/zlab_comic.py story.md --art manga --tone warm -o comic/
# 幻灯片
python scenes/slide-deck/zlab_slide_deck.py article.md --style blueprint -o slides/
# 文章智能配图
python scenes/article-illust/zlab_article_illustrator.py article.md --style notion -o images/
# 小红书卡片
python scenes/xhs/zlab_xhs_images.py article.md --style cute --layout balanced -o xhs/所有场景脚本支持 --list 查看可用选项。
目录结构
image-service/
├── SKILL.md 场景路由器(Agent入口)
├── config/settings.json API配置
├── core/ 底层引擎(4个)
├── scenes/ 8大场景
│ ├── infographic/ 信息图(20布局×17风格)
│ ├── cover/ 封面图(五维定制)
│ ├── long-image/ 长图(串行生成规范)
│ ├── slide-deck/ 幻灯片(16预设)
│ ├── comic/ 漫画(画风×基调×布局)
│ ├── article-illust/ 文章插图(智能匹配)
│ ├── xhs/ 小红书(风格×布局)
│ └── marketing/ 营销物料(模板库)
├── guides/ 通用指南
└── presets/ 共享预设依赖
- Python 3.10+
httpx— HTTP 请求Pillow+numpy— 长图拼接
pip install httpx pillow numpyLicense
MIT
文章插图场景
概述
智能文章插图生成器,分析Markdown文章结构,自动识别需要配图的位置和类型,批量生成插图。6类型 × 8风格 = 48种组合。
快速使用
# 自动分析+生成(Notion风)
python scenes/article-illust/zlab_article_illustrator.py article.md --style notion -o images/
# 强制所有插图为流程图类型
python scenes/article-illust/zlab_article_illustrator.py article.md --type flowchart --style blueprint -o images/
# 查看选项
python scenes/article-illust/zlab_article_illustrator.py --list工作流程
1. 分析文章 → 按h2/h3拆分章节 2. 智能匹配 → 根据关键词自动推荐每个章节的插图类型 3. 输出规划 → 展示配图位置和类型 4. 逐张生成 → 按类型×风格生成插图
类型(6种)— 自动匹配
| 类型 | 名称 | 触发关键词 |
|---|---|---|
| infographic | 数据可视化 | 数据、统计、增长、比例 |
| scene | 氛围插图 | 场景、想象、故事、感受 |
| flowchart | 流程图 | 步骤、流程、首先、然后 |
| comparison | 对比图 | 对比、vs、区别、优劣 |
| framework | 概念图 | 架构、框架、模型、体系 |
| timeline | 时间线 | 历史、阶段、发展、版本 |
风格(8种)
| 风格 | 名称 | 适用 |
|---|---|---|
| notion | 极简线条 | 知识分享(默认) |
| elegant | 精致优雅 | 商业 |
| warm | 友好亲切 | 个人成长 |
| minimal | 极简禅意 | 哲学 |
| blueprint | 技术蓝图 | 架构设计 |
| watercolor | 水彩艺术 | 生活方式 |
| editorial | 杂志编辑 | 科技解说 |
| scientific | 学术精确 | 学术研究 |
参数
| 参数 | 说明 |
|---|---|
| article | Markdown文章路径(必填) |
| --style | 视觉风格(默认 notion) |
| --type | 强制指定插图类型(不指定则自动判断) |
| -r, --ratio | 宽高比(默认 16:9) |
| -o, --output | 输出目录(默认 illustrations/) |
| --list | 列出所有选项 |
#!/usr/bin/env python3
"""
智能文章插图生成器 (zlab Article Illustrator)
分析文章结构,识别需要配图的位置,自动生成插图
类型 × 风格 二维系统
Author: 翟星人
"""
import argparse
import subprocess
import sys
import re
import os
from pathlib import Path
SKILL_DIR = Path(__file__).parent.parent.parent
CORE_SCRIPT = SKILL_DIR / "core" / "text_to_image.py"
# === 类型(信息结构)===
TYPES = {
"infographic": {
"name": "数据可视化",
"prompt": "数据可视化信息图,图表、指标、数据展示",
"trigger_words": ["数据", "统计", "增长", "比例", "百分比", "趋势"]
},
"scene": {
"name": "氛围插图",
"prompt": "氛围场景插图,情绪渲染,画面感强",
"trigger_words": ["场景", "想象", "故事", "经历", "感受"]
},
"flowchart": {
"name": "流程图",
"prompt": "流程步骤可视化,从左到右或从上到下,箭头连接",
"trigger_words": ["步骤", "流程", "首先", "然后", "最后", "过程"]
},
"comparison": {
"name": "对比图",
"prompt": "左右并排对比,突出差异,双栏布局",
"trigger_words": ["对比", "vs", "区别", "不同", "相比", "优劣"]
},
"framework": {
"name": "概念图",
"prompt": "概念关系图,节点连线,结构化展示",
"trigger_words": ["架构", "框架", "模型", "体系", "核心", "组成"]
},
"timeline": {
"name": "时间线",
"prompt": "时间线进展图,横向或纵向轴线,节点标注",
"trigger_words": ["历史", "阶段", "发展", "演进", "里程碑", "版本"]
},
}
# === 风格(视觉美学)===
STYLES = {
"notion": {
"name": "极简线条",
"prompt": "极简手绘线条画风格,黑色细线条,少量色彩点缀,白色背景,干净简洁"
},
"elegant": {
"name": "精致优雅",
"prompt": "精致优雅风格,低饱和配色,细腻渐变,高级质感,留白构图"
},
"warm": {
"name": "友好亲切",
"prompt": "友好温暖风格,暖色调,圆润造型,亲切感,柔和光线"
},
"minimal": {
"name": "极简禅意",
"prompt": "极简禅意风格,大面积留白,极少元素,呼吸感强,克制优雅"
},
"blueprint": {
"name": "技术蓝图",
"prompt": "技术蓝图风格,深蓝背景白色线条,网格坐标,工程制图感"
},
"watercolor": {
"name": "水彩艺术",
"prompt": "水彩画风格,柔和晕染,颜色自然渗透,艺术感强"
},
"editorial": {
"name": "杂志编辑",
"prompt": "杂志编辑风格,专业排版感,信息图设计,高对比清晰"
},
"scientific": {
"name": "学术精确",
"prompt": "学术精确图表风格,数据标注精确,刻度清晰,论文级严谨"
},
}
def analyze_article(md_content):
"""分析文章结构,识别需要配图的段落"""
sections = []
current_title = ""
current_body = ""
for line in md_content.split('\n'):
# h2/h3标题
h_match = re.match(r'^(#{2,3})\s+(.+)$', line)
if h_match:
if current_title and current_body:
sections.append({"title": current_title, "body": current_body.strip()})
current_title = h_match.group(2)
current_body = ""
else:
current_body += line + "\n"
if current_title and current_body:
sections.append({"title": current_title, "body": current_body.strip()})
# 为每个章节推荐插图类型
results = []
for section in sections:
text = section["title"] + " " + section["body"]
best_type = "infographic" # 默认
best_score = 0
for type_key, type_def in TYPES.items():
score = sum(1 for word in type_def["trigger_words"] if word in text)
if score > best_score:
best_score = score
best_type = type_key
# 只为有足够内容的章节生成插图(>50字)
if len(section["body"]) > 50:
results.append({
"title": section["title"],
"body": section["body"][:300],
"recommended_type": best_type,
"confidence": best_score,
})
return results
def build_illust_prompt(section, type_key, style_key):
"""构建插图提示词"""
type_def = TYPES[type_key]
style_def = STYLES[style_key]
parts = [
f"文章配图插图,16:9横版",
type_def['prompt'],
f"图解主题:「{section['title']}」",
f"需要可视化的内容:{section['body'][:200]}",
style_def['prompt'],
"所有标注使用中文,文字清晰可读。构图简洁明了,信息层次分明。配色与文章风格协调,不喧宾夺主。适合嵌入Markdown文章中阅读",
]
return ",".join(parts)
def generate_illustrations(md_path, style, ratio, output_dir, type_override=None):
"""主生成流程"""
if style not in STYLES:
print(f"错误: 未知风格 '{style}',可用: {', '.join(STYLES.keys())}")
sys.exit(1)
with open(md_path, 'r', encoding='utf-8') as f:
md_content = f.read()
sections = analyze_article(md_content)
if not sections:
print("文章中未找到适合配图的章节")
sys.exit(0)
print(f"✏️ 文章插图生成")
print(f" 文章: {md_path}")
print(f" 风格: {STYLES[style]['name']} ({style})")
print(f" 比例: {ratio}")
print(f" 识别到 {len(sections)} 个配图位置")
print()
# 输出分析结果
print("=== 配图规划 ===")
for i, sec in enumerate(sections, 1):
t = type_override or sec['recommended_type']
print(f" {i}. [{TYPES[t]['name']}] {sec['title']} (置信度:{sec['confidence']})")
print()
# 创建输出目录
Path(output_dir).mkdir(parents=True, exist_ok=True)
# 逐张生成
for i, sec in enumerate(sections, 1):
t = type_override or sec['recommended_type']
prompt = build_illust_prompt(sec, t, style)
output_path = os.path.join(output_dir, f"illust_{i:02d}_{t}.png")
print(f" 生成第{i}/{len(sections)}张: {sec['title']} [{TYPES[t]['name']}]")
cmd = [sys.executable, str(CORE_SCRIPT), prompt, "-r", ratio, "-o", output_path]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f" ⚠️ 失败: {result.stderr[:200]}")
else:
print(f" ✅ {output_path}")
print(f"\n全部完成!{len(sections)}张插图已保存到 {output_dir}/")
def list_options():
print("=== 插图类型 (6种) ===\n")
for k, v in TYPES.items():
print(f" {k:15s} {v['name']:8s} — 触发词: {', '.join(v['trigger_words'][:4])}")
print("\n=== 视觉风格 (8种) ===\n")
for k, v in STYLES.items():
print(f" {k:15s} {v['name']}")
print()
def main():
parser = argparse.ArgumentParser(
description="zlab 智能文章插图生成器 — 6类型 × 8风格",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
# 自动分析文章并生成插图
python zlab_article_illustrator.py article.md --style notion -o images/
# 指定所有插图类型为流程图
python zlab_article_illustrator.py article.md --type flowchart --style blueprint -o images/
# 查看选项
python zlab_article_illustrator.py --list
"""
)
parser.add_argument("article", nargs="?", help="Markdown文章路径")
parser.add_argument("--style", default="notion", choices=STYLES.keys(), help="视觉风格(默认 notion)")
parser.add_argument("--type", choices=TYPES.keys(), help="强制指定所有插图类型(不指定则自动判断)")
parser.add_argument("-r", "--ratio", default="16:9", help="宽高比(默认 16:9)")
parser.add_argument("-o", "--output", default="illustrations", help="输出目录(默认 illustrations/)")
parser.add_argument("--list", action="store_true", help="列出所有选项")
args = parser.parse_args()
if args.list:
list_options()
return
if not args.article:
parser.print_help()
print("\n错误: 请提供文章文件路径")
sys.exit(1)
if not os.path.exists(args.article):
print(f"错误: 文件不存在 {args.article}")
sys.exit(1)
generate_illustrations(args.article, args.style, args.ratio, args.output, args.type)
if __name__ == "__main__":
main()
漫画场景
概述
知识漫画生成器,5画风 × 7基调 × 6布局 = 210种组合。逐页串行生成,参考上一页保持角色和风格一致。
快速使用
# 日漫风温暖基调
python scenes/comic/zlab_comic.py story.md --art manga --tone warm -o comic/
# 水墨动作风
python scenes/comic/zlab_comic.py story.md --art ink-brush --tone action --layout cinematic -o comic/
# 查看所有选项
python scenes/comic/zlab_comic.py --list画风(5种)
| 画风 | 名称 | 描述 |
|---|---|---|
| ligne-claire | 清线 | 统一线条、平涂色彩,欧洲漫画(默认) |
| manga | 日漫 | 大眼睛、表情丰富、速度线 |
| realistic | 写实 | 数字绘画、精致渲染 |
| ink-brush | 水墨 | 中国水墨、笔触晕染 |
| chalk | 粉笔 | 黑板粉笔、温暖童趣 |
基调(7种)
| 基调 | 名称 | 描述 |
|---|---|---|
| neutral | 中性 | 平衡理性(默认) |
| warm | 温暖 | 怀旧温馨 |
| dramatic | 戏剧 | 高对比紧张 |
| romantic | 浪漫 | 柔和唯美 |
| energetic | 活力 | 明亮动感 |
| vintage | 复古 | 历史做旧 |
| action | 动作 | 速度线战斗 |
布局(6种)
| 布局 | 名称 | 每页格数 | 适用 |
|---|---|---|---|
| standard | 标准 | 4-6格 | 叙事推进(默认) |
| cinematic | 电影 | 2-4格 | 戏剧时刻 |
| dense | 密集 | 6-9格 | 技术说明 |
| splash | 跨页 | 1-2大图 | 关键揭示 |
| mixed | 混合 | 3-7不等 | 复杂叙事 |
| webtoon | 条漫 | 3-5竖向 | 手机阅读 |
参数
| 参数 | 说明 |
|---|---|
| source | 素材Markdown文件(必填) |
| --art | 画风(默认 ligne-claire) |
| --tone | 基调(默认 neutral) |
| --layout | 布局(默认 standard) |
| -r, --ratio | 宽高比(默认 3:4竖版) |
| -o, --output | 输出目录(默认 comic/) |
| --pages | 最大页数(默认 12) |
| --list | 列出所有选项 |
生成逻辑
- 首页用
text_to_image.py定调 - 后续页用
image_to_image.py参考上一页,保持角色和风格一致 - 串行生成,不并发
#!/usr/bin/env python3
"""
知识漫画生成器 (zlab Comic Generator)
画风 × 基调 × 布局,逐页分镜生成
底层调用 core/text_to_image.py + core/image_to_image.py
Author: 翟星人
"""
import argparse
import subprocess
import sys
import re
import os
from pathlib import Path
SKILL_DIR = Path(__file__).parent.parent.parent
CORE_T2I = SKILL_DIR / "core" / "text_to_image.py"
CORE_I2I = SKILL_DIR / "core" / "image_to_image.py"
# === 三维定义 ===
ARTS = {
"ligne-claire": {
"name": "清线",
"prompt": "清晰统一线条,平涂色彩填充,欧洲漫画传统风格(丁丁历险记风),干净利落的黑色轮廓线"
},
"manga": {
"name": "日漫",
"prompt": "日本漫画风格,大眼睛角色,表情丰富夸张,速度线和效果线,网点阴影,动感十足"
},
"realistic": {
"name": "写实",
"prompt": "数字绘画写实风格,准确人体比例,精致细腻渲染,电影级光影效果"
},
"ink-brush": {
"name": "水墨",
"prompt": "中国水墨画风格,毛笔笔触,水墨晕染效果,留白意境,黑白灰为主偶有点缀色"
},
"chalk": {
"name": "粉笔",
"prompt": "粉笔画风格,黑板深色背景,彩色粉笔手绘线条,粗糙质感,温暖童趣"
},
}
TONES = {
"neutral": {
"name": "中性",
"prompt": "平衡理性的色调,中性明度,教育性氛围,冷静客观"
},
"warm": {
"name": "温暖",
"prompt": "温暖怀旧色调,暖黄暖橙为主,柔和光线,亲切感人"
},
"dramatic": {
"name": "戏剧",
"prompt": "高对比戏剧性色调,强烈明暗对比,紧张感,冲击力强"
},
"romantic": {
"name": "浪漫",
"prompt": "柔和浪漫色调,粉色紫色为主,装饰性花瓣元素,梦幻唯美"
},
"energetic": {
"name": "活力",
"prompt": "明亮高饱和活力色调,动感构图,充满能量和激情"
},
"vintage": {
"name": "复古",
"prompt": "复古做旧色调,泛黄胶片质感,历史感,时代真实性"
},
"action": {
"name": "动作",
"prompt": "动作场面色调,速度线冲击效果,爆炸光效,战斗视觉张力"
},
}
LAYOUTS = {
"standard": {
"name": "标准",
"panels": "4-6格",
"prompt": "标准漫画分格,4到6个格子,大小均匀,叙事节奏平稳"
},
"cinematic": {
"name": "电影",
"panels": "2-4格",
"prompt": "电影宽银幕分格,2到4个宽幅画面,横向构图,戏剧张力"
},
"dense": {
"name": "密集",
"panels": "6-9格",
"prompt": "密集分格,6到9个小格子,信息量大,适合技术说明"
},
"splash": {
"name": "跨页",
"panels": "1-2大图",
"prompt": "跨页大图,1到2个超大画面,视觉冲击力极强,关键时刻"
},
"mixed": {
"name": "混合",
"panels": "3-7不等",
"prompt": "混合分格,大小不一的格子3到7个,丰富的视觉节奏变化"
},
"webtoon": {
"name": "条漫",
"panels": "3-5竖向",
"prompt": "竖向条漫布局,3到5个竖向排列的画面,适合手机阅读"
},
}
def parse_source_to_pages(content, max_pages=12):
"""将文字素材拆分为漫画页"""
pages = []
# 按段落/章节拆分
paragraphs = re.split(r'\n{2,}', content.strip())
# 合并短段落,拆分长段落
buffer = ""
for para in paragraphs:
para = para.strip()
if not para:
continue
# h2/h3标记作为分页点
if re.match(r'^#{2,3}\s', para):
if buffer:
pages.append(buffer.strip())
buffer = ""
buffer = para + "\n"
else:
buffer += para + "\n"
# 超过200字分一页
if len(buffer) > 200:
pages.append(buffer.strip())
buffer = ""
if buffer:
pages.append(buffer.strip())
return pages[:max_pages]
def build_comic_prompt(page_content, page_num, total, art_def, tone_def, layout_def):
"""构建单页漫画的生图提示词"""
parts = [
f"知识漫画,第{page_num}页(共{total}页),竖版3:4",
art_def['prompt'],
tone_def['prompt'],
layout_def['prompt'],
f"本页剧情内容:{page_content[:300]}",
"对话气泡中使用中文,文字清晰可读。角色造型一致,表情生动夸张。画面构图饱满但不拥挤,留出气泡空间。分格之间有清晰的边框线分隔。背景适当简化突出人物和对话",
]
return ",".join(parts)
def generate_comic(source_path, art, tone, layout, ratio, output_dir, max_pages):
"""主生成流程"""
if art not in ARTS:
print(f"错误: 未知画风 '{art}',可用: {', '.join(ARTS.keys())}")
sys.exit(1)
if tone not in TONES:
print(f"错误: 未知基调 '{tone}',可用: {', '.join(TONES.keys())}")
sys.exit(1)
if layout not in LAYOUTS:
print(f"错误: 未知布局 '{layout}',可用: {', '.join(LAYOUTS.keys())}")
sys.exit(1)
art_def = ARTS[art]
tone_def = TONES[tone]
layout_def = LAYOUTS[layout]
# 读取素材
with open(source_path, 'r', encoding='utf-8') as f:
content = f.read()
# 拆分为页
pages = parse_source_to_pages(content, max_pages)
print(f"📖 漫画生成")
print(f" 画风: {art_def['name']} ({art})")
print(f" 基调: {tone_def['name']} ({tone})")
print(f" 布局: {layout_def['name']} ({layout}),{layout_def['panels']}")
print(f" 页数: {len(pages)}")
print(f" 比例: {ratio}")
print(f" 输出: {output_dir}/")
print()
# 输出分镜大纲
print("=== 分镜大纲 ===")
for i, page in enumerate(pages, 1):
summary = page[:60].replace('\n', ' ')
print(f" 第{i}页: {summary}...")
print()
# 创建输出目录
Path(output_dir).mkdir(parents=True, exist_ok=True)
# 逐页串行生成(首页text_to_image,后续image_to_image参考上一页)
prev_page_path = None
for i, page in enumerate(pages, 1):
prompt = build_comic_prompt(page, i, len(pages), art_def, tone_def, layout_def)
output_path = os.path.join(output_dir, f"page_{i:02d}.png")
print(f" 生成第{i}/{len(pages)}页...")
if i == 1 or prev_page_path is None:
# 首页:文生图定调
cmd = [sys.executable, str(CORE_T2I), prompt, "-r", ratio, "-o", output_path]
else:
# 后续页:参考上一页,保持角色和风格一致
ref_prompt = f"参考模板图的角色造型、画风和色调,保持完全一致。{prompt}"
cmd = [sys.executable, str(CORE_I2I), prev_page_path, ref_prompt, "-r", ratio, "-o", output_path]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f" ⚠️ 第{i}页失败: {result.stderr[:200]}")
else:
print(f" ✅ {output_path}")
prev_page_path = output_path
print(f"\n全部完成!{len(pages)}页漫画已保存到 {output_dir}/")
def list_options():
print("=== 画风 (5种) ===\n")
for k, v in ARTS.items():
print(f" {k:15s} {v['name']}")
print("\n=== 基调 (7种) ===\n")
for k, v in TONES.items():
print(f" {k:15s} {v['name']}")
print("\n=== 布局 (6种) ===\n")
for k, v in LAYOUTS.items():
print(f" {k:15s} {v['name']:6s} {v['panels']}")
print(f"\n共 {len(ARTS)} × {len(TONES)} × {len(LAYOUTS)} = {len(ARTS)*len(TONES)*len(LAYOUTS)} 种组合")
def main():
parser = argparse.ArgumentParser(
description="zlab 知识漫画生成器 — 5画风 × 7基调 × 6布局",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
# 日漫风温暖基调
python zlab_comic.py story.md --art manga --tone warm -o comic/
# 水墨动作风
python zlab_comic.py story.md --art ink-brush --tone action --layout cinematic -o comic/
# 查看所有选项
python zlab_comic.py --list
"""
)
parser.add_argument("source", nargs="?", help="文字素材Markdown文件")
parser.add_argument("--art", default="ligne-claire", choices=ARTS.keys(), help="画风(默认 ligne-claire)")
parser.add_argument("--tone", default="neutral", choices=TONES.keys(), help="基调(默认 neutral)")
parser.add_argument("--layout", default="standard", choices=LAYOUTS.keys(), help="布局(默认 standard)")
parser.add_argument("-r", "--ratio", default="3:4", help="宽高比(默认 3:4竖版)")
parser.add_argument("-o", "--output", default="comic", help="输出目录(默认 comic/)")
parser.add_argument("--pages", type=int, default=12, help="最大页数(默认 12)")
parser.add_argument("--list", action="store_true", help="列出所有选项")
args = parser.parse_args()
if args.list:
list_options()
return
if not args.source:
parser.print_help()
print("\n错误: 请提供素材文件路径")
sys.exit(1)
if not os.path.exists(args.source):
print(f"错误: 文件不存在 {args.source}")
sys.exit(1)
generate_comic(args.source, args.art, args.tone, args.layout, args.ratio, args.output, args.pages)
if __name__ == "__main__":
main()
封面图场景
概述
五维定制系统:类型(6) × 配色(9) × 渲染(6) × 文字(4) × 氛围(3) = 3888种组合
专为文章、公众号、博客封面设计。
快速使用
# 只需标题,其他自动选择
python scenes/cover/zlab_cover.py -n "AI Agent的前世今生"
# 深色科技风
python scenes/cover/zlab_cover.py -n "深入AI Agent" --type conceptual --palette dark --rendering digital
# 极简风
python scenes/cover/zlab_cover.py -n "极简主义" --type minimal --palette mono --mood subtle五维参数
类型 (--type)
| 值 | 名称 | 说明 |
|---|---|---|
| hero | 主视觉 | 大气居中,视觉冲击(默认) |
| conceptual | 概念隐喻 | 视觉符号表达抽象主题 |
| typography | 文字排版 | 以字体设计为主 |
| metaphor | 视觉比喻 | 具象物体比喻抽象概念 |
| scene | 场景氛围 | 环境渲染,沉浸感 |
| minimal | 极简留白 | 大面积留白,高级感 |
配色 (--palette)
| 值 | 名称 | 说明 |
|---|---|---|
| warm | 暖色调 | 橙红金黄 |
| elegant | 优雅 | 低饱和,灰粉米白 |
| cool | 冷色调 | 蓝绿青灰(默认) |
| dark | 深色 | 深蓝黑灰,高端 |
| earth | 大地色 | 棕褐橄榄 |
| vivid | 鲜艳 | 高饱和撞色 |
| pastel | 粉彩 | 马卡龙色 |
| mono | 单色 | 同色相深浅 |
| retro | 复古 | 怀旧暖黄 |
渲染 (--rendering)
| 值 | 名称 | 说明 |
|---|---|---|
| flat-vector | 扁平矢量 | 纯色块,简洁线条(默认) |
| hand-drawn | 手绘 | 手工线条感 |
| painterly | 绘画 | 油画笔触 |
| digital | 数字渲染 | 3D光影 |
| pixel | 像素 | 8-bit复古 |
| chalk | 粉笔 | 黑板粉笔 |
文字 (--text)
| 值 | 名称 | 说明 |
|---|---|---|
| none | 无文字 | 纯视觉 |
| title-only | 仅标题 | 含主标题(默认) |
| title-subtitle | 标题+副标题 | 主副标题 |
| text-rich | 文字丰富 | 标题+要点 |
氛围 (--mood)
| 值 | 名称 | 说明 |
|---|---|---|
| subtle | 含蓄 | 克制,留白多 |
| balanced | 均衡 | 不张扬不克制(默认) |
| bold | 大胆 | 强烈视觉冲击 |
#!/usr/bin/env python3
"""
封面图生成器 (zlab Cover Image Generator)
五维定制系统:类型 × 配色 × 渲染 × 文字 × 氛围
底层调用 core/text_to_image.py
Author: 翟星人
"""
import argparse
import subprocess
import sys
from pathlib import Path
SKILL_DIR = Path(__file__).parent.parent.parent
CORE_SCRIPT = SKILL_DIR / "core" / "text_to_image.py"
# === 五维定义 ===
TYPES = {
"hero": {"name": "主视觉", "hint": "大气主视觉构图,核心元素居中,视觉冲击力强"},
"conceptual": {"name": "概念隐喻", "hint": "概念隐喻画面,用视觉符号表达抽象主题"},
"typography": {"name": "文字排版", "hint": "以文字排版为主的设计,字体艺术化处理"},
"metaphor": {"name": "视觉比喻", "hint": "具象物体比喻抽象概念,创意视觉表达"},
"scene": {"name": "场景氛围", "hint": "环境场景渲染,营造沉浸式氛围"},
"minimal": {"name": "极简留白", "hint": "大面积留白,极简元素点缀,高级感构图"},
}
PALETTES = {
"warm": {"name": "暖色调", "hint": "暖色调配色,橙红金黄,温暖活力"},
"elegant": {"name": "优雅", "hint": "优雅配色,低饱和度,灰粉米白,高级质感"},
"cool": {"name": "冷色调", "hint": "冷色调配色,蓝绿青灰,理性冷静"},
"dark": {"name": "深色", "hint": "深色系配色,深蓝黑灰,高端神秘"},
"earth": {"name": "大地色", "hint": "大地色系,棕褐橄榄,自然质朴"},
"vivid": {"name": "鲜艳", "hint": "高饱和鲜艳配色,强对比撞色,活力四射"},
"pastel": {"name": "粉彩", "hint": "粉彩马卡龙配色,柔和梦幻"},
"mono": {"name": "单色", "hint": "单色系配色,同一色相的深浅变化,统一纯净"},
"retro": {"name": "复古", "hint": "复古做旧配色,怀旧暖黄,胶片质感"},
}
RENDERINGS = {
"flat-vector": {"name": "扁平矢量", "hint": "扁平矢量插画风格,纯色块填充,简洁线条"},
"hand-drawn": {"name": "手绘", "hint": "手绘插画风格,线条有手工质感,温暖亲切"},
"painterly": {"name": "绘画", "hint": "油画/丙烯绘画风格,笔触可见,艺术感强"},
"digital": {"name": "数字渲染", "hint": "数字3D渲染风格,光影精致,质感细腻"},
"pixel": {"name": "像素", "hint": "像素艺术风格,8-bit复古感"},
"chalk": {"name": "粉笔", "hint": "粉笔画风格,黑板或深色纸张背景,粉笔纹理"},
}
TEXT_MODES = {
"none": {"name": "无文字", "hint": "纯视觉画面,不包含任何文字"},
"title-only": {"name": "仅标题", "hint": "画面中包含主标题文字,醒目位置"},
"title-subtitle": {"name": "标题+副标题", "hint": "包含主标题和副标题文字"},
"text-rich": {"name": "文字丰富", "hint": "包含较多文字内容,标题+副标题+要点"},
}
MOODS = {
"subtle": {"name": "含蓄", "hint": "含蓄克制的氛围,留白多,呼吸感强"},
"balanced": {"name": "均衡", "hint": "均衡的视觉表达,不过于张扬也不过于克制"},
"bold": {"name": "大胆", "hint": "大胆张扬的视觉冲击,高对比,强烈表达"},
}
def build_prompt(title, subtitle, type_key, palette_key, rendering_key, text_key, mood_key):
parts = [
f"专业文章封面图,横版16:9构图",
TYPES[type_key]['hint'],
PALETTES[palette_key]['hint'],
RENDERINGS[rendering_key]['hint'],
MOODS[mood_key]['hint'],
]
if text_key != "none":
parts.append(f"画面中包含中文标题「{title}」,字号大而醒目,放在画面视觉中心或黄金分割位置")
if subtitle and text_key in ("title-subtitle", "text-rich"):
parts.append(f"副标题「{subtitle}」放在主标题下方,字号较小")
parts.append(TEXT_MODES[text_key]['hint'])
parts.append("标题文字清晰可读,笔画完整无乱码,与背景有足够对比度")
else:
parts.append(f"主题:{title},用视觉元素表达主题意境,不含文字")
parts.append("构图平衡,主体突出,背景不喧宾夺主。高清晰度,专业封面设计水准,适合公众号/博客头图")
return ",".join(parts)
def generate(title, subtitle, type_key, palette_key, rendering_key, text_key, mood_key, ratio, output):
prompt = build_prompt(title, subtitle, type_key, palette_key, rendering_key, text_key, mood_key)
print(f"🖼️ 封面图生成")
print(f" 类型: {TYPES[type_key]['name']} ({type_key})")
print(f" 配色: {PALETTES[palette_key]['name']} ({palette_key})")
print(f" 渲染: {RENDERINGS[rendering_key]['name']} ({rendering_key})")
print(f" 文字: {TEXT_MODES[text_key]['name']} ({text_key})")
print(f" 氛围: {MOODS[mood_key]['name']} ({mood_key})")
print(f" 比例: {ratio}")
print(f" 输出: {output}")
cmd = [sys.executable, str(CORE_SCRIPT), prompt, "-r", ratio, "-o", output]
result = subprocess.run(cmd, capture_output=False)
if result.returncode != 0:
print("生成失败")
sys.exit(1)
def main():
parser = argparse.ArgumentParser(
description="zlab 封面图生成器 — 五维定制系统",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
# 自动选择(只需标题)
python zlab_cover.py -n "AI Agent的前世今生"
# 指定维度
python zlab_cover.py -n "深入AI Agent" --type conceptual --palette dark --rendering digital
# 带副标题
python zlab_cover.py -n "Prompt工程" --subtitle "从入门到精通" --text title-subtitle
# 极简风封面
python zlab_cover.py -n "极简主义" --type minimal --palette mono --mood subtle -o cover.png
"""
)
parser.add_argument("-n", "--name", required=True, help="文章/封面标题")
parser.add_argument("--subtitle", help="副标题")
parser.add_argument("--type", default="hero", choices=TYPES.keys(), help="类型(默认 hero)")
parser.add_argument("--palette", default="cool", choices=PALETTES.keys(), help="配色(默认 cool)")
parser.add_argument("--rendering", default="flat-vector", choices=RENDERINGS.keys(), help="渲染风格(默认 flat-vector)")
parser.add_argument("--text", default="title-only", choices=TEXT_MODES.keys(), help="文字模式(默认 title-only)")
parser.add_argument("--mood", default="balanced", choices=MOODS.keys(), help="氛围(默认 balanced)")
parser.add_argument("-r", "--ratio", default="16:9", help="宽高比(默认 16:9)")
parser.add_argument("-o", "--output", help="输出文件路径")
args = parser.parse_args()
output = args.output or f"cover_{args.name[:10]}.png"
generate(args.name, args.subtitle, args.type, args.palette, args.rendering, args.text, args.mood, args.ratio, output)
if __name__ == "__main__":
main()
{
"layouts": {
"pyramid": {
"name": "金字塔",
"description": "层级金字塔,从上到下表示重要性递减",
"prompt_hint": "金字塔层级信息图。画面中央一个等腰三角形,从上到下分3-5层,顶层最窄放核心概念,底层最宽放基础要素。每层用不同深浅色块填充,层与层之间有细线分隔。每层左侧或内部标注中文名称,右侧标注简短说明。三角形外侧留白,整体居中构图",
"best_for": "层级关系、马斯洛需求、优先级"
},
"funnel": {
"name": "漏斗图",
"description": "从宽到窄的漏斗形状,表示筛选/转化过程",
"prompt_hint": "漏斗形信息图。画面中央一个从上到下逐层收窄的漏斗,分4-6层。每层用渐变色块填充(顶层最浅底层最深),层内居中标注中文名称和数字。漏斗两侧用虚线引出每层的详细说明。底部用箭头指向最终结果。整体对称居中",
"best_for": "转化漏斗、筛选过程、销售管道"
},
"fishbone": {
"name": "鱼骨图",
"description": "因果分析图,主干+分支结构",
"prompt_hint": "鱼骨图(石川图)信息图。画面中央一条水平主轴线,右端是鱼头位置放核心问题/结果。主轴上方和下方各伸出3-4条45度斜线作为主要原因分支,每条斜线旁标注中文原因类别。每条主分支上再伸出更细的小分支标注具体子因素。整体呈鱼骨骼形态,左密右疏",
"best_for": "根因分析、问题诊断"
},
"venn": {
"name": "韦恩图",
"description": "重叠圆形表示概念交集",
"prompt_hint": "韦恩图信息图。画面中央2-3个大圆形部分重叠,每个圆用不同半透明颜色填充。各圆的独立区域内标注该概念的独有特征,重叠交集区域用高亮色标注共有特征。每个圆外侧上方标注中文概念名称(大字加粗)。圆形之间间距适中,交集面积占每个圆的20-30%",
"best_for": "概念交集、共性分析"
},
"timeline": {
"name": "时间线",
"description": "横向或纵向时间轴,标注事件节点",
"prompt_hint": "时间线信息图。画面中央一条水平轴线贯穿左右,轴线上均匀分布5-8个圆形节点。节点上方和下方交替排列说明卡片(上方放奇数节点、下方放偶数节点),卡片内含日期/阶段名+简短中文说明。节点之间用渐变色轴线连接,从左到右颜色逐渐变化表示时间推进。首尾节点略大突出起止",
"best_for": "历史、项目进度、版本演进"
},
"mind-map": {
"name": "思维导图",
"description": "中心发散的树状结构",
"prompt_hint": "思维导图信息图。画面正中央一个大圆形/圆角矩形放核心主题(大字醒目),从中心向四周发散出4-6条主分支线,每条主分支末端连接一个中等大小的节点标注子主题。每个子主题再向外伸出2-3条细分支连接小节点。分支线条有机弯曲(非直线),不同主分支用不同颜色区分。整体呈放射状均匀分布",
"best_for": "头脑风暴、知识梳理"
},
"circular-flow": {
"name": "循环流程",
"description": "环形箭头连接的循环过程",
"prompt_hint": "循环流程信息图。画面中央4-6个节点沿圆形均匀排列,节点之间用弧形箭头顺时针连接形成闭环。每个节点是圆角矩形或圆形,内部放图标和中文步骤名称。箭头上可标注过渡说明。圆环中心可放循环主题名称。节点大小一致,间距均匀,整体呈圆形对称",
"best_for": "循环过程、迭代流程、生命周期"
},
"comparison": {
"name": "对比图",
"description": "左右分栏对比,突出差异",
"prompt_hint": "左右对比信息图。画面垂直一分为二,中间用分隔线或VS标志分开。左栏和右栏分别用不同主色调(如蓝vs橙),顶部各有一个大标题。每栏内纵向列出4-6个对比要点,每个要点前有图标。相同维度的对比项左右对齐。底部可有总结行。两栏结构完全对称",
"best_for": "产品对比、方案比较、优劣分析"
},
"grid-cards": {
"name": "卡片网格",
"description": "网格排列的信息卡片",
"prompt_hint": "卡片网格信息图。画面上方居中放大标题,下方是2×3或3×3网格排列的卡片。每张卡片是圆角矩形,内部从上到下依次是:图标/小插图、中文标题(加粗)、1-2行说明文字。卡片之间等间距排列,统一大小。每张卡片可用不同的浅色背景区分,但整体色系协调",
"best_for": "多主题概览、功能列表"
},
"layers-stack": {
"name": "分层堆叠",
"description": "水平分层堆叠,表示技术栈或架构层次",
"prompt_hint": "分层堆叠信息图。画面中央4-6个水平矩形层叠排列,从底到顶依次堆叠。每层宽度一致或逐层变窄(倒梯形),用不同颜色填充。每层内居中标注中文层名,左侧或右侧用引线引出该层的详细说明。最底层最基础,最顶层最上层应用。层与层之间有细线分隔或微小间距",
"best_for": "技术栈、分层架构、OSI模型"
},
"iceberg": {
"name": "冰山图",
"description": "水面上下分区,表面vs深层",
"prompt_hint": "冰山信息图。画面中央一座冰山,水平线将画面分为上下两部分。水面上方是冰山露出部分(约占画面1/3),浅蓝色天空背景,标注可见的表象内容。水面下方是冰山水下部分(约占画面2/3),深蓝色海水背景,体积远大于水面上方,标注隐藏的深层内容。水面线处标注分隔标签。水下部分分2-3层逐渐深入",
"best_for": "表象vs本质、显性vs隐性"
},
"bridge": {
"name": "桥接图",
"description": "问题→解决方案的桥梁结构",
"prompt_hint": "桥接信息图。画面左侧是问题/现状区域(红色或灰色调),右侧是方案/目标区域(绿色或蓝色调),中间用一座桥梁图形连接。桥梁上方标注解决方案/路径。左侧区域内列出2-3个痛点,右侧区域内列出2-3个目标。桥梁下方可标注需要跨越的鸿沟/挑战。整体从左到右阅读,箭头指向右侧",
"best_for": "问题→方案、现状→目标"
},
"tree-hierarchy": {
"name": "树状层级",
"description": "从上到下的树状分类结构",
"prompt_hint": "树状层级信息图。画面顶部居中放根节点(最大的圆角矩形),向下分支展开为2-3个第二层节点,每个第二层节点再向下分出2-3个第三层节点。节点之间用直线或折线连接。根节点用深色强调,越往下层颜色越浅。每个节点内标注中文名称。整体呈倒三角树形,层次分明",
"best_for": "组织架构、分类体系"
},
"nested-circles": {
"name": "嵌套圆",
"description": "同心圆嵌套,由内到外表示影响范围",
"prompt_hint": "嵌套同心圆信息图。画面中央3-5个同心圆由内到外层层扩展。最内圈面积最小放核心概念(深色),最外圈面积最大放外围影响(浅色),颜色从内到外逐渐变浅。每圈的环形区域内标注该层级的中文名称。圆的右侧或下方用引线引出每层的详细说明。整体圆心居中",
"best_for": "影响范围、关注圈层"
},
"quadrants": {
"name": "四象限",
"description": "十字交叉分四个区域",
"prompt_hint": "四象限矩阵信息图。画面中央一个十字坐标轴将空间分为四个象限,横轴和纵轴两端各标注维度名称(如紧急/不紧急、重要/不重要)。四个象限用不同浅色背景区分,每个象限内标注象限名称和2-3个代表性条目。坐标原点可放主题名称。整体正方形构图,四象限面积相等",
"best_for": "优先级矩阵、SWOT分析"
},
"scale-balance": {
"name": "天平图",
"description": "左右两端的天平/秤,表示权衡",
"prompt_hint": "天平权衡信息图。画面中央一个天平/秤的图形,左盘和右盘分别放置对比要素。天平支柱顶部标注主题名称。左盘上方列出2-4个优势/利好项(绿色调),右盘上方列出2-4个劣势/风险项(红色调)。天平可倾斜表示偏向某侧,或平衡表示势均力敌。底座标注总结结论",
"best_for": "利弊权衡、决策分析"
},
"journey-path": {
"name": "旅程路径",
"description": "蜿蜒路径串联里程碑",
"prompt_hint": "旅程路径信息图。画面中一条蜿蜒曲折的路径从左下角延伸到右上角(或S形蜿蜒),路径上均匀分布5-7个里程碑节点(圆形或旗帜标志)。每个节点旁标注阶段名称和简短中文说明,交替排列在路径两侧。路径起点标注「起点」,终点标注「目标」。路径用渐变色表示进展,沿途点缀小图标装饰",
"best_for": "用户旅程、成长路径"
},
"flow": {
"name": "流程图",
"description": "从上到下或从左到右的流程步骤",
"prompt_hint": "流程图信息图。画面从上到下(或从左到右)排列4-6个步骤节点,用箭头依次连接。普通步骤用圆角矩形,判断节点用菱形,起止节点用椭圆。每个节点内标注中文步骤名称。判断节点引出「是/否」两条分支。节点之间间距均匀,箭头清晰可见。整体流向一目了然",
"best_for": "工作流、决策流程、步骤说明"
},
"feature-list": {
"name": "功能列表",
"description": "图标+文字的纵向列表",
"prompt_hint": "功能列表信息图。画面顶部居中放大标题,下方纵向排列4-7个功能条目。每个条目水平排列:左侧一个彩色圆形图标,右侧是功能名称(加粗)和1行简短说明。条目之间有细分隔线或适当间距。每个图标用不同颜色区分但色系统一。整体左对齐,列表整齐有序",
"best_for": "产品功能、要点列表"
},
"equation": {
"name": "公式图",
"description": "A + B = C的公式化表达",
"prompt_hint": "公式表达信息图。画面中央水平排列:左侧2-3个输入要素(用图标+中文标签的卡片表示),中间用加号(+)或箭头(→)连接,右侧等号(=)后放输出结果(用更大的卡片突出)。每个要素卡片大小一致,结果卡片略大并用强调色。整体水平居中,公式从左到右阅读流畅",
"best_for": "公式分解、组合关系"
}
}
}
信息图场景
概述
20种布局 × 17种风格 = 340种组合,覆盖几乎所有信息可视化场景。
快速使用
# 金字塔 + 手绘风
python scenes/infographic/zlab_infographic.py -l pyramid -s craft-handmade -n "标题" -c "内容描述" -o output.png
# 查看所有组合
python scenes/infographic/zlab_infographic.py --list布局速查(20种)
| 布局 | 名称 | 适用场景 |
|---|---|---|
| pyramid | 金字塔 | 层级关系、优先级 |
| funnel | 漏斗图 | 转化漏斗、筛选 |
| fishbone | 鱼骨图 | 根因分析 |
| venn | 韦恩图 | 概念交集 |
| timeline | 时间线 | 历史、项目进度 |
| mind-map | 思维导图 | 知识梳理、头脑风暴 |
| circular-flow | 循环流程 | 迭代、生命周期 |
| comparison | 对比图 | 方案比较、优劣分析 |
| grid-cards | 卡片网格 | 多主题概览 |
| layers-stack | 分层堆叠 | 技术栈、架构层 |
| iceberg | 冰山图 | 表象vs本质 |
| bridge | 桥接图 | 问题→方案 |
| tree-hierarchy | 树状层级 | 组织架构、分类 |
| nested-circles | 嵌套圆 | 影响范围、圈层 |
| quadrants | 四象限 | 优先级矩阵、SWOT |
| scale-balance | 天平图 | 利弊权衡 |
| journey-path | 旅程路径 | 用户旅程、成长路径 |
| flow | 流程图 | 工作流、决策流程 |
| feature-list | 功能列表 | 产品功能、要点 |
| equation | 公式图 | 公式分解、组合 |
风格速查(17种)
| 风格 | 名称 | 描述 |
|---|---|---|
| craft-handmade | 手绘插画 | 手绘线条、纸艺质感(默认) |
| claymation | 黏土动画 | 3D黏土、定格动画感 |
| kawaii | 可爱日系 | 大眼Q版、粉彩色 |
| watercolor | 水彩绘本 | 柔和晕染、童话感 |
| chalkboard | 粉笔黑板 | 彩色粉笔、黑板质感 |
| cyberpunk | 赛博朋克 | 霓虹灯光、暗色未来 |
| bold-graphic | 漫画波普 | 粗线条、网点、高对比 |
| aged-academia | 复古学术 | 泛黄素描、手稿感 |
| corporate | 商务扁平 | 矢量人物、鲜艳填充 |
| technical | 技术蓝图 | 蓝图线条、工程图 |
| origami | 折纸 | 几何折面、纸张质感 |
| pixel-art | 像素复古 | 8-bit、怀旧游戏 |
| wireframe | 线框原型 | 灰度、UI原型 |
| subway-map | 地铁线路 | 彩色线路站点图 |
| ikea-manual | 说明书 | 极简线条、步骤图示 |
| knolling | 整齐平铺 | 俯拍、整齐排列 |
| lego | 乐高积木 | 积木拼搭、童趣 |
参数
| 参数 | 说明 |
|---|---|
| -l, --layout | 布局类型(必填) |
| -s, --style | 视觉风格(默认 craft-handmade) |
| -n, --name | 标题(必填) |
| -c, --content | 内容描述(必填) |
| -r, --ratio | 宽高比(默认 16:9) |
| -o, --output | 输出路径(必填) |
| --list | 列出所有布局和风格 |
{
"styles": {
"craft-handmade": {
"name": "手绘插画",
"description": "手绘线条、纸艺质感",
"prompt_suffix": "手绘插画风格。铅笔和马克笔质感的线条,线条粗细有变化不完全均匀,有手工绘制的温暖感。颜色用水彩式半透明填充,不追求精确边缘。浅米色/奶白色带纸张纹理的背景。标注文字用手写体风格。整体像一幅精心绘制的手账页面"
},
"claymation": {
"name": "黏土动画",
"description": "3D黏土人物,定格动画感",
"prompt_suffix": "3D黏土动画风格。所有元素呈现圆润的黏土质感,表面光滑略有指纹痕迹。柔和的漫射光照明,阴影边缘柔软。配色饱和度适中,类似橡皮泥的鲜艳但不刺眼。浅灰色或浅蓝色干净背景。元素像是摆在桌面上的微缩模型,有轻微景深效果"
},
"kawaii": {
"name": "可爱日系",
"description": "大眼睛、粉彩色、Q版",
"prompt_suffix": "日系可爱卡通风格。圆润Q版造型,大眼睛表情丰富的小角色作为装饰。粉色、浅紫、薄荷绿、浅黄等马卡龙色系配色。圆角矩形和气泡形状的容器。星星、爱心、闪光等可爱小元素点缀。白色或浅粉色背景,整体甜美明亮"
},
"watercolor": {
"name": "水彩绘本",
"description": "柔和水彩,童话绘本风",
"prompt_suffix": "水彩画风格。颜色在边缘自然渗透晕染,有水彩特有的深浅过渡和水痕效果。配色柔和淡雅,以蓝绿棕为主色调。白色水彩纸背景,纸张纹理隐约可见。线条用淡灰色细线勾勒,不突兀。整体像一页精美的水彩绘本插图"
},
"chalkboard": {
"name": "粉笔黑板",
"description": "彩色粉笔,黑板质感",
"prompt_suffix": "粉笔黑板风格。深绿色或深灰色黑板纹理背景,边缘可见木质边框。所有图形和文字用彩色粉笔绘制,线条有粉笔特有的粗糙颗粒质感和断续效果。白色粉笔做主要线条,彩色粉笔(黄、蓝、粉、绿)做强调和填充。局部有粉笔灰飘落和擦除痕迹"
},
"cyberpunk": {
"name": "赛博朋克",
"description": "霓虹灯光、暗色未来感",
"prompt_suffix": "赛博朋克风格。深蓝黑色背景,霓虹蓝(#00F0FF)和霓虹粉(#FF00FF)作为主要发光色。线条和边框带有发光辉光效果(glow),文字有LED显示屏质感。图形元素有全息投影般的半透明效果。网格线条和数据流装饰。整体暗色调高对比,科技感和未来感强烈"
},
"bold-graphic": {
"name": "漫画波普",
"description": "漫画风格、网点、高对比",
"prompt_suffix": "波普漫画风格。粗黑色轮廓线(3-4px),内部用纯色平涂填充。背景使用漫画网点(Ben-Day dots)效果。配色用红、黄、蓝等高饱和原色,强烈对比。爆炸形状的强调框、锯齿状对话泡。整体像美式漫画的一个画面,视觉冲击力强"
},
"aged-academia": {
"name": "复古学术",
"description": "泛黄素描、复古科学感",
"prompt_suffix": "复古学术风格。泛黄羊皮纸/牛皮纸背景纹理,边缘有做旧磨损效果。图形用棕褐色墨水细线素描绘制,类似达芬奇手稿或老百科全书插图。标注文字用衬线体,有手写注释感。偶尔点缀红色或深蓝色作为强调。整体有历史厚重感和学术严谨感"
},
"corporate": {
"name": "商务扁平",
"description": "扁平矢量人物,鲜艳填充",
"prompt_suffix": "现代商务扁平设计风格。纯色块填充的矢量图形,无渐变无阴影。圆角矩形和几何形状为主。配色用2-3种品牌色(蓝色系为主,橙色或绿色做强调)。简洁的线性图标。扁平化人物插图作为装饰。纯白色背景,大面积留白,排版规整专业"
},
"technical": {
"name": "技术蓝图",
"description": "蓝图、等距3D、工程图",
"prompt_suffix": "技术蓝图风格。深蓝色(#1B2838)背景配白色网格坐标线,图形用白色和浅蓝色线条绘制。标注用等宽字体,有尺寸标线和角度标注的工程制图感。元素可以用等距(isometric)视角呈现3D效果。连接线用虚线和实线区分层级。整体像一张精密的工程图纸"
},
"origami": {
"name": "折纸",
"description": "折纸形态,几何感",
"prompt_suffix": "折纸艺术风格。所有图形元素呈现纸张折叠的几何面效果,每个面有不同深浅的同色系填充模拟光影。折痕线清晰可见。配色简洁优雅(白色、灰色搭配一个彩色强调色)。浅色背景上投下柔和的阴影。整体像一组精美的折纸艺术品,简约而不简单"
},
"pixel-art": {
"name": "像素复古",
"description": "8-bit像素,怀旧游戏",
"prompt_suffix": "8-bit像素艺术风格。所有图形由可见的方块像素组成,边缘呈锯齿状阶梯形。配色使用有限色板(16-32色),颜色鲜明饱和。黑色或深色背景。文字使用像素字体。整体像经典红白机/GameBoy游戏画面,充满复古游戏的怀旧感"
},
"wireframe": {
"name": "线框原型",
"description": "灰度框图、界面原型",
"prompt_suffix": "线框原型风格。纯灰度色调(白、浅灰、中灰、深灰、黑),无彩色。图形元素用细线框表示,填充用灰色色块和交叉线条(placeholder)。文字区域用横线条代替。整体像UI设计师画的低保真线框原型图,简洁克制极度理性"
},
"subway-map": {
"name": "地铁线路",
"description": "地铁图风格,彩色线路",
"prompt_suffix": "地铁线路图风格。白色背景上用粗彩色线条(红、蓝、绿、橙、紫等)绘制路线,线条只沿水平、垂直和45度角方向延伸。站点用小圆圈标注,换乘站用大圆圈或方形标注。站名用小号中文标注在站点旁。线条交叉处有清晰的上下层关系。整体像一张真实的地铁线路图"
},
"ikea-manual": {
"name": "说明书",
"description": "极简线条、组装说明风格",
"prompt_suffix": "IKEA说明书风格。极简黑色线条画,无色彩填充(偶尔用浅灰阴影)。简笔画火柴人角色做动作示意。步骤编号用大数字标注(①②③)。箭头指示方向和动作。完全不使用文字说明,纯图示传达信息。白色背景,线条均匀整洁。整体像宜家家具组装手册"
},
"knolling": {
"name": "整齐平铺",
"description": "俯拍整齐排列",
"prompt_suffix": "Knolling平铺风格。纯俯视角度(正上方90度),所有元素整齐排列成行列网格,彼此之间等间距,与画面边缘平行。每个元素朝同一方向摆放。干净的纯色背景(白色/浅灰/浅木纹)。均匀的环境光照,几乎无阴影。整体像一张强迫症满足的物品整理照"
},
"lego": {
"name": "乐高积木",
"description": "乐高积木风格,童趣拼搭",
"prompt_suffix": "乐高积木风格。所有元素由乐高积木块拼搭而成,可见积木表面的圆形凸起(studs)。塑料材质光泽感,鲜艳的红黄蓝绿基础色。微距摄影般的浅景深效果。浅色桌面或底板背景。角色用乐高小人仔(minifigure)造型表示。整体童趣十足,像一个精心搭建的乐高作品"
}
}
}
#!/usr/bin/env python3
"""
信息图生成器 (zlab Infographic Generator)
支持 20 种布局 × 17 种风格 = 340 种组合
底层调用 core/text_to_image.py
Author: 翟星人
"""
import argparse
import json
import subprocess
import sys
import os
from pathlib import Path
BASE_DIR = Path(__file__).parent
SKILL_DIR = BASE_DIR.parent.parent
CORE_SCRIPT = SKILL_DIR / "core" / "text_to_image.py"
LAYOUTS_FILE = BASE_DIR / "layouts.json"
STYLES_FILE = BASE_DIR / "styles.json"
def load_json(path):
with open(path, 'r', encoding='utf-8') as f:
return json.load(f)
def list_options(layouts, styles):
print("=== 可用布局 (20种) ===\n")
for key, layout in layouts.items():
print(f" {key:20s} {layout['name']:8s} — {layout['best_for']}")
print(f"\n=== 可用风格 (17种) ===\n")
for key, style in styles.items():
print(f" {key:20s} {style['name']:8s} — {style['description']}")
print(f"\n共 {len(layouts)} × {len(styles)} = {len(layouts) * len(styles)} 种组合")
def build_prompt(title, content, layout_def, style_def):
"""组装完整提示词"""
prompt_parts = [
f"专业信息图,{layout_def['name']}布局",
layout_def['prompt_hint'],
f"大标题「{title}」放在画面顶部居中位置,字号醒目",
f"信息内容:{content}",
style_def['prompt_suffix'],
"所有文字使用中文,标注清晰可读,笔画完整无乱码。信息层级分明,主次有序。配色协调统一,不超过3-4种主色。高清晰度,专业信息图设计水准"
]
return "。".join(prompt_parts)
def generate(title, content, layout, style, ratio, output):
layouts = load_json(LAYOUTS_FILE)['layouts']
styles = load_json(STYLES_FILE)['styles']
if layout not in layouts:
print(f"错误: 未知布局 '{layout}'")
print(f"可用: {', '.join(layouts.keys())}")
sys.exit(1)
if style not in styles:
print(f"错误: 未知风格 '{style}'")
print(f"可用: {', '.join(styles.keys())}")
sys.exit(1)
layout_def = layouts[layout]
style_def = styles[style]
prompt = build_prompt(title, content, layout_def, style_def)
print(f"📊 信息图生成")
print(f" 布局: {layout_def['name']} ({layout})")
print(f" 风格: {style_def['name']} ({style})")
print(f" 比例: {ratio}")
print(f" 输出: {output}")
print(f" 提示词: {prompt[:100]}...")
cmd = [
sys.executable,
str(CORE_SCRIPT),
prompt,
"-r", ratio,
"-o", output
]
result = subprocess.run(cmd, capture_output=False)
if result.returncode != 0:
print("生成失败")
sys.exit(1)
def main():
parser = argparse.ArgumentParser(
description="zlab 信息图生成器 — 20种布局 × 17种风格",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
# 金字塔 + 手绘风
python zlab_infographic.py -l pyramid -s craft-handmade -n "AI技术栈" -c "顶层AGI,中层大模型,底层算力" -o ai_stack.png
# 鱼骨图 + 技术蓝图风
python zlab_infographic.py -l fishbone -s technical -n "性能瓶颈分析" -c "CPU、内存、IO、网络四大分支" -o perf.png
# 韦恩图 + 水彩风
python zlab_infographic.py -l venn -s watercolor -n "全栈工程师" -c "前端、后端、DevOps三圈交集" -o fullstack.png
# 查看所有布局和风格
python zlab_infographic.py --list
"""
)
parser.add_argument("-l", "--layout", help="布局类型(20种可选,用 --list 查看)")
parser.add_argument("-s", "--style", default="craft-handmade", help="视觉风格(17种可选,默认 craft-handmade)")
parser.add_argument("-n", "--name", help="信息图标题")
parser.add_argument("-c", "--content", help="信息图内容描述")
parser.add_argument("-r", "--ratio", default="16:9", help="宽高比(默认 16:9)")
parser.add_argument("-o", "--output", help="输出文件路径")
parser.add_argument("--list", action="store_true", help="列出所有布局和风格")
args = parser.parse_args()
if args.list:
layouts = load_json(LAYOUTS_FILE)['layouts']
styles = load_json(STYLES_FILE)['styles']
list_options(layouts, styles)
return
if not all([args.layout, args.name, args.content, args.output]):
parser.print_help()
print("\n错误: 必须提供 -l, -n, -c, -o 参数")
sys.exit(1)
generate(args.name, args.content, args.layout, args.style, args.ratio, args.output)
if __name__ == "__main__":
main()
营销物料场景
概述
营销物料生成器,预设多种产品营销模板,支持海报、主图、九宫格等。
详细模板见 templates.md
可用模板类型
| 类型 | 说明 |
|---|---|
| 产品主图 | 电商主图(1:1)、详情页Banner(16:9) |
| 元素拆解 | 功能爆炸图、微距特写、材质对比 |
| 使用场景 | 生活场景、工作场景、使用前后对比 |
| 营销创意 | 促销海报、对比评测图、九宫格套图 |
| 多配色展示 | 多SKU横向排列 |
风格前缀(5类)
- 科技数码类
- 生活消费类
- 时尚潮流类
- 食品饮品类
- 母婴教育类
使用
目前通过 core/text_to_image.py 配合 templates.md 中的模板手动组装提示词。
# 用模板组装prompt后调用
python core/text_to_image.py "{根据模板组装的prompt}" -r 1:1 -o product.png幻灯片图场景
概述
从Markdown内容生成专业幻灯片图片。4维度组合:纹理 × 氛围 × 字体 × 密度,16种预设风格。
快速使用
# 从Markdown生成
python scenes/slide-deck/zlab_slide_deck.py article.md --style blueprint -o slides/
# 仅看大纲
python scenes/slide-deck/zlab_slide_deck.py article.md --outline-only
# 查看所有预设
python scenes/slide-deck/zlab_slide_deck.py --list预设风格(16种)
| 预设 | 名称 | 适用 | 维度组合 |
|---|---|---|---|
| blueprint | 技术蓝图 | 架构、系统设计 | grid×cool×technical×balanced |
| chalkboard | 粉笔黑板 | 教育、教程 | organic×warm×handwritten×balanced |
| corporate | 商务专业 | 投资演示、提案 | clean×professional×geometric×balanced |
| minimal | 极简 | 高管简报 | clean×neutral×geometric×minimal |
| notion | Notion风 | 产品演示、SaaS | clean×neutral×geometric×dense |
| dark-atmospheric | 暗色氛围 | 娱乐、游戏 | clean×dark×editorial×balanced |
| bold-editorial | 大胆编辑 | 产品发布、主题演讲 | clean×vibrant×editorial×balanced |
| pixel-art | 像素艺术 | 游戏、开发者 | pixel×vibrant×technical×balanced |
| scientific | 科学学术 | 医学、化学 | clean×cool×technical×dense |
| sketch-notes | 手绘笔记 | 教育、教程 | organic×warm×handwritten×balanced |
| watercolor | 水彩 | 生活、健康 | organic×warm×humanist×minimal |
| editorial-infographic | 编辑信息图 | 科技解说 | clean×cool×editorial×dense |
| fantasy-animation | 幻想动画 | 教育故事 | organic×vibrant×handwritten×minimal |
| vector-illustration | 矢量插画 | 创意、儿童 | clean×vibrant×humanist×balanced |
| vintage | 复古 | 历史、传记 | paper×warm×editorial×balanced |
| intuition-machine | 机器直觉 | 技术文档 | clean×cool×technical×dense |
参数
| 参数 | 说明 |
|---|---|
| markdown | Markdown文件路径(必填) |
| --style | 预设风格(默认 blueprint) |
| --slides | 最大页数(默认 20) |
| -r, --ratio | 宽高比(默认 16:9) |
| -o, --output | 输出目录(默认 slides/) |
| --outline-only | 仅输出大纲不生成图 |
| --list | 列出所有预设 |
工作流程
1. 读取Markdown文件 2. 按h2标题拆分为幻灯片页(自动生成标题页+结尾页) 3. 提取每页要点 4. 按预设风格逐页生成图片 5. 输出到指定目录