Add files via upload

This commit is contained in:
tusharbhutt
2023-10-03 15:56:31 -06:00
committed by GitHub
parent ed36d83051
commit 80783644a3
5 changed files with 865 additions and 376 deletions

View File

@@ -195,4 +195,3 @@ These nodes may or may not be maintained. They work on my system but may not on
+[Mikey nodes](https://github.com/bash-j/mikey_nodes )to grab code snippet to pass scoring metadata to image
# Took some base code from the [WAS save image node](https://github.com/WASasquatch/was-node-suite-comfyui) to repurpose it

View File

@@ -1,8 +1,8 @@
"""
@author: BiffMunky
@title: 🌌 An Endless Sea of Stars Nodes 🌌
@nickname: 🌌 Endless Nodes 🌌
@description: A small set of nodes I created for various numerical and text inputs. Features switches for text and numbers, parameter collection nodes, and two aesthetic scoring modwls.
@title: Endless ️🌊🌠 Node 🌌
@nickname: ♾️🌊🌠
@description: A small set of nodes I created for various numerical and text inputs. Features switches for text and numbers, parameter collection nodes, and two aesthetic scoring models.
"""
from .endless_nodes import *
@@ -15,16 +15,27 @@ NODE_CLASS_MAPPINGS = {
"Endless Nodes Parameterizer & Prompts": EndlessNode_XLParameterizerPrompt,
"Endless Nodes Combo Parameterizer": EndlessNode_ComboXLParameterizer,
"Endless Nodes Combo Parameterizer & Prompts": EndlessNode_ComboXLParameterizerPrompt,
#"Endless Nodes Image Save with Text File": EndlessNode_ImageSaver,
# "Endless Nodes Display String": EndlessNode_DisplayString,
# "Endless Nodes Display Number": EndlessNode_DisplayNumber,
# "Endless Nodes Display Integer": EndlessNode_DisplayInt,
# "Endless Nodes Display Float": EndlessNode_DisplayFloat,
"Endless Nodes Image Save with Text File": EndlessNode_ImageSaver,
"Endless Nodes Aesthetic Scoring": EndlessNode_Scoring,
"Endless Nodes Aesthetic Scoring Auto": EndlessNode_ScoringAutoScore,
"Endless Nodes Image Reward": EndlessNode_ImageReward,
"Endless Nodes Image Reward Auto": EndlessNode_ImageRewardAutoScore,
"Endless Nodes Float to Integer": EndlessNode_FloattoInt,
"Endless Nodes Float to Number": EndlessNode_FloattoNum,
"Endless Nodes Float to String": EndlessNode_FloattoString,
"Endless Nodes Number to Float": EndlessNode_NumtoFloat,
"Endless Nodes Number to Integer": EndlessNode_NumtoInt,
"Endless Nodes Number to String": EndlessNode_NumtoString,
"Endless Nodes Integer to Float": EndlessNode_InttoFloat,
"Endless Nodes Integer to Number": EndlessNode_InttoNum,
"Endless Nodes Integer to String": EndlessNode_InttoString,
"Endless Nodes Float to X": EndlessNode_FloattoX,
"Endless Nodes Integer to X": EndlessNode_InttoX,
"Endless Nodes Number to X": EndlessNode_NumtoX,
}
__all__ = ['NODE_CLASS_MAPPINGS']
print("\033[36m 🌌 An Endless Sea of Stars Custom Nodes 🌌 V0.24 \033[34m: \033[92mLoaded\033[0m")
print("\033[36m 🌌 An Endless Sea of Stars Custom Nodes 🌌 \033[33mreV0.28 \033[34m: \033[92mLoaded\033[0m")

View File

@@ -1,3 +1,8 @@
Oct 3/23: 0.29- Save Image module added, saves images and JSON to separate folder if requested
Sep 28/23: 0.28 - Unreleased - Added Variable types to X
Sep 28/23: 0.27 - Unreleased - Corrected scoring nodes to actually add the value of the score into the image metadata .... still goobered!
Sep 24/23: 0.26 - Unreleased - starting to correct scoring to get to image metadata
Sep 24/23: 0.25 - Added various X to String Nodes
Sep 24/23: 0.24 - Added In Image Reward scoring model with a single node to load model and output standard deviation and scoring via number or string nodes
Sep 24/23: 0.23 - Rework Aesthetic Score model and integrate it into single node to display score, added a requirements file
Sep 23/23: 0.22 - Unreleased, convert ImageReward output to base ten score

View File

@@ -1,30 +1,40 @@
"""
@author: BiffMunky
@title: 🌌 An Endless Sea of Stars Nodes 🌌
@title: 🌌 An Endless Sea of Stars Node 🌌
@nickname: 🌌 Endless Nodes 🌌
@description: A small set of nodes I created for various numerical and text inputs. Features switches for text and numbers, parameter collection nodes, and two aesthetic scoring modwls.
@description: A small set of nodes I created for various numerical and text inputs. Features switches for text and numbers, parameter collection nodes, and two aesthetic scoring models.
"""
# Version 0.24 - Imagr Rearwd nodeaddeded
#0.23 - Aesthetic Scorer addeded
#0.22 Unreleased - intro'd asestheic score
#0.29 - Save Image module added, saves images and JSON to separate folder if requested
#0.28 - Unreleased - Added Variable types to X
#0.27 - Unreleased - Corrected scoring nodes to actually add the value of the score into the image metadata .... still goobered!
#0.26 - Unreleased - starting to correct scoring to get to image metadata
#0.25 - Added various X to String Nodes
#0.24 - Image reward node added
#0.23 - Aesthetic Scorer added
#0.22 Unreleased - intro'd aestheticscore
#0.21 unreleased -- trying for display nodes
#0.20 sorted categories of nodes
#--------------------------------------
#----------------------------------------------
# Endless Sea of Stars Custom Node Collection
#https://github.com/tusharbhutt/Endless-Nodes
#----------------------------------------------
#
#
#______________________________________________________________________________________________________________________________________________________________
# IMPORT MODULES BLOCK #
#import torch
from PIL import Image
from PIL.PngImagePlugin import PngInfo
from colorama import init, Fore, Back, Style
from os.path import join
from warnings import filterwarnings
import ImageReward as RM
import clip
import colorama
import datetime
import folder_paths as endless_paths
import io
import json
import math
@@ -32,24 +42,30 @@ import numpy as np
import os
import pytorch_lightning as pl
import re
import sys
import socket
import statistics
import sys
import time
import torch
import torch.nn as nn
import ImageReward as RM
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "comfy"))
import comfy.sd
import comfy.utils
import folder_paths
#import folder_paths
import typing as tg
#--------------------------------------
#Six Text Input Node for selection
# Initialize colorama for colored text
colorama.init(autoreset=True)
#______________________________________________________________________________________________________________________________________________________________
# "SWITCHES" BLOCK #
#----------------------------------------------
# Six Text Input Node for selection
class EndlessNode_SixTextInputSwitch:
def __init__(self):
@@ -93,6 +109,7 @@ class EndlessNode_SixTextInputSwitch:
else:
return (text6,)
#----------------------------------------------
# Eight Text Input Node for selection (needed more slots, what can I say)
@@ -144,8 +161,8 @@ class EndlessNode_EightTextInputSwitch:
else:
return (text8,)
#--------------------------------------
##Six Integer Input and Output via connectors
#----------------------------------------------
# Six Integer Input and Output via connectors
class EndlessNode_SixIntIOSwitch:
@@ -188,8 +205,8 @@ class EndlessNode_SixIntIOSwitch:
else:
return (INT6,)
#--------------------------------------
##Six Integer Input and Output by Widget
#----------------------------------------------
# Six Integer Input and Output by Widget
class EndlessNode_SixIntIOWidget:
def __init__(self):
@@ -219,6 +236,11 @@ class EndlessNode_SixIntIOWidget:
def six_int_widget(self,int1,int2,int3,int4,int5,int6):
return(int1,int2,int3,int4,int5,int6)
#______________________________________________________________________________________________________________________________________________________________
# PARAMETERS BLOCK #
#----------------------------------------------
# Text Encode Combo Box with prompt
class EndlessNode_XLParameterizerPrompt:
@@ -255,7 +277,8 @@ class EndlessNode_XLParameterizerPrompt:
def ParameterizerPrompt(self,base_width,base_height,base_crop_w,base_crop_h,base_target_w,base_target_h,refiner_width,refiner_height,refiner_ascore,endlessG,endlessL):
return(base_width,base_height,base_crop_w,base_crop_h,base_target_w,base_target_h,refiner_width,refiner_height,refiner_ascore,endlessG,endlessL)
# CLIP tect encodee box without prompt
#----------------------------------------------
# CLIP text encode box without prompt
class EndlessNode_XLParameterizer:
def __init__(self):
@@ -286,7 +309,7 @@ class EndlessNode_XLParameterizer:
def Parameterizer(self,base_width,base_height,base_crop_w,base_crop_h,base_target_w,base_target_h,refiner_width,refiner_height,refiner_ascore):
return(base_width,base_height,base_crop_w,base_crop_h,base_target_w,base_target_h,refiner_width,refiner_height,refiner_ascore)
#----------------------------------------------
# Text Encode Combo Box with prompt
class EndlessNode_ComboXLParameterizerPrompt:
@@ -326,6 +349,7 @@ class EndlessNode_ComboXLParameterizerPrompt:
def ComboParameterizerPrompt(self,base_width,base_height,base_crop_w,base_crop_h,base_target_w,base_target_h,refiner_width,refiner_height,refiner_pascore,refiner_nascore,PendlessG,PendlessL,NendlessG,NendlessL):
return(base_width,base_height,base_crop_w,base_crop_h,base_target_w,base_target_h,refiner_width,refiner_height,refiner_pascore,refiner_nascore,PendlessG,PendlessL,NendlessG,NendlessL)
#----------------------------------------------
# CLIP text encode box without prompt, COMBO that allows one box for both pos/neg parameters to be fed to CLIP text, with separate POS/NEG Aesthetic score
class EndlessNode_ComboXLParameterizer:
@@ -358,9 +382,11 @@ class EndlessNode_ComboXLParameterizer:
def ComboParameterizer(self,base_width,base_height,base_crop_w,base_crop_h,base_target_w,base_target_h,refiner_width,refiner_height,refiner_pascore, refiner_nascore):
return(base_width,base_height,base_crop_w,base_crop_h,base_target_w,base_target_h,refiner_width,refiner_height,refiner_pascore, refiner_nascore)
#______________________________________________________________________________________________________________________________________________________________
# IMAGE SCORING BLOCK #
#--------------------------------------
## Aesthetic Scoring Type One
#----------------------------------------------
# Aesthetic Scoring Node
folder_paths.folder_names_and_paths["aesthetic"] = ([os.path.join(folder_paths.models_dir,"aesthetic")], folder_paths.supported_pt_extensions)
@@ -422,7 +448,7 @@ class EndlessNode_Scoring:
}
}
RETURN_TYPES = ("NUM",)
RETURN_TYPES = ("NUMBER","IMAGE")
FUNCTION = "calc_score"
CATEGORY = "Endless 🌌/Scoring"
@@ -448,7 +474,53 @@ class EndlessNode_Scoring:
del model
return (final_prediction,)
# #---------------------------------------------- NOT WORKING, NEED TO LOOK AT IT
# # Aesthetic Scoring Node with Scoring passed to image
# class EndlessNode_ScoringAutoScore:
# def __init__(self):
# pass
# @classmethod
# def INPUT_TYPES(cls):
# return {
# "required": {
# "model_name": (folder_paths.get_filename_list("aesthetic"), {"multiline": False, "default": "chadscorer.pth"}),
# "image": ("IMAGE",),
# }
# }
# RETURN_TYPES = ("NUMBER","IMAGE")
# FUNCTION = "calc_score"
# OUTPUT_NODE = True
# CATEGORY = "Endless 🌌/Scoring"
# def calc_score(self, model_name, image):
# m_path = folder_paths.folder_names_and_paths["aesthetic"][0]
# m_path2 = os.path.join(m_path[0], model_name)
# model = MLP(768) # CLIP embedding dim is 768 for CLIP ViT L 14
# s = torch.load(m_path2)
# model.load_state_dict(s)
# model.to("cuda")
# model.eval()
# device = "cuda"
# model2, preprocess = clip.load("ViT-L/14", device=device) # RN50x64
# tensor_image = image[0]
# img = (tensor_image * 255).to(torch.uint8).numpy()
# pil_image = Image.fromarray(img, mode='RGB')
# image2 = preprocess(pil_image).unsqueeze(0).to(device)
# with torch.no_grad():
# image_features = model2.encode_image(image2)
# im_emb_arr = normalized(image_features.cpu().detach().numpy())
# prediction = model(torch.from_numpy(im_emb_arr).to(device).type(torch.cuda.FloatTensor))
# final_prediction = round(float(prediction[0]), 2)
# del model
# # Metadata part
# extra_pnginfo = {"SCORE": str(final_prediction)}
# return (final_prediction, image)
#----------------------------------------------
# Image Reward Scoring
class EndlessNode_ImageReward:
def __init__(self):
@@ -461,13 +533,11 @@ class EndlessNode_ImageReward:
"model": ("STRING", {"multiline": False, "default": "ImageReward-v1.0"}),
"prompt": ("STRING", {"multiline": True, "forceInput": True}),
"images": ("IMAGE",),
# "rounded": ("BOOL", {"default": False}) # Add a boolean input
},
}
RETURN_TYPES = ("FLOAT", "STRING", "FLOAT", "STRING")
RETURN_NAMES = ("SCORE_FLOAT", "SCORE_STRING", "VALUE_FLOAT", "VALUE_STRING")
OUTPUT_NODE = False
CATEGORY = "Endless 🌌/Scoring"
@@ -484,120 +554,510 @@ class EndlessNode_ImageReward:
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
score += self.model.score(prompt, [img])
score /= len(images)
# if rounded:
# # Round the score to two decimal places
# score = round(score, 2)
# assume std dev follows normal distribution curve
valuescale = 0.5 * (1 + math.erf(score / math.sqrt(2))) * 10 # *10 to get a value between -10
return (score, str(score), valuescale, str(valuescale))
# ##test of image saver ##
# #---------------------------------------------- NOT WORKING, NEED TO LOOK AT
# # Image Reward Scoring with score passed to image
# class EndlessNode_ImageSaver:
# class EndlessNode_ImageRewardAutoScore:
# def __init__(self):
# self.output_dir = folder_paths.get_output_directory()
# self.type = "output"
# self.model = None
# @classmethod
# def INPUT_TYPES(cls):
# return {
# "required": {
# "model": ("STRING", {"multiline": False, "default": "ImageReward-v1.0"}),
# "prompt": ("STRING", {"multiline": True, "forceInput": True}),
# "images": ("IMAGE",),
# "filename_prefix": ("STRING", {"default": "ComfyUI"}),
# "subfolder": ("STRING", {"default": None}), # Add subfolder input
# },
# "hidden": {
# "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"
# },
# }
# RETURN_TYPES = ()
# FUNCTION = "save_images"
# RETURN_TYPES = ("FLOAT", "STRING", "FLOAT", "STRING", "IMAGE")
# RETURN_NAMES = ("SCORE_FLOAT", "SCORE_STRING", "VALUE_FLOAT", "VALUE_STRING", "TO_IMAGE")
# OUTPUT_NODE = True
# CATEGORY = "Endless 🌌/IO"
# CATEGORY = "Endless 🌌/Scoring"
# def save_images(self, images, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None, subfolder=None):
# FUNCTION = "score_meta"
# # Replace illegal characters in the filename prefix with dashes
# filename_prefix = re.sub(r'[<>:"\/\\|?*]', '-', filename_prefix)
# def score_meta(self, model, prompt, images):
# if self.model is None:
# self.model = RM.load(model)
# # Get the current date in Y-m-d format
# today = datetime.datetime.now().strftime("%Y-%m-%d")
# # If a custom subfolder is provided, use it; otherwise, use the date
# if subfolder is not None:
# full_output_folder = os.path.join(self.output_dir, subfolder)
# else:
# full_output_folder = os.path.join(self.output_dir, today)
# # Create the subfolder if it doesn't exist
# os.makedirs(full_output_folder, exist_ok=True)
# counter = self.get_next_number(full_output_folder)
# results = list()
# # Scoring part
# score = 0.0
# for image in images:
# i = 255. * image.cpu().numpy()
# i = 255.0 * image.cpu().numpy()
# img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
# score += self.model.score(prompt, [img])
# score /= len(images)
# valuescale = 0.5 * (1 + math.erf(score / math.sqrt(2))) * 10
# 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]))
# # Metadata part
# extra_pnginfo = {"SCORE": str(score)}
# file = f"{counter:05}-c-{filename_prefix}.png"
# img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=4)
# results.append({
# "filename": file,
# "subfolder": full_output_folder,
# "type": self.type
# })
# # Returning both the score and the modified image
# return (score, str(score), valuescale, str(valuescale), images)
# # Check if a user-specified folder for TEXT files is provided
# if subfolder is not None:
# # Create the full path for the TEXT file using the same name as the PNG
# text_file = os.path.join(subfolder, f"{counter:05}-c-{filename_prefix}.txt")
# else:
# # Use the same folder as the image if no custom subfolder is provided
# text_file = os.path.join(full_output_folder, f"{counter:05}-c-{filename_prefix}.txt")
# ______________________________________________________________________________________________________________________________________________________________
# IMAGE SAVERS BLOCK #
# # Save some example text content to the TEXT file (you can modify this)
# with open(text_file, 'w') as text:
# text.write("This is an example text file.")
# counter += 1
# return {"ui": {"images": results}}
# def get_next_number(self, directory):
# files = os.listdir(directory)
# highest_number = 0
# for file in files:
# parts = file.split('-')
# try:
# num = int(parts[0])
# if num > highest_number:
# highest_number = num
# except ValueError:
# # If it's not a number, skip this file
# continue
# # Return the next number
# return highest_number + 1
# ----------------------------------------------
# Saver type one: saves IMAGE and JSON files, can specify separate folders for each, or one, or none, and use Python timestamps
#--------------------------------------
# CREDITS
class EndlessNode_ImageSaver:
def __init__(self):
self.output_dir = endless_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"],),
"img_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',
img_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, img_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),
"text_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, img_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"
# Construct full paths for image and text files based on folders provided
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 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):
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"
# Construct full paths for image and text files based on folders provided
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 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)
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
# ______________________________________________________________________________________________________________________________________________________________
# CONVERTER NODES BLOCK #
# ----------------------------------------------
# Float value to Integer
class EndlessNode_FloattoInt:
CATEGORY = "Endless 🌌/Converters"
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {"FloatValue": ("FLOAT", {"default": 0.0},)}
}
RETURN_TYPES = ("INT",)
FUNCTION = "inputfloat"
def inputfloat(self, FloatValue):
return int(FloatValue,)
# ----------------------------------------------
# Float value to Number, passes minimum one decimal
# There is no real "Number" function in Python, this is here so that nodes that need a NUMBER can take the FLOAT value
class EndlessNode_FloattoNum:
CATEGORY = "Endless 🌌/Converters"
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {"FloatValue": ("FLOAT", {"default": 0.0,}),}
}
RETURN_TYPES = ("NUMBER",)
FUNCTION = "inputfloat"
def inputfloat(self, FloatValue):
return float(FloatValue,)
# ----------------------------------------------
# Float value to String, passes one to eight decimals
class EndlessNode_FloattoString:
CATEGORY = "Endless 🌌/Converters"
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {"FloatValue": ("FLOAT", {"default": 0.0,},)}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "inputfloat"
def inputfloat(self, FloatValue):
if isinstance(FloatValue, float):
formatted_value = f'{FloatValue:.1f}' if FloatValue.is_integer() else f'{FloatValue:.8f}'
return str((formatted_value),)
elif isinstance(FloatValue, int):
#Convert integer to float and then format
formatted_value = f'{float(FloatValue):.1f}'
return str((formatted_value),)
else:
try:
#Try to convert to float, and then format
float_value = float(FloatValue)
formatted_value = f'{float_value:.1f}' if float_value.is_integer() else f'{float_value:.8f}'
return str((formatted_value),)
except ValueError:
return ("Not a valid float",)
# # ---------------------------------------------- NEED TO FIX
# # Float value to X
# class EndlessNode_FloattoX:
# CATEGORY = "Endless 🌌/Converters"
# def __init__(self):
# pass
# @classmethod
# def INPUT_TYPES(cls):
# return {
# "required": {
# "FloatValue": ("FLOAT", {"default": 0, "min": -8675309362436420, "max": 8675309362436420}),
# },
# }
# RETURN_TYPES = ("INT", "NUMBER", "STR")
# FUNCTION = "return_constant_number"
# def return_constant_number(self, FloatValue):
# # Return number
# return (int(FloatValue), (FloatValue), str(FloatValue))
# ----------------------------------------------
# Integer to Float
class EndlessNode_InttoFloat:
CATEGORY = "Endless 🌌/Converters"
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {"IntegerValue": ("INT",),}
}
RETURN_TYPES = ("FLOAT",)
FUNCTION = "inputint"
def inputint(self, IntegerValue):
return int((IntegerValue),)
# ----------------------------------------------
# Integer to Number
class EndlessNode_InttoNum:
CATEGORY = "Endless 🌌/Converters"
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {"IntegerValue": ("INT",),}
}
RETURN_TYPES = ("NUMBER",)
FUNCTION = "inputint"
def inputint(self, IntegerValue):
return int((IntegerValue),)
# ----------------------------------------------
# Integer to String
class EndlessNode_InttoString:
CATEGORY = "Endless 🌌/Converters"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {"IntegerValue": ("INT",{"default": 0,},)}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "inputint"
def inputint(self, IntegerValue):
return str((IntegerValue),)
# # ---------------------------------------------- NOT ORKING, NEED TO FIX
# # Integer value to X
# class EndlessNode_InttoX:
# CATEGORY = "Endless 🌌/Converters"
# def __init__(self):
# pass
# @classmethod
# def INPUT_TYPES(cls):
# return {
# "required": {
# "number": ("INT", {"default": 0, "min": -8675309, "max": 8675309}),
# },
# }
# RETURN_TYPES = ("FLOAT", "NUMBER", "STR")
# FUNCTION = "return_constant_number"
# def return_constant_number(self, number):
# # Return number
# return (float(number), float(number), str(number))
# ----------------------------------------------
# Number to Float
class EndlessNode_NumtoFloat:
CATEGORY = "Endless 🌌/Converters"
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {"NumberValue": ("NUMBER",),}
}
RETURN_TYPES = ("FLOAT",)
FUNCTION = "inputnum"
def inputnum(self, NumberValue):
return float((NumberValue),)
# ----------------------------------------------
# Number to Integer
class EndlessNode_NumtoInt:
CATEGORY = "Endless 🌌/Converters"
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {"NumberValue": ("NUMBER",),}
}
RETURN_TYPES = ("INT",)
FUNCTION = "inputnum"
def inputnum(self, NumberValue):
return int((NumberValue),)
# ----------------------------------------------
# Number value to String
class EndlessNode_NumtoString:
def __init__(self):
pass
CATEGORY = "Endless 🌌/Converters"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {"NumberValue": ("NUMBER",),}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "inputnum"
def inputnum(self, NumberValue):
return str((NumberValue),)
# NEED TO FIX STILL
# class EndlessNode_NumtoX:
# CATEGORY = "Endless 🌌/Converters"
# def __init__(self):
# pass
# @classmethod
# def INPUT_TYPES(cls):
# return {
# "required": {
# "number": ("FLOAT", {"default": 0, "min": -8675309362436420, "max": 8675309362436420}),
# },
# }
# RETURN_TYPES = ("FLOAT", "INT", "STR")
# FUNCTION = "return_constant_number"
# def return_constant_number(self, number):
# # Return number
# return (float(number), int(number), str(number))
#______________________________________________________________________________________________________________________________________________________________
# CREDITS #
#
# Comfyroll Custom Nodes for the overall node code layout, coding snippets, and inspiration for the text input and number switches
# Comfyroll Custom Nodes for the initial node code layout, coding snippets, and inspiration for the text input and number switches
#
# https://github.com/RockOfFire/ComfyUI_Comfyroll_CustomNode
#
@@ -608,14 +1068,28 @@ class EndlessNode_ImageReward:
#
# https://github.com/comfyanonymous/ComfyUI
#
# ComfyUI-Strimmlarns-Aesthetic-Score for the original coding for Aesthetic Scoring Type One
# ComfyUI-Strimmlarns-Aesthetic-Score for the original coding for Aesthetic Scoring
#
# https://github.com/strimmlarn/ComfyUI-Strimmlarns-Aesthetic-Score
#
# The scorer uses the MLP class code from Christoph Schuhmann
#
#https://github.com/christophschuhmann/improved-aesthetic-predictor
#[Zane A's ComfyUI-ImageReward](https://github.com/ZaneA/ComfyUI-ImageReward) for the original coding for the Umagr Reward nodee
#[Zane A's ComfyUI-ImageReward](https://github.com/ZaneA/ComfyUI-ImageReward) for the original coding for the Image Reward node
#
#Zane's node in turn uses [ImageReward](https://github.com/THUDM/ImageReward)
#
#
#Mikey nodes to grab code snippet to pass scoring metadata to image
#
#https://github.com/bash-j/mikey_nodes
# Took some base code from the WAS save image node to repurpose it
#
#https://github.com/WASasquatch/was-node-suite-comfyui
#--------------------------------------
######################################################################################## CELLAR DWELLERS

BIN
img/converters.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB