Removed scorers, reverted file saver

This commit is contained in:
tusharbhutt
2023-10-08 00:02:48 -06:00
committed by GitHub
parent 370389c703
commit ff48129768
5 changed files with 320 additions and 262 deletions

View File

@@ -12,16 +12,20 @@ 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. 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 7, 2023**
+ ***REMOVED THE AESTHERIC SCORERS, TOO MANY PEOPLE CAN'T GET CLIP LOADED. WILL REVISIT AFTER VACATION***
**UPDATE: Oct 4, 2023** **UPDATE: Oct 4, 2023**
+ Squished the bugs in the numeric to numerical and string nodes. Special thanks to [chrisgoringe](https://github.com/chrisgoringe) for some vital insight into correcting messy commas in the tuples for the converter nodes, much appreciated! + Squished the bugs in the numeric to numeric and string nodes. Special thanks to [chrisgoringe](https://github.com/chrisgoringe) for some vital insight into correcting messy commas in the tuples for the converter nodes, much appreciated!
+ Added nodes to convert from string to numeric values, with some basic error checking. + Added nodes to convert from string to numeric values, with some basic error checking.
**UPDATE: Oct 3, 2023** **UPDATE: Oct 3, 2023**
+ Added an Image Saver that can place JSON files ***in separate folders*** + 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. + Added nodes to convert from one numeric type to another, and to string. **Still in beta**
**UPDATE: Sep 24, 2023** **UPDATE: Sep 24, 2023**

View File

@@ -7,69 +7,71 @@
from .endless_nodes import * from .endless_nodes import *
NODE_CLASS_MAPPINGS = { NODE_CLASS_MAPPINGS = {
"Endless Nodes Six Input Text Switch": EndlessNode_SixTextInputSwitch, "ESS Six Input Text Switch": EndlessNode_SixTextInputSwitch,
"Endless Nodes Eight Input Text Switch": EndlessNode_EightTextInputSwitch, "ESS Eight Input Text Switch": EndlessNode_EightTextInputSwitch,
"Endless Nodes Six Integer IO Switch": EndlessNode_SixIntIOSwitch, "ESS Six Input Random": EndlessNode_SixTextRandomSwitch,
"Endless Nodes Six Integer IO Widget": EndlessNode_SixIntIOWidget, "ESS Eight Input Random": EndlessNode_EightTextRandomSwitch,
"Endless Nodes Parameterizer": EndlessNode_XLParameterizer, "ESS Six Integer IO Switch": EndlessNode_SixIntIOSwitch,
"Endless Nodes Parameterizer & Prompts": EndlessNode_XLParameterizerPrompt, "ESS Six Integer IO Widget": EndlessNode_SixIntIOWidget,
"Endless Nodes Combo Parameterizer": EndlessNode_ComboXLParameterizer, "ESS Parameterizer": EndlessNode_XLParameterizer,
"Endless Nodes Combo Parameterizer & Prompts": EndlessNode_ComboXLParameterizerPrompt, "ESS Parameterizer & Prompts": EndlessNode_XLParameterizerPrompt,
"Endless Nodes Image Saver with JSON": EndlessNode_ImageSaver, "ESS Combo Parameterizer": EndlessNode_ComboXLParameterizer,
"Endless Nodes Aesthetic Scoring": EndlessNode_Scoring, "ESS Combo Parameterizer & Prompts": EndlessNode_ComboXLParameterizerPrompt,
# "Endless Nodes Aesthetic Scoring Auto": EndlessNode_ScoringAutoScore, "ESS Image Saver with JSON": EndlessNode_ImageSaver,
"Endless Nodes Image Reward": EndlessNode_ImageReward, # "ESS Aesthetic Scoring": EndlessNode_Scoring,
# "Endless Nodes Image Reward Auto": EndlessNode_ImageRewardAutoScore, # "ESS Aesthetic Scoring Auto": EndlessNode_ScoringAutoScore,
"Endless Nodes Float to Integer": EndlessNode_FloattoInt, # "ESS Image Reward": EndlessNode_ImageReward,
"Endless Nodes Float to Number": EndlessNode_FloattoNum, # "ESS Image Reward Auto": EndlessNode_ImageRewardAutoScore,
"Endless Nodes Float to String": EndlessNode_FloattoString, "ESS Float to Integer": EndlessNode_FloattoInt,
"Endless Nodes Float to X": EndlessNode_FloattoX, "ESS Float to Number": EndlessNode_FloattoNum,
"Endless Nodes Integer to Float": EndlessNode_InttoFloat, "ESS Float to String": EndlessNode_FloattoString,
"Endless Nodes Integer to Number": EndlessNode_InttoNum, "ESS Float to X": EndlessNode_FloattoX,
"Endless Nodes Integer to String": EndlessNode_InttoString, "ESS Integer to Float": EndlessNode_InttoFloat,
"Endless Nodes Integer to X": EndlessNode_InttoX, "ESS Integer to Number": EndlessNode_InttoNum,
"Endless Nodes Number to Float": EndlessNode_NumtoFloat, "ESS Integer to String": EndlessNode_InttoString,
"Endless Nodes Number to Integer": EndlessNode_NumtoInt, "ESS Integer to X": EndlessNode_InttoX,
"Endless Nodes Number to String": EndlessNode_NumtoString, "ESS Number to Float": EndlessNode_NumtoFloat,
"Endless Nodes Number to X": EndlessNode_NumtoX, "ESS Number to Integer": EndlessNode_NumtoInt,
"Endless Nodes String to Float": EndlessNode_StringtoFloat, "ESS Number to String": EndlessNode_NumtoString,
"Endless Nodes String to Integer": EndlessNode_StringtoInt, "ESS Number to X": EndlessNode_NumtoX,
"Endless Nodes String to Num": EndlessNode_StringtoNum, "ESS String to Float": EndlessNode_StringtoFloat,
"Endless Nodes String to X": EndlessNode_StringtoX, "ESS String to Integer": EndlessNode_StringtoInt,
"ESS String to Num": EndlessNode_StringtoNum,
"ESS String to X": EndlessNode_StringtoX,
} }
NODE_DISPLAY_NAME_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = {
"Endless Nodes Six Input Text Switch" : "Endless 🌊✨ Six Input Text Switch", "ESS Six Input Text Switch" : "♾️🌊✨ Six Input Text Switch",
"Endless Nodes Eight Input Text Switch": "Endless 🌊✨ Eight Input Text Switch", "ESS Eight Input Text Switch": "♾️🌊✨ Eight Input Text Switch",
"Endless Nodes Six Integer IO Switch": "Endless 🌊✨ Six Integer IO Switch", "ESS Six Integer IO Switch": "♾️🌊✨ Six Integer IO Switch",
"Endless Nodes Six Integer IO Widget": "Endless 🌊✨ Six Integer IO Widget", "ESS Six Integer IO Widget": "♾️🌊✨ Six Integer IO Widget",
"Endless Nodes Parameterizer": "Endless 🌊✨ Parameterizer", "ESS Parameterizer": "♾️🌊✨ Parameterizer",
"Endless Nodes Parameterizer & Prompts": "Endless 🌊✨ Parameterizer & Prompts", "ESS Parameterizer & Prompts": "♾️🌊✨ Parameterizer & Prompts",
"Endless Nodes Combo Parameterizer": "Endless 🌊✨ Combo Parameterizer", "ESS Combo Parameterizer": "♾️🌊✨ Combo Parameterizer",
"Endless Nodes Combo Parameterizer & Prompts": "Endless 🌊✨ Combo Parameterizer & Prompts", "ESS Combo Parameterizer & Prompts": "♾️🌊✨ Combo Parameterizer & Prompts",
"Endless Nodes Image Saver with JSON": "Endless 🌊✨ Image Saver with JSON", "ESS Image Saver with JSON": "♾️🌊✨ Image Saver with JSON",
"Endless Nodes Aesthetic Scoring": "Endless 🌊✨ Aesthetic Scoring", "ESS Aesthetic Scoring": "♾️🌊✨ Aesthetic Scoring",
# "Endless Nodes Aesthetic Scoring Auto": "Endless 🌊✨ Aesthetic Scoring Auto", # "ESS Aesthetic Scoring Auto": "♾️🌊✨ Aesthetic Scoring Auto",
"Endless Nodes Image Reward": "Endless 🌊✨ Image Reward", "ESS Image Reward": "♾️🌊✨ Image Reward",
# "Endless Nodes Image Reward Auto": "Endless 🌊✨ Image Reward Auto", # "ESS Image Reward Auto": "♾️🌊✨ Image Reward Auto",
"Endless Nodes Float to Integer": "Endless 🌊✨ Float to Integer", "ESS Float to Integer": "♾️🌊✨ Float to Integer",
"Endless Nodes Float to Number": "Endless 🌊✨ Float to Number", "ESS Float to Number": "♾️🌊✨ Float to Number",
"Endless Nodes Float to String": "Endless 🌊✨ Float to String", "ESS Float to String": "♾️🌊✨ Float to String",
"Endless Nodes Float to X": "Endless 🌊✨ Float to X", "ESS Float to X": "♾️🌊✨ Float to X",
"Endless Nodes Integer to Float": "Endless 🌊✨ Integer to Float", "ESS Integer to Float": "♾️🌊✨ Integer to Float",
"Endless Nodes Integer to Number": "Endless 🌊✨ Integer to Number", "ESS Integer to Number": "♾️🌊✨ Integer to Number",
"Endless Nodes Integer to String": "Endless 🌊✨ Integer to String", "ESS Integer to String": "♾️🌊✨ Integer to String",
"Endless Nodes Integer to X": "Endless 🌊✨ Integer to X", "ESS Integer to X": "♾️🌊✨ Integer to X",
"Endless Nodes Number to Float": "Endless 🌊✨ Number to Float", "ESS Number to Float": "♾️🌊✨ Number to Float",
"Endless Nodes Number to Integer": "Endless 🌊✨ Number to Integer", "ESS Number to Integer": "♾️🌊✨ Number to Integer",
"Endless Nodes Number to String": "Endless 🌊✨ Number to String", "ESS Number to String": "♾️🌊✨ Number to String",
"Endless Nodes Number to X": "Endless 🌊✨ Number to X", "ESS Number to X": "♾️🌊✨ Number to X",
"Endless Nodes String to Float": "Endless 🌊✨ String to Float", "ESS String to Float": "♾️🌊✨ String to Float",
"Endless Nodes String to Integer": "Endless 🌊✨ String to Integer", "ESS String to Integer": "♾️🌊✨ String to Integer",
"Endless Nodes String to Num": "Endless 🌊✨ String to Num", "ESS String to Num": "♾️🌊✨ String to Num",
"Endless Nodes String to X": "Endless 🌊✨ String to X", "ESS String to X": "♾️🌊✨ String to X",
} }
#Heh, doesn't seem to work :(
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']

View File

@@ -1,4 +1,5 @@
Oct 05/23, V0.32: Set rules for image saver so paths + filename length do not exceed 248 (leaves room for extension) Oct 07/23, V0.33: Removed Aesthetic Scorer and ImageReward until I can figure out why the CLIP module isn't working for a few people
Oct 05/23, V0.32: (UNRELEASED)Set rules for image saver so paths + filename length do not exceed 248 (leaves room for extension)
Oct 04/23, V0.31: Release of V0.28 functionality (int, float, num to X), added String to X, code cleanup, vanity node renaming and recategorization Oct 04/23, V0.31: Release of V0.28 functionality (int, float, num to X), added String to X, code cleanup, vanity node renaming and recategorization
Oct 04/23, V0.30: Squished bugs in the various X to X nodes Oct 04/23, V0.30: Squished bugs in the various X to X nodes
Oct 03/23, V0.29: Save Image module added, saves images and JSON to separate folder if requested Oct 03/23, V0.29: Save Image module added, saves images and JSON to separate folder if requested

View File

@@ -10,6 +10,9 @@
# Endless Sea of Stars Custom Node Collection # Endless Sea of Stars Custom Node Collection
#https://github.com/tusharbhutt/Endless-Nodes #https://github.com/tusharbhutt/Endless-Nodes
#---------------------------------------------- #----------------------------------------------
# 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
# Oct 05/23, V0.32: Set rules for image saver so paths + filename length do not exceed 248 (leaves room for extension) # Oct 05/23, V0.32: Set rules for image saver so paths + filename length do not exceed 248 (leaves room for extension)
# Oct 04/23, V0.31: Release of V0.28 functionality (int, float, num to X), added String to X, code cleanup, vanity node renaming and recategorization # Oct 04/23, V0.31: Release of V0.28 functionality (int, float, num to X), added String to X, code cleanup, vanity node renaming and recategorization
# Oct 04/23, V0.30: Squished bugs in the various X to X nodes # Oct 04/23, V0.30: Squished bugs in the various X to X nodes
@@ -41,8 +44,8 @@ from PIL.PngImagePlugin import PngInfo
from colorama import init, Fore, Back, Style from colorama import init, Fore, Back, Style
from os.path import join from os.path import join
from warnings import filterwarnings from warnings import filterwarnings
import ImageReward as RM # import ImageReward as RM
import clip # import clip
import colorama import colorama
import datetime import datetime
import folder_paths import folder_paths
@@ -59,7 +62,7 @@ import sys
import time import time
import torch import torch
import torch.nn as nn import torch.nn as nn
# import random
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "comfy")) sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "comfy"))
@@ -103,7 +106,7 @@ class EndlessNode_SixTextInputSwitch:
RETURN_NAMES = ("Output",) RETURN_NAMES = ("Output",)
FUNCTION = "six_text_switch" FUNCTION = "six_text_switch"
CATEGORY = "Endless 🌊✨/Switches" CATEGORY = "Endless 🌊✨/Switches/Fixed"
def six_text_switch(self, Input, text1=None,text2=None,text3=None,text4=None,text5=None,text6=None): def six_text_switch(self, Input, text1=None,text2=None,text3=None,text4=None,text5=None,text6=None):
@@ -151,7 +154,7 @@ class EndlessNode_EightTextInputSwitch:
RETURN_NAMES = ("Output",) RETURN_NAMES = ("Output",)
FUNCTION = "eight_text_switch" FUNCTION = "eight_text_switch"
CATEGORY = "Endless 🌊✨/Switches" CATEGORY = "Endless 🌊✨/Switches/Fixed"
def eight_text_switch(self,Input,text1=None,text2=None,text3=None,text4=None,text5=None,text6=None,text7=None,text8=None,): def eight_text_switch(self,Input,text1=None,text2=None,text3=None,text4=None,text5=None,text6=None,text7=None,text8=None,):
@@ -199,7 +202,7 @@ class EndlessNode_SixIntIOSwitch:
RETURN_NAMES = ("INT1","INT2","INT3","INT4","INT5","INT6",) RETURN_NAMES = ("INT1","INT2","INT3","INT4","INT5","INT6",)
FUNCTION = "six_intIO_switch" FUNCTION = "six_intIO_switch"
CATEGORY = "Endless 🌊✨/Switches" CATEGORY = "Endless 🌊✨/Switches/Fixed"
def six_intIO_switch(self, Input, INT1=0, INT2=0, INT3=0, INT4=0, INT5=0, INT6=0): def six_intIO_switch(self, Input, INT1=0, INT2=0, INT3=0, INT4=0, INT5=0, INT6=0):
@@ -241,7 +244,7 @@ class EndlessNode_SixIntIOWidget:
RETURN_NAMES = ("INT1","INT2","INT3","INT4","INT5","INT6",) RETURN_NAMES = ("INT1","INT2","INT3","INT4","INT5","INT6",)
FUNCTION = "six_int_widget" FUNCTION = "six_int_widget"
CATEGORY = "Endless 🌊✨/Switches" CATEGORY = "Endless 🌊✨/Switches/Fixed"
def six_int_widget(self,int1,int2,int3,int4,int5,int6): def six_int_widget(self,int1,int2,int3,int4,int5,int6):
@@ -394,96 +397,96 @@ class EndlessNode_ComboXLParameterizer:
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) 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 # # IMAGE SCORING BLOCK # IT'S DEAD JIM, WHY CAN'T WE HAVE NICE THINGS?
#---------------------------------------------- #----------------------------------------------
# Aesthetic Scoring Node # Aesthetic Scoring Node
folder_paths.folder_names_and_paths["aesthetic"] = ([os.path.join(folder_paths.models_dir,"aesthetic")], folder_paths.supported_pt_extensions) # folder_paths.folder_names_and_paths["aesthetic"] = ([os.path.join(folder_paths.models_dir,"aesthetic")], folder_paths.supported_pt_extensions)
class MLP(pl.LightningModule): # class MLP(pl.LightningModule):
def __init__(self, input_size, xcol='emb', ycol='avg_rating'): # def __init__(self, input_size, xcol='emb', ycol='avg_rating'):
super().__init__() # super().__init__()
self.input_size = input_size # self.input_size = input_size
self.xcol = xcol # self.xcol = xcol
self.ycol = ycol # self.ycol = ycol
self.layers = nn.Sequential( # self.layers = nn.Sequential(
nn.Linear(self.input_size, 1024), # nn.Linear(self.input_size, 1024),
#nn.ReLU(), # #nn.ReLU(),
nn.Dropout(0.2), # nn.Dropout(0.2),
nn.Linear(1024, 128), # nn.Linear(1024, 128),
#nn.ReLU(), # #nn.ReLU(),
nn.Dropout(0.2), # nn.Dropout(0.2),
nn.Linear(128, 64), # nn.Linear(128, 64),
#nn.ReLU(), # #nn.ReLU(),
nn.Dropout(0.1), # nn.Dropout(0.1),
nn.Linear(64, 16), # nn.Linear(64, 16),
#nn.ReLU(), # #nn.ReLU(),
nn.Linear(16, 1) # nn.Linear(16, 1)
) # )
def forward(self, x): # def forward(self, x):
return self.layers(x) # return self.layers(x)
def training_step(self, batch, batch_idx): # def training_step(self, batch, batch_idx):
x = batch[self.xcol] # x = batch[self.xcol]
y = batch[self.ycol].reshape(-1, 1) # y = batch[self.ycol].reshape(-1, 1)
x_hat = self.layers(x) # x_hat = self.layers(x)
loss = F.mse_loss(x_hat, y) # loss = F.mse_loss(x_hat, y)
return loss # return loss
def validation_step(self, batch, batch_idx): # def validation_step(self, batch, batch_idx):
x = batch[self.xcol] # x = batch[self.xcol]
y = batch[self.ycol].reshape(-1, 1) # y = batch[self.ycol].reshape(-1, 1)
x_hat = self.layers(x) # x_hat = self.layers(x)
loss = F.mse_loss(x_hat, y) # loss = F.mse_loss(x_hat, y)
return loss # return loss
def configure_optimizers(self): # def configure_optimizers(self):
optimizer = torch.optim.Adam(self.parameters(), lr=1e-3) # optimizer = torch.optim.Adam(self.parameters(), lr=1e-3)
return optimizer # return optimizer
def normalized(a, axis=-1, order=2): # def normalized(a, axis=-1, order=2):
import numpy as np # pylint: disable=import-outside-toplevel # import numpy as np # pylint: disable=import-outside-toplevel
l2 = np.atleast_1d(np.linalg.norm(a, order, axis)) # l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
l2[l2 == 0] = 1 # l2[l2 == 0] = 1
return a / np.expand_dims(l2, axis) # return a / np.expand_dims(l2, axis)
class EndlessNode_Scoring: # class EndlessNode_Scoring:
def __init__(self): # def __init__(self):
pass # pass
@classmethod # @classmethod
def INPUT_TYPES(cls): # def INPUT_TYPES(cls):
return { # return {
"required": { # "required": {
"model_name": (folder_paths.get_filename_list("aesthetic"), {"multiline": False, "default": "chadscorer.pth"}), # "model_name": (folder_paths.get_filename_list("aesthetic"), {"multiline": False, "default": "chadscorer.pth"}),
"image": ("IMAGE",), # "image": ("IMAGE",),
} # }
} # }
RETURN_TYPES = ("NUMBER","IMAGE") # RETURN_TYPES = ("NUMBER","IMAGE")
FUNCTION = "calc_score" # FUNCTION = "calc_score"
CATEGORY = "Endless 🌊✨/Scoring" # CATEGORY = "Endless 🌊✨/Scoring"
def calc_score(self, model_name, image): # def calc_score(self, model_name, image):
m_path = folder_paths.folder_names_and_paths["aesthetic"][0] # m_path = folder_paths.folder_names_and_paths["aesthetic"][0]
m_path2 = os.path.join(m_path[0], model_name) # m_path2 = os.path.join(m_path[0], model_name)
model = MLP(768) # CLIP embedding dim is 768 for CLIP ViT L 14 # model = MLP(768) # CLIP embedding dim is 768 for CLIP ViT L 14
s = torch.load(m_path2) # s = torch.load(m_path2)
model.load_state_dict(s) # model.load_state_dict(s)
model.to("cuda") # model.to("cuda")
model.eval() # model.eval()
device = "cuda" # device = "cuda"
model2, preprocess = clip.load("ViT-L/14", device=device) # RN50x64 # model2, preprocess = clip.load("ViT-L/14", device=device) # RN50x64
tensor_image = image[0] # tensor_image = image[0]
img = (tensor_image * 255).to(torch.uint8).numpy() # img = (tensor_image * 255).to(torch.uint8).numpy()
pil_image = Image.fromarray(img, mode='RGB') # pil_image = Image.fromarray(img, mode='RGB')
image2 = preprocess(pil_image).unsqueeze(0).to(device) # image2 = preprocess(pil_image).unsqueeze(0).to(device)
with torch.no_grad(): # with torch.no_grad():
image_features = model2.encode_image(image2) # image_features = model2.encode_image(image2)
im_emb_arr = normalized(image_features.cpu().detach().numpy()) # im_emb_arr = normalized(image_features.cpu().detach().numpy())
prediction = model(torch.from_numpy(im_emb_arr).to(device).type(torch.cuda.FloatTensor)) # prediction = model(torch.from_numpy(im_emb_arr).to(device).type(torch.cuda.FloatTensor))
final_prediction = round(float(prediction[0]), 2) # final_prediction = round(float(prediction[0]), 2)
del model # del model
return (final_prediction,) # return (final_prediction,)
# #---------------------------------------------- NOT WORKING, NEED TO LOOK AT IT # #---------------------------------------------- NOT WORKING, NEED TO LOOK AT IT
# # Aesthetic Scoring Node with Scoring passed to image # # Aesthetic Scoring Node with Scoring passed to image
@@ -533,86 +536,86 @@ class EndlessNode_Scoring:
#---------------------------------------------- #----------------------------------------------
# Image Reward Scoring # Image Reward Scoring
class EndlessNode_ImageReward: # class EndlessNode_ImageReward:
def __init__(self): # def __init__(self):
self.model = None # self.model = None
@classmethod # @classmethod
def INPUT_TYPES(cls): # def INPUT_TYPES(cls):
return { # return {
"required": { # "required": {
"model": ("STRING", {"multiline": False, "default": "ImageReward-v1.0"}), # "model": ("STRING", {"multiline": False, "default": "ImageReward-v1.0"}),
"prompt": ("STRING", {"multiline": True, "forceInput": True}), # "prompt": ("STRING", {"multiline": True, "forceInput": True}),
"images": ("IMAGE",), # "images": ("IMAGE",),
}, # },
} # }
RETURN_TYPES = ("FLOAT", "STRING", "FLOAT", "STRING") # RETURN_TYPES = ("FLOAT", "STRING", "FLOAT", "STRING")
RETURN_NAMES = ("SCORE_FLOAT", "SCORE_STRING", "VALUE_FLOAT", "VALUE_STRING") # RETURN_NAMES = ("SCORE_FLOAT", "SCORE_STRING", "VALUE_FLOAT", "VALUE_STRING")
CATEGORY = "Endless 🌊✨/Scoring" # CATEGORY = "Endless 🌊✨/Scoring"
FUNCTION = "process_images" # FUNCTION = "process_images"
def process_images(self, model, prompt, images,): #rounded): # def process_images(self, model, prompt, images,): #rounded):
if self.model is None: # if self.model is None:
self.model = RM.load(model) # self.model = RM.load(model)
score = 0.0 # score = 0.0
for image in images: # for image in images:
# convert to PIL image # # convert to PIL image
i = 255.0 * image.cpu().numpy() # i = 255.0 * image.cpu().numpy()
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) # img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
score += self.model.score(prompt, [img]) # score += self.model.score(prompt, [img])
score /= len(images) # score /= len(images)
# assume std dev follows normal distribution curve # # 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 # 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)) # return (score, str(score), valuescale, str(valuescale))
#---------------------------------------------- NOT WORKING, NEED TO LOOK AT # #---------------------------------------------- NOT WORKING, NEED TO LOOK AT
# Image Reward Scoring with score passed to image # # Image Reward Scoring with score passed to image
class EndlessNode_ImageRewardAutoScore: # class EndlessNode_ImageRewardAutoScore:
def __init__(self): # def __init__(self):
self.model = None # self.model = None
@classmethod # @classmethod
def INPUT_TYPES(cls): # def INPUT_TYPES(cls):
return { # return {
"required": { # "required": {
"model": ("STRING", {"multiline": False, "default": "ImageReward-v1.0"}), # "model": ("STRING", {"multiline": False, "default": "ImageReward-v1.0"}),
"prompt": ("STRING", {"multiline": True, "forceInput": True}), # "prompt": ("STRING", {"multiline": True, "forceInput": True}),
"images": ("IMAGE",), # "images": ("IMAGE",),
}, # },
} # }
RETURN_TYPES = ("FLOAT", "STRING", "FLOAT", "STRING", "IMAGE") # RETURN_TYPES = ("FLOAT", "STRING", "FLOAT", "STRING", "IMAGE")
RETURN_NAMES = ("SCORE_FLOAT", "SCORE_STRING", "VALUE_FLOAT", "VALUE_STRING", "TO_IMAGE") # RETURN_NAMES = ("SCORE_FLOAT", "SCORE_STRING", "VALUE_FLOAT", "VALUE_STRING", "TO_IMAGE")
OUTPUT_NODE = True # OUTPUT_NODE = True
CATEGORY = "Endless 🌊✨/Scoring" # CATEGORY = "Endless 🌊✨/Scoring"
FUNCTION = "score_meta" # FUNCTION = "score_meta"
def score_meta(self, model, prompt, images): # def score_meta(self, model, prompt, images):
if self.model is None: # if self.model is None:
self.model = RM.load(model) # self.model = RM.load(model)
# Scoring part # # Scoring part
score = 0.0 # score = 0.0
for image in images: # for image in images:
i = 255.0 * image.cpu().numpy() # i = 255.0 * image.cpu().numpy()
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) # img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
score += self.model.score(prompt, [img]) # score += self.model.score(prompt, [img])
score /= len(images) # score /= len(images)
valuescale = 0.5 * (1 + math.erf(score / math.sqrt(2))) * 10 # valuescale = 0.5 * (1 + math.erf(score / math.sqrt(2))) * 10
# Metadata part # # Metadata part
extra_pnginfo = {"SCORE": str(score)} # extra_pnginfo = {"SCORE": str(score)}
# Returning both the score and the modified image # # Returning both the score and the modified image
return (score, str(score), valuescale, str(valuescale), images) # return (score, str(score), valuescale, str(valuescale), images)
# ______________________________________________________________________________________________________________________________________________________________ # ______________________________________________________________________________________________________________________________________________________________
# IMAGE SAVERS BLOCK # # IMAGE SAVERS BLOCK #
@@ -642,52 +645,12 @@ class EndlessNode_ImageSaver:
"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO" "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"
}, },
} }
RETURN_TYPES = () RETURN_TYPES = ()
FUNCTION = "save_images" FUNCTION = "save_images"
OUTPUT_NODE = True OUTPUT_NODE = True
CATEGORY = "Endless 🌊✨/IO" CATEGORY = "Endless 🌊✨/IO"
def generate_filenames(self, filename_prefix, delimiter, counter, filename_number_padding, filename_number_start, img_extension, img_folder, json_folder):
# Determine the maximum allowed length for the complete path and subtract eight for the filename extensino
max_path_length = 248
# Calculate the length of the parts that are constant (excluding the variable counter part)
constant_part_length = len(filename_prefix) + len(delimiter) + len(img_extension) + 1 # +1 for the counter itself
# Calculate the available space for the variable counter part
available_space = max_path_length - constant_part_length - len(str(filename_number_padding))
# Truncate the filename_prefix if it exceeds the available space
if len(filename_prefix) > available_space:
filename_prefix = filename_prefix[:available_space]
# Generate the filename with the truncated prefix
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"
# Calculate the maximum allowed length for image and JSON folders
max_folder_length = max_path_length - len(os.path.basename(img_file)) - len(img_extension)
# Check and truncate img_folder length if necessary
if img_folder and len(img_folder) > max_folder_length:
img_folder = img_folder[:max_folder_length]
# Check and truncate json_folder length if necessary
if json_folder and len(json_folder) > max_folder_length:
json_folder = json_folder[:max_folder_length]
# Construct full paths for image and text files based on folders provided
img_file = os.path.join(img_folder, os.path.basename(img_file)) if img_folder else os.path.join(self.output_dir, img_file)
json_file = os.path.join(json_folder, os.path.basename(json_file)) if json_folder else os.path.join(os.path.dirname(img_file), os.path.basename(json_file))
# ...
return img_file, json_file
def save_images(self, images, filename_prefix="ComfyUI", delimiter="_", def save_images(self, images, filename_prefix="ComfyUI", delimiter="_",
filename_number_padding=4, filename_number_start='false', filename_number_padding=4, filename_number_start='false',
img_folder=None, json_folder=None, prompt=None, extra_pnginfo=None): img_folder=None, json_folder=None, prompt=None, extra_pnginfo=None):
@@ -713,7 +676,9 @@ class EndlessNode_ImageSaver:
for x in extra_pnginfo: for x in extra_pnginfo:
metadata.add_text(x, json.dumps(extra_pnginfo[x])) 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) img_file, json_file = self.generate_filenames(filename_prefix, delimiter, counter,
filename_number_padding, filename_number_start,
img_extension, img_folder, json_folder)
try: try:
if img_extension == '.png': if img_extension == '.png':
@@ -746,6 +711,90 @@ class EndlessNode_ImageSaver:
counter += 1 counter += 1
return {"ui": {"results": results}} 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 # # CONVERTER NODES BLOCK #
# #
@@ -771,7 +820,7 @@ class EndlessNode_FloattoInt:
return (int(FloatValue),) return (int(FloatValue),)
# ---------------------------------------------- # ----------------------------------------------
# Float value to Number. There is no real "Number" function in Python, this is here so that nodes that need a NUMBER can take the FLOAT value # Float to Number. 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: class EndlessNode_FloattoNum:
CATEGORY = "Endless 🌊✨/Converters/Float" CATEGORY = "Endless 🌊✨/Converters/Float"
@@ -814,7 +863,7 @@ class EndlessNode_FloattoString:
return(str(FloatValue),) return(str(FloatValue),)
# ---------------------------------------------- # ----------------------------------------------
# Float value to X # Float to X
class EndlessNode_FloattoX: class EndlessNode_FloattoX:
CATEGORY = "Endless 🌊✨/Converters/Float" CATEGORY = "Endless 🌊✨/Converters/Float"
@@ -1098,6 +1147,8 @@ class EndlessNode_StringtoX:
except (ValueError, TypeError): # Handle non-numerical input here by returning default value of 0 except (ValueError, TypeError): # Handle non-numerical input here by returning default value of 0
return 0, 0.0, 0.0 return 0, 0.0, 0.0
#______________________________________________________________________________________________________________________________________________________________ #______________________________________________________________________________________________________________________________________________________________
# CREDITS # # CREDITS #
# #

View File

@@ -1,7 +1,7 @@
clip @ git+https://github.com/openai/CLIP.git # clip @ git+https://github.com/openai/CLIP.git
pytorch-lightning pytorch-lightning
image-reward==1.4 # image-reward==1.4
colorama colorama
ftfy # ftfy
regex # regex
tqdm # tqdm