goods-images

GitHub 作者 LeoYeAI/openclaw-master-skills

Use when the user wants to generate product detail images or carousel/main images for e-commerce platforms like Taobao. Triggers on keywords like 商品图, 详情图, 产品图, 电商图, 淘宝图, 轮播图, 主图, or when user uploads a product photo and asks for marketing images.

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install github:LeoYeAI~openclaw-master-skills~goods-images
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/github%3ALeoYeAI~openclaw-master-skills~goods-images/file -o goods-images.md
# 商品详情图 & 轮播图生成 Skill

## Overview

根据用户提供的商品图片和描述,生成电商平台(淘宝/天猫/京东)的 **商品详情图**(9张)和/或 **商品轮播图**(5张)。

## 核心原则

1. **商品图必须保真。** 用户提供的商品图片中的图案、文字、Logo、颜色不得有任何改变。详情图和轮播图是在原图基础上做排版设计和文案包装,不是重新生成商品。
2. **用户零操作。** 全部后台自动完成,直接在对话中输出图片结果。不打开可见浏览器窗口,不让用户手动导出。
3. **环境自适应。** 优先使用 `run_command` + Python PIL 精确渲染中文文字,如果环境不支持则自动降级到 `generate_image` 方案。

## When to Use

- 用户上传了商品图片,要求生成详情图或轮播图
- 用户提供了商品文字描述,要求生成电商图片
- 用户提到"淘宝详情图"、"商品图"、"电商图"、"轮播图"、"主图"等关键词

## 需要收集的信息

| 必须 | 轮播图额外需要 | 可选 |
|------|-------------|------|
| 商品图片(至少1张) | 品牌 Logo(文字或图片) | 规格参数(尺寸/材质等) |
| 商品描述 | 活动内容(如"1件9折") | 品牌故事 |
| | | 核心卖点 |

**输入处理**:
- 有图片 → 观察图片中的产品,推断品类、外观、卖点
- 仅文字 → 从描述中提取信息,用 `generate_image` 先生成 1 张商品图
- 信息不足时追问,**最多补问 1 轮**,其余从图片和描述中推断

**⚠️ 用户可能只要轮播图或只要详情图。** 如果用户明确说只要其中一种,跳过另一种。不确定时默认两种都生成。

## ⚠️ 执行清单(必读)

**你必须生成 14 张图片,不是 1 张!** 按以下步骤逐一执行:

### 轮播图(5张)
1. 用 `generate_image` 生成模特图1(传入用户原图作为 ImagePaths)
2. 用 `generate_image` 生成模特图2(传入用户原图作为 ImagePaths)
3. 用 `generate_image` 生成模特图3(传入用户原图作为 ImagePaths)
4. 用 PIL 脚本或 `generate_image` 在模特图上叠加 Logo + 活动条 + 卖点关键词,生成 carousel_01 ~ carousel_04
5. 用 PIL 脚本或 `generate_image` 生成 carousel_05(白底图,仅 Logo)

### 详情图(9张)
6. `generate_image` → 主图封面(传入原图)
7. `generate_image` → 卖点1(传入原图)
8. `generate_image` → 卖点2(传入原图)
9. `generate_image` → 卖点3(传入原图)
10. `generate_image` → 细节标注图(传入原图)
11. `generate_image` → 穿着/使用场景
12. `generate_image` → 产品参数表(传入原图)
13. `generate_image` → 尺码/规格表
14. `generate_image` → 售后保障

### 关键规则
- **每张图都要单独调用一次 `generate_image`**,不要试图一次生成多张
- **必须传入用户原图作为 ImagePaths**,否则 AI 会画出不一样的商品
- **Logo 和活动文字不要写在 generate_image 的 prompt 里**(AI 画中文会变形),应该用 PIL 后处理
- **背景不要纯白**,要有场景感

---

## 流程

```
输入 → 商品分析 → 风格判断 → 并行生成(详情图 + 轮播图)→ 直接输出
```

---

## Part 1: 商品分析

从用户输入提取:

| 分析项 | 来源 |
|-------|------|
| 商品品类 | 图片观察 + 描述(上衣/裤子/鞋/配饰/3C/家居/食品...) |
| 核心卖点(2-4个) | 描述 + 推断 |
| 目标人群 | 描述 + 推断(儿童/成人/男/女) |
| 商品风格 | 图片观察(潮酷/日系/运动/甜美/简约/商务...) |
| 卖点关键词 | 从描述中提取 2-3 个核心词(如"加绒"、"保暖") |

## Part 2: 风格判断

| 风格 | 适用品类 | 配色方案 |
|------|---------|---------|
| **简约高端** | 数码3C、高端家居、护肤品、商务服饰 | 背景 #fafafa, 文字 #1a1a1a, 点缀 #c9a96e |
| **营销促销** | 食品零食、日用百货、平价商品 | 背景 #fff, 强调 #e63946, 点缀 #ff6b35 |
| **种草生活** | 时尚服饰、美妆、母婴、童装 | 背景 #fdf8f3, 文字 #3d3024, 点缀 #c17a50 |
| **科技未来** | 电子产品、智能设备、数码配件 | 背景 #0a0a0a, 文字 #fff, 点缀 #00d4ff |

---

## Part 3: 轮播图生成(5张)

### 规范

- 尺寸:**800×800 像素(正方形)**
- 5 张:3 张模特/场景图 + 1 张原图场景化 + 1 张商品特写
- 左上角:品牌 Logo
- **前 4 张左侧显示卖点关键词**(从描述提取,如"加绒"、"保暖"),半透明背景条+白色文字
- 前 4 张底部:促销活动条
- **背景不要纯白/纯灰**,应配合商品风格

### Step A: 生成模特/场景图

用 `generate_image` 生成 3 张图,**必须传入用户原图作为 ImagePaths**。

**⚠️ 根据商品品类决定构图和主体:**

| 品类 | 主体 | 构图 | Prompt 关键词 |
|------|------|------|-------------|
| 上衣(卫衣/T恤/外套) | 模特 | 半身照(头到臀) | `upper body shot, waist-up, cropped at hip` |
| 裤子/裙子 | 模特 | 下半身 | `lower body focus, hip to feet` |
| 全身套装/连衣裙 | 模特 | 全身照 | `full body shot` |
| 鞋子 | 脚部特写 | 特写 | `close-up of shoes on feet, ground level angle` |
| 帽子/围巾 | 模特 | 头肩特写 | `close-up, head and shoulders` |
| 数码3C/家居 | 产品 | 场景摆拍 | `product in lifestyle setting, styled flat lay` |
| 食品 | 产品 | 美食摄影 | `food photography, styled plating, appetizing` |

**⚠️ 背景配合商品风格:**

| 风格 | 背景场景 | Prompt 参考 |
|------|---------|------------|
| 潮酷/街头 | 涂鸦墙、砖墙、城市夜景 | `urban concrete wall with graffiti`, `city night lights bokeh` |
| 日系/文艺 | 庭院、咖啡店、公园 | `cozy cafe interior`, `park with warm sunlight` |
| 运动活力 | 操场、户外阳光 | `playground`, `outdoor bright sunlight` |
| 甜美可爱 | 花墙、游乐场 | `pink flower wall`, `pastel balloons` |
| 简约高端 | 大理石台面、极简空间 | `marble surface`, `minimal white interior` |
| 科技感 | 暗色桌面、霓虹灯 | `dark desk setup`, `neon accent lighting` |

**Prompt 模板(服饰类)**:
```
A [age]-year-old Asian [boy/girl/man/woman] model wearing [商品英文描述],
[姿态描述], [场景背景],
[构图方式] focusing on the [商品],
e-commerce fashion photography,
[lighting], professional catalog style, high resolution
```

**Prompt 模板(非服饰类)**:
```
Product photography of [商品英文描述],
[场景/摆放方式], [背景描述],
e-commerce product photography,
soft studio lighting, professional, high resolution, 800x800
```

3 张分别用不同姿态/角度和背景。

### Step B: 叠加 Logo + 活动条 + 卖点关键词

**Agent 应按以下优先级自动选择方案:**

#### 方案 1(首选):`run_command` + Python PIL

先测试环境:
```bash
python3 -c "from PIL import Image; print('ok')" 2>/dev/null || pip install Pillow -q
```

如果可用,用以下脚本精确叠加中文 Logo、活动条和卖点关键词:

```python
from PIL import Image, ImageDraw, ImageFont
import os

CANVAS = 800
FONT_PATHS = [
    '/System/Library/Fonts/PingFang.ttc',                        # macOS
    '/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc',    # Linux Noto
    '/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc',    # Linux Noto alt
    '/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf',  # Linux Droid
    '/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc',             # Linux 文泉驿
]

def get_font(size, bold=False):
    for fp in FONT_PATHS:
        if os.path.exists(fp):
            try:
                idx = 1 if bold and fp.endswith('.ttc') else 0
                return ImageFont.truetype(fp, size, index=idx)
            except:
                try: return ImageFont.truetype(fp, size, index=0)
                except: continue
    return ImageFont.load_default()

def add_overlay(img_path, output_path, logo_text,
                promos=None, keywords=None,
                tag_text=None, tag_sub='店铺折扣叠加官方立减'):
    """
    promos:   活动列表, e.g. ['1件9折', '3件85折'] — 支持 1-N 条
    keywords: 卖点关键词, e.g. ['加绒', '保暖'] — 左侧竖排显示
    tag_text: 标签文字, e.g. '秋冬上新' — 为 None 时根据当前月份自动判断
    """
    img = Image.open(img_path).convert('RGBA')
    ratio = max(CANVAS / img.width, CANVAS / img.height)
    img = img.resize((int(img.width * ratio), int(img.height * ratio)), Image.LANCZOS)
    left = (img.width - CANVAS) // 2
    img = img.crop((left, 0, left + CANVAS, CANVAS))

    overlay = Image.new('RGBA', (CANVAS, CANVAS), (0, 0, 0, 0))
    draw = ImageDraw.Draw(overlay)

    # ===== Logo(左上角 + 阴影)=====
    lf = get_font(32, bold=True)
    for dx, dy in [(2,2),(1,1)]:
        draw.text((24+dx, 20+dy), logo_text, fill=(0,0,0,80), font=lf)
    draw.text((24, 20), logo_text, fill=(255,255,255,250), font=lf)

    # ===== 卖点关键词(左侧竖排)=====
    if keywords:
        kf = get_font(36, bold=True)
        total_h = len(keywords) * 50
        ky = (CANVAS - total_h) // 2 - 40
        for kw in keywords:
            bb = draw.textbbox((0,0), kw, font=kf)
            kw_w = bb[2] - bb[0]
            draw.rounded_rectangle([16, ky-4, 16+kw_w+24, ky+42],
                                   radius=6, fill=(0,0,0,100))
            draw.text((29, ky+1), kw, fill=(0,0,0,60), font=kf)
            draw.text((28, ky), kw, fill=(255,255,255,250), font=kf)
            ky += 50

    # ===== 底部活动条 =====
    if promos:
        # 自动判断季节标签
        if tag_text is None:
            import datetime
            m = datetime.datetime.now().month
            tag_text = {1:'年货节',2:'开春上新',3:'春季上新',4:'春季上新',
                        5:'初夏上新',6:'夏季上新',7:'夏季上新',8:'秋季上新',
                        9:'秋季上新',10:'秋冬上新',11:'秋冬上新',12:'年终大促'}.get(m,'新品上市')

        h = 95; th = 32; y0 = CANVAS - h
        # 过渡阴影
        for i in range(30):
            draw.rectangle([0, y0-30+i, CANVAS, y0-29+i], fill=(0,0,0,int(i*6)))
        # 白色标签栏
        draw.rectangle([0, y0, CANVAS, y0+th], fill=(255,255,255,250))
        tf = get_font(14, bold=True)
        tb = draw.textbbox((0,0), tag_text, font=tf)
        tw = tb[2]-tb[0]+28
        draw.rounded_rectangle([14, y0+4, 14+tw, y0+4+24], radius=5, fill=(56,161,105))
        draw.text((28, y0+7), tag_text, fill='white', font=tf)
        sf = get_font(12)
        draw.text((14+tw+14, y0+9), tag_sub, fill=(170,170,170), font=sf)

        # 红色主条(渐变)
        my = y0 + th
        for x in range(CANVAS):
            r = min(int(210+20*x/CANVAS), 230)
            draw.line([(x,my),(x,CANVAS)], fill=(r, min(int(50+8*x/CANVAS),58), 55))

        # 活动文字(数字大 42px,文字小 22px,支持 N 条活动)
        nf = get_font(42, bold=True)
        xf = get_font(22, bold=True)
        def measure(s):
            return sum(draw.textbbox((0,0),c,font=nf if c.isdigit(