Skip to content

Commit dcfae2f

Browse files
committed
add the ImageTransitionRightToLeft node and the ImageTransitionBottomToTop node.
1 parent c0ed806 commit dcfae2f

File tree

3 files changed

+152
-19
lines changed

3 files changed

+152
-19
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,15 @@ This node is designed to generate a transition image between two images. The fir
125125
## ImageTransitionLeftToRight
126126
This node is designed to generate a transition image between two images. The first image gradually slides to the right while the second image simultaneously appears from the left, creating a smooth transition effect.
127127

128+
## ImageTransitionRightToLeft
129+
This node is designed to generate a transition image between two images. The first image gradually slides to the left while the second image simultaneously appears from the right, creating a smooth transition effect.
130+
128131
## ImageTransitionTopToBottom
129132
This node is designed to generate a transition image between two images. The first image gradually slides down while the second image simultaneously appears from the top, creating a smooth vertical transition effect.
130133

134+
## ImageTransitionBottomToTop
135+
This node is designed to generate a transition image between two images. The first image gradually slides up while the second image simultaneously appears from the bottom, creating a smooth vertical transition effect.
136+
131137
## ImageMaskColorAverage
132138
This node is designed to calculate the average color of the image within the mask. It returns the decimal and hexadecimal values of the average color.
133139

py/nodes_video.py

Lines changed: 145 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ def INPUT_TYPES(cls):
9393
},
9494
"optional": {
9595
"bounce_back": ("BOOLEAN", {"default": False}),
96+
"start_end_pause_percent": ("FLOAT", {"default": 0.09, "min": 0.0, "max": 0.5, "step": 0.01}),
97+
"middle_pause_percent": ("FLOAT", {"default": 0.15, "min": 0.0, "max": 0.5, "step": 0.01}),
98+
"start_end_position_percent": ("FLOAT", {"default": 0.06, "min": 0.0, "max": 0.5, "step": 0.01}),
99+
"transition_line_width": ("INT", {"default": 8, "min": 1, "max": 20, "step": 1}),
96100
}
97101
}
98102

@@ -101,7 +105,7 @@ def INPUT_TYPES(cls):
101105
FUNCTION = "create_transition"
102106
CATEGORY = "utils"
103107

104-
def create_transition(self, before_image: torch.Tensor, after_image: torch.Tensor, duration: float, fps: float, bounce_back: bool = False):
108+
def create_transition(self, before_image: torch.Tensor, after_image: torch.Tensor, duration: float, fps: float, bounce_back: bool = False, start_end_pause_percent: float = 0, middle_pause_percent: float = 0, start_end_position_percent: float = 0, transition_line_width: int = 1):
105109
# 确保输入是单张图片,如果是批次则取第一张
106110
if len(before_image.shape) == 4 and before_image.shape[0] > 1:
107111
before_image = before_image[0:1]
@@ -119,31 +123,68 @@ def create_transition(self, before_image: torch.Tensor, after_image: torch.Tenso
119123

120124
if bounce_back:
121125
# 如果启用回弹效果,先从左到右,再从右到左
122-
# 前半段:从左到右
123-
half_frames = total_frames // 2
124-
for i in range(half_frames):
125-
progress = i / (half_frames - 1) if half_frames > 1 else 1.0
126-
new_frame = self.create_transition_frame(before_image, adjusted_after, progress)
126+
# 计算各阶段的帧数
127+
start_pause_frames = int(total_frames * start_end_pause_percent)
128+
middle_pause_frames = int(total_frames * middle_pause_percent)
129+
transition_frames = (total_frames - 2 * start_pause_frames - middle_pause_frames) // 2
130+
131+
# 第一阶段:起始停留(显示前图)
132+
for i in range(start_pause_frames):
133+
frames.append(before_image.clone())
134+
135+
# 第二阶段:从左到右过渡
136+
for i in range(transition_frames):
137+
progress = i / (transition_frames - 1) if transition_frames > 1 else 1.0
138+
# 调整进度以考虑起始和结束位置
139+
adjusted_progress = start_end_position_percent + (1.0 - 2 * start_end_position_percent) * progress
140+
new_frame = self.create_transition_frame(before_image, adjusted_after, adjusted_progress, transition_line_width)
127141
frames.append(new_frame)
128142

129-
# 后半段:从右到左
130-
for i in range(half_frames, total_frames):
131-
progress = 1.0 - ((i - half_frames) / (total_frames - half_frames - 1) if total_frames - half_frames > 1 else 0.0)
132-
new_frame = self.create_transition_frame(before_image, adjusted_after, progress)
143+
# 第三阶段:中间停留(显示后图)
144+
for i in range(middle_pause_frames):
145+
frames.append(adjusted_after.clone())
146+
147+
# 第四阶段:从右到左过渡
148+
for i in range(transition_frames):
149+
progress = i / (transition_frames - 1) if transition_frames > 1 else 1.0
150+
# 调整进度以考虑起始和结束位置,反向
151+
adjusted_progress = 1.0 - start_end_position_percent - (1.0 - 2 * start_end_position_percent) * progress
152+
new_frame = self.create_transition_frame(before_image, adjusted_after, adjusted_progress, transition_line_width)
133153
frames.append(new_frame)
154+
155+
# 第五阶段:结束停留(显示前图)
156+
remaining_frames = total_frames - len(frames)
157+
for i in range(remaining_frames):
158+
frames.append(before_image.clone())
134159
else:
135160
# 正常的单向过渡
136-
for i in range(total_frames):
137-
progress = i / (total_frames - 1) if total_frames > 1 else 1.0
138-
new_frame = self.create_transition_frame(before_image, adjusted_after, progress)
161+
# 计算各阶段的帧数
162+
start_pause_frames = int(total_frames * start_end_pause_percent)
163+
transition_frames = total_frames - 2 * start_pause_frames
164+
165+
# 第一阶段:起始停留(显示前图)
166+
for i in range(start_pause_frames):
167+
frames.append(before_image.clone())
168+
169+
# 第二阶段:过渡
170+
for i in range(transition_frames):
171+
progress = i / (transition_frames - 1) if transition_frames > 1 else 1.0
172+
# 调整进度以考虑起始和结束位置
173+
adjusted_progress = start_end_position_percent + (1.0 - 2 * start_end_position_percent) * progress
174+
new_frame = self.create_transition_frame(before_image, adjusted_after, adjusted_progress, transition_line_width)
139175
frames.append(new_frame)
176+
177+
# 第三阶段:结束停留(显示后图)
178+
remaining_frames = total_frames - len(frames)
179+
for i in range(remaining_frames):
180+
frames.append(adjusted_after.clone())
140181

141182
# 合并所有帧
142183
result = torch.cat(frames, dim=0)
143184

144185
return (result, duration, fps)
145186

146-
def create_transition_frame(self, before_image: torch.Tensor, after_image: torch.Tensor, progress: float):
187+
def create_transition_frame(self, before_image: torch.Tensor, after_image: torch.Tensor, progress: float, line_width: int = 2):
147188
"""创建过渡帧的抽象方法,子类需要实现"""
148189
raise NotImplementedError("子类必须实现create_transition_frame方法")
149190

@@ -197,7 +238,7 @@ def check_and_resize_size(self, before_image, after_image):
197238
class ImageTransitionLeftToRight(ImageTransitionBase):
198239
"""从左到右的图像过渡效果"""
199240

200-
def create_transition_frame(self, before_image: torch.Tensor, after_image: torch.Tensor, progress: float):
241+
def create_transition_frame(self, before_image: torch.Tensor, after_image: torch.Tensor, progress: float, line_width: int = 2):
201242
# 获取目标尺寸(前图的尺寸)
202243
_, target_width = before_image.shape[1:3]
203244

@@ -215,12 +256,21 @@ def create_transition_frame(self, before_image: torch.Tensor, after_image: torch
215256
if transition_x > 0:
216257
new_frame[0, :, :transition_x, :] = after_image[0, :, :transition_x, :]
217258

259+
# 添加白色过渡线条
260+
if line_width > 0 and transition_x > 0 and transition_x < target_width:
261+
# 计算线条的起始和结束位置
262+
line_start = max(0, transition_x - line_width // 2)
263+
line_end = min(target_width, transition_x + line_width // 2)
264+
265+
# 用白色填充线条区域
266+
new_frame[0, :, line_start:line_end, :] = 1.0
267+
218268
return new_frame
219269

220270
class ImageTransitionTopToBottom(ImageTransitionBase):
221271
"""从上到下的图像过渡效果"""
222272

223-
def create_transition_frame(self, before_image: torch.Tensor, after_image: torch.Tensor, progress: float):
273+
def create_transition_frame(self, before_image: torch.Tensor, after_image: torch.Tensor, progress: float, line_width: int = 2):
224274
# 获取目标尺寸(前图的尺寸)
225275
target_height, _ = before_image.shape[1:3]
226276

@@ -238,16 +288,93 @@ def create_transition_frame(self, before_image: torch.Tensor, after_image: torch
238288
if transition_y > 0:
239289
new_frame[0, :transition_y, :, :] = after_image[0, :transition_y, :, :]
240290

291+
# 添加白色过渡线条
292+
if line_width > 0 and transition_y > 0 and transition_y < target_height:
293+
# 计算线条的起始和结束位置
294+
line_start = max(0, transition_y - line_width // 2)
295+
line_end = min(target_height, transition_y + line_width // 2)
296+
297+
# 用白色填充线条区域
298+
new_frame[0, line_start:line_end, :, :] = 1.0
299+
300+
return new_frame
301+
302+
class ImageTransitionBottomToTop(ImageTransitionBase):
303+
"""从底到顶的图像过渡效果"""
304+
305+
def create_transition_frame(self, before_image: torch.Tensor, after_image: torch.Tensor, progress: float, line_width: int = 2):
306+
# 获取目标尺寸(前图的尺寸)
307+
target_height, _ = before_image.shape[1:3]
308+
309+
# 计算过渡线的y坐标(从底部开始)
310+
transition_y = int(target_height * (1.0 - progress))
311+
312+
# 创建新帧
313+
new_frame = torch.zeros_like(before_image)
314+
315+
# 从底到顶过渡:下方显示后图,上方显示前图
316+
# 先填充整个前图
317+
new_frame[0] = before_image[0]
318+
319+
# 然后在下方填充后图(覆盖前图)
320+
if transition_y < target_height:
321+
new_frame[0, transition_y:, :, :] = after_image[0, transition_y:, :, :]
322+
323+
# 添加白色过渡线条
324+
if line_width > 0 and transition_y > 0 and transition_y < target_height:
325+
# 计算线条的起始和结束位置
326+
line_start = max(0, transition_y - line_width // 2)
327+
line_end = min(target_height, transition_y + line_width // 2)
328+
329+
# 用白色填充线条区域
330+
new_frame[0, line_start:line_end, :, :] = 1.0
331+
332+
return new_frame
333+
334+
class ImageTransitionRightToLeft(ImageTransitionBase):
335+
"""从右到左的图像过渡效果"""
336+
337+
def create_transition_frame(self, before_image: torch.Tensor, after_image: torch.Tensor, progress: float, line_width: int = 2):
338+
# 获取目标尺寸(前图的尺寸)
339+
_, target_width = before_image.shape[1:3]
340+
341+
# 计算过渡线的x坐标(从右侧开始)
342+
transition_x = int(target_width * (1.0 - progress))
343+
344+
# 创建新帧
345+
new_frame = torch.zeros_like(before_image)
346+
347+
# 从右到左过渡:右侧显示后图,左侧显示前图
348+
# 先填充整个前图
349+
new_frame[0] = before_image[0]
350+
351+
# 然后在右侧填充后图(覆盖前图)
352+
if transition_x < target_width:
353+
new_frame[0, :, transition_x:, :] = after_image[0, :, transition_x:, :]
354+
355+
# 添加白色过渡线条
356+
if line_width > 0 and transition_x > 0 and transition_x < target_width:
357+
# 计算线条的起始和结束位置
358+
line_start = max(0, transition_x - line_width // 2)
359+
line_end = min(target_width, transition_x + line_width // 2)
360+
361+
# 用白色填充线条区域
362+
new_frame[0, :, line_start:line_end, :] = 1.0
363+
241364
return new_frame
242365

243366
NODE_CLASS_MAPPINGS = {
244367
"FrameAdjuster": FrameAdjuster,
245368
"ImageTransitionLeftToRight": ImageTransitionLeftToRight,
246-
"ImageTransitionTopToBottom": ImageTransitionTopToBottom
369+
"ImageTransitionTopToBottom": ImageTransitionTopToBottom,
370+
"ImageTransitionRightToLeft": ImageTransitionRightToLeft,
371+
"ImageTransitionBottomToTop": ImageTransitionBottomToTop
247372
}
248373

249374
NODE_DISPLAY_NAME_MAPPINGS = {
250375
"FrameAdjuster": "Frame Adjuster",
251376
"ImageTransitionLeftToRight": "Image Transition Left to Right",
252-
"ImageTransitionTopToBottom": "Image Transition Top to Bottom"
377+
"ImageTransitionTopToBottom": "Image Transition Top to Bottom",
378+
"ImageTransitionRightToLeft": "Image Transition Right to Left",
379+
"ImageTransitionBottomToTop": "Image Transition Bottom to Top"
253380
}

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "comfyui-utils-nodes"
33
description = "Nodes:LoadImageWithSwitch, ImageBatchOneOrMore, ModifyTextGender, GenderControlOutput, ImageCompositeMaskedWithSwitch, ImageCompositeMaskedOneByOne, ColorCorrectOfUtils, SplitMask, MaskFastGrow, CheckpointLoaderSimpleWithSwitch, ImageResizeTo8x, MatchImageRatioToPreset, MaskFromFaceModel, MaskCoverFourCorners, DetectorForNSFW, DeepfaceAnalyzeFaceAttributes, VolcanoOutpainting, VolcanoImageEdit, etc."
4-
version = "1.3.3"
4+
version = "1.3.4"
55
license = { file = "LICENSE" }
66
dependencies = []
77

0 commit comments

Comments
 (0)