Enhance image resizing functionality by adding mask generation for all resizing methods. Updated the resize_image function to return a mask alongside the resized image, ensuring better handling of image boundaries. Improved the calculate_mask function to support various alignment modes and ensure accurate mask creation. This update enhances the overall image processing capabilities.

This commit is contained in:
fujammy
2024-12-26 21:29:45 +08:00
parent 38ed66c9bb
commit d8bb35f328
11 changed files with 126 additions and 86 deletions
+4
View File
@@ -0,0 +1,4 @@
# Python cache files
__pycache__/
*.py[cod]
*$py.class
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+122 -86
View File
@@ -44,70 +44,81 @@ def resize_image(image, target_width, target_height, method='contain', backgroun
y_offset = (target_height - new_height) // 2
padded_img.paste(resized_img, (x_offset, y_offset))
# 在返回之前计算mask
# 生成mask
mask = calculate_mask((img_width, img_height), (target_width, target_height), method)
return np.array(padded_img).astype(np.float32)/255.0, mask, new_width, new_height
return np.array(padded_img).astype(np.float32)/255.0, mask, target_width, target_height
elif method == 'cover':
# Cover: 缩放图像以覆盖目标尺寸,保持宽高比,可能裁剪
img_ratio = img_width / img_height
target_ratio = target_width / target_height
if img_ratio > target_ratio:
img_ratio = img_width / img_height
target_ratio = target_width / target_height
if img_ratio > target_ratio:
new_height = target_height
new_width = int(target_height * img_ratio)
else:
else:
new_width = target_width
new_height = int(target_width / img_ratio)
resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
x_offset = (new_width - target_width) // 2
y_offset = (new_height - target_height) // 2
cropped_img = resized_img.crop((x_offset, y_offset, x_offset + target_width, y_offset + target_height))
return np.array(cropped_img).astype(np.float32)/255.0 , target_width, target_height
resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
x_offset = (new_width - target_width) // 2
y_offset = (new_height - target_height) // 2
cropped_img = resized_img.crop((x_offset, y_offset, x_offset + target_width, y_offset + target_height))
# 生成mask
mask = calculate_mask((img_width, img_height), (target_width, target_height), method)
return np.array(cropped_img).astype(np.float32)/255.0, mask, target_width, target_height
elif method == 'fill':
# Fill: 拉伸图像以填充目标尺寸,忽略宽高比,可能变形
resized_img = img.resize((target_width, target_height), Image.Resampling.LANCZOS)
return np.array(resized_img).astype(np.float32)/255.0, target_width, target_height
# 生成mask
mask = calculate_mask((img_width, img_height), (target_width, target_height), method)
return np.array(resized_img).astype(np.float32)/255.0, mask, target_width, target_height
elif method == 'inside':
# Inside: 和 contain 相同,保持宽高比,缩小或不改变图像使其完全适合容器
img_ratio = img_width / img_height
target_ratio = target_width / target_height
if img_ratio > target_ratio:
# Inside: 和 contain 相同,保持宽高比,缩小或不改变图像使其完全适合容器
img_ratio = img_width / img_height
target_ratio = target_width / target_height
if img_ratio > target_ratio:
new_width = target_width
new_height = int(target_width / img_ratio)
else:
else:
new_height = target_height
new_width = int(target_height * img_ratio)
resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
# 解析背景颜色
try:
color = tuple(int(background_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
except ValueError:
color = (0, 0, 0) # 默认黑色
padded_img = Image.new('RGB', (target_width, target_height), color)
x_offset = (target_width - new_width) // 2
y_offset = (target_height - new_height) // 2
padded_img.paste(resized_img, (x_offset, y_offset))
return np.array(padded_img).astype(np.float32)/255.0, target_width, target_height
resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
# 解析背景颜色
try:
color = tuple(int(background_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
except ValueError:
color = (0, 0, 0) # 默认黑色
padded_img = Image.new('RGB', (target_width, target_height), color)
x_offset = (target_width - new_width) // 2
y_offset = (target_height - new_height) // 2
padded_img.paste(resized_img, (x_offset, y_offset))
# 生成mask
mask = calculate_mask((img_width, img_height), (target_width, target_height), method)
return np.array(padded_img).astype(np.float32)/255.0, mask, target_width, target_height
elif method == 'outside':
# Outside: 和 cover 相同,保持宽高比,放大或不改变图像使其完全覆盖容器
img_ratio = img_width / img_height
target_ratio = target_width / target_height
if img_ratio > target_ratio:
new_height = target_height
new_width = int(target_height * img_ratio)
else:
new_width = target_width
new_height = int(target_width / img_ratio)
resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
x_offset = (new_width - target_width) // 2
y_offset = (new_height - target_height) // 2
cropped_img = resized_img.crop((x_offset, y_offset, x_offset + target_width, y_offset + target_height))
return np.array(cropped_img).astype(np.float32)/255.0 , target_width, target_height
# Outside: 和 cover 相同,保持宽高比,放大或不改变图像使其完全覆盖容器
img_ratio = img_width / img_height
target_ratio = target_width / target_height
if img_ratio > target_ratio:
new_height = target_height
new_width = int(target_height * img_ratio)
else:
new_width = target_width
new_height = int(target_width / img_ratio)
resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
x_offset = (new_width - target_width) // 2
y_offset = (new_height - target_height) // 2
cropped_img = resized_img.crop((x_offset, y_offset, x_offset + target_width, y_offset + target_height))
# 生成mask
mask = calculate_mask((img_width, img_height), (target_width, target_height), method)
return np.array(cropped_img).astype(np.float32)/255.0, mask, target_width, target_height
def pad_image(image, target_width, target_height, position='center', background_color='#000000'):
"""Pad an image to the target dimensions with specified background color."""
@@ -247,72 +258,97 @@ def create_outline(image, background_color):
return np.array(outlined).astype(np.float32) / 255.0
def calculate_mask(original_size, target_size, extend_mode, feather=0):
"""计算填充区域的mask
"""计算填充区域的mask,白色(1)表示图像区域,黑色(0)表示背景/填充区域
Args:
original_size: (width, height) 原始图像尺寸
target_size: (width, height) 目标尺寸
extend_mode: 扩展模式
feather: 羽化程度
Returns:
torch.Tensor: mask张量
torch.Tensor: mask张量,1表示图像区域,0表示背景/填充区域
"""
orig_w, orig_h = original_size
target_w, target_h = target_size
# 创建目标尺寸的mask,初始化为0(黑色,表示填充区域)
# 创建目标尺寸的mask(默认全黑,表示背景)
mask = torch.zeros((target_h, target_w))
if extend_mode in ["contain", "inside"]:
# 计算缩放后的尺寸
if extend_mode == "fill":
mask.fill_(1.0)
elif extend_mode in ["cover", "outside"]:
mask.fill_(1.0)
elif extend_mode in ["contain", "inside"]:
ratio = min(target_w/orig_w, target_h/orig_h)
new_w = int(orig_w * ratio)
new_h = int(orig_h * ratio)
# 计算偏移量(居中)
x_offset = (target_w - new_w) // 2
y_offset = (target_h - new_h) // 2
# 设置非填充区域为1(白色)
mask[y_offset:y_offset + new_h, x_offset:x_offset + new_w] = 1
mask[y_offset:y_offset + new_h, x_offset:x_offset + new_w] = 1.0
elif extend_mode in ["top", "bottom", "left", "right", "center"]:
# 计算偏移量
if extend_mode == "top":
x_offset = (target_w - orig_w) // 2
# 1. 首先判断原始图像是否需要缩放
if orig_w <= target_w and orig_h <= target_h:
# 图像小于或等于目标尺寸,不需要缩放
new_w = orig_w
new_h = orig_h
else:
# 需要缩放,根据对齐方式选择缩放策略
if extend_mode in ["left", "right"]:
# 左右对齐优先考虑高度
ratio = target_h / orig_h
new_h = target_h
new_w = int(orig_w * ratio)
# 如果宽度超出,则按宽度重新计算
if new_w > target_w:
ratio = target_w / orig_w
new_w = target_w
new_h = int(orig_h * ratio)
elif extend_mode in ["top", "bottom"]:
# 上下对齐优先考虑宽度
ratio = target_w / orig_w
new_w = target_w
new_h = int(orig_h * ratio)
# 如果高度超出,则按高度重新计算
if new_h > target_h:
ratio = target_h / orig_h
new_h = target_h
new_w = int(orig_w * ratio)
else: # center
# 居中对齐选择最适合的缩放比例
ratio = min(target_w / orig_w, target_h / orig_h)
new_w = int(orig_w * ratio)
new_h = int(orig_h * ratio)
# 2. 根据对齐方式计算偏移量
if extend_mode == "center":
x_offset = (target_w - new_w) // 2
y_offset = (target_h - new_h) // 2
elif extend_mode == "top":
x_offset = (target_w - new_w) // 2
y_offset = 0
elif extend_mode == "bottom":
x_offset = (target_w - orig_w) // 2
y_offset = target_h - orig_h
x_offset = (target_w - new_w) // 2
y_offset = target_h - new_h
elif extend_mode == "left":
x_offset = 0
y_offset = (target_h - orig_h) // 2
elif extend_mode == "right":
x_offset = target_w - orig_w
y_offset = (target_h - orig_h) // 2
else: # center
x_offset = (target_w - orig_w) // 2
y_offset = (target_h - orig_h) // 2
# 设置非填充区域为1(白色)
mask[y_offset:y_offset + orig_h, x_offset:x_offset + orig_w] = 1
# 应用羽化效果
if feather > 0:
# 将mask转换为适合卷积的格式 [B, C, H, W]
mask = mask.unsqueeze(0).unsqueeze(0)
# 创建高斯核进行羽化
kernel_size = 2 * feather + 1
sigma = feather / 3
# 应用高斯模糊
mask = F.gaussian_blur(mask, kernel_size=(kernel_size, kernel_size), sigma=(sigma, sigma))
# 转换回原始格式
mask = mask.squeeze(0).squeeze(0)
# 确保mask值在0-1范围内
mask = torch.clamp(mask, 0, 1)
y_offset = (target_h - new_h) // 2
else: # right
x_offset = target_w - new_w
y_offset = (target_h - new_h) // 2
# 3. 确保所有值都在有效范围内
x_offset = max(0, min(x_offset, target_w - new_w))
y_offset = max(0, min(y_offset, target_h - new_h))
new_w = min(new_w, target_w)
new_h = min(new_h, target_h)
# 4. 生成mask
if new_w > 0 and new_h > 0:
mask[y_offset:y_offset + new_h, x_offset:x_offset + new_w] = 1.0
return mask
@@ -356,7 +392,7 @@ class ImageResolutionAdjuster:
"min_height": ("INT", {"default": 640, "min": 1, "max": 8192, "step": 1}),
"background_color": ("STRING", {"default": "#000000", "multiline": False}),
"add_outline": ("BOOLEAN", {"default": False}),
"feather": ("INT", {"default": 0, "min": 0, "max": 10, "step": 1}),
# "feather": ("INT", {"default": 0, "min": 0, "max": 10, "step": 1}),
},
"hidden": {"color_widget": "COMBO"}
}