diff --git a/README.md b/README.md index 3be8960..d1c96a1 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,13 @@ Custom nodes for ComfyUI allow to inpaint using Brushnet: ["BrushNet: A Plug-an My contribution is limited to the ComfyUI adaptation, and all credit goes to the authors of the paper. +## Updates + +Apr 28, 2014. Another rework, sorry for inconvenience. But now BrushNet is native to ComfyUI. Famous cubiq's [IPAdapter Plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus) is now working with BrushNet! I hope... :) Please, report any bugs you found. + +Apr 18, 2024. Complete rework, no more custom `diffusers` library. It is possible to use LoRA models. + +Apr 11, 2024. Initial commit. ## Installation @@ -22,26 +29,44 @@ Both `segmentation_mask_brushnet_ckpt` and `random_mask_brushnet_ckpt` or only o Base model should be SD1.5 type (realisticVisionV60B1_v51VAE, for example). -This is complete rework of previous version. There is no more custom `diffusers` library. -There is no loadings from github. I also changed the workflow so now it is possible to use LoRa models. -To users who already have previoous version: please, uninstall `diffusers` library before installation: - -``` -pip uninstall diffusers -``` - -Then install as usual. - ## Usage Below is an example for the intended workflow. The [json file](example/BrushNet_basic.json) for the example can be found inside the 'example' directory. ![example workflow](example/BrushNet_basic.png?raw=true) -This is example with LoRA: [json file](example/BrushNet_with_LoRA.json). +
+ IPAdapter plus + +[json file](example/BrushNet_with_IPA.json) + +![example workflow](example/BrushNet_with_IPA.png?raw=true) + +
+ +
+ LoRA + +[json file](example/BrushNet_with_LoRA.json) ![example workflow](example/BrushNet_with_LoRA.png?raw=true) +
+ +
+ Blending inpaint + +Sometimes inference and VAE broke image, so you need to blend inpaint image with the original: [json file](example/BrushNet_inpaint.json) + +![example workflow](example/BrushNet_inpaint.png?raw=true) + +You can see blurred and broken text after inpainting in the first image and how I suppose to repair it. + +
+ + + + ## Credits diff --git a/__init__.py b/__init__.py index e3a6c83..a9f0da8 100644 --- a/__init__.py +++ b/__init__.py @@ -1,4 +1,4 @@ -from .brushnet_nodes import BrushNetLoader, BrushNetPipeline, BrushNetInpaint, BlendInpaint +from .brushnet_nodes import BrushNetLoader, BrushNet, BlendInpaint """ @author: nullquant @title: BrushNet @@ -10,15 +10,13 @@ from .brushnet_nodes import BrushNetLoader, BrushNetPipeline, BrushNetInpaint, B # NOTE: names should be globally unique NODE_CLASS_MAPPINGS = { "BrushNetLoader": BrushNetLoader, - "BrushNetPipeline": BrushNetPipeline, - "BrushNetInpaint": BrushNetInpaint, + "BrushNet": BrushNet, "BlendInpaint": BlendInpaint, } # A dictionary that contains the friendly/humanly readable titles for the nodes NODE_DISPLAY_NAME_MAPPINGS = { "BrushNetLoader": "BrushNet Loader", - "BrushNetPipeline": "BrushNet Pipeline", - "BrushNetInpaint": "BrushNet Inpaint", + "BrushNet": "BrushNet", "BlendInpaint": "Blend Inpaint", -} \ No newline at end of file +} diff --git a/brushnet/brushnet.py b/brushnet/brushnet.py index eb6010d..8a60318 100644 --- a/brushnet/brushnet.py +++ b/brushnet/brushnet.py @@ -679,9 +679,10 @@ class BrushNetModel(ModelMixin, ConfigMixin): def forward( self, sample: torch.FloatTensor, - timestep: Union[torch.Tensor, float, int], encoder_hidden_states: torch.Tensor, brushnet_cond: torch.FloatTensor, + timestep = None, + time_emb = None, conditioning_scale: float = 1.0, class_labels: Optional[torch.Tensor] = None, timestep_cond: Optional[torch.Tensor] = None, @@ -730,6 +731,7 @@ class BrushNetModel(ModelMixin, ConfigMixin): If `return_dict` is `True`, a [`~models.brushnet.BrushNetOutput`] is returned, otherwise a tuple is returned where the first element is the sample tensor. """ + # check channel order channel_order = self.config.brushnet_conditioning_channel_order @@ -746,72 +748,77 @@ class BrushNetModel(ModelMixin, ConfigMixin): attention_mask = (1 - attention_mask.to(sample.dtype)) * -10000.0 attention_mask = attention_mask.unsqueeze(1) - # 1. time - timesteps = timestep - if not torch.is_tensor(timesteps): - # TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can - # This would be a good case for the `match` statement (Python 3.10+) - is_mps = sample.device.type == "mps" - if isinstance(timestep, float): - dtype = torch.float32 if is_mps else torch.float64 - else: - dtype = torch.int32 if is_mps else torch.int64 - timesteps = torch.tensor([timesteps], dtype=dtype, device=sample.device) - elif len(timesteps.shape) == 0: - timesteps = timesteps[None].to(sample.device) + if timestep is None and time_emb is None: + raise ValueError(f"`timestep` and `emb` are both None") - # broadcast to batch dimension in a way that's compatible with ONNX/Core ML - timesteps = timesteps.expand(sample.shape[0]) + if timestep is not None: + # 1. time + timesteps = timestep + if not torch.is_tensor(timesteps): + # TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can + # This would be a good case for the `match` statement (Python 3.10+) + is_mps = sample.device.type == "mps" + if isinstance(timestep, float): + dtype = torch.float32 if is_mps else torch.float64 + else: + dtype = torch.int32 if is_mps else torch.int64 + timesteps = torch.tensor([timesteps], dtype=dtype, device=sample.device) + elif len(timesteps.shape) == 0: + timesteps = timesteps[None].to(sample.device) - t_emb = self.time_proj(timesteps) + # broadcast to batch dimension in a way that's compatible with ONNX/Core ML + timesteps = timesteps.expand(sample.shape[0]) - # timesteps does not contain any weights and will always return f32 tensors - # but time_embedding might actually be running in fp16. so we need to cast here. - # there might be better ways to encapsulate this. - t_emb = t_emb.to(dtype=sample.dtype) + t_emb = self.time_proj(timesteps) - emb = self.time_embedding(t_emb, timestep_cond) - aug_emb = None + # timesteps does not contain any weights and will always return f32 tensors + # but time_embedding might actually be running in fp16. so we need to cast here. + # there might be better ways to encapsulate this. + t_emb = t_emb.to(dtype=sample.dtype) - if self.class_embedding is not None: - if class_labels is None: - raise ValueError("class_labels should be provided when num_class_embeds > 0") + emb = self.time_embedding(t_emb, timestep_cond) + aug_emb = None - if self.config.class_embed_type == "timestep": - class_labels = self.time_proj(class_labels) + if self.class_embedding is not None: + if class_labels is None: + raise ValueError("class_labels should be provided when num_class_embeds > 0") - class_emb = self.class_embedding(class_labels).to(dtype=self.dtype) - emb = emb + class_emb + if self.config.class_embed_type == "timestep": + class_labels = self.time_proj(class_labels) - if self.config.addition_embed_type is not None: - if self.config.addition_embed_type == "text": - aug_emb = self.add_embedding(encoder_hidden_states) + class_emb = self.class_embedding(class_labels).to(dtype=self.dtype) + emb = emb + class_emb - elif self.config.addition_embed_type == "text_time": - if "text_embeds" not in added_cond_kwargs: - raise ValueError( - f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `text_embeds` to be passed in `added_cond_kwargs`" - ) - text_embeds = added_cond_kwargs.get("text_embeds") - if "time_ids" not in added_cond_kwargs: - raise ValueError( - f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `time_ids` to be passed in `added_cond_kwargs`" - ) - time_ids = added_cond_kwargs.get("time_ids") - time_embeds = self.add_time_proj(time_ids.flatten()) - time_embeds = time_embeds.reshape((text_embeds.shape[0], -1)) + if self.config.addition_embed_type is not None: + if self.config.addition_embed_type == "text": + aug_emb = self.add_embedding(encoder_hidden_states) - add_embeds = torch.concat([text_embeds, time_embeds], dim=-1) - add_embeds = add_embeds.to(emb.dtype) - aug_emb = self.add_embedding(add_embeds) + elif self.config.addition_embed_type == "text_time": + if "text_embeds" not in added_cond_kwargs: + raise ValueError( + f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `text_embeds` to be passed in `added_cond_kwargs`" + ) + text_embeds = added_cond_kwargs.get("text_embeds") + if "time_ids" not in added_cond_kwargs: + raise ValueError( + f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `time_ids` to be passed in `added_cond_kwargs`" + ) + time_ids = added_cond_kwargs.get("time_ids") + time_embeds = self.add_time_proj(time_ids.flatten()) + time_embeds = time_embeds.reshape((text_embeds.shape[0], -1)) - emb = emb + aug_emb if aug_emb is not None else emb + add_embeds = torch.concat([text_embeds, time_embeds], dim=-1) + add_embeds = add_embeds.to(emb.dtype) + aug_emb = self.add_embedding(add_embeds) + emb = emb + aug_emb if aug_emb is not None else emb + else: + emb = time_emb + # 2. pre-process brushnet_cond=torch.concat([sample,brushnet_cond],1) sample = self.conv_in_condition(brushnet_cond) - # 3. down down_block_res_samples = (sample,) for downsample_block in self.down_blocks: @@ -851,7 +858,6 @@ class BrushNetModel(ModelMixin, ConfigMixin): # 6. BrushNet mid blocks brushnet_mid_block_res_sample = self.brushnet_mid_block(sample) - # 7. up up_block_res_samples = () for i, upsample_block in enumerate(self.up_blocks): diff --git a/brushnet/pipeline_brushnet.py b/brushnet/pipeline_brushnet.py index af68b42..c2ec676 100644 --- a/brushnet/pipeline_brushnet.py +++ b/brushnet/pipeline_brushnet.py @@ -1124,6 +1124,7 @@ class StableDiffusionBrushNetPipeline( ) # 6.1 prepare condition latents + conditioning_latents=self.vae.encode(image).latent_dist.sample() * self.vae.config.scaling_factor mask = torch.nn.functional.interpolate( original_mask, @@ -1134,7 +1135,6 @@ class StableDiffusionBrushNetPipeline( ) conditioning_latents = torch.concat([conditioning_latents,mask],1) - # 6.5 Optionally get Guidance Scale Embedding timestep_cond = None if self.unet.config.time_cond_proj_dim is not None: @@ -1197,9 +1197,9 @@ class StableDiffusionBrushNetPipeline( down_block_res_samples, mid_block_res_sample, up_block_res_samples = self.brushnet( control_model_input, - t, encoder_hidden_states=brushnet_prompt_embeds, brushnet_cond=conditioning_latents, + timestep=t, conditioning_scale=cond_scale, guess_mode=guess_mode, return_dict=False, diff --git a/brushnet/unet_2d_blocks.py b/brushnet/unet_2d_blocks.py index 12d5196..55f0d3f 100644 --- a/brushnet/unet_2d_blocks.py +++ b/brushnet/unet_2d_blocks.py @@ -1393,6 +1393,7 @@ class CrossAttnDownBlock2D(nn.Module): if down_block_add_samples is not None: hidden_states = hidden_states + down_block_add_samples.pop(0) + output_states = output_states + (hidden_states,) if self.downsamplers is not None: @@ -2808,7 +2809,6 @@ class UpBlock2D(nn.Module): output_states = output_states + (hidden_states,) if up_block_add_samples is not None: hidden_states = hidden_states + up_block_add_samples.pop(0) # todo: add before or after - if return_res_samples: return hidden_states, output_states diff --git a/brushnet/unet_2d_condition.py b/brushnet/unet_2d_condition.py index fdbbd8a..088e0ef 100644 --- a/brushnet/unet_2d_condition.py +++ b/brushnet/unet_2d_condition.py @@ -1221,6 +1221,8 @@ class UNet2DConditionModel(ModelMixin, ConfigMixin, UNet2DConditionLoadersMixin, if is_adapter and len(down_intrablock_additional_residuals) > 0: additional_residuals["additional_residuals"] = down_intrablock_additional_residuals.pop(0) + i = len(down_block_add_samples) + if is_brushnet and len(down_block_add_samples)>0: additional_residuals["down_block_add_samples"] = [down_block_add_samples.pop(0) for _ in range(len(downsample_block.resnets)+(downsample_block.downsamplers !=None))] @@ -1236,6 +1238,9 @@ class UNet2DConditionModel(ModelMixin, ConfigMixin, UNet2DConditionLoadersMixin, ) else: additional_residuals = {} + + i = len(down_block_add_samples) + if is_brushnet and len(down_block_add_samples)>0: additional_residuals["down_block_add_samples"] = [down_block_add_samples.pop(0) for _ in range(len(downsample_block.resnets)+(downsample_block.downsamplers !=None))] @@ -1299,6 +1304,9 @@ class UNet2DConditionModel(ModelMixin, ConfigMixin, UNet2DConditionLoadersMixin, if hasattr(upsample_block, "has_cross_attention") and upsample_block.has_cross_attention: additional_residuals = {} + + i = len(up_block_add_samples) + if is_brushnet and len(up_block_add_samples)>0: additional_residuals["up_block_add_samples"] = [up_block_add_samples.pop(0) for _ in range(len(upsample_block.resnets)+(upsample_block.upsamplers !=None))] @@ -1316,6 +1324,9 @@ class UNet2DConditionModel(ModelMixin, ConfigMixin, UNet2DConditionLoadersMixin, ) else: additional_residuals = {} + + i = len(up_block_add_samples) + if is_brushnet and len(up_block_add_samples)>0: additional_residuals["up_block_add_samples"] = [up_block_add_samples.pop(0) for _ in range(len(upsample_block.resnets)+(upsample_block.upsamplers !=None))] diff --git a/brushnet_nodes.py b/brushnet_nodes.py index 0b4d57e..1f08a2d 100644 --- a/brushnet_nodes.py +++ b/brushnet_nodes.py @@ -1,4 +1,5 @@ import torch +import torchvision.transforms as T import cv2 import numpy as np from PIL import Image @@ -7,23 +8,24 @@ import os import yaml import folder_paths +import sys +# Get the parent directory of 'comfy' and add it to the Python path +comfy_parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../')) +sys.path.append(comfy_parent_dir) +import comfy +import nodes +import latent_preview + import importlib from contextlib import nullcontext is_accelerate_available = importlib.util.find_spec("accelerate") is not None if is_accelerate_available: from accelerate import init_empty_weights, load_checkpoint_and_dispatch -from comfy.model_management import load_models_gpu - -from diffusers.loaders.single_file_utils import create_unet_diffusers_config, convert_ldm_unet_checkpoint -from diffusers.loaders.single_file_utils import create_scheduler_from_ldm, create_diffusers_vae_model_from_ldm -from diffusers.models.modeling_utils import load_model_dict_into_meta - -from diffusers import UniPCMultistepScheduler +from diffusers.loaders.single_file_utils import create_diffusers_vae_model_from_ldm +from diffusers.image_processor import VaeImageProcessor from .brushnet.brushnet import BrushNetModel -from .brushnet.pipeline_brushnet import StableDiffusionBrushNetPipeline -from .brushnet.unet_2d_condition import UNet2DConditionModel from typing import Tuple @@ -97,74 +99,113 @@ def inpaint_safetensors(): return names -class BrushNetPipeline: +class BrushNet: @classmethod def INPUT_TYPES(s): return {"required": { "model": ("MODEL",), - "clip": ("CLIP", ), "vae": ("VAE", ), + "image": ("IMAGE",), + "mask": ("MASK",), "brushnet": ("BRMODEL", ), + "scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0}), }, } CATEGORY = "inpaint" - RETURN_TYPES = ("BRPL",) - RETURN_NAMES = ("BRPL",) + RETURN_TYPES = ("MODEL",) + RETURN_NAMES = ("model",) - FUNCTION = "pipeline_loading" + FUNCTION = "model_update" - def pipeline_loading(self, model, clip, vae, brushnet): - load_models_gpu([model, clip.load_model()]) - checkpoint = model.model.state_dict_for_saving(clip.get_sd(), vae.get_sd(), None) + def model_update(self, model, vae, image, mask, brushnet, scale): + + # prepare image and mask + # no batches yet + #if len(image.shape) > 3: + # image = image[0] + #if len(mask.shape) > 2: + # mask = mask[0] + + masked_image = image * (1.0 - mask[:,:,:,None]) + masked_image = masked_image.permute(0, 3, 1, 2) + + vae_scale_factor = 8 # 2 ** (len(diff_vae.config.block_out_channels) - 1) + image_processor = VaeImageProcessor(vae_scale_factor=vae_scale_factor, do_convert_rgb=True) + + width = masked_image.shape[2] + height = masked_image.shape[3] + + # create diffusers VAE with open(original_config_file, "r") as fp: original_config_data = fp.read() original_config = yaml.safe_load(original_config_data) - unet = create_unet(original_config, checkpoint, torch_dtype) - vae = create_diffusers_vae_model_from_ldm( - 'StableDiffusionBrushNetPipeline', + diff_vae = create_diffusers_vae_model_from_ldm( + "StableDiffusionBrushNetPipeline", original_config, - checkpoint, + vae.get_sd(), image_size=None, scaling_factor=None, torch_dtype=None, model_type=None, - ) + )['vae'] - scheduler_type = "ddim" - prediction_type = None - scheduler_components = create_scheduler_from_ldm( - 'StableDiffusionBrushNetPipeline', - original_config, - checkpoint, - scheduler_type=scheduler_type, - prediction_type=prediction_type, - model_type=None, - ) + _ = diff_vae.to('cuda').to(dtype=torch_dtype) - pipe = StableDiffusionBrushNetPipeline( - unet=unet['unet'], - vae=vae['vae'], - text_encoder=None, - tokenizer=None, - scheduler=scheduler_components['scheduler'], - brushnet=brushnet, - requires_safety_checker=False, - safety_checker=None, - feature_extractor=None - ) + processed_image = image_processor.preprocess(masked_image, height=height, width=width).to(dtype=torch.float32) + processed_image = torch.cat([processed_image] * 2) + processed_image = processed_image.to(device=diff_vae.device, dtype=diff_vae.dtype) - pipe.to(dtype=torch_dtype) - pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config) - pipe.enable_model_cpu_offload() + image_latents = diff_vae.encode(processed_image).latent_dist.sample() * diff_vae.config.scaling_factor - print("BrushNet pipeline is loaded") + processed_mask = torch.cat([1. - mask[None,:,:,:]] * 2) + interpolated_mask = torch.nn.functional.interpolate( + processed_mask, + size=( + image_latents.shape[-2], + image_latents.shape[-1] + ) + ) + interpolated_mask = interpolated_mask.to(device=diff_vae.device, dtype=diff_vae.dtype) - return (pipe,) + conditioning_latents = torch.concat([image_latents, interpolated_mask], 1).to(dtype=torch_dtype).to(brushnet.device) + + del diff_vae + + # apply patches to code + + brushnet_conditioning_scale = scale + control_guidance_start = 0.0 + control_guidance_end = 1.0 + + if not hasattr(nodes, 'original_common_ksampler'): + nodes.original_common_ksampler = nodes.common_ksampler + nodes.common_ksampler = modified_common_ksampler + + if not hasattr(comfy.ldm.modules.diffusionmodules.openaimodel, 'original_forward_timestep_embed'): + comfy.ldm.modules.diffusionmodules.openaimodel.original_forward_timestep_embed = \ + comfy.ldm.modules.diffusionmodules.openaimodel.forward_timestep_embed + comfy.ldm.modules.diffusionmodules.openaimodel.forward_timestep_embed = modified_forward_timestep_embed + + # apply patch to model + + add_brushnet_patch(model, brushnet, conditioning_latents, + [brushnet_conditioning_scale, control_guidance_start, control_guidance_end]) + + add_model_patch(model, brushnet_inference, ('input', 0), (0, 'before')) + input_blocks = [[0,0],[1,1],[2,1],[3,0],[4,1],[5,1],[6,0],[7,1],[8,1],[9,0],[10,0],[11,0]] + for i, j in input_blocks: + add_model_patch(model, apply_brushnet, ('input', i), (j, 'after')) + add_model_patch(model, apply_brushnet, ('middle', 0), (2, 'after')) + output_blocks = [[0,0],[1,0],[2,0],[2,1],[3,1],[4,1],[5,1],[5,2],[6,1],[7,1],[8,1],[8,2],[9,1],[10,1],[11,1]] + for i, j in output_blocks: + add_model_patch(model, apply_brushnet, ('output', i), (j, 'after')) + + return (model,) class BrushNetInpaint: @@ -240,46 +281,202 @@ class BlendInpaint: def INPUT_TYPES(s): return {"required": { - "image1": ("IMAGE",), - "image2": ("IMAGE",), + "inpaint": ("IMAGE",), + "original": ("IMAGE",), "mask": ("MASK",), - "blur": ("INT", {"default": 21, "min": 0, "max": 1000}), + "kernel": ("INT", {"default": 10, "min": 1, "max": 1000}), + "sigma": ("FLOAT", {"default": 10.0, "min": 0.01, "max": 1000}), }, } CATEGORY = "inpaint" - RETURN_TYPES = ("IMAGE",) - RETURN_NAMES = ("image",) + RETURN_TYPES = ("IMAGE","MASK",) + RETURN_NAMES = ("image","MASK",) FUNCTION = "blend_inpaint" - def blend_inpaint(self, image1: torch.Tensor, image2: torch.Tensor, mask: torch.Tensor, blur: int) -> Tuple[torch.Tensor]: + def blend_inpaint(self, inpaint: torch.Tensor, original: torch.Tensor, mask: torch.Tensor, kernel: int, sigma:int) -> Tuple[torch.Tensor]: - # no batches - if len(image1.shape) > 3: - image1 = image1[0] - if len(image2.shape) > 3: - image2 = image1[0] + # no batches over mask and original image if len(mask.shape) > 2: mask = mask[0] + if len(original.shape) > 3: + original = original[0] - init_image1 = image_from_tensor(image1) - init_image2 = image_from_tensor(image2) - mask_image = image_from_tensor(mask) + if kernel % 2 == 0: + kernel += 1 + transform = T.GaussianBlur(kernel_size=(kernel, kernel), sigma=(sigma, sigma)) + blurred_mask = transform(mask[None,None,:,:]) - mask_image = 1.*(mask_image>250)[:,:,np.newaxis] + ret = [] + for result in inpaint: + ret.append(original * (1.0 - blurred_mask[0][0][:,:,None]) + result * blurred_mask[0][0][:,:,None]) - # blur, you can adjust the parameters for better performance - mask_blurred = cv2.GaussianBlur(mask_image*255, (blur, blur), 0)/255 - mask_blurred = mask_blurred[:,:,np.newaxis] - mask_image = 1-(1-mask_image) * (1-mask_blurred) - - image_pasted=init_image1 * (1-mask_image) + init_image2*mask_image - result=image_pasted.astype(init_image1.dtype) - - return (torch.stack([numpy_to_tensor(np.array(result)).squeeze(0)]),) + return (torch.stack(ret), blurred_mask[0],) +# Unfortunately, ModelPatcher does not have necessary hooks to patch, so we have to patch code instead +def modified_forward_timestep_embed(block, x, emb, context=None, transformer_options={}, + output_shape=None, time_context=None, + num_video_frames=None, image_only_indicator=None): + + if 'model_patch' not in transformer_options or transformer_options['block'] not in transformer_options['model_patch']: + return comfy.ldm.modules.diffusionmodules.openaimodel.original_forward_timestep_embed(block, x, emb, context, + transformer_options, + output_shape, time_context, + num_video_frames, image_only_indicator) + + block_patch = transformer_options['model_patch'][transformer_options['block']] + + for i, layer in enumerate(block): + + if (i, 'before') in block_patch: + x = block_patch[(i, 'before')](x, emb, context, (transformer_options['block'], i, 'before'), transformer_options) + + if isinstance(layer, comfy.ldm.modules.diffusionmodules.openaimodel.VideoResBlock): + x = layer(x, emb, num_video_frames, image_only_indicator) + elif isinstance(layer, comfy.ldm.modules.diffusionmodules.openaimodel.TimestepBlock): + x = layer(x, emb) + elif isinstance(layer, comfy.ldm.modules.attention.SpatialVideoTransformer): + x = layer(x, context, time_context, num_video_frames, image_only_indicator, transformer_options) + if "transformer_index" in transformer_options: + transformer_options["transformer_index"] += 1 + elif isinstance(layer, comfy.ldm.modules.attention.SpatialTransformer): + x = layer(x, context, transformer_options) + if "transformer_index" in transformer_options: + transformer_options["transformer_index"] += 1 + elif isinstance(layer, comfy.ldm.modules.diffusionmodules.openaimodel.Upsample): + x = layer(x, output_shape=output_shape) + else: + x = layer(x) + + if (i, 'after') in block_patch: + x = block_patch[(i, 'after')](x, emb, context, (transformer_options['block'], i, 'after'), transformer_options) + + return x + +# Model needs current step number at inference stepю. It is possible to write a custom KSampler but we'd like to use ComfyUI's one. +def modified_common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent, denoise=1.0, + disable_noise=False, start_step=None, last_step=None, force_full_denoise=False): + latent_image = latent["samples"] + if disable_noise: + noise = torch.zeros(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout, device="cpu") + else: + batch_inds = latent["batch_index"] if "batch_index" in latent else None + noise = comfy.sample.prepare_noise(latent_image, seed, batch_inds) + + noise_mask = None + if "noise_mask" in latent: + noise_mask = latent["noise_mask"] + + ####################################################################################### + # + latent_preview_callback = latent_preview.prepare_callback(model, steps) + + to = add_model_patch_option(model) + to['model_patch']['step'] = 0 + to['model_patch']['total_steps'] = steps + + def callback(step, x0, x, total_steps): + to['model_patch']['step'] = step + 1 + latent_preview_callback(steps, x0, x, total_steps) + # + ####################################################################################### + + disable_pbar = not comfy.utils.PROGRESS_BAR_ENABLED + samples = comfy.sample.sample(model, noise, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, + denoise=denoise, disable_noise=disable_noise, start_step=start_step, last_step=last_step, + force_full_denoise=force_full_denoise, noise_mask=noise_mask, callback=callback, + disable_pbar=disable_pbar, seed=seed) + out = latent.copy() + out["samples"] = samples + return (out, ) + +def add_model_patch_option(model): + if 'transformer_options' not in model.model_options: + model.model_options['transformer_options'] = {} + to = model.model_options['transformer_options'] + if "model_patch" not in to: + to["model_patch"] = {} + return to + +def add_model_patch(model, patch, block, key, replace=True): + to = add_model_patch_option(model) + if block not in to["model_patch"]: + to["model_patch"][block] = {} + if key not in to["model_patch"][block]: + to["model_patch"][block][key] = [] + + if replace: + to["model_patch"][block][key] = patch + else: + to["model_patch"][block][key].append(patch) + +def add_brushnet_patch(model, brushnet, conditioning_latents, controls): + to = add_model_patch_option(model) + to['model_patch']['brushnet_model'] = brushnet + to['model_patch']['brushnet_latents'] = conditioning_latents + to['model_patch']['brushnet_controls'] = controls + to['model_patch']['input_samples'] = [] + to['model_patch']['middle_sample'] = 0 + to['model_patch']['output_samples'] = [] + +def brushnet_inference(x, emb, context, loc, transformer_options): + # x : sample + # emb : time embedding + # context[0] == neg_cond[0][0][0] + # context[1] == pos_cond[0][0][0] + # loc = (('input'|'middle'|'output', i), j, 'before'|'after') + + if 'model_patch' not in transformer_options: + print('BrushNet inference: there is no model_patch in transformer_options') + return x + mp = transformer_options['model_patch'] + brushnet = mp['brushnet_model'] + if isinstance(brushnet, BrushNetModel): + conditioning_latents = mp['brushnet_latents'] + step = mp['step'] + total_steps = mp['total_steps'] + brushnet_conditioning_scale, control_guidance_start, control_guidance_end = mp['brushnet_controls'] + brushnet_keep = [] + for i in range(total_steps): + keeps = [ + 1.0 - float(i / total_steps < s or (i + 1) / total_steps > e) + for s, e in zip([control_guidance_start], [control_guidance_end]) + ] + brushnet_keep.append(keeps[0]) + cond_scale = brushnet_conditioning_scale * brushnet_keep[step] + + down_samples, mid_sample, up_samples = brushnet(x, + encoder_hidden_states=context[1], + brushnet_cond=conditioning_latents, + time_emb=emb, + conditioning_scale=cond_scale, + guess_mode=False, + return_dict=False,) + transformer_options['model_patch']['input_samples'] = down_samples + transformer_options['model_patch']['middle_sample'] = mid_sample + transformer_options['model_patch']['output_samples'] = up_samples + else: + print('BrushNet model is not a BrushNetModel class') + + return x + +def apply_brushnet(x, emb, context, loc, transformer_options): + if loc[0][0] == 'input': + if len(transformer_options['model_patch']['input_samples']) > 0: + return x + transformer_options['model_patch']['input_samples'].pop(0) + else: + print('BrushNet: something is not right, input samples are empty', loc) + return x + elif loc[0][0] == 'middle': + return x + transformer_options['model_patch']['middle_sample'] + else: + if len(transformer_options['model_patch']['output_samples']) > 0: + return x + transformer_options['model_patch']['output_samples'].pop(0) + else: + print('BrushNet: something is not right, output samples are empty', loc) + return x def image_from_tensor(t: torch.Tensor) -> np.ndarray: image_np = t.numpy() @@ -291,34 +488,3 @@ def numpy_to_tensor(array: np.ndarray) -> torch.Tensor: """Convert a numpy array to a tensor and scale its values from 0-255 to 0-1.""" array = array.astype(np.float32) / 255.0 return torch.from_numpy(array)[None,] - -def create_unet(original_config, checkpoint, torch_dtype): - num_in_channels = 4 - image_size = 512 - - unet_config = create_unet_diffusers_config(original_config, image_size=image_size) - unet_config["in_channels"] = num_in_channels - diffusers_format_unet_checkpoint = convert_ldm_unet_checkpoint(checkpoint, unet_config, extract_ema=False) - - ctx = init_empty_weights if is_accelerate_available else nullcontext - - with ctx(): - unet = UNet2DConditionModel(**unet_config) - - if is_accelerate_available: - unexpected_keys = load_model_dict_into_meta(unet, diffusers_format_unet_checkpoint, dtype=torch_dtype) - if unet._keys_to_ignore_on_load_unexpected is not None: - for pat in unet._keys_to_ignore_on_load_unexpected: - unexpected_keys = [k for k in unexpected_keys if re.search(pat, k) is None] - - if len(unexpected_keys) > 0: - print( - f"Some weights of the model checkpoint were not used when initializing {unet.__name__}: \n {[', '.join(unexpected_keys)]}" - ) - else: - unet.load_state_dict(diffusers_format_unet_checkpoint) - - if torch_dtype is not None: - unet = unet.to(torch_dtype) - - return {"unet": unet} diff --git a/example/BrushNet_basic.json b/example/BrushNet_basic.json index 91b0489..ba4af14 100644 --- a/example/BrushNet_basic.json +++ b/example/BrushNet_basic.json @@ -1 +1 @@ -{"last_node_id": 50, "last_link_id": 81, "nodes": [{"id": 27, "type": "GroundingDinoModelLoader (segment anything)", "pos": [45, 1126], "size": {"0": 532.800048828125, "1": 104.4000244140625}, "flags": {}, "order": 0, "mode": 0, "outputs": [{"name": "GROUNDING_DINO_MODEL", "type": "GROUNDING_DINO_MODEL", "links": [56], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "GroundingDinoModelLoader (segment anything)"}, "widgets_values": ["GroundingDINO_SwinT_OGC (694MB)"]}, {"id": 28, "type": "SAMModelLoader (segment anything)", "pos": [254, 1014], "size": {"0": 315, "1": 58}, "flags": {}, "order": 1, "mode": 0, "outputs": [{"name": "SAM_MODEL", "type": "SAM_MODEL", "links": [55], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "SAMModelLoader (segment anything)"}, "widgets_values": ["sam_vit_h (2.56GB)"]}, {"id": 12, "type": "PreviewImage", "pos": [1634, 411], "size": {"0": 523.5944213867188, "1": 547.4853515625}, "flags": {}, "order": 12, "mode": 0, "inputs": [{"name": "images", "type": "IMAGE", "link": 74}], "properties": {"Node name for S&R": "PreviewImage"}}, {"id": 32, "type": "PreviewImage", "pos": [1386, 711], "size": {"0": 210, "1": 246}, "flags": {}, "order": 11, "mode": 0, "inputs": [{"name": "images", "type": "IMAGE", "link": 60}], "properties": {"Node name for S&R": "PreviewImage"}}, {"id": 31, "type": "MaskToImage", "pos": [1140, 711], "size": {"0": 210, "1": 26}, "flags": {}, "order": 9, "mode": 0, "inputs": [{"name": "mask", "type": "MASK", "link": 59}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [60], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "MaskToImage"}}, {"id": 45, "type": "BrushNetLoader", "pos": [49, 238], "size": {"0": 576.2000122070312, "1": 104}, "flags": {}, "order": 2, "mode": 0, "outputs": [{"name": "brushnet", "type": "BRMODEL", "links": [72], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNetLoader"}, "widgets_values": ["brushnet/random_mask_brushnet_ckpt/diffusion_pytorch_model.safetensors"]}, {"id": 46, "type": "BrushNetPipeline", "pos": [827, 114], "size": {"0": 210, "1": 86}, "flags": {}, "order": 8, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 69}, {"name": "clip", "type": "CLIP", "link": 70}, {"name": "vae", "type": "VAE", "link": 71}, {"name": "brushnet", "type": "BRMODEL", "link": 72}], "outputs": [{"name": "BRPL", "type": "BRPL", "links": [73], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNetPipeline"}}, {"id": 1, "type": "LoadImage", "pos": [101, 386], "size": {"0": 470.19439697265625, "1": 578.6854248046875}, "flags": {}, "order": 3, "mode": 0, "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [57, 76], "shape": 3, "slot_index": 0}, {"name": "MASK", "type": "MASK", "links": null, "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "LoadImage"}, "widgets_values": ["test_image.jpg", "image"]}, {"id": 29, "type": "GroundingDinoSAMSegment (segment anything)", "pos": [749, 640], "size": {"0": 352.79998779296875, "1": 124.0001220703125}, "flags": {}, "order": 5, "mode": 0, "inputs": [{"name": "sam_model", "type": "SAM_MODEL", "link": 55}, {"name": "grounding_dino_model", "type": "GROUNDING_DINO_MODEL", "link": 56}, {"name": "image", "type": "IMAGE", "link": 57}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [], "shape": 3, "slot_index": 0}, {"name": "MASK", "type": "MASK", "links": [59, 77], "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "GroundingDinoSAMSegment (segment anything)"}, "widgets_values": ["cake", 0.3]}, {"id": 50, "type": "CLIPTextEncode", "pos": [741, 438], "size": {"0": 339.20001220703125, "1": 96.39999389648438}, "flags": {}, "order": 7, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 80}], "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [81], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": [""], "color": "#322", "bgcolor": "#533"}, {"id": 49, "type": "CLIPTextEncode", "pos": [738, 290], "size": {"0": 339.20001220703125, "1": 96.39999389648438}, "flags": {}, "order": 6, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 78}], "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [79], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": ["burger"], "color": "#232", "bgcolor": "#353"}, {"id": 48, "type": "BrushNetInpaint", "pos": [1218, 131], "size": {"0": 315, "1": 210}, "flags": {}, "order": 10, "mode": 0, "inputs": [{"name": "BRPL", "type": "BRPL", "link": 73}, {"name": "image", "type": "IMAGE", "link": 76}, {"name": "mask", "type": "MASK", "link": 77}, {"name": "positive", "type": "CONDITIONING", "link": 79}, {"name": "negative", "type": "CONDITIONING", "link": 81}], "outputs": [{"name": "image", "type": "IMAGE", "links": [74], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNetInpaint"}, "widgets_values": [1, "fixed", 30, 0.9]}, {"id": 47, "type": "CheckpointLoaderSimple", "pos": [109, 40], "size": {"0": 481, "1": 158}, "flags": {}, "order": 4, "mode": 0, "outputs": [{"name": "MODEL", "type": "MODEL", "links": [69], "shape": 3, "slot_index": 0}, {"name": "CLIP", "type": "CLIP", "links": [70, 78, 80], "shape": 3, "slot_index": 1}, {"name": "VAE", "type": "VAE", "links": [71], "shape": 3, "slot_index": 2}], "properties": {"Node name for S&R": "CheckpointLoaderSimple"}, "widgets_values": ["realisticVisionV60B1_v51VAE.safetensors"]}], "links": [[55, 28, 0, 29, 0, "SAM_MODEL"], [56, 27, 0, 29, 1, "GROUNDING_DINO_MODEL"], [57, 1, 0, 29, 2, "IMAGE"], [59, 29, 1, 31, 0, "MASK"], [60, 31, 0, 32, 0, "IMAGE"], [69, 47, 0, 46, 0, "MODEL"], [70, 47, 1, 46, 1, "CLIP"], [71, 47, 2, 46, 2, "VAE"], [72, 45, 0, 46, 3, "BRMODEL"], [73, 46, 0, 48, 0, "BRPL"], [74, 48, 0, 12, 0, "IMAGE"], [76, 1, 0, 48, 1, "IMAGE"], [77, 29, 1, 48, 2, "MASK"], [78, 47, 1, 49, 0, "CLIP"], [79, 49, 0, 48, 3, "CONDITIONING"], [80, 47, 1, 50, 0, "CLIP"], [81, 50, 0, 48, 4, "CONDITIONING"]], "groups": [], "config": {}, "extra": {}, "version": 0.4} \ No newline at end of file +{"last_node_id": 54, "last_link_id": 93, "nodes": [{"id": 27, "type": "GroundingDinoModelLoader (segment anything)", "pos": [45, 1126], "size": {"0": 532.800048828125, "1": 104.4000244140625}, "flags": {}, "order": 0, "mode": 0, "outputs": [{"name": "GROUNDING_DINO_MODEL", "type": "GROUNDING_DINO_MODEL", "links": [56], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "GroundingDinoModelLoader (segment anything)"}, "widgets_values": ["GroundingDINO_SwinT_OGC (694MB)"]}, {"id": 28, "type": "SAMModelLoader (segment anything)", "pos": [254, 1014], "size": {"0": 315, "1": 58}, "flags": {}, "order": 1, "mode": 0, "outputs": [{"name": "SAM_MODEL", "type": "SAM_MODEL", "links": [55], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "SAMModelLoader (segment anything)"}, "widgets_values": ["sam_vit_h (2.56GB)"]}, {"id": 1, "type": "LoadImage", "pos": [101, 386], "size": {"0": 470.19439697265625, "1": 578.6854248046875}, "flags": {}, "order": 2, "mode": 0, "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [57, 84], "shape": 3, "slot_index": 0}, {"name": "MASK", "type": "MASK", "links": null, "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "LoadImage"}, "widgets_values": ["test_image.jpg", "image"]}, {"id": 45, "type": "BrushNetLoader", "pos": [49, 238], "size": {"0": 576.2000122070312, "1": 104}, "flags": {}, "order": 3, "mode": 0, "outputs": [{"name": "brushnet", "type": "BRMODEL", "links": [85], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNetLoader"}, "widgets_values": ["brushnet/random_mask_brushnet_ckpt/diffusion_pytorch_model.safetensors"]}, {"id": 47, "type": "CheckpointLoaderSimple", "pos": [109, 40], "size": {"0": 481, "1": 158}, "flags": {}, "order": 4, "mode": 0, "outputs": [{"name": "MODEL", "type": "MODEL", "links": [82], "shape": 3, "slot_index": 0}, {"name": "CLIP", "type": "CLIP", "links": [78, 80], "shape": 3, "slot_index": 1}, {"name": "VAE", "type": "VAE", "links": [83, 92], "shape": 3, "slot_index": 2}], "properties": {"Node name for S&R": "CheckpointLoaderSimple"}, "widgets_values": ["realisticVisionV60B1_v51VAE.safetensors"]}, {"id": 54, "type": "VAEDecode", "pos": [1868, 82], "size": {"0": 210, "1": 46}, "flags": {}, "order": 13, "mode": 0, "inputs": [{"name": "samples", "type": "LATENT", "link": 91}, {"name": "vae", "type": "VAE", "link": 92}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [93], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "VAEDecode"}}, {"id": 29, "type": "GroundingDinoSAMSegment (segment anything)", "pos": [695, 628], "size": {"0": 352.79998779296875, "1": 124.0001220703125}, "flags": {}, "order": 6, "mode": 0, "inputs": [{"name": "sam_model", "type": "SAM_MODEL", "link": 55}, {"name": "grounding_dino_model", "type": "GROUNDING_DINO_MODEL", "link": 56}, {"name": "image", "type": "IMAGE", "link": 57}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [], "shape": 3, "slot_index": 0}, {"name": "MASK", "type": "MASK", "links": [59, 86], "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "GroundingDinoSAMSegment (segment anything)"}, "widgets_values": ["cake", 0.3]}, {"id": 49, "type": "CLIPTextEncode", "pos": [704, 258], "size": {"0": 339.20001220703125, "1": 96.39999389648438}, "flags": {}, "order": 7, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 78}], "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [88], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": ["burger"], "color": "#232", "bgcolor": "#353"}, {"id": 53, "type": "EmptyLatentImage", "pos": [1129, 321], "size": {"0": 315, "1": 106}, "flags": {}, "order": 5, "mode": 0, "outputs": [{"name": "LATENT", "type": "LATENT", "links": [90], "shape": 3}], "properties": {"Node name for S&R": "EmptyLatentImage"}, "widgets_values": [512, 512, 1]}, {"id": 31, "type": "MaskToImage", "pos": [1102, 710], "size": {"0": 210, "1": 26}, "flags": {}, "order": 9, "mode": 0, "inputs": [{"name": "mask", "type": "MASK", "link": 59}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [60], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "MaskToImage"}}, {"id": 32, "type": "PreviewImage", "pos": [1352, 710], "size": {"0": 210, "1": 246}, "flags": {}, "order": 11, "mode": 0, "inputs": [{"name": "images", "type": "IMAGE", "link": 60}], "properties": {"Node name for S&R": "PreviewImage"}}, {"id": 12, "type": "PreviewImage", "pos": [1624, 422], "size": {"0": 523.5944213867188, "1": 547.4853515625}, "flags": {}, "order": 14, "mode": 0, "inputs": [{"name": "images", "type": "IMAGE", "link": 93}], "properties": {"Node name for S&R": "PreviewImage"}}, {"id": 50, "type": "CLIPTextEncode", "pos": [704, 415], "size": {"0": 339.20001220703125, "1": 96.39999389648438}, "flags": {}, "order": 8, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 80}], "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [89], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": [""], "color": "#322", "bgcolor": "#533"}, {"id": 51, "type": "BrushNet", "pos": [1128, 65], "size": {"0": 315, "1": 138}, "flags": {}, "order": 10, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 82}, {"name": "vae", "type": "VAE", "link": 83}, {"name": "image", "type": "IMAGE", "link": 84}, {"name": "mask", "type": "MASK", "link": 86}, {"name": "brushnet", "type": "BRMODEL", "link": 85}], "outputs": [{"name": "model", "type": "MODEL", "links": [87], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNet"}, "widgets_values": [0.9]}, {"id": 52, "type": "KSampler", "pos": [1497, 69], "size": {"0": 315, "1": 262}, "flags": {}, "order": 12, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 87}, {"name": "positive", "type": "CONDITIONING", "link": 88}, {"name": "negative", "type": "CONDITIONING", "link": 89}, {"name": "latent_image", "type": "LATENT", "link": 90, "slot_index": 3}], "outputs": [{"name": "LATENT", "type": "LATENT", "links": [91], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "KSampler"}, "widgets_values": [0, "fixed", 15, 7.5, "euler", "normal", 1]}], "links": [[55, 28, 0, 29, 0, "SAM_MODEL"], [56, 27, 0, 29, 1, "GROUNDING_DINO_MODEL"], [57, 1, 0, 29, 2, "IMAGE"], [59, 29, 1, 31, 0, "MASK"], [60, 31, 0, 32, 0, "IMAGE"], [78, 47, 1, 49, 0, "CLIP"], [80, 47, 1, 50, 0, "CLIP"], [82, 47, 0, 51, 0, "MODEL"], [83, 47, 2, 51, 1, "VAE"], [84, 1, 0, 51, 2, "IMAGE"], [85, 45, 0, 51, 4, "BRMODEL"], [86, 29, 1, 51, 3, "MASK"], [87, 51, 0, 52, 0, "MODEL"], [88, 49, 0, 52, 1, "CONDITIONING"], [89, 50, 0, 52, 2, "CONDITIONING"], [90, 53, 0, 52, 3, "LATENT"], [91, 52, 0, 54, 0, "LATENT"], [92, 47, 2, 54, 1, "VAE"], [93, 54, 0, 12, 0, "IMAGE"]], "groups": [], "config": {}, "extra": {}, "version": 0.4} \ No newline at end of file diff --git a/example/BrushNet_basic.png b/example/BrushNet_basic.png index 77855cc..4581074 100644 Binary files a/example/BrushNet_basic.png and b/example/BrushNet_basic.png differ diff --git a/example/BrushNet_inpaint.json b/example/BrushNet_inpaint.json new file mode 100644 index 0000000..b14c6ce --- /dev/null +++ b/example/BrushNet_inpaint.json @@ -0,0 +1 @@ +{"last_node_id": 58, "last_link_id": 97, "nodes": [{"id": 45, "type": "BrushNetLoader", "pos": [49, 238], "size": {"0": 576.2000122070312, "1": 104}, "flags": {}, "order": 0, "mode": 0, "outputs": [{"name": "brushnet", "type": "BRMODEL", "links": [84], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNetLoader"}, "widgets_values": ["brushnet/random_mask_brushnet_ckpt/diffusion_pytorch_model.safetensors"]}, {"id": 50, "type": "CLIPTextEncode", "pos": [700, 444], "size": {"0": 339.20001220703125, "1": 96.39999389648438}, "flags": {}, "order": 6, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 80}], "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [90], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": ["grass, deformed, pink, blue, text, horns"], "color": "#322", "bgcolor": "#533"}, {"id": 49, "type": "CLIPTextEncode", "pos": [698, 274], "size": {"0": 339.20001220703125, "1": 96.39999389648438}, "flags": {}, "order": 5, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 78}], "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [89], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": ["closeup photo of white goat head"], "color": "#232", "bgcolor": "#353"}, {"id": 51, "type": "BrushNet", "pos": [1059, 46], "size": {"0": 315, "1": 138}, "flags": {}, "order": 7, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 82}, {"name": "vae", "type": "VAE", "link": 83}, {"name": "image", "type": "IMAGE", "link": 85}, {"name": "mask", "type": "MASK", "link": 86}, {"name": "brushnet", "type": "BRMODEL", "link": 84}], "outputs": [{"name": "model", "type": "MODEL", "links": [87], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNet"}, "widgets_values": [1]}, {"id": 12, "type": "PreviewImage", "pos": [2049, 50], "size": {"0": 523.5944213867188, "1": 547.4853515625}, "flags": {}, "order": 10, "mode": 0, "inputs": [{"name": "images", "type": "IMAGE", "link": 92}], "properties": {"Node name for S&R": "PreviewImage"}}, {"id": 55, "type": "EmptyLatentImage", "pos": [1069, 264], "size": {"0": 315, "1": 106}, "flags": {}, "order": 1, "mode": 0, "outputs": [{"name": "LATENT", "type": "LATENT", "links": [88], "shape": 3}], "properties": {"Node name for S&R": "EmptyLatentImage"}, "widgets_values": [512, 512, 1]}, {"id": 53, "type": "LoadImageMask", "pos": [612, 638], "size": [315, 318], "flags": {}, "order": 2, "mode": 0, "outputs": [{"name": "MASK", "type": "MASK", "links": [86, 93], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "LoadImageMask"}, "widgets_values": ["test_mask2 (1).png", "red", "image"]}, {"id": 1, "type": "LoadImage", "pos": [101, 386], "size": {"0": 470.19439697265625, "1": 578.6854248046875}, "flags": {}, "order": 3, "mode": 0, "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [85, 94], "shape": 3, "slot_index": 0}, {"name": "MASK", "type": "MASK", "links": null, "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "LoadImage"}, "widgets_values": ["test_image2 (1).png", "image"]}, {"id": 56, "type": "VAEDecode", "pos": [1805, 54], "size": {"0": 210, "1": 46}, "flags": {}, "order": 9, "mode": 0, "inputs": [{"name": "samples", "type": "LATENT", "link": 91}, {"name": "vae", "type": "VAE", "link": 97}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [92, 95], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "VAEDecode"}}, {"id": 57, "type": "BlendInpaint", "pos": [1532, 734], "size": {"0": 315, "1": 122}, "flags": {}, "order": 11, "mode": 0, "inputs": [{"name": "inpaint", "type": "IMAGE", "link": 95}, {"name": "original", "type": "IMAGE", "link": 94}, {"name": "mask", "type": "MASK", "link": 93}], "outputs": [{"name": "image", "type": "IMAGE", "links": [96], "shape": 3, "slot_index": 0}, {"name": "MASK", "type": "MASK", "links": null, "shape": 3}], "properties": {"Node name for S&R": "BlendInpaint"}, "widgets_values": [10, 10]}, {"id": 47, "type": "CheckpointLoaderSimple", "pos": [109, 40], "size": {"0": 481, "1": 158}, "flags": {}, "order": 4, "mode": 0, "outputs": [{"name": "MODEL", "type": "MODEL", "links": [82], "shape": 3, "slot_index": 0}, {"name": "CLIP", "type": "CLIP", "links": [78, 80], "shape": 3, "slot_index": 1}, {"name": "VAE", "type": "VAE", "links": [83, 97], "shape": 3, "slot_index": 2}], "properties": {"Node name for S&R": "CheckpointLoaderSimple"}, "widgets_values": ["realisticVisionV60B1_v51VAE.safetensors"]}, {"id": 54, "type": "KSampler", "pos": [1447, 52], "size": {"0": 315, "1": 262}, "flags": {}, "order": 8, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 87}, {"name": "positive", "type": "CONDITIONING", "link": 89}, {"name": "negative", "type": "CONDITIONING", "link": 90}, {"name": "latent_image", "type": "LATENT", "link": 88, "slot_index": 3}], "outputs": [{"name": "LATENT", "type": "LATENT", "links": [91], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "KSampler"}, "widgets_values": [0, "fixed", 20, 8, "euler", "normal", 1]}, {"id": 58, "type": "PreviewImage", "pos": [2061, 666], "size": [509.60009765625, 539.2001800537109], "flags": {}, "order": 12, "mode": 0, "inputs": [{"name": "images", "type": "IMAGE", "link": 96}], "properties": {"Node name for S&R": "PreviewImage"}}], "links": [[78, 47, 1, 49, 0, "CLIP"], [80, 47, 1, 50, 0, "CLIP"], [82, 47, 0, 51, 0, "MODEL"], [83, 47, 2, 51, 1, "VAE"], [84, 45, 0, 51, 4, "BRMODEL"], [85, 1, 0, 51, 2, "IMAGE"], [86, 53, 0, 51, 3, "MASK"], [87, 51, 0, 54, 0, "MODEL"], [88, 55, 0, 54, 3, "LATENT"], [89, 49, 0, 54, 1, "CONDITIONING"], [90, 50, 0, 54, 2, "CONDITIONING"], [91, 54, 0, 56, 0, "LATENT"], [92, 56, 0, 12, 0, "IMAGE"], [93, 53, 0, 57, 2, "MASK"], [94, 1, 0, 57, 1, "IMAGE"], [95, 56, 0, 57, 0, "IMAGE"], [96, 57, 0, 58, 0, "IMAGE"], [97, 47, 2, 56, 1, "VAE"]], "groups": [], "config": {}, "extra": {}, "version": 0.4} \ No newline at end of file diff --git a/example/BrushNet_inpaint.png b/example/BrushNet_inpaint.png new file mode 100644 index 0000000..10ef8be Binary files /dev/null and b/example/BrushNet_inpaint.png differ diff --git a/example/BrushNet_with_IPA.json b/example/BrushNet_with_IPA.json new file mode 100644 index 0000000..46033fb --- /dev/null +++ b/example/BrushNet_with_IPA.json @@ -0,0 +1 @@ +{"last_node_id": 61, "last_link_id": 117, "nodes": [{"id": 27, "type": "GroundingDinoModelLoader (segment anything)", "pos": [45, 1126], "size": {"0": 532.800048828125, "1": 104.4000244140625}, "flags": {}, "order": 0, "mode": 0, "outputs": [{"name": "GROUNDING_DINO_MODEL", "type": "GROUNDING_DINO_MODEL", "links": [56], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "GroundingDinoModelLoader (segment anything)"}, "widgets_values": ["GroundingDINO_SwinT_OGC (694MB)"]}, {"id": 28, "type": "SAMModelLoader (segment anything)", "pos": [254, 1014], "size": {"0": 315, "1": 58}, "flags": {}, "order": 1, "mode": 0, "outputs": [{"name": "SAM_MODEL", "type": "SAM_MODEL", "links": [55], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "SAMModelLoader (segment anything)"}, "widgets_values": ["sam_vit_h (2.56GB)"]}, {"id": 45, "type": "BrushNetLoader", "pos": [49, 238], "size": {"0": 576.2000122070312, "1": 104}, "flags": {}, "order": 2, "mode": 0, "outputs": [{"name": "brushnet", "type": "BRMODEL", "links": [98], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNetLoader"}, "widgets_values": ["brushnet/random_mask_brushnet_ckpt/diffusion_pytorch_model.safetensors"]}, {"id": 1, "type": "LoadImage", "pos": [101, 386], "size": {"0": 470.19439697265625, "1": 578.6854248046875}, "flags": {}, "order": 3, "mode": 0, "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [57, 100], "shape": 3, "slot_index": 0}, {"name": "MASK", "type": "MASK", "links": null, "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "LoadImage"}, "widgets_values": ["test_image.jpg", "image"]}, {"id": 50, "type": "CLIPTextEncode", "pos": [1033, 417], "size": {"0": 339.20001220703125, "1": 96.39999389648438}, "flags": {}, "order": 10, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 114}], "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [103], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": [""], "color": "#322", "bgcolor": "#533"}, {"id": 29, "type": "GroundingDinoSAMSegment (segment anything)", "pos": [684, 765], "size": {"0": 352.79998779296875, "1": 124.0001220703125}, "flags": {}, "order": 7, "mode": 0, "inputs": [{"name": "sam_model", "type": "SAM_MODEL", "link": 55}, {"name": "grounding_dino_model", "type": "GROUNDING_DINO_MODEL", "link": 56}, {"name": "image", "type": "IMAGE", "link": 57}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [], "shape": 3, "slot_index": 0}, {"name": "MASK", "type": "MASK", "links": [99], "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "GroundingDinoSAMSegment (segment anything)"}, "widgets_values": ["cake", 0.3]}, {"id": 56, "type": "EmptyLatentImage", "pos": [1051, 566], "size": {"0": 315, "1": 106}, "flags": {}, "order": 4, "mode": 0, "outputs": [{"name": "LATENT", "type": "LATENT", "links": [104], "shape": 3}], "properties": {"Node name for S&R": "EmptyLatentImage"}, "widgets_values": [512, 512, 1]}, {"id": 57, "type": "VAEDecode", "pos": [2009.6002197265625, 135.59999084472656], "size": {"0": 210, "1": 46}, "flags": {}, "order": 14, "mode": 0, "inputs": [{"name": "samples", "type": "LATENT", "link": 105}, {"name": "vae", "type": "VAE", "link": 107}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [106], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "VAEDecode"}}, {"id": 12, "type": "PreviewImage", "pos": [1666, 438], "size": {"0": 523.5944213867188, "1": 547.4853515625}, "flags": {}, "order": 15, "mode": 0, "inputs": [{"name": "images", "type": "IMAGE", "link": 106}], "properties": {"Node name for S&R": "PreviewImage"}}, {"id": 47, "type": "CheckpointLoaderSimple", "pos": [-71, 21], "size": {"0": 481, "1": 158}, "flags": {}, "order": 5, "mode": 0, "outputs": [{"name": "MODEL", "type": "MODEL", "links": [116], "shape": 3, "slot_index": 0}, {"name": "CLIP", "type": "CLIP", "links": [113, 114], "shape": 3, "slot_index": 1}, {"name": "VAE", "type": "VAE", "links": [97, 107], "shape": 3, "slot_index": 2}], "properties": {"Node name for S&R": "CheckpointLoaderSimple"}, "widgets_values": ["realisticVisionV60B1_v51VAE.safetensors"]}, {"id": 61, "type": "IPAdapterUnifiedLoader", "pos": [452, -96], "size": {"0": 315, "1": 78}, "flags": {}, "order": 8, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 116}, {"name": "ipadapter", "type": "IPADAPTER", "link": null}], "outputs": [{"name": "model", "type": "MODEL", "links": [117], "shape": 3, "slot_index": 0}, {"name": "ipadapter", "type": "IPADAPTER", "links": [115], "shape": 3}], "properties": {"Node name for S&R": "IPAdapterUnifiedLoader"}, "widgets_values": ["STANDARD (medium strength)"]}, {"id": 58, "type": "IPAdapter", "pos": [807, -100], "size": {"0": 315, "1": 190}, "flags": {}, "order": 11, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 117}, {"name": "ipadapter", "type": "IPADAPTER", "link": 115, "slot_index": 1}, {"name": "image", "type": "IMAGE", "link": 112, "slot_index": 2}, {"name": "attn_mask", "type": "MASK", "link": null}], "outputs": [{"name": "MODEL", "type": "MODEL", "links": [110], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "IPAdapter"}, "widgets_values": [1, 0, 1, "style transfer"]}, {"id": 60, "type": "LoadImage", "pos": [65, -355], "size": [315, 314], "flags": {}, "order": 6, "mode": 0, "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [112], "shape": 3}, {"name": "MASK", "type": "MASK", "links": null, "shape": 3}], "properties": {"Node name for S&R": "LoadImage"}, "widgets_values": ["ComfyUI_temp_mynbi_00021_ (1).png", "image"]}, {"id": 49, "type": "CLIPTextEncode", "pos": [1041, 267], "size": {"0": 339.20001220703125, "1": 96.39999389648438}, "flags": {}, "order": 9, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 113}], "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [102], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": ["glowing bowl"], "color": "#232", "bgcolor": "#353"}, {"id": 55, "type": "KSampler", "pos": [1628.000244140625, 69.19998931884766], "size": {"0": 315, "1": 262}, "flags": {}, "order": 13, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 101}, {"name": "positive", "type": "CONDITIONING", "link": 102}, {"name": "negative", "type": "CONDITIONING", "link": 103}, {"name": "latent_image", "type": "LATENT", "link": 104, "slot_index": 3}], "outputs": [{"name": "LATENT", "type": "LATENT", "links": [105], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "KSampler"}, "widgets_values": [0, "fixed", 20, 7, "euler_ancestral", "normal", 1]}, {"id": 54, "type": "BrushNet", "pos": [1226, 20], "size": {"0": 315, "1": 138}, "flags": {}, "order": 12, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 110}, {"name": "vae", "type": "VAE", "link": 97}, {"name": "image", "type": "IMAGE", "link": 100}, {"name": "mask", "type": "MASK", "link": 99}, {"name": "brushnet", "type": "BRMODEL", "link": 98}], "outputs": [{"name": "model", "type": "MODEL", "links": [101], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNet"}, "widgets_values": [0.9]}], "links": [[55, 28, 0, 29, 0, "SAM_MODEL"], [56, 27, 0, 29, 1, "GROUNDING_DINO_MODEL"], [57, 1, 0, 29, 2, "IMAGE"], [97, 47, 2, 54, 1, "VAE"], [98, 45, 0, 54, 4, "BRMODEL"], [99, 29, 1, 54, 3, "MASK"], [100, 1, 0, 54, 2, "IMAGE"], [101, 54, 0, 55, 0, "MODEL"], [102, 49, 0, 55, 1, "CONDITIONING"], [103, 50, 0, 55, 2, "CONDITIONING"], [104, 56, 0, 55, 3, "LATENT"], [105, 55, 0, 57, 0, "LATENT"], [106, 57, 0, 12, 0, "IMAGE"], [107, 47, 2, 57, 1, "VAE"], [110, 58, 0, 54, 0, "MODEL"], [112, 60, 0, 58, 2, "IMAGE"], [113, 47, 1, 49, 0, "CLIP"], [114, 47, 1, 50, 0, "CLIP"], [115, 61, 1, 58, 1, "IPADAPTER"], [116, 47, 0, 61, 0, "MODEL"], [117, 61, 0, 58, 0, "MODEL"]], "groups": [], "config": {}, "extra": {}, "version": 0.4} \ No newline at end of file diff --git a/example/BrushNet_with_IPA.png b/example/BrushNet_with_IPA.png new file mode 100644 index 0000000..0bf9098 Binary files /dev/null and b/example/BrushNet_with_IPA.png differ diff --git a/example/BrushNet_with_LoRA.json b/example/BrushNet_with_LoRA.json index 4d1c236..001e3e2 100644 --- a/example/BrushNet_with_LoRA.json +++ b/example/BrushNet_with_LoRA.json @@ -1 +1 @@ -{"last_node_id": 53, "last_link_id": 95, "nodes": [{"id": 27, "type": "GroundingDinoModelLoader (segment anything)", "pos": [45, 1126], "size": {"0": 532.800048828125, "1": 104.4000244140625}, "flags": {}, "order": 0, "mode": 0, "outputs": [{"name": "GROUNDING_DINO_MODEL", "type": "GROUNDING_DINO_MODEL", "links": [56], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "GroundingDinoModelLoader (segment anything)"}, "widgets_values": ["GroundingDINO_SwinT_OGC (694MB)"]}, {"id": 28, "type": "SAMModelLoader (segment anything)", "pos": [254, 1014], "size": {"0": 315, "1": 58}, "flags": {}, "order": 1, "mode": 0, "outputs": [{"name": "SAM_MODEL", "type": "SAM_MODEL", "links": [55], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "SAMModelLoader (segment anything)"}, "widgets_values": ["sam_vit_h (2.56GB)"]}, {"id": 32, "type": "PreviewImage", "pos": [1386, 711], "size": {"0": 210, "1": 246}, "flags": {}, "order": 11, "mode": 0, "inputs": [{"name": "images", "type": "IMAGE", "link": 60}], "properties": {"Node name for S&R": "PreviewImage"}}, {"id": 31, "type": "MaskToImage", "pos": [1140, 711], "size": {"0": 210, "1": 26}, "flags": {}, "order": 7, "mode": 0, "inputs": [{"name": "mask", "type": "MASK", "link": 59}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [60], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "MaskToImage"}}, {"id": 45, "type": "BrushNetLoader", "pos": [49, 238], "size": {"0": 576.2000122070312, "1": 104}, "flags": {}, "order": 2, "mode": 0, "outputs": [{"name": "brushnet", "type": "BRMODEL", "links": [72], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNetLoader"}, "widgets_values": ["brushnet/random_mask_brushnet_ckpt/diffusion_pytorch_model.safetensors"]}, {"id": 1, "type": "LoadImage", "pos": [101, 386], "size": {"0": 470.19439697265625, "1": 578.6854248046875}, "flags": {}, "order": 3, "mode": 0, "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [57, 76], "shape": 3, "slot_index": 0}, {"name": "MASK", "type": "MASK", "links": null, "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "LoadImage"}, "widgets_values": ["test_image.jpg", "image"]}, {"id": 29, "type": "GroundingDinoSAMSegment (segment anything)", "pos": [749, 640], "size": {"0": 352.79998779296875, "1": 124.0001220703125}, "flags": {}, "order": 5, "mode": 0, "inputs": [{"name": "sam_model", "type": "SAM_MODEL", "link": 55}, {"name": "grounding_dino_model", "type": "GROUNDING_DINO_MODEL", "link": 56}, {"name": "image", "type": "IMAGE", "link": 57}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [], "shape": 3, "slot_index": 0}, {"name": "MASK", "type": "MASK", "links": [59, 77], "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "GroundingDinoSAMSegment (segment anything)"}, "widgets_values": ["cake", 0.3]}, {"id": 46, "type": "BrushNetPipeline", "pos": [1134, 49], "size": {"0": 210, "1": 86}, "flags": {}, "order": 8, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 92}, {"name": "clip", "type": "CLIP", "link": 93}, {"name": "vae", "type": "VAE", "link": 71}, {"name": "brushnet", "type": "BRMODEL", "link": 72}], "outputs": [{"name": "BRPL", "type": "BRPL", "links": [73], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNetPipeline"}}, {"id": 50, "type": "CLIPTextEncode", "pos": [1078, 444], "size": {"0": 339.20001220703125, "1": 96.39999389648438}, "flags": {}, "order": 10, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 95}], "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [81], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": [""], "color": "#322", "bgcolor": "#533"}, {"id": 47, "type": "CheckpointLoaderSimple", "pos": [109, 40], "size": {"0": 481, "1": 158}, "flags": {}, "order": 4, "mode": 0, "outputs": [{"name": "MODEL", "type": "MODEL", "links": [82], "shape": 3, "slot_index": 0}, {"name": "CLIP", "type": "CLIP", "links": [83], "shape": 3, "slot_index": 1}, {"name": "VAE", "type": "VAE", "links": [71], "shape": 3, "slot_index": 2}], "properties": {"Node name for S&R": "CheckpointLoaderSimple"}, "widgets_values": ["realisticVisionV60B1_v51VAE.safetensors"]}, {"id": 12, "type": "PreviewImage", "pos": [1847, 147], "size": {"0": 523.5944213867188, "1": 547.4853515625}, "flags": {}, "order": 13, "mode": 0, "inputs": [{"name": "images", "type": "IMAGE", "link": 74}], "properties": {"Node name for S&R": "PreviewImage"}}, {"id": 51, "type": "LoraLoader", "pos": [641, 43], "size": {"0": 315, "1": 126}, "flags": {}, "order": 6, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 82}, {"name": "clip", "type": "CLIP", "link": 83}], "outputs": [{"name": "MODEL", "type": "MODEL", "links": [92], "shape": 3, "slot_index": 0}, {"name": "CLIP", "type": "CLIP", "links": [93, 94, 95], "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "LoraLoader"}, "widgets_values": ["glasssculpture_v8.safetensors", 1, 1]}, {"id": 49, "type": "CLIPTextEncode", "pos": [1083, 296], "size": {"0": 339.20001220703125, "1": 96.39999389648438}, "flags": {}, "order": 9, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 94}], "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [79], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": ["a glasssculpture of burger, transparent, translucent, reflections"], "color": "#232", "bgcolor": "#353"}, {"id": 48, "type": "BrushNetInpaint", "pos": [1507, 145], "size": {"0": 315, "1": 210}, "flags": {}, "order": 12, "mode": 0, "inputs": [{"name": "BRPL", "type": "BRPL", "link": 73}, {"name": "image", "type": "IMAGE", "link": 76}, {"name": "mask", "type": "MASK", "link": 77}, {"name": "positive", "type": "CONDITIONING", "link": 79}, {"name": "negative", "type": "CONDITIONING", "link": 81}], "outputs": [{"name": "image", "type": "IMAGE", "links": [74], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNetInpaint"}, "widgets_values": [0, "fixed", 30, 0.9]}], "links": [[55, 28, 0, 29, 0, "SAM_MODEL"], [56, 27, 0, 29, 1, "GROUNDING_DINO_MODEL"], [57, 1, 0, 29, 2, "IMAGE"], [59, 29, 1, 31, 0, "MASK"], [60, 31, 0, 32, 0, "IMAGE"], [71, 47, 2, 46, 2, "VAE"], [72, 45, 0, 46, 3, "BRMODEL"], [73, 46, 0, 48, 0, "BRPL"], [74, 48, 0, 12, 0, "IMAGE"], [76, 1, 0, 48, 1, "IMAGE"], [77, 29, 1, 48, 2, "MASK"], [79, 49, 0, 48, 3, "CONDITIONING"], [81, 50, 0, 48, 4, "CONDITIONING"], [82, 47, 0, 51, 0, "MODEL"], [83, 47, 1, 51, 1, "CLIP"], [92, 51, 0, 46, 0, "MODEL"], [93, 51, 1, 46, 1, "CLIP"], [94, 51, 1, 49, 0, "CLIP"], [95, 51, 1, 50, 0, "CLIP"]], "groups": [], "config": {}, "extra": {}, "version": 0.4} \ No newline at end of file +{"last_node_id": 57, "last_link_id": 107, "nodes": [{"id": 27, "type": "GroundingDinoModelLoader (segment anything)", "pos": [45, 1126], "size": {"0": 532.800048828125, "1": 104.4000244140625}, "flags": {}, "order": 0, "mode": 0, "outputs": [{"name": "GROUNDING_DINO_MODEL", "type": "GROUNDING_DINO_MODEL", "links": [56], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "GroundingDinoModelLoader (segment anything)"}, "widgets_values": ["GroundingDINO_SwinT_OGC (694MB)"]}, {"id": 28, "type": "SAMModelLoader (segment anything)", "pos": [254, 1014], "size": {"0": 315, "1": 58}, "flags": {}, "order": 1, "mode": 0, "outputs": [{"name": "SAM_MODEL", "type": "SAM_MODEL", "links": [55], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "SAMModelLoader (segment anything)"}, "widgets_values": ["sam_vit_h (2.56GB)"]}, {"id": 51, "type": "LoraLoader", "pos": [641, 43], "size": {"0": 315, "1": 126}, "flags": {}, "order": 7, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 82}, {"name": "clip", "type": "CLIP", "link": 83}], "outputs": [{"name": "MODEL", "type": "MODEL", "links": [96], "shape": 3, "slot_index": 0}, {"name": "CLIP", "type": "CLIP", "links": [94, 95], "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "LoraLoader"}, "widgets_values": ["glasssculpture_v8.safetensors", 1, 1]}, {"id": 45, "type": "BrushNetLoader", "pos": [49, 238], "size": {"0": 576.2000122070312, "1": 104}, "flags": {}, "order": 2, "mode": 0, "outputs": [{"name": "brushnet", "type": "BRMODEL", "links": [98], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNetLoader"}, "widgets_values": ["brushnet/random_mask_brushnet_ckpt/diffusion_pytorch_model.safetensors"]}, {"id": 1, "type": "LoadImage", "pos": [101, 386], "size": {"0": 470.19439697265625, "1": 578.6854248046875}, "flags": {}, "order": 3, "mode": 0, "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [57, 100], "shape": 3, "slot_index": 0}, {"name": "MASK", "type": "MASK", "links": null, "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "LoadImage"}, "widgets_values": ["test_image.jpg", "image"]}, {"id": 49, "type": "CLIPTextEncode", "pos": [1041, 267], "size": {"0": 339.20001220703125, "1": 96.39999389648438}, "flags": {}, "order": 10, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 94}], "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [102], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": ["a glasssculpture of burger, transparent, translucent, reflections"], "color": "#232", "bgcolor": "#353"}, {"id": 50, "type": "CLIPTextEncode", "pos": [1033, 417], "size": {"0": 339.20001220703125, "1": 96.39999389648438}, "flags": {}, "order": 11, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 95}], "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [103], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": [""], "color": "#322", "bgcolor": "#533"}, {"id": 29, "type": "GroundingDinoSAMSegment (segment anything)", "pos": [684, 765], "size": {"0": 352.79998779296875, "1": 124.0001220703125}, "flags": {}, "order": 6, "mode": 0, "inputs": [{"name": "sam_model", "type": "SAM_MODEL", "link": 55}, {"name": "grounding_dino_model", "type": "GROUNDING_DINO_MODEL", "link": 56}, {"name": "image", "type": "IMAGE", "link": 57}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [], "shape": 3, "slot_index": 0}, {"name": "MASK", "type": "MASK", "links": [59, 99], "shape": 3, "slot_index": 1}], "properties": {"Node name for S&R": "GroundingDinoSAMSegment (segment anything)"}, "widgets_values": ["cake", 0.3]}, {"id": 31, "type": "MaskToImage", "pos": [1083, 791], "size": {"0": 210, "1": 26}, "flags": {}, "order": 8, "mode": 0, "inputs": [{"name": "mask", "type": "MASK", "link": 59}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [60], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "MaskToImage"}}, {"id": 32, "type": "PreviewImage", "pos": [1335, 781], "size": {"0": 210, "1": 246}, "flags": {}, "order": 12, "mode": 0, "inputs": [{"name": "images", "type": "IMAGE", "link": 60}], "properties": {"Node name for S&R": "PreviewImage"}}, {"id": 56, "type": "EmptyLatentImage", "pos": [1051, 566], "size": {"0": 315, "1": 106}, "flags": {}, "order": 4, "mode": 0, "outputs": [{"name": "LATENT", "type": "LATENT", "links": [104], "shape": 3}], "properties": {"Node name for S&R": "EmptyLatentImage"}, "widgets_values": [512, 512, 1]}, {"id": 57, "type": "VAEDecode", "pos": [2009.6002197265625, 135.59999084472656], "size": {"0": 210, "1": 46}, "flags": {}, "order": 14, "mode": 0, "inputs": [{"name": "samples", "type": "LATENT", "link": 105}, {"name": "vae", "type": "VAE", "link": 107}], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [106], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "VAEDecode"}}, {"id": 12, "type": "PreviewImage", "pos": [1666, 438], "size": {"0": 523.5944213867188, "1": 547.4853515625}, "flags": {}, "order": 15, "mode": 0, "inputs": [{"name": "images", "type": "IMAGE", "link": 106}], "properties": {"Node name for S&R": "PreviewImage"}}, {"id": 54, "type": "BrushNet", "pos": [1232, 44], "size": {"0": 315, "1": 138}, "flags": {}, "order": 9, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 96}, {"name": "vae", "type": "VAE", "link": 97}, {"name": "image", "type": "IMAGE", "link": 100}, {"name": "mask", "type": "MASK", "link": 99}, {"name": "brushnet", "type": "BRMODEL", "link": 98}], "outputs": [{"name": "model", "type": "MODEL", "links": [101], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "BrushNet"}, "widgets_values": [0.9]}, {"id": 47, "type": "CheckpointLoaderSimple", "pos": [109, 40], "size": {"0": 481, "1": 158}, "flags": {}, "order": 5, "mode": 0, "outputs": [{"name": "MODEL", "type": "MODEL", "links": [82], "shape": 3, "slot_index": 0}, {"name": "CLIP", "type": "CLIP", "links": [83], "shape": 3, "slot_index": 1}, {"name": "VAE", "type": "VAE", "links": [97, 107], "shape": 3, "slot_index": 2}], "properties": {"Node name for S&R": "CheckpointLoaderSimple"}, "widgets_values": ["realisticVisionV60B1_v51VAE.safetensors"]}, {"id": 55, "type": "KSampler", "pos": [1628.000244140625, 69.19998931884766], "size": {"0": 315, "1": 262}, "flags": {}, "order": 13, "mode": 0, "inputs": [{"name": "model", "type": "MODEL", "link": 101}, {"name": "positive", "type": "CONDITIONING", "link": 102}, {"name": "negative", "type": "CONDITIONING", "link": 103}, {"name": "latent_image", "type": "LATENT", "link": 104, "slot_index": 3}], "outputs": [{"name": "LATENT", "type": "LATENT", "links": [105], "shape": 3, "slot_index": 0}], "properties": {"Node name for S&R": "KSampler"}, "widgets_values": [0, "fixed", 30, 7, "euler_ancestral", "normal", 1]}], "links": [[55, 28, 0, 29, 0, "SAM_MODEL"], [56, 27, 0, 29, 1, "GROUNDING_DINO_MODEL"], [57, 1, 0, 29, 2, "IMAGE"], [59, 29, 1, 31, 0, "MASK"], [60, 31, 0, 32, 0, "IMAGE"], [82, 47, 0, 51, 0, "MODEL"], [83, 47, 1, 51, 1, "CLIP"], [94, 51, 1, 49, 0, "CLIP"], [95, 51, 1, 50, 0, "CLIP"], [96, 51, 0, 54, 0, "MODEL"], [97, 47, 2, 54, 1, "VAE"], [98, 45, 0, 54, 4, "BRMODEL"], [99, 29, 1, 54, 3, "MASK"], [100, 1, 0, 54, 2, "IMAGE"], [101, 54, 0, 55, 0, "MODEL"], [102, 49, 0, 55, 1, "CONDITIONING"], [103, 50, 0, 55, 2, "CONDITIONING"], [104, 56, 0, 55, 3, "LATENT"], [105, 55, 0, 57, 0, "LATENT"], [106, 57, 0, 12, 0, "IMAGE"], [107, 47, 2, 57, 1, "VAE"]], "groups": [], "config": {}, "extra": {}, "version": 0.4} \ No newline at end of file diff --git a/example/BrushNet_with_LoRA.png b/example/BrushNet_with_LoRA.png index c013ec8..6dbb845 100644 Binary files a/example/BrushNet_with_LoRA.png and b/example/BrushNet_with_LoRA.png differ diff --git a/example/test_image2.png b/example/test_image2.png new file mode 100644 index 0000000..ab05e4d Binary files /dev/null and b/example/test_image2.png differ diff --git a/example/test_mask2.png b/example/test_mask2.png new file mode 100644 index 0000000..9161134 Binary files /dev/null and b/example/test_mask2.png differ diff --git a/requirements.txt b/requirements.txt index e3a48be..ddcc805 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ torch +torchvision diffusers>=0.27.0 accelerate>=0.29.0 numpy