commit 1666c1db914c250cd83fbe83250c7087692de892 Author: tok_main01 Date: Fri Dec 20 13:58:25 2024 +0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8323524 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +.env +.venv +env/ +venv/ +ENV/ +dist/ +build/ +*.egg-info/ +.DS_Store +*.so +.Python +develop-eggs/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d75ffed --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 kazeyori + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3c122bf --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# Quick Image Sequence Process (QuickSeq) + +A ComfyUI plugin for efficient image sequence processing. Features frame insertion, duplication, and removal with intuitive controls. + + + +## 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 + - 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 + - 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 + - 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**: + - 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**: + - Shows the total number of frames after processing + - Helps track sequence length changes + +## Installation + +1. Clone this repository into your ComfyUI's `custom_nodes` directory: + ```bash + cd custom_nodes + git clone https://github.com/yourusername/ComfyUI-QuickImageSequenceProcess.git + ``` +2. Restart ComfyUI +3. Find "Quick Image Sequence Process" in the node menu + +## Usage Example + +1. Load an image sequence into ComfyUI +2. Add the "Quick Image Sequence Process" node +3. Configure parameters: + - `frames_between`: Number of frames to insert between existing frames + - `copy_frame`: Choose "previous" or "next" for inserted 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 + +## Output + +The node outputs: +- `image`: The processed image sequence +- `width`: Frame width +- `height`: Frame height +- `count`: Total number of frames in the sequence + +## License + +Copyright 2024 kazeyori + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +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. + diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..9aab1c8 --- /dev/null +++ b/__init__.py @@ -0,0 +1,12 @@ +# __init__.py +from .quick_image_sequence_process import QuickImageSequenceProcessNode + +NODE_CLASS_MAPPINGS = { + "QuickImageSequenceProcess": QuickImageSequenceProcessNode, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "QuickImageSequenceProcess": "Quick Image Sequence Process", +} + +__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] \ No newline at end of file diff --git a/examples/example.json b/examples/example.json new file mode 100644 index 0000000..ec98a5b --- /dev/null +++ b/examples/example.json @@ -0,0 +1,608 @@ +{ + "last_node_id": 13, + "last_link_id": 13, + "nodes": [ + { + "id": 2, + "type": "VHS_VideoCombine", + "pos": [ + 1456, + 360 + ], + "size": [ + 220.5830078125, + 363.43499755859375 + ], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 7, + "label": "images" + }, + { + "name": "audio", + "type": "AUDIO", + "link": null, + "shape": 7, + "label": "audio" + }, + { + "name": "meta_batch", + "type": "VHS_BatchManager", + "link": null, + "shape": 7, + "label": "meta_batch" + }, + { + "name": "vae", + "type": "VAE", + "link": null, + "shape": 7, + "label": "vae" + } + ], + "outputs": [ + { + "name": "Filenames", + "type": "VHS_FILENAMES", + "links": null, + "label": "Filenames" + } + ], + "properties": { + "Node name for S&R": "VHS_VideoCombine" + }, + "widgets_values": { + "frame_rate": 8, + "loop_count": 0, + "filename_prefix": "AnimateDiff", + "format": "image/gif", + "pingpong": false, + "save_output": true, + "videopreview": { + "hidden": false, + "paused": false, + "params": { + "filename": "AnimateDiff_00002.gif", + "subfolder": "", + "type": "output", + "format": "image/gif", + "frame_rate": 8, + "workflow": "AnimateDiff_00002.png", + "fullpath": "\\\\NAS11\\Resource_Backup_AI_LLM\\AI_Outputs\\ComfyUI\\output\\AnimateDiff_00002.gif" + }, + "muted": false + } + } + }, + { + "id": 10, + "type": "Display Int (rgthree)", + "pos": [ + 1117, + 238 + ], + "size": [ + 278.4212341308594, + 76 + ], + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + { + "name": "input", + "type": "INT", + "link": 10, + "widget": { + "name": "input" + }, + "dir": 3, + "label": "input" + } + ], + "outputs": [], + "title": "Frame Count(original)", + "properties": { + "Node name for S&R": "Video Frame Count" + }, + "widgets_values": [ + 0, + "" + ], + "color": "#322", + "bgcolor": "#533" + }, + { + "id": 5, + "type": "Display Int (rgthree)", + "pos": [ + 1116, + 381 + ], + "size": [ + 278.4212341308594, + 76 + ], + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "input", + "type": "INT", + "link": 3, + "widget": { + "name": "input" + }, + "dir": 3, + "label": "input" + } + ], + "outputs": [], + "title": "Frame Count(Processed)", + "properties": { + "Node name for S&R": "Video Frame Count" + }, + "widgets_values": [ + 0, + "" + ], + "color": "#322", + "bgcolor": "#533" + }, + { + "id": 9, + "type": "Display Int (rgthree)", + "pos": [ + 661.454345703125, + 685.2728271484375 + ], + "size": [ + 278.4212341308594, + 76 + ], + "flags": {}, + "order": 7, + "mode": 0, + "inputs": [ + { + "name": "input", + "type": "INT", + "link": 6, + "widget": { + "name": "input" + }, + "dir": 3, + "label": "input" + } + ], + "outputs": [], + "title": "width", + "properties": { + "Node name for S&R": "Video Frame Count" + }, + "widgets_values": [ + 0, + "" + ], + "color": "#432", + "bgcolor": "#653" + }, + { + "id": 8, + "type": "Display Int (rgthree)", + "pos": [ + 662.2725830078125, + 809.7266235351562 + ], + "size": [ + 278.4212341308594, + 76 + ], + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [ + { + "name": "input", + "type": "INT", + "link": 5, + "widget": { + "name": "input" + }, + "dir": 3, + "label": "input" + } + ], + "outputs": [], + "title": "heigh", + "properties": { + "Node name for S&R": "Video Frame Count" + }, + "widgets_values": [ + 0, + "" + ], + "color": "#432", + "bgcolor": "#653" + }, + { + "id": 1, + "type": "QuickImageSequenceProcess", + "pos": [ + 689, + 420 + ], + "size": [ + 352.79998779296875, + 214 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 9, + "label": "images" + } + ], + "outputs": [ + { + "name": "image", + "type": "IMAGE", + "links": [ + 2, + 7 + ], + "label": "image", + "slot_index": 0 + }, + { + "name": "width", + "type": "INT", + "links": [ + 6 + ], + "label": "width", + "slot_index": 1 + }, + { + "name": "height", + "type": "INT", + "links": [ + 5 + ], + "label": "height", + "slot_index": 2 + }, + { + "name": "count", + "type": "INT", + "links": [ + 3 + ], + "label": "count", + "slot_index": 3 + } + ], + "properties": { + "Node name for S&R": "QuickImageSequenceProcess" + }, + "widgets_values": [ + 2, + "next", + 1, + 1, + "yes" + ] + }, + { + "id": 12, + "type": "VHS_LoadVideo", + "pos": [ + 163.33587646484375, + 333.7574768066406 + ], + "size": [ + 253.279296875, + 262 + ], + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [ + { + "name": "meta_batch", + "type": "VHS_BatchManager", + "link": null, + "shape": 7, + "label": "meta_batch" + }, + { + "name": "vae", + "type": "VAE", + "link": null, + "shape": 7, + "label": "vae" + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 11 + ], + "label": "IMAGE", + "slot_index": 0 + }, + { + "name": "frame_count", + "type": "INT", + "links": null, + "label": "frame_count" + }, + { + "name": "audio", + "type": "AUDIO", + "links": null, + "label": "audio" + }, + { + "name": "video_info", + "type": "VHS_VIDEOINFO", + "links": null, + "label": "video_info" + } + ], + "properties": { + "Node name for S&R": "VHS_LoadVideo" + }, + "widgets_values": { + "video": "01_VIDGEN_00015.mp4", + "force_rate": 0, + "force_size": "Disabled", + "custom_width": 512, + "custom_height": 512, + "frame_load_cap": 0, + "skip_first_frames": 0, + "select_every_nth": 1, + "choose video to upload": "image", + "videopreview": { + "hidden": false, + "paused": false, + "params": { + "force_rate": 0, + "frame_load_cap": 0, + "skip_first_frames": 0, + "select_every_nth": 1 + }, + "muted": false + } + } + }, + { + "id": 4, + "type": "PreviewImage", + "pos": [ + 1218.5001220703125, + 683.30029296875 + ], + "size": [ + 210, + 246 + ], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 2, + "label": "images" + } + ], + "outputs": [], + "title": "Preview Image(processed)", + "properties": { + "Node name for S&R": "PreviewImage" + }, + "color": "#322", + "bgcolor": "#533" + }, + { + "id": 11, + "type": "GetImageSizeAndCount", + "pos": [ + 487, + 247 + ], + "size": [ + 202.2143096923828, + 99.23601531982422 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 11, + "slot_index": 0, + "label": "image" + } + ], + "outputs": [ + { + "name": "image", + "type": "IMAGE", + "links": [ + 9, + 13 + ], + "slot_index": 0, + "shape": 3, + "label": "image" + }, + { + "name": "width", + "type": "INT", + "links": [], + "slot_index": 1, + "shape": 3, + "label": "width" + }, + { + "name": "height", + "type": "INT", + "links": [], + "slot_index": 2, + "shape": 3, + "label": "height" + }, + { + "name": "count", + "type": "INT", + "links": [ + 10 + ], + "slot_index": 3, + "shape": 3, + "label": "count" + } + ], + "properties": { + "Node name for S&R": "GetImageSizeAndCount" + } + }, + { + "id": 13, + "type": "PreviewImage", + "pos": [ + 979.3359375, + 681.7574462890625 + ], + "size": [ + 210, + 246 + ], + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 13, + "label": "images" + } + ], + "outputs": [], + "title": "Preview Image(original)", + "properties": { + "Node name for S&R": "PreviewImage" + }, + "color": "#322", + "bgcolor": "#533" + } + ], + "links": [ + [ + 2, + 1, + 0, + 4, + 0, + "IMAGE" + ], + [ + 3, + 1, + 3, + 5, + 0, + "INT" + ], + [ + 5, + 1, + 2, + 8, + 0, + "INT" + ], + [ + 6, + 1, + 1, + 9, + 0, + "INT" + ], + [ + 7, + 1, + 0, + 2, + 0, + "IMAGE" + ], + [ + 9, + 11, + 0, + 1, + 0, + "IMAGE" + ], + [ + 10, + 11, + 3, + 10, + 0, + "INT" + ], + [ + 11, + 12, + 0, + 11, + 0, + "IMAGE" + ], + [ + 13, + 11, + 0, + 13, + 0, + "IMAGE" + ] + ], + "groups": [], + "config": {}, + "extra": { + "ds": { + "scale": 0.9090909090909091, + "offset": [ + -47.735885123966824, + -25.25747024793393 + ] + }, + "ue_links": [], + "workspace_info": { + "id": "ycPEdZk_Iy0gIIOmbx1r8" + } + }, + "version": 0.4 +} \ No newline at end of file diff --git a/quick_image_sequence_process.py b/quick_image_sequence_process.py new file mode 100644 index 0000000..b3ab0f6 --- /dev/null +++ b/quick_image_sequence_process.py @@ -0,0 +1,111 @@ +""" +@author: kazeyori +@title: Quick Image Sequence Process +@nickname: QuickSeq +@description: A ComfyUI plugin for efficient image sequence processing. Features frame insertion, duplication, and removal with intuitive controls. +""" + +# quick_image_sequence_process.py +import numpy as np +import torch + +class QuickImageSequenceProcessNode: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "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 + "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 + }, + } + + RETURN_TYPES = ("IMAGE", "INT", "INT", "INT") # Output: image, width, height, count + RETURN_NAMES = ("image", "width", "height", "count") + FUNCTION = "edit_image_sequence" + + CATEGORY = "image/sequence" + + def process_between_frames(self, images, frames_between, copy_frame): + """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 i < len(images) - 1: + if copy_frame == "previous": + frame_to_copy = images[i] + else: + frame_to_copy = images[i + 1] + + for _ in range(frames_between): + new_images.append(frame_to_copy) + return new_images + + def process_end_frames(self, images, quick_process_first_frames, quick_process_last_frames): + """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 + 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) + 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): + """ + Edit image sequence: + 1. Process order determined by process_F_L_frames_first + 2. Insert specified number of copied frames between frames + 3. Add or remove frames at the beginning and end + """ + # Ensure input is PyTorch tensor + if isinstance(images, np.ndarray): + images = torch.from_numpy(images) + + 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) + 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) + + # Ensure at least one frame remains + if not new_images: + raise ValueError("No frames remaining after processing. Please adjust parameters to ensure at least one frame.") + + # Convert image sequence to PyTorch tensor + new_images = torch.stack(new_images) + + # Get dimensions + width = new_images.shape[2] + height = new_images.shape[1] + frame_count = len(new_images) + + # Display frame count + print(f"Processed sequence contains {frame_count} frames") + + # Return results + return (new_images, width, height, frame_count) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..54b7057 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +numpy +torch \ No newline at end of file