Fix manual region reset and allow no-mask pass-through

This commit is contained in:
InpaintRegionEditor Dev
2026-07-26 10:37:48 +08:00
parent 4ba01f56ba
commit d9e2d7d698
4 changed files with 66 additions and 15 deletions
+9
View File
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.6.0] - 2026-03-04
### Fixed
- **保留手动调整的参考区**:图像重新加载、关闭编辑器窗口后,优先恢复 `region_coords` 中保存的手动选区,不再重置回 padding 计算值
- **padding 变化时同步 widget**:调整 padding 后同步更新 `region_coords`,保证前端显示与后端输出一致
### Changed
- **允许无蒙版运行**:没有 Alpha 通道或蒙版为空时不再报错,输出空蒙版并把参考区设为整张图像,使节点可作为纯图像输入使用
## [1.5.0] - 2026-03-04
### Added
+14 -10
View File
@@ -75,13 +75,8 @@ class InpaintRegionEditor:
mask = np.array(img.getchannel("A")).astype(np.float32) / 255.0
mask = 1.0 - mask # ComfyUI 约定:1=遮罩区域
else:
raise ValueError(
"Please create a mask first (inpainting requires a mask)\n"
"You can create one by:\n"
"1. Upload a PNG with Alpha channel\n"
"2. Edit in Photopea and save\n"
"3. Use ComfyUI's built-in mask editor"
)
# 没有 Alpha 通道时允许作为纯图像输入,输出空蒙版
mask = np.zeros((img_height, img_width), dtype=np.float32)
# 计算蒙版边界框
mask_bounds = self._calculate_mask_bounds(mask)
@@ -94,11 +89,20 @@ class InpaintRegionEditor:
except:
pass
if not region_rect:
# 没有有效蒙版时直接返回全图作为参考区,不再强制要求绘制蒙版
has_mask = mask_bounds["width"] > 0 and mask_bounds["height"] > 0
if not has_mask:
region_rect = {
"x": 0,
"y": 0,
"width": img_width,
"height": img_height,
}
elif not region_rect:
region_rect = self._calculate_region_rect(mask_bounds, padding)
# 验证
if (
# 只有在有蒙版时才验证选区必须包含蒙版
if has_mask and (
region_rect["width"] < mask_bounds["width"]
or region_rect["height"] < mask_bounds["height"]
):
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui-inpaint-region-editor"
description = "A ComfyUI custom node for inpainting workflows with integrated Photopea editing and adjustable region selection."
version = "1.5.1"
version = "1.6.0"
license = { file = "LICENSE" }
dependencies = []
+42 -4
View File
@@ -665,6 +665,14 @@ function closePhotopeaMaskModal() {
const nodeImageData = new Map(); // 存储每个节点的图像和选区数据
// 检查选区是否包含蒙版边界框
function regionContainsMask(region, mask) {
return region.x <= mask.x &&
region.y <= mask.y &&
region.x + region.width >= mask.x + mask.width &&
region.y + region.height >= mask.y + mask.height;
}
// 约束选区:框住蒙版且不超出图像边界
function constrainRegion(data, padding) {
if (!data.hasMask || !data.maskBounds) return;
@@ -773,11 +781,31 @@ async function loadImageAndDetectMask(node, imageName) {
if (hasMask && minX <= maxX && minY <= maxY) {
data.maskBounds = { x: minX, y: minY, width: maxX - minX + 1, height: maxY - minY + 1 };
// 使用统一的约束函数计算选区
constrainRegion(data, padding);
// 同步到 region_coords widget
// 优先恢复用户手动调整过的选区(如果仍然包含当前蒙版)
// 避免图像重新加载、关闭编辑器窗口等操作把手动调整重置回 padding 计算值
const coordsWidget = node.widgets?.find(w => w.name === "region_coords");
let restored = false;
if (coordsWidget && coordsWidget.value && coordsWidget.value !== "{}") {
try {
const saved = JSON.parse(coordsWidget.value);
if (saved && saved.width > 0 && saved.height > 0 &&
regionContainsMask(saved, data.maskBounds)) {
data.regionX = saved.x;
data.regionY = saved.y;
data.regionWidth = saved.width;
data.regionHeight = saved.height;
restored = true;
}
} catch (e) {}
}
if (!restored) {
// 使用统一的约束函数计算选区
constrainRegion(data, padding);
}
// 同步到 region_coords widget
if (coordsWidget) {
coordsWidget.value = JSON.stringify({
x: Math.round(data.regionX),
@@ -1339,6 +1367,16 @@ app.registerExtension({
if (data && data.hasMask && data.maskBounds) {
// 使用统一的约束函数重新计算选区
constrainRegion(data, v);
// 同步到 region_coords widget,保证显示与后端使用一致
const coordsWidget = node.widgets?.find(w => w.name === "region_coords");
if (coordsWidget) {
coordsWidget.value = JSON.stringify({
x: Math.round(data.regionX),
y: Math.round(data.regionY),
width: Math.round(data.regionWidth),
height: Math.round(data.regionHeight)
});
}
}
node.setDirtyCanvas(true);
};