From 1ff09e554170b8083292e309fd13a0642170d88e Mon Sep 17 00:00:00 2001 From: "Dr.Lt.Data" Date: Fri, 19 May 2023 23:51:59 +0900 Subject: [PATCH] apply latent_to_rgb for default preview apply optimize for png attach format text for preview --- comfy/utils.py | 18 +++++++++++++++ logo.png | Bin 8248 -> 0 bytes misc/latent.png | Bin 0 -> 2814 bytes nodes.py | 58 ++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 67 insertions(+), 9 deletions(-) delete mode 100644 logo.png create mode 100644 misc/latent.png diff --git a/comfy/utils.py b/comfy/utils.py index 09e05d4ed..19b5cf19e 100644 --- a/comfy/utils.py +++ b/comfy/utils.py @@ -1,5 +1,6 @@ import torch import math +from PIL import Image def load_torch_file(ckpt, safe_load=False): if ckpt.lower().endswith(".safetensors"): @@ -117,3 +118,20 @@ class ProgressBar: def update(self, value): self.update_absolute(self.current + value) + + +def latent_to_rgb(latent_tensor): + latent_rgb_factors = torch.tensor([ + # R G B + [0.298, 0.207, 0.208], # L1 + [0.187, 0.286, 0.173], # L2 + [-0.158, 0.189, 0.264], # L3 + [-0.184, -0.271, -0.473], # L4 + ], device="cpu") + rgb = torch.einsum('...lhw,lr -> ...rhw', latent_tensor.cpu().float(), latent_rgb_factors) + tensor = (((rgb + 1) / 2) + .clamp(0, 1) # change scale from -1..1 to 0..1 + .mul(0xFF) # to 0..255 + .byte()) + + return Image.fromarray(tensor.movedim(1, -1)[0].cpu().numpy()) diff --git a/logo.png b/logo.png deleted file mode 100644 index e85d4d70670f47a34cd5159225d7372665788b5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8248 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 diff --git a/misc/latent.png b/misc/latent.png new file mode 100644 index 0000000000000000000000000000000000000000..19fed324a25a7e1a2252400e7752ce5586742429 GIT binary patch literal 2814 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 QSO5S307*qoM6N<$g27oX=l}o! literal 0 HcmV?d00001 diff --git a/nodes.py b/nodes.py index ba6f1fe2a..d164cb7b5 100644 --- a/nodes.py +++ b/nodes.py @@ -8,7 +8,7 @@ import traceback import math import time -from PIL import Image +from PIL import Image, ImageDraw from PIL.PngImagePlugin import PngInfo import numpy as np import safetensors.torch @@ -327,24 +327,64 @@ class SavePreviewLatent(SaveLatent): metadata.add_text(x, json.dumps(extra_pnginfo[x])) exif_bytes = piexif.dump(exif_data) - image.save(image_path, format='png', exif=exif_bytes, pnginfo=metadata) + image.save(image_path, format='png', exif=exif_bytes, pnginfo=metadata, optimize=True) @staticmethod - def load_preview(image): + def prepare_preview(image, latent_tensor): + lower_bound = 128 + upper_bound = 512 + if image is None: - comfy_path = os.path.dirname(__file__) - image_path = os.path.join(comfy_path, "logo.png") - return Image.open(image_path) + image = comfy.utils.latent_to_rgb(latent_tensor) + + min_size = min(image.size[0], image.size[1]) + max_size = max(image.size[0], image.size[1]) + + scale_factor = 1 + if max_size > upper_bound: + scale_factor = upper_bound/max_size + + # prevent too small preview + if min_size*scale_factor < lower_bound: + scale_factor = lower_bound/min_size + + w = int(image.size[0] * scale_factor) + h = int(image.size[1] * scale_factor) + + image = image.resize((w, h)) + else: + # don't resize if provide preview image intentionally i = 255. * image[0].cpu().numpy() image = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) - return image - def save_preview_latent(self, samples, filename_prefix="ComfyUI", image=None, prompt=None, extra_pnginfo=None): + return SavePreviewLatent.attach_format_text(image) + + @staticmethod + def attach_format_text(image): + width_a, height_a = image.size + + letter_image = Image.open("misc/latent.png") + width_b, height_b = letter_image.size + + new_width = max(width_a, width_b) + new_height = height_a + height_b + + new_image = Image.new('RGB', (new_width, new_height), (0, 0, 0)) + + offset_x = (new_width - width_b) // 2 + offset_y = (height_a + (new_height - height_a - height_b) // 2) + new_image.paste(letter_image, (offset_x, offset_y)) + + new_image.paste(image, (0, 0)) + + return new_image + + def save_preview_latent(self, samples, filename_prefix="ComfyUI", image_opt=None, prompt=None, extra_pnginfo=None): full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir) # load preview - preview = SavePreviewLatent.load_preview(image) + preview = SavePreviewLatent.prepare_preview(image_opt, samples['samples']) # support save metadata for latent sharing file = f"{filename}_{counter:05}_.latent.png"