# !/usr/bin/env python # -*- coding: UTF-8 -*- import numpy as np import torch import os import random import sys import re from PIL import ImageFont import yaml from PIL import Image from huggingface_hub import hf_hub_download from diffusers import (StableDiffusionXLPipeline, DiffusionPipeline, DDIMScheduler, ControlNetModel, KDPM2AncestralDiscreteScheduler, LMSDiscreteScheduler, AutoPipelineForInpainting, DPMSolverMultistepScheduler, DPMSolverSinglestepScheduler, EulerDiscreteScheduler, HeunDiscreteScheduler, UNet2DConditionModel, AutoPipelineForText2Image, StableDiffusionXLControlNetImg2ImgPipeline, KDPM2DiscreteScheduler, EulerAncestralDiscreteScheduler, UniPCMultistepScheduler, AutoencoderKL, StableDiffusionXLControlNetPipeline, DDPMScheduler, LCMScheduler) from transformers import CLIPVisionModelWithProjection from transformers import CLIPImageProcessor from .msdiffusion.models.projection import Resampler from .msdiffusion.models.model import MSAdapter from .msdiffusion.utils import get_phrase_idx, get_eot_idx from diffusers.schedulers.scheduling_ddim import DDIMScheduler import folder_paths from comfy.utils import common_upscale from nodes import LoadImage from .utils.utils import get_comic from .utils.style_template import styles STYLE_NAMES = list(styles.keys()) import diffusers LoadImage=LoadImage() dif_version = str(diffusers.__version__) dif_version_int = int(dif_version.split(".")[1]) device = ( "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" ) MAX_SEED = np.iinfo(np.int32).max dir_path = os.path.dirname(os.path.abspath(__file__)) path_dir = os.path.dirname(dir_path) file_path = os.path.dirname(path_dir) fonts_path = os.path.join(dir_path, "fonts") fonts_lists = os.listdir(fonts_path) control_paths = [] for search_path in folder_paths.get_folder_paths("diffusers"): if os.path.exists(search_path): for root, subdir, files in os.walk(search_path, followlinks=True): if "config.json" in files: control_paths.append(os.path.relpath(root, start=search_path)) control_paths = ([z for z in control_paths if "controlnet-canny-sdxl-1.0" in z] + [p for p in control_paths if "MistoLine" in p] + [o for o in control_paths if "lcm-sdxl" in o] + [Q for Q in control_paths if "controlnet-openpose-sdxl-1.0" in Q] + [Z for Z in control_paths if "controlnet-scribble-sdxl-1.0" in Z] + [a for a in control_paths if "controlnet-depth-sdxl-1.0" in a] +[b for b in control_paths if "controlnet-tile-sdxl-1.0" in b] +[c for c in control_paths if "controlnet-zoe-depth-sdxl-1.0" in c] +[d for d in control_paths if "sdxl-controlnet-seg " in d]) if control_paths: control_paths = ["none"] + [x for x in control_paths if x] else: control_paths = ["none",] diff_paths = [] for search_path in folder_paths.get_folder_paths("diffusers"): if os.path.exists(search_path): for root, subdir, files in os.walk(search_path, followlinks=True): if "model_index.json" in files: diff_paths.append(os.path.relpath(root, start=search_path)) if diff_paths: diff_paths = ["none"] + [x for x in diff_paths if x] else: diff_paths = ["none",] clip_paths = [] for search_path in folder_paths.get_folder_paths("clip_vision"): if os.path.exists(search_path): for root, subdir, files in os.walk(search_path, followlinks=True): if "config.json" in files: clip_paths.append(os.path.relpath(root, start=search_path)) if diff_paths: clip_paths = ["none"] + [x for x in clip_paths if x] else: clip_paths = ["none",] def pil2tensor(img): img_convert_to_numpy = np.array(img) # (32, 32, 3) tensor = torch.tensor(img_convert_to_numpy.transpose(2, 0, 1) / 255) # torch.Size([3, 32, 32]) return tensor def apply_style_positive(style_name: str, positive: str): p, n = styles.get(style_name, styles[style_name]) #print(p, "test0", n) return p.replace("{prompt}", positive) def apply_style(style_name: str, positives: list, negative: str = ""): p, n = styles.get(style_name, styles[style_name]) #print(p,"test1",n) return [ p.replace("{prompt}", positive) for positive in positives ], n + " " + negative def get_instance_path(path): instance_path = os.path.normpath(path) if sys.platform == 'win32': instance_path = instance_path.replace('\\', "/") return instance_path loras_path = get_instance_path(os.path.join(dir_path,"config","lora.yaml")) def get_lora_dict(): # 打开并读取YAML文件 with open(loras_path, 'r', encoding="UTF-8") as stream: try: # 解析YAML文件内容 data = yaml.safe_load(stream) # 此时 'data' 是一个Python字典,里面包含了YAML文件的所有数据 # print(data) return data except yaml.YAMLError as exc: # 如果在解析过程中发生了错误,打印异常信息 print(exc) datas = get_lora_dict() lora_lightning_list = datas["lightning_xl_lora"] scheduler_list = [ "Euler", "Euler a", "DDIM", "DDPM", "DPM++ 2M", "DPM++ 2M Karras", "DPM++ 2M SDE", "DPM++ 2M SDE Karras", "DPM++ SDE", "DPM++ SDE Karras", "DPM2", "DPM2 Karras", "DPM2 a", "DPM2 a Karras", "Heun", "LCM", "LMS", "LMS Karras", "UniPC" ] def phi2narry(img): img = torch.from_numpy(np.array(img).astype(np.float32) / 255.0).unsqueeze(0) return img def add_pil(list, list_add, num): new_list = list[:num] + list_add + list[num:] return new_list def tensor_to_image(tensor): # tensor = tensor.cpu() image_np = tensor.squeeze().mul(255).clamp(0, 255).byte().numpy() image = Image.fromarray(image_np, mode='RGB') return image # get fonts list def has_parentheses(s): return bool(re.search(r'\(.*?\)', s)) def contains_brackets(s): return '[' in s or ']' in s def narry_list(list_in): for i in range(len(list_in)): value = list_in[i] modified_value = phi2narry(value) list_in[i] = modified_value return list_in def remove_punctuation_from_strings(lst): pattern = r"[\W]+$" # 匹配字符串末尾的所有非单词字符 return [re.sub(pattern, '', s) for s in lst] def format_punctuation_from_strings(lst): pattern = r"[\W]+$" # 匹配字符串末尾的所有非单词字符 return [re.sub(pattern, ';', s) for s in lst] def phi_list(list_in): for i in range(len(list_in)): value = list_in[i] list_in[i] = value return list_in def narry_list_pil(list_in): for i in range(len(list_in)): value = list_in[i] modified_value = tensor_to_image(value) list_in[i] = modified_value return list_in def get_local_path(file_path, model_path,file): path = os.path.join(file_path, "models", file, model_path) model_path = os.path.normpath(path) if sys.platform.startswith('win32'): model_path = model_path.replace('\\', "/") return model_path def setup_seed(seed): torch.manual_seed(seed) if device == "cuda": torch.cuda.manual_seed_all(seed) np.random.seed(seed) random.seed(seed) torch.backends.cudnn.deterministic = True def get_image_path_list(folder_name): image_basename_list = os.listdir(folder_name) image_path_list = sorted( [os.path.join(folder_name, basename) for basename in image_basename_list] ) return image_path_list def get_scheduler(name): scheduler = False if name == "Euler": scheduler = EulerDiscreteScheduler() elif name == "Euler a": scheduler = EulerAncestralDiscreteScheduler() elif name == "DDIM": scheduler = DDIMScheduler() elif name == "DDPM": scheduler = DDPMScheduler() elif name == "DPM++ 2M": scheduler = DPMSolverMultistepScheduler() elif name == "DPM++ 2M Karras": scheduler = DPMSolverMultistepScheduler(use_karras_sigmas=True) elif name == "DPM++ 2M SDE": scheduler = DPMSolverMultistepScheduler(algorithm_type="sde-dpmsolver++") elif name == "DPM++ 2M SDE Karras": scheduler = DPMSolverMultistepScheduler(use_karras_sigmas=True, algorithm_type="sde-dpmsolver++") elif name == "DPM++ SDE": scheduler = DPMSolverSinglestepScheduler() elif name == "DPM++ SDE Karras": scheduler = DPMSolverSinglestepScheduler(use_karras_sigmas=True) elif name == "DPM2": scheduler = KDPM2DiscreteScheduler() elif name == "DPM2 Karras": scheduler = KDPM2DiscreteScheduler(use_karras_sigmas=True) elif name == "DPM2 a": scheduler = KDPM2AncestralDiscreteScheduler() elif name == "DPM2 a Karras": scheduler = KDPM2AncestralDiscreteScheduler(use_karras_sigmas=True) elif name == "Heun": scheduler = HeunDiscreteScheduler() elif name == "LCM": scheduler = LCMScheduler() elif name == "LMS": scheduler = LMSDiscreteScheduler() elif name == "LMS Karras": scheduler = LMSDiscreteScheduler(use_karras_sigmas=True) elif name == "UniPC": scheduler = UniPCMultistepScheduler() return scheduler def nomarl_upscale(img, width, height): samples = img.movedim(-1, 1) img = common_upscale(samples, width, height, "nearest-exact", "center") samples = img.movedim(1, -1) img = tensor_to_image(samples) return img def input_size_adaptation_output(img_tensor,base_in, width, height): #basein=512 if width == height: img_pil = nomarl_upscale(img_tensor, base_in, base_in) # 2pil else: if min(1,width/ height)==1: #高 r=height/base_in img_pil = nomarl_upscale(img_tensor, round(width/r), base_in) # 2pil else: #宽 r=width/base_in img_pil = nomarl_upscale(img_tensor, base_in, round(height/r)) # 2pil img_pil.resize((width,height)) return img_pil def instance_path(path, repo,file): if repo == "": if path == "none": repo = "none" else: model_path = get_local_path(file_path, path,file) repo = get_instance_path(model_path) return repo class MSdiffusion_Model_Loader: def __init__(self): pass @classmethod def INPUT_TYPES(cls): return { "required": { "ckpt_name": (["none"]+folder_paths.get_filename_list("checkpoints"),), "diffuser_model": (diff_paths,), "repo_id": ("STRING", {"default": "stabilityai/stable-diffusion-xl-base-1.0"}), "clip_vision_local":(clip_paths,), "clip_vision_repo": ("STRING", {"default": "laion/CLIP-ViT-bigG-14-laion2B-39B-b160k"}), "controlnet_model_path": (control_paths,), "lora": (["none"] + folder_paths.get_filename_list("loras"),), "lora_scale": ("FLOAT", {"default": 0.8, "min": 0.1, "max": 1.0, "step": 0.1}), "trigger_words": ("STRING", {"default": "best quality"}), "scheduler": (scheduler_list,),} } RETURN_TYPES = ("MODEL","MODEL","MODEL","MODEL", "STRING",) RETURN_NAMES = ("pipe","ms_model","image_encoder", "image_processor","info",) FUNCTION = "ms_model_loader" CATEGORY = "MSdiffusion" def ms_model_loader(self, ckpt_name,diffuser_model,repo_id,clip_vision_local, clip_vision_repo, controlnet_model_path,lora, lora_scale, trigger_words,scheduler, ): repo_id = instance_path(diffuser_model, repo_id,"diffusers") clip_vision=instance_path(clip_vision_local, clip_vision_repo,"clip_vision") if clip_vision=="none": raise "need clip_vision" scheduler_choice = get_scheduler(scheduler) if lora != "none": lora_path = folder_paths.get_full_path("loras", lora) lora_path = get_instance_path(lora_path) else: lora_path = "" if "/" in lora: lora = lora.split("/")[-1] if "\\" in lora: lora = lora.split("\\")[-1] # load SDXL pipeline original_config_file = os.path.join(dir_path, 'config', 'sd_xl_base.yaml') original_config_file = get_instance_path(original_config_file) if controlnet_model_path != "none": #启用control controlnet_model_path = get_instance_path(get_local_path(file_path, controlnet_model_path,"diffusers")) controlnet = ControlNetModel.from_pretrained(controlnet_model_path, variant="fp16", use_safetensors=True, torch_dtype=torch.float16).to(device) if repo_id == "none": if ckpt_name == "none": raise "need choice checkpoints or fill repo or choice diffuser model" ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name) repo_id = get_instance_path(ckpt_path) if dif_version_int >= 28: pipe = StableDiffusionXLControlNetPipeline.from_single_file( repo_id, original_config=original_config_file, controlnet=controlnet, torch_dtype=torch.float16, add_watermarker=False, ).to(device) else: pipe = StableDiffusionXLControlNetPipeline.from_single_file( repo_id, original_config_file=original_config_file, controlnet=controlnet, torch_dtype=torch.float16, add_watermarker=False, ).to(device) else: pipe = StableDiffusionXLControlNetPipeline.from_pretrained( repo_id, torch_dtype=torch.float16, add_watermarker=False, controlnet=controlnet, ).to(device) else: # 非controlnet模式 if repo_id == "none": if ckpt_name == "none": raise "need choice checkpoints or fill repo or choice diffuser model" ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name) repo_id = get_instance_path(ckpt_path) if dif_version_int >= 28: pipe = StableDiffusionXLPipeline.from_single_file( repo_id, original_config=original_config_file, torch_dtype=torch.float16, add_watermarker=False, ).to(device) else: pipe = StableDiffusionXLPipeline.from_single_file( repo_id, original_config_file=original_config_file, torch_dtype=torch.float16, add_watermarker=False, ).to(device) else: pipe = StableDiffusionXLPipeline.from_pretrained( repo_id, torch_dtype=torch.float16, add_watermarker=False, ).to(device) if lora != "none": if lora in lora_lightning_list: pipe.load_lora_weights(lora_path) pipe.fuse_lora() else: pipe.load_lora_weights(lora_path, adapter_name=trigger_words) pipe.fuse_lora(adapter_names=[trigger_words, ], lora_scale=lora_scale) pipe.scheduler = scheduler_choice.from_config(pipe.scheduler.config) pipe.enable_freeu(s1=0.6, s2=0.4, b1=1.1, b2=1.2) pipe.enable_vae_slicing() if device != "mps": pipe.enable_model_cpu_offload() ms_dir = os.path.join(dir_path, "weights") photomaker_local_path = os.path.join(ms_dir, "ms_adapter.bin") if not os.path.exists(photomaker_local_path): ms_path = hf_hub_download( repo_id="doge1516/MS-Diffusion", filename="ms_adapter.bin", repo_type="model", local_dir=ms_dir, ) else: ms_path = photomaker_local_path ms_ckpt = get_instance_path(ms_path) image_processor = CLIPImageProcessor() #print(type(image_processor)) image_encoder = CLIPVisionModelWithProjection.from_pretrained(clip_vision).to(device, dtype=torch.float16) # print(type(image_encoder)) image_encoder_projection_dim = image_encoder.config.projection_dim num_tokens = 16 image_encoder_type = "clip" image_proj_type = "resampler" latent_init_mode = "grounding" # latent_init_mode = "random" image_proj_model = Resampler( dim=1280, depth=4, dim_head=64, heads=20, num_queries=num_tokens, embedding_dim=image_encoder.config.hidden_size, output_dim=pipe.unet.config.cross_attention_dim, ff_mult=4, latent_init_mode=latent_init_mode, phrase_embeddings_dim=pipe.text_encoder.config.projection_dim, ).to(device, dtype=torch.float16) ms_model = MSAdapter(pipe.unet, image_proj_model, ckpt_path=ms_ckpt, device=device, num_tokens=num_tokens) ms_model.to(device, dtype=torch.float16) info = str(";".join( [lora, trigger_words,controlnet_model_path,image_encoder_type,image_proj_type])) return (pipe,ms_model,image_encoder,image_processor, info,) class MSdiffusion_Sampler: def __init__(self): pass @classmethod def INPUT_TYPES(cls): return { "required": { "image": ("IMAGE",), "info": ("STRING", {"forceInput": True, "default": ""}), "pipe": ("MODEL",), "ms_model": ("MODEL",), "image_encoder": ("MODEL",), "image_processor": ("MODEL",), "character_prompt": ("STRING", {"multiline": True, "default": "[woman] wearing a white T-shirt, blue loose hair.\n" "[man] wearing a suit,black hair."}), "scene_prompts": ("STRING", {"multiline": True, "default": "[woman] and [man] have breakfast,medium shot;\n[woman] go to company;\n[man] have lunch,medium shot;\n[NC] a living room."}), "split_prompt": ("STRING", {"default": ""}), "negative_prompt": ("STRING", {"multiline": True, "default": "bad anatomy, bad hands, missing fingers, extra fingers, " "three hands, three legs, bad arms, missing legs, " "missing arms, poorly drawn face, bad face, fused face, " "cloned face, three crus, fused feet, fused thigh, " "extra crus, ugly fingers, horn," "amputation, disconnected limbs"}), "img_style": ( ["No_style", "Realistic", "Japanese_Anime", "Digital_Oil_Painting", "Pixar_Disney_Character", "Photographic", "Comic_book", "Line_art", "Black_and_White_Film_Noir", "Isometric_Rooms"],), "seed": ("INT", {"default": 0, "min": 0, "max": MAX_SEED}), "steps": ("INT", {"default": 20, "min": 1, "max": 100}), "cfg": ("FLOAT", {"default": 7, "min": 0.1, "max": 10.0, "step": 0.1, "round": 0.01}), "role_scale": ( "FLOAT", {"default": 0.8, "min": 0.0, "max": 1.0, "step": 0.1, "round": 0.01}), "mask_threshold": ( "FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.1, "round": 0.01}), "start_step": ("INT", {"default": 5, "min": 1, "max": 1024}), "controlnet_scale": ( "FLOAT", {"default": 0.8, "min": 0.0, "max": 1.0, "step": 0.1, "round": 0.01}), "width": ("INT", {"default": 768, "min": 256, "max": 2048, "step": 64, "display": "number"}), "height": ("INT", {"default": 768, "min": 256, "max": 2048, "step": 64, "display": "number"}), "layout_guidance":("BOOLEAN", {"default": False},), "guidance_list": ("STRING", {"multiline": True,"default": "0., 0.25, 0.4, 0.75;0.6, 0.25, 1., 0.75"}), }, "optional": {"control_image": ("IMAGE",),} } RETURN_TYPES = ("IMAGE", "STRING",) RETURN_NAMES = ("image", "prompt_array",) FUNCTION = "ms_sampler" CATEGORY = "MSdiffusion" def main_normal(self,prompts_dual,pipe,phrases,ms_model,input_images,num_samples,steps,seed,negative_prompt,role_scale,image_encoder,cfg,image_processor, boxes,mask_threshold,start_step,image_proj_type,image_encoder_type,drop_grounding_tokens,height,width,img_style): image_ouput = [] for i, prompt in enumerate(prompts_dual): prompt = apply_style_positive(img_style, prompt) #print(prompt) # used to get the attention map, return zero if the phrase is not in the prompt phrase_idxes = [self.get_phrases_idx(pipe.tokenizer, phrases[0], prompt)] eot_idxes = [[get_eot_idx(pipe.tokenizer, prompt)] * len(phrases[0])] # print(phrase_idxes, eot_idxes) images = ms_model.generate(pipe=pipe, pil_images=[input_images], num_samples=num_samples, num_inference_steps=steps, seed=seed, prompt=[prompt], negative_prompt=negative_prompt, scale=role_scale, image_encoder=image_encoder, guidance_scale=cfg, image_processor=image_processor, boxes=boxes, mask_threshold=mask_threshold, start_step=start_step, image_proj_type=image_proj_type, image_encoder_type=image_encoder_type, phrases=phrases, drop_grounding_tokens=drop_grounding_tokens, phrase_idxes=phrase_idxes, eot_idxes=eot_idxes, height=height, width=width) image_ouput += images return image_ouput def main_control(self,prompts_dual,control_img_list,width,height,pipe,phrases,ms_model,input_images,num_samples,steps,seed,negative_prompt,role_scale,image_encoder,cfg, image_processor,boxes,mask_threshold,start_step,image_proj_type,image_encoder_type,drop_grounding_tokens,controlnet_scale,img_style): j = 0 image_ouput=[] for i, prompt in enumerate(prompts_dual): control_image = control_img_list[j] j += 1 prompt=apply_style_positive(img_style,prompt) #print(prompt) # used to get the attention map, return zero if the phrase is not in the prompt phrase_idxes = [self.get_phrases_idx(pipe.tokenizer, phrases[0], prompt)] eot_idxes = [[get_eot_idx(pipe.tokenizer, prompt)] * len(phrases[0])] # print(phrase_idxes, eot_idxes) images = ms_model.generate(pipe=pipe, pil_images=[input_images], num_samples=num_samples, num_inference_steps=steps, seed=seed, prompt=[prompt], negative_prompt=negative_prompt, scale=role_scale, image_encoder=image_encoder, guidance_scale=cfg, image_processor=image_processor, boxes=boxes, mask_threshold=mask_threshold, start_step=start_step, image_proj_type=image_proj_type, image_encoder_type=image_encoder_type, phrases=phrases, drop_grounding_tokens=drop_grounding_tokens, phrase_idxes=phrase_idxes, eot_idxes=eot_idxes, height=height, width=width, image=control_image, ontrolnet_conditioning_scale=controlnet_scale) image_ouput += images return image_ouput def get_phrases_idx(self,tokenizer, phrases, prompt): res = [] phrase_cnt = {} for phrase in phrases: if phrase in phrase_cnt: cur_cnt = phrase_cnt[phrase] phrase_cnt[phrase] += 1 else: cur_cnt = 0 phrase_cnt[phrase] = 1 res.append(get_phrase_idx(tokenizer, phrase, prompt, num=cur_cnt)[0]) return res def add_lora(self,trigger_words,lora,prompts_list): # 添加Lora trigger add_trigger_words = "," + trigger_words + " " + "style" + ";" prompts_list = remove_punctuation_from_strings(prompts_list) if lora not in lora_lightning_list: # 加速lora不需要trigger prompts_list = [item + add_trigger_words for item in prompts_list] return prompts_list def ms_sampler(self,image,info,pipe,ms_model,image_encoder,image_processor, character_prompt, scene_prompts,split_prompt,negative_prompt,img_style,seed, steps, cfg,role_scale, mask_threshold, start_step,controlnet_scale,width,height,layout_guidance,guidance_list,**kwargs): lora, trigger_words,controlnet_model_path,image_encoder_type,image_proj_type = info.split(";") guidance_list=guidance_list.strip().split(";") box_a = guidance_list[0].split(",") box_b = guidance_list[1].split(",") box_a_float=[float(x) for x in box_a] box_b_float = [float(x) for x in box_b] box_add=[box_a_float,box_b_float] #print(box_a_float, box_b_float,box_add) #处理prompt if split_prompt: scene_prompts.replace("\n", "").replace(split_prompt, ";\n").strip() character_prompt.replace("\n", "").replace(split_prompt, ";\n").strip() else: scene_prompts.strip() character_prompt.strip() if "\n" not in scene_prompts: scene_prompts.replace(";", ";\n").strip() if "\n" in character_prompt: if character_prompt.count("\n") > 1: character_prompt.replace("\n", "").replace("[", "\n[").strip() if character_prompt.count("\n") > 1: character_prompt.replace("\n", "").replace("[", "\n[", 2).strip() # 多行角色在这里强行转为双角色 # 从角色列表获取角色方括号信息 char_origin_f = character_prompt.splitlines() char_describe = [char.replace("]", " ").replace("[", " ") for char in char_origin_f] #del character's [] char_origin = [char.split("]")[0] + "]" for char in char_origin_f] # [[role1],[role2]] role_str=[x.replace("]", "").replace("[", "") for x in char_origin] # [role1,role2] prompts_origin = scene_prompts.splitlines() control_check=prompts_origin prompts_origin_unuse, negative_prompt = apply_style(img_style, prompts_origin, negative_prompt) #获取 style p prompt num_samples = 1 #批次 #image = kwargs["image"] d1, _, _, _ = image.size() if d1 == 1: prompt_single_NC = [prompt for prompt in prompts_origin if "[NC]" in prompt] # NC场景列表,单人, prompt_single_NC=[prompt.replace("[NC]","") for prompt in prompt_single_NC ] # 去除符号 index_single_NC = [index for index, prompt in enumerate(prompts_origin) if "[NC]" in prompt] # NC场景index prompts_origin = [prompt for prompt in prompts_origin if role_str[0] in prompt] # 排除 nc input_images = [nomarl_upscale(image, width, height)] torch.cuda.empty_cache() # 加入角色描述 prompt_c = char_origin_f[0].replace("]", " ").replace("[", " ") role = char_origin[0].replace("]", "").replace("[", "") prompts_dual = [x.replace(char_origin[0],prompt_c) for x in prompts_origin] prompts_dual = [x.replace("]", "").replace("[", "") for x in prompts_dual] if layout_guidance: boxes=random.choice(box_add) boxes=[[boxes]] #随机选1个自定义的 #boxes = [[[0.25, 0.25, 0.75, 0.75]]] # 1 role else: boxes = [[[0., 0., 0., 0.]]] # 1 role phrases = [[role]] drop_grounding_tokens = [0] # set to 1 if you want to drop the grounding tokens if len(char_origin_f) != 1: # 单人 raise "need one role prompts " if controlnet_model_path != "none": control_image = kwargs["control_image"] d1, _, _, _ = control_image.size() if d1!=len(control_check): raise "The number of scene prompts that must match the control_image" control_img = list(torch.chunk(control_image, chunks=d1)) #tensor control_img = [input_size_adaptation_output(control_image, 768, width, height) for control_image in control_img ] #pil control_img_list=[img for index, img in enumerate(control_img) if index not in index_single_NC ] #del NC if lora != "none": prompts_dual=self.add_lora(trigger_words, lora, prompts_dual) image_main=self.main_control(prompts_dual,control_img_list,width,height,pipe,phrases,ms_model,input_images,num_samples,steps,seed,negative_prompt,role_scale,image_encoder,cfg, image_processor,boxes,mask_threshold,start_step,image_proj_type,image_encoder_type,drop_grounding_tokens,controlnet_scale,img_style) if prompt_single_NC: if lora != "none": prompt_single_NC = self.add_lora(trigger_words, lora, prompt_single_NC) control_img_list_NC= [img for index, img in enumerate(control_img) if index in index_single_NC ] # NC input_images= [Image.new('RGB', (width, height), (255, 255, 255))] image_nc = self.main_control(prompt_single_NC, control_img_list_NC, width, height, pipe, phrases, ms_model, input_images, num_samples, steps, seed, negative_prompt, role_scale, image_encoder, cfg, image_processor, boxes, mask_threshold, start_step, image_proj_type, image_encoder_type, drop_grounding_tokens, controlnet_scale,img_style) jj = 0 for i in index_single_NC: # 重新将NC场景插入原序列 img = image_nc[jj] image_main.insert(int(i), img) jj += 1 image_list = narry_list(image_main) else: image_list = narry_list(image_main) else: #no controlnet if lora != "none": prompts_dual=self.add_lora(trigger_words, lora, prompts_dual) image_main=self.main_normal( prompts_dual, pipe, phrases, ms_model, input_images, num_samples, steps, seed, negative_prompt, role_scale, image_encoder, cfg, image_processor, boxes, mask_threshold, start_step, image_proj_type, image_encoder_type, drop_grounding_tokens, height, width,img_style) if prompt_single_NC: if lora != "none": prompt_single_NC = self.add_lora(trigger_words, lora, prompt_single_NC) input_images= [Image.new('RGB', (width, height), (255, 255, 255))] image_nc = self.main_normal( prompt_single_NC, pipe, phrases, ms_model, input_images, num_samples, steps, seed, negative_prompt, role_scale, image_encoder, cfg, image_processor, boxes, mask_threshold, start_step, image_proj_type, image_encoder_type, drop_grounding_tokens, height, width,img_style) jj = 0 for i in index_single_NC: # 重新将NC场景插入原序列 img = image_nc[jj] image_main.insert(int(i), img) jj += 1 image_list = narry_list(image_main) else: prompt_dual_NC = [prompt for prompt in prompts_origin if "[NC]" in prompt] # NC场景列表,双人, prompt_dual_NC=[prompt.replace("[NC]","") for prompt in prompt_dual_NC] # 去除符号 index_dual_NC = [index for index, prompt in enumerate(prompts_origin) if "[NC]" in prompt] # NC场景index prompts_origin_role_a = [prompt for prompt in prompts_origin if char_origin[0] in prompt and char_origin[1] not in prompt] # 排除 nc,仅单人A prompts_origin_role_a_index = [index for index, prompt in enumerate( prompts_origin) if char_origin[0] in prompt and char_origin[1] not in prompt] prompts_origin_role_b = [prompt for prompt in prompts_origin if char_origin[1] in prompt and char_origin[0] not in prompt] # 排除 nc,仅单人B prompts_origin_role_b_index = [index for index, prompt in enumerate(prompts_origin) if char_origin[1] in prompt and char_origin[0] not in prompt] prompts_origin = [prompt for prompt in prompts_origin if (char_origin[0] in prompt and char_origin[1] in prompt)] # 排除单人,nc,仅双人 #print(prompt_dual_NC, "prompt_dual_NC",index_dual_NC, "index_dual_NC",prompts_origin_role_a ,"prompts_origin_role_a ",prompts_origin_role_a_index,"prompts_origin_role_a_index",prompts_origin_role_b,"prompts_origin_role_b",prompts_origin,"prompts_origin") img_list = list(torch.chunk(image, chunks=d1)) input_images = [nomarl_upscale(img, width, height) for img in img_list] input_images_a =[input_images[0]] input_images_b = [input_images[1]] # get role name and describe role_a = role_str[0] char_describe_a= char_describe[0] role_b =role_str[1] char_describe_b = char_describe[1] # 双角色场景列表 prompt加描述 prompts_dual = [item.replace(char_origin[0],char_describe_a).replace(char_origin[1],char_describe_b) for item in prompts_origin] torch.cuda.empty_cache() if layout_guidance: boxes=[box_add] #boxes = [[[0., 0.25, 0.4, 0.75], [0.6, 0.25, 1., 0.75]]] # man+women else: boxes = [[[0., 0., 0., 0.], [0., 0., 0., 0.]]] # used if you want no layout guidance phrases = [[role_a, role_b]] drop_grounding_tokens = [0] # set to 1 if you want to drop the grounding tokens image_new=Image.new('RGB', (width, height), (255, 255, 255)) if controlnet_model_path != "none": control_image = kwargs["control_image"] d1, _, _, _ = control_image.size() if d1 != len(control_check): raise "The number of scene prompts that must match the control_image" control_img = list(torch.chunk(control_image, chunks=d1)) # tensor control_img = [input_size_adaptation_output(control_image, 768, width, height) for control_image in control_img] # pil control_img_list = [img for index, img in enumerate(control_img) if index not in index_dual_NC] # del NC if lora != "none": prompts_dual=self.add_lora(trigger_words, lora, prompts_dual) image_main=self.main_control(prompts_dual,control_img_list,width,height,pipe,phrases,ms_model,input_images,num_samples,steps,seed,negative_prompt,role_scale,image_encoder,cfg, image_processor,boxes,mask_threshold,start_step,image_proj_type,image_encoder_type,drop_grounding_tokens,controlnet_scale,img_style) if prompt_dual_NC: if lora != "none": prompt_dual_NC = self.add_lora(trigger_words, lora, prompt_dual_NC) control_img_list_NC = [img for index, img in enumerate(control_img) if index in index_dual_NC] # NC input_images = [image_new,image_new] image_nc = self.main_control(prompt_dual_NC, control_img_list_NC, width, height, pipe, phrases, ms_model, input_images, num_samples, steps, seed, negative_prompt, role_scale, image_encoder, cfg, image_processor, boxes, mask_threshold, start_step, image_proj_type, image_encoder_type, drop_grounding_tokens, controlnet_scale,img_style) jj = 0 for i in index_dual_NC: # 重新将NC场景插入原序列 img = image_nc[jj] image_main.insert(int(i), img) jj += 1 if prompts_origin_role_a: if lora != "none": prompts_origin_role_a = self.add_lora(trigger_words, lora, prompts_origin_role_a) prompts_origin_role_a =[x.replace(char_origin[0],char_describe_a) for x in prompts_origin_role_a] # 加入角色描述 control_img_list = [img for index, img in enumerate(control_img) if index in prompts_origin_role_a_index] # role a if layout_guidance: boxes = random.choice(box_add) boxes = [[boxes]] # 随机选1个自定义的 #boxes = [[[0.25, 0.25, 0.75, 0.75]]] # 1 role else: boxes = [[[0., 0., 0., 0.]]] # 1 role phrases = [[role_a]] input_images = input_images_a image_dual_a = self.main_control(prompts_origin_role_a, control_img_list, width, height, pipe, phrases, ms_model, input_images, num_samples, steps, seed, negative_prompt, role_scale, image_encoder, cfg, image_processor, boxes, mask_threshold, start_step, image_proj_type, image_encoder_type, drop_grounding_tokens, controlnet_scale,img_style) a = 0 for i in prompts_origin_role_a_index: # 重新将NC场景插入原序列 img = image_dual_a[a] image_main.insert(int(i), img) a += 1 if prompts_origin_role_b: if lora != "none": prompts_origin_role_b = self.add_lora(trigger_words, lora, prompts_origin_role_b) prompts_origin_role_b = [x.replace(char_origin[1], char_describe_b) for x in prompts_origin_role_b] # 加入角色描述 control_img_list = [img for index, img in enumerate(control_img) if index in prompts_origin_role_b_index] # role b if layout_guidance: boxes = random.choice(box_add) boxes = [[boxes]] # 随机选1个自定义的 #boxes = [[[0.25, 0.25, 0.75, 0.75]]] # 1 role else: boxes = [[[0., 0., 0., 0.]]] # 1 role phrases = [[role_b]] input_images = input_images_b image_dual_b = self.main_control(prompts_origin_role_b, control_img_list, width, height, pipe, phrases, ms_model, input_images, num_samples, steps, seed, negative_prompt, role_scale, image_encoder, cfg, image_processor, boxes, mask_threshold, start_step, image_proj_type, image_encoder_type, drop_grounding_tokens, controlnet_scale,img_style) b = 0 for i in prompts_origin_role_b_index: # 重新将NC场景插入原序列 img = image_dual_b[b] image_main.insert(int(i), img) b += 1 image_list = narry_list(image_main) else: if lora != "none": prompts_dual = self.add_lora(trigger_words, lora, prompts_dual) image_main = self.main_normal(prompts_dual, pipe, phrases, ms_model, input_images, num_samples, steps, seed, negative_prompt, role_scale, image_encoder, cfg, image_processor, boxes, mask_threshold, start_step, image_proj_type, image_encoder_type, drop_grounding_tokens, height, width,img_style) if prompt_dual_NC: if lora != "none": prompt_dual_NC = self.add_lora(trigger_words, lora, prompt_dual_NC) input_images = [image_new,image_new] image_nc = self.main_normal( prompt_dual_NC, pipe, phrases, ms_model, input_images, num_samples, steps, seed, negative_prompt, role_scale, image_encoder, cfg, image_processor, boxes, mask_threshold, start_step, image_proj_type, image_encoder_type, drop_grounding_tokens, height, width,img_style) jj = 0 for i in index_dual_NC: # 重新将NC场景插入原序列 img = image_nc[jj] image_main.insert(int(i), img) jj += 1 if prompts_origin_role_a: if lora != "none": prompts_origin_role_a = self.add_lora(trigger_words, lora, prompts_origin_role_a) prompts_origin_role_a = [x.replace(char_origin[0], char_describe_a) for x in prompts_origin_role_a] # 加入角色描述 if layout_guidance: boxes = random.choice(box_add) boxes = [[boxes]] # 随机选1个自定义的 #boxes = [[[0.25, 0.25, 0.75, 0.75]]] # 1 role else: boxes = [[[0., 0., 0., 0.]]] # 1 role phrases = [[role_a]] input_images = input_images_a image_role_a = self.main_normal(prompts_origin_role_a, pipe, phrases, ms_model, input_images, num_samples, steps, seed, negative_prompt, role_scale, image_encoder, cfg, image_processor, boxes, mask_threshold, start_step, image_proj_type, image_encoder_type, drop_grounding_tokens, height, width,img_style) jj = 0 for i in prompts_origin_role_a_index: # 重新将NC场景插入原序列 img = image_role_a[jj] image_main.insert(int(i), img) jj += 1 if prompts_origin_role_b: if lora != "none": prompts_origin_role_b = self.add_lora(trigger_words, lora, prompts_origin_role_b) prompts_origin_role_b = [x.replace(char_origin[1], char_describe_b) for x in prompts_origin_role_b] # 加入角色描述 if layout_guidance: boxes = random.choice(box_add) boxes = [[boxes]] # 随机选1个自定义的 #boxes = [[[0.25, 0.25, 0.75, 0.75]]] # 1 role else: boxes = [[[0., 0., 0., 0.]]] # 1 role phrases = [[role_b]] input_images = input_images_b image_role_b = self.main_normal(prompts_origin_role_b, pipe, phrases, ms_model, input_images, num_samples, steps, seed, negative_prompt, role_scale, image_encoder, cfg, image_processor, boxes, mask_threshold, start_step, image_proj_type, image_encoder_type, drop_grounding_tokens, height, width,img_style) jj = 0 for i in prompts_origin_role_b_index: # 重新将NC场景插入原序列 img = image_role_b[jj] image_main.insert(int(i), img) jj += 1 image_list = narry_list(image_main) image = torch.from_numpy(np.fromiter(image_list, np.dtype((np.float32, (height, width, 3))))) torch.cuda.empty_cache() return (image, scene_prompts,) class MS_Comic_Type: @classmethod def INPUT_TYPES(s): return {"required": {"image": ("IMAGE",), "scene_prompts": ("STRING", {"multiline": True, "forceInput": True, "default": ""}), "fonts_list": (fonts_lists,), "text_size": ("INT", {"default": 40, "min": 1, "max": 100}), "comic_type": (["Four_Pannel", "Classic_Comic_Style"],), "split_lines": ("STRING", {"default": ";"}), }} RETURN_TYPES = ("IMAGE",) ETURN_NAMES = ("image",) FUNCTION = "comic_gen" CATEGORY = "MSdiffusion" def comic_gen(self, image, scene_prompts, fonts_list, text_size, comic_type, split_lines): result = [item for index, item in enumerate(image)] total_results = narry_list_pil(result) font_choice = os.path.join(dir_path, "fonts", fonts_list) captions = scene_prompts.splitlines() if len(captions) > 1: # captions = [caption.replace("(", "").replace(")", "") if "(" or ")" in caption else caption # for caption in captions] # del () captions = [caption.replace("[NC]", "") for caption in captions] captions = [caption.replace("]", "").replace("[", "") for caption in captions] # captions = [ # caption.split("#")[-1] if "#" in caption else caption # for caption in captions # ] else:#trans prompt_array = scene_prompts.replace(split_lines, "\n") captions = prompt_array.splitlines() font = ImageFont.truetype(font_choice, text_size) images = ( get_comic(total_results, comic_type, captions=captions, font=font) + total_results ) images = phi2narry(images[0]) return (images,) NODE_CLASS_MAPPINGS = { "MSdiffusion_Model_Loader": MSdiffusion_Model_Loader, "MSdiffusion_Sampler": MSdiffusion_Sampler, "MS_Comic_Type":MS_Comic_Type } NODE_DISPLAY_NAME_MAPPINGS = { "MSdiffusion_Model_Loader": "MSdiffusion_Model_Loader", "MSdiffusion_Sampler": "MSdiffusion_Sampler", "MS_Comic_Type":"MS_Comic_Type" }