first commit
This commit is contained in:
+384
@@ -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'
|
||||
]
|
||||
Reference in New Issue
Block a user