first commit

This commit is contained in:
diSty
2024-03-08 11:19:13 +02:00
commit 97ebe8f8de
13 changed files with 2702 additions and 0 deletions
+384
View File
@@ -0,0 +1,384 @@
import numpy as np
import torch
from PIL import Image
# FrameMaker 0.1.0 by diSty
class FrameMaker:
@classmethod
def INPUT_TYPES(cls):
return {
'required': {
'subject_image': ('IMAGE',),
'frame_count': ('INT', {'default': 4}),
"invert_alpha": (['true','false'], {"default": 'false'}),
"movement_presets": (movement,{"default": 'left to center',}),
'movement_presets_distance': ('INT', {'default': 50, 'min': -10000, 'max': 10000}),
"movement_direction": (['None','front', 'back'],{"default": 'back',}),
'x_pos_start': ('INT', {'default': 0, 'min': -10000, 'max': 10000}),
'x_pos_end': ('INT', {'default': 0, 'min': -10000, 'max': 10000}),
'y_pos_start': ('INT', {'default': 0, 'min': -10000, 'max': 10000}),
'y_pos_end': ('INT', {'default': 0, 'min': -10000, 'max': 10000}),
'scale_start': ('FLOAT', {'default': 1, 'min': 0.05, 'step': 0.05, 'max': 10}),
'scale_end': ('FLOAT', {'default': 1, 'min': 0.05, 'step': 0.05, 'max': 10}),
"scale_anchor_point": (['center','top center', 'bottom center', 'top left', 'top right', 'bottom left', 'bottom right'],),
},
"optional": {
"subject_alpha": ("MASK",),
'bg1': ('IMAGE',),
'bg2': ('IMAGE',),
'bg3': ('IMAGE',),
'bg4': ('IMAGE',),
'frames': ('IMAGE',),
"resize_frame_input": (['true','false'], {"default": 'true'}),
'frame_input_width': ('INT', {'default': 768, 'min': 16,'step': 16, 'max': 8192}),
'frame_input_height': ('INT', {'default': 768, 'min': 16,'step': 16, 'max': 8192}),
"resize_frame_output": (['true','false'], {"default": 'false'}),
'frame_output_width': ('INT', {'default': 512, 'min': 16,'step': 16, 'max': 8192}),
'frame_output_height': ('INT', {'default': 512, 'min': 16,'step': 16, 'max': 8192}),
"invert_order": (['true','false'], {"default": 'false'}),
# "limit_frames": (['true','false'], {"default": 'false'}),
# 'limit_frames_count': ('INT', {'default': 4, 'min': 1,'step': 1, 'max': 10000}),
# 'limit_frames_index': ('INT', {'default': 1, 'min': 1,'step': 1, 'max': 10000}),
}
}
RETURN_TYPES = ('IMAGE','IMAGE','INT', 'INT', 'INT',)
RETURN_NAMES = ('frames','subject_image', 'width', 'height','frame_count',)
FUNCTION = 'process_frames'
CATEGORY = '🅓 diSty/FrameMaker'
def process_frames(self, subject_image, frame_count,invert_alpha,movement_presets,movement_presets_distance,movement_direction,
x_pos_start, x_pos_end, y_pos_start, y_pos_end, scale_start, scale_end, scale_anchor_point,
subject_alpha=None,bg1=None, bg2=None, bg3=None, bg4=None,frames=None,width=0, height=0,resize_frame_input=0, frame_input_width=0,
frame_input_height=0,resize_frame_output=None, frame_output_width=0,frame_output_height=0,invert_order='false'
# , **kwargs
):
if subject_alpha is not None:
if invert_alpha == 'true':
subject_alpha = 1.0 - subject_alpha
subject_image = join_image_with_alpha(subject_image, subject_alpha)
foreground_image_pil = tensor2pil(subject_image)
backgrounds = [bg1, bg2, bg3, bg4]
backgrounds = [b for b in backgrounds if b is not None]
if frames is not None:
backgrounds = []
for i in range(frames.shape[0]):
frame_tensor = frames[i]
backgrounds.append(frame_tensor)
if resize_frame_input == 'true':
foreground_image_pil = foreground_image_pil.resize((frame_input_width, frame_input_height), Image.Resampling.LANCZOS)
width = frame_input_width
height = frame_input_height
resized_backgrounds_pil = []
for bg_tensor in backgrounds:
bg_pil = tensor2pil(bg_tensor)
resized_bg_pil = bg_pil.resize((frame_input_width, frame_input_height), Image.Resampling.LANCZOS)
resized_backgrounds_pil.append(resized_bg_pil)
movement_map = movement_map_f(movement_presets_distance)
if movement_presets != 'None' and movement_presets in movement_map:
preset = movement_map[movement_presets]
x_pos_start = preset['x_start']
x_pos_end = preset['x_end']
y_pos_start = preset['y_start']
y_pos_end = preset['y_end']
x_pos_start_pix = x_pos_start * foreground_image_pil.width / 100
x_pos_end_pix = x_pos_end * foreground_image_pil.width / 100
y_pos_start_pix = y_pos_start * foreground_image_pil.height / 100
y_pos_end_pix = y_pos_end * foreground_image_pil.height / 100
if movement_direction != 'None':
if movement_direction == 'front':
scale_start = .3
scale_end = 1.3
if movement_direction == 'back':
scale_start = 1.3
scale_end = .3
x_step = (x_pos_end_pix - x_pos_start_pix) / (frame_count - 1)
y_step = (y_pos_end_pix - y_pos_start_pix) / (frame_count - 1)
scale_step = (scale_end - scale_start) / (frame_count - 1)
result_images = []
for frame in range(frame_count):
if resize_frame_input == 'true':
background_image_pil = resized_backgrounds_pil[frame % len(resized_backgrounds_pil)]
else:
background_image_pil = tensor2pil(backgrounds[frame % len(backgrounds)])
scale = scale_start + frame * scale_step
scaled_width = int(foreground_image_pil.width * scale)
scaled_height = int(foreground_image_pil.height * scale)
foreground_resized = foreground_image_pil.resize((scaled_width, scaled_height), Image.Resampling.LANCZOS)
x_pos = x_pos_start_pix + frame * x_step
y_pos = y_pos_start_pix + frame * y_step
width_diff = (scaled_width - foreground_image_pil.width)
height_diff = (scaled_height - foreground_image_pil.height)
if 'center' in scale_anchor_point.lower():
x_pos -= width_diff // 2
if scale_anchor_point.lower() == 'center':
y_pos -= height_diff // 2
elif 'bottom' in scale_anchor_point.lower():
y_pos -= height_diff
if 'right' in scale_anchor_point.lower():
x_pos -= width_diff
frame_image = background_image_pil.copy()
if foreground_resized.mode != 'RGBA':
foreground_resized = foreground_resized.convert('RGBA')
frame_image.paste(foreground_resized, (int(x_pos), int(y_pos)), foreground_resized)
if resize_frame_output == 'true':
frame_image = frame_image.resize((frame_output_width, frame_output_height), Image.Resampling.LANCZOS)
width = frame_output_width
height = frame_output_height
result_images.append(pil2tensor(frame_image))
batch = torch.cat(result_images, dim=0)
if invert_order == 'true':
batch = batch.flip(dims=[0])
return (batch,) + (subject_image,) + (width,) + (height,) + (frame_count,)
class FrameMakerBatch:
@classmethod
def INPUT_TYPES(cls):
return {
'required': {
'frames': ('IMAGE',),
'subject_image': ('IMAGE',),
'frame_count': ('INT', {'default': 4}),
"invert_alpha": (['true','false'], {"default": 'false'}),
"movement_presets": (movement,{"default": 'left to center',}),
'movement_presets_distance': ('INT', {'default': 50, 'min': -10000, 'max': 10000}),
"movement_direction": (['None','front', 'back'],{"default": 'back',}),
'x_pos_start': ('INT', {'default': 0, 'min': -10000, 'max': 10000}),
'x_pos_end': ('INT', {'default': 0, 'min': -10000, 'max': 10000}),
'y_pos_start': ('INT', {'default': 0, 'min': -10000, 'max': 10000}),
'y_pos_end': ('INT', {'default': 0, 'min': -10000, 'max': 10000}),
'scale_start': ('FLOAT', {'default': 1, 'min': 0.05, 'step': 0.05, 'max': 10}),
'scale_end': ('FLOAT', {'default': 1, 'min': 0.05, 'step': 0.05, 'max': 10}),
"scale_anchor_point": (['center','top center', 'bottom center', 'top left', 'top right', 'bottom left', 'bottom right'],),
},
"optional": {
"subject_alpha": ("MASK",),
"resize_frame_input": (['true','false'], {"default": 'true'}),
'frame_input_width': ('INT', {'default': 768, 'min': 16,'step': 16, 'max': 8192}),
'frame_input_height': ('INT', {'default': 768, 'min': 16,'step': 16, 'max': 8192}),
"resize_frame_output": (['true','false'], {"default": 'false'}),
'frame_output_width': ('INT', {'default': 512, 'min': 16,'step': 16, 'max': 8192}),
'frame_output_height': ('INT', {'default': 512, 'min': 16,'step': 16, 'max': 8192}),
"invert_order": (['true','false'], {"default": 'false'}),
}
}
RETURN_TYPES = ('IMAGE','IMAGE','INT', 'INT', 'INT',)
RETURN_NAMES = ('frames','subject_image', 'width', 'height','frame_count',)
FUNCTION = 'process_frames'
CATEGORY = '🅓 diSty/FrameMaker'
def process_frames(self,frames,subject_image, frame_count,invert_alpha,movement_presets,movement_presets_distance,movement_direction,
x_pos_start, x_pos_end, y_pos_start, y_pos_end, scale_start, scale_end, scale_anchor_point,
subject_alpha=None,width=0, height=0,resize_frame_input=0, frame_input_width=0,
frame_input_height=0,resize_frame_output=None, frame_output_width=0,frame_output_height=0,invert_order='false'):
if subject_alpha is not None:
if invert_alpha == 'true':
subject_alpha = 1.0 - subject_alpha
subject_image = join_image_with_alpha(subject_image, subject_alpha)
foreground_image_pil = tensor2pil(subject_image)
if frames is not None:
backgrounds = []
for i in range(frames.shape[0]):
frame_tensor = frames[i]
backgrounds.append(frame_tensor)
if resize_frame_input == 'true':
foreground_image_pil = foreground_image_pil.resize((frame_input_width, frame_input_height), Image.Resampling.LANCZOS)
width = frame_input_width
height = frame_input_height
resized_backgrounds_pil = []
for bg_tensor in backgrounds:
bg_pil = tensor2pil(bg_tensor)
resized_bg_pil = bg_pil.resize((frame_input_width, frame_input_height), Image.Resampling.LANCZOS)
resized_backgrounds_pil.append(resized_bg_pil)
movement_map = movement_map_f(movement_presets_distance)
if movement_presets != 'None' and movement_presets in movement_map:
preset = movement_map[movement_presets]
x_pos_start = preset['x_start']
x_pos_end = preset['x_end']
y_pos_start = preset['y_start']
y_pos_end = preset['y_end']
x_pos_start_pix = x_pos_start * foreground_image_pil.width / 100
x_pos_end_pix = x_pos_end * foreground_image_pil.width / 100
y_pos_start_pix = y_pos_start * foreground_image_pil.height / 100
y_pos_end_pix = y_pos_end * foreground_image_pil.height / 100
if movement_direction != 'None':
if movement_direction == 'front':
scale_start = .3
scale_end = 1.3
if movement_direction == 'back':
scale_start = 1.3
scale_end = .3
x_step = (x_pos_end_pix - x_pos_start_pix) / (frame_count - 1)
y_step = (y_pos_end_pix - y_pos_start_pix) / (frame_count - 1)
scale_step = (scale_end - scale_start) / (frame_count - 1)
result_images = []
for frame in range(frame_count):
if resize_frame_input == 'true':
background_image_pil = resized_backgrounds_pil[frame % len(resized_backgrounds_pil)]
else:
background_image_pil = tensor2pil(backgrounds[frame % len(backgrounds)])
scale = scale_start + frame * scale_step
scaled_width = int(foreground_image_pil.width * scale)
scaled_height = int(foreground_image_pil.height * scale)
foreground_resized = foreground_image_pil.resize((scaled_width, scaled_height), Image.Resampling.LANCZOS)
x_pos = x_pos_start_pix + frame * x_step
y_pos = y_pos_start_pix + frame * y_step
width_diff = (scaled_width - foreground_image_pil.width)
height_diff = (scaled_height - foreground_image_pil.height)
if 'center' in scale_anchor_point.lower():
x_pos -= width_diff // 2
if scale_anchor_point.lower() == 'center':
y_pos -= height_diff // 2
elif 'bottom' in scale_anchor_point.lower():
y_pos -= height_diff
if 'right' in scale_anchor_point.lower():
x_pos -= width_diff
frame_image = background_image_pil.copy()
if foreground_resized.mode != 'RGBA':
foreground_resized = foreground_resized.convert('RGBA')
frame_image.paste(foreground_resized, (int(x_pos), int(y_pos)), foreground_resized)
if resize_frame_output == 'true':
frame_image = frame_image.resize((frame_output_width, frame_output_height), Image.Resampling.LANCZOS)
width = frame_output_width
height = frame_output_height
result_images.append(pil2tensor(frame_image))
batch = torch.cat(result_images, dim=0)
if invert_order == 'true':
batch = batch.flip(dims=[0])
return (batch,) + (subject_image,) + (width,) + (height,) + (frame_count,)
NODE_CLASS_MAPPINGS = {
"FrameMaker": FrameMaker,
"FrameMakerBatch": FrameMakerBatch
}
NODE_DISPLAY_NAME_MAPPINGS = {
"FrameMaker": f"🅓 Frame Maker ",
"FrameMakerBatch": f"🅓 Frame Maker Batch"
}
def tensor2pil(image):
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
def pil2tensor(image):
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
def resize_mask(mask, shape):
return torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(shape[0], shape[1]), mode="bilinear").squeeze(1)
def join_image_with_alpha(image: torch.Tensor, alpha: torch.Tensor):
batch_size = min(len(image), len(alpha))
out_images = []
alpha = 1.0 - resize_mask(alpha, image.shape[1:])
for i in range(batch_size):
out_images.append(torch.cat((image[i][:,:,:3], alpha[i].unsqueeze(2)), dim=2))
result = (torch.stack(out_images))
return result
def movement_map_f(movement_presets_distance):
movement_map = {
'left to center': {'x_start': -movement_presets_distance, 'x_end': 0, 'y_start': 0, 'y_end': 0},
'right to center': {'x_start': movement_presets_distance, 'x_end': 0, 'y_start': 0, 'y_end': 0},
'center to left': {'x_start': 0, 'x_end': -movement_presets_distance, 'y_start': 0, 'y_end': 0},
'center to right': {'x_start': 0, 'x_end': movement_presets_distance, 'y_start': 0, 'y_end': 0},
'left to right': {'x_start': -movement_presets_distance, 'x_end': movement_presets_distance, 'y_start': 0, 'y_end': 0},
'right to left': {'x_start': movement_presets_distance, 'x_end': -movement_presets_distance, 'y_start': 0, 'y_end': 0},
'top to center': {'x_start': 0, 'x_end': 0, 'y_start': -movement_presets_distance, 'y_end': 0},
'bottom to center': {'x_start': 0, 'x_end': 0, 'y_start': movement_presets_distance, 'y_end': 0},
'center to top': {'x_start': 0, 'x_end': 0, 'y_start': 0, 'y_end': -movement_presets_distance},
'center to bottom': {'x_start': 0, 'x_end': 0, 'y_start': 0, 'y_end': movement_presets_distance},
'top to bottom': {'x_start': 0, 'x_end': 0, 'y_start': -movement_presets_distance, 'y_end': movement_presets_distance},
'bottom to top': {'x_start': 0, 'x_end': 0, 'y_start': movement_presets_distance, 'y_end': -movement_presets_distance},
'top left to center': {'x_start': -movement_presets_distance, 'x_end': 0, 'y_start': -movement_presets_distance, 'y_end': 0},
'top right to center': {'x_start': movement_presets_distance, 'x_end': 0, 'y_start': -movement_presets_distance, 'y_end': 0},
'bottom left to center': {'x_start': -movement_presets_distance, 'x_end': 0, 'y_start': movement_presets_distance, 'y_end': 0},
'bottom right to center': {'x_start': movement_presets_distance, 'x_end': 0, 'y_start': movement_presets_distance, 'y_end': 0},
'center to top left': {'x_start': 0, 'x_end': -movement_presets_distance, 'y_start': 0, 'y_end': -movement_presets_distance},
'center to top right': {'x_start': 0, 'x_end': movement_presets_distance, 'y_start': 0, 'y_end': -movement_presets_distance},
'center to bottom left': {'x_start': 0, 'x_end': -movement_presets_distance, 'y_start': 0, 'y_end': movement_presets_distance},
'center to bottom right': {'x_start': 0, 'x_end': movement_presets_distance, 'y_start': 0, 'y_end': movement_presets_distance},
'top left to bottom right': {'x_start': -movement_presets_distance, 'x_end': movement_presets_distance, 'y_start': -movement_presets_distance, 'y_end': movement_presets_distance},
'bottom right to top left': {'x_start': movement_presets_distance, 'x_end': -movement_presets_distance, 'y_start': movement_presets_distance, 'y_end': -movement_presets_distance},
'top right to bottom left': {'x_start': movement_presets_distance, 'x_end': -movement_presets_distance, 'y_start': -movement_presets_distance, 'y_end': movement_presets_distance},
'bottom left to top right': {'x_start': -movement_presets_distance, 'x_end': movement_presets_distance, 'y_start': movement_presets_distance, 'y_end': -movement_presets_distance},
}
return movement_map
movement = ['None',
'left to center',
'right to center',
'center to left',
'center to right',
'left to right',
'right to left',
'top to center',
'bottom to center',
'top to bottom',
'bottom to top',
'center to top',
'center to bottom',
'top left to center',
'top right to center',
'bottom left to center',
'bottom right to center',
'center to top left',
'center to top right',
'center to bottom left',
'center to bottom right',
'top left to bottom right',
'bottom right to top left',
'top right to bottom left',
'bottom left to top right'
]
+158
View File
@@ -0,0 +1,158 @@
<!-- Improved compatibility of back to top link: See: https://github.com/othneildrew/Best-README-Template/pull/73 -->
<a name="readme-top"></a>
<!--
*** Thanks for checking out the Best-README-Template. If you have a suggestion
*** that would make this better, please fork the repo and create a pull request
*** or simply open an issue with the tag "enhancement".
*** Don't forget to give the project a star!
*** Thanks again! Now go create something AMAZING! :D
-->
<!--
*** I'm using markdown "reference style" links for readability.
*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).
*** See the bottom of this document for the declaration of the reference variables
*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.
*** https://www.markdownguide.org/basic-syntax/#reference-style-links
-->
<br />
<div align="center">
[![](media/svg/patreon.svg)](https://www.patreon.com/distyx)
<!-- <img src="media/covers/cover.jpg"> -->
<h1 align="center">ComfyUI Frame Maker</h1>
<h4 align="center"><em>Add control and cool effects into your animation.</em></h4>
<h4 align="center">Don't forget to leave a star.</h4>
<p align="center">
<a href="https://github.com/diStyApps/ComfyUI_FrameMaker/issues">Report Bug</a>
</p>
</div>
<!-- TABLE OF CONTENTS -->
<details>
<summary>Table of Contents</summary>
<ol>
<li>
<a href="#about-the-project">About The Project</a>
</li>
<li><a href="#requirements">Requirements</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#my-other-projects">My Other Projects</a></li>
</ol>
</details>
<!-- ABOUT THE PROJECT -->
## About The Project
### This node creates a sequence of frames by moving and scaling a subject image over a background image.
## <a href="workflows/frame_maker_basic.json">Basic frame maker workflow</a>
<img src="workflows/frame_maker_basic.png">
## <a href="workflows/frame_maker_batch">Basic batch frame maker workflow</a>
<img src="media/preview/frame_maker_1.gif">
## <a href="workflows/frame_maker_basic.json">layerdiffusion frame maker workflow</a>
<img src="media/preview/frame_maker_2.gif">
## frame maker + splash workflow (soon)
<img src="media/preview/frame_maker_3.gif">
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- Requirements -->
## Requirements
- [CompfyUI](https://github.com/comfyanonymous/ComfyUI)
### Recommended
- [ComfyUI-Manager](https://github.com/ltdrdata/ComfyUI-Manager)
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- Requirements -->
## Install
```
cd custom_nodes
git clone https://github.com/diStyApps/ComfyUI_FrameMaker.git
```
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- CONTACT -->
## Contact
distty@gmail.com
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- MY OTHER PROJECTS -->
## My Other Projects
[SEAIT](https://github.com/diStyApps/seait)
[VisionCrafter](https://github.com/diStyApps/VisionCrafter)
[FaceSwapSuite](https://github.com/diStyApps/FaceSwapSuite)
[VisualClipPicker](https://github.com/diStyApps/VisualClipPicker)
[Stable-Diffusion-Pickle-Scanner-GUI](https://github.com/diStyApps/Stable-Diffusion-Pickle-Scanner-GUI)
[Safe-and-Stable-Ckpt2Safetensors-Conversion-Tool-GUI](https://github.com/diStyApps/Safe-and-Stable-Ckpt2Safetensors-Conversion-Tool-GUI)
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- MARKDOWN LINKS & IMAGES -->
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
[contributors-shield]: https://img.shields.io/github/contributors/othneildrew/Best-README-Template.svg?style=for-the-badge
[contributors-url]: https://github.com/othneildrew/Best-README-Template/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/othneildrew/Best-README-Template.svg?style=for-the-badge
[forks-url]: https://github.com/othneildrew/Best-README-Template/network/members
[stars-shield]: https://img.shields.io/github/stars/othneildrew/Best-README-Template.svg?style=for-the-badge
[stars-url]: https://github.com/othneildrew/Best-README-Template/stargazers
[issues-shield]: https://img.shields.io/github/issues/othneildrew/Best-README-Template.svg?style=for-the-badge
[issues-url]: https://github.com/othneildrew/Best-README-Template/issues
[license-shield]: https://img.shields.io/github/license/othneildrew/Best-README-Template.svg?style=for-the-badge
[license-url]: https://github.com/othneildrew/Best-README-Template/blob/master/LICENSE.txt
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
[linkedin-url]: https://linkedin.com/in/othneildrew
[product-screenshot]: images/screenshot.png
[Next.js]: https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white
[Next-url]: https://nextjs.org/
[React.js]: https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB
[React-url]: https://reactjs.org/
[Vue.js]: https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge&logo=vuedotjs&logoColor=4FC08D
[Vue-url]: https://vuejs.org/
[Angular.io]: https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular&logoColor=white
[Angular-url]: https://angular.io/
[Svelte.dev]: https://img.shields.io/badge/Svelte-4A4A55?style=for-the-badge&logo=svelte&logoColor=FF3E00
[Svelte-url]: https://svelte.dev/
[Laravel.com]: https://img.shields.io/badge/Laravel-FF2D20?style=for-the-badge&logo=laravel&logoColor=white
[Laravel-url]: https://laravel.com
[Bootstrap.com]: https://img.shields.io/badge/Bootstrap-563D7C?style=for-the-badge&logo=bootstrap&logoColor=white
[Bootstrap-url]: https://getbootstrap.com
[JQuery.com]: https://img.shields.io/badge/jQuery-0769AD?style=for-the-badge&logo=jquery&logoColor=white
[JQuery-url]: https://jquery.com
+6
View File
@@ -0,0 +1,6 @@
from .FrameMaker import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
WEB_DIRECTORY = "js"
CC_VERSION = 2.0
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'CC_VERSION']
+66
View File
@@ -0,0 +1,66 @@
import { app } from "../../scripts/app.js";
const COLOR_THEMES = {
red: { nodeColor: "#332222", nodeBgColor: "#553333" },
green: { nodeColor: "#223322", nodeBgColor: "#335533" },
blue: { nodeColor: "#222233", nodeBgColor: "#333355" },
pale_blue: { nodeColor: "#2a363b", nodeBgColor: "#3f5159" },
cyan: { nodeColor: "#223333", nodeBgColor: "#335555" },
purple: { nodeColor: "#332233", nodeBgColor: "#553355" },
yellow: { nodeColor: "#443322", nodeBgColor: "#665533" },
// purple_custom: { nodeColor: "#7345b0" , nodeBgColor: "#482b6e" },
purple_custom: { nodeColor: "#111111" , nodeBgColor: "#482b6e" },
none: { nodeColor: null, nodeBgColor: null } // no color
};
const NODE_COLORS = {
"FrameMaker": "purple_custom",
"FrameMakerBatch": "purple_custom",
};
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]]; // Swap elements
}
}
let colorKeys = Object.keys(COLOR_THEMES).filter(key => key !== "none");
shuffleArray(colorKeys); // Shuffle the color themes initially
function setNodeColors(node, theme) {
if (!theme) {return;}
node.shape = "box";
if(theme.nodeColor && theme.nodeBgColor) {
node.color = theme.nodeColor;
node.bgcolor = theme.nodeBgColor;
}
}
const ext = {
name: "disty.appearance",
nodeCreated(node) {
const nclass = node.comfyClass;
if (NODE_COLORS.hasOwnProperty(nclass)) {
let colorKey = NODE_COLORS[nclass];
if (colorKey === "random") {
// Check for a valid color key before popping
if (colorKeys.length === 0 || !COLOR_THEMES[colorKeys[colorKeys.length - 1]]) {
colorKeys = Object.keys(COLOR_THEMES).filter(key => key !== "none");
shuffleArray(colorKeys);
}
colorKey = colorKeys.pop();
}
const theme = COLOR_THEMES[colorKey];
setNodeColors(node, theme);
}
}
};
app.registerExtension(ext);
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

+271
View File
@@ -0,0 +1,271 @@
{
"last_node_id": 16,
"last_link_id": 28,
"nodes": [
{
"id": 2,
"type": "LoadImage",
"pos": [
120,
70
],
"size": [
240,
310
],
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
1
],
"shape": 3
},
{
"name": "MASK",
"type": "MASK",
"links": [
2
],
"shape": 3,
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"ComfyUI_13306_.png",
"image"
],
"shape": 1
},
{
"id": 4,
"type": "LoadImage",
"pos": [
110,
430
],
"size": {
"0": 250,
"1": 350
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
4
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": [],
"shape": 3,
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"gm_forest_00008_.png",
"image"
],
"shape": 1
},
{
"id": 1,
"type": "FrameMaker",
"pos": [
400,
70
],
"size": {
"0": 342.5999755859375,
"1": 610
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "subject_image",
"type": "IMAGE",
"link": 1,
"slot_index": 0
},
{
"name": "subject_alpha",
"type": "MASK",
"link": 2
},
{
"name": "bg1",
"type": "IMAGE",
"link": 4,
"slot_index": 2
},
{
"name": "bg2",
"type": "IMAGE",
"link": null
},
{
"name": "bg3",
"type": "IMAGE",
"link": null
},
{
"name": "bg4",
"type": "IMAGE",
"link": null
},
{
"name": "frames",
"type": "IMAGE",
"link": null
}
],
"outputs": [
{
"name": "frames",
"type": "IMAGE",
"links": [
28
],
"shape": 3,
"slot_index": 0
},
{
"name": "subject_image",
"type": "IMAGE",
"links": [],
"shape": 3,
"slot_index": 1
},
{
"name": "width",
"type": "INT",
"links": null,
"shape": 3
},
{
"name": "height",
"type": "INT",
"links": null,
"shape": 3
},
{
"name": "frame_count",
"type": "INT",
"links": [],
"shape": 3,
"slot_index": 4
}
],
"properties": {
"Node name for S&R": "FrameMaker"
},
"widgets_values": [
4,
"false",
"center to right",
50,
"front",
0,
0,
0,
0,
1,
1,
"center",
"true",
768,
768,
"false",
512,
512,
"false"
],
"color": "#111111",
"bgcolor": "#482b6e",
"shape": 1
},
{
"id": 16,
"type": "PreviewImage",
"pos": [
780,
70
],
"size": {
"0": 590,
"1": 610
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 28
}
],
"properties": {
"Node name for S&R": "PreviewImage"
},
"shape": 1
}
],
"links": [
[
1,
2,
0,
1,
0,
"IMAGE"
],
[
2,
2,
1,
1,
1,
"MASK"
],
[
4,
4,
0,
1,
2,
"IMAGE"
],
[
28,
1,
0,
16,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 577 KiB

+768
View File
@@ -0,0 +1,768 @@
{
"last_node_id": 15,
"last_link_id": 27,
"nodes": [
{
"id": 2,
"type": "LoadImage",
"pos": [
20,
60
],
"size": [
320,
310
],
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
1
],
"shape": 3
},
{
"name": "MASK",
"type": "MASK",
"links": [
2
],
"shape": 3,
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"ComfyUI_13306_.png",
"image"
],
"shape": 1
},
{
"id": 10,
"type": "FrameMakerBatch",
"pos": [
740,
70
],
"size": [
350,
590
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "frames",
"type": "IMAGE",
"link": 8,
"slot_index": 0
},
{
"name": "subject_image",
"type": "IMAGE",
"link": 9
},
{
"name": "subject_alpha",
"type": "MASK",
"link": null
},
{
"name": "frame_count",
"type": "INT",
"link": 11,
"widget": {
"name": "frame_count"
}
}
],
"outputs": [
{
"name": "frames",
"type": "IMAGE",
"links": [
15
],
"shape": 3,
"slot_index": 0
},
{
"name": "subject_image",
"type": "IMAGE",
"links": [
16
],
"shape": 3,
"slot_index": 1
},
{
"name": "width",
"type": "INT",
"links": null,
"shape": 3
},
{
"name": "height",
"type": "INT",
"links": null,
"shape": 3
},
{
"name": "frame_count",
"type": "INT",
"links": [
17
],
"shape": 3,
"slot_index": 4
}
],
"properties": {
"Node name for S&R": "FrameMakerBatch"
},
"widgets_values": [
4,
"false",
"top right to bottom left",
50,
"front",
0,
0,
0,
0,
1,
1,
"center",
"true",
768,
768,
"false",
512,
512,
"false"
],
"color": "#111111",
"bgcolor": "#482b6e",
"shape": 1
},
{
"id": 1,
"type": "FrameMaker",
"pos": [
370,
60
],
"size": {
"0": 342.5999755859375,
"1": 610
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "subject_image",
"type": "IMAGE",
"link": 1,
"slot_index": 0
},
{
"name": "subject_alpha",
"type": "MASK",
"link": 2
},
{
"name": "bg1",
"type": "IMAGE",
"link": 4,
"slot_index": 2
},
{
"name": "bg2",
"type": "IMAGE",
"link": null
},
{
"name": "bg3",
"type": "IMAGE",
"link": null
},
{
"name": "bg4",
"type": "IMAGE",
"link": null
},
{
"name": "frames",
"type": "IMAGE",
"link": null
}
],
"outputs": [
{
"name": "frames",
"type": "IMAGE",
"links": [
8
],
"shape": 3,
"slot_index": 0
},
{
"name": "subject_image",
"type": "IMAGE",
"links": [
9
],
"shape": 3,
"slot_index": 1
},
{
"name": "width",
"type": "INT",
"links": null,
"shape": 3
},
{
"name": "height",
"type": "INT",
"links": null,
"shape": 3
},
{
"name": "frame_count",
"type": "INT",
"links": [
11
],
"shape": 3,
"slot_index": 4
}
],
"properties": {
"Node name for S&R": "FrameMaker"
},
"widgets_values": [
16,
"false",
"left to center",
50,
"front",
0,
0,
0,
0,
1,
1,
"center",
"true",
768,
768,
"false",
512,
512,
"false"
],
"color": "#111111",
"bgcolor": "#482b6e",
"shape": 1
},
{
"id": 11,
"type": "FrameMakerBatch",
"pos": [
1110,
70
],
"size": {
"0": 350,
"1": 590
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "frames",
"type": "IMAGE",
"link": 15,
"slot_index": 0
},
{
"name": "subject_image",
"type": "IMAGE",
"link": 16
},
{
"name": "subject_alpha",
"type": "MASK",
"link": null
},
{
"name": "frame_count",
"type": "INT",
"link": 17,
"widget": {
"name": "frame_count"
}
}
],
"outputs": [
{
"name": "frames",
"type": "IMAGE",
"links": [
22
],
"shape": 3,
"slot_index": 0
},
{
"name": "subject_image",
"type": "IMAGE",
"links": [
23
],
"shape": 3,
"slot_index": 1
},
{
"name": "width",
"type": "INT",
"links": null,
"shape": 3
},
{
"name": "height",
"type": "INT",
"links": [],
"shape": 3,
"slot_index": 3
},
{
"name": "frame_count",
"type": "INT",
"links": [
25
],
"shape": 3,
"slot_index": 4
}
],
"properties": {
"Node name for S&R": "FrameMakerBatch"
},
"widgets_values": [
4,
"false",
"center to right",
50,
"back",
0,
0,
0,
0,
1,
1,
"center",
"true",
768,
768,
"false",
512,
512,
"false"
],
"color": "#111111",
"bgcolor": "#482b6e",
"shape": 1
},
{
"id": 12,
"type": "FrameMakerBatch",
"pos": [
1480,
70
],
"size": {
"0": 350,
"1": 590
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "frames",
"type": "IMAGE",
"link": 22,
"slot_index": 0
},
{
"name": "subject_image",
"type": "IMAGE",
"link": 23
},
{
"name": "subject_alpha",
"type": "MASK",
"link": null
},
{
"name": "frame_count",
"type": "INT",
"link": 25,
"widget": {
"name": "frame_count"
}
}
],
"outputs": [
{
"name": "frames",
"type": "IMAGE",
"links": [
21
],
"shape": 3,
"slot_index": 0
},
{
"name": "subject_image",
"type": "IMAGE",
"links": null,
"shape": 3
},
{
"name": "width",
"type": "INT",
"links": null,
"shape": 3
},
{
"name": "height",
"type": "INT",
"links": null,
"shape": 3
},
{
"name": "frame_count",
"type": "INT",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "FrameMakerBatch"
},
"widgets_values": [
4,
"false",
"bottom left to center",
50,
"front",
0,
0,
0,
0,
1,
1,
"center",
"true",
768,
768,
"false",
512,
512,
"false"
],
"color": "#111111",
"bgcolor": "#482b6e",
"shape": 1
},
{
"id": 6,
"type": "RIFE VFI",
"pos": [
1850,
70
],
"size": {
"0": 319.9336853027344,
"1": 309.7534484863281
},
"flags": {
"collapsed": true
},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "frames",
"type": "IMAGE",
"link": 21
},
{
"name": "optional_interpolation_states",
"type": "INTERPOLATION_STATES",
"link": null
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
27
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "RIFE VFI"
},
"widgets_values": [
"rife47.pth",
10,
2,
true,
true,
1
],
"shape": 1
},
{
"id": 4,
"type": "LoadImage",
"pos": [
20,
420
],
"size": [
320,
410
],
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
4
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": [],
"shape": 3,
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"gm_forest_00008_.png",
"image"
],
"shape": 1
},
{
"id": 15,
"type": "VHS_VideoCombine",
"pos": [
1850,
110
],
"size": [
530,
810
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 27
},
{
"name": "audio",
"type": "VHS_AUDIO",
"link": null
},
{
"name": "batch_manager",
"type": "VHS_BatchManager",
"link": null
}
],
"outputs": [
{
"name": "Filenames",
"type": "VHS_FILENAMES",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "VHS_VideoCombine"
},
"widgets_values": {
"frame_rate": 32,
"loop_count": 0,
"filename_prefix": "AnimateDiff",
"format": "video/h264-mp4",
"pix_fmt": "yuv420p",
"crf": 19,
"save_metadata": true,
"pingpong": false,
"save_output": true,
"videopreview": {
"hidden": false,
"paused": false,
"params": {
"filename": "AnimateDiff_00002.mp4",
"subfolder": "",
"type": "output",
"format": "video/h264-mp4"
}
}
},
"shape": 1
}
],
"links": [
[
1,
2,
0,
1,
0,
"IMAGE"
],
[
2,
2,
1,
1,
1,
"MASK"
],
[
4,
4,
0,
1,
2,
"IMAGE"
],
[
8,
1,
0,
10,
0,
"IMAGE"
],
[
9,
1,
1,
10,
1,
"IMAGE"
],
[
11,
1,
4,
10,
3,
"INT"
],
[
15,
10,
0,
11,
0,
"IMAGE"
],
[
16,
10,
1,
11,
1,
"IMAGE"
],
[
17,
10,
4,
11,
3,
"INT"
],
[
21,
12,
0,
6,
0,
"IMAGE"
],
[
22,
11,
0,
12,
0,
"IMAGE"
],
[
23,
11,
1,
12,
1,
"IMAGE"
],
[
25,
11,
4,
12,
3,
"INT"
],
[
27,
6,
0,
15,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 462 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 582 KiB