Initial commit: Fast Preview node
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.DS_Store
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
from .fast_preview import FastPreviewImage
|
||||
|
||||
WEB_DIRECTORY = "./web"
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"FastPreviewImage": FastPreviewImage
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"FastPreviewImage": "Fast Preview & Optimize"
|
||||
}
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY']
|
||||
@@ -0,0 +1,68 @@
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import folder_paths
|
||||
|
||||
class FastPreviewImage:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"images": ("IMAGE",),
|
||||
"image_format": (["webp", "jpeg", "png"], {"default": "webp"}),
|
||||
"quality": ("INT", {"default": 80, "min": 1, "max": 100}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
OUTPUT_NODE = True
|
||||
FUNCTION = "save_images"
|
||||
CATEGORY = "image"
|
||||
|
||||
def save_images(self, images, image_format="webp", quality=80):
|
||||
results = list()
|
||||
file_sizes = list()
|
||||
temp_dir = folder_paths.get_temp_directory()
|
||||
|
||||
for batch_number, image in enumerate(images):
|
||||
# Convert tensor to PIL Image
|
||||
i = 255. * image.cpu().numpy()
|
||||
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
||||
|
||||
if image_format == "jpeg" and img.mode in ("RGBA", "P"):
|
||||
img = img.convert("RGB")
|
||||
|
||||
# Generate random 8-character string for the prefix
|
||||
prefix = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
|
||||
filename = f"fast_preview_{prefix}_{batch_number}.{image_format}"
|
||||
file_path = os.path.join(temp_dir, filename)
|
||||
|
||||
# Determine compression kwargs based on format
|
||||
kwargs = {}
|
||||
if image_format == "webp":
|
||||
kwargs = {"quality": quality, "method": 2}
|
||||
elif image_format == "jpeg":
|
||||
kwargs = {"quality": quality, "optimize": True}
|
||||
elif image_format == "png":
|
||||
kwargs = {"compress_level": max(0, min(9, int((100 - quality) / 10)))}
|
||||
|
||||
# Save the image
|
||||
img.save(file_path, **kwargs)
|
||||
|
||||
# Calculate file size
|
||||
size_in_bytes = os.path.getsize(file_path)
|
||||
if size_in_bytes < 1024 * 1024:
|
||||
size_str = f"{size_in_bytes / 1024:.2f} KB"
|
||||
else:
|
||||
size_str = f"{size_in_bytes / (1024 * 1024):.2f} MB"
|
||||
|
||||
file_sizes.append(size_str)
|
||||
results.append({
|
||||
"filename": filename,
|
||||
"subfolder": "",
|
||||
"type": "temp"
|
||||
})
|
||||
|
||||
return {"ui": {"images": results, "file_sizes": file_sizes}}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { ComfyWidgets } from "../../scripts/widgets.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.FastPreviewImage",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData.name === "FastPreviewImage") {
|
||||
const onExecuted = nodeType.prototype.onExecuted;
|
||||
|
||||
nodeType.prototype.onExecuted = function(message) {
|
||||
const r = onExecuted ? onExecuted.apply(this, arguments) : undefined;
|
||||
|
||||
if (message?.file_sizes) {
|
||||
let widget = this.widgets?.find((w) => w.name === "file_size_display");
|
||||
|
||||
if (!widget) {
|
||||
const widgetObj = ComfyWidgets["STRING"](this, "file_size_display", ["STRING", { multiline: false }], app);
|
||||
widget = widgetObj.widget || widgetObj;
|
||||
|
||||
if (widget && widget.inputEl) {
|
||||
widget.inputEl.readOnly = true;
|
||||
widget.inputEl.style.opacity = "0.8";
|
||||
widget.inputEl.style.textAlign = "center";
|
||||
}
|
||||
}
|
||||
|
||||
if (widget) {
|
||||
widget.value = `Size: ${message.file_sizes.join(" | ")}`;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user