Simplify VideoContinuationGenerator: Improve parameter naming and remove complex replacement logic
- Rename parameters for maximum clarity: • when_to_start_control_images → how_to_use_control_images • when_to_start_inpaint_masks → how_to_use_inpaint_masks - Update option names to clearly indicate input frame prioritization: • beginning_of_generation → start_sequence_at_beginning_but_prioritise_input_frames • after_overlap_frames → start_sequence_after_overlap_frames_prioritise_input_frames - Remove instead_of_input_frames option and all complex replacement logic - Simplify to two clear options that always prioritize input overlap frames - Improve tooltips to clearly explain prioritization behavior - Focus on user-friendly workflows that preserve input video structure
This commit is contained in:
+14
-46
@@ -822,8 +822,8 @@ class VideoContinuationGenerator:
|
||||
"end_frame": ("IMAGE", {"tooltip": "Optional single frame to place at the end of the continuation video."}),
|
||||
"control_images": ("IMAGE", {"tooltip": "Optional control images to fill the empty frames."}),
|
||||
"inpaint_mask": ("MASK", {"tooltip": "Optional inpaint mask to use for the empty frames, overriding the default mask."}),
|
||||
"when_to_start_control_images": (["beginning_of_generation", "after_overlap_frames", "instead_of_input_frames"], {"default": "beginning_of_generation", "tooltip": "If beginning_of_generation is selected, aligns control images with the start of the output (frame 0). Overlap frames from the input video will take priority, so control images will become visible starting after the overlap period. If after_overlap_frames is selected, control images will start being placed after the overlap frames from the input video. If instead_of_input_frames is selected, control images will replace the overlap frames entirely."}),
|
||||
"when_to_start_inpaint_masks": (["beginning_of_generation", "after_overlap_frames", "instead_of_input_frames"], {"default": "beginning_of_generation", "tooltip": "If beginning_of_generation is selected, aligns inpaint masks with the start of the output (frame 0). If after_overlap_frames is selected, inpaint masks will start being placed after the overlap frames from the input video. If instead_of_input_frames is selected, inpaint masks will replace the overlap frames entirely."}),
|
||||
"how_to_use_control_images": (["start_sequence_at_beginning_but_prioritise_input_frames", "start_sequence_after_overlap_frames_prioritise_input_frames"], {"default": "start_sequence_at_beginning_but_prioritise_input_frames", "tooltip": "If start_sequence_at_beginning_but_prioritise_input_frames is selected, control images align with frame 0 but input overlap frames take priority, so control images become visible after the overlap period. If start_sequence_after_overlap_frames_prioritise_input_frames is selected, control images start being placed after the overlap frames from the input video."}),
|
||||
"how_to_use_inpaint_masks": (["start_sequence_at_beginning_but_prioritise_input_frames", "start_sequence_after_overlap_frames_prioritise_input_frames"], {"default": "start_sequence_at_beginning_but_prioritise_input_frames", "tooltip": "If start_sequence_at_beginning_but_prioritise_input_frames is selected, inpaint masks align with frame 0 but preserve input overlap frames as known. If start_sequence_after_overlap_frames_prioritise_input_frames is selected, inpaint masks only affect frames after the overlap period."}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -833,7 +833,7 @@ class VideoContinuationGenerator:
|
||||
CATEGORY = "Steerable-Motion"
|
||||
DESCRIPTION = "Creates a continuation video by placing overlap frames from the end of input video at the start, with optional end frame."
|
||||
|
||||
def generate_continuation_video(self, input_video_frames, total_output_frames, overlap_frames, empty_frame_fill_level, end_frame=None, control_images=None, inpaint_mask=None, when_to_start_control_images="beginning_of_generation", when_to_start_inpaint_masks="beginning_of_generation"):
|
||||
def generate_continuation_video(self, input_video_frames, total_output_frames, overlap_frames, empty_frame_fill_level, end_frame=None, control_images=None, inpaint_mask=None, how_to_use_control_images="start_sequence_at_beginning_but_prioritise_input_frames", how_to_use_inpaint_masks="start_sequence_at_beginning_but_prioritise_input_frames"):
|
||||
# 1. Validation and Setup
|
||||
total_output_frames = int(total_output_frames)
|
||||
if (total_output_frames - 1) % 4 != 0:
|
||||
@@ -878,10 +878,10 @@ class VideoContinuationGenerator:
|
||||
|
||||
if num_middle_frames > 0:
|
||||
if control_images is not None:
|
||||
log.info(f"Using 'control_images' to fill the {num_middle_frames} middle frames with '{when_to_start_control_images}' mode.")
|
||||
log.info(f"Using 'control_images' to fill the {num_middle_frames} middle frames with '{how_to_use_control_images}' mode.")
|
||||
control_images_resized = common_upscale(control_images.movedim(-1, 1), frame_width, frame_height, "lanczos", "disabled").movedim(1, -1)
|
||||
|
||||
if when_to_start_control_images == "beginning_of_generation":
|
||||
if how_to_use_control_images == "start_sequence_at_beginning_but_prioritise_input_frames":
|
||||
# Skip the first overlap_frames control images to avoid duplication
|
||||
duplicate_count = min(actual_overlap_frames, control_images_resized.shape[0])
|
||||
available_after_dup = control_images_resized.shape[0] - duplicate_count
|
||||
@@ -893,7 +893,7 @@ class VideoContinuationGenerator:
|
||||
middle_frames_part = torch.cat([selected_control, padding], dim=0)
|
||||
else:
|
||||
middle_frames_part = control_images_resized[duplicate_count:duplicate_count + num_middle_frames].clone()
|
||||
elif when_to_start_control_images == "after_overlap_frames":
|
||||
else: # "start_sequence_after_overlap_frames_prioritise_input_frames"
|
||||
# Use control frames from the beginning of the sequence (C0, C1, C2...)
|
||||
if control_images_resized.shape[0] < num_middle_frames:
|
||||
log.warning(f"Provided 'control_images' have {control_images_resized.shape[0]} frames, less than needed ({num_middle_frames}). Padding with 'empty_frame_fill_level'.")
|
||||
@@ -902,21 +902,6 @@ class VideoContinuationGenerator:
|
||||
middle_frames_part = torch.cat([control_images_resized, padding], dim=0)
|
||||
else:
|
||||
middle_frames_part = control_images_resized[:num_middle_frames].clone()
|
||||
else: # "instead_of_input_frames"
|
||||
# Replace overlap frames with control images, use remaining for middle frames and potentially end frame
|
||||
total_frames_needed = actual_overlap_frames + num_middle_frames + num_end_frames
|
||||
if control_images_resized.shape[0] < total_frames_needed:
|
||||
log.warning(f"Provided 'control_images' have {control_images_resized.shape[0]} frames, less than needed ({total_frames_needed} = {actual_overlap_frames} overlap + {num_middle_frames} middle + {num_end_frames} end). Padding with 'empty_frame_fill_level'.")
|
||||
padding_needed = total_frames_needed - control_images_resized.shape[0]
|
||||
padding = torch.ones((padding_needed, frame_height, frame_width, num_channels), device=device, dtype=dtype) * empty_frame_fill_level
|
||||
extended_control = torch.cat([control_images_resized, padding], dim=0)
|
||||
else:
|
||||
extended_control = control_images_resized[:total_frames_needed].clone()
|
||||
# Split into overlap replacement, middle parts, and end frame replacement
|
||||
start_frames_part = extended_control[:actual_overlap_frames]
|
||||
middle_frames_part = extended_control[actual_overlap_frames:actual_overlap_frames + num_middle_frames]
|
||||
if num_end_frames > 0:
|
||||
end_frame_part = extended_control[actual_overlap_frames + num_middle_frames:actual_overlap_frames + num_middle_frames + num_end_frames]
|
||||
else:
|
||||
log.info(f"No 'control_images', filling {num_middle_frames} middle frames with level {empty_frame_fill_level}.")
|
||||
middle_frames_part = torch.ones((num_middle_frames, frame_height, frame_width, num_channels), device=device, dtype=dtype) * empty_frame_fill_level
|
||||
@@ -927,8 +912,8 @@ class VideoContinuationGenerator:
|
||||
# 6. Create Mask
|
||||
continuation_frame_masks = torch.ones((total_output_frames, frame_height, frame_width), device=device, dtype=dtype)
|
||||
|
||||
# Apply mask logic based on when_to_start_inpaint_masks parameter
|
||||
if when_to_start_inpaint_masks == "beginning_of_generation":
|
||||
# Apply mask logic based on how_to_use_inpaint_masks parameter
|
||||
if how_to_use_inpaint_masks == "start_sequence_at_beginning_but_prioritise_input_frames":
|
||||
# Set known frames (overlap and end) to 0.0, but also set middle section based on control frame logic
|
||||
if actual_overlap_frames > 0:
|
||||
continuation_frame_masks[0:actual_overlap_frames] = 0.0
|
||||
@@ -944,29 +929,16 @@ class VideoContinuationGenerator:
|
||||
middle_start = actual_overlap_frames
|
||||
middle_end = middle_start + num_middle_frames
|
||||
continuation_frame_masks[middle_start:middle_end] = 0.0
|
||||
elif when_to_start_inpaint_masks == "after_overlap_frames":
|
||||
else: # "start_sequence_after_overlap_frames_prioritise_input_frames"
|
||||
# Set known frames (overlap and end) to 0.0, rest stay as 1.0 (inpaint)
|
||||
if actual_overlap_frames > 0:
|
||||
continuation_frame_masks[0:actual_overlap_frames] = 0.0
|
||||
if num_end_frames > 0:
|
||||
continuation_frame_masks[-num_end_frames:] = 0.0
|
||||
else: # "instead_of_input_frames"
|
||||
# All frames become inpaint areas (1.0) by default when replacing input frames entirely
|
||||
# If control images replace all frames (overlap + middle + end), those areas can be set as known
|
||||
if control_images is not None and when_to_start_control_images == "instead_of_input_frames":
|
||||
total_frames_needed = actual_overlap_frames + num_middle_frames + num_end_frames
|
||||
if control_images.shape[0] >= total_frames_needed:
|
||||
# Set all sections as known since we have enough control images to replace everything
|
||||
continuation_frame_masks[:] = 0.0
|
||||
elif control_images.shape[0] >= actual_overlap_frames + num_middle_frames:
|
||||
# Set middle section as known since we have enough control images for overlap + middle
|
||||
middle_start = actual_overlap_frames
|
||||
middle_end = middle_start + num_middle_frames
|
||||
continuation_frame_masks[middle_start:middle_end] = 0.0
|
||||
|
||||
# 7. Handle optional inpaint_mask with when_to_start_inpaint_masks logic
|
||||
# 7. Handle optional inpaint_mask with how_to_use_inpaint_masks logic
|
||||
if inpaint_mask is not None:
|
||||
log.info(f"Processing provided 'inpaint_mask' with '{when_to_start_inpaint_masks}' timing.")
|
||||
log.info(f"Processing provided 'inpaint_mask' with '{how_to_use_inpaint_masks}' timing.")
|
||||
processed_mask = common_upscale(inpaint_mask.unsqueeze(1), frame_width, frame_height, "nearest-exact", "disabled").squeeze(1).to(device)
|
||||
|
||||
if processed_mask.shape[0] != total_output_frames:
|
||||
@@ -977,24 +949,20 @@ class VideoContinuationGenerator:
|
||||
else:
|
||||
processed_mask = processed_mask[:total_output_frames]
|
||||
|
||||
# Apply when_to_start_inpaint_masks logic to the provided mask
|
||||
if when_to_start_inpaint_masks == "beginning_of_generation":
|
||||
# Apply how_to_use_inpaint_masks logic to the provided mask
|
||||
if how_to_use_inpaint_masks == "start_sequence_at_beginning_but_prioritise_input_frames":
|
||||
# Use the provided mask as-is, but preserve known frames (overlap and end)
|
||||
if actual_overlap_frames > 0:
|
||||
processed_mask[0:actual_overlap_frames] = 0.0 # Keep overlap frames as known
|
||||
if num_end_frames > 0:
|
||||
processed_mask[-num_end_frames:] = 0.0 # Keep end frame as known
|
||||
elif when_to_start_inpaint_masks == "after_overlap_frames":
|
||||
else: # "start_sequence_after_overlap_frames_prioritise_input_frames"
|
||||
# Only apply the provided mask after overlap frames
|
||||
if actual_overlap_frames > 0:
|
||||
processed_mask[0:actual_overlap_frames] = 0.0 # Keep overlap frames as known
|
||||
# The provided mask affects frames starting after overlap
|
||||
if num_end_frames > 0:
|
||||
processed_mask[-num_end_frames:] = 0.0 # Keep end frame as known
|
||||
else: # "instead_of_input_frames"
|
||||
# Use the provided mask starting from frame 0, affecting the entire timeline
|
||||
# No frames are preserved as "known" - the provided mask has full control
|
||||
pass # The provided mask affects the entire timeline including overlap frames and end frame
|
||||
|
||||
continuation_frame_masks = processed_mask.to(dtype=dtype)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user