diff --git a/adv_control/control.py b/adv_control/control.py index 4eb0d18..715d725 100644 --- a/adv_control/control.py +++ b/adv_control/control.py @@ -8,7 +8,7 @@ import comfy.utils import comfy.model_management import comfy.model_detection import comfy.controlnet as comfy_cn -from comfy.controlnet import ControlBase, ControlNet, ControlLora, T2IAdapter +from comfy.controlnet import ControlBase, ControlNet, ControlLora, T2IAdapter, StrengthType from comfy.model_patcher import ModelPatcher from .control_sparsectrl import SparseModelPatcher, SparseControlNet, SparseCtrlMotionWrapper, SparseSettings, SparseConst @@ -21,13 +21,23 @@ from .logger import logger class ControlNetAdvanced(ControlNet, AdvancedControlBase): - def __init__(self, control_model, timestep_keyframes: TimestepKeyframeGroup, global_average_pooling=False, compression_ratio=8, latent_format=None, device=None, load_device=None, manual_cast_dtype=None): + def __init__(self, control_model, timestep_keyframes: TimestepKeyframeGroup, global_average_pooling=False, compression_ratio=8, latent_format=None, device=None, load_device=None, manual_cast_dtype=None, extra_conds=["y"], strength_type=StrengthType.CONSTANT): super().__init__(control_model=control_model, global_average_pooling=global_average_pooling, compression_ratio=compression_ratio, latent_format=latent_format, device=device, load_device=load_device, manual_cast_dtype=manual_cast_dtype) AdvancedControlBase.__init__(self, super(), timestep_keyframes=timestep_keyframes, weights_default=ControlWeights.controlnet()) + self.is_flux = False + self.x_noisy_shape = None def get_universal_weights(self) -> ControlWeights: - raw_weights = [(self.weights.base_multiplier ** float(12 - i)) for i in range(13)] - return self.weights.copy_with_new_weights(raw_weights) + def cn_weights_func(idx: int, control: dict[str, list[Tensor]], key: str): + if key == "middle": + return 1.0 + c_len = len(control[key]) + raw_weights = [(self.weights.base_multiplier ** float((c_len) - i)) for i in range(c_len+1)] + raw_weights = raw_weights[:-1] + if key == "input": + raw_weights.reverse() + return raw_weights[idx] + return self.weights.copy_with_new_weights(new_weight_func=cn_weights_func) def get_control_advanced(self, x_noisy, t, cond, batched_number): # perform special version of get_control that supports sliding context and masks @@ -49,7 +59,6 @@ class ControlNetAdvanced(ControlNet, AdvancedControlBase): if self.manual_cast_dtype is not None: dtype = self.manual_cast_dtype - output_dtype = x_noisy.dtype # make cond_hint appropriate dimensions # TODO: change this to not require cond_hint upscaling every step when self.sub_idxs are present if self.sub_idxs is not None or 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]: @@ -64,9 +73,9 @@ class ControlNetAdvanced(ControlNet, AdvancedControlBase): actual_cond_hint_orig = self.cond_hint_original if self.cond_hint_original.size(0) < self.full_latent_length: actual_cond_hint_orig = extend_to_batch_size(tensor=actual_cond_hint_orig, batch_size=self.full_latent_length) - self.cond_hint = comfy.utils.common_upscale(actual_cond_hint_orig[self.sub_idxs], x_noisy.shape[3] * compression_ratio, x_noisy.shape[2] * compression_ratio, 'nearest-exact', "center") + self.cond_hint = comfy.utils.common_upscale(actual_cond_hint_orig[self.sub_idxs], x_noisy.shape[3] * compression_ratio, x_noisy.shape[2] * compression_ratio, self.upscale_algorithm, "center") else: - self.cond_hint = comfy.utils.common_upscale(self.cond_hint_original, x_noisy.shape[3] * compression_ratio, x_noisy.shape[2] * compression_ratio, 'nearest-exact', "center") + 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") if self.vae is not None: loaded_models = comfy.model_management.loaded_models(only_currently_used=True) self.cond_hint = self.vae.encode(self.cond_hint.movedim(1, -1)) @@ -81,25 +90,44 @@ class ControlNetAdvanced(ControlNet, AdvancedControlBase): self.prepare_mask_cond_hint(x_noisy=x_noisy, t=t, cond=cond, batched_number=batched_number, dtype=dtype) context = cond.get('crossattn_controlnet', cond['c_crossattn']) - y = cond.get('y', None) - if y is not None: - y = y.to(dtype) + extra = self.extra_args.copy() + for c in self.extra_conds: + temp = cond.get(c, None) + if temp is not None: + extra[c] = temp.to(dtype) + timestep = self.model_sampling_current.timestep(t) x_noisy = self.model_sampling_current.calculate_input(t, x_noisy) + self.x_noisy_shape = x_noisy.shape + control = self.control_model(x=x_noisy.to(dtype), hint=self.cond_hint, timesteps=timestep.to(dtype), context=context.to(dtype), **extra) + return self.control_merge(control, control_prev, output_dtype=None) - control = self.control_model(x=x_noisy.to(dtype), hint=self.cond_hint, timesteps=timestep.float(), context=context.to(dtype), y=y) - return self.control_merge(control, control_prev, output_dtype) + def pre_run_advanced(self, *args, **kwargs): + self.is_flux = "Flux" in str(type(self.control_model).__name__) + return super().pre_run_advanced(*args, **kwargs) + + def apply_advanced_strengths_and_masks(self, x: Tensor, batched_number: int, flux_shape=None): + if self.is_flux: + flux_shape = self.x_noisy_shape + return super().apply_advanced_strengths_and_masks(x, batched_number, flux_shape) def copy(self): c = ControlNetAdvanced(self.control_model, self.timestep_keyframes, global_average_pooling=self.global_average_pooling, load_device=self.load_device, manual_cast_dtype=self.manual_cast_dtype) + c.control_model = self.control_model + c.control_model_wrapped = self.control_model_wrapped self.copy_to(c) self.copy_to_advanced(c) return c + def cleanup_advanced(self): + self.x_noisy_shape = None + return super().cleanup_advanced() + @staticmethod def from_vanilla(v: ControlNet, timestep_keyframe: TimestepKeyframeGroup=None) -> 'ControlNetAdvanced': to_return = ControlNetAdvanced(control_model=v.control_model, timestep_keyframes=timestep_keyframe, - global_average_pooling=v.global_average_pooling, compression_ratio=v.compression_ratio, latent_format=v.latent_format, device=v.device, load_device=v.load_device, manual_cast_dtype=v.manual_cast_dtype) + global_average_pooling=v.global_average_pooling, compression_ratio=v.compression_ratio, latent_format=v.latent_format, device=v.device, load_device=v.load_device, + manual_cast_dtype=v.manual_cast_dtype) v.copy_to(to_return) return to_return @@ -121,18 +149,28 @@ class T2IAdapterAdvanced(T2IAdapter, AdvancedControlBase): return AdvancedControlBase.control_merge_inject(self, control, control_prev, output_dtype) def get_universal_weights(self) -> ControlWeights: - raw_weights = [(self.weights.base_multiplier ** float(7 - i)) for i in range(8)] - raw_weights = [raw_weights[-8], raw_weights[-3], raw_weights[-2], raw_weights[-1]] - raw_weights = get_properly_arranged_t2i_weights(raw_weights) - raw_weights.reverse() # need to reverse to match recent ComfyUI changes - return self.weights.copy_with_new_weights(raw_weights) + def t2i_weights_func(idx: int, control: dict[str, list[Tensor]], key: str): + if key == "middle": + return 1.0 + c_len = 8 #len(control[key]) + raw_weights = [(self.weights.base_multiplier ** float((c_len-1) - i)) for i in range(c_len)] + raw_weights = [raw_weights[-c_len], raw_weights[-3], raw_weights[-2], raw_weights[-1]] + raw_weights = get_properly_arranged_t2i_weights(raw_weights) + if key == "input": + raw_weights.reverse() + return raw_weights[idx] + return self.weights.copy_with_new_weights(new_weight_func=t2i_weights_func) def get_calc_pow(self, idx: int, control: dict[str, list[Tensor]], key: str) -> int: + if key == "middle": + return 0 # match how T2IAdapterAdvanced deals with universal weights - indeces = [7 - i for i in range(8)] - indeces = [indeces[-8], indeces[-3], indeces[-2], indeces[-1]] + c_len = 8 #len(control[key]) + indeces = [(c_len-1) - i for i in range(c_len)] + indeces = [indeces[-c_len], indeces[-3], indeces[-2], indeces[-1]] indeces = get_properly_arranged_t2i_weights(indeces) - indeces.reverse() # need to reverse to match recent ComfyUI changes + if key == "input": + indeces.reverse() # need to reverse to match recent ComfyUI changes return indeces[idx] def get_control_advanced(self, x_noisy, t, cond, batched_number): @@ -381,11 +419,11 @@ class SparseCtrlAdvanced(ControlNetAdvanced): control = self.control_model(x=x_noisy.to(dtype), hint=self.cond_hint, timesteps=timestep.float(), context=context.to(dtype), y=y) return self.control_merge(control, control_prev, output_dtype) - def apply_advanced_strengths_and_masks(self, x: Tensor, batched_number: int): + def apply_advanced_strengths_and_masks(self, x: Tensor, batched_number: int, *args, **kwargs): # apply mults to indexes with and without a direct condhint x[self.local_sparse_idxs] *= self.sparse_settings.sparse_hint_mult * self.weights.extras.get(SparseConst.HINT_MULT, 1.0) x[self.local_sparse_idxs_inverse] *= self.sparse_settings.sparse_nonhint_mult * self.weights.extras.get(SparseConst.NONHINT_MULT, 1.0) - return super().apply_advanced_strengths_and_masks(x, batched_number) + return super().apply_advanced_strengths_and_masks(x, batched_number, *args, **kwargs) def pre_run_advanced(self, model, percent_to_timestep_function): super().pre_run_advanced(model, percent_to_timestep_function) diff --git a/adv_control/control_plusplus.py b/adv_control/control_plusplus.py index a6f5ca9..cb5c80b 100644 --- a/adv_control/control_plusplus.py +++ b/adv_control/control_plusplus.py @@ -237,9 +237,16 @@ class ControlNetPlusPlusAdvanced(ControlNet, AdvancedControlBase): self.single_control_type: str = None def get_universal_weights(self) -> ControlWeights: - # TODO: match actual layer count of model - raw_weights = [(self.weights.base_multiplier ** float(12 - i)) for i in range(13)] - return self.weights.copy_with_new_weights(raw_weights) + def cn_weights_func(idx: int, control: dict[str, list[Tensor]], key: str): + if key == "middle": + return 1.0 + c_len = len(control[key]) + raw_weights = [(self.weights.base_multiplier ** float((c_len) - i)) for i in range(c_len+1)] + raw_weights = raw_weights[:-1] + if key == "input": + raw_weights.reverse() + return raw_weights[idx] + return self.weights.copy_with_new_weights(new_weight_func=cn_weights_func) def verify_control_type(self, model_name: str, pp_group: PlusPlusInputGroup=None): if pp_group is not None: diff --git a/adv_control/nodes.py b/adv_control/nodes.py index 55c2db2..9846f93 100644 --- a/adv_control/nodes.py +++ b/adv_control/nodes.py @@ -7,15 +7,18 @@ from comfy.model_patcher import ModelPatcher from .control import load_controlnet, convert_to_advanced, is_advanced_controlnet, is_sd3_advanced_controlnet from .utils import ControlWeights, LatentKeyframeGroup, TimestepKeyframeGroup, AbstractPreprocWrapper, BIGMAX -from .nodes_weight import (DefaultWeights, ScaledSoftMaskedUniversalWeights, ScaledSoftUniversalWeights, SoftControlNetWeights, CustomControlNetWeights, - SoftT2IAdapterWeights, CustomT2IAdapterWeights) +from .nodes_weight import (DefaultWeights, ScaledSoftMaskedUniversalWeights, ScaledSoftUniversalWeights, + SoftControlNetWeightsSD15, CustomControlNetWeightsSD15, CustomControlNetWeightsFlux, + SoftT2IAdapterWeights, CustomT2IAdapterWeights) from .nodes_keyframes import (LatentKeyframeGroupNode, LatentKeyframeInterpolationNode, LatentKeyframeBatchedGroupNode, LatentKeyframeNode, TimestepKeyframeNode, TimestepKeyframeInterpolationNode, TimestepKeyframeFromStrengthListNode) from .nodes_sparsectrl import SparseCtrlMergedLoaderAdvanced, SparseCtrlLoaderAdvanced, SparseIndexMethodNode, SparseSpreadMethodNode, RgbSparseCtrlPreprocessor, SparseWeightExtras from .nodes_reference import ReferenceControlNetNode, ReferenceControlFinetune, ReferencePreprocessorNode from .nodes_plusplus import PlusPlusLoaderAdvanced, PlusPlusLoaderSingle, PlusPlusInputNode from .nodes_loosecontrol import ControlNetLoaderWithLoraAdvanced -from .nodes_deprecated import LoadImagesFromDirectory +from .nodes_deprecated import (LoadImagesFromDirectory, ScaledSoftUniversalWeightsDeprecated, + SoftControlNetWeightsDeprecated, CustomControlNetWeightsDeprecated, + SoftT2IAdapterWeightsDeprecated, CustomT2IAdapterWeightsDeprecated) from .logger import logger from .sampling import acn_sample_factory @@ -104,7 +107,7 @@ class AdvancedControlNetApply: "weights_override": ("CONTROL_NET_WEIGHTS", ), "model_optional": ("MODEL",), "vae_optional": ("VAE",), - "autosize": ("ACNAUTOSIZE", {"padding": 40}), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), } } @@ -117,7 +120,7 @@ class AdvancedControlNetApply: def apply_controlnet(self, positive, negative, control_net, image, strength, start_percent, end_percent, mask_optional: Tensor=None, model_optional: ModelPatcher=None, vae_optional=None, timestep_kf: TimestepKeyframeGroup=None, latent_kf_override: LatentKeyframeGroup=None, - weights_override: ControlWeights=None): + weights_override: ControlWeights=None, control_apply_to_uncond=False): if strength == 0: return (positive, negative, model_optional) if model_optional: @@ -129,59 +132,101 @@ class AdvancedControlNetApply: out = [] for conditioning in [positive, negative]: c = [] - for t in conditioning: - d = t[1].copy() + if conditioning is not None: + for t in conditioning: + d = t[1].copy() - prev_cnet = d.get('control', None) - if prev_cnet in cnets: - c_net = cnets[prev_cnet] - else: - # copy, convert to advanced if needed, and set cond - c_net = convert_to_advanced(control_net.copy()).set_cond_hint(control_hint, strength, (start_percent, end_percent), vae_optional) - if is_advanced_controlnet(c_net): - # disarm node check - c_net.disarm() - # if model required, verify model is passed in, and if so patch it - if c_net.require_model: - if not model_optional: - raise Exception(f"Type '{type(c_net).__name__}' requires model_optional input, but got None.") - c_net.patch_model(model=model_optional) - # if vae required, verify vae is passed in - if c_net.require_vae: - # if controlnet can accept preprocced condhint latents and is the case, ignore vae requirement - if c_net.allow_condhint_latents and isinstance(control_hint, AbstractPreprocWrapper): - pass - elif not vae_optional: - # make sure SD3 ControlNet will get a special message instead of generic type mention - if is_sd3_advanced_controlnet: - raise Exception(f"SD3 ControlNet requires vae_optional input, but got None.") - else: - raise Exception(f"Type '{type(c_net).__name__}' requires vae_optional input, but got None.") - # apply optional parameters and overrides, if provided - if timestep_kf is not None: - c_net.set_timestep_keyframes(timestep_kf) - if latent_kf_override is not None: - c_net.latent_keyframe_override = latent_kf_override - if weights_override is not None: - c_net.weights_override = weights_override - # verify weights are compatible - c_net.verify_all_weights() - # set cond hint mask - if mask_optional is not None: - mask_optional = mask_optional.clone() - # if not in the form of a batch, make it so - if len(mask_optional.shape) < 3: - mask_optional = mask_optional.unsqueeze(0) - c_net.set_cond_hint_mask(mask_optional) - c_net.set_previous_controlnet(prev_cnet) - cnets[prev_cnet] = c_net + prev_cnet = d.get('control', None) + if prev_cnet in cnets: + c_net = cnets[prev_cnet] + else: + # copy, convert to advanced if needed, and set cond + c_net = convert_to_advanced(control_net.copy()).set_cond_hint(control_hint, strength, (start_percent, end_percent), vae_optional) + if is_advanced_controlnet(c_net): + # disarm node check + c_net.disarm() + # if model required, verify model is passed in, and if so patch it + if c_net.require_model: + if not model_optional: + raise Exception(f"Type '{type(c_net).__name__}' requires model_optional input, but got None.") + c_net.patch_model(model=model_optional) + # if vae required, verify vae is passed in + if c_net.require_vae: + # if controlnet can accept preprocced condhint latents and is the case, ignore vae requirement + if c_net.allow_condhint_latents and isinstance(control_hint, AbstractPreprocWrapper): + pass + elif not vae_optional: + # make sure SD3 ControlNet will get a special message instead of generic type mention + if is_sd3_advanced_controlnet: + raise Exception(f"SD3 ControlNet requires vae_optional input, but got None.") + else: + raise Exception(f"Type '{type(c_net).__name__}' requires vae_optional input, but got None.") + # apply optional parameters and overrides, if provided + if timestep_kf is not None: + c_net.set_timestep_keyframes(timestep_kf) + if latent_kf_override is not None: + c_net.latent_keyframe_override = latent_kf_override + if weights_override is not None: + c_net.weights_override = weights_override + # verify weights are compatible + c_net.verify_all_weights() + # set cond hint mask + if mask_optional is not None: + mask_optional = mask_optional.clone() + # if not in the form of a batch, make it so + if len(mask_optional.shape) < 3: + mask_optional = mask_optional.unsqueeze(0) + c_net.set_cond_hint_mask(mask_optional) + c_net.set_previous_controlnet(prev_cnet) + cnets[prev_cnet] = c_net - d['control'] = c_net - d['control_apply_to_uncond'] = False - n = [t[0], d] - c.append(n) + d['control'] = c_net + d['control_apply_to_uncond'] = control_apply_to_uncond + n = [t[0], d] + c.append(n) out.append(c) return (out[0], out[1], model_optional) + + +class AdvancedControlNetApplySingle: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "conditioning": ("CONDITIONING", ), + "control_net": ("CONTROL_NET", ), + "image": ("IMAGE", ), + "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), + "end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}) + }, + "optional": { + "mask_optional": ("MASK", ), + "timestep_kf": ("TIMESTEP_KEYFRAME", ), + "latent_kf_override": ("LATENT_KEYFRAME", ), + "weights_override": ("CONTROL_NET_WEIGHTS", ), + "model_optional": ("MODEL",), + "vae_optional": ("VAE",), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), + } + } + + RETURN_TYPES = ("CONDITIONING","MODEL",) + RETURN_NAMES = ("CONDITIONING", "model_opt") + FUNCTION = "apply_controlnet" + + CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝" + + def apply_controlnet(self, conditioning, control_net, image, strength, start_percent, end_percent, + mask_optional: Tensor=None, model_optional: ModelPatcher=None, vae_optional=None, + timestep_kf: TimestepKeyframeGroup=None, latent_kf_override: LatentKeyframeGroup=None, + weights_override: ControlWeights=None): + values = AdvancedControlNetApply.apply_controlnet(self, positive=conditioning, negative=None, control_net=control_net, image=image, + strength=strength, start_percent=start_percent, end_percent=end_percent, + mask_optional=mask_optional, model_optional=model_optional, vae_optional=vae_optional, + timestep_kf=timestep_kf, latent_kf_override=latent_kf_override, weights_override=weights_override, + control_apply_to_uncond=True) + return (values[0], values[2]) # NODE MAPPING @@ -196,16 +241,18 @@ NODE_CLASS_MAPPINGS = { "LatentKeyframeGroup": LatentKeyframeGroupNode, # Conditioning "ACN_AdvancedControlNetApply": AdvancedControlNetApply, + "ACN_AdvancedControlNetApplySingle": AdvancedControlNetApplySingle, # Loaders "ControlNetLoaderAdvanced": ControlNetLoaderAdvanced, "DiffControlNetLoaderAdvanced": DiffControlNetLoaderAdvanced, # Weights - "ScaledSoftControlNetWeights": ScaledSoftUniversalWeights, + "ACN_ScaledSoftControlNetWeights": ScaledSoftUniversalWeights, "ScaledSoftMaskedUniversalWeights": ScaledSoftMaskedUniversalWeights, - "SoftControlNetWeights": SoftControlNetWeights, - "CustomControlNetWeights": CustomControlNetWeights, - "SoftT2IAdapterWeights": SoftT2IAdapterWeights, - "CustomT2IAdapterWeights": CustomT2IAdapterWeights, + "ACN_SoftControlNetWeightsSD15": SoftControlNetWeightsSD15, + "ACN_CustomControlNetWeightsSD15": CustomControlNetWeightsSD15, + "ACN_CustomControlNetWeightsFlux": CustomControlNetWeightsFlux, + "ACN_SoftT2IAdapterWeights": SoftT2IAdapterWeights, + "ACN_CustomT2IAdapterWeights": CustomT2IAdapterWeights, "ACN_DefaultUniversalWeights": DefaultWeights, # SparseCtrl "ACN_SparseCtrlRGBPreprocessor": RgbSparseCtrlPreprocessor, @@ -226,6 +273,11 @@ NODE_CLASS_MAPPINGS = { #"ACN_ControlNetLoaderWithLoraAdvanced": ControlNetLoaderWithLoraAdvanced, # Deprecated "LoadImagesFromDirectory": LoadImagesFromDirectory, + "ScaledSoftControlNetWeights": ScaledSoftUniversalWeightsDeprecated, + "SoftControlNetWeights": SoftControlNetWeightsDeprecated, + "CustomControlNetWeights": CustomControlNetWeightsDeprecated, + "SoftT2IAdapterWeights": SoftT2IAdapterWeightsDeprecated, + "CustomT2IAdapterWeights": CustomT2IAdapterWeightsDeprecated, } NODE_DISPLAY_NAME_MAPPINGS = { @@ -239,16 +291,18 @@ NODE_DISPLAY_NAME_MAPPINGS = { "LatentKeyframeGroup": "Latent Keyframe Group 🛂🅐🅒🅝", # Conditioning "ACN_AdvancedControlNetApply": "Apply Advanced ControlNet 🛂🅐🅒🅝", + "ACN_AdvancedControlNetApplySingle": "Apply Advanced ControlNet(1) 🛂🅐🅒🅝", # Loaders "ControlNetLoaderAdvanced": "Load Advanced ControlNet Model 🛂🅐🅒🅝", "DiffControlNetLoaderAdvanced": "Load Advanced ControlNet Model (diff) 🛂🅐🅒🅝", # Weights - "ScaledSoftControlNetWeights": "Scaled Soft Weights 🛂🅐🅒🅝", + "ACN_ScaledSoftControlNetWeights": "Scaled Soft Weights 🛂🅐🅒🅝", "ScaledSoftMaskedUniversalWeights": "Scaled Soft Masked Weights 🛂🅐🅒🅝", - "SoftControlNetWeights": "ControlNet Soft Weights 🛂🅐🅒🅝", - "CustomControlNetWeights": "ControlNet Custom Weights 🛂🅐🅒🅝", - "SoftT2IAdapterWeights": "T2IAdapter Soft Weights 🛂🅐🅒🅝", - "CustomT2IAdapterWeights": "T2IAdapter Custom Weights 🛂🅐🅒🅝", + "ACN_SoftControlNetWeightsSD15": "ControlNet Soft Weights [SD1.5] 🛂🅐🅒🅝", + "ACN_CustomControlNetWeightsSD15": "ControlNet Custom Weights [SD1.5] 🛂🅐🅒🅝", + "ACN_CustomControlNetWeightsFlux": "ControlNet Custom Weights [Flux] 🛂🅐🅒🅝", + "ACN_SoftT2IAdapterWeights": "T2IAdapter Soft Weights 🛂🅐🅒🅝", + "ACN_CustomT2IAdapterWeights": "T2IAdapter Custom Weights 🛂🅐🅒🅝", "ACN_DefaultUniversalWeights": "Default Weights 🛂🅐🅒🅝", # SparseCtrl "ACN_SparseCtrlRGBPreprocessor": "RGB SparseCtrl 🛂🅐🅒🅝", @@ -269,4 +323,9 @@ NODE_DISPLAY_NAME_MAPPINGS = { #"ACN_ControlNetLoaderWithLoraAdvanced": "Load Adv. ControlNet Model w/ LoRA 🛂🅐🅒🅝", # Deprecated "LoadImagesFromDirectory": "🚫Load Images [DEPRECATED] 🛂🅐🅒🅝", + "ScaledSoftControlNetWeights": "Scaled Soft Weights 🛂🅐🅒🅝", + "SoftControlNetWeights": "ControlNet Soft Weights 🛂🅐🅒🅝", + "CustomControlNetWeights": "ControlNet Custom Weights 🛂🅐🅒🅝", + "SoftT2IAdapterWeights": "T2IAdapter Soft Weights 🛂🅐🅒🅝", + "CustomT2IAdapterWeights": "T2IAdapter Custom Weights 🛂🅐🅒🅝", } diff --git a/adv_control/nodes_deprecated.py b/adv_control/nodes_deprecated.py index bd98607..bd1d2f0 100644 --- a/adv_control/nodes_deprecated.py +++ b/adv_control/nodes_deprecated.py @@ -4,7 +4,7 @@ import torch import numpy as np from PIL import Image, ImageOps -from .utils import BIGMAX +from .utils import BIGMAX, ControlWeights, TimestepKeyframeGroup, TimestepKeyframe, get_properly_arranged_t2i_weights from .logger import logger @@ -69,3 +69,183 @@ class LoadImagesFromDirectory: raise FileNotFoundError(f"No images could be loaded from directory '{directory}'.") return (torch.cat(images, dim=0), torch.stack(masks, dim=0), image_count) + + +class ScaledSoftUniversalWeightsDeprecated: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "base_multiplier": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 1.0, "step": 0.001}, ), + "flip_weights": ("BOOLEAN", {"default": False}), + }, + "optional": { + "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), + "cn_extras": ("CN_WEIGHTS_EXTRAS",), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), + } + } + + RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) + RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT") + FUNCTION = "load_weights" + + CATEGORY = "" + + def load_weights(self, base_multiplier, flip_weights, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): + weights = ControlWeights.universal(base_multiplier=base_multiplier, uncond_multiplier=uncond_multiplier, extras=cn_extras) + return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + + +class SoftControlNetWeightsDeprecated: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "weight_00": ("FLOAT", {"default": 0.09941396206337118, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_01": ("FLOAT", {"default": 0.12050177219802567, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_02": ("FLOAT", {"default": 0.14606275417942507, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_03": ("FLOAT", {"default": 0.17704576264172736, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_04": ("FLOAT", {"default": 0.214600924414215, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_05": ("FLOAT", {"default": 0.26012233262329093, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_06": ("FLOAT", {"default": 0.3152997971191405, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_07": ("FLOAT", {"default": 0.3821815722656249, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_08": ("FLOAT", {"default": 0.4632503906249999, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_09": ("FLOAT", {"default": 0.561515625, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_10": ("FLOAT", {"default": 0.6806249999999999, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_11": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_12": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "flip_weights": ("BOOLEAN", {"default": False}), + }, + "optional": { + "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), + "cn_extras": ("CN_WEIGHTS_EXTRAS",), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), + } + } + + DEPRECATED = True + RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) + RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT") + FUNCTION = "load_weights" + + CATEGORY = "" + + def load_weights(self, weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, + weight_07, weight_08, weight_09, weight_10, weight_11, weight_12, flip_weights, + uncond_multiplier: float=1.0, cn_extras: dict[str]={}): + weights_output = [weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, + weight_07, weight_08, weight_09, weight_10, weight_11] + weights_middle = [weight_12] + weights = ControlWeights.controlnet(weights_output=weights_output, weights_middle=weights_middle, uncond_multiplier=uncond_multiplier, extras=cn_extras) + return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + + +class CustomControlNetWeightsDeprecated: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "weight_00": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_01": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_02": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_03": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_04": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_05": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_06": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_07": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_08": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_09": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_10": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_11": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_12": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "flip_weights": ("BOOLEAN", {"default": False}), + }, + "optional": { + "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), + "cn_extras": ("CN_WEIGHTS_EXTRAS",), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), + } + } + + DEPRECATED = True + RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) + RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT") + FUNCTION = "load_weights" + + CATEGORY = "" + + def load_weights(self, weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, + weight_07, weight_08, weight_09, weight_10, weight_11, weight_12, flip_weights, + uncond_multiplier: float=1.0, cn_extras: dict[str]={}): + weights_output = [weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, + weight_07, weight_08, weight_09, weight_10, weight_11] + weights_middle = [weight_12] + weights = ControlWeights.controlnet(weights_output=weights_output, weights_middle=weights_middle, uncond_multiplier=uncond_multiplier, extras=cn_extras) + return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + + +class SoftT2IAdapterWeightsDeprecated: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "weight_00": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_01": ("FLOAT", {"default": 0.62, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_02": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_03": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "flip_weights": ("BOOLEAN", {"default": False}), + }, + "optional": { + "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), + "cn_extras": ("CN_WEIGHTS_EXTRAS",), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), + } + } + + DEPRECATED = True + RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) + RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT") + FUNCTION = "load_weights" + + CATEGORY = "" + + def load_weights(self, weight_00, weight_01, weight_02, weight_03, flip_weights, + uncond_multiplier: float=1.0, cn_extras: dict[str]={}): + weights = [weight_00, weight_01, weight_02, weight_03] + weights = get_properly_arranged_t2i_weights(weights) + weights = ControlWeights.t2iadapter(weights_input=weights, uncond_multiplier=uncond_multiplier, extras=cn_extras) + return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + + +class CustomT2IAdapterWeightsDeprecated: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "weight_00": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_01": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_02": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "weight_03": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "flip_weights": ("BOOLEAN", {"default": False}), + }, + "optional": { + "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), + "cn_extras": ("CN_WEIGHTS_EXTRAS",), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), + } + } + + DEPRECATED = True + RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) + RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT") + FUNCTION = "load_weights" + + CATEGORY = "" + + def load_weights(self, weight_00, weight_01, weight_02, weight_03, flip_weights, + uncond_multiplier: float=1.0, cn_extras: dict[str]={}): + weights = [weight_00, weight_01, weight_02, weight_03] + weights = get_properly_arranged_t2i_weights(weights) + weights = ControlWeights.t2iadapter(weights_input=weights, uncond_multiplier=uncond_multiplier, extras=cn_extras) + return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) diff --git a/adv_control/nodes_weight.py b/adv_control/nodes_weight.py index 5b9bd04..09c3dee 100644 --- a/adv_control/nodes_weight.py +++ b/adv_control/nodes_weight.py @@ -42,7 +42,7 @@ class ScaledSoftMaskedUniversalWeights: "optional": { "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), "cn_extras": ("CN_WEIGHTS_EXTRAS",), - "autosize": ("ACNAUTOSIZE", {"padding": 75}), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), } } @@ -72,12 +72,11 @@ class ScaledSoftUniversalWeights: return { "required": { "base_multiplier": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 1.0, "step": 0.001}, ), - "flip_weights": ("BOOLEAN", {"default": False}), }, "optional": { "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), "cn_extras": ("CN_WEIGHTS_EXTRAS",), - "autosize": ("ACNAUTOSIZE", {"padding": 20}), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), } } @@ -87,77 +86,34 @@ class ScaledSoftUniversalWeights: CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights" - def load_weights(self, base_multiplier, flip_weights, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): - weights = ControlWeights.universal(base_multiplier=base_multiplier, flip_weights=flip_weights, uncond_multiplier=uncond_multiplier, extras=cn_extras) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) - - -class SoftControlNetWeights: - @classmethod - def INPUT_TYPES(s): - return { - "required": { - "weight_00": ("FLOAT", {"default": 0.09941396206337118, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_01": ("FLOAT", {"default": 0.12050177219802567, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_02": ("FLOAT", {"default": 0.14606275417942507, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_03": ("FLOAT", {"default": 0.17704576264172736, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_04": ("FLOAT", {"default": 0.214600924414215, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_05": ("FLOAT", {"default": 0.26012233262329093, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_06": ("FLOAT", {"default": 0.3152997971191405, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_07": ("FLOAT", {"default": 0.3821815722656249, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_08": ("FLOAT", {"default": 0.4632503906249999, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_09": ("FLOAT", {"default": 0.561515625, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_10": ("FLOAT", {"default": 0.6806249999999999, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_11": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_12": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "flip_weights": ("BOOLEAN", {"default": False}), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - "autosize": ("ACNAUTOSIZE", {"padding": 50}), - } - } - - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = WEIGHTS_RETURN_NAMES - FUNCTION = "load_weights" - - CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet" - - def load_weights(self, weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, - weight_07, weight_08, weight_09, weight_10, weight_11, weight_12, flip_weights, - uncond_multiplier: float=1.0, cn_extras: dict[str]={}): - weights = [weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, - weight_07, weight_08, weight_09, weight_10, weight_11, weight_12] - weights = ControlWeights.controlnet(weights, flip_weights=flip_weights, uncond_multiplier=uncond_multiplier, extras=cn_extras) + def load_weights(self, base_multiplier, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): + weights = ControlWeights.universal(base_multiplier=base_multiplier, uncond_multiplier=uncond_multiplier, extras=cn_extras) return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) -class CustomControlNetWeights: +class SoftControlNetWeightsSD15: @classmethod def INPUT_TYPES(s): return { "required": { - "weight_00": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_01": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_02": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_03": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_04": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_05": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_06": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_07": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_08": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_09": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_10": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_11": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_12": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "flip_weights": ("BOOLEAN", {"default": False}), + "output_0": ("FLOAT", {"default": 0.09941396206337118, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_1": ("FLOAT", {"default": 0.12050177219802567, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_2": ("FLOAT", {"default": 0.14606275417942507, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_3": ("FLOAT", {"default": 0.17704576264172736, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_4": ("FLOAT", {"default": 0.214600924414215, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_5": ("FLOAT", {"default": 0.26012233262329093, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_6": ("FLOAT", {"default": 0.3152997971191405, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_7": ("FLOAT", {"default": 0.3821815722656249, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_8": ("FLOAT", {"default": 0.4632503906249999, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_9": ("FLOAT", {"default": 0.561515625, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_10": ("FLOAT", {"default": 0.6806249999999999, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_11": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "middle_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), }, "optional": { "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), "cn_extras": ("CN_WEIGHTS_EXTRAS",), - "autosize": ("ACNAUTOSIZE", {"padding": 65}), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), } } @@ -167,12 +123,105 @@ class CustomControlNetWeights: CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet" - def load_weights(self, weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, - weight_07, weight_08, weight_09, weight_10, weight_11, weight_12, flip_weights, + def load_weights(self, output_0, output_1, output_2, output_3, output_4, output_5, output_6, + output_7, output_8, output_9, output_10, output_11, middle_0, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): - weights = [weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, - weight_07, weight_08, weight_09, weight_10, weight_11, weight_12] - weights = ControlWeights.controlnet(weights, flip_weights=flip_weights, uncond_multiplier=uncond_multiplier, extras=cn_extras) + return CustomControlNetWeightsSD15.load_weights(self, + output_0=output_0, output_1=output_1, output_2=output_2, output_3=output_3, + output_4=output_4, output_5=output_5, output_6=output_6, output_7=output_7, + output_8=output_8, output_9=output_9, output_10=output_10, output_11=output_11, + middle_0=middle_0, + uncond_multiplier=uncond_multiplier, cn_extras=cn_extras) + + +class CustomControlNetWeightsSD15: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "output_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_4": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_5": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_6": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_7": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_8": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_9": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_10": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "output_11": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "middle_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + }, + "optional": { + "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), + "cn_extras": ("CN_WEIGHTS_EXTRAS",), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), + } + } + + RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) + RETURN_NAMES = WEIGHTS_RETURN_NAMES + FUNCTION = "load_weights" + + CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet" + + def load_weights(self, output_0, output_1, output_2, output_3, output_4, output_5, output_6, + output_7, output_8, output_9, output_10, output_11, middle_0, + uncond_multiplier: float=1.0, cn_extras: dict[str]={}): + weights_output = [output_0, output_1, output_2, output_3, output_4, output_5, output_6, + output_7, output_8, output_9, output_10, output_11] + weights_middle = [middle_0] + weights = ControlWeights.controlnet(weights_output=weights_output, weights_middle=weights_middle, uncond_multiplier=uncond_multiplier, extras=cn_extras) + return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + + +class CustomControlNetWeightsFlux: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "input_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_4": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_5": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_6": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_7": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_8": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_9": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_10": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_11": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_12": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_13": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_14": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_15": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_16": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_17": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_18": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + }, + "optional": { + "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), + "cn_extras": ("CN_WEIGHTS_EXTRAS",), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), + } + } + + RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) + RETURN_NAMES = WEIGHTS_RETURN_NAMES + FUNCTION = "load_weights" + + CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet" + + def load_weights(self, input_0, input_1, input_2, input_3, input_4, input_5, input_6, + input_7, input_8, input_9, input_10, input_11, input_12, input_13, + input_14, input_15, input_16, input_17, input_18, + uncond_multiplier: float=1.0, cn_extras: dict[str]={}): + weights_input = [input_0, input_1, input_2, input_3, input_4, input_5, + input_6, input_7, input_8, input_9, input_10, input_11, + input_12, input_13, input_14, input_15, input_16, input_17, input_18] + weights = ControlWeights.controlnet(weights_input=weights_input, uncond_multiplier=uncond_multiplier, extras=cn_extras) return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) @@ -181,16 +230,15 @@ class SoftT2IAdapterWeights: def INPUT_TYPES(s): return { "required": { - "weight_00": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_01": ("FLOAT", {"default": 0.62, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_02": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_03": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "flip_weights": ("BOOLEAN", {"default": False}), + "input_0": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_1": ("FLOAT", {"default": 0.62, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_2": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), }, "optional": { "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), "cn_extras": ("CN_WEIGHTS_EXTRAS",), - "autosize": ("ACNAUTOSIZE", {"padding": 55}), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), } } @@ -200,13 +248,10 @@ class SoftT2IAdapterWeights: CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/T2IAdapter" - def load_weights(self, weight_00, weight_01, weight_02, weight_03, flip_weights, + def load_weights(self, input_0, input_1, input_2, input_3, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): - weights = [weight_00, weight_01, weight_02, weight_03] - weights = get_properly_arranged_t2i_weights(weights) - weights.reverse() # to account for recent ComfyUI changes - weights = ControlWeights.t2iadapter(weights, flip_weights=flip_weights, uncond_multiplier=uncond_multiplier, extras=cn_extras) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + return CustomT2IAdapterWeights.load_weights(self, input_0=input_0, input_1=input_1, input_2=input_2, input_3=input_3, + uncond_multiplier=uncond_multiplier, cn_extras=cn_extras) class CustomT2IAdapterWeights: @@ -214,16 +259,15 @@ class CustomT2IAdapterWeights: def INPUT_TYPES(s): return { "required": { - "weight_00": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_01": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_02": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_03": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "flip_weights": ("BOOLEAN", {"default": False}), + "input_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), + "input_3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), }, "optional": { "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), "cn_extras": ("CN_WEIGHTS_EXTRAS",), - "autosize": ("ACNAUTOSIZE", {"padding": 65}), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), } } @@ -233,10 +277,9 @@ class CustomT2IAdapterWeights: CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/T2IAdapter" - def load_weights(self, weight_00, weight_01, weight_02, weight_03, flip_weights, + def load_weights(self, input_0, input_1, input_2, input_3, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): - weights = [weight_00, weight_01, weight_02, weight_03] + weights = [input_0, input_1, input_2, input_3] weights = get_properly_arranged_t2i_weights(weights) - weights.reverse() # to account for recent ComfyUI changes - weights = ControlWeights.t2iadapter(weights, flip_weights=flip_weights, uncond_multiplier=uncond_multiplier, extras=cn_extras) + weights = ControlWeights.t2iadapter(weights_input=weights, uncond_multiplier=uncond_multiplier, extras=cn_extras) return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) diff --git a/adv_control/utils.py b/adv_control/utils.py index b71e371..9c7c1bb 100644 --- a/adv_control/utils.py +++ b/adv_control/utils.py @@ -3,6 +3,7 @@ from typing import Callable, Union import torch from torch import Tensor import torch.nn.functional +from einops import rearrange import numpy as np import math @@ -157,14 +158,16 @@ class ControlWeightType: class ControlWeights: - def __init__(self, weight_type: str, base_multiplier: float=1.0, flip_weights: bool=False, weights: list[float]=None, weight_mask: Tensor=None, + def __init__(self, weight_type: str, base_multiplier: float=1.0, + weights_input: list[float]=None, weights_middle: list[float]=None, weights_output: list[float]=None, + weight_func: Callable=None, weight_mask: Tensor=None, uncond_multiplier=1.0, uncond_mask: Tensor=None, extras: dict[str]={},): self.weight_type = weight_type self.base_multiplier = base_multiplier - self.flip_weights = flip_weights - self.weights = weights - if self.weights is not None and self.flip_weights: - self.weights.reverse() + self.weights_input = weights_input + self.weights_middle = weights_middle + self.weights_output = weights_output + self.weight_func = weight_func self.weight_mask = weight_mask self.uncond_multiplier = float(uncond_multiplier) self.has_uncond_multiplier = not math.isclose(self.uncond_multiplier, 1.0) @@ -173,63 +176,59 @@ class ControlWeights: self.extras = extras def get(self, idx: int, control: dict[str, list[Tensor]], key: str, default=1.0) -> Union[float, Tensor]: + # if weight_func present, use it + if self.weight_func is not None: + return self.weight_func(idx=idx, control=control, key=key) # if weights is not none, return index - if self.weights is not None: - # if middle weight, need to pretend index is actually after all the output weights (if applicable) - if key == "middle" and "output" in control: - idx += len(control["output"]) + relevant_weights = None + if key == "middle": + relevant_weights = self.weights_middle + elif key == "input": + relevant_weights = self.weights_input + if relevant_weights is not None: + relevant_weights = list(reversed(relevant_weights)) + else: + relevant_weights = self.weights_output + if relevant_weights is None: + return default + elif idx >= len(relevant_weights): + return default + return relevant_weights[idx] - if key == "output": - # this implies weights list is not aligning with expectations - will need to adjust code - if idx >= len(self.weights)-1: - return default - else: - # this implies weights list is not aligning with expectations - will need to adjust code - if idx >= len(self.weights): - return default - return self.weights[idx] - return 1.0 - - def copy_with_new_weights(self, new_weights: list[float]): - return ControlWeights(weight_type=self.weight_type, base_multiplier=self.base_multiplier, flip_weights=self.flip_weights, - weights=new_weights, weight_mask=self.weight_mask, uncond_multiplier=self.uncond_multiplier, extras=self.extras) + def copy_with_new_weights(self, new_weights_input: list[float]=None, new_weights_middle: list[float]=None, new_weights_output: list[float]=None, + new_weight_func: Callable=None): + return ControlWeights(weight_type=self.weight_type, base_multiplier=self.base_multiplier, + weights_input=new_weights_input, weights_middle=new_weights_middle, weights_output=new_weights_output, + weight_func=new_weight_func, weight_mask=self.weight_mask, + uncond_multiplier=self.uncond_multiplier, extras=self.extras) @classmethod def default(cls, extras: dict[str]={}): return cls(ControlWeightType.DEFAULT, extras=extras) @classmethod - def universal(cls, base_multiplier: float, flip_weights: bool=False, uncond_multiplier: float=1.0, extras: dict[str]={}): - return cls(ControlWeightType.UNIVERSAL, base_multiplier=base_multiplier, flip_weights=flip_weights, uncond_multiplier=uncond_multiplier, extras=extras) + def universal(cls, base_multiplier: float, uncond_multiplier: float=1.0, extras: dict[str]={}): + return cls(ControlWeightType.UNIVERSAL, base_multiplier=base_multiplier, uncond_multiplier=uncond_multiplier, extras=extras) @classmethod def universal_mask(cls, weight_mask: Tensor, uncond_multiplier: float=1.0, extras: dict[str]={}): return cls(ControlWeightType.UNIVERSAL, weight_mask=weight_mask, uncond_multiplier=uncond_multiplier, extras=extras) @classmethod - def t2iadapter(cls, weights: list[float]=None, flip_weights: bool=False, uncond_multiplier: float=1.0, extras: dict[str]={}): - if weights is None: - weights = [1.0]*12 - return cls(ControlWeightType.T2IADAPTER, weights=weights,flip_weights=flip_weights, uncond_multiplier=uncond_multiplier, extras=extras) + def t2iadapter(cls, weights_input: list[float]=None, uncond_multiplier: float=1.0, extras: dict[str]={}): + return cls(ControlWeightType.T2IADAPTER, weights_input=weights_input, uncond_multiplier=uncond_multiplier, extras=extras) @classmethod - def controlnet(cls, weights: list[float]=None, flip_weights: bool=False, uncond_multiplier: float=1.0, extras: dict[str]={}): - if weights is None: - weights = [1.0]*13 - return cls(ControlWeightType.CONTROLNET, weights=weights, flip_weights=flip_weights, uncond_multiplier=uncond_multiplier, extras=extras) + def controlnet(cls, weights_output: list[float]=None, weights_middle: list[float]=None, weights_input: list[float]=None, uncond_multiplier: float=1.0, extras: dict[str]={}): + return cls(ControlWeightType.CONTROLNET, weights_output=weights_output, weights_middle=weights_middle, weights_input=weights_input, uncond_multiplier=uncond_multiplier, extras=extras) @classmethod - def controllora(cls, weights: list[float]=None, flip_weights: bool=False, uncond_multiplier: float=1.0, extras: dict[str]={}): - if weights is None: - weights = [1.0]*10 - return cls(ControlWeightType.CONTROLLORA, weights=weights, flip_weights=flip_weights, uncond_multiplier=uncond_multiplier, extras=extras) + def controllora(cls, weights_output: list[float]=None, weights_middle: list[float]=None, weights_input: list[float]=None, uncond_multiplier: float=1.0, extras: dict[str]={}): + return cls(ControlWeightType.CONTROLLORA, weights_output=weights_output, weights_middle=weights_middle, weights_input=weights_input, uncond_multiplier=uncond_multiplier, extras=extras) @classmethod - def controllllite(cls, weights: list[float]=None, flip_weights: bool=False, uncond_multiplier: float=1.0, extras: dict[str]={}): - if weights is None: - # TODO: make this have a real value - weights = [1.0]*200 - return cls(ControlWeightType.CONTROLLLLITE, weights=weights, flip_weights=flip_weights, uncond_multiplier=uncond_multiplier, extras=extras) + def controllllite(cls, weights_output: list[float]=None, weights_middle: list[float]=None, weights_input: list[float]=None, uncond_multiplier: float=1.0, extras: dict[str]={}): + return cls(ControlWeightType.CONTROLLLLITE, weights_output=weights_output, weights_middle=weights_middle, weights_input=weights_input, uncond_multiplier=uncond_multiplier, extras=extras) class StrengthInterpolation: @@ -434,14 +433,19 @@ class manual_cast_clean_groupnorm(comfy.ops.manual_cast): # adapted from comfy/sample.py -def prepare_mask_batch(mask: Tensor, shape: Tensor, multiplier: int=1, match_dim1=False, match_shape=False): +def prepare_mask_batch(mask: Tensor, shape: Tensor, multiplier: int=1, match_dim1=False, match_shape=False, flux_shape=None): mask = mask.clone() - mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(shape[-2]*multiplier, shape[-1]*multiplier), mode="bilinear") + if flux_shape is not None: + multiplier = multiplier * 0.5 + mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(round(flux_shape[-2]*multiplier), round(flux_shape[-1]*multiplier)), mode="bilinear") + mask = rearrange(mask, "b c h w -> b (h w) c") + else: + mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(round(shape[-2]*multiplier), round(shape[-1]*multiplier)), mode="bilinear") if match_dim1: if match_shape and len(shape) < 4: raise Exception(f"match_dim1 cannot be True if shape is under 4 dims; was {len(shape)}.") mask = torch.cat([mask] * shape[1], dim=1) - if match_shape and len(shape) == 3: + if match_shape and len(shape) == 3 and len(mask.shape) != 3: mask = mask.squeeze(1) return mask @@ -775,11 +779,14 @@ class AdvancedControlBase: return self.weights.get(idx=idx, control=control, key=key) def get_calc_pow(self, idx: int, control: dict[str, list[Tensor]], key: str) -> int: - c_len = len(control[key])-1 - if key == "output": - if "middle" in control: - c_len += len(control["middle"]) - return c_len-idx + if key == "middle": + return 0 + else: + c_len = len(control[key]) + real_idx = c_len-idx + if key == "input": + real_idx = c_len - real_idx + 1 + return real_idx def calc_latent_keyframe_mults(self, x: Tensor, batched_number: int) -> Tensor: # apply strengths, and get batch indeces to null out @@ -829,7 +836,7 @@ class AdvancedControlBase: final_tensor = final_tensor.unsqueeze(-1) return final_tensor - def apply_advanced_strengths_and_masks(self, x: Tensor, batched_number: int): + def apply_advanced_strengths_and_masks(self, x: Tensor, batched_number: int, flux_shape: tuple=None): # handle weight's uncond_multiplier, if applicable if self.weights.has_uncond_multiplier: cond_or_uncond = self.batched_number.cond_or_uncond @@ -845,10 +852,10 @@ class AdvancedControlBase: x[:] = x[:] * self.calc_latent_keyframe_mults(x=x, batched_number=batched_number) # apply masks, resizing mask to required dims if self.mask_cond_hint is not None: - masks = prepare_mask_batch(self.mask_cond_hint, x.shape, match_shape=True) + masks = prepare_mask_batch(self.mask_cond_hint, x.shape, match_shape=True, flux_shape=flux_shape) x[:] = x[:] * masks if self.tk_mask_cond_hint is not None: - masks = prepare_mask_batch(self.tk_mask_cond_hint, x.shape, match_shape=True) + masks = prepare_mask_batch(self.tk_mask_cond_hint, x.shape, match_shape=True, flux_shape=flux_shape) x[:] = x[:] * masks # apply timestep keyframe strengths if self._current_timestep_keyframe.strength != 1.0: @@ -871,7 +878,7 @@ class AdvancedControlBase: self.apply_advanced_strengths_and_masks(x, self.batched_number) x *= self.strength * self.calc_weight(i, x, control, key) - if x.dtype != output_dtype: + if output_dtype is not None and x.dtype != output_dtype: x = x.to(output_dtype) out[key].append(x) @@ -913,7 +920,7 @@ class AdvancedControlBase: del out_mask # TODO: perform upscale on only the sub_idxs masks at a time instead of all to conserve RAM # resize mask and match batch count - out_mask = prepare_mask_batch(orig_mask, x_noisy.shape, multiplier=multiplier) + out_mask = prepare_mask_batch(orig_mask, x_noisy.shape, multiplier=multiplier, match_shape=True) actual_latent_length = x_noisy.shape[0] // batched_number out_mask = extend_to_batch_size(out_mask, actual_latent_length if self.sub_idxs is None else self.full_latent_length) if self.sub_idxs is not None: diff --git a/pyproject.toml b/pyproject.toml index 9d1085a..f8eba83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui-advanced-controlnet" description = "Nodes for scheduling ControlNet strength across timesteps and batched latents, as well as applying custom weights and attention masks." -version = "1.2.1" +version = "1.2.2" license = { file = "LICENSE" } dependencies = []