diff --git a/chibi_nodes.py b/chibi_nodes.py index eb06e3e..f255612 100644 --- a/chibi_nodes.py +++ b/chibi_nodes.py @@ -2,24 +2,28 @@ import torch import folder_paths import comfy.sd import comfy.utils +import comfy.sample +import comfy.samplers from comfy.cli_args import args -from PIL import Image, ImageOps, ImageFont, ImageDraw +from PIL import Image, ImageOps, ImageFont, ImageDraw, ExifTags from PIL.PngImagePlugin import PngInfo import numpy as np +import re import random import os import time import json - +import math import hashlib - +import latent_preview ### GLOBALS ### MAX_RESOLUTION=32768 base_path = os.path.dirname(os.path.realpath(__file__)) -models_dir = os.path.join(base_path, "extras") +extras_dir = os.path.join(base_path, "extras") -folder_paths.folder_names_and_paths["chibi-wildcards"] = ([os.path.join(models_dir, "chibi-wildcards")], {".txt"}) +folder_paths.folder_names_and_paths["chibi-wildcards"] = ([os.path.join(extras_dir, "chibi-wildcards")], {".txt"}) +folder_paths.folder_names_and_paths["fonts"] = ([os.path.join(extras_dir, "fonts")], {".ttf"}) @@ -266,11 +270,7 @@ class LoadEmbedding: - - - - -class ConditionText: +class ConditionTextMulti: def __init__(self): pass @@ -296,6 +296,8 @@ class ConditionText: def conditiontext(self, clip, first='', second='', third='', fourth='', ): emptystring = "" returnedcond = [] + + #!I probably want to fix this mess at some point. if first != '': firstraw = clip.tokenize(first) first_cond, first_pooled = clip.encode_from_tokens(firstraw, return_pooled=True) @@ -335,6 +337,33 @@ class ConditionText: return (clip,returnedcond[0],returnedcond[1],returnedcond[2],returnedcond[3],) +class ConditionText: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "clip": ("CLIP",), + "text" : ("STRING", {"forceInput": True},), + }} + + RETURN_TYPES = ("CLIP","CONDITIONING",) + FUNCTION = "conditiontext" + CATEGORY = "Chibi-Nodes/Text" + + def conditiontext(self, clip, text=None ): + + if text != None: + tokens = clip.tokenize(text) + else: + tokens = clip.tokenize("") + cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True) + + + return (clip,[[cond, {"pooled_output": pooled}]],) + class SaveImages: def __init__(self): pass @@ -344,19 +373,20 @@ class SaveImages: return { "required": { "filename_type":(["Timestamp","Fixed","Fixed Single"],), - "fixed_filename":("STRING",{"default":"output"}) + "fixed_filename":("STRING",{"default":"output",}) }, "optional":{ "images" : ("IMAGE",), "latents" : ("LATENT",), "vae" : ("VAE",), + "fixed_filename_override":("STRING",{"forceInput": True},) }, "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"}, } - RETURN_TYPES = ("IMAGE",) - RETURN_NAMES = ("images",) + RETURN_TYPES = ("IMAGE","STRING",) + RETURN_NAMES = ("images","filename_list",) FUNCTION = "saveimage" OUTPUT_NODE = True CATEGORY = "Chibi-Nodes" @@ -367,13 +397,17 @@ class SaveImages: def IS_CHANGED(s,seed): seed = random.seed() - def saveimage(self,filename_type,fixed_filename,vae=None, latents=None, images=None, prompt=None, extra_pnginfo=None): + def saveimage(self,filename_type,fixed_filename,fixed_filename_override=None,vae=None, latents=None, images=None, prompt=None, extra_pnginfo=None): + if fixed_filename_override != None: + fixed_filename_override = fixed_filename_override.rsplit(".",1)[0] + fixed_filename = fixed_filename_override now = str(round(time.time())) results = list() + filename_list = [] counter = 0 if images != None: full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(now, folder_paths.get_output_directory(), images[0].shape[1], images[0].shape[0]) @@ -396,6 +430,7 @@ class SaveImages: if filename_type == "Fixed Single": file = f"{fixed_filename}.png" + filename_list.append(file) img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=4) results.append({ "filename": file, @@ -427,6 +462,8 @@ class SaveImages: file = f"{fixed_filename}_{counter:03}.png" if filename_type == "Fixed Single": file = f"{fixed_filename}.png" + + filename_list.append(file) img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=4) results.append({ "filename": file, @@ -437,7 +474,7 @@ class SaveImages: return_results = decoded_latents # return { "ui": { "images": results }} - return {"ui": { "images": results },"result": (return_results,)} + return {"ui": { "images": results },"result": (return_results,str(filename_list),)} @@ -510,14 +547,15 @@ class ImageSimpleResize: "edge":(["largest","smallest","all","width","height"],), }, "optional":{ - "size_override":("INT",{"forceInput": True}) + "size_override":("INT",{"forceInput": True}), + "vae":("VAE",) }} - RETURN_TYPES = ("IMAGE",) + RETURN_TYPES = ("IMAGE","LATENT") OUTPUT_NODE = False FUNCTION = "imagesimpleresize" CATEGORY = "Chibi-Nodes/Image" - def imagesimpleresize(self, image, size, edge, size_override=None): + def imagesimpleresize(self, image, size, edge, size_override=None, vae=None): if size_override: size = size_override @@ -552,7 +590,7 @@ class ImageSimpleResize: image = image.resize((round(size/ratio),round(size)), Image.LANCZOS) if width < height: if size < width: - image = ImageOps.contain(image, (MAX_RESOLUTION,size), Image.LANCZOS) + image = ImageOps.contain(image, (size,MAX_RESOLUTION), Image.LANCZOS) else: image = image.resize((round(size),round(size*ratio)), Image.LANCZOS) if width == height: @@ -576,7 +614,20 @@ class ImageSimpleResize: image = np.array(image).astype(np.float32) / 255.0 image = torch.from_numpy(image)[None,] - return(image,) + if vae != None: + + latent = image + x = (latent.shape[1] // 8) * 8 + y = (latent.shape[2] // 8) * 8 + if latent.shape[1] != x or latent.shape[2] != y: + x_offset = (latent.shape[1] % 8) // 2 + y_offset = (latent.shape[2] % 8) // 2 + latent = latent[:, x_offset:x + x_offset, y_offset:y + y_offset, :] + latent = vae.encode(latent[:,:,:,:3]) + + return (image,{"samples":latent}) + else: + return(image,None,) class Int2String: def __init__(self): @@ -594,21 +645,384 @@ class Int2String: CATEGORY = "Chibi-Nodes/Text" def int2string(self, Int): - print(Int) return(str(Int),) +class LoadImageExtended: + def __init__(self): + + pass + + @classmethod + def INPUT_TYPES(s): + input_dir = folder_paths.get_input_directory() + files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))] + return {"required": + {"image": (sorted(files), {"image_upload": True}), + }, + "optional":{"vae": ("VAE", )} + } + + CATEGORY = "Chibi-Nodes/Image" + + #changes here + RETURN_TYPES = ("IMAGE", "MASK", "LATENT", "STRING", "STRING","INT","INT",) + RETURN_NAMES = ("IMAGE", "MASK", "LATENT","filename","image Info","width","height",) + # + + FUNCTION = "load_image" + + def load_image(self, image, vae=None): + + + image_path = folder_paths.get_annotated_filepath(image) + filename = image_path.rsplit('/',1)[-1] + + im = Image.open(image_path) + + ### Start ai-info.py section, with no exif + def type_changer(value): + if value.isnumeric(): + return int(value) + else: + return value + + + + im.load() + + prompt = {} + if "prompt" in im.info.keys(): + #comfyui, workflow is also available but we aren't getting that today + # prompt = {} + prompt.update({"prompt":json.loads(im.info["prompt"])}) + else: + # automatic111, gosh this is a mess. + if "parameters" in im.info.keys(): + parameters = im.info["parameters"] + prompt = {"parameters": {}} + parameters = re.split('(Negative prompt): |(Negative Template): |(Template): |(ControlNet): |\n',parameters) + + + #removes None and new lines + parameters_clean_none = [] + for i in range(0,len(parameters)): + if parameters[i] == None: + pass + elif parameters[i] == "": + pass + else: + parameters_clean_none.append(parameters[i]) + parameters = parameters_clean_none + + + #settings field + parameters_settings = {} + for i in range(0,len(parameters)): + if parameters[i].split(":",1)[0] == "Steps": + parameters[i] = re.split(", ",parameters[i]) + for k in parameters[i]: + k = k.split(": ",1) + if len(k) == 2: + k[1] = type_changer(k[1]) + + #makes "Size" : "(widthxheight)" into two keys + if k[0] == "Size": + k[1] = k[1].split("x") + for s in range(0,len(k[1])): + k[1][s] = type_changer(k[1][s]) + parameters_settings.update({"width" : k[1][0]}) + parameters_settings.update({"height" : k[1][1]}) + + else: + parameters_settings.update({k[0]:k[1]}) + + + parameters[i] = parameters_settings + + #builder + parameters_built = {} + for i in range(0,len(parameters)): + match parameters[i]: + case "Negative prompt": + parameters_built.update({parameters[i]:parameters[i+1]}) + case "Negative Template": + parameters_built.update({parameters[i]:parameters[i+1]}) + case "Template": + parameters_built.update({parameters[i]:parameters[i+1]}) + case "ControlNet": + parameters_built.update({parameters[i]:parameters[i+1]}) + case dict(): + parameters_built.update(parameters[i]) + case _: + if i == 0: + parameters_built.update({"Positive prompt": parameters[i]}) + pass + + + + prompt["parameters"] = parameters_built + if type(prompt) == dict: + prompt = json.dumps(prompt,indent=2) + + elif type(prompt) == str: + prompt = json.dumps(json.loads(prompt),indent=2) + + + ###end section + + + im = ImageOps.exif_transpose(im) + image = im.convert("RGB") + image = np.array(image).astype(np.float32) / 255.0 + image = torch.from_numpy(image)[None,] + shape = image.shape + width = shape[2] + height = shape[1] + if 'A' in im.getbands(): + mask = np.array(im.getchannel('A')).astype(np.float32) / 255.0 + mask = 1. - torch.from_numpy(mask) + else: + mask = torch.zeros((64,64), dtype=torch.float32, device="cpu") + if vae != None: + + latent = image + x = (latent.shape[1] // 8) * 8 + y = (latent.shape[2] // 8) * 8 + if latent.shape[1] != x or latent.shape[2] != y: + x_offset = (latent.shape[1] % 8) // 2 + y_offset = (latent.shape[2] % 8) // 2 + latent = latent[:, x_offset:x + x_offset, y_offset:y + y_offset, :] + latent = vae.encode(latent[:,:,:,:3]) + + return (image, mask.unsqueeze(0),{"samples":latent},filename,str(prompt),width,height,) + else: + return (image, mask.unsqueeze(0),None,filename,str(prompt),width,height,) + + @classmethod + def IS_CHANGED(s, image, vae=None): + image_path = folder_paths.get_annotated_filepath(image) + m = hashlib.sha256() + with open(image_path, 'rb') as f: + m.update(f.read()) + return m.digest().hex() + + @classmethod + def VALIDATE_INPUTS(s, image, vae=None): + if not folder_paths.exists_annotated_filepath(image): + return "Invalid image file: {}".format(image) + + return True + + + + +class SimpleSampler: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "model": ("MODEL",), + "sampler":(["Normal - euler","Normal - uni_pc","LCM Lora - lcm","SDXL Turbo - dpmpp_sde karras"],), + + "positive": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + "latents": ("LATENT", ), + "mode": (["txt2img","img2img"],) + + }, + "optional": { + "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff,"forceInput": True}), + } + } + + RETURN_TYPES = ("LATENT",) + FUNCTION = "sample" + + CATEGORY = "Chibi-Nodes" + + + def IS_CHANGED(s,seed): + seed = random.seed() + + def sample(self, model,sampler, positive, negative, latents, mode,seed=None, scheduler="normal",sampler_name="euler"): + + # ['euler', 'euler_ancestral', 'heun', 'heunpp2', 'dpm_2', 'dpm_2_ancestral', 'lms', 'dpm_fast', 'dpm_adaptive','dpmpp_2s_ancestral', 'dpmpp_sde', 'dpmpp_sde_gpu', 'dpmpp_2m', 'dpmpp_2m_sde', 'dpmpp_2m_sde_gpu', 'dpmpp_3m_sde', 'dpmpp_3m_sde_gpu', 'ddpm', 'lcm', 'ddim', 'uni_pc', 'uni_pc_bh2'] + # ['normal', 'karras', 'exponential', 'sgm_uniform', 'simple', 'ddim_uniform'] + + match sampler: + case "Normal - euler": + sampler_name = "uni_pc" + steps = 20 + cfg = 7 + case "Normal - uni_pc": + sampler_name = "uni_pc" + steps = 20 + cfg = 7 + case "LCM Lora - lcm": + sampler_name = "lcm" + steps = 8 + cfg = 1.8 + case "SDXL Turbo - dpmpp_sde karras": + sampler_name = "ddmpp_sde" + steps = 8 + cfg = 1.8 + scheduler = "karras" + case _: + steps = 20 + cfg = 7 + + match mode: + case "txt2img": + denoise = 1.0 + case "img2img": + denoise = 0.6 + case _: + denoise = 1.0 + + + + if seed == None: + seed = random.seed() + seed = math.floor(random.random() * 10000000000000000) + + latent_image = latents["samples"] + + batch_inds = latents["batch_index"] if "batch_index" in latents else None + noise = comfy.sample.prepare_noise(latent_image, seed, batch_inds) + + noise_mask = None + if "noise_mask" in latents: + noise_mask = latents["noise_mask"] + + + callback = latent_preview.prepare_callback(model, steps) + + samples = comfy.sample.sample(model=model, noise=noise, steps=steps, cfg=cfg, sampler_name=sampler_name, scheduler=scheduler, positive=positive, negative=negative, latent_image=latent_image, + denoise=denoise, disable_noise=False, start_step=0, last_step=steps, + force_full_denoise=True, noise_mask=noise_mask, callback=callback, disable_pbar=False, seed=seed) + out = latents.copy() + out["samples"] = samples + return (out,) + +class SeedGenerator: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required":{ + "mode":(["Random","Fixed"],), + "fixed_seed":("INT",{"default": 8008135, "min": 0, "max": 0xffffffffffffffff, "step": 1}) + + },} + RETURN_TYPES = ("INT","STRING",) + RETURN_NAMES = ("seed","text") + OUTPUT_NODE = False + FUNCTION = "generator" + CATEGORY = "Chibi-Nodes/Numbers" + + + def IS_CHANGED(s,fixed_seed): + seed = random.seed() + + def generator(self, mode,fixed_seed): + if mode == "Random": + fixed_seed = math.floor(random.random() * 10000000000000000) + if mode == "Fixed": + fixed_seed = fixed_seed + + return(fixed_seed,str(fixed_seed),) + +class ImageAddText: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return{ + "required":{ + + "text": ("STRING",{"default":"Chibi-Nodes","multiline":True},), + "font" : [sorted(folder_paths.get_filename_list("fonts"))], + "font_size":("INT",{"default": 24, "min": 0, "max": 200, "step": 1}), + "font_colour":(["black","white","red","green","blue"],), + "invert_mask":([False,True],), + "position_x":("INT",{"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}), + "position_y":("INT",{"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}), + "width":("INT",{"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1}), + "height":("INT",{"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1}) + }, + "optional":{ + "image": ("IMAGE",), + } + } + + RETURN_TYPES = ("IMAGE","MASK","STRING",) + RETURN_NAMES = ("IMAGE","MASK","text",) + FUNCTION = "addtext" + CATEGORY = "Chibi-Nodes/Image" + + def addtext(self, text, width, height,font,font_size,position_x,position_y,font_colour,invert_mask,image=None): + if image != None: + width = image.shape[2] + height = image.shape[1] + image = Image.fromarray(np.clip(255. * image[0].cpu().numpy(),0,255).astype(np.uint8)) + image = image.convert("RGBA") + else: + image = Image.new('RGBA', (width,height), (255, 255, 255, 0)) + + text_image = Image.new('RGBA', (width,height), (0, 255, 255, 0)) + imaget = ImageDraw.Draw(text_image,) + + msg = text + imaget.fontmode = 'L' + fnt = ImageFont.truetype(folder_paths.get_full_path("fonts", font), font_size) + + imaget.text((position_x,position_y),msg,font=fnt,fill=font_colour) + + if 'A' in text_image.getbands(): + mask = np.array(text_image.getchannel('A')).astype(np.float32) / 255.0 + mask = 1. - torch.from_numpy(mask) + else: + mask = torch.zeros((64,64), dtype=torch.float32, device="cpu") + image.paste(text_image,(0,0),text_image) + image = ImageOps.exif_transpose(image) + image = image.convert("RGB") + image = np.array(image).astype(np.float32) / 255.0 + image = torch.from_numpy(image)[None,] + + if invert_mask: + mask = 1.0 - mask + + + return (image,mask.unsqueeze(0),text,) + + NODE_CLASS_MAPPINGS = { "Loader":Loader, + "SimpleSampler" : SimpleSampler, "Prompts": Prompts, "ImageTool": ImageTool, "Wildcards": Wildcards, "LoadEmbedding": LoadEmbedding, "ConditionText": ConditionText, - "SaveImages":SaveImages, + "ConditionTextMulti": ConditionTextMulti, + "Textbox":Textbox, + + + "ImageSizeInfo" : ImageSizeInfo, "ImageSimpleResize" : ImageSimpleResize, - "Int2String": Int2String, + "ImageAddText" : ImageAddText, + "Int2String": Int2String, + "LoadImageExtended": LoadImageExtended, + "SeedGenerator" : SeedGenerator, + "SaveImages":SaveImages, } diff --git a/examples/chibi-nodes_example_workflow.json b/examples/chibi-nodes_example_workflow.json new file mode 100644 index 0000000..da42868 --- /dev/null +++ b/examples/chibi-nodes_example_workflow.json @@ -0,0 +1,967 @@ +{ + "last_node_id": 17, + "last_link_id": 31, + "nodes": [ + { + "id": 2, + "type": "SimpleSampler", + "pos": [ + 1242, + 42 + ], + "size": [ + 315, + 374.00000762939453 + ], + "flags": {}, + "order": 12, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 1 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 29 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 30 + }, + { + "name": "latents", + "type": "LATENT", + "link": 19 + }, + { + "name": "seed", + "type": "INT", + "link": 31, + "widget": { + "name": "seed" + } + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 6 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "SimpleSampler" + }, + "widgets_values": [ + "LCM Lora - lcm", + "txt2img", + 52035115894296, + "randomize" + ] + }, + { + "id": 4, + "type": "SaveImages", + "pos": [ + 1659, + 51 + ], + "size": [ + 478.60660644531254, + 623.8273217773437 + ], + "flags": {}, + "order": 13, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": null + }, + { + "name": "latents", + "type": "LATENT", + "link": 6 + }, + { + "name": "vae", + "type": "VAE", + "link": 7 + }, + { + "name": "fixed_filename_override", + "type": "STRING", + "link": null, + "widget": { + "name": "fixed_filename_override" + } + } + ], + "outputs": [ + { + "name": "images", + "type": "IMAGE", + "links": [], + "shape": 3, + "slot_index": 0 + }, + { + "name": "filename_list", + "type": "STRING", + "links": null, + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "SaveImages" + }, + "widgets_values": [ + "Timestamp", + "output", + "" + ] + }, + { + "id": 6, + "type": "SaveImages", + "pos": [ + 1654, + 758 + ], + "size": [ + 486.1166064453123, + 508.0973217773437 + ], + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 9 + }, + { + "name": "latents", + "type": "LATENT", + "link": null + }, + { + "name": "vae", + "type": "VAE", + "link": null + }, + { + "name": "fixed_filename_override", + "type": "STRING", + "link": null, + "widget": { + "name": "fixed_filename_override" + } + } + ], + "outputs": [ + { + "name": "images", + "type": "IMAGE", + "links": [], + "shape": 3, + "slot_index": 0 + }, + { + "name": "filename_list", + "type": "STRING", + "links": null, + "shape": 3, + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "SaveImages" + }, + "widgets_values": [ + "Timestamp", + "output", + "" + ] + }, + { + "id": 5, + "type": "ImageAddText", + "pos": [ + 1202, + 760 + ], + "size": { + "0": 400, + "1": 308 + }, + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 18 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 9 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": [], + "shape": 3, + "slot_index": 1 + }, + { + "name": "text", + "type": "STRING", + "links": null, + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "ImageAddText" + }, + "widgets_values": [ + "Chibi-Nodes", + "Ubuntu-Bold.ttf", + 200, + "black", + true, + 10, + 259, + 512, + 512 + ] + }, + { + "id": 10, + "type": "Textbox", + "pos": [ + 1220, + 491 + ], + "size": [ + 400, + 200 + ], + "flags": {}, + "order": 10, + "mode": 0, + "inputs": [ + { + "name": "passthrough", + "type": "STRING", + "link": 13, + "widget": { + "name": "passthrough" + } + } + ], + "outputs": [ + { + "name": "text", + "type": "STRING", + "links": [], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "Textbox" + }, + "widgets_values": [ + "photo of a Cheetah", + "" + ] + }, + { + "id": 3, + "type": "Prompts", + "pos": [ + 36, + 468 + ], + "size": { + "0": 400, + "1": 206 + }, + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 11 + } + ], + "outputs": [ + { + "name": "Positive CONDITIONING", + "type": "CONDITIONING", + "links": [], + "shape": 3, + "slot_index": 0 + }, + { + "name": "Negative CONDITIONING", + "type": "CONDITIONING", + "links": [], + "shape": 3, + "slot_index": 1 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 10, + 27 + ], + "shape": 3, + "slot_index": 2 + }, + { + "name": "Positive text", + "type": "STRING", + "links": [ + 12 + ], + "shape": 3, + "slot_index": 3 + }, + { + "name": "Negative text", + "type": "STRING", + "links": [ + 25 + ], + "shape": 3, + "slot_index": 4 + } + ], + "properties": { + "Node name for S&R": "Prompts" + }, + "widgets_values": [ + "photo of a __wildcard__", + "Negative Prompt" + ] + }, + { + "id": 12, + "type": "LoadImageExtended", + "pos": [ + 44, + 739 + ], + "size": [ + 315, + 414.0000305175781 + ], + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [ + { + "name": "vae", + "type": "VAE", + "link": null + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 17 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + }, + { + "name": "LATENT", + "type": "LATENT", + "links": null, + "shape": 3 + }, + { + "name": "filename", + "type": "STRING", + "links": [], + "shape": 3, + "slot_index": 3 + }, + { + "name": "image Info", + "type": "STRING", + "links": [ + 22 + ], + "shape": 3, + "slot_index": 4 + }, + { + "name": "width", + "type": "INT", + "links": null, + "shape": 3 + }, + { + "name": "height", + "type": "INT", + "links": null, + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "LoadImageExtended" + }, + "widgets_values": [ + "1701907829_001.png", + "image" + ] + }, + { + "id": 14, + "type": "ImageSimpleResize", + "pos": [ + 631, + 753 + ], + "size": [ + 315, + 126 + ], + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 17 + }, + { + "name": "vae", + "type": "VAE", + "link": null + }, + { + "name": "size_override", + "type": "INT", + "link": null, + "widget": { + "name": "size_override" + } + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 18 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "LATENT", + "type": "LATENT", + "links": null, + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "ImageSimpleResize" + }, + "widgets_values": [ + 1200, + "width", + 0 + ] + }, + { + "id": 13, + "type": "Textbox", + "pos": [ + 424, + 931 + ], + "size": [ + 750.536597290039, + 360.5073205566405 + ], + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + { + "name": "passthrough", + "type": "STRING", + "link": 22, + "widget": { + "name": "passthrough" + } + } + ], + "outputs": [ + { + "name": "text", + "type": "STRING", + "links": null, + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "Textbox" + }, + "widgets_values": [ + "{\n \"prompt\": {\n \"1\": {\n \"inputs\": {\n \"Checkpoint\": \"revCounter_lcm.safetensors\",\n \"Vae\": \"Included\",\n \"stop_at_clip_layer\": -1,\n \"width\": 512,\n \"height\": 512,\n \"batch_size\": 1\n },\n \"class_type\": \"Loader\"\n },\n \"2\": {\n \"inputs\": {\n \"sampler\": \"LCM Lora - lcm\",\n \"mode\": \"txt2img\",\n \"model\": [\n \"1\",\n 0\n ],\n \"positive\": [\n \"8\",\n 0\n ],\n \"negative\": [\n \"3\",\n 1\n ],\n \"latents\": [\n \"1\",\n 3\n ]\n },\n \"class_type\": \"SimpleSampler\"\n },\n \"3\": {\n \"inputs\": {\n \"Positive\": \"photo of a __wildcard__\",\n \"Negative\": \"Negative Prompt\",\n \"clip\": [\n \"1\",\n 2\n ]\n },\n \"class_type\": \"Prompts\"\n },\n \"4\": {\n \"inputs\": {\n \"filename_type\": \"Timestamp\",\n \"fixed_filename\": \"output\",\n \"latents\": [\n \"2\",\n 0\n ],\n \"vae\": [\n \"1\",\n 1\n ]\n },\n \"class_type\": \"SaveImages\"\n },\n \"5\": {\n \"inputs\": {\n \"text\": \"Chibi-Nodes\",\n \"font\": \"Ubuntu-Bold.ttf\",\n \"font_size\": 200,\n \"font_colour\": \"white (mask)\",\n \"invert_mask\": true,\n \"position_x\": 10,\n \"position_y\": 259,\n \"width\": 512,\n \"height\": 512,\n \"image\": [\n \"14\",\n 0\n ]\n },\n \"class_type\": \"ImageAddText\"\n },\n \"6\": {\n \"inputs\": {\n \"filename_type\": \"Timestamp\",\n \"fixed_filename\": \"output\",\n \"images\": [\n \"5\",\n 0\n ]\n },\n \"class_type\": \"SaveImages\"\n },\n \"7\": {\n \"inputs\": {\n \"text\": [\n \"3\",\n 4\n ],\n \"embedding\": \"EasyNegative.pt\",\n \"weight\": 1\n },\n \"class_type\": \"LoadEmbedding\"\n },\n \"8\": {\n \"inputs\": {\n \"textfile\": \"animals.txt\",\n \"keyword\": \"__wildcard__\",\n \"entries_returned\": 1,\n \"text\": [\n \"3\",\n 3\n ],\n \"clip\": [\n \"3\",\n 2\n ]\n },\n \"class_type\": \"Wildcards\"\n },\n \"9\": {\n \"inputs\": {\n \"text\": [\n \"7\",\n 0\n ],\n \"clip\": [\n \"3\",\n 2\n ]\n },\n \"class_type\": \"ConditionText\"\n },\n \"10\": {\n \"inputs\": {\n \"text\": \"photo of a Octopus\",\n \"passthrough\": [\n \"8\",\n 1\n ]\n },\n \"class_type\": \"Textbox\"\n },\n \"12\": {\n \"inputs\": {\n \"image\": \"1701858475_001.png\",\n \"upload\": \"image\"\n },\n \"class_type\": \"LoadImageExtended\",\n \"is_changed\": [\n \"73542fc8113da6a57e0cf2457d2a6fff3d5d740db5a811a795af85450346a0d9\"\n ]\n },\n \"13\": {\n \"inputs\": {\n \"text\": \"\\n \\\"prompt\\\": \\n \\\"2\\\": \\n \\\"inputs\\\": \\n \\\"text\\\": \\\"['EasyNegative, (simple background), 2d']\\\"\\n ,\\n \\\"class_type\\\": \\\"Textbox\\\"\\n ,\\n \\\"3\\\": \\n \\\"inputs\\\": \\n \\\"text\\\": \\\"['1girl, (rainbow multicolored diamond hair), intricate black red rose gown with patterns, chubby, muscles, 3d realistic 4k ultra detailed, happy,smiling, victorian']\\\"\\n ,\\n \\\"class_type\\\": \\\"Textbox\\\"\\n ,\\n \\\"4\\\": \\n \\\"inputs\\\": \\n \\\"text\\\": \\\"png\\\"\\n ,\\n \\\"class_type\\\": \\\"Textbox\\\"\\n ,\\n \\\"5\\\": \\n \\\"inputs\\\": \\n \\\"text\\\": \\\"1701853548_001\\\"\\n ,\\n \\\"class_type\\\": \\\"Textbox\\\"\\n ,\\n \\\"7\\\": \\n \\\"inputs\\\": \\n \\\"sampler\\\": \\\"LCM Lora - lcm\\\",\\n \\\"mode\\\": \\\"txt2img\\\",\\n \\\"model\\\": [\\n \\\"9\\\",\\n 0\\n ],\\n \\\"positive\\\": [\\n \\\"10\\\",\\n 0\\n ],\\n \\\"negative\\\": [\\n \\\"10\\\",\\n 1\\n ],\\n \\\"latents\\\": [\\n \\\"9\\\",\\n 3\\n ]\\n ,\\n \\\"class_type\\\": \\\"SimpleSampler\\\"\\n ,\\n \\\"8\\\": \\n \\\"inputs\\\": \\n \\\"image\\\": \\\"00005-2786280302.0.png\\\",\\n \\\"upload\\\": \\\"image\\\"\\n ,\\n \\\"class_type\\\": \\\"LoadImageExtended\\\",\\n \\\"is_changed\\\": [\\n \\\"9140d5405c3621b88ad63bb21d4d52449b4654cd6a366d88e9657917938947b5\\\"\\n ]\\n ,\\n \\\"9\\\": \\n \\\"inputs\\\": \\n \\\"Checkpoint\\\": \\\"revCounter_lcm.safetensors\\\",\\n \\\"Vae\\\": \\\"blessed2.vae.pt\\\",\\n \\\"stop_at_clip_layer\\\": -1,\\n \\\"width\\\": 512,\\n \\\"height\\\": 512,\\n \\\"batch_size\\\": 1\\n ,\\n \\\"class_type\\\": \\\"Loader\\\"\\n ,\\n \\\"10\\\": \\n \\\"inputs\\\": \\n \\\"Positive\\\": \\\"Positive Prompt Positive1\\\",\\n \\\"Negative\\\": \\\"Negative Prompt Negative1\\\",\\n \\\"clip\\\": [\\n \\\"9\\\",\\n 2\\n ]\\n ,\\n \\\"class_type\\\": \\\"Prompts\\\"\\n ,\\n \\\"11\\\": \\n \\\"inputs\\\": \\n \\\"sampler\\\": \\\"LCM Lora - lcm\\\",\\n \\\"mode\\\": \\\"img2img\\\",\\n \\\"model\\\": [\\n \\\"9\\\",\\n 0\\n ],\\n \\\"positive\\\": [\\n \\\"12\\\",\\n 0\\n ],\\n \\\"negative\\\": [\\n \\\"13\\\",\\n 0\\n ],\\n \\\"latents\\\": [\\n \\\"7\\\",\\n 0\\n ]\\n ,\\n \\\"class_type\\\": \\\"SimpleSampler\\\"\\n ,\\n \\\"12\\\": \\n \\\"inputs\\\": \\n \\\"text\\\": \\\"pos2 and stuff goes pos2\\\",\\n \\\"clip\\\": [\\n \\\"9\\\",\\n 2\\n ]\\n ,\\n \\\"class_type\\\": \\\"CLIPTextEncode\\\"\\n ,\\n \\\"13\\\": \\n \\\"inputs\\\": \\n \\\"text\\\": \\\"neg2 and stuff goes neg2\\\",\\n \\\"clip\\\": [\\n \\\"9\\\",\\n 2\\n ]\\n ,\\n \\\"class_type\\\": \\\"CLIPTextEncode\\\"\\n ,\\n \\\"14\\\": \\n \\\"inputs\\\": \\n \\\"filename_type\\\": \\\"Timestamp\\\",\\n \\\"fixed_filename\\\": \\\"output\\\",\\n \\\"latents\\\": [\\n \\\"11\\\",\\n 0\\n ],\\n \\\"vae\\\": [\\n \\\"9\\\",\\n 1\\n ]\\n ,\\n \\\"class_type\\\": \\\"SaveImages\\\"\\n \\n \\n\",\n \"passthrough\": [\n \"12\",\n 4\n ]\n },\n \"class_type\": \"Textbox\"\n },\n \"14\": {\n \"inputs\": {\n \"size\": 1200,\n \"edge\": \"width\",\n \"image\": [\n \"12\",\n 0\n ]\n },\n \"class_type\": \"ImageSimpleResize\"\n }\n }\n}", + "" + ] + }, + { + "id": 8, + "type": "Wildcards", + "pos": [ + 839, + 546 + ], + "size": [ + 315, + 150 + ], + "flags": {}, + "order": 7, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 10 + }, + { + "name": "text", + "type": "STRING", + "link": 12, + "widget": { + "name": "text" + } + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 29 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "text", + "type": "STRING", + "links": [ + 13 + ], + "shape": 3, + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "Wildcards" + }, + "widgets_values": [ + "animals.txt", + "__wildcard__", + 1, + "" + ] + }, + { + "id": 9, + "type": "ConditionText", + "pos": [ + 867, + 238 + ], + "size": [ + 315, + 78 + ], + "flags": {}, + "order": 11, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 27 + }, + { + "name": "text", + "type": "STRING", + "link": 26, + "widget": { + "name": "text" + } + } + ], + "outputs": [ + { + "name": "CLIP", + "type": "CLIP", + "links": null, + "shape": 3 + }, + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 30 + ], + "shape": 3, + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "ConditionText" + }, + "widgets_values": [ + "" + ] + }, + { + "id": 7, + "type": "LoadEmbedding", + "pos": [ + 507, + 364 + ], + "size": [ + 315, + 126 + ], + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [ + { + "name": "text", + "type": "STRING", + "link": 25, + "widget": { + "name": "text" + } + } + ], + "outputs": [ + { + "name": "text", + "type": "STRING", + "links": [ + 26 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "Preview Image", + "type": "IMAGE", + "links": null, + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "LoadEmbedding" + }, + "widgets_values": [ + "", + "EasyNegative.pt", + 1 + ] + }, + { + "id": 17, + "type": "SeedGenerator", + "pos": [ + 427, + 185 + ], + "size": { + "0": 315, + "1": 102 + }, + "flags": {}, + "order": 1, + "mode": 0, + "outputs": [ + { + "name": "seed", + "type": "INT", + "links": [ + 31 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "text", + "type": "STRING", + "links": null, + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "SeedGenerator" + }, + "widgets_values": [ + "Fixed", + 8008135 + ] + }, + { + "id": 1, + "type": "Loader", + "pos": [ + 7, + 49 + ], + "size": { + "0": 315, + "1": 238 + }, + "flags": {}, + "order": 2, + "mode": 0, + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 1 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "VAE", + "type": "VAE", + "links": [ + 7 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 11 + ], + "shape": 3, + "slot_index": 2 + }, + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 19 + ], + "shape": 3, + "slot_index": 3 + } + ], + "properties": { + "Node name for S&R": "Loader" + }, + "widgets_values": [ + "revCounter_lcm.safetensors", + "blessed2.vae.pt", + -1, + 512, + 512, + 1 + ] + } + ], + "links": [ + [ + 1, + 1, + 0, + 2, + 0, + "MODEL" + ], + [ + 6, + 2, + 0, + 4, + 1, + "LATENT" + ], + [ + 7, + 1, + 1, + 4, + 2, + "VAE" + ], + [ + 9, + 5, + 0, + 6, + 0, + "IMAGE" + ], + [ + 10, + 3, + 2, + 8, + 0, + "CLIP" + ], + [ + 11, + 1, + 2, + 3, + 0, + "CLIP" + ], + [ + 12, + 3, + 3, + 8, + 1, + "STRING" + ], + [ + 13, + 8, + 1, + 10, + 0, + "STRING" + ], + [ + 17, + 12, + 0, + 14, + 0, + "IMAGE" + ], + [ + 18, + 14, + 0, + 5, + 0, + "IMAGE" + ], + [ + 19, + 1, + 3, + 2, + 3, + "LATENT" + ], + [ + 22, + 12, + 4, + 13, + 0, + "STRING" + ], + [ + 25, + 3, + 4, + 7, + 0, + "STRING" + ], + [ + 26, + 7, + 0, + 9, + 1, + "STRING" + ], + [ + 27, + 3, + 2, + 9, + 0, + "CLIP" + ], + [ + 29, + 8, + 0, + 2, + 1, + "CONDITIONING" + ], + [ + 30, + 9, + 1, + 2, + 2, + "CONDITIONING" + ], + [ + 31, + 17, + 0, + 2, + 4, + "INT" + ] + ], + "groups": [], + "config": {}, + "extra": {}, + "version": 0.4 +} \ No newline at end of file diff --git a/extras/fonts/UFL.txt b/extras/fonts/UFL.txt new file mode 100644 index 0000000..6e722c8 --- /dev/null +++ b/extras/fonts/UFL.txt @@ -0,0 +1,96 @@ +------------------------------- +UBUNTU FONT LICENCE Version 1.0 +------------------------------- + +PREAMBLE +This licence allows the licensed fonts to be used, studied, modified and +redistributed freely. The fonts, including any derivative works, can be +bundled, embedded, and redistributed provided the terms of this licence +are met. The fonts and derivatives, however, cannot be released under +any other licence. The requirement for fonts to remain under this +licence does not require any document created using the fonts or their +derivatives to be published under this licence, as long as the primary +purpose of the document is not to be a vehicle for the distribution of +the fonts. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this licence and clearly marked as such. This may +include source files, build scripts and documentation. + +"Original Version" refers to the collection of Font Software components +as received under this licence. + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to +a new environment. + +"Copyright Holder(s)" refers to all individuals and companies who have a +copyright ownership of the Font Software. + +"Substantially Changed" refers to Modified Versions which can be easily +identified as dissimilar to the Font Software by users of the Font +Software comparing the Original Version with the Modified Version. + +To "Propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification and with or without charging +a redistribution fee), making available to the public, and in some +countries other activities as well. + +PERMISSION & CONDITIONS +This licence does not grant any rights under trademark law and all such +rights are reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Font Software, to propagate the Font Software, subject to +the below conditions: + +1) Each copy of the Font Software must contain the above copyright +notice and this licence. These can be included either as stand-alone +text files, human-readable headers or in the appropriate machine- +readable metadata fields within text or binary files as long as those +fields can be easily viewed by the user. + +2) The font name complies with the following: +(a) The Original Version must retain its name, unmodified. +(b) Modified Versions which are Substantially Changed must be renamed to +avoid use of the name of the Original Version or similar names entirely. +(c) Modified Versions which are not Substantially Changed must be +renamed to both (i) retain the name of the Original Version and (ii) add +additional naming elements to distinguish the Modified Version from the +Original Version. The name of such Modified Versions must be the name of +the Original Version, with "derivative X" where X represents the name of +the new work, appended to that name. + +3) The name(s) of the Copyright Holder(s) and any contributor to the +Font Software shall not be used to promote, endorse or advertise any +Modified Version, except (i) as required by this licence, (ii) to +acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with +their explicit written permission. + +4) The Font Software, modified or unmodified, in part or in whole, must +be distributed entirely under this licence, and must not be distributed +under any other licence. The requirement for fonts to remain under this +licence does not affect any document created using the Font Software, +except any version of the Font Software extracted from a document +created using the Font Software may only be distributed under this +licence. + +TERMINATION +This licence becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER +DEALINGS IN THE FONT SOFTWARE. diff --git a/extras/fonts/Ubuntu-Bold.ttf b/extras/fonts/Ubuntu-Bold.ttf new file mode 100644 index 0000000..c2293d5 Binary files /dev/null and b/extras/fonts/Ubuntu-Bold.ttf differ diff --git a/extras/fonts/Ubuntu-BoldItalic.ttf b/extras/fonts/Ubuntu-BoldItalic.ttf new file mode 100644 index 0000000..ce6e784 Binary files /dev/null and b/extras/fonts/Ubuntu-BoldItalic.ttf differ diff --git a/extras/fonts/Ubuntu-Italic.ttf b/extras/fonts/Ubuntu-Italic.ttf new file mode 100644 index 0000000..a599244 Binary files /dev/null and b/extras/fonts/Ubuntu-Italic.ttf differ diff --git a/extras/fonts/Ubuntu-Light.ttf b/extras/fonts/Ubuntu-Light.ttf new file mode 100644 index 0000000..b310d15 Binary files /dev/null and b/extras/fonts/Ubuntu-Light.ttf differ diff --git a/extras/fonts/Ubuntu-LightItalic.ttf b/extras/fonts/Ubuntu-LightItalic.ttf new file mode 100644 index 0000000..ad0741b Binary files /dev/null and b/extras/fonts/Ubuntu-LightItalic.ttf differ diff --git a/extras/fonts/Ubuntu-Medium.ttf b/extras/fonts/Ubuntu-Medium.ttf new file mode 100644 index 0000000..7340a40 Binary files /dev/null and b/extras/fonts/Ubuntu-Medium.ttf differ diff --git a/extras/fonts/Ubuntu-MediumItalic.ttf b/extras/fonts/Ubuntu-MediumItalic.ttf new file mode 100644 index 0000000..36ac1ae Binary files /dev/null and b/extras/fonts/Ubuntu-MediumItalic.ttf differ diff --git a/extras/fonts/Ubuntu-Regular.ttf b/extras/fonts/Ubuntu-Regular.ttf new file mode 100644 index 0000000..f98a2da Binary files /dev/null and b/extras/fonts/Ubuntu-Regular.ttf differ diff --git a/extras/fonts/UbuntuCondensed-Regular.ttf b/extras/fonts/UbuntuCondensed-Regular.ttf new file mode 100644 index 0000000..add1c24 Binary files /dev/null and b/extras/fonts/UbuntuCondensed-Regular.ttf differ diff --git a/extras/fonts/UbuntuMono-Bold.ttf b/extras/fonts/UbuntuMono-Bold.ttf new file mode 100644 index 0000000..01ad81b Binary files /dev/null and b/extras/fonts/UbuntuMono-Bold.ttf differ diff --git a/extras/fonts/UbuntuMono-BoldItalic.ttf b/extras/fonts/UbuntuMono-BoldItalic.ttf new file mode 100644 index 0000000..731884e Binary files /dev/null and b/extras/fonts/UbuntuMono-BoldItalic.ttf differ diff --git a/extras/fonts/UbuntuMono-Italic.ttf b/extras/fonts/UbuntuMono-Italic.ttf new file mode 100644 index 0000000..b89338d Binary files /dev/null and b/extras/fonts/UbuntuMono-Italic.ttf differ diff --git a/extras/fonts/UbuntuMono-Regular.ttf b/extras/fonts/UbuntuMono-Regular.ttf new file mode 100644 index 0000000..4977028 Binary files /dev/null and b/extras/fonts/UbuntuMono-Regular.ttf differ diff --git a/screenshots/screenshot5.png b/screenshots/screenshot5.png new file mode 100644 index 0000000..e7a0a29 Binary files /dev/null and b/screenshots/screenshot5.png differ