CoolFace
Apppublic

fred-dev/comfy_ui_ali

sourceHugging Facemitupdated 1y agoView on Hugging Face
0likes
controlnet.py857 linesDownload Raw Back to comfy
1"""2    This file is part of ComfyUI.3    Copyright (C) 2024 Comfy4 5    This program is free software: you can redistribute it and/or modify6    it under the terms of the GNU General Public License as published by7    the Free Software Foundation, either version 3 of the License, or8    (at your option) any later version.9 10    This program is distributed in the hope that it will be useful,11    but WITHOUT ANY WARRANTY; without even the implied warranty of12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the13    GNU General Public License for more details.14 15    You should have received a copy of the GNU General Public License16    along with this program.  If not, see <https://www.gnu.org/licenses/>.17"""18 19 20import torch21from enum import Enum22import math23import os24import logging25import comfy.utils26import comfy.model_management27import comfy.model_detection28import comfy.model_patcher29import comfy.ops30import comfy.latent_formats31 32import comfy.cldm.cldm33import comfy.t2i_adapter.adapter34import comfy.ldm.cascade.controlnet35import comfy.cldm.mmdit36import comfy.ldm.hydit.controlnet37import comfy.ldm.flux.controlnet38import comfy.cldm.dit_embedder39from typing import TYPE_CHECKING40if TYPE_CHECKING:41    from comfy.hooks import HookGroup42 43 44def broadcast_image_to(tensor, target_batch_size, batched_number):45    current_batch_size = tensor.shape[0]46    #print(current_batch_size, target_batch_size)47    if current_batch_size == 1:48        return tensor49 50    per_batch = target_batch_size // batched_number51    tensor = tensor[:per_batch]52 53    if per_batch > tensor.shape[0]:54        tensor = torch.cat([tensor] * (per_batch // tensor.shape[0]) + [tensor[:(per_batch % tensor.shape[0])]], dim=0)55 56    current_batch_size = tensor.shape[0]57    if current_batch_size == target_batch_size:58        return tensor59    else:60        return torch.cat([tensor] * batched_number, dim=0)61 62class StrengthType(Enum):63    CONSTANT = 164    LINEAR_UP = 265 66class ControlBase:67    def __init__(self):68        self.cond_hint_original = None69        self.cond_hint = None70        self.strength = 1.071        self.timestep_percent_range = (0.0, 1.0)72        self.latent_format = None73        self.vae = None74        self.global_average_pooling = False75        self.timestep_range = None76        self.compression_ratio = 877        self.upscale_algorithm = 'nearest-exact'78        self.extra_args = {}79        self.previous_controlnet = None80        self.extra_conds = []81        self.strength_type = StrengthType.CONSTANT82        self.concat_mask = False83        self.extra_concat_orig = []84        self.extra_concat = None85        self.extra_hooks: HookGroup = None86        self.preprocess_image = lambda a: a87 88    def set_cond_hint(self, cond_hint, strength=1.0, timestep_percent_range=(0.0, 1.0), vae=None, extra_concat=[]):89        self.cond_hint_original = cond_hint90        self.strength = strength91        self.timestep_percent_range = timestep_percent_range92        if self.latent_format is not None:93            if vae is None:94                logging.warning("WARNING: no VAE provided to the controlnet apply node when this controlnet requires one.")95            self.vae = vae96        self.extra_concat_orig = extra_concat.copy()97        if self.concat_mask and len(self.extra_concat_orig) == 0:98            self.extra_concat_orig.append(torch.tensor([[[[1.0]]]]))99        return self100 101    def pre_run(self, model, percent_to_timestep_function):102        self.timestep_range = (percent_to_timestep_function(self.timestep_percent_range[0]), percent_to_timestep_function(self.timestep_percent_range[1]))103        if self.previous_controlnet is not None:104            self.previous_controlnet.pre_run(model, percent_to_timestep_function)105 106    def set_previous_controlnet(self, controlnet):107        self.previous_controlnet = controlnet108        return self109 110    def cleanup(self):111        if self.previous_controlnet is not None:112            self.previous_controlnet.cleanup()113 114        self.cond_hint = None115        self.extra_concat = None116        self.timestep_range = None117 118    def get_models(self):119        out = []120        if self.previous_controlnet is not None:121            out += self.previous_controlnet.get_models()122        return out123 124    def get_extra_hooks(self):125        out = []126        if self.extra_hooks is not None:127            out.append(self.extra_hooks)128        if self.previous_controlnet is not None:129            out += self.previous_controlnet.get_extra_hooks()130        return out131 132    def copy_to(self, c):133        c.cond_hint_original = self.cond_hint_original134        c.strength = self.strength135        c.timestep_percent_range = self.timestep_percent_range136        c.global_average_pooling = self.global_average_pooling137        c.compression_ratio = self.compression_ratio138        c.upscale_algorithm = self.upscale_algorithm139        c.latent_format = self.latent_format140        c.extra_args = self.extra_args.copy()141        c.vae = self.vae142        c.extra_conds = self.extra_conds.copy()143        c.strength_type = self.strength_type144        c.concat_mask = self.concat_mask145        c.extra_concat_orig = self.extra_concat_orig.copy()146        c.extra_hooks = self.extra_hooks.clone() if self.extra_hooks else None147        c.preprocess_image = self.preprocess_image148 149    def inference_memory_requirements(self, dtype):150        if self.previous_controlnet is not None:151            return self.previous_controlnet.inference_memory_requirements(dtype)152        return 0153 154    def control_merge(self, control, control_prev, output_dtype):155        out = {'input':[], 'middle':[], 'output': []}156 157        for key in control:158            control_output = control[key]159            applied_to = set()160            for i in range(len(control_output)):161                x = control_output[i]162                if x is not None:163                    if self.global_average_pooling:164                        x = torch.mean(x, dim=(2, 3), keepdim=True).repeat(1, 1, x.shape[2], x.shape[3])165 166                    if x not in applied_to: #memory saving strategy, allow shared tensors and only apply strength to shared tensors once167                        applied_to.add(x)168                        if self.strength_type == StrengthType.CONSTANT:169                            x *= self.strength170                        elif self.strength_type == StrengthType.LINEAR_UP:171                            x *= (self.strength ** float(len(control_output) - i))172 173                    if output_dtype is not None and x.dtype != output_dtype:174                        x = x.to(output_dtype)175 176                out[key].append(x)177 178        if control_prev is not None:179            for x in ['input', 'middle', 'output']:180                o = out[x]181                for i in range(len(control_prev[x])):182                    prev_val = control_prev[x][i]183                    if i >= len(o):184                        o.append(prev_val)185                    elif prev_val is not None:186                        if o[i] is None:187                            o[i] = prev_val188                        else:189                            if o[i].shape[0] < prev_val.shape[0]:190                                o[i] = prev_val + o[i]191                            else:192                                o[i] = prev_val + o[i] #TODO: change back to inplace add if shared tensors stop being an issue193        return out194 195    def set_extra_arg(self, argument, value=None):196        self.extra_args[argument] = value197 198 199class ControlNet(ControlBase):200    def __init__(self, control_model=None, global_average_pooling=False, compression_ratio=8, latent_format=None, load_device=None, manual_cast_dtype=None, extra_conds=["y"], strength_type=StrengthType.CONSTANT, concat_mask=False, preprocess_image=lambda a: a):201        super().__init__()202        self.control_model = control_model203        self.load_device = load_device204        if control_model is not None:205            self.control_model_wrapped = comfy.model_patcher.ModelPatcher(self.control_model, load_device=load_device, offload_device=comfy.model_management.unet_offload_device())206 207        self.compression_ratio = compression_ratio208        self.global_average_pooling = global_average_pooling209        self.model_sampling_current = None210        self.manual_cast_dtype = manual_cast_dtype211        self.latent_format = latent_format212        self.extra_conds += extra_conds213        self.strength_type = strength_type214        self.concat_mask = concat_mask215        self.preprocess_image = preprocess_image216 217    def get_control(self, x_noisy, t, cond, batched_number, transformer_options):218        control_prev = None219        if self.previous_controlnet is not None:220            control_prev = self.previous_controlnet.get_control(x_noisy, t, cond, batched_number, transformer_options)221 222        if self.timestep_range is not None:223            if t[0] > self.timestep_range[0] or t[0] < self.timestep_range[1]:224                if control_prev is not None:225                    return control_prev226                else:227                    return None228 229        dtype = self.control_model.dtype230        if self.manual_cast_dtype is not None:231            dtype = self.manual_cast_dtype232 233        if self.cond_hint is None or x_noisy.shape[2] * self.compression_ratio != self.cond_hint.shape[2] or x_noisy.shape[3] * self.compression_ratio != self.cond_hint.shape[3]:234            if self.cond_hint is not None:235                del self.cond_hint236            self.cond_hint = None237            compression_ratio = self.compression_ratio238            if self.vae is not None:239                compression_ratio *= self.vae.downscale_ratio240            else:241                if self.latent_format is not None:242                    raise ValueError("This Controlnet needs a VAE but none was provided, please use a ControlNetApply node with a VAE input and connect it.")243            self.cond_hint = comfy.utils.common_upscale(self.cond_hint_original, x_noisy.shape[3] * compression_ratio, x_noisy.shape[2] * compression_ratio, self.upscale_algorithm, "center")244            self.cond_hint = self.preprocess_image(self.cond_hint)245            if self.vae is not None:246                loaded_models = comfy.model_management.loaded_models(only_currently_used=True)247                self.cond_hint = self.vae.encode(self.cond_hint.movedim(1, -1))248                comfy.model_management.load_models_gpu(loaded_models)249            if self.latent_format is not None:250                self.cond_hint = self.latent_format.process_in(self.cond_hint)251            if len(self.extra_concat_orig) > 0:252                to_concat = []253                for c in self.extra_concat_orig:254                    c = c.to(self.cond_hint.device)255                    c = comfy.utils.common_upscale(c, self.cond_hint.shape[3], self.cond_hint.shape[2], self.upscale_algorithm, "center")256                    to_concat.append(comfy.utils.repeat_to_batch_size(c, self.cond_hint.shape[0]))257                self.cond_hint = torch.cat([self.cond_hint] + to_concat, dim=1)258 259            self.cond_hint = self.cond_hint.to(device=x_noisy.device, dtype=dtype)260        if x_noisy.shape[0] != self.cond_hint.shape[0]:261            self.cond_hint = broadcast_image_to(self.cond_hint, x_noisy.shape[0], batched_number)262 263        context = cond.get('crossattn_controlnet', cond['c_crossattn'])264        extra = self.extra_args.copy()265        for c in self.extra_conds:266            temp = cond.get(c, None)267            if temp is not None:268                extra[c] = temp.to(dtype)269 270        timestep = self.model_sampling_current.timestep(t)271        x_noisy = self.model_sampling_current.calculate_input(t, x_noisy)272 273        control = self.control_model(x=x_noisy.to(dtype), hint=self.cond_hint, timesteps=timestep.to(dtype), context=context.to(dtype), **extra)274        return self.control_merge(control, control_prev, output_dtype=None)275 276    def copy(self):277        c = ControlNet(None, global_average_pooling=self.global_average_pooling, load_device=self.load_device, manual_cast_dtype=self.manual_cast_dtype)278        c.control_model = self.control_model279        c.control_model_wrapped = self.control_model_wrapped280        self.copy_to(c)281        return c282 283    def get_models(self):284        out = super().get_models()285        out.append(self.control_model_wrapped)286        return out287 288    def pre_run(self, model, percent_to_timestep_function):289        super().pre_run(model, percent_to_timestep_function)290        self.model_sampling_current = model.model_sampling291 292    def cleanup(self):293        self.model_sampling_current = None294        super().cleanup()295 296class ControlLoraOps:297    class Linear(torch.nn.Module, comfy.ops.CastWeightBiasOp):298        def __init__(self, in_features: int, out_features: int, bias: bool = True,299                    device=None, dtype=None) -> None:300            super().__init__()301            self.in_features = in_features302            self.out_features = out_features303            self.weight = None304            self.up = None305            self.down = None306            self.bias = None307 308        def forward(self, input):309            weight, bias = comfy.ops.cast_bias_weight(self, input)310            if self.up is not None:311                return torch.nn.functional.linear(input, weight + (torch.mm(self.up.flatten(start_dim=1), self.down.flatten(start_dim=1))).reshape(self.weight.shape).type(input.dtype), bias)312            else:313                return torch.nn.functional.linear(input, weight, bias)314 315    class Conv2d(torch.nn.Module, comfy.ops.CastWeightBiasOp):316        def __init__(317            self,318            in_channels,319            out_channels,320            kernel_size,321            stride=1,322            padding=0,323            dilation=1,324            groups=1,325            bias=True,326            padding_mode='zeros',327            device=None,328            dtype=None329        ):330            super().__init__()331            self.in_channels = in_channels332            self.out_channels = out_channels333            self.kernel_size = kernel_size334            self.stride = stride335            self.padding = padding336            self.dilation = dilation337            self.transposed = False338            self.output_padding = 0339            self.groups = groups340            self.padding_mode = padding_mode341 342            self.weight = None343            self.bias = None344            self.up = None345            self.down = None346 347 348        def forward(self, input):349            weight, bias = comfy.ops.cast_bias_weight(self, input)350            if self.up is not None:351                return torch.nn.functional.conv2d(input, weight + (torch.mm(self.up.flatten(start_dim=1), self.down.flatten(start_dim=1))).reshape(self.weight.shape).type(input.dtype), bias, self.stride, self.padding, self.dilation, self.groups)352            else:353                return torch.nn.functional.conv2d(input, weight, bias, self.stride, self.padding, self.dilation, self.groups)354 355 356class ControlLora(ControlNet):357    def __init__(self, control_weights, global_average_pooling=False, model_options={}): #TODO? model_options358        ControlBase.__init__(self)359        self.control_weights = control_weights360        self.global_average_pooling = global_average_pooling361        self.extra_conds += ["y"]362 363    def pre_run(self, model, percent_to_timestep_function):364        super().pre_run(model, percent_to_timestep_function)365        controlnet_config = model.model_config.unet_config.copy()366        controlnet_config.pop("out_channels")367        controlnet_config["hint_channels"] = self.control_weights["input_hint_block.0.weight"].shape[1]368        self.manual_cast_dtype = model.manual_cast_dtype369        dtype = model.get_dtype()370        if self.manual_cast_dtype is None:371            class control_lora_ops(ControlLoraOps, comfy.ops.disable_weight_init):372                pass373        else:374            class control_lora_ops(ControlLoraOps, comfy.ops.manual_cast):375                pass376            dtype = self.manual_cast_dtype377 378        controlnet_config["operations"] = control_lora_ops379        controlnet_config["dtype"] = dtype380        self.control_model = comfy.cldm.cldm.ControlNet(**controlnet_config)381        self.control_model.to(comfy.model_management.get_torch_device())382        diffusion_model = model.diffusion_model383        sd = diffusion_model.state_dict()384 385        for k in sd:386            weight = sd[k]387            try:388                comfy.utils.set_attr_param(self.control_model, k, weight)389            except:390                pass391 392        for k in self.control_weights:393            if k not in {"lora_controlnet"}:394                comfy.utils.set_attr_param(self.control_model, k, self.control_weights[k].to(dtype).to(comfy.model_management.get_torch_device()))395 396    def copy(self):397        c = ControlLora(self.control_weights, global_average_pooling=self.global_average_pooling)398        self.copy_to(c)399        return c400 401    def cleanup(self):402        del self.control_model403        self.control_model = None404        super().cleanup()405 406    def get_models(self):407        out = ControlBase.get_models(self)408        return out409 410    def inference_memory_requirements(self, dtype):411        return comfy.utils.calculate_parameters(self.control_weights) * comfy.model_management.dtype_size(dtype) + ControlBase.inference_memory_requirements(self, dtype)412 413def controlnet_config(sd, model_options={}):414    model_config = comfy.model_detection.model_config_from_unet(sd, "", True)415 416    unet_dtype = model_options.get("dtype", None)417    if unet_dtype is None:418        weight_dtype = comfy.utils.weight_dtype(sd)419 420        supported_inference_dtypes = list(model_config.supported_inference_dtypes)421        unet_dtype = comfy.model_management.unet_dtype(model_params=-1, supported_dtypes=supported_inference_dtypes, weight_dtype=weight_dtype)422 423    load_device = comfy.model_management.get_torch_device()424    manual_cast_dtype = comfy.model_management.unet_manual_cast(unet_dtype, load_device)425 426    operations = model_options.get("custom_operations", None)427    if operations is None:428        operations = comfy.ops.pick_operations(unet_dtype, manual_cast_dtype, disable_fast_fp8=True)429 430    offload_device = comfy.model_management.unet_offload_device()431    return model_config, operations, load_device, unet_dtype, manual_cast_dtype, offload_device432 433def controlnet_load_state_dict(control_model, sd):434    missing, unexpected = control_model.load_state_dict(sd, strict=False)435 436    if len(missing) > 0:437        logging.warning("missing controlnet keys: {}".format(missing))438 439    if len(unexpected) > 0:440        logging.debug("unexpected controlnet keys: {}".format(unexpected))441    return control_model442 443 444def load_controlnet_mmdit(sd, model_options={}):445    new_sd = comfy.model_detection.convert_diffusers_mmdit(sd, "")446    model_config, operations, load_device, unet_dtype, manual_cast_dtype, offload_device = controlnet_config(new_sd, model_options=model_options)447    num_blocks = comfy.model_detection.count_blocks(new_sd, 'joint_blocks.{}.')448    for k in sd:449        new_sd[k] = sd[k]450 451    concat_mask = False452    control_latent_channels = new_sd.get("pos_embed_input.proj.weight").shape[1]453    if control_latent_channels == 17: #inpaint controlnet454        concat_mask = True455 456    control_model = comfy.cldm.mmdit.ControlNet(num_blocks=num_blocks, control_latent_channels=control_latent_channels, operations=operations, device=offload_device, dtype=unet_dtype, **model_config.unet_config)457    control_model = controlnet_load_state_dict(control_model, new_sd)458 459    latent_format = comfy.latent_formats.SD3()460    latent_format.shift_factor = 0 #SD3 controlnet weirdness461    control = ControlNet(control_model, compression_ratio=1, latent_format=latent_format, concat_mask=concat_mask, load_device=load_device, manual_cast_dtype=manual_cast_dtype)462    return control463 464 465class ControlNetSD35(ControlNet):466    def pre_run(self, model, percent_to_timestep_function):467        if self.control_model.double_y_emb:468            missing, unexpected = self.control_model.orig_y_embedder.load_state_dict(model.diffusion_model.y_embedder.state_dict(), strict=False)469        else:470            missing, unexpected = self.control_model.x_embedder.load_state_dict(model.diffusion_model.x_embedder.state_dict(), strict=False)471        super().pre_run(model, percent_to_timestep_function)472 473    def copy(self):474        c = ControlNetSD35(None, global_average_pooling=self.global_average_pooling, load_device=self.load_device, manual_cast_dtype=self.manual_cast_dtype)475        c.control_model = self.control_model476        c.control_model_wrapped = self.control_model_wrapped477        self.copy_to(c)478        return c479 480def load_controlnet_sd35(sd, model_options={}):481    control_type = -1482    if "control_type" in sd:483        control_type = round(sd.pop("control_type").item())484 485    # blur_cnet = control_type == 0486    canny_cnet = control_type == 1487    depth_cnet = control_type == 2488 489    new_sd = {}490    for k in comfy.utils.MMDIT_MAP_BASIC:491        if k[1] in sd:492            new_sd[k[0]] = sd.pop(k[1])493    for k in sd:494        new_sd[k] = sd[k]495    sd = new_sd496 497    y_emb_shape = sd["y_embedder.mlp.0.weight"].shape498    depth = y_emb_shape[0] // 64499    hidden_size = 64 * depth500    num_heads = depth501    head_dim = hidden_size // num_heads502    num_blocks = comfy.model_detection.count_blocks(new_sd, 'transformer_blocks.{}.')503 504    load_device = comfy.model_management.get_torch_device()505    offload_device = comfy.model_management.unet_offload_device()506    unet_dtype = comfy.model_management.unet_dtype(model_params=-1)507 508    manual_cast_dtype = comfy.model_management.unet_manual_cast(unet_dtype, load_device)509 510    operations = model_options.get("custom_operations", None)511    if operations is None:512        operations = comfy.ops.pick_operations(unet_dtype, manual_cast_dtype, disable_fast_fp8=True)513 514    control_model = comfy.cldm.dit_embedder.ControlNetEmbedder(img_size=None,515                                                               patch_size=2,516                                                               in_chans=16,517                                                               num_layers=num_blocks,518                                                               main_model_double=depth,519                                                               double_y_emb=y_emb_shape[0] == y_emb_shape[1],520                                                               attention_head_dim=head_dim,521                                                               num_attention_heads=num_heads,522                                                               adm_in_channels=2048,523                                                               device=offload_device,524                                                               dtype=unet_dtype,525                                                               operations=operations)526 527    control_model = controlnet_load_state_dict(control_model, sd)528 529    latent_format = comfy.latent_formats.SD3()530    preprocess_image = lambda a: a531    if canny_cnet:532        preprocess_image = lambda a: (a * 255 * 0.5 + 0.5)533    elif depth_cnet:534        preprocess_image = lambda a: 1.0 - a535 536    control = ControlNetSD35(control_model, compression_ratio=1, latent_format=latent_format, load_device=load_device, manual_cast_dtype=manual_cast_dtype, preprocess_image=preprocess_image)537    return control538 539 540 541def load_controlnet_hunyuandit(controlnet_data, model_options={}):542    model_config, operations, load_device, unet_dtype, manual_cast_dtype, offload_device = controlnet_config(controlnet_data, model_options=model_options)543 544    control_model = comfy.ldm.hydit.controlnet.HunYuanControlNet(operations=operations, device=offload_device, dtype=unet_dtype)545    control_model = controlnet_load_state_dict(control_model, controlnet_data)546 547    latent_format = comfy.latent_formats.SDXL()548    extra_conds = ['text_embedding_mask', 'encoder_hidden_states_t5', 'text_embedding_mask_t5', 'image_meta_size', 'style', 'cos_cis_img', 'sin_cis_img']549    control = ControlNet(control_model, compression_ratio=1, latent_format=latent_format, load_device=load_device, manual_cast_dtype=manual_cast_dtype, extra_conds=extra_conds, strength_type=StrengthType.CONSTANT)550    return control551 552def load_controlnet_flux_xlabs_mistoline(sd, mistoline=False, model_options={}):553    model_config, operations, load_device, unet_dtype, manual_cast_dtype, offload_device = controlnet_config(sd, model_options=model_options)554    control_model = comfy.ldm.flux.controlnet.ControlNetFlux(mistoline=mistoline, operations=operations, device=offload_device, dtype=unet_dtype, **model_config.unet_config)555    control_model = controlnet_load_state_dict(control_model, sd)556    extra_conds = ['y', 'guidance']557    control = ControlNet(control_model, load_device=load_device, manual_cast_dtype=manual_cast_dtype, extra_conds=extra_conds)558    return control559 560def load_controlnet_flux_instantx(sd, model_options={}):561    new_sd = comfy.model_detection.convert_diffusers_mmdit(sd, "")562    model_config, operations, load_device, unet_dtype, manual_cast_dtype, offload_device = controlnet_config(new_sd, model_options=model_options)563    for k in sd:564        new_sd[k] = sd[k]565 566    num_union_modes = 0567    union_cnet = "controlnet_mode_embedder.weight"568    if union_cnet in new_sd:569        num_union_modes = new_sd[union_cnet].shape[0]570 571    control_latent_channels = new_sd.get("pos_embed_input.weight").shape[1] // 4572    concat_mask = False573    if control_latent_channels == 17:574        concat_mask = True575 576    control_model = comfy.ldm.flux.controlnet.ControlNetFlux(latent_input=True, num_union_modes=num_union_modes, control_latent_channels=control_latent_channels, operations=operations, device=offload_device, dtype=unet_dtype, **model_config.unet_config)577    control_model = controlnet_load_state_dict(control_model, new_sd)578 579    latent_format = comfy.latent_formats.Flux()580    extra_conds = ['y', 'guidance']581    control = ControlNet(control_model, compression_ratio=1, latent_format=latent_format, concat_mask=concat_mask, load_device=load_device, manual_cast_dtype=manual_cast_dtype, extra_conds=extra_conds)582    return control583 584def convert_mistoline(sd):585    return comfy.utils.state_dict_prefix_replace(sd, {"single_controlnet_blocks.": "controlnet_single_blocks."})586 587 588def load_controlnet_state_dict(state_dict, model=None, model_options={}):589    controlnet_data = state_dict590    if 'after_proj_list.18.bias' in controlnet_data.keys(): #Hunyuan DiT591        return load_controlnet_hunyuandit(controlnet_data, model_options=model_options)592 593    if "lora_controlnet" in controlnet_data:594        return ControlLora(controlnet_data, model_options=model_options)595 596    controlnet_config = None597    supported_inference_dtypes = None598 599    if "controlnet_cond_embedding.conv_in.weight" in controlnet_data: #diffusers format600        controlnet_config = comfy.model_detection.unet_config_from_diffusers_unet(controlnet_data)601        diffusers_keys = comfy.utils.unet_to_diffusers(controlnet_config)602        diffusers_keys["controlnet_mid_block.weight"] = "middle_block_out.0.weight"603        diffusers_keys["controlnet_mid_block.bias"] = "middle_block_out.0.bias"604 605        count = 0606        loop = True607        while loop:608            suffix = [".weight", ".bias"]609            for s in suffix:610                k_in = "controlnet_down_blocks.{}{}".format(count, s)611                k_out = "zero_convs.{}.0{}".format(count, s)612                if k_in not in controlnet_data:613                    loop = False614                    break615                diffusers_keys[k_in] = k_out616            count += 1617 618        count = 0619        loop = True620        while loop:621            suffix = [".weight", ".bias"]622            for s in suffix:623                if count == 0:624                    k_in = "controlnet_cond_embedding.conv_in{}".format(s)625                else:626                    k_in = "controlnet_cond_embedding.blocks.{}{}".format(count - 1, s)627                k_out = "input_hint_block.{}{}".format(count * 2, s)628                if k_in not in controlnet_data:629                    k_in = "controlnet_cond_embedding.conv_out{}".format(s)630                    loop = False631                diffusers_keys[k_in] = k_out632            count += 1633 634        new_sd = {}635        for k in diffusers_keys:636            if k in controlnet_data:637                new_sd[diffusers_keys[k]] = controlnet_data.pop(k)638 639        if "control_add_embedding.linear_1.bias" in controlnet_data: #Union Controlnet640            controlnet_config["union_controlnet_num_control_type"] = controlnet_data["task_embedding"].shape[0]641            for k in list(controlnet_data.keys()):642                new_k = k.replace('.attn.in_proj_', '.attn.in_proj.')643                new_sd[new_k] = controlnet_data.pop(k)644 645        leftover_keys = controlnet_data.keys()646        if len(leftover_keys) > 0:647            logging.warning("leftover keys: {}".format(leftover_keys))648        controlnet_data = new_sd649    elif "controlnet_blocks.0.weight" in controlnet_data:650        if "double_blocks.0.img_attn.norm.key_norm.scale" in controlnet_data:651            return load_controlnet_flux_xlabs_mistoline(controlnet_data, model_options=model_options)652        elif "pos_embed_input.proj.weight" in controlnet_data:653            if "transformer_blocks.0.adaLN_modulation.1.bias" in controlnet_data:654                return load_controlnet_sd35(controlnet_data, model_options=model_options) #Stability sd3.5 format655            else:656                return load_controlnet_mmdit(controlnet_data, model_options=model_options) #SD3 diffusers controlnet657        elif "controlnet_x_embedder.weight" in controlnet_data:658            return load_controlnet_flux_instantx(controlnet_data, model_options=model_options)659    elif "controlnet_blocks.0.linear.weight" in controlnet_data: #mistoline flux660        return load_controlnet_flux_xlabs_mistoline(convert_mistoline(controlnet_data), mistoline=True, model_options=model_options)661 662    pth_key = 'control_model.zero_convs.0.0.weight'663    pth = False664    key = 'zero_convs.0.0.weight'665    if pth_key in controlnet_data:666        pth = True667        key = pth_key668        prefix = "control_model."669    elif key in controlnet_data:670        prefix = ""671    else:672        net = load_t2i_adapter(controlnet_data, model_options=model_options)673        if net is None:674            logging.error("error could not detect control model type.")675        return net676 677    if controlnet_config is None:678        model_config = comfy.model_detection.model_config_from_unet(controlnet_data, prefix, True)679        supported_inference_dtypes = list(model_config.supported_inference_dtypes)680        controlnet_config = model_config.unet_config681 682    unet_dtype = model_options.get("dtype", None)683    if unet_dtype is None:684        weight_dtype = comfy.utils.weight_dtype(controlnet_data)685 686        if supported_inference_dtypes is None:687            supported_inference_dtypes = [comfy.model_management.unet_dtype()]688 689        unet_dtype = comfy.model_management.unet_dtype(model_params=-1, supported_dtypes=supported_inference_dtypes, weight_dtype=weight_dtype)690 691    load_device = comfy.model_management.get_torch_device()692 693    manual_cast_dtype = comfy.model_management.unet_manual_cast(unet_dtype, load_device)694    operations = model_options.get("custom_operations", None)695    if operations is None:696        operations = comfy.ops.pick_operations(unet_dtype, manual_cast_dtype)697 698    controlnet_config["operations"] = operations699    controlnet_config["dtype"] = unet_dtype700    controlnet_config["device"] = comfy.model_management.unet_offload_device()701    controlnet_config.pop("out_channels")702    controlnet_config["hint_channels"] = controlnet_data["{}input_hint_block.0.weight".format(prefix)].shape[1]703    control_model = comfy.cldm.cldm.ControlNet(**controlnet_config)704 705    if pth:706        if 'difference' in controlnet_data:707            if model is not None:708                comfy.model_management.load_models_gpu([model])709                model_sd = model.model_state_dict()710                for x in controlnet_data:711                    c_m = "control_model."712                    if x.startswith(c_m):713                        sd_key = "diffusion_model.{}".format(x[len(c_m):])714                        if sd_key in model_sd:715                            cd = controlnet_data[x]716                            cd += model_sd[sd_key].type(cd.dtype).to(cd.device)717            else:718                logging.warning("WARNING: Loaded a diff controlnet without a model. It will very likely not work.")719 720        class WeightsLoader(torch.nn.Module):721            pass722        w = WeightsLoader()723        w.control_model = control_model724        missing, unexpected = w.load_state_dict(controlnet_data, strict=False)725    else:726        missing, unexpected = control_model.load_state_dict(controlnet_data, strict=False)727 728    if len(missing) > 0:729        logging.warning("missing controlnet keys: {}".format(missing))730 731    if len(unexpected) > 0:732        logging.debug("unexpected controlnet keys: {}".format(unexpected))733 734    global_average_pooling = model_options.get("global_average_pooling", False)735    control = ControlNet(control_model, global_average_pooling=global_average_pooling, load_device=load_device, manual_cast_dtype=manual_cast_dtype)736    return control737 738def load_controlnet(ckpt_path, model=None, model_options={}):739    if "global_average_pooling" not in model_options:740        filename = os.path.splitext(ckpt_path)[0]741        if filename.endswith("_shuffle") or filename.endswith("_shuffle_fp16"): #TODO: smarter way of enabling global_average_pooling742            model_options["global_average_pooling"] = True743 744    cnet = load_controlnet_state_dict(comfy.utils.load_torch_file(ckpt_path, safe_load=True), model=model, model_options=model_options)745    if cnet is None:746        logging.error("error checkpoint does not contain controlnet or t2i adapter data {}".format(ckpt_path))747    return cnet748 749class T2IAdapter(ControlBase):750    def __init__(self, t2i_model, channels_in, compression_ratio, upscale_algorithm, device=None):751        super().__init__()752        self.t2i_model = t2i_model753        self.channels_in = channels_in754        self.control_input = None755        self.compression_ratio = compression_ratio756        self.upscale_algorithm = upscale_algorithm757        if device is None:758            device = comfy.model_management.get_torch_device()759        self.device = device760 761    def scale_image_to(self, width, height):762        unshuffle_amount = self.t2i_model.unshuffle_amount763        width = math.ceil(width / unshuffle_amount) * unshuffle_amount764        height = math.ceil(height / unshuffle_amount) * unshuffle_amount765        return width, height766 767    def get_control(self, x_noisy, t, cond, batched_number, transformer_options):768        control_prev = None769        if self.previous_controlnet is not None:770            control_prev = self.previous_controlnet.get_control(x_noisy, t, cond, batched_number, transformer_options)771 772        if self.timestep_range is not None:773            if t[0] > self.timestep_range[0] or t[0] < self.timestep_range[1]:774                if control_prev is not None:775                    return control_prev776                else:777                    return None778 779        if self.cond_hint is None or x_noisy.shape[2] * self.compression_ratio != self.cond_hint.shape[2] or x_noisy.shape[3] * self.compression_ratio != self.cond_hint.shape[3]:780            if self.cond_hint is not None:781                del self.cond_hint782            self.control_input = None783            self.cond_hint = None784            width, height = self.scale_image_to(x_noisy.shape[3] * self.compression_ratio, x_noisy.shape[2] * self.compression_ratio)785            self.cond_hint = comfy.utils.common_upscale(self.cond_hint_original, width, height, self.upscale_algorithm, "center").float().to(self.device)786            if self.channels_in == 1 and self.cond_hint.shape[1] > 1:787                self.cond_hint = torch.mean(self.cond_hint, 1, keepdim=True)788        if x_noisy.shape[0] != self.cond_hint.shape[0]:789            self.cond_hint = broadcast_image_to(self.cond_hint, x_noisy.shape[0], batched_number)790        if self.control_input is None:791            self.t2i_model.to(x_noisy.dtype)792            self.t2i_model.to(self.device)793            self.control_input = self.t2i_model(self.cond_hint.to(x_noisy.dtype))794            self.t2i_model.cpu()795 796        control_input = {}797        for k in self.control_input:798            control_input[k] = list(map(lambda a: None if a is None else a.clone(), self.control_input[k]))799 800        return self.control_merge(control_input, control_prev, x_noisy.dtype)801 802    def copy(self):803        c = T2IAdapter(self.t2i_model, self.channels_in, self.compression_ratio, self.upscale_algorithm)804        self.copy_to(c)805        return c806 807def load_t2i_adapter(t2i_data, model_options={}): #TODO: model_options808    compression_ratio = 8809    upscale_algorithm = 'nearest-exact'810 811    if 'adapter' in t2i_data:812        t2i_data = t2i_data['adapter']813    if 'adapter.body.0.resnets.0.block1.weight' in t2i_data: #diffusers format814        prefix_replace = {}815        for i in range(4):816            for j in range(2):817                prefix_replace["adapter.body.{}.resnets.{}.".format(i, j)] = "body.{}.".format(i * 2 + j)818            prefix_replace["adapter.body.{}.".format(i, )] = "body.{}.".format(i * 2)819        prefix_replace["adapter."] = ""820        t2i_data = comfy.utils.state_dict_prefix_replace(t2i_data, prefix_replace)821    keys = t2i_data.keys()822 823    if "body.0.in_conv.weight" in keys:824        cin = t2i_data['body.0.in_conv.weight'].shape[1]825        model_ad = comfy.t2i_adapter.adapter.Adapter_light(cin=cin, channels=[320, 640, 1280, 1280], nums_rb=4)826    elif 'conv_in.weight' in keys:827        cin = t2i_data['conv_in.weight'].shape[1]828        channel = t2i_data['conv_in.weight'].shape[0]829        ksize = t2i_data['body.0.block2.weight'].shape[2]830        use_conv = False831        down_opts = list(filter(lambda a: a.endswith("down_opt.op.weight"), keys))832        if len(down_opts) > 0:833            use_conv = True834        xl = False835        if cin == 256 or cin == 768:836            xl = True837        model_ad = comfy.t2i_adapter.adapter.Adapter(cin=cin, channels=[channel, channel*2, channel*4, channel*4][:4], nums_rb=2, ksize=ksize, sk=True, use_conv=use_conv, xl=xl)838    elif "backbone.0.0.weight" in keys:839        model_ad = comfy.ldm.cascade.controlnet.ControlNet(c_in=t2i_data['backbone.0.0.weight'].shape[1], proj_blocks=[0, 4, 8, 12, 51, 55, 59, 63])840        compression_ratio = 32841        upscale_algorithm = 'bilinear'842    elif "backbone.10.blocks.0.weight" in keys:843        model_ad = comfy.ldm.cascade.controlnet.ControlNet(c_in=t2i_data['backbone.0.weight'].shape[1], bottleneck_mode="large", proj_blocks=[0, 4, 8, 12, 51, 55, 59, 63])844        compression_ratio = 1845        upscale_algorithm = 'nearest-exact'846    else:847        return None848 849    missing, unexpected = model_ad.load_state_dict(t2i_data)850    if len(missing) > 0:851        logging.warning("t2i missing {}".format(missing))852 853    if len(unexpected) > 0:854        logging.debug("t2i unexpected {}".format(unexpected))855 856    return T2IAdapter(model_ad, model_ad.input_channels, compression_ratio, upscale_algorithm)857