7.5 KiB
7.5 KiB
Documentation
- Class name:
ImageEffectsLensZoomBurst - Category:
image/effects/lens - Output node:
False
ImageEffectsLensZoomBurst节点用于为图像应用放大爆炸效果,通过动态缩放、旋转和可选的稳定处理来改变图像外观,从而创造出类似运动的视觉效果。该节点旨在通过模拟快速放大效果来增强视觉内容,常用于为图像特定区域添加戏剧性效果或焦点。
Input types
Required
images- 需要应用放大爆炸效果的输入图像。这些图像是变换的主要对象,效果参数将应用于其上以创造所需的视觉效果。
- Comfy dtype: IMAGE
- Python dtype: torch.Tensor
scale- 定义放大爆炸效果的缩放因子,决定了在效果应用过程中图像会被放大或缩小的程度。
- Comfy dtype: FLOAT
- Python dtype: float
samples- 指定创建放大爆炸效果时使用的样本数量,影响最终输出的平滑度和强度。
- Comfy dtype: INT
- Python dtype: int
position_x- 放大爆炸效果在图像中心的x坐标位置,允许针对特定图像区域进行有针对性的视觉强调。
- Comfy dtype: FLOAT
- Python dtype: int
position_y- 放大爆炸效果在图像中心的y坐标位置,允许针对特定图像区域进行有针对性的视觉强调。
- Comfy dtype: FLOAT
- Python dtype: int
rotation- 确定与放大效果一起应用于图像的旋转角度,为视觉效果增添动态扭转。
- Comfy dtype: FLOAT
- Python dtype: float
method- 应用放大爆炸效果的方法,可能影响效果的外观和性能。
- Comfy dtype: COMBO[STRING]
- Python dtype: str
stabilization- 指示是否应用稳定技术来减少潜在的模糊或失真效果,增强放大爆炸的视觉清晰度。
- Comfy dtype: COMBO[STRING]
- Python dtype: bool
Output types
image- 应用放大爆炸效果后的输出图像,展示了经过变换的视觉外观,呈现出预期的动态、类似运动的视觉效果。
- Comfy dtype: IMAGE
- Python dtype: torch.Tensor
Usage tips
- Infra type:
GPU - Common nodes: unknown
Source code
class ImageEffectsLensZoomBurst:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"images": ("IMAGE",),
"scale": ("FLOAT", {
"default": 1.5,
"min": 1.0,
"step": 0.01
}),
"samples": ("INT", {
"default": 100,
"min": 1,
}),
"position_x": ("FLOAT", {
"default": 0.5,
"max": 1.0,
"step": 0.01
}),
"position_y": ("FLOAT", {
"default": 0.5,
"max": 1.0,
"step": 0.01
}),
"rotation": ("FLOAT", {
"default": 0.0,
"min": 0.0,
"max": 360.0,
}),
"method": (["circle", "point"],),
"stabilization": (["true", "false"],),
},
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "node"
CATEGORY = "image/effects/lens"
# noinspection PyUnresolvedReferences
def zoom_burst(
self,
image,
scale,
samples,
position,
rotation,
method,
stabilization,
):
if scale < 1.0:
raise ValueError("Parameter scale can't be smaller then initial image size.")
h, w = image.shape[:2]
x = np.arange(w)
y = np.arange(h)
xx, yy = np.meshgrid(x, y)
cx = int(w * position[0])
cy = int(h * position[1])
if method == "circle":
d = np.sqrt((xx - cx) ** 2 + (yy - cy) ** 2)
max_d = np.sqrt((w / 2) ** 2 + (h / 2) ** 2)
map_x_up = (xx - d * (scale - 1.0) / max_d * (xx - cx) / samples).astype(np.float32)
map_y_up = (yy - d * (scale - 1.0) / max_d * (yy - cy) / samples).astype(np.float32)
map_x_down = (xx + d * (scale - 1.0) / max_d * (xx - cx) / samples).astype(np.float32)
map_y_down = (yy + d * (scale - 1.0) / max_d * (yy - cy) / samples).astype(np.float32)
elif method == "point":
map_x_up = (xx - (xx - cx) * (scale - 1.0) / samples).astype(np.float32)
map_y_up = (yy - (yy - cy) * (scale - 1.0) / samples).astype(np.float32)
map_x_down = (xx + (xx - cx) * (scale - 1.0) / samples).astype(np.float32)
map_y_down = (yy + (yy - cy) * (scale - 1.0) / samples).astype(np.float32)
else:
raise ValueError("Unsupported method.")
if rotation > 0.0:
angle_step = rotation / samples
rm_up = cv2.getRotationMatrix2D((cx, cy), angle_step, 1)
rm_down = cv2.getRotationMatrix2D((cx, cy), -angle_step, 1)
else:
vibration_angle = 1.0
vibration_step = vibration_angle / samples
rm_up_even = cv2.getRotationMatrix2D((cx, cy), vibration_step, 1)
rm_down_even = cv2.getRotationMatrix2D((cx, cy), -vibration_step, 1)
rm_up_odd = cv2.getRotationMatrix2D((cx, cy), -vibration_step, 1)
rm_down_odd = cv2.getRotationMatrix2D((cx, cy), vibration_step, 1)
for i in range(samples):
if stabilization:
tmp_up = cv2.remap(image, map_x_up, map_y_up, cv2.INTER_LINEAR)
tmp_down = cv2.remap(image, map_x_down, map_y_down, cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT)
if rotation > 0.0:
tmp_up = cv2.warpAffine(tmp_up, rm_up, (w, h), borderMode=cv2.BORDER_REFLECT)
tmp_down = cv2.warpAffine(tmp_down, rm_down, (w, h), borderMode=cv2.BORDER_REFLECT)
else:
if i % 2 == 0:
tmp_up = cv2.warpAffine(tmp_up, rm_up_even, (w, h), borderMode=cv2.BORDER_REFLECT)
tmp_down = cv2.warpAffine(tmp_down, rm_down_even, (w, h), borderMode=cv2.BORDER_REFLECT)
else:
tmp_up = cv2.warpAffine(tmp_up, rm_up_odd, (w, h), borderMode=cv2.BORDER_REFLECT)
tmp_down = cv2.warpAffine(tmp_down, rm_down_odd, (w, h), borderMode=cv2.BORDER_REFLECT)
image = cv2.addWeighted(tmp_up, 0.5, tmp_down, 0.5, 0)
else:
tmp = cv2.remap(image, map_x_up, map_y_up, cv2.INTER_LINEAR)
if rotation > 0.0:
rm = cv2.getRotationMatrix2D((cx, cy), angle_step, 1)
tmp = cv2.warpAffine(tmp, rm, (w, h), borderMode=cv2.BORDER_REFLECT)
else:
if i % 2 == 0:
tmp = cv2.warpAffine(tmp, rm_up_even, (w, h), borderMode=cv2.BORDER_REFLECT)
else:
tmp = cv2.warpAffine(tmp, rm_up_odd, (w, h), borderMode=cv2.BORDER_REFLECT)
image = cv2.addWeighted(tmp, 0.5, image, 0.5, 0)
return image
def node(self, images, scale, samples, position_x, position_y, rotation, method, stabilization):
tensor = images.clone().detach()
return (cv2_layer(tensor, lambda x: self.zoom_burst(
x, scale, samples, (position_x, position_y), rotation, method, True if stabilization == "true" else False
)),)