diff --git a/README.md b/README.md index 73bd528..e8fba09 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Some basic custom nodes for the ComfyUI user interface for Stable Diffusion. Features: + An image saver for images and JSON files to base folder, custom folders for one, or custom folders for both. Also allows for Python timestamping -+ Two aesthetic scoring models, one based on the same as AUTO1111, the other based on Image Reward ++ (REMOVED FOR NOW) Two aesthetic scoring models, one based on the same as AUTO1111, the other based on Image Reward + Converters for various numerical functions to the other (int, float, number) and to strings. Still in beta + Switches for text and numbers + Parameter collection nodes @@ -12,6 +12,11 @@ When using the [ComfyUI](https://github.com/comfyanonymous/ComfyUI) interface fo Rightly or wrongly, I am pretending to teach myself a bit of Python to get some nodes up and running to do what I'd like. Yes, I am using ChatGPT, and yes I am a glutton for punishment. There are no promises that these nodes will work for you or that I will maintain them. Feel free to do with them as you wish, according to the license model. +**UPDATE: Oct 8, 2023** + ++ ***Added a standalone saver node. Fixed bug in this node and main node where image files were overwritten*** + + **UPDATE: Oct 7, 2023** + ***REMOVED THE AESTHETIC SCORERS, TOO MANY PEOPLE CAN'T GET CLIP LOADED. WILL REVISIT AFTER VACATION*** @@ -25,7 +30,7 @@ Rightly or wrongly, I am pretending to teach myself a bit of Python to get some **UPDATE: Oct 3, 2023** + Added an Image Saver that can place JSON files ***in separate folders*** -+ Added nodes to convert from one numeric type to another, and to string. **Still in beta** ++ Added nodes to convert from one numeric type to another, and to string. **UPDATE: Sep 24, 2023** @@ -207,4 +212,4 @@ These nodes may or may not be maintained. They work on my system but may not on + Took some base code from the [WAS save image node](https://github.com/WASasquatch/was-node-suite-comfyui) to repurpose it + Thanks to [chrisgoringe](https://github.com/chrisgoringe) for some vital insgiht into correcting mesy commas in the tuples for the converter nodes, much appreciated! -# +# \ No newline at end of file diff --git a/RevertSaver.py b/RevertSaver.py new file mode 100644 index 0000000..0f671a0 --- /dev/null +++ b/RevertSaver.py @@ -0,0 +1,176 @@ +import numpy as np +from PIL import Image +from PIL.PngImagePlugin import PngInfo +import colorama +from colorama import init, Fore, Back, Style +import os +import json +import time +import socket +import re +import folder_paths +import datetime # Added to support date and time placeholders + +# Initialize colorama +colorama.init(autoreset=True) + +class EndlessNode_ImageSaver: + def __init__(self): + self.output_dir = folder_paths.get_output_directory() + self.type = "output" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + "filename_prefix": ("STRING", {"default": "ComfyUI"}), + "delimiter": ("STRING", {"default": "_"}), + "filename_number_padding": ("INT", {"default": 4, "min": 1, "max": 9, "step": 1}), + "filename_number_start": (["false", "true"],), + "image_folder": ("STRING", {"default": None}), + "json_folder": ("STRING", {"default": None}), + }, + "hidden": { + "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO" + }, + } + + RETURN_TYPES = () + FUNCTION = "save_images" + OUTPUT_NODE = True + CATEGORY = "Endless 🌊✨/IO" + + def save_images(self, images, filename_prefix="ComfyUI", delimiter="_", + filename_number_padding=4, filename_number_start='false', + image_folder=None, json_folder=None, prompt=None, extra_pnginfo=None): + + # Replace illegal characters in the filename prefix with dashes + filename_prefix = re.sub(r'[<>:"\/\\|?*]', '-', filename_prefix) + + # Set IMG Extension + img_extension = '.png' + + counter = 1 + + results = list() + + for image in images: + i = 255. * image.cpu().numpy() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + + metadata = PngInfo() + if prompt is not None: + metadata.add_text("prompt", json.dumps(prompt)) + if extra_pnginfo is not None: + for x in extra_pnginfo: + metadata.add_text(x, json.dumps(extra_pnginfo[x])) + + img_file, json_file = self.generate_filenames(filename_prefix, delimiter, counter, + filename_number_padding, filename_number_start, + img_extension, image_folder, json_folder) + + try: + if img_extension == '.png': + img.save(img_file, pnginfo=metadata, compress_level=4) + elif img_extension == '.jpeg': + img.save(img_file, quality=100, optimize=True) + + with open(json_file, 'w', encoding='utf-8', newline='\n') as f: + if prompt is not None: + f.write(json.dumps(prompt, indent=4)) + + print(Fore.GREEN + f"+ File(s) saved to: {img_file}") + + results.append({ + "image_filename": os.path.basename(img_file), + "image_path": img_file, + "json_filename": os.path.basename(json_file), + "json_path": json_file, + "type": self.type + }) + + except OSError as e: + print(Fore.RED + " + Unable to save file: ", end='') + print({img_file}) + print(e) + except Exception as e: + print(Fore.RED + " + Unable to save file due to the following error: ", end='') + print(e) + + counter += 1 + + return {"ui": {"results": results}} + + def generate_filenames(self, filename_prefix, delimiter, counter, + filename_number_padding, filename_number_start, img_extension, + image_folder, json_folder): + if filename_number_start == 'true': + img_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}{img_extension}" + json_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}.json" + else: + img_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}{img_extension}" + json_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}.json" + + # Apply placeholders for date and time in filenames + img_file = self.replace_date_time_placeholders(img_file) + json_file = self.replace_date_time_placeholders(json_file) + + # Construct full paths for image and text files based on folders provided + if image_folder: + img_file = os.path.join(image_folder, img_file) + else: + img_file = os.path.join(self.output_dir, img_file) + + if json_folder: + json_file = os.path.join(json_folder, json_file) + else: + json_file = os.path.join(os.path.dirname(img_file), json_file) + + # Check if files with the same name exist, increment counter if necessary + while os.path.exists(img_file) or os.path.exists(json_file): + counter += 1 + if filename_number_start == 'true': + img_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}{img_extension}" + json_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}.json" + else: + img_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}{img_extension}" + json_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}.json" + + # Apply placeholders for date and time in filenames + img_file = self.replace_date_time_placeholders(img_file) + json_file = self.replace_date_time_placeholders(json_file) + + if image_folder: + img_file = os.path.join(image_folder, img_file) + else: + img_file = os.path.join(self.output_dir, img_file) + + if json_folder: + json_file = os.path.join(json_folder, json_file) + else: + json_file = os.path.join(os.path.dirname(img_file), json_file) + + return img_file, json_file + + def replace_date_time_placeholders(self, filename): + # Replace date and time placeholders with actual date and time strings + now = datetime.datetime.now() + placeholders = { + '%Y': now.strftime('%Y'), # Year with century as a decimal number + '%y': now.strftime('%y'), # Year without century as a zero-padded decimal number + '%m': now.strftime('%m'), # Month as a zero-padded decimal number + '%d': now.strftime('%d'), # Day of the month as a zero-padded decimal number + '%H': now.strftime('%H'), # Hour (24-hour clock) as a zero-padded decimal number + '%M': now.strftime('%M'), # Minute as a zero-padded decimal number + '%S': now.strftime('%S'), # Second as a zero-padded decimal number + } + + for placeholder, replacement in placeholders.items(): + filename = filename.replace(placeholder, replacement) + + return filename + +NODE_CLASS_MAPPINGS = { + "β™ΎοΈπŸŒŠβœ¨ Image Saver with JSON": EndlessNode_ImageSaver +} diff --git a/__init__.py b/__init__.py index 1ed66b2..1c2019a 100644 --- a/__init__.py +++ b/__init__.py @@ -51,9 +51,9 @@ NODE_DISPLAY_NAME_MAPPINGS = { "ESS Combo Parameterizer": "β™ΎοΈπŸŒŠβœ¨ Combo Parameterizer", "ESS Combo Parameterizer & Prompts": "β™ΎοΈπŸŒŠβœ¨ Combo Parameterizer & Prompts", "ESS Image Saver with JSON": "β™ΎοΈπŸŒŠβœ¨ Image Saver with JSON", - "ESS Aesthetic Scoring": "β™ΎοΈπŸŒŠβœ¨ Aesthetic Scoring", +# "ESS Aesthetic Scoring": "β™ΎοΈπŸŒŠβœ¨ Aesthetic Scoring", # "ESS Aesthetic Scoring Auto": "β™ΎοΈπŸŒŠβœ¨ Aesthetic Scoring Auto", - "ESS Image Reward": "β™ΎοΈπŸŒŠβœ¨ Image Reward", +# "ESS Image Reward": "β™ΎοΈπŸŒŠβœ¨ Image Reward", # "ESS Image Reward Auto": "β™ΎοΈπŸŒŠβœ¨ Image Reward Auto", "ESS Float to Integer": "β™ΎοΈπŸŒŠβœ¨ Float to Integer", "ESS Float to Number": "β™ΎοΈπŸŒŠβœ¨ Float to Number", @@ -75,4 +75,4 @@ NODE_DISPLAY_NAME_MAPPINGS = { __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] -print("\033[36m An Endless Sea of Stars Custom Nodes V0.31 \033[34m: \033[92mLoaded\033[0m") \ No newline at end of file +print("\033[36m An Endless Sea of Stars Custom Nodes V0.37 \033[34m: \033[92mLoaded\033[0m") \ No newline at end of file diff --git a/endless_nodes.py b/endless_nodes.py index f7cf235..ff72384 100644 --- a/endless_nodes.py +++ b/endless_nodes.py @@ -10,6 +10,8 @@ # Endless Sea of Stars Custom Node Collection #https://github.com/tusharbhutt/Endless-Nodes #---------------------------------------------- +# Oct 08/23, V0.37: Bug fix in Image Saver module that would overwrite files was corrected +# Oct 07/23, V0.36: Killed the scorers until I figure out why CLIP won't load for some people # Oct 06/23, V0.35: Reverted the Image Saver module as I had inadvertently removed the ability to add date and time to the filenames # Oct 05/23, V0.34: Renamed nodes to make them shorter and easier to search for, breaks names of previous workflows though # Oct 05/23, V0.33: Added random text input choice for six and eight nodes inputs @@ -54,7 +56,7 @@ import json import math import numpy as np import os -import pytorch_lightning as pl +#import pytorch_lightning as pl import re import socket import statistics @@ -623,7 +625,6 @@ class EndlessNode_ComboXLParameterizer: # ---------------------------------------------- # Saver type one: saves IMAGE and JSON files, can specify separate folders for each, or one, or none, and use Python timestamps - class EndlessNode_ImageSaver: def __init__(self): self.output_dir = folder_paths.get_output_directory() @@ -638,7 +639,7 @@ class EndlessNode_ImageSaver: "delimiter": ("STRING", {"default": "_"}), "filename_number_padding": ("INT", {"default": 4, "min": 1, "max": 9, "step": 1}), "filename_number_start": (["false", "true"],), - "img_folder": ("STRING", {"default": None}), + "image_folder": ("STRING", {"default": None}), "json_folder": ("STRING", {"default": None}), }, "hidden": { @@ -653,7 +654,7 @@ class EndlessNode_ImageSaver: def save_images(self, images, filename_prefix="ComfyUI", delimiter="_", filename_number_padding=4, filename_number_start='false', - img_folder=None, json_folder=None, prompt=None, extra_pnginfo=None): + image_folder=None, json_folder=None, prompt=None, extra_pnginfo=None): # Replace illegal characters in the filename prefix with dashes filename_prefix = re.sub(r'[<>:"\/\\|?*]', '-', filename_prefix) @@ -678,7 +679,7 @@ class EndlessNode_ImageSaver: img_file, json_file = self.generate_filenames(filename_prefix, delimiter, counter, filename_number_padding, filename_number_start, - img_extension, img_folder, json_folder) + img_extension, image_folder, json_folder) try: if img_extension == '.png': @@ -696,7 +697,7 @@ class EndlessNode_ImageSaver: "image_filename": os.path.basename(img_file), "image_path": img_file, "json_filename": os.path.basename(json_file), - "text_path": json_file, + "json_path": json_file, "type": self.type }) @@ -713,8 +714,8 @@ class EndlessNode_ImageSaver: return {"ui": {"results": results}} def generate_filenames(self, filename_prefix, delimiter, counter, - filename_number_padding, filename_number_start, img_extension, - img_folder, json_folder): + filename_number_padding, filename_number_start, img_extension, + image_folder, json_folder): if filename_number_start == 'true': img_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}{img_extension}" json_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}.json" @@ -722,27 +723,20 @@ class EndlessNode_ImageSaver: img_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}{img_extension}" json_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}.json" - # Construct full paths for image and text files based on folders provided + # Apply placeholders for date and time in filenames + img_file = self.replace_date_time_placeholders(img_file) + json_file = self.replace_date_time_placeholders(json_file) - if img_folder: - img_folder = self.replace_date_time_placeholders(img_folder) - os.makedirs(img_folder, exist_ok=True) # Create the image folder if it doesn't exist - img_file = os.path.join(img_folder, img_file) + # Construct full paths for image and text files based on folders provided + if image_folder: + img_file = os.path.join(image_folder, img_file) else: img_file = os.path.join(self.output_dir, img_file) if json_folder: - json_folder = self.replace_date_time_placeholders(json_folder) - os.makedirs(json_folder, exist_ok=True) # Create the image folder if it doesn't exist json_file = os.path.join(json_folder, json_file) else: json_file = os.path.join(os.path.dirname(img_file), json_file) - - # Apply placeholders for date and time in filenames and folder - img_file = self.replace_date_time_placeholders(img_file) - json_file = self.replace_date_time_placeholders(json_file) - - # Check if files with the same name exist, increment counter if necessary while os.path.exists(img_file) or os.path.exists(json_file): @@ -754,25 +748,19 @@ class EndlessNode_ImageSaver: img_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}{img_extension}" json_file = f"{filename_prefix}{delimiter}{counter:0{filename_number_padding}}.json" - # Construct full paths for image and text files based on folders provided + # Apply placeholders for date and time in filenames + img_file = self.replace_date_time_placeholders(img_file) + json_file = self.replace_date_time_placeholders(json_file) - if img_folder: - img_folder = self.replace_date_time_placeholders(img_folder) - os.makedirs(img_folder, exist_ok=True) # Create the image folder if it doesn't exist - img_file = os.path.join(img_folder, img_file) - else: - img_file = os.path.join(self.output_dir, img_file) + if image_folder: + img_file = os.path.join(image_folder, img_file) + else: + img_file = os.path.join(self.output_dir, img_file) - if json_folder: - json_folder = self.replace_date_time_placeholders(json_folder) - os.makedirs(json_folder, exist_ok=True) # Create the image folder if it doesn't exist - json_file = os.path.join(json_folder, json_file) - else: - json_file = os.path.join(os.path.dirname(img_file), json_file) - - # Apply placeholders for date and time in filenames and folder - img_file = self.replace_date_time_placeholders(img_file) - json_file = self.replace_date_time_placeholders(json_file) + if json_folder: + json_file = os.path.join(json_folder, json_file) + else: + json_file = os.path.join(os.path.dirname(img_file), json_file) return img_file, json_file @@ -793,8 +781,6 @@ class EndlessNode_ImageSaver: filename = filename.replace(placeholder, replacement) return filename - - # ______________________________________________________________________________________________________________________________________________________________ # CONVERTER NODES BLOCK # # diff --git a/requirements.txt b/requirements.txt index 361b427..3d90aaa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1 @@ -# clip @ git+https://github.com/openai/CLIP.git -pytorch-lightning -# image-reward==1.4 -colorama -# ftfy -# regex -# tqdm \ No newline at end of file +colorama \ No newline at end of file