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.
**UPDATE: Oct 7, 2023**
+ ***REMOVED THE AESTHERIC SCORERS, TOO MANY PEOPLE CAN'T GET CLIP LOADED. WILL REVISIT AFTER VACATION***
**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.
**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.
+ Added nodes to convert from one numeric type to another, and to string. **Still in beta**
**UPDATE: Sep 24, 2023**

View File

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

View File

@@ -10,6 +10,9 @@
# Endless Sea of Stars Custom Node Collection
#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 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
@@ -41,8 +44,8 @@ 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 ImageReward as RM
# import clip
import colorama
import datetime
import folder_paths
@@ -59,7 +62,7 @@ import sys
import time
import torch
import torch.nn as nn
# import random
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",)
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):
@@ -151,7 +154,7 @@ class EndlessNode_EightTextInputSwitch:
RETURN_NAMES = ("Output",)
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,):
@@ -199,7 +202,7 @@ class EndlessNode_SixIntIOSwitch:
RETURN_NAMES = ("INT1","INT2","INT3","INT4","INT5","INT6",)
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):
@@ -241,7 +244,7 @@ class EndlessNode_SixIntIOWidget:
RETURN_NAMES = ("INT1","INT2","INT3","INT4","INT5","INT6",)
FUNCTION = "six_int_widget"
CATEGORY = "Endless 🌊✨/Switches"
CATEGORY = "Endless 🌊✨/Switches/Fixed"
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)
#______________________________________________________________________________________________________________________________________________________________
# IMAGE SCORING BLOCK #
# IMAGE SCORING BLOCK # IT'S DEAD JIM, WHY CAN'T WE HAVE NICE THINGS?
#----------------------------------------------
# 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):
def __init__(self, input_size, xcol='emb', ycol='avg_rating'):
super().__init__()
self.input_size = input_size
self.xcol = xcol
self.ycol = ycol
self.layers = nn.Sequential(
nn.Linear(self.input_size, 1024),
#nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(1024, 128),
#nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(128, 64),
#nn.ReLU(),
nn.Dropout(0.1),
nn.Linear(64, 16),
#nn.ReLU(),
nn.Linear(16, 1)
)
def forward(self, x):
return self.layers(x)
def training_step(self, batch, batch_idx):
x = batch[self.xcol]
y = batch[self.ycol].reshape(-1, 1)
x_hat = self.layers(x)
loss = F.mse_loss(x_hat, y)
return loss
def validation_step(self, batch, batch_idx):
x = batch[self.xcol]
y = batch[self.ycol].reshape(-1, 1)
x_hat = self.layers(x)
loss = F.mse_loss(x_hat, y)
return loss
def configure_optimizers(self):
optimizer = torch.optim.Adam(self.parameters(), lr=1e-3)
return optimizer
def normalized(a, axis=-1, order=2):
import numpy as np # pylint: disable=import-outside-toplevel
l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
l2[l2 == 0] = 1
return a / np.expand_dims(l2, axis)
# class MLP(pl.LightningModule):
# def __init__(self, input_size, xcol='emb', ycol='avg_rating'):
# super().__init__()
# self.input_size = input_size
# self.xcol = xcol
# self.ycol = ycol
# self.layers = nn.Sequential(
# nn.Linear(self.input_size, 1024),
# #nn.ReLU(),
# nn.Dropout(0.2),
# nn.Linear(1024, 128),
# #nn.ReLU(),
# nn.Dropout(0.2),
# nn.Linear(128, 64),
# #nn.ReLU(),
# nn.Dropout(0.1),
# nn.Linear(64, 16),
# #nn.ReLU(),
# nn.Linear(16, 1)
# )
# def forward(self, x):
# return self.layers(x)
# def training_step(self, batch, batch_idx):
# x = batch[self.xcol]
# y = batch[self.ycol].reshape(-1, 1)
# x_hat = self.layers(x)
# loss = F.mse_loss(x_hat, y)
# return loss
# def validation_step(self, batch, batch_idx):
# x = batch[self.xcol]
# y = batch[self.ycol].reshape(-1, 1)
# x_hat = self.layers(x)
# loss = F.mse_loss(x_hat, y)
# return loss
# def configure_optimizers(self):
# optimizer = torch.optim.Adam(self.parameters(), lr=1e-3)
# return optimizer
# def normalized(a, axis=-1, order=2):
# import numpy as np # pylint: disable=import-outside-toplevel
# l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
# l2[l2 == 0] = 1
# return a / np.expand_dims(l2, axis)
class EndlessNode_Scoring:
def __init__(self):
pass
# class EndlessNode_Scoring:
# 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",),
}
}
# @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"
CATEGORY = "Endless 🌊✨/Scoring"
# RETURN_TYPES = ("NUMBER","IMAGE")
# FUNCTION = "calc_score"
# 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
return (final_prediction,)
# 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
# return (final_prediction,)
# #---------------------------------------------- NOT WORKING, NEED TO LOOK AT IT
# # Aesthetic Scoring Node with Scoring passed to image
@@ -533,86 +536,86 @@ class EndlessNode_Scoring:
#----------------------------------------------
# Image Reward Scoring
class EndlessNode_ImageReward:
def __init__(self):
self.model = None
# class EndlessNode_ImageReward:
# def __init__(self):
# 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",),
},
}
# @classmethod
# def INPUT_TYPES(cls):
# return {
# "required": {
# "model": ("STRING", {"multiline": False, "default": "ImageReward-v1.0"}),
# "prompt": ("STRING", {"multiline": True, "forceInput": True}),
# "images": ("IMAGE",),
# },
# }
RETURN_TYPES = ("FLOAT", "STRING", "FLOAT", "STRING")
RETURN_NAMES = ("SCORE_FLOAT", "SCORE_STRING", "VALUE_FLOAT", "VALUE_STRING")
# RETURN_TYPES = ("FLOAT", "STRING", "FLOAT", "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):
if self.model is None:
self.model = RM.load(model)
# def process_images(self, model, prompt, images,): #rounded):
# if self.model is None:
# self.model = RM.load(model)
score = 0.0
for image in images:
# convert to PIL image
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)
# 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))
# score = 0.0
# for image in images:
# # convert to PIL image
# 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)
# # 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))
#---------------------------------------------- NOT WORKING, NEED TO LOOK AT
# Image Reward Scoring with score passed to image
# #---------------------------------------------- NOT WORKING, NEED TO LOOK AT
# # Image Reward Scoring with score passed to image
class EndlessNode_ImageRewardAutoScore:
def __init__(self):
self.model = None
# class EndlessNode_ImageRewardAutoScore:
# def __init__(self):
# 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",),
},
}
# @classmethod
# def INPUT_TYPES(cls):
# return {
# "required": {
# "model": ("STRING", {"multiline": False, "default": "ImageReward-v1.0"}),
# "prompt": ("STRING", {"multiline": True, "forceInput": True}),
# "images": ("IMAGE",),
# },
# }
RETURN_TYPES = ("FLOAT", "STRING", "FLOAT", "STRING", "IMAGE")
RETURN_NAMES = ("SCORE_FLOAT", "SCORE_STRING", "VALUE_FLOAT", "VALUE_STRING", "TO_IMAGE")
OUTPUT_NODE = True
# RETURN_TYPES = ("FLOAT", "STRING", "FLOAT", "STRING", "IMAGE")
# RETURN_NAMES = ("SCORE_FLOAT", "SCORE_STRING", "VALUE_FLOAT", "VALUE_STRING", "TO_IMAGE")
# OUTPUT_NODE = True
CATEGORY = "Endless 🌊✨/Scoring"
# CATEGORY = "Endless 🌊✨/Scoring"
FUNCTION = "score_meta"
# FUNCTION = "score_meta"
def score_meta(self, model, prompt, images):
if self.model is None:
self.model = RM.load(model)
# def score_meta(self, model, prompt, images):
# if self.model is None:
# self.model = RM.load(model)
# Scoring part
score = 0.0
for image in images:
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
# # Scoring part
# score = 0.0
# for image in images:
# 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 part
extra_pnginfo = {"SCORE": str(score)}
# # Metadata part
# extra_pnginfo = {"SCORE": str(score)}
# Returning both the score and the modified image
return (score, str(score), valuescale, str(valuescale), images)
# # Returning both the score and the modified image
# return (score, str(score), valuescale, str(valuescale), images)
# ______________________________________________________________________________________________________________________________________________________________
# IMAGE SAVERS BLOCK #
@@ -642,52 +645,12 @@ class EndlessNode_ImageSaver:
"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"
},
}
RETURN_TYPES = ()
FUNCTION = "save_images"
OUTPUT_NODE = True
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="_",
filename_number_padding=4, filename_number_start='false',
img_folder=None, json_folder=None, prompt=None, extra_pnginfo=None):
@@ -713,7 +676,9 @@ class EndlessNode_ImageSaver:
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)
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':
@@ -746,6 +711,90 @@ class EndlessNode_ImageSaver:
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 #
#
@@ -771,7 +820,7 @@ class EndlessNode_FloattoInt:
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:
CATEGORY = "Endless 🌊✨/Converters/Float"
@@ -814,7 +863,7 @@ class EndlessNode_FloattoString:
return(str(FloatValue),)
# ----------------------------------------------
# Float value to X
# Float to X
class EndlessNode_FloattoX:
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
return 0, 0.0, 0.0
#______________________________________________________________________________________________________________________________________________________________
# 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
image-reward==1.4
# image-reward==1.4
colorama
ftfy
regex
tqdm
# ftfy
# regex
# tqdm