From 08c1d8e2e0e904fb9b3fb8214f7e9405f583ebd0 Mon Sep 17 00:00:00 2001 From: Jianqi Pan Date: Wed, 20 Mar 2024 02:40:49 +0900 Subject: [PATCH] :tada: init: jannchie's ComfyUI custom nodes --- __init__.py | 505 ++++++++++++++++++ __pycache__/__init__.cpython-311.pyc | Bin 0 -> 18403 bytes __pycache__/__init__.cpython-39.pyc | Bin 0 -> 1168 bytes pipelines/__init__.py | 73 +++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 3607 bytes requirements.txt | 3 + 6 files changed, 581 insertions(+) create mode 100644 __init__.py create mode 100644 __pycache__/__init__.cpython-311.pyc create mode 100644 __pycache__/__init__.cpython-39.pyc create mode 100644 pipelines/__init__.py create mode 100644 pipelines/__pycache__/__init__.cpython-311.pyc create mode 100644 requirements.txt diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..53fbc3e --- /dev/null +++ b/__init__.py @@ -0,0 +1,505 @@ +import contextlib +import random +from collections import Counter + +import numpy as np +import torch +from compel import Compel, DiffusersTextualInversionManager +from diffusers import StableDiffusionPipeline +from diffusers.utils.torch_utils import randn_tensor + +import comfy.model_management +import folder_paths +from comfy.utils import ProgressBar + +from .pipelines import PipelineWrapper, schedulers + + +def get_prompt_embeds(pipe, prompt, negative_prompt): + textual_inversion_manager = DiffusersTextualInversionManager(pipe) + compel = Compel( + tokenizer=pipe.tokenizer, + text_encoder=pipe.text_encoder, + textual_inversion_manager=textual_inversion_manager, + truncate_long_prompts=False, + ) + + prompt_embeds = compel.build_conditioning_tensor(prompt) + negative_prompt_embeds = compel.build_conditioning_tensor(negative_prompt) + [ + prompt_embeds, + negative_prompt_embeds, + ] = compel.pad_conditioning_tensors_to_same_length( + [prompt_embeds, negative_prompt_embeds] + ) + return prompt_embeds, negative_prompt_embeds + + +def latents_to_tensor(pipeline, latents): + image_numpy = pipeline.decode_latents(latents) # numpy + return torch.tensor(image_numpy) + + +def prepare_latents( + pipe: StableDiffusionPipeline, + batch_size: int, + num_channels_latents: int, + height: int, + width: int, + dtype: torch.dtype, + device: torch.device, + generator: torch.Generator, + latents=None, +): + shape = ( + batch_size, + num_channels_latents, + height // pipe.vae_scale_factor, + width // pipe.vae_scale_factor, + ) + if isinstance(generator, list) and len(generator) != batch_size: + raise ValueError( + f"You have passed a list of generators of length {len(generator)}, but requested an effective batch" + f" size of {batch_size}. Make sure the batch size matches the length of the generators." + ) + + if latents is None: + latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype) + else: + latents = latents.to(device) + + # scale the initial noise by the standard deviation required by the scheduler + latents = latents * pipe.scheduler.init_noise_sigma + return latents + + +class GetFilledColorImage: + RETURN_TYPES = ("IMAGE",) + FUNCTION = "run" + CATEGORY = "Jannchie" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "width": ( + "INT", + { + "default": 512, + "min": 0, + "max": 8192, + "step": 64, + "display": "number", + }, + ), + "height": ( + "INT", + { + "default": 512, + "min": 0, + "max": 8192, + "step": 64, + "display": "number", + }, + ), + "red": ( + "FLOAT", + { + "default": 0.0, + "min": 0.0, + "max": 1.0, + "step": 0.1, + "display": "number", + }, + ), + "green": ( + "FLOAT", + { + "default": 0.0, + "min": 0.0, + "max": 1.0, + "step": 0.1, + "display": "number", + }, + ), + "blue": ( + "FLOAT", + { + "default": 0.0, + "min": 0.0, + "max": 1.0, + "step": 0.1, + "display": "number", + }, + ), + }, + } + + def run(self, width, height, red, green, blue): + image = torch.tensor(np.full((height, width, 3), (red, green, blue))) + # 再转换成 0 - 1 之间的浮点数 + image = image + image = image.unsqueeze(0) + return (image,) + + +class DiffusersCompelPromptEmbedding: + CATEGORY = "Jannchie" + FUNCTION = "run" + RETURN_TYPES = ("DIFFUSERS_PROMPT_EMBEDDING", "DIFFUSERS_PROMPT_EMBEDDING") + RETURN_NAMES = ("positive prompt embedding", "negative prompt embedding") + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "pipeline": ("DIFFUSERS_PIPELINE",), + "positive_prompt": ( + "STRING", + { + "multiline": True, + "default": "(masterpiece)1.2, (best quality)1.4", + }, + ), + "negative_prompt": ("STRING", {"multiline": True, "default": ""}), + } + } + + def run( + self, + pipeline: StableDiffusionPipeline, + positive_prompt: str, + negative_prompt: str, + ): + return get_prompt_embeds(pipeline, positive_prompt, negative_prompt) + + +class DiffusersTextureInversionLoader: + CATEGORY = "Jannchie" + FUNCTION = "run" + RETURN_TYPES = ("DIFFUSERS_PIPELINE",) + RETURN_NAMES = ("pipeline",) + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "pipeline": ("DIFFUSERS_PIPELINE",), + "texture_inversion": (folder_paths.get_filename_list("embeddings"),), + }, + } + + def run(self, pipeline: StableDiffusionPipeline, texture_inversion: str): + with contextlib.suppress(Exception): + path = folder_paths.get_full_path("embeddings", texture_inversion) + token = texture_inversion.split(".")[0] + pipeline.load_textual_inversion(path, token=token) + return (pipeline,) + + +class GetAverageColorFromImage: + CATEGORY = "Jannchie" + FUNCTION = "run" + RETURN_TYPES = ("FLOAT", "FLOAT", "FLOAT") + RETURN_NAMES = ("red", "green", "blue") + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "image": ("IMAGE",), + "average": ("STRING", {"default": "mean", "options": ["mean", "mode"]}), + }, + "optional": { + "mask": ("MASK",), + }, + } + + def run(self, image: torch.Tensor, average: str, mask: torch.Tensor = None): + if average == "mean": + return self.run_avg(image, mask) + elif average == "mode": + return self.run_mode(image, mask) + + def run_avg(self, image: torch.Tensor, mask: torch.Tensor = None): + if mask is not None: + mask = mask.unsqueeze(1) + masked_image = image * mask if mask is not None else image + pixel_sum = torch.sum(masked_image, dim=(2, 3)) + pixel_count = ( + torch.sum(mask, dim=(2, 3)) + if mask is not None + else torch.prod(torch.tensor(image.shape[2:])) + ) + average_rgb = pixel_sum / pixel_count.unsqueeze(1) + + average_rgb = torch.round(average_rgb) + + return tuple(average_rgb.squeeze().tolist()) + + def run_mode(self, image: torch.Tensor, mask: torch.Tensor = None): + image = image.permute(0, 3, 1, 2) + if mask is not None: + mask = mask.unsqueeze(1) + + masked_image = image * mask if mask is not None else image + pixel_values = masked_image.view( + masked_image.shape[0], masked_image.shape[1], -1 + ) + pixel_values = pixel_values.permute(0, 2, 1) + pixel_values = pixel_values.reshape(-1, pixel_values.shape[2]) + pixel_values = [ + tuple(color.tolist()) for color in pixel_values.numpy() if color.max() > 0 + ] + + if not pixel_values: + return (0, 0, 0) + + color_counts = Counter(pixel_values) + + return max(color_counts, key=color_counts.get) + + +class DiffusersPipeline: + CATEGORY = "Jannchie" + FUNCTION = "run" + RETURN_TYPES = ("DIFFUSERS_PIPELINE",) + RETURN_NAMES = ("pipeline",) + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "ckpt_name": (folder_paths.get_filename_list("checkpoints"),), + }, + "optional": { + "vae_name": ( + folder_paths.get_filename_list("vae") + ["-"], + {"default": "-"}, + ), + "scheduler_name": ( + list(schedulers.keys()) + ["-"], + { + "default": "-", + }, + ), + }, + } + + def run(self, ckpt_name: str, vae_name: str = None, scheduler_name: str = None): + ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name) + if vae_name == "-": + vae_path = None + else: + vae_path = folder_paths.get_full_path("vae", vae_name) + if scheduler_name == "-": + scheduler_name = None + + self.pipeline_wrapper = PipelineWrapper(ckpt_path, vae_path, scheduler_name) + return (self.pipeline_wrapper.pipeline,) + + +class DiffusersPrepareLatents: + CATEGORY = "Jannchie" + FUNCTION = "run" + RETURN_TYPES = ("LATENT",) + RETURN_NAMES = ("latents",) + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "pipeline": ("DIFFUSERS_PIPELINE",), + "batch_size": ("INT", {"default": 1, "min": 1, "max": 16, "step": 1}), + "height": ("INT", {"default": 512, "min": 0, "max": 8192, "step": 64}), + "width": ("INT", {"default": 512, "min": 0, "max": 8192, "step": 64}), + }, + "optional": { + "latents": ("LATENT", {"default": None}), + "seed": ( + "INT", + {"default": None, "min": 0, "step": 1, "max": 999999999}, + ), + }, + } + + def run( + self, + pipeline: StableDiffusionPipeline, + batch_size: int = 1, + height: int = 512, + width: int = 512, + latents: torch.Tensor | None = None, + seed: int | None = None, + ): + if seed is None: + seed = random.randint(0, 999999999) + device = comfy.model_management.get_torch_device() + generator = torch.Generator(device) + generator.manual_seed(seed) + latents = prepare_latents( + pipe=pipeline, + batch_size=batch_size, + num_channels_latents=4, + height=height, + width=width, + dtype=comfy.model_management.VAE_DTYPE, + device=device, + generator=generator, + latents=latents, + ) + return (latents,) + + +class DiffusersDecoder: + CATEGORY = "Jannchie" + FUNCTION = "run" + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("images",) + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "pipeline": ("DIFFUSERS_PIPELINE",), + "latents": ("LATENT",), + }, + } + + def run(self, pipeline: StableDiffusionPipeline, latents: torch.Tensor): + return (latents_to_tensor(pipeline, latents),) + + +class DiffusersGenerate: + CATEGORY = "Jannchie" + FUNCTION = "run" + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("images",) + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "pipeline": ("DIFFUSERS_PIPELINE",), + "positive_prompt_embedding": ("DIFFUSERS_PROMPT_EMBEDDING",), + "negative_prompt_embedding": ("DIFFUSERS_PROMPT_EMBEDDING",), + "strength": ( + "FLOAT", + {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.02}, + ), + "num_inference_steps": ( + "INT", + {"default": 30, "min": 1, "max": 100, "step": 1}, + ), + }, + "optional": { + "images": ("IMAGE",), + "seed": ( + "INT", + {"default": 0, "min": 0, "step": 1, "max": 999999999999}, + ), + "batch_size": ("INT", {"default": 1, "min": 1, "max": 16, "step": 1}), + "width": ( + "INT", + { + "default": 512, + "min": 64, + "max": 8192, + "step": 64, + }, + ), + "height": ( + "INT", + { + "default": 512, + "min": 64, + "max": 8192, + "step": 64, + }, + ), + }, + } + + def run( + self, + pipeline: StableDiffusionPipeline, + positive_prompt_embedding: torch.Tensor, + negative_prompt_embedding: torch.Tensor, + width: int, + height: int, + batch_size: int, + images: torch.Tensor | None = None, + num_inference_steps: int = 30, + strength: float = 1.0, + seed=None, + ): + pbar = ProgressBar(int(num_inference_steps * strength)) + device = comfy.model_management.get_torch_device() + if not seed: + seed = random.randint(0, 999999999999) + generator = torch.Generator(device) + generator.manual_seed(seed) + # (B, H, W, C) to (B, C, H, W) + if images is None: + latents = prepare_latents( + pipe=pipeline, + batch_size=batch_size, + num_channels_latents=4, + height=height, + width=width, + generator=generator, + dtype=comfy.model_management.VAE_DTYPE, + device=device, + ) + images = latents_to_tensor(pipeline, latents).permute(0, 3, 1, 2) + else: + images = images.permute(0, 3, 1, 2) + # positive_prompt_embedding 和 negative_prompt_embedding 需要匹配 batch_size + positive_prompt_embedding = positive_prompt_embedding.repeat(batch_size, 1, 1) + negative_prompt_embedding = negative_prompt_embedding.repeat(batch_size, 1, 1) + + def callback(*_): + pbar.update(1) + + result = pipeline( + image=images, + generator=generator, + prompt_embeds=positive_prompt_embedding, + negative_prompt_embeds=negative_prompt_embedding, + num_inference_steps=num_inference_steps, + callback_steps=1, + strength=strength, + callback=callback, + return_dict=True, + ) + # image = result["images"][0] + # images to torch.Tensor + imgs = [np.array(img) for img in result["images"]] + imgs = torch.tensor(imgs, dtype=images.dtype) + result["images"][0].save("1.png") + # 0 ~ 255 to 0 ~ 1 + imgs = imgs / 255 + # (B, C, H, W) to (B, H, W, C) + return (imgs,) + + +NODE_CLASS_MAPPINGS = { + "GetFilledColorImage": GetFilledColorImage, + "GetAverageColorFromImage": GetAverageColorFromImage, + "DiffusersPipeline": DiffusersPipeline, + "DiffusersGenerate": DiffusersGenerate, + "DiffusersPrepareLatents": DiffusersPrepareLatents, + "DiffusersDecoder": DiffusersDecoder, + "DiffusersCompelPromptEmbedding": DiffusersCompelPromptEmbedding, + "DiffusersTextureInversionLoader": DiffusersTextureInversionLoader, +} +NODE_DISPLAY_NAME_MAPPINGS = { + "GetFilledColorImage": "Get Filled Color Image Jannchie", + "GetAverageColorFromImage": "Get Average Color From Image Jannchie", + "DiffusersPipeline": "Diffusers Pipeline", + "DiffusersGenerate": "Diffusers Generate", + "DiffusersPrepareLatents": "Diffusers Prepare Latents", + "DiffusersDecoder": "Diffusers Decoder", + "DiffusersCompelPromptEmbedding": "Diffusers Compel Prompt Embedding", + "DiffusersTextureInversionLoader": "Diffusers Texture Inversion Embedding Loader", +} diff --git a/__pycache__/__init__.cpython-311.pyc b/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77815f940004336cb4ac0d2698e921ded29dc5ea GIT binary patch literal 18403 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 Hi~#>XQH5W) literal 0 HcmV?d00001 diff --git a/__pycache__/__init__.cpython-39.pyc b/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49a8eda1439c21582fc83e77b1a9afffbba36bb6 GIT binary patch literal 1168 zcmbtTPfrs;6rY*h*)A=hf*R%2cxz%jBOyvtN>c+%Lt7IzB$K5xrDJxtW&cEJmJ|3b zIOx$&WHMJz{RT!8@y!;n7!nV@&HL@ln>TOXo8KF6VWEgX7P{Ysv=REIC$nO}WCJvQ z2GBqpaeNMXMqEIFBz7^!oWP%Oqm3$N5BzS)O@!xQ76|;2ON60!ouf1Cn%rCU@c}LliI8J3GxH6hfMf3h!%YH{WO1((tpmsnwR61+f=@oIT~uB&7Eqa4h5&7vafUPSvs97 zf#yUMG^cwF20%CR(W#YLpKwMpAir;9);scgnVA9e0yHf8NT%2)>nJM#iG~xI z5MiQQvtlR;Y{6~Nv51DTqNxm%)rt)~3BMmLf=_f$!+i}8G%Nuq5{?v=fp;YSkP$u7 z%TEAagT^{%gl+N=ui)|0Ul#zSI?&=aW1$xa##E89V8~NnkLMXXPCfr>rPTAiI1WTI z81m>b#8mTp^-g!snNp|ht*%q+GG@vAegQCoz*)-Y6^7PG>^G8>{ro*Wixrt~8XrJ}vfDo{Oeqam&WP0rD^sfdBvi literal 0 HcmV?d00001 diff --git a/pipelines/__init__.py b/pipelines/__init__.py new file mode 100644 index 0000000..79c5e5d --- /dev/null +++ b/pipelines/__init__.py @@ -0,0 +1,73 @@ +import torch +from diffusers import AutoencoderKL, StableDiffusionImg2ImgPipeline +from diffusers.schedulers import ( + DEISMultistepScheduler, + DPMSolverMultistepScheduler, + DPMSolverSinglestepScheduler, + EulerAncestralDiscreteScheduler, + EulerDiscreteScheduler, + HeunDiscreteScheduler, + KDPM2AncestralDiscreteScheduler, + KDPM2DiscreteScheduler, + LMSDiscreteScheduler, + UniPCMultistepScheduler, +) + +import comfy.model_management + +schedulers = { + "DPM++ 2M": DPMSolverMultistepScheduler(), + "DPM++ 2M Karras": DPMSolverMultistepScheduler(use_karras_sigmas=True), + "DPM++ 2M SDE": DPMSolverMultistepScheduler(algorithm_type="sde-dpmsolver++"), + "DPM++ 2M SDE Karras": DPMSolverMultistepScheduler( + use_karras_sigmas=True, algorithm_type="sde-dpmsolver++" + ), + "DPM++ SDE": DPMSolverSinglestepScheduler(), + "DPM++ SDE Karras": DPMSolverSinglestepScheduler(use_karras_sigmas=True), + "DPM2": KDPM2DiscreteScheduler(), + "DPM2 Karras": KDPM2DiscreteScheduler(use_karras_sigmas=True), + "DPM2 a": KDPM2AncestralDiscreteScheduler(), + "DPM2 a Karras": KDPM2AncestralDiscreteScheduler(use_karras_sigmas=True), + "Euler": EulerDiscreteScheduler(), + "Euler a": EulerAncestralDiscreteScheduler(), + "Heun": HeunDiscreteScheduler(), + "LMS": LMSDiscreteScheduler(), + "LMS Karras": LMSDiscreteScheduler(use_karras_sigmas=True), + "DEIS": DEISMultistepScheduler(), + "UniPC": UniPCMultistepScheduler(), +} + + +class PipelineWrapper: + + def __init__( + self, ckpt_path: str, vae_path: str = None, scheduler_name: str = None + ): + scheduler = schedulers.get(scheduler_name) + device = comfy.model_management.get_torch_device() + dtype = comfy.model_management.VAE_DTYPE + if ckpt_path.endswith(".safetensors"): + self.pipeline = StableDiffusionImg2ImgPipeline.from_single_file( + ckpt_path, + torch_dtype=dtype, + ) + else: + self.pipeline = StableDiffusionImg2ImgPipeline.from_pretrained( + ckpt_path, + torch_dtype=dtype, + ) + if vae_path: + if vae_path.endswith(".safetensors"): + self.pipeline.vae = AutoencoderKL.from_single_file( + vae_path, + torch_dtype=torch.bfloat16, + ) + else: + self.pipeline.vae = AutoencoderKL.from_pretrained( + vae_path, + torch_dtype=torch.bfloat16, + ) + if scheduler: + self.pipeline.scheduler = scheduler + self.pipeline.to(device) + self.pipeline.safety_checker = None diff --git a/pipelines/__pycache__/__init__.cpython-311.pyc b/pipelines/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8240ebc45beae27db2eeb86ab9950dde9bfb7d47 GIT binary patch literal 3607 zcmcgu&2QVt6(3TfOi84^Wy!W|%NyBQRkfXH7mFYb+`5smjV-$loNZAe0HGBrM~*0| zLpntS1;}9w_>e=<7RVlbC{h%gKVWaW*F6*<0|F5U7+7FWy)j7k&^`4HB`T8TZVN21 zqsKQhzxUp}H^X_4`a91D5VSA;`EU6v7=1$~Zu1QWuUQ(Qr$|B))j$I2=?10H0sV3?0W`o zK68DdzfclGP|5dv>KO$Rc=`<_5C};8&jS;ZVBZF0+%t&wGkAtpJjkXdyo3mPp_ed7 zxC#9TK`+#gf+RShA0z06`f-r>C-f75vJ+?$(7*&b4Jbc>rU-hW$23To3H=#BL+4Qe zcJm>UW-9A8YarS2a{;( zqh@{o=H#wAEVwBYvc=s_*d1keDi~MPJ9)~A9;>o=-9d4+0P}=H*RY_iz z+KTF&TA=_op-SVhh3ncqMbsX);wrP zKpmemsdV^$?`ks60e7aHE?cp@_a@*8k@OlLXPmzoS zNYv~c3cgK_Q9u4CaL#|fyCu6I=jr<3#+Vx_t>+PZukHd2v zMNdikOtdaD{#M9`Zq!PQx+W5WGV0muaBFI%2F0qWwy+8r9MoF4dQX%{`sQO+V0d>` z%X27e7E^60wTD(jfr8r*l}fX6C@XSPvmziB2gdS)dR4akzuDXpOLu;^zh$v(dr9e?3rBtld8?wc=2Nl{1JF+$uP+WnEEn#w{Eyh{dVn41mS{3c;2bKrY z%kmGSvuLelg`BNC6d~QJ_hp=)vb?I?sNoq{0nZXPM_7R@=#%HQde>qE)Kr+a_iJKZo6MKe)6@t;Mf&FJNq(Q7ZF*No_OGkX2RcNR&Skrgv?<%H=4 z6EB1FFM{)*HjUt_8C2vt%=#`Oqr_>5yWa-T{LjqH z_J38eS;Qr56!6D-;nd4;_C+{*x?qH_nBgnnfsdW#u9~@D_7a)1?DF42;A1%kK9*zP zV>$LG?_YT{vZP0rz|V5g%x!+ZB5{_q5#_(qTUjRon%t!x%RS*=vgsFW`qOEH&6;dh zXS2P_g%j@+-ehySL$BfNP@dk~%QNJtNkJ7wDk_wV@?!*e_Uw{Bp&!A<09Dn z1L60e!&eC-Y45N+4Ae5P$HSlPLp#*3DciHa{M1IkdNUbk(tEei?@J%P6*?N{Sl9ebmHVjK7d2*|fa{UdyXY!5|IJ;XhBem!(kzldx) zNYOU>@!2e*7IZY>bBG8w2^whSI+BQ?$HUk29 zw@h45tP}eAzMi;kByO9D+x8TZv51+~=RN@H%`WKczpy=Y#`g*VdqbG*CrTm2MD*w? zkP%%sqw98@D2AAz&bQp<5EIkmd7#fr zdVJG}Z<_H2eFrg7 zJ+=b$c|wnE7_kjAwqai)qTKmSEfaLb2_&cUIfGv@`4#&zppOIgT9$%Lnq;pC_T~bC Hb4~vP`k`o* literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a431d03 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +compel +diffusers +numpy \ No newline at end of file