diff --git a/__init__.py b/__init__.py index 2498b35..4cf2d6f 100644 --- a/__init__.py +++ b/__init__.py @@ -6,6 +6,7 @@ from .src.lora_tester import LoraTestNode from .src.character_select import CharacterSelectNode from .src.prompt_combine import CombineStringNode from .src.simple_sampler_xl import SimpleSamplerXlNode +from .src.simple_sampler import SimpleSamplerNode from .src.lora_stacker import LoraStackerNode from .src.load_lora_xl import LoraLoraXLNode from .src.load_lora_15 import LoraLora15Node @@ -24,6 +25,7 @@ NODE_CLASS_MAPPINGS = { "Character Selector": CharacterSelectNode, "Prompt Combine": CombineStringNode, "Simple Sampler XL": SimpleSamplerXlNode, + "Simple Sampler": SimpleSamplerNode, "Lora Stacker": LoraStackerNode, "Load Lora XL": LoraLoraXLNode, "Load Lora 1.5": LoraLora15Node, @@ -43,6 +45,7 @@ NODE_DISPLAY_NAME_MAPPINGS = { "Character Selector": "Character Selector", "Prompt Combine": "Prompt Combine", "Simple Sampler XL": "Simple Sampler XL", + "Simple Sampler": "Simple Sampler", "Lora Stacker": "Lora Stacker", "Load Lora XL": "Load Lora XL", "Load Lora 1.5": "Load Lora 1.5", diff --git a/characters.json b/characters.json index c8dfa76..906a068 100644 --- a/characters.json +++ b/characters.json @@ -187,7 +187,7 @@ { "name": "Hanekawa Tsubasa", "associated_string": "hanekawa tsubasa, monogatari \\(series\\), bakemonogatari", - "prompt": "braid, braided ponytail, brown hair, large breasts, long hair, red eyes, single braid" + "prompt": "braid, braided ponytail, brown hair, large breasts, long hair, purple eyes, single braid" }, { "name": "Iori Utahime", @@ -198,5 +198,10 @@ "name": "Tsunade", "associated_string": "tsunade \\(naruto\\), naruto \\(series\\), naruto", "prompt": "blonde hair, forehead mark, large breasts, long hair, parted bangs, brown eyes, mature female, hair intakes" + }, + { + "name": "Asuna (blue archive)", + "associated_string": "asuna \\(blue archive\\), asuna \\(bunny\\) (blue archive), blue archive", + "prompt": "blue eyes, blue ribbon, hair ribbon, large breasts, light brown hair, very long hair, ribbon" } ] diff --git a/prompts.json b/prompts.json index 01c08a5..772e878 100644 --- a/prompts.json +++ b/prompts.json @@ -1,6 +1,6 @@ [ { - "name": "Positive_XL", + "name": "Positive", "prompt": "(masterpiece, exceptional, best aesthetic, best quality, masterpiece, extremely detailed:1.1), " }, { @@ -10,5 +10,9 @@ { "name": "Negative_safe_XL", "prompt": "(lowres, bad anatomy, bad hands, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts:1.15), (rating:explicit, rating:questionable, rating:sensitive, signature, watermark, username, gumroad username, patreon username, artist name:1.1), multiple views, text, error, blurry" + }, + { + "name": "Negative_1.5", + "prompt": "embedding:aid210, embedding:easynegative, embedding:verybadimagenegative_v1.3, speech bubble, disfigured, extra limbs, low quality, blurred, multiple views, futanari, loli, chibi, gumroad username, patreon username, (extra fingers, watermark, deformed hands, polydactyl, asymmetrical breasts, monochrome, greyscale:1.1)" } ] diff --git a/src/load_lora_character.py b/src/load_lora_character.py index 28bcd8d..947b700 100644 --- a/src/load_lora_character.py +++ b/src/load_lora_character.py @@ -78,10 +78,8 @@ class LoadLoraCharacterNode: # Extract just the filenames for comparison lora_filenames = [path.split('\\')[-1] for path in lora_paths] - # Assume character_name is a string containing the character's name. - character_name_parts = character_name.lower().split() - - # Assume lora_filenames is a list of filenames to search through. + character_name_lower = character_name.lower() + character_name_parts = character_name_lower.split() closest_match = None lowest_distance = float('inf') @@ -89,10 +87,15 @@ class LoadLoraCharacterNode: # Convert filename to lowercase for case-insensitive comparison. filename_lower = filename.lower() - # Check if any part of the character name is in the filename. if any(part in filename_lower for part in character_name_parts): - # Calculate the levenshtein distance for a closer match. - total_distance = sum(self.levenshtein_distance(part, filename_lower) for part in character_name_parts) + # Calculate the Levenshtein distance for the full character name as one of the metrics. + full_name_distance = self.levenshtein_distance(character_name_lower, filename_lower) + + # Calculate the sum of distances for each part of the character name. + parts_distance = sum(self.levenshtein_distance(part, filename_lower) for part in character_name_parts) + + # Use the minimum of full name distance and parts distance as the total distance. + total_distance = min(full_name_distance, parts_distance) print_sn0w("Distance: " + str(total_distance) + " Lora: " + filename) if total_distance < lowest_distance: diff --git a/src/simple_sampler.py b/src/simple_sampler.py new file mode 100644 index 0000000..d0b29d1 --- /dev/null +++ b/src/simple_sampler.py @@ -0,0 +1,67 @@ +from nodes import KSamplerAdvanced, VAEDecode, VAEEncode, EmptyLatentImage, ImageScaleBy, CLIPTextEncode +from comfy_extras.nodes_upscale_model import ImageUpscaleWithModel +import comfy.samplers + +class SimpleSamplerNode: + @classmethod + def INPUT_TYPES(s): + return {"required": + {"model": ("MODEL",), + "clip": ("CLIP", ), + "vae": ("VAE", ), + "add_noise": (["enable", "disable"], ), + "noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + "steps": ("INT", {"default": 20, "min": 1, "max": 10000}), + "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}), + "width": ("INT", {"default": 0, "min": 0}), + "height": ("INT", {"default": 0, "min": 0}), + "sampler_name": (comfy.samplers.KSampler.SAMPLERS, ), + "scheduler": (comfy.samplers.KSampler.SCHEDULERS, ), + "positive": ("STRING", {"default": ""}), + "negative": ("STRING", {"default": ""}), + "start_at_step": ("INT", {"default": 0, "min": 0, "max": 10000}), + "end_at_step": ("INT", {"default": 10000, "min": 0, "max": 10000}), + "return_with_leftover_noise": (["disable", "enable"], ), + "hires": ("BOOLEAN", {"default": False},), + "upscale_model": ("UPSCALE_MODEL",), + "hires_steps": ("INT", {"default": 5, "min": 1, "max": 20}), + } + } + + RETURN_TYPES = ("IMAGE", "CONDITIONING", "CONDITIONING") + RETURN_NAMES = ("IMAGE", "POSITIVE", "NEGATIVE") + FUNCTION = "sample" + + CATEGORY = "sampling" + + def sample(self, model, clip, vae, add_noise, noise_seed, steps, cfg, width, height, sampler_name, scheduler, positive, negative, start_at_step, end_at_step, return_with_leftover_noise, hires, upscale_model, hires_steps): + k_sampler_node = KSamplerAdvanced() + vae_decode = VAEDecode() + vae_encode = VAEEncode() + text_encode = CLIPTextEncode() + upscaler = ImageUpscaleWithModel() + scale_image = ImageScaleBy() + + latent_image = EmptyLatentImage().generate(width, height)[0] + + positive_prompt = text_encode.encode(clip, positive)[0] + negative_prompt = text_encode.encode(clip, negative)[0] + + # Sampling + samples = k_sampler_node.sample(model, add_noise, noise_seed, steps, cfg, sampler_name, scheduler, positive_prompt, negative_prompt, latent_image, start_at_step, end_at_step, return_with_leftover_noise)[0] + + if (hires): + # Decode the samples + image = vae_decode.decode(vae, samples)[0] + + total_hires_steps = 25 - hires_steps + + upscaled_image = upscaler.upscale(upscale_model, image)[0] + upscaled_image = scale_image.upscale(upscaled_image, "nearest-exact", 0.5)[0] + upscaled_latent = vae_encode.encode(vae, upscaled_image)[0] + upscaled_samples = k_sampler_node.sample(model, add_noise, noise_seed, 25, cfg, sampler_name, scheduler, positive_prompt, negative_prompt, upscaled_latent, total_hires_steps, 1000, return_with_leftover_noise)[0] + image = vae_decode.decode(vae, upscaled_samples) + else: + image = vae_decode.decode(vae, samples) + + return (image[0], positive_prompt, negative_prompt) \ No newline at end of file