主要更改: - 重写前端 extension.js,完全自己控制图像渲染 - 去掉 nodes.py 的 image_upload,避免双图像问题 - 实现选区框绘制(橙色)+ 遮罩区域显示(红色) - 实现选区框拖动功能 - 修复 onMouseDown 参数格式: (e, pos, canvas) - 更新文档和开发日志
6.2 KiB
6.2 KiB
快速开始指南
给接手团队的 5 分钟快速了解
1. 这个项目是做什么的?
一句话:ComfyUI 局部重绘的增强编辑器
三个功能:
- 图像编辑:集成 Photopea(在线 PS),实现液化、仿制图章等功能
- 蒙版编辑:在 Photopea 里画复杂的蒙版(羽化、透明度等)
- 选区调整:手动拖动参考区域位置(原有功能只能中心扩散)
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 天)
# 修改 nodes.py
1. 实现 INPUT_TYPES(image + padding 参数)
2. 实现 process 函数:
- 加载图像
- 提取 Alpha → MASK
- 计算遮罩边界
- 计算选区 = 遮罩边界 + padding
- 输出 region_*
3. 测试:上传带 Alpha 的 PNG → 验证输出
第二步:选区 UI(2 天)
# 创建 region-box-editor.js
参考:ComfyUI-Impact-Pack/js/mask-rect-area.js
功能:
1. 在节点 Canvas 上绘制遮罩(半透明红)
2. 在节点 Canvas 上绘制选区框(橙色虚线)
3. 实现鼠标拖动选区框
4. 同步坐标到 hidden widget
第三步:Photopea 集成(2-3 天)
# 创建 photopea-modal.js + photopea-bridge.js
功能:
1. 右键菜单 → 打开全屏 Photopea
2. 加载图像到 Photopea
3. 导出图像回传
4. ⚠️ 导出蒙版(待验证,可能无法实现)
5. 技术要点速查
5.1 蒙版提取(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)
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)
// 发送
photopeaWindow.postMessage('app.activeDocument.saveToOE("png");', "*");
// 接收(验证 origin)
window.addEventListener("message", (event) => {
if (event.origin !== "https://www.photopea.com") return;
// 处理...
});
5.4 图像加载(JavaScript)
// ⚠️ 关键:加 &channel=rgb 获取纯 RGB
const response = await fetch(`/view?filename=${imagePath}&type=input&channel=rgb`);
6. 待验证的技术点
高优先级(决定方案)
- P1: Photopea 能否单独导出蒙版?
- P2: Photopea 能否同时加载原图 + 蒙版?
验证方法:
// 在 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:没有蒙版
# ❌ 错误:返回全黑蒙版
mask = torch.zeros((64,64), dtype=torch.float32, device="cpu")
# ✅ 正确:报错
raise ValueError("请先创建蒙版(局部重绘需要蒙版)")
错误 2:选区小于遮罩
# ❌ 错误:静默修正
region_width = max(region_width, mask_width)
# ✅ 正确:报错
if region_width < mask_width:
raise ValueError("选区必须 >= 遮罩")
错误 3:Photopea 消息验证
// ❌ 错误:不验证 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. 问题反馈
遇到问题?
- 技术细节 → 查看
docs/HANDOFF.md(完整文档) - 实现方案 → 查看
docs/implementation-plan.md(详细代码) - Photopea 问题 → 查看
docs/technical-validation.md(验证清单) - 历史记录 → 查看
DEVLOG.md(开发日志)
创建时间:2026-03-02
5 分钟快速了解,详细文档见 docs/HANDOFF.md