主要更改: - 重写前端 extension.js,完全自己控制图像渲染 - 去掉 nodes.py 的 image_upload,避免双图像问题 - 实现选区框绘制(橙色)+ 遮罩区域显示(红色) - 实现选区框拖动功能 - 修复 onMouseDown 参数格式: (e, pos, canvas) - 更新文档和开发日志
243 lines
6.2 KiB
Markdown
243 lines
6.2 KiB
Markdown
# 快速开始指南
|
||
|
||
> 给接手团队的 5 分钟快速了解
|
||
|
||
---
|
||
|
||
## 1. 这个项目是做什么的?
|
||
|
||
**一句话**:ComfyUI 局部重绘的增强编辑器
|
||
|
||
**三个功能**:
|
||
1. **图像编辑**:集成 Photopea(在线 PS),实现液化、仿制图章等功能
|
||
2. **蒙版编辑**:在 Photopea 里画复杂的蒙版(羽化、透明度等)
|
||
3. **选区调整**:手动拖动参考区域位置(原有功能只能中心扩散)
|
||
|
||
---
|
||
|
||
## 2. 核心需求(必须实现)
|
||
|
||
```
|
||
用户上传图像 → 自动检测蒙版(Alpha 通道)
|
||
↓
|
||
没有蒙版?→ 报错:"请先创建蒙版"
|
||
↓
|
||
有蒙版?→ 自动计算选区 = 遮罩边界 + padding
|
||
↓
|
||
在图像预览上绘制:
|
||
- 遮罩(半透明红)
|
||
- 选区框(橙色虚线,可拖动)
|
||
↓
|
||
输出:IMAGE, MASK, region_top, region_left, region_width, region_height
|
||
```
|
||
|
||
**关键点**:
|
||
- 蒙版从 Alpha 通道提取
|
||
- 没蒙版就报错(局部重绘必须有蒙版)
|
||
- 选区是矩形框,必须框住遮罩
|
||
- 用户可以拖动选区位置
|
||
|
||
---
|
||
|
||
## 3. 文件结构
|
||
|
||
```
|
||
InpaintRegionEditor/
|
||
├── nodes.py # ⭐ 后端核心(优先实现)
|
||
├── __init__.py # 节点注册
|
||
│
|
||
├── web/
|
||
│ ├── extension.js # 前端入口
|
||
│ ├── region-box-editor.js # ⭐ 选区框绘制(优先实现)
|
||
│ ├── photopea-modal.js # Photopea 对话框
|
||
│ └── photopea-bridge.js # Photopea 通信
|
||
│
|
||
└── docs/
|
||
├── HANDOFF.md # ⭐ 完整交接文档(最重要)
|
||
├── implementation-plan.md # 详细实现方案
|
||
└── technical-validation.md # 技术验证清单
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 开发顺序建议
|
||
|
||
### 第一步:后端基础(1 天)
|
||
```bash
|
||
# 修改 nodes.py
|
||
1. 实现 INPUT_TYPES(image + padding 参数)
|
||
2. 实现 process 函数:
|
||
- 加载图像
|
||
- 提取 Alpha → MASK
|
||
- 计算遮罩边界
|
||
- 计算选区 = 遮罩边界 + padding
|
||
- 输出 region_*
|
||
3. 测试:上传带 Alpha 的 PNG → 验证输出
|
||
```
|
||
|
||
### 第二步:选区 UI(2 天)
|
||
```bash
|
||
# 创建 region-box-editor.js
|
||
参考:ComfyUI-Impact-Pack/js/mask-rect-area.js
|
||
|
||
功能:
|
||
1. 在节点 Canvas 上绘制遮罩(半透明红)
|
||
2. 在节点 Canvas 上绘制选区框(橙色虚线)
|
||
3. 实现鼠标拖动选区框
|
||
4. 同步坐标到 hidden widget
|
||
```
|
||
|
||
### 第三步:Photopea 集成(2-3 天)
|
||
```bash
|
||
# 创建 photopea-modal.js + photopea-bridge.js
|
||
功能:
|
||
1. 右键菜单 → 打开全屏 Photopea
|
||
2. 加载图像到 Photopea
|
||
3. 导出图像回传
|
||
4. ⚠️ 导出蒙版(待验证,可能无法实现)
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 技术要点速查
|
||
|
||
### 5.1 蒙版提取(Python)
|
||
```python
|
||
if 'A' in img.getbands():
|
||
mask = np.array(img.getchannel('A')).astype(np.float32) / 255.0
|
||
mask = 1.0 - mask # 反转:1=遮罩区域
|
||
else:
|
||
raise ValueError("请先创建蒙版")
|
||
```
|
||
|
||
### 5.2 选区计算(Python)
|
||
```python
|
||
def _calculate_region_rect(mask_bounds, padding):
|
||
return {
|
||
'x': mask_bounds['x'] - padding,
|
||
'y': mask_bounds['y'] - padding,
|
||
'width': mask_bounds['width'] + padding * 2,
|
||
'height': mask_bounds['height'] + padding * 2
|
||
}
|
||
```
|
||
|
||
### 5.3 Photopea 通信(JavaScript)
|
||
```javascript
|
||
// 发送
|
||
photopeaWindow.postMessage('app.activeDocument.saveToOE("png");', "*");
|
||
|
||
// 接收(验证 origin)
|
||
window.addEventListener("message", (event) => {
|
||
if (event.origin !== "https://www.photopea.com") return;
|
||
// 处理...
|
||
});
|
||
```
|
||
|
||
### 5.4 图像加载(JavaScript)
|
||
```javascript
|
||
// ⚠️ 关键:加 &channel=rgb 获取纯 RGB
|
||
const response = await fetch(`/view?filename=${imagePath}&type=input&channel=rgb`);
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 待验证的技术点
|
||
|
||
### 高优先级(决定方案)
|
||
- [ ] **P1**: Photopea 能否单独导出蒙版?
|
||
- [ ] **P2**: Photopea 能否同时加载原图 + 蒙版?
|
||
|
||
**验证方法**:
|
||
```javascript
|
||
// 在 Photopea 控制台测试
|
||
app.activeDocument.activeLayer.mask; // 返回 null(已知限制)
|
||
|
||
// Workaround
|
||
doc.activeChannel = layer.mask; // 可能工作
|
||
```
|
||
|
||
**备选方案**:
|
||
- 如果不能导出蒙版 → 用户用其他方式创建(系统工具/上传)
|
||
|
||
---
|
||
|
||
## 7. 参考代码位置
|
||
|
||
| 功能 | 参考文件 | 路径 |
|
||
|------|---------|------|
|
||
| 选区框绘制 | `mask-rect-area.js` | `E:\ComfyUI\custom_nodes\ComfyUI-Impact-Pack\js\` |
|
||
| 图像加载 | `nodes.py` (LoadImage) | `E:\ComfyUI\nodes.py` (line 1702) |
|
||
| 上传 API | `server.py` | `E:\ComfyUI\server.py` |
|
||
|
||
---
|
||
|
||
## 8. 常见错误
|
||
|
||
### 错误 1:没有蒙版
|
||
```python
|
||
# ❌ 错误:返回全黑蒙版
|
||
mask = torch.zeros((64,64), dtype=torch.float32, device="cpu")
|
||
|
||
# ✅ 正确:报错
|
||
raise ValueError("请先创建蒙版(局部重绘需要蒙版)")
|
||
```
|
||
|
||
### 错误 2:选区小于遮罩
|
||
```python
|
||
# ❌ 错误:静默修正
|
||
region_width = max(region_width, mask_width)
|
||
|
||
# ✅ 正确:报错
|
||
if region_width < mask_width:
|
||
raise ValueError("选区必须 >= 遮罩")
|
||
```
|
||
|
||
### 错误 3:Photopea 消息验证
|
||
```javascript
|
||
// ❌ 错误:不验证 origin
|
||
window.addEventListener("message", handler);
|
||
|
||
// ✅ 正确:验证 origin
|
||
window.addEventListener("message", (event) => {
|
||
if (event.origin !== "https://www.photopea.com") return;
|
||
// 处理...
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 测试清单
|
||
|
||
### 后端测试
|
||
- [ ] 上传带 Alpha 的 PNG → 正确提取 MASK
|
||
- [ ] 上传不带 Alpha 的 PNG → 报错
|
||
- [ ] padding = 64 → 选区正确计算
|
||
- [ ] 拖动选区后 → 坐标正确传递
|
||
|
||
### 前端测试
|
||
- [ ] 节点显示遮罩预览(半透明红)
|
||
- [ ] 节点显示选区框(橙色虚线)
|
||
- [ ] 拖动选区框 → 流畅
|
||
- [ ] 调整 padding → 选区自动重算
|
||
|
||
### Photopea 测试
|
||
- [ ] 打开 Photopea → 图像加载
|
||
- [ ] 编辑后保存 → 图像同步
|
||
- [ ] 导出蒙版 → 验证可行性
|
||
|
||
---
|
||
|
||
## 10. 问题反馈
|
||
|
||
遇到问题?
|
||
|
||
1. **技术细节** → 查看 `docs/HANDOFF.md`(完整文档)
|
||
2. **实现方案** → 查看 `docs/implementation-plan.md`(详细代码)
|
||
3. **Photopea 问题** → 查看 `docs/technical-validation.md`(验证清单)
|
||
4. **历史记录** → 查看 `DEVLOG.md`(开发日志)
|
||
|
||
---
|
||
|
||
*创建时间:2026-03-02*
|
||
*5 分钟快速了解,详细文档见 `docs/HANDOFF.md`*
|