177 lines
7.1 KiB
Markdown
177 lines
7.1 KiB
Markdown
|
|
# Documentation
|
|
- Class name: Image Overlay
|
|
- Category: Efficiency Nodes/Image
|
|
- Output node: False
|
|
|
|
Image Overlay节点用于将一个图像叠加到另一个图像上,并提供多种自定义选项,如调整大小、旋转、透明度调整和可选的遮罩。该节点通过对叠加图像应用变换和混合技术,然后将其与基础图像合并,从而实现创建复合图像。
|
|
|
|
# Input types
|
|
## Required
|
|
- base_image
|
|
- 作为叠加图像应用目标的基础图像。它充当复合图像的背景。
|
|
- Comfy dtype: IMAGE
|
|
- Python dtype: torch.Tensor
|
|
- overlay_image
|
|
- 要叠加到基础图像上的图像。在叠加之前,可以调整此图像的大小、旋转角度和透明度。
|
|
- Comfy dtype: IMAGE
|
|
- Python dtype: torch.Tensor
|
|
- overlay_resize
|
|
- 指定如何调整叠加图像的大小以适应基础图像,选项包括适应尺寸、按因子缩放或调整到特定的宽度和高度。
|
|
- Comfy dtype: COMBO[STRING]
|
|
- Python dtype: str
|
|
- resize_method
|
|
- 用于调整叠加图像大小的方法,影响调整后图像的质量和外观。
|
|
- Comfy dtype: COMBO[STRING]
|
|
- Python dtype: str
|
|
- rescale_factor
|
|
- 当选择"按rescale_factor调整大小"选项时,叠加图像的缩放因子。
|
|
- Comfy dtype: FLOAT
|
|
- Python dtype: float
|
|
- width
|
|
- 当选择"调整到指定宽度和高度"选项时,叠加图像调整后的宽度。
|
|
- Comfy dtype: INT
|
|
- Python dtype: int
|
|
- height
|
|
- 当选择"调整到指定宽度和高度"选项时,叠加图像调整后的高度。
|
|
- Comfy dtype: INT
|
|
- Python dtype: int
|
|
- x_offset
|
|
- 叠加图像在基础图像上放置的水平偏移量。
|
|
- Comfy dtype: INT
|
|
- Python dtype: int
|
|
- y_offset
|
|
- 叠加图像在基础图像上放置的垂直偏移量。
|
|
- Comfy dtype: INT
|
|
- Python dtype: int
|
|
- rotation
|
|
- 在应用到基础图像之前,叠加图像旋转的角度(以度为单位)。
|
|
- Comfy dtype: INT
|
|
- Python dtype: float
|
|
- opacity
|
|
- 叠加图像的不透明度,允许创建透明叠加效果。
|
|
- Comfy dtype: FLOAT
|
|
- Python dtype: float
|
|
## Optional
|
|
- optional_mask
|
|
- 可选的遮罩,可应用于叠加图像,控制叠加图像不同部分的透明度。
|
|
- Comfy dtype: MASK
|
|
- Python dtype: Optional[torch.Tensor]
|
|
|
|
# Output types
|
|
- image
|
|
- 应用叠加后的基础图像,反映了所有指定的变换和调整。
|
|
- Comfy dtype: IMAGE
|
|
- Python dtype: torch.Tensor
|
|
|
|
|
|
## Usage tips
|
|
- Infra type: `GPU`
|
|
- Common nodes: unknown
|
|
|
|
|
|
## Source code
|
|
```python
|
|
class TSC_ImageOverlay:
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"base_image": ("IMAGE",),
|
|
"overlay_image": ("IMAGE",),
|
|
"overlay_resize": (["None", "Fit", "Resize by rescale_factor", "Resize to width & heigth"],),
|
|
"resize_method": (["nearest-exact", "bilinear", "area"],),
|
|
"rescale_factor": ("FLOAT", {"default": 1, "min": 0.01, "max": 16.0, "step": 0.1}),
|
|
"width": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 64}),
|
|
"height": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 64}),
|
|
"x_offset": ("INT", {"default": 0, "min": -48000, "max": 48000, "step": 10}),
|
|
"y_offset": ("INT", {"default": 0, "min": -48000, "max": 48000, "step": 10}),
|
|
"rotation": ("INT", {"default": 0, "min": -180, "max": 180, "step": 5}),
|
|
"opacity": ("FLOAT", {"default": 0, "min": 0, "max": 100, "step": 5}),
|
|
},
|
|
"optional": {"optional_mask": ("MASK",),}
|
|
}
|
|
|
|
RETURN_TYPES = ("IMAGE",)
|
|
FUNCTION = "apply_overlay_image"
|
|
CATEGORY = "Efficiency Nodes/Image"
|
|
|
|
def apply_overlay_image(self, base_image, overlay_image, overlay_resize, resize_method, rescale_factor,
|
|
width, height, x_offset, y_offset, rotation, opacity, optional_mask=None):
|
|
|
|
# Pack tuples and assign variables
|
|
size = width, height
|
|
location = x_offset, y_offset
|
|
mask = optional_mask
|
|
|
|
# Check for different sizing options
|
|
if overlay_resize != "None":
|
|
#Extract overlay_image size and store in Tuple "overlay_image_size" (WxH)
|
|
overlay_image_size = overlay_image.size()
|
|
overlay_image_size = (overlay_image_size[2], overlay_image_size[1])
|
|
if overlay_resize == "Fit":
|
|
h_ratio = base_image.size()[1] / overlay_image_size[1]
|
|
w_ratio = base_image.size()[2] / overlay_image_size[0]
|
|
ratio = min(h_ratio, w_ratio)
|
|
overlay_image_size = tuple(round(dimension * ratio) for dimension in overlay_image_size)
|
|
elif overlay_resize == "Resize by rescale_factor":
|
|
overlay_image_size = tuple(int(dimension * rescale_factor) for dimension in overlay_image_size)
|
|
elif overlay_resize == "Resize to width & heigth":
|
|
overlay_image_size = (size[0], size[1])
|
|
|
|
samples = overlay_image.movedim(-1, 1)
|
|
overlay_image = comfy.utils.common_upscale(samples, overlay_image_size[0], overlay_image_size[1], resize_method, False)
|
|
overlay_image = overlay_image.movedim(1, -1)
|
|
|
|
overlay_image = tensor2pil(overlay_image)
|
|
|
|
# Add Alpha channel to overlay
|
|
overlay_image = overlay_image.convert('RGBA')
|
|
overlay_image.putalpha(Image.new("L", overlay_image.size, 255))
|
|
|
|
# If mask connected, check if the overlay_image image has an alpha channel
|
|
if mask is not None:
|
|
# Convert mask to pil and resize
|
|
mask = tensor2pil(mask)
|
|
mask = mask.resize(overlay_image.size)
|
|
# Apply mask as overlay's alpha
|
|
overlay_image.putalpha(ImageOps.invert(mask))
|
|
|
|
# Rotate the overlay image
|
|
overlay_image = overlay_image.rotate(rotation, expand=True)
|
|
|
|
# Apply opacity on overlay image
|
|
r, g, b, a = overlay_image.split()
|
|
a = a.point(lambda x: max(0, int(x * (1 - opacity / 100))))
|
|
overlay_image.putalpha(a)
|
|
|
|
# Split the base_image tensor along the first dimension to get a list of tensors
|
|
base_image_list = torch.unbind(base_image, dim=0)
|
|
|
|
# Convert each tensor to a PIL image, apply the overlay, and then convert it back to a tensor
|
|
processed_base_image_list = []
|
|
for tensor in base_image_list:
|
|
# Convert tensor to PIL Image
|
|
image = tensor2pil(tensor)
|
|
|
|
# Paste the overlay image onto the base image
|
|
if mask is None:
|
|
image.paste(overlay_image, location)
|
|
else:
|
|
image.paste(overlay_image, location, overlay_image)
|
|
|
|
# Convert PIL Image back to tensor
|
|
processed_tensor = pil2tensor(image)
|
|
|
|
# Append to list
|
|
processed_base_image_list.append(processed_tensor)
|
|
|
|
# Combine the processed images back into a single tensor
|
|
base_image = torch.stack([tensor.squeeze() for tensor in processed_base_image_list])
|
|
|
|
# Return the edited base image
|
|
return (base_image,)
|
|
|
|
```
|