Add PreviewAnimation -node

This commit is contained in:
Kijai 2024-05-07 14:07:24 +03:00
parent a93b8687ac
commit 43dded2f42
3 changed files with 103 additions and 0 deletions

View File

@ -57,6 +57,7 @@ NODE_CONFIG = {
"ImageUpscaleWithModelBatched": {"class": ImageUpscaleWithModelBatched, "name": "Image Upscale With Model Batched"},
"InsertImagesToBatchIndexed": {"class": InsertImagesToBatchIndexed, "name": "Insert Images To Batch Indexed"},
"MergeImageChannels": {"class": MergeImageChannels, "name": "Merge Image Channels"},
"PreviewAnimation": {"class": PreviewAnimation, "name": "Preview Animation"},
"RemapImageRange": {"class": RemapImageRange, "name": "Remap Image Range"},
"ReverseImageBatch": {"class": ReverseImageBatch, "name": "Reverse Image Batch"},
"ReplaceImagesInBatch": {"class": ReplaceImagesInBatch, "name": "Replace Images In Batch"},

View File

@ -1074,3 +1074,87 @@ with the **inputcount** and clicking update.
new_image = kwargs[f"image_{c + 1}"]
image, = image_batch_node.batch(image, new_image)
return (image,)
class PreviewAnimation:
def __init__(self):
self.output_dir = folder_paths.get_temp_directory()
self.type = "temp"
self.prefix_append = "_temp_" + ''.join(random.choice("abcdefghijklmnopqrstupvxyz") for x in range(5))
self.compress_level = 1
methods = {"default": 4, "fastest": 0, "slowest": 6}
@classmethod
def INPUT_TYPES(s):
return {"required":
{
"fps": ("FLOAT", {"default": 8.0, "min": 0.01, "max": 1000.0, "step": 0.01}),
},
"optional": {
"images": ("IMAGE", ),
"masks": ("MASK", ),
},
}
RETURN_TYPES = ()
FUNCTION = "preview"
OUTPUT_NODE = True
CATEGORY = "KJNodes/image"
def preview(self, fps, images=None, masks=None):
filename_prefix = "AnimPreview"
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir)
results = list()
pil_images = []
if images is not None and masks is not None:
for image in images:
i = 255. * image.cpu().numpy()
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
pil_images.append(img)
for mask in masks:
if pil_images:
mask_np = mask.cpu().numpy()
mask_np = np.clip(mask_np * 255, 0, 255).astype(np.uint8) # Convert to values between 0 and 255
mask_img = Image.fromarray(mask_np, mode='L')
img = pil_images.pop(0) # Remove and get the first image
img = img.convert("RGBA") # Convert base image to RGBA
# Create a new RGBA image based on the grayscale mask
rgba_mask_img = Image.new("RGBA", img.size, (255, 255, 255, 255))
rgba_mask_img.putalpha(mask_img) # Use the mask image as the alpha channel
# Composite the RGBA mask onto the base image
composited_img = Image.alpha_composite(img, rgba_mask_img)
pil_images.append(composited_img) # Add the composited image back
elif images is not None and masks is None:
for image in images:
i = 255. * image.cpu().numpy()
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
pil_images.append(img)
elif masks is not None and images is None:
for mask in masks:
mask_np = 255. * mask.cpu().numpy()
mask_img = Image.fromarray(np.clip(mask_np, 0, 255).astype(np.uint8))
pil_images.append(mask_img)
else:
print("PreviewAnimation: No images or masks provided")
return { "ui": { "images": results, "animated": (None,), "text": "empty" }}
num_frames = len(pil_images)
c = len(pil_images)
for i in range(0, c, num_frames):
file = f"{filename}_{counter:05}_.webp"
pil_images[i].save(os.path.join(full_output_folder, file), save_all=True, duration=int(1000.0/fps), append_images=pil_images[i + 1:i + num_frames], lossless=False, quality=80, method=4)
results.append({
"filename": file,
"subfolder": subfolder,
"type": self.type
})
counter += 1
animated = num_frames != 1
return { "ui": { "images": results, "animated": (animated,), "text": [f"{num_frames}x{pil_images[0].size[0]}x{pil_images[0].size[1]}"] } }

View File

@ -113,6 +113,24 @@ app.registerExtension({
return r
}
break;
case "PreviewAnimation":
const onPreviewAnimationConnectInput = nodeType.prototype.onConnectInput;
nodeType.prototype.onConnectInput = function (targetSlot, type, output, originNode, originSlot) {
const v = onPreviewAnimationConnectInput?.(this, arguments);
targetSlot.title = "Preview Animation"
return v;
}
const onPreviewAnimationExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function(message) {
const r = onPreviewAnimationExecuted? onPreviewAnimationExecuted.apply(this,arguments): undefined
let values = message["text"].toString();
console.log(this)
this.title = "Preview Animation " + values
return r
}
break;
case "VRAM_Debug":
const onVRAM_DebugConnectInput = nodeType.prototype.onConnectInput;
nodeType.prototype.onConnectInput = function (targetSlot, type, output, originNode, originSlot) {