raubatz's picture
download
raw
8.98 kB
from comfy_extras.nodes_mask import ImageCompositeMasked
import torch
import torch.nn.functional as F
class ImageCompositeWatermark(ImageCompositeMasked):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"destination": ("IMAGE",),
"watermark": ("IMAGE",),
"position": (["top_right", "top_center", "top_left", "bottom_right", "bottom_center", "bottom_left"], {"default": "bottom_right"}),
"resize_ratio": ("FLOAT", {"default": 1, "min": 0, "max": 10, "step": 0.05}),
"margin": ("INT", {"default": 0, "min": 0, "max": 1000, "step": 1}),
},
"optional": {
"mask": ("MASK",),
"enabled": ("BOOLEAN", {"default": True, "label_on": "enabled", "label_off": "disabled"}),
"invert_mask": ("BOOLEAN", {"default": False, "label_on": "enabled", "label_off": "disabled"}),
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "composite_watermark"
CATEGORY = "utils/image"
def composite_watermark(self, destination, watermark, position, resize_ratio, margin, mask=None, enabled=True, invert_mask=False):
if not enabled:
return (destination,)
dest_h, dest_w = destination.shape[1:3]
water_h, water_w = watermark.shape[1:3]
scale = 1
if water_h > dest_h or water_w > dest_w:
# 计算需要的缩放比例
scale_h = dest_h / water_h
scale_w = dest_w / water_w
scale = min(scale_h, scale_w)
if resize_ratio != 1 or scale != 1:
watermark = torch.nn.functional.interpolate(
watermark.movedim(-1, 1), scale_factor=resize_ratio * scale, mode="bicubic", antialias=True).movedim(1, -1).clamp(0.0, 1.0)
if mask is not None:
mask = torch.nn.functional.interpolate(mask.unsqueeze(
0), scale_factor=resize_ratio * scale, mode="bicubic", antialias=True).squeeze(0).clamp(0.0, 1.0)
water_h, water_w = watermark.shape[1:3]
# 计算y坐标
if position.startswith("top"):
y = margin
else: # bottom positions
y = dest_h - water_h - margin
x = 0
# 根据position计算x坐标
if position.endswith("left"):
x = margin
elif position.endswith("center"):
x = (dest_w - water_w) // 2
elif position.endswith("right"):
x = dest_w - water_w - margin
if invert_mask and mask is not None:
mask = 1.0 - mask
return self.composite(destination, watermark, x, y, False, mask)
class ImageTransition:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"first_image": ("IMAGE",),
"last_image": ("IMAGE",),
"frames": ("INT", {"default": 24, "min": 2, "max": 120, "step": 1}),
"transition_type": (["uniform", "smooth"], {"default": "uniform"}),
"smooth_effect": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 10.0, "step": 0.1}),
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "generate_transition"
CATEGORY = "utils/image"
def generate_transition(self, first_image, last_image, frames, transition_type="uniform", smooth_effect=1.0):
# 生成插值权重
if transition_type == "uniform":
weights = torch.linspace(0, 1, frames)
else: # sigmoid
x = torch.linspace(-20, 20, frames)
weights = torch.sigmoid(x / smooth_effect)
# 创建输出张量列表
output_frames = []
# 生成过渡帧
for w in weights:
# 使用权重进行插值
transition_frame = first_image * (1 - w) + last_image * w
output_frames.append(transition_frame)
# 将所有帧拼接在一起
result = torch.cat(output_frames, dim=0)
return (result,)
class ImageMaskColorAverage:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE",),
"mask": ("MASK",),
}
}
RETURN_TYPES = ("INT", "STRING")
RETURN_NAMES = ("COLOR_DEC", "COLOR_HEX")
FUNCTION = "calculate_average_color"
CATEGORY = "utils/image"
def calculate_average_color(self, image, mask):
# 确保mask是二维的
if len(mask.shape) > 2:
mask = mask.squeeze()
# 将mask扩展为与图像相同的通道数
expanded_mask = mask.unsqueeze(-1).expand(-1, -1, 3)
# 计算mask区域的像素总数
pixel_count = torch.sum(mask)
if pixel_count == 0:
# 如果mask中没有选中区域,返回黑色
return (0, "#000000")
# 计算mask区域的颜色总和
masked_image = image * expanded_mask.unsqueeze(0)
color_sum = torch.sum(masked_image, dim=[0, 1, 2])
# 计算平均颜色
avg_color = color_sum / pixel_count
# 转换为0-255范围的整数
r = int(avg_color[0].item() * 255)
g = int(avg_color[1].item() * 255)
b = int(avg_color[2].item() * 255)
# 生成十六进制颜色代码
hex_color = f"#{r:02x}{g:02x}{b:02x}"
# 计算十进制颜色值 (R*65536 + G*256 + B)
dec_color = r * 65536 + g * 256 + b
return (dec_color, hex_color)
class ImagesConcanateToGrid:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"image1": ("IMAGE",),
"direction": (
['right',
'down',
],
{
"default": 'right'
}),
"dimension_number": ("INT", {"default": 2, "min": 1, "max": 20, "step": 1}),
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "concanate"
CATEGORY = "utils/image"
def concanate(self, image1, direction='right', dimension_number=2):
# 检查图像维度,如果不是4维直接返回
if len(image1.shape) != 4:
return (image1,)
batch_size = image1.shape[0]
# 如果批次大小为1,直接返回
if batch_size == 1:
return (image1,)
# 将批次图像分离为单独的图像列表
images = [image1[i:i+1] for i in range(batch_size)]
# 根据方向计算行数和列数
if direction == 'right':
cols = dimension_number
rows = (batch_size + cols - 1) // cols # 向上取整
else: # direction == 'down'
rows = dimension_number
cols = (batch_size + rows - 1) // rows # 向上取整
# 创建网格来存放图像
grid_rows = []
for row in range(rows):
row_images = []
for col in range(cols):
idx = row * cols + col if direction == 'right' else col * rows + row
if idx < len(images):
row_images.append(images[idx])
else:
# 如果没有足够的图像,用黑色图像填充
black_image = torch.zeros_like(images[0])
row_images.append(black_image)
# 水平拼接当前行的图像
if row_images:
row_concat = torch.cat(row_images, dim=2) # 在宽度维度拼接
grid_rows.append(row_concat)
# 垂直拼接所有行
if grid_rows:
result = torch.cat(grid_rows, dim=1) # 在高度维度拼接
else:
result = image1
return (result,)
class NeedImageSizeAndCount:
@classmethod
def INPUT_TYPES(s):
return {"required": {"image": ("IMAGE",)}}
RETURN_TYPES = ("INT", "INT", "INT")
RETURN_NAMES = ("width", "height", "count")
FUNCTION = "get_image_size_and_count"
CATEGORY = "utils/image"
def get_image_size_and_count(self, image):
return (image.shape[2], image.shape[1], image.shape[0])
NODE_CLASS_MAPPINGS = {
#image
"ImageCompositeWatermark": ImageCompositeWatermark,
"ImageTransition": ImageTransition,
"ImageMaskColorAverage": ImageMaskColorAverage,
"ImagesConcanateToGrid": ImagesConcanateToGrid,
"NeedImageSizeAndCount": NeedImageSizeAndCount,
}
NODE_DISPLAY_NAME_MAPPINGS = {
# Image
"ImageCompositeWatermark": "Image Composite Watermark",
"ImageTransition": "Image Transition",
"ImageMaskColorAverage": "Image Mask Color Average",
"ImagesConcanateToGrid": "Images Concanate To Grid",
"NeedImageSizeAndCount": "get Image Size And Count by utils",
}

Xet Storage Details

Size:
8.98 kB
·
Xet hash:
9b72bfdf40b42b30cda1da4e2375f631c8916c005de682145781595106926a16

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.