Files
swan7-py-ComfyUI-ImageAlpha…/nodes.py
T
2025-11-20 09:41:43 +08:00

292 lines
11 KiB
Python

import torch
import numpy as np
from PIL import Image
class ImageAlphaCrop:
"""
自动裁剪图像Alpha通道的透明边框
可以处理四周透明和中间镂空的情况
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"images": ("IMAGE",),
"padding": ("INT", {
"default": 0,
"min": 0,
"max": 100,
"step": 1,
"display": "number"
}),
"alpha_threshold": ("FLOAT", {
"default": 0.01,
"min": 0.0,
"max": 1.0,
"step": 0.01,
"display": "number"
}),
},
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("cropped_images",)
FUNCTION = "crop_alpha"
CATEGORY = "Swan"
DESCRIPTION = "根据Alpha通道自动裁剪图像的透明边框,输出带Alpha通道的图像"
def crop_alpha(self, images, padding=0, alpha_threshold=0.01):
"""
根据Alpha通道裁剪图像的透明边框
Args:
images: 输入图像张量 [B, H, W, C]
padding: 裁剪后添加的额外边距
alpha_threshold: Alpha阈值,低于此值视为透明
Returns:
cropped_images: 裁剪后的带Alpha通道的图像
"""
batch_size, height, width, channels = images.shape
cropped_images = []
for i in range(batch_size):
image_tensor = images[i]
# 确保图像有Alpha通道
if channels < 4:
# 如果没有Alpha通道,创建一个完全不透明的Alpha
alpha = torch.ones((height, width, 1), dtype=image_tensor.dtype)
image_with_alpha = torch.cat([image_tensor, alpha], dim=2)
else:
image_with_alpha = image_tensor
# 转换为numpy数组进行边界检测
image_np = (image_with_alpha.cpu().numpy() * 255).astype(np.uint8)
alpha_channel = image_np[:, :, 3]
# 找到非透明区域的边界
non_transparent = alpha_channel > (alpha_threshold * 255)
if not np.any(non_transparent):
# 如果整个图像都是透明的,返回原图
cropped_images.append(image_with_alpha.unsqueeze(0))
continue
# 找到边界坐标
rows = np.any(non_transparent, axis=1)
cols = np.any(non_transparent, axis=0)
y_min, y_max = np.where(rows)[0][[0, -1]]
x_min, x_max = np.where(cols)[0][[0, -1]]
# 添加边距
y_min = max(0, y_min - padding)
y_max = min(height - 1, y_max + padding)
x_min = max(0, x_min - padding)
x_max = min(width - 1, x_max + padding)
# 裁剪图像
cropped_image_np = image_np[y_min:y_max+1, x_min:x_max+1]
# 转换回tensor (保持RGBA格式)
cropped_image_tensor = torch.from_numpy(cropped_image_np.astype(np.float32) / 255.0)
cropped_images.append(cropped_image_tensor.unsqueeze(0))
# 合并批次
cropped_images_batch = torch.cat(cropped_images, dim=0)
return (cropped_images_batch,)
class ImageAlphaCropAdvanced:
"""
高级Alpha通道裁剪,支持保持宽高比和指定输出尺寸
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"images": ("IMAGE",),
"padding": ("INT", {
"default": 0,
"min": 0,
"max": 100,
"step": 1
}),
"alpha_threshold": ("FLOAT", {
"default": 0.01,
"min": 0.0,
"max": 1.0,
"step": 0.01
}),
"target_width": ("INT", {
"default": 0,
"min": 0,
"max": 4096,
"step": 8
}),
"target_height": ("INT", {
"default": 0,
"min": 0,
"max": 4096,
"step": 8
}),
"keep_aspect_ratio": (["true", "false"], {
"default": "true"
}),
"background_color": (["transparent", "black", "white"], {
"default": "transparent"
}),
},
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("cropped_images",)
FUNCTION = "crop_alpha_advanced"
CATEGORY = "Swan"
DESCRIPTION = "高级Alpha通道裁剪,支持保持宽高比和调整尺寸"
def crop_alpha_advanced(self, images, padding=0, alpha_threshold=0.01,
target_width=0, target_height=0, keep_aspect_ratio="true",
background_color="transparent"):
"""
高级Alpha通道裁剪功能
"""
batch_size, height, width, channels = images.shape
cropped_images = []
for i in range(batch_size):
image_tensor = images[i]
# 确保图像有Alpha通道
if channels < 4:
alpha = torch.ones((height, width, 1), dtype=image_tensor.dtype)
image_with_alpha = torch.cat([image_tensor, alpha], dim=2)
else:
image_with_alpha = image_tensor
# 转换为numpy数组进行边界检测
image_np = (image_with_alpha.cpu().numpy() * 255).astype(np.uint8)
alpha_channel = image_np[:, :, 3]
# 找到非透明区域的边界
non_transparent = alpha_channel > (alpha_threshold * 255)
if not np.any(non_transparent):
# 如果整个图像都是透明的,返回原图
cropped_images.append(image_with_alpha.unsqueeze(0))
continue
# 找到边界坐标
rows = np.any(non_transparent, axis=1)
cols = np.any(non_transparent, axis=0)
y_min, y_max = np.where(rows)[0][[0, -1]]
x_min, x_max = np.where(cols)[0][[0, -1]]
# 添加边距
y_min = max(0, y_min - padding)
y_max = min(height - 1, y_max + padding)
x_min = max(0, x_min - padding)
x_max = min(width - 1, x_max + padding)
# 裁剪图像
cropped_image_np = image_np[y_min:y_max+1, x_min:x_max+1]
crop_height, crop_width = cropped_image_np.shape[:2]
# 调整尺寸(如果需要)
if target_width > 0 and target_height > 0:
if keep_aspect_ratio == "true":
# 保持宽高比调整尺寸
scale = min(target_width / crop_width, target_height / crop_height)
new_width = int(crop_width * scale)
new_height = int(crop_height * scale)
# 使用PIL进行高质量缩放
pil_image = Image.fromarray(cropped_image_np, mode='RGBA')
resized_pil = pil_image.resize((new_width, new_height), Image.LANCZOS)
# 如果需要,创建带背景的图像
if background_color != "transparent" and new_width != target_width or new_height != target_height:
background_img = Image.new('RGBA', (target_width, target_height),
(0, 0, 0, 0) if background_color == "transparent" else
(255, 255, 255, 255) if background_color == "white" else
(0, 0, 0, 255))
# 将缩放后的图像居中放置
x_offset = (target_width - new_width) // 2
y_offset = (target_height - new_height) // 2
background_img.paste(resized_pil, (x_offset, y_offset), resized_pil)
resized_pil = background_img
resized_np = np.array(resized_pil).astype(np.float32) / 255.0
cropped_image_tensor = torch.from_numpy(resized_np)
else:
# 直接调整到目标尺寸
pil_image = Image.fromarray(cropped_image_np, mode='RGBA')
resized_pil = pil_image.resize((target_width, target_height), Image.LANCZOS)
resized_np = np.array(resized_pil).astype(np.float32) / 255.0
cropped_image_tensor = torch.from_numpy(resized_np)
else:
# 不调整尺寸,直接转换
cropped_image_tensor = torch.from_numpy(cropped_image_np.astype(np.float32) / 255.0)
cropped_images.append(cropped_image_tensor.unsqueeze(0))
# 合并批次
cropped_images_batch = torch.cat(cropped_images, dim=0)
return (cropped_images_batch,)
class ImageAddAlphaChannel:
"""
为RGB图像添加Alpha通道
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"images": ("IMAGE",),
"alpha_value": ("FLOAT", {
"default": 1.0,
"min": 0.0,
"max": 1.0,
"step": 0.01
}),
},
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("images_with_alpha",)
FUNCTION = "add_alpha"
CATEGORY = "Swan
DESCRIPTION = "为RGB图像添加Alpha通道"
def add_alpha(self, images, alpha_value=1.0):
batch_size, height, width, channels = images.shape
if channels >= 4:
# 如果已经有Alpha通道,直接返回
return (images,)
# 创建Alpha通道
alpha = torch.full((batch_size, height, width, 1), alpha_value, dtype=images.dtype)
# 合并RGB和Alpha
if channels == 3:
images_with_alpha = torch.cat([images, alpha], dim=3)
else:
# 如果是单通道或双通道,先转换为RGB
rgb_images = images.repeat(1, 1, 1, 3) if channels == 1 else images[:, :, :, :3]
images_with_alpha = torch.cat([rgb_images, alpha], dim=3)
return (images_with_alpha,)