Merge pull request #4 from kazeyori/250305_nf01
Add fill mode options for frame insertion
This commit is contained in:
@@ -2,36 +2,47 @@
|
||||
|
||||
A ComfyUI plugin for efficient image sequence processing. Features frame insertion, duplication, and removal with intuitive controls.
|
||||
|
||||
## Recent Updates
|
||||
|
||||
- **New Fill Mode Options**: Added ability to fill new frames with either copied frames (original behavior), pure white frames, or pure black frames
|
||||
- **Enhanced Frame Processing**: Fill mode applies to both between-frame insertion and first/last frame operations
|
||||
- **Improved Flexibility**: More options for creative sequence manipulation and special effects
|
||||
|
||||
## Features
|
||||
|
||||
1. **Frame Insertion Between Frames**:
|
||||
- Set the number of frames to insert between existing frames
|
||||
- Choose whether to copy from the previous or next frame
|
||||
- Select fill mode: copy existing frames or use solid white/black frames
|
||||
- Example: With frames_between=2, sequence [A,B] becomes [A,A,A,B] or [A,B,B,B]
|
||||
|
||||
2. **Quick Process First Frames**:
|
||||
- Add or remove frames at the beginning of the sequence
|
||||
- Positive values: Add duplicates of the first frame
|
||||
- Positive values: Add duplicates of the first frame or solid color frames
|
||||
- Negative values: Remove frames from the beginning
|
||||
- Example: With quick_process_first_frames=2, [A,B,C] becomes [A,A,A,B,C]
|
||||
- Example: With quick_process_first_frames=-1, [A,B,C] becomes [B,C]
|
||||
|
||||
3. **Quick Process Last Frames**:
|
||||
- Add or remove frames at the end of the sequence
|
||||
- Positive values: Add duplicates of the last frame
|
||||
- Positive values: Add duplicates of the last frame or solid color frames
|
||||
- Negative values: Remove frames from the end
|
||||
- Example: With quick_process_last_frames=2, [A,B,C] becomes [A,B,C,C,C]
|
||||
- Example: With quick_process_last_frames=-1, [A,B,C] becomes [A,B]
|
||||
|
||||
4. **Processing Order Control**:
|
||||
4. **Fill Mode Options**:
|
||||
- `copy_frame`: Use copies of existing frames (original behavior)
|
||||
- `white`: Insert pure white frames
|
||||
- `black`: Insert pure black frames
|
||||
- Applies to both between-frame insertion and first/last frame processing
|
||||
|
||||
5. **Processing Order Control**:
|
||||
- Choose whether to process first/last frames before or after the between-frame operations
|
||||
- Affects the final result when combining multiple operations
|
||||
- "Yes": Process first/last frames first
|
||||
- "No": Process between-frames first
|
||||
|
||||
5. **Real-time Frame Count Display**:
|
||||
6. **Real-time Frame Count Display**:
|
||||
- Shows the total number of frames after processing
|
||||
- Helps track sequence length changes
|
||||
|
||||
@@ -52,6 +63,7 @@ A ComfyUI plugin for efficient image sequence processing. Features frame inserti
|
||||
3. Configure parameters:
|
||||
- `frames_between`: Number of frames to insert between existing frames
|
||||
- `copy_frame`: Choose "previous" or "next" for inserted frames
|
||||
- `fill_mode`: Choose between copying frames or using solid white/black frames
|
||||
- `quick_process_first_frames`: Add/remove frames at start (negative to remove)
|
||||
- `quick_process_last_frames`: Add/remove frames at end (negative to remove)
|
||||
- `process_F_L_frames_first`: Choose processing order priority
|
||||
@@ -78,5 +90,4 @@ Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
limitations under the License.
|
||||
@@ -20,6 +20,7 @@ class QuickImageSequenceProcessNode:
|
||||
"images": ("IMAGE",), # Input image sequence
|
||||
"frames_between": ("INT", {"default": 1}), # Number of frames to copy between each frame
|
||||
"copy_frame": (["previous", "next"],), # Whether to copy the previous or next frame
|
||||
"fill_mode": (["copy_frame", "white", "black"],), # Fill mode for added frames
|
||||
"quick_process_first_frames": ("INT", {"default": 0}), # Number of frames to add/remove at the beginning (negative values for removal)
|
||||
"quick_process_last_frames": ("INT", {"default": 0}), # Number of frames to add/remove at the end (negative values for removal)
|
||||
"process_F_L_frames_first": (["yes", "no"],), # Whether to prioritize processing first and last frames
|
||||
@@ -32,51 +33,92 @@ class QuickImageSequenceProcessNode:
|
||||
|
||||
CATEGORY = "image/sequence"
|
||||
|
||||
def process_between_frames(self, images, frames_between, copy_frame):
|
||||
def create_solid_frame(self, reference_frame, color="white"):
|
||||
"""Create a solid white or black frame with the same dimensions as the reference frame"""
|
||||
# Get dimensions from reference frame
|
||||
if isinstance(reference_frame, torch.Tensor):
|
||||
h, w = reference_frame.shape[0], reference_frame.shape[1]
|
||||
c = reference_frame.shape[2] if len(reference_frame.shape) > 2 else 3
|
||||
|
||||
# Create solid frame
|
||||
if color == "white":
|
||||
solid_frame = torch.ones((h, w, c), dtype=reference_frame.dtype)
|
||||
else: # black
|
||||
solid_frame = torch.zeros((h, w, c), dtype=reference_frame.dtype)
|
||||
|
||||
return solid_frame
|
||||
else:
|
||||
raise TypeError("Reference frame must be a torch tensor")
|
||||
|
||||
def process_between_frames(self, images, frames_between, copy_frame, fill_mode):
|
||||
"""Process middle frames (including frame insertion)"""
|
||||
new_images = []
|
||||
for i in range(len(images)):
|
||||
# Add current frame
|
||||
new_images.append(images[i])
|
||||
|
||||
# If not the last frame, insert copied frames between current and next
|
||||
# If not the last frame, insert frames between current and next
|
||||
if i < len(images) - 1:
|
||||
if copy_frame == "previous":
|
||||
frame_to_copy = images[i]
|
||||
else:
|
||||
frame_to_copy = images[i + 1]
|
||||
if fill_mode == "copy_frame":
|
||||
# Use original copy frame logic
|
||||
if copy_frame == "previous":
|
||||
frame_to_add = images[i]
|
||||
else: # "next"
|
||||
frame_to_add = images[i + 1]
|
||||
elif fill_mode == "white":
|
||||
frame_to_add = self.create_solid_frame(images[i], "white")
|
||||
else: # "black"
|
||||
frame_to_add = self.create_solid_frame(images[i], "black")
|
||||
|
||||
for _ in range(frames_between):
|
||||
new_images.append(frame_to_copy)
|
||||
new_images.append(frame_to_add)
|
||||
return new_images
|
||||
|
||||
def process_end_frames(self, images, quick_process_first_frames, quick_process_last_frames):
|
||||
def process_end_frames(self, images, quick_process_first_frames, quick_process_last_frames, fill_mode):
|
||||
"""Process first and last frames (adding or removing)"""
|
||||
new_images = list(images) # Convert to list for modification
|
||||
|
||||
if quick_process_first_frames >= 0:
|
||||
# Add frames at the beginning
|
||||
first_frame = new_images[0]
|
||||
new_images = [first_frame] * quick_process_first_frames + new_images
|
||||
if fill_mode == "copy_frame":
|
||||
first_frame = new_images[0]
|
||||
frames_to_add = [first_frame] * quick_process_first_frames
|
||||
elif fill_mode == "white":
|
||||
first_frame = self.create_solid_frame(new_images[0], "white")
|
||||
frames_to_add = [first_frame] * quick_process_first_frames
|
||||
else: # "black"
|
||||
first_frame = self.create_solid_frame(new_images[0], "black")
|
||||
frames_to_add = [first_frame] * quick_process_first_frames
|
||||
|
||||
new_images = frames_to_add + new_images
|
||||
else:
|
||||
# Remove frames from the beginning
|
||||
new_images = new_images[-quick_process_first_frames:]
|
||||
|
||||
if quick_process_last_frames >= 0:
|
||||
# Add frames at the end
|
||||
last_frame = new_images[-1]
|
||||
new_images.extend([last_frame] * quick_process_last_frames)
|
||||
if fill_mode == "copy_frame":
|
||||
last_frame = new_images[-1]
|
||||
frames_to_add = [last_frame] * quick_process_last_frames
|
||||
elif fill_mode == "white":
|
||||
last_frame = self.create_solid_frame(new_images[-1], "white")
|
||||
frames_to_add = [last_frame] * quick_process_last_frames
|
||||
else: # "black"
|
||||
last_frame = self.create_solid_frame(new_images[-1], "black")
|
||||
frames_to_add = [last_frame] * quick_process_last_frames
|
||||
|
||||
new_images.extend(frames_to_add)
|
||||
else:
|
||||
# Remove frames from the end
|
||||
new_images = new_images[:quick_process_last_frames]
|
||||
|
||||
return new_images
|
||||
|
||||
def edit_image_sequence(self, images, frames_between, copy_frame, quick_process_first_frames, quick_process_last_frames, process_F_L_frames_first):
|
||||
def edit_image_sequence(self, images, frames_between, copy_frame, fill_mode, quick_process_first_frames, quick_process_last_frames, process_F_L_frames_first):
|
||||
"""
|
||||
Edit image sequence:
|
||||
1. Process order determined by process_F_L_frames_first
|
||||
2. Insert specified number of copied frames between frames
|
||||
2. Insert specified number of frames between frames (copied or solid color)
|
||||
3. Add or remove frames at the beginning and end
|
||||
"""
|
||||
# Ensure input is PyTorch tensor
|
||||
@@ -85,12 +127,12 @@ class QuickImageSequenceProcessNode:
|
||||
|
||||
if process_F_L_frames_first == "yes":
|
||||
# Process first/last frames first, then middle frames
|
||||
intermediate_images = self.process_end_frames(images, quick_process_first_frames, quick_process_last_frames)
|
||||
new_images = self.process_between_frames(intermediate_images, frames_between, copy_frame)
|
||||
intermediate_images = self.process_end_frames(images, quick_process_first_frames, quick_process_last_frames, fill_mode)
|
||||
new_images = self.process_between_frames(intermediate_images, frames_between, copy_frame, fill_mode)
|
||||
else:
|
||||
# Process middle frames first, then first/last frames
|
||||
intermediate_images = self.process_between_frames(images, frames_between, copy_frame)
|
||||
new_images = self.process_end_frames(intermediate_images, quick_process_first_frames, quick_process_last_frames)
|
||||
intermediate_images = self.process_between_frames(images, frames_between, copy_frame, fill_mode)
|
||||
new_images = self.process_end_frames(intermediate_images, quick_process_first_frames, quick_process_last_frames, fill_mode)
|
||||
|
||||
# Ensure at least one frame remains
|
||||
if not new_images:
|
||||
|
||||
Reference in New Issue
Block a user