diff --git a/py/constrain_image_for_video.py b/py/constrain_image_for_video.py new file mode 100644 index 0000000..91c0c50 --- /dev/null +++ b/py/constrain_image_for_video.py @@ -0,0 +1,72 @@ +import torch +import numpy as np +from PIL import Image + +class ConstrainImageforVideo: + """ + A node that constrains an image to a maximum and minimum size while maintaining aspect ratio. + """ + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + "max_width": ("INT", {"default": 1024, "min": 0}), + "max_height": ("INT", {"default": 1024, "min": 0}), + "min_width": ("INT", {"default": 0, "min": 0}), + "min_height": ("INT", {"default": 0, "min": 0}), + "crop_if_required": (["yes", "no"], {"default": "no"}), + }, + } + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("IMAGE",) + FUNCTION = "constrain_image_for_video" + CATEGORY = "image" + + def constrain_image_for_video(self, images, max_width, max_height, min_width, min_height, crop_if_required): + crop_if_required = crop_if_required == "yes" + results = [] + for image in images: + i = 255. * image.cpu().numpy() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)).convert("RGB") + + current_width, current_height = img.size + aspect_ratio = current_width / current_height + + constrained_width = max(min(current_width, min_width), max_width) + constrained_height = max(min(current_height, min_height), max_height) + + if constrained_width / constrained_height > aspect_ratio: + constrained_width = max(int(constrained_height * aspect_ratio), min_width) + if crop_if_required: + constrained_height = int(current_height / (current_width / constrained_width)) + else: + constrained_height = max(int(constrained_width / aspect_ratio), min_height) + if crop_if_required: + constrained_width = int(current_width / (current_height / constrained_height)) + + resized_image = img.resize((constrained_width, constrained_height), Image.LANCZOS) + + if crop_if_required and (constrained_width > max_width or constrained_height > max_height): + left = max((constrained_width - max_width) // 2, 0) + top = max((constrained_height - max_height) // 2, 0) + right = min(constrained_width, max_width) + left + bottom = min(constrained_height, max_height) + top + resized_image = resized_image.crop((left, top, right, bottom)) + + resized_image = np.array(resized_image).astype(np.float32) / 255.0 + resized_image = torch.from_numpy(resized_image)[None,] + results.append(resized_image) + all_images = torch.cat(results, dim=0) + + return (all_images, all_images.size(0),) + +NODE_CLASS_MAPPINGS = { + "ConstrainImageforVideo|pysssss": ConstrainImageforVideo, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "ConstrainImageforVideo|pysssss": "Constrain Image for Video 🐍", +} diff --git a/web/js/betterCombos.js b/web/js/betterCombos.js index bf3690d..cdea67a 100644 --- a/web/js/betterCombos.js +++ b/web/js/betterCombos.js @@ -27,10 +27,11 @@ app.registerExtension({ left: 0; top: 0; transform: translate(-100%, 0); - width: 256px; - height: 256px; - background-size: cover; - background-position: center; + width: 384px; + height: 384px; + background-size: contain; + background-position: top right; + background-repeat: no-repeat; filter: brightness(65%); } `, diff --git a/web/js/imageFeed.js b/web/js/imageFeed.js index b477912..e214d22 100644 --- a/web/js/imageFeed.js +++ b/web/js/imageFeed.js @@ -410,7 +410,7 @@ app.registerExtension({ api.addEventListener("executed", ({ detail }) => { if (visible && detail?.output?.images) { for (const src of detail.output.images) { - const href = `/view?filename=${encodeURIComponent(src.filename)}&type=${ + const href = `./view?filename=${encodeURIComponent(src.filename)}&type=${ src.type }&subfolder=${encodeURIComponent(src.subfolder)}&t=${+new Date()}`; diff --git a/web/js/showImageOnMenu.js b/web/js/showImageOnMenu.js index 7e153eb..4fed9de 100644 --- a/web/js/showImageOnMenu.js +++ b/web/js/showImageOnMenu.js @@ -52,7 +52,7 @@ const ext = { if (!images || !images.length) return; const format = app.getPreviewFormatParam(); const src = [ - `/view?filename=${encodeURIComponent(images[0].filename)}`, + `./view?filename=${encodeURIComponent(images[0].filename)}`, `type=${images[0].type}`, `subfolder=${encodeURIComponent(images[0].subfolder)}`, `t=${+new Date()}${format}`,].join('&'); diff --git a/web/js/workflows.js b/web/js/workflows.js index df84c51..b979f6e 100644 --- a/web/js/workflows.js +++ b/web/js/workflows.js @@ -81,6 +81,9 @@ async function saveWorkflow(name, workflow, overwrite) { class PysssssWorkflows { async load() { this.workflows = await getWorkflows(); + if(this.workflows.length) { + this.workflows.sort(); + } this.loadMenu.style.display = this.workflows.length ? "flex" : "none"; }