import random import comfy.utils import os import numpy as np from urllib import request, parse import folder_paths from PIL import Image, ImageOps,ImageFilter,ImageEnhance,ImageDraw,ImageSequence, ImageFont from PIL.PngImagePlugin import PngInfo import hashlib import requests import json # def queue_prompt(prompt_workflow): # p = {"prompt": prompt_workflow} # data = json.dumps(p).encode('utf-8') # req = request.Request("http://127.0.0.1:8188/prompt", data=data) # request.urlopen(req) embeddings_path=os.path.join(folder_paths.models_dir, "embeddings") def get_files_with_extension(directory, extension): file_list = [] for root, dirs, files in os.walk(directory): for file in files: if file.endswith(extension): file_name = os.path.splitext(file)[0] file_list.append(file_name) return file_list def join_with_(text_list,delimiter): joined_text = delimiter.join(text_list) return joined_text def load_json(file_path): try: with open(file_path, 'r') as json_file: data = json.load(json_file) return data except FileNotFoundError: print(f"File not found: {file_path}") return None except json.JSONDecodeError: print(f"Error decoding JSON in file: {file_path}") return None def save_json(data_dict, file_path): try: with open(file_path, 'w') as json_file: json.dump(data_dict, json_file, indent=4) print(f"Data saved to {file_path}") except Exception as e: print(f"Error saving JSON to file: {e}") # pysss的lora加载器 # def get_model_version_info(hash_value): # # http://127.0.0.1:1082 # proxies = {'http': 'http://127.0.0.1:1082', 'https': 'https://127.0.0.1:1082'} # api_url = f"https://civitai.com/api/v1/model-versions/by-hash/{hash_value}" # print(api_url) # response = requests.get(api_url,proxies=proxies, verify=False) # if response.status_code == 200: # return response.json() # else: # return None # def calculate_sha256(file_path): # sha256_hash = hashlib.sha256() # with open(file_path, "rb") as f: # for chunk in iter(lambda: f.read(4096), b""): # sha256_hash.update(chunk) # return sha256_hash.hexdigest() class AnyType(str): """A special class that is always equal in not equal comparisons. Credit to pythongosssss""" def __ne__(self, __value: object) -> bool: return False any_type = AnyType("*") default_prompt1='''Swing Slide Climbing frame Sandbox See-saw Merry-go-round Jungle gym Trampoline Monkey bars Rocking horse Playhouse Hopscotch Balance beam Spring rider Water play area Ball pit Tunnel Zip line Basketball hoop Bicycle rack Spinner Climbing wall Rope ladder Tetherball Flying fox Swinging bridge Spiral slide Water sprinkler Pedal go-kart Miniature golf course ''' default_prompt1="\n".join([p.strip() for p in default_prompt1.split('\n') if p.strip()!='']) def tensor2pil(image): return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8)) def addWeight(text, weight=1): if weight == 1: return text else: return f"({text}:{round(weight,3)})" def prompt_delete_words(sentence, new_words_length): # 使用逗号分割句子,并去除空格 words = [word.strip() for word in sentence.split(",")] # 计算需要删除的单词数量 num_to_delete = len(words) - new_words_length words_to=[w for w in words] # 逐个删除单词并存储在新列表中 new_words = [] for i in range(len(words)): if num_to_delete > 0: num_to_delete -= 1 else: words_to.pop() if len(words_to)>0: new_words.append(", ".join(words_to)) return new_words # # 测试方法 # sentence = "a computer, a glass tablet with a keyboard on a dark background, 3d illustration, reflection, cgi 8k, clear glass, archaic, cut-away, white outline" # new_words_length = 5 # result = prompt_delete_words(sentence, new_words_length) # print(result) class PromptImage: def __init__(self): self.output_dir = folder_paths.get_output_directory() self.type = "output" self.prefix_append = "PromptImage" self.compress_level = 4 @classmethod def INPUT_TYPES(s): return { "required": { "prompts": ("STRING", { "multiline": True, "default": '', "dynamicPrompts": False }), "images": ("IMAGE",{"default": None}), "save_to_image": (["enable", "disable"],), } } RETURN_TYPES = () OUTPUT_NODE = True INPUT_IS_LIST = True FUNCTION = "run" CATEGORY = "♾️Mixlab/Output" # 运行的函数 def run(self,prompts,images,save_to_image): filename_prefix="mixlab_" filename_prefix += self.prefix_append full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path( filename_prefix, self.output_dir, images[0].shape[1], images[0].shape[0]) results = list() save_to_image=save_to_image[0]=='enable' for index in range(len(images)): res=[] imgs=images[index] for image in imgs: img=tensor2pil(image) metadata = None if save_to_image: metadata = PngInfo() prompt_text=prompts[index] if prompt_text is not None: metadata.add_text("prompt_text", prompt_text) file = f"{filename}_{index}_{counter:05}_.png" img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=self.compress_level) res.append({ "filename": file, "subfolder": subfolder, "type": self.type }) counter += 1 results.append(res) return { "ui": { "_images": results,"prompts":prompts } } class PromptSimplification: @classmethod def INPUT_TYPES(s): return { "required": { "prompt": ("STRING", { "multiline": True, "default": '', "dynamicPrompts": False }), "length":("INT", {"default": 5, "min": 1,"max":100, "step": 1, "display": "number"}), # "min_value":("FLOAT", { # "default": -2, # "min": -10, # "max": 0xffffffffffffffff, # "step": 0.01, # "display": "number" # }), # "max_value":("FLOAT", { # "default": 2, # "min": -10, # "max": 0xffffffffffffffff, # "step": 0.01, # "display": "number" # }), } } RETURN_TYPES = ("STRING",) RETURN_NAMES = ("prompts",) FUNCTION = "run" CATEGORY = "♾️Mixlab/Prompt" INPUT_IS_LIST = True OUTPUT_IS_LIST = (True,) OUTPUT_NODE = True # 运行的函数 def run(self,prompt,length): length=length[0] result=[] for p in prompt: nps=prompt_delete_words(p,length) for n in nps: result.append(n) result= [elem.strip() for elem in result if elem.strip()] return {"ui": {"prompts": result}, "result": (result,)} class PromptSlide: @classmethod def INPUT_TYPES(s): return { "required": { "prompt_keyword": ("STRING", { "multiline": False, "default": '', "dynamicPrompts": False }), "weight":("FLOAT", {"default": 1, "min": -3,"max": 3, "step": 0.01, "display": "slider"}), # "min_value":("FLOAT", { # "default": -2, # "min": -10, # "max": 0xffffffffffffffff, # "step": 0.01, # "display": "number" # }), # "max_value":("FLOAT", { # "default": 2, # "min": -10, # "max": 0xffffffffffffffff, # "step": 0.01, # "display": "number" # }), } } RETURN_TYPES = ("STRING",) RETURN_NAMES = ("prompt",) FUNCTION = "run" CATEGORY = "♾️Mixlab/Prompt" INPUT_IS_LIST = False OUTPUT_IS_LIST = (False,) OUTPUT_NODE = False # 运行的函数 def run(self,prompt_keyword,weight): # if weight < min_value: # weight= min_value # elif weight > max_value: # weight= max_value p=addWeight(prompt_keyword,weight) return (p,) class RandomPrompt: ''' @classmethod 是Python中的一个装饰器,用于将一个方法标记为类方法。 类方法是与类相关联的方法,而不是与实例相关联的方法。 这意味着类方法可以直接通过类进行调用,而不需要先创建一个类的实例。 ''' @classmethod def INPUT_TYPES(s): return { "required": { "max_count": ("INT", {"default": 9, "min": 1, "max": 1000}), # "image_field": ("IMAGE",), "mutable_prompt": ("STRING", { "multiline": True, "default": default_prompt1 }), "immutable_prompt": ("STRING", { "multiline": True, "default": 'sticker, Cartoon, ``' }), "random_sample": (["enable", "disable"],), # "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "step": 1}), }, "optional":{ "seed": (any_type, {"default": 0, "min": 0, "max": 0xffffffffffffffff}), } } RETURN_TYPES = ("STRING",) FUNCTION = "run" CATEGORY = "♾️Mixlab/Prompt" OUTPUT_IS_LIST = (True,) OUTPUT_NODE = True # 运行的函数 def run(self,max_count,mutable_prompt,immutable_prompt,random_sample,seed=0): # print('#运行的函数',mutable_prompt,immutable_prompt,max_count,random_sample) # Split the text into an array of words words1 = mutable_prompt.split("\n") # Split the text into an array of words words2 = immutable_prompt.split("\n") # 进度条 pbar = comfy.utils.ProgressBar(len(words1)*len(words2)) # Select a random word from the array # random_word = random.choice(words) prompts=[] for w1 in words1: w1=w1.strip() for w2 in words2: w2=w2.strip() if '``' not in w2: if w2=="": w2='``' else: w2=w2+',``' if w1!='' and w2!='': prompts.append(w2.replace('``', w1)) pbar.update(1) if len(prompts)==0: prompts.append(immutable_prompt) if random_sample=='enable': # 随机从数组中取max个元素 prompts = random.sample(prompts, min(max_count,len(prompts))) else: prompts = prompts[:min(max_count,len(prompts))] prompts= [elem.strip() for elem in prompts if elem.strip()] # return (new_prompt) return {"ui": {"prompts": prompts}, "result": (prompts,)} # class LoraPrompt: # @classmethod # def INPUT_TYPES(s): # return { # "required": { # "lora_name":(sorted(folder_paths.get_filename_list("loras"), key=str.lower),), # "weight": ("FLOAT", {"default": 1, "min": -2, "max": 2,"step":0.01 ,"display": "slider"}), # "force_update": ("BOOLEAN", {"default": False}), # }, # } # RETURN_TYPES = ("STRING","STRING",any_type) # RETURN_NAMES = ("lora_name","prompt","tags",) # FUNCTION = "run" # CATEGORY = "♾️Mixlab/Prompt" # OUTPUT_IS_LIST = (False,False,True,) # # OUTPUT_NODE = True # # 运行的函数 # def run(self,lora_name,weight,force_update=False): # # print('##LoraPrompt',__file__) # # 从本地数据库读取 # json_tags_path = os.path.join(os.path.dirname(os.path.dirname(__file__)),r'data/loras_tags.json') # if not os.path.exists(json_tags_path): # save_json({},json_tags_path) # lora_tags = load_json(json_tags_path) # output_tags = lora_tags.get(lora_name, None) if lora_tags is not None else None # if output_tags is not None: # output_tags = ",".join(output_tags) # print("trainedWords:",output_tags) # else: # output_tags = "" # lora_path = folder_paths.get_full_path("loras", lora_name) # if output_tags == "" or force_update: # print("calculating lora hash") # LORAsha256 = calculate_sha256(lora_path) # print("requesting infos") # model_info = get_model_version_info(LORAsha256) # if model_info is not None: # if "trainedWords" in model_info: # print("tags found!") # if lora_tags is None: # lora_tags = {} # lora_tags[lora_name] = model_info["trainedWords"] # save_json(lora_tags,json_tags_path) # output_tags = ",".join(model_info["trainedWords"]) # print("trainedWords:",output_tags) # else: # print("No informations found.") # if lora_tags is None: # lora_tags = {} # lora_tags[lora_name] = [] # save_json(lora_tags,json_tags_path) # weight = round(weight, 3) # prompt=[] # for p in output_tags.split(','): # if weight!=1: # prompt.append('('+p+':'+str(weight)+')') # else: # prompt.append(p) # prompt=",".join(prompt) # return (lora_name,prompt,output_tags.split(','),) class EmbeddingPrompt: @classmethod def INPUT_TYPES(s): return { "required": { "embedding":(folder_paths.get_filename_list("embeddings"),), "weight": ("FLOAT", {"default": 1, "min": -2, "max": 2,"step":0.01 ,"display": "slider"}), }, } RETURN_TYPES = ("STRING",) FUNCTION = "run" CATEGORY = "♾️Mixlab/Prompt" OUTPUT_IS_LIST = (False,) # OUTPUT_NODE = True # 运行的函数 def run(self,embedding,weight): weight = round(weight, 3) prompt='embedding:'+embedding if weight!=1: prompt='('+prompt+':'+str(weight)+')' prompt=" "+prompt+' ' # return (new_prompt) return (prompt,) # RETURN_TYPES = (any_type,) # conditioning :提示,正向or负向 # clip:clip模型 # gligen_textbox_model:gligen模型 # grids:矩形框的集合 # labels:每个矩形框对应的标签的集合 # index:选取第几个矩形框作为gligen的box class GLIGENTextBoxApply_Advanced: @classmethod def INPUT_TYPES(s): return {"required": {"conditioning": ("CONDITIONING", ), "clip": ("CLIP", ), "gligen_textbox_model": ("GLIGEN", ), "grids": ("_GRID",), "labels": ("STRING", { "multiline": True, "default": "", "forceInput": True }), "index": ("INT", {"default": -1, "min": -1, "max": 300, "step": 1}), "max_size": ("INT", {"default": 8, "min": 1, "max": 300, "step": 1}), "random_shuffle":(["on","off"],), }, "optional":{ "seed": (any_type, {"default": 0, "min": 0, "max": 0xffffffffffffffff,"step": 1}), } } RETURN_TYPES = ("CONDITIONING","STRING",) RETURN_NAMES = ("CONDITIONING","label",) FUNCTION = "run" # INPUT_IS_LIST = True CATEGORY = "♾️Mixlab/Prompt" def run(self, conditioning, clip, gligen_textbox_model, grids, labels, index,max_size,random_shuffle,seed=0): # print('grids',grids) # conditioning=conditioning[0] # clip=clip[0] # gligen_textbox_model=gligen_textbox_model[0] # index=index[0] # max_size=max_size[0] # random_shuffle=random_shuffle[0] texts=labels if index>-1: texts=[labels[index]] grids=[grids[index]] if random_shuffle=='on': sss=[[texts[i],grids[i]] for i in range(len(texts))] random.shuffle(sss) texts=[s[0] for s in sss] grids=[s[1] for s in sss] if len(texts) > max_size: texts = texts[:max_size] c = [] for t in conditioning: n = [t[0], t[1].copy()] # 多个 position_params=[] for i in range(len(texts)): text=texts[i] grid=grids[i] x,y,width,height=grid # print(text) cond, cond_pooled = clip.encode_from_tokens(clip.tokenize(text), return_pooled=True) position_params =position_params+ [(cond_pooled, height // 8, width // 8, y // 8, x // 8)] # 前一个 prev = [] if "gligen" in n[1]: prev = n[1]['gligen'][2] n[1]['gligen'] = ("position", gligen_textbox_model, prev + position_params) # print('gligen',n) c.append(n) # 下面这个写法有bug # for i in range(len(texts)): # text=texts[i] # grid=grids[i] # x,y,width,height=grid # cond, cond_pooled = clip.encode_from_tokens(clip.tokenize(text), return_pooled=True) # for t in conditioning: # n = [t[0], t[1].copy()] # position_params = [(cond_pooled, height // 8, width // 8, y // 8, x // 8)] # prev = [] # if "gligen" in n[1]: # prev = n[1]['gligen'][2] # n[1]['gligen'] = ("position", gligen_textbox_model, prev + position_params) # c.append(n) return (c,texts, ) class JoinWithDelimiter: @classmethod def INPUT_TYPES(s): return {"required": { "text_list": (any_type,), "delimiter":(["newline","comma","backslash","space"],), }, } RETURN_TYPES = ("STRING",) FUNCTION = "run" CATEGORY = "♾️Mixlab/Text" INPUT_IS_LIST = True # 当true的时候,输入时list,当false的时候,如果输入是list,则会自动包一层for循环调用 OUTPUT_IS_LIST = (False,) def run(self,text_list,delimiter): delimiter=delimiter[0] if delimiter =='newline': delimiter='\n' elif delimiter=='comma': delimiter=',' elif delimiter=='backslash': delimiter='\\' elif delimiter=='space': delimiter=' ' t='' if isinstance(text_list, list): t=join_with_(text_list,delimiter) return (t,)