<title>网格图片生成</title>

# 宫格图片生成功能文档

## 功能概述

宫格图片生成功能允许用户在剧本节点或幕节点中一键生成4宫格或9宫格的分镜图片，系统会自动拆分并分配到各个分镜节点。

### 支持的节点类型

1. **剧本节点**: 点击"拆分幕 + 宫格生图"按钮，系统会自动解析剧本、创建幕和分镜节点，并生成宫格图片
2. **幕节点**: 点击"宫格生图"按钮，为该幕下的所有分镜节点批量生成宫格图片

幕节点不在"添加节点"菜单中提供手动新建入口，只能由剧本节点解析/拆分后自动创建；工作流重新加载时仍会恢复已保存的幕节点。

## 使用流程

### 1. 准备剧本

在剧本节点中输入或上传剧本内容。

### 2. 选择宫格模型

剧本节点和幕节点的宫格生图模型默认使用 **标准版（默认模型）**。原有"智能模式"入口已删除，旧工作流中保存的智能模式标识会在重新加载后迁移为标准版（默认模型）。

仍可在下拉框中选择其他支持宫格生图的模型，例如：

- **标准版（默认）**: 剧本节点、幕节点、分镜节点的默认生图模型；宫格类型可单独选择自动、4宫格或9宫格
- **加强版（4宫格）**: 图像生成模型 B
- **加强版（9宫格）**: 图像生成模型 C
- **另一加强选项（9宫格）**: 图像生成模型 D

### 3. 点击"拆分幕 + 宫格生图"

系统会自动执行以下步骤：

1. 调用大语言模型解析剧本，生成幕和分镜节点
2. **收集参考图片**（角色、场景、道具）
3. 统计分镜数量和参考图片数量，决定使用4宫格还是9宫格
4. 预估算力消耗
5. 弹出确认对话框，显示生成信息（包括参考图片数量）
6. 并行调用图片编辑能力生成宫格图片（如有参考图片则传递）
7. 为每个分镜节点创建分镜图子节点

### 4. 自动同步

系统使用两阶段轮询机制：

**第一阶段：任务完成轮询（每10秒检查一次）**

- 与其他视频/图片生成任务共享同一个轮询队列
- 当AI工具完成生成后，仅标记节点 `status: 'splitting'`，不直接调用拆分接口
- 标记完成后立即触发第二阶段

**第二阶段：节点状态轮询（每60秒或立即触发）**

- 检测 `isSplit:true` 且 `url` 为空的宫格节点
- 顺序调用 `/api/ai-tools/{id}/grid-split` 接口拆分图片
- 拆分成功后更新节点的 `url` 和 `preview`
- 如果后端返回 `code:1`（处理中），下次轮询重试

## 幕节点宫格生图

### 使用场景

当你已经有幕节点时，可以直接使用幕节点的宫格生图功能，无需从剧本节点重新开始。

### 使用步骤

1. **选择宫格生图模型**

   - 在幕节点中选择宫格生图模型，默认是 **标准版（默认模型）**。
   - 宫格类型单独选择：自动、4宫格或9宫格。
2. **点击"宫格生图"按钮**

   - 系统会自动执行以下步骤：
   
     - 检查是否已有分镜节点，如果没有则自动生成
     - 收集所有分镜节点的参考图片（角色、场景、道具）
     - 根据选择的模型和分镜数量生成宫格图片
     - 为每个分镜节点创建分镜图子节点
3. **确认生成信息**

   - 系统会弹出确认对话框，显示：
   
     - 将要生成的宫格图片数量
     - 分镜数量
     - 使用的模型（标准版/加强版）
     - 参考图片数量
     - 预计消耗的算力
4. **等待生成完成**

   - 系统会自动轮询生成状态
   - 生成完成后自动拆分并更新分镜图节点

### 注意事项

- 如果幕下还没有分镜节点，系统会先自动生成分镜节点
- 宫格模型默认使用标准版（默认模型），不再提供"智能模式"模型选项
- 如果参考图片数量超过当前模型限制，系统会按模型能力裁剪参考图或使用用户选择的支持模型

## 参考图片收集

宫格生图会自动收集所有分镜中涉及的参考图片：

1. **角色参考图**: 从分镜提示词中提取用【【】】标记的角色名，查询相关数据表获取参考图
2. **场景参考图**: 根据分镜关联的场景ID，查询相关数据表获取场景参考图
3. **道具参考图**: 根据分镜中出现的道具，查询相关数据表获取道具参考图

**注意事项**：

- 需要先在左上角选择世界，才能正确匹配角色
- 相同的角色/场景/道具只会收集一次，避免重复
- 如果有参考图片，系统会使用图片编辑API（`/api/image-edit`）
- 如果没有参考图片，系统会使用文生图API（`/api/text-to-image`）

## 宫格选择逻辑

系统会根据分镜数量自动选择宫格类型；模型默认使用标准版（默认模型），除非用户在下拉框中显式选择其他模型：

- **1个分镜**: 不生成宫格图片（提示用户）
- **2-5个分镜**: 默认使用4宫格（2x2）
- **>5个分镜**: 默认使用9宫格（3x3）

## 算力计算

算力按实际使用量计费，具体定价以产品页为准。确认对话框中会展示预计消耗，供用户参考。

## 提示词格式

系统会将分镜的图片提示词拼接成JSON格式：

### 4宫格示例

```JSON
{
  "grid_layout": "2x2",
  "grid_aspect_ratio": "16:9",
  "grid_structure": "2 rows x 2 columns",
  "cell_aspect_ratio": "16:9",
  "style_guidance": "<系统生成的版式与风格约束文本>",
  "art_style": "赛博朋克风格",
  "shots": [
    {"shot_number": "1 (Top-Left)", "prompt_text": "**[Panel 1 of 4: Top-Left Quadrant]**\n<Subject>: 清晨的城市街道，阳光洒在建筑物上\n<Composition>: Cinematic 16:9 Wide framing."},
    {"shot_number": "2 (Top-Right)", "prompt_text": "**[Panel 2 of 4: Top-Right Quadrant]**\n<Subject>: 咖啡店内部，温暖的灯光\n<Composition>: ..."},
    {"shot_number": "3 (Bottom-Left)", "prompt_text": "..."},
    {"shot_number": "4 (Bottom-Right)", "prompt_text": "..."}
  ]
}
```

其中 `style_guidance` 为系统生成的风格约束文本（要求全图统一画风、不带文字编号等），具体文案不在此列出；`art_style` 仅在用户设置了画风时包含。

### 9宫格示例

```JSON
{
  "grid_layout": "3x3",
  "grid_aspect_ratio": "16:9",
  "grid_structure": "3 rows x 3 columns",
  "cell_aspect_ratio": "16:9",
  "style_guidance": "<系统生成的版式与风格约束文本>",
  "art_style": "水墨画风格",
  "shots": [
    {"shot_number": "1 (Top-Left)", "prompt_text": "..."},
    {"shot_number": "2 (Top-Center)", "prompt_text": "..."},
    ...
    {"shot_number": "9 (Bottom-Right)", "prompt_text": "..."}
  ]
}
```

### 图片填充逻辑

**重要**：当分镜数量不足填满宫格时（例如5个分镜使用4宫格，第二张图只有1个分镜），系统会**自动复制最后一个分镜的提示词填充剩余格子**；若最后一个分镜没有可用提示词，则回退到固定的默认场景提示词。

**示例**：5个分镜使用4宫格

- 第1张图：Shot 1, 2, 3, 4（正常）
- 第2张图：Shot 5, 以及 Shot 5 的提示词复制填充的 3 个格子

这样可以确保：

1. **内容可控**：复用最后一个分镜的提示词，避免模型对空缺格子随机填充内容
2. **兜底提示词**：无可用分镜提示词时回退到固定默认提示词，保证每个格子都有明确输入
3. **尺寸正确**：保持宫格结构完整，确保拆分后的图片尺寸正确

## 节点类型

### 图片节点（宫格生图）

宫格生图现在使用标准的图片节点（`image`类型），拥有完整的图片功能（上传、编辑、下载等）。

**节点数据结构**：

```JavaScript
{
  type: 'image',
  title: '分镜图 1/4',
  data: {
    name: '分镜图 1/4',
    aiToolsId: 12345,         // 关联的AI工具ID
    project_id: 12345,        // 关联的AI工具ID
    gridIndex: 1,             // 在宫格中的位置（1-4或1-9）
    gridSize: 4,              // 宫格大小（4或9）
    url: '/upload/...',       // 拆分后的图片URL（拆分完成前为空）
    preview: '/upload/...',   // 预览图片URL（拆分完成前为空）
    isSplit: true,            // 宫格拆分节点标记（创建时即为true）
    status: 'completed',      // 状态：pending→splitting→completed/failed
    shotFrameNodeId: 678,     // 父分镜节点ID
    // 标准图片节点属性
    file: null,
    prompt: '',
    ratio: '16:9',
    model: '<图像生成模型标识>',
    drawCount: 1
  }
}
```

**图片节点功能**：

- 上传图片（替换生成的分镜图）
- 编辑图片（基于提示词修改）
- 下载图片
- 图片比例选择
- 连接到图生视频节点

## API接口

### 1. 获取宫格拆分图片

```Plaintext
GET /api/ai-tools/{ai_tools_id}/grid-split?grid_index={index}&user_id={user_id}
```

**参数**：

- `ai_tools_id`: AI工具ID
- `grid_index`: 宫格位置（1-4或1-9）
- `user_id`: 用户ID

**响应**：

拆分成功：

```JSON
{
  "code": 0,
  "message": "获取成功",
  "data": {
    "image_url": "/upload/workflow/1/grid_split/12345/1.png",
    "grid_index": 1,
    "grid_size": 4
  }
}
```

处理中（AI 任务进行中，或其他 worker 正在下载/拆分）：

```JSON
{
  "code": 1,
  "message": "拆分处理中，请稍后重试"
}
```

前端收到 `code:1` 时不计入失败次数，下次轮询自动重试。

### 2. 查询AI工具状态

```Plaintext
GET /api/get-status/{ai_tool_id}
```

**响应**：

```JSON
{
  "code": 0,
  "message": "获取成功",
  "data": {
    "id": 12345,
    "status": 2,           // 0=待处理, 1=处理中, 2=已完成, 3=失败
    "type": 7,             // 1=标准版, 7=加强版
    "result_url": "...",
    "message": null
  }
}
```

## 文件存储路径

- **原始宫格图缓存**: `upload/workflow/{user_id}/grid_cache/{ai_tools_id}/original.png`
- **拆分锁文件**: `upload/workflow/{user_id}/grid_cache/{ai_tools_id}/.lock`
- **拆分后的图片**: `upload/workflow/{user_id}/grid_split/{ai_tools_id}/{grid_index}.png`

拆分后的图片会被缓存，重复请求会直接返回缓存的图片。

## 技术实现

### 轮询与拆分机制

宫格图片生成使用两阶段机制：

- **第一阶段**: 任务完成轮询每 10 秒检查 AI 生图任务是否完成，完成后标记节点为 `splitting`
- **第二阶段**: 节点状态轮询每 60 秒检测待拆分节点，**顺序**调用 grid-split 接口
- **防重叠**: 使用运行中标志防止节点状态轮询并发执行（定时器 + 手动触发可能重叠）
- **失败重试上限**: 前端最多重试 20 次，超过后标记节点为 `failed`；`code:1`（处理中/任务未完成）不计入失败次数
- **并发安全**: 后端使用文件锁确保同一 AI 工具 ID 只有一个 worker 进程执行下载+拆分
- **非阻塞**: 图片验证和拆分在线程池中执行，不阻塞事件循环
- **处理中响应**: AI 任务未完成或锁被其他进程持有时，均返回 `code:1`，前端下次轮询重试

### 图片大小

- **标准版**: 使用默认图片大小
- **加强版**: 自动传入 4K 分辨率（3840x2160）

## 注意事项

1. **算力消耗**: 宫格生图会消耗算力，请确保账户有足够的算力
2. **分镜数量**: 只有1个分镜时不会生成宫格图片
3. **模型限制**: 标准版只支持4宫格，参考图片数量>5张时会自动切换到加强版
4. **并行生成**: 多张宫格图片会并行生成，提高效率
5. **自动同步**: 复用现有轮询机制，每10秒检查一次，无需手动刷新
6. **4K输出**: 加强版模型自动使用4K分辨率，确保图片质量
7. **画风传递**: 如果用户设置了画风，所有生图入口（分镜节点、幕节点、剧本节点）都会自动将画风信息传递到提示词中。分镜节点通过 `图片风格：xxx` 追加到 prompt 末尾，多宫格生图通过 JSON 中的 `art_style` 字段传递

## 分镜节点顺序

当通过"拆分幕"或"拆分幕 + 宫格生图"功能生成分镜节点时，系统会确保分镜节点按照 `shot_number` 字段从小到大依次排列（从上到下），保证镜头一、镜头二等按顺序显示。

### 实现细节

1. **幕 Y 间距动态计算**：创建幕节点时，根据每个幕内的分镜数量动态计算 Y 偏移量，避免不同幕的分镜节点在垂直方向交错。
2. **分镜排序**：创建分镜节点前对分镜数组按 `shot_number` 排序，确保组内分镜顺序正确。

---

## 故障排查

### 问题：宫格图已生成但未被拆分

**已彻底重构**：

- 宫格节点创建时 `isSplit` 直接设为 `true`，`url` 和 `preview` 保持为空
- AI 生图完成后仅标记 `status: 'splitting'`，不再直接调用拆分接口
- 节点状态轮询统一驱动拆分，检测 `isSplit:true && !url` 的节点并顺序调用 grid-split
- 后端状态轮询接口排除宫格节点，避免将原始宫格图 URL 写入节点
- 后端 grid-split 使用文件锁确保跨 worker 进程只有一个实例执行下载+拆分

### 问题：分镜图节点一直显示"等待生成..."

**可能原因**：

1. AI工具生成失败
2. 网络问题导致轮询失败
3. 后端拆分接口异常

**解决方法**：

1. 检查浏览器控制台的错误日志
2. 手动刷新页面重新加载工作流
3. 检查后端错误日志

### 问题：拆分后的图片无法显示

**可能原因**：

1. 图片路径错误
2. 文件权限问题
3. 原始宫格图不存在

**解决方法**：

1. 检查 `upload/workflow/{user_id}/grid_split/` 目录是否存在
2. 检查文件权限是否正确
3. 查看后端日志确认拆分是否成功

### 问题：图片拆分失败或切分后图片不完整

**根本原因**：

图片库打开图片时默认惰性加载，只读取文件头而不加载像素数据，实际裁剪时才读取像素。若此时出现 IO 问题（并发访问、文件句柄等），像素数据可能不完整，导致切分后的图片只有一半或出现黑色/灰色区域。

**系统处理**（已修复并进一步重构）：

1. **强制加载**：打开图片后立即强制加载全部像素数据到内存，确保裁剪时数据完整
2. **下载验证**：下载原图后在线程池中执行写入+验证，不阻塞事件循环
3. **原子写入**：下载图片时先写入临时文件，校验通过后再原子重命名为目标文件
4. **文件锁保护**：确保同一 AI 工具 ID 只有一个 worker 进程执行下载+拆分，避免重复下载
5. **损坏清理**：校验失败时自动清理临时文件，不会留下损坏的缓存

**手动解决方法**：

1. 删除损坏的切分缓存：`rm -rf upload/workflow/{user_id}/grid_split/{ai_tools_id}/`
2. 重新访问图片URL或调用拆分接口，系统会自动重新切分

**自动切分机制**：

直接访问图片URL（如 `/upload/workflow/{user_id}/grid_split/{ai_tools_id}/{grid_index}.png`）时，如果文件不存在，系统会自动触发切分逻辑，无需手动调用API。

## 相关文件

- **后端**: 宫格拆分与状态查询 API 实现（`/api/ai-tools/{id}/grid-split`, `/api/get-status/{ai_tool_id}`）、图片拆分工具、算力配置
- **前端**: 节点创建和宫格生图逻辑（任务完成轮询仅标记状态）、工作流节点状态轮询驱动宫格拆分、视频工作流页面
- **文档**: 本文档（docs 目录对应说明）
