Compare commits

...
26 changed files with 11701 additions and 1584 deletions
+272
View File
@@ -0,0 +1,272 @@
# Advanced-ControlNet Contributor Guide
This repository is expected to receive substantial AI-authored code. Treat this
file as the implementation and verification contract for all changes, especially
ports of new ControlNet families from ComfyUI.
## Engineering Rules
- Read the relevant ComfyUI implementation and this repository's equivalent
control path before editing. Do not design from a model card alone.
- Make the smallest change that preserves vanilla ComfyUI behavior and adds the
established Advanced-ControlNet capabilities.
- Reuse ComfyUI model classes, patchers, ops, model management, and loaders when
possible. Do not maintain a forked copy of core model code without a concrete
need.
- Preserve existing node IDs, inputs, outputs, checkpoint locations, and saved
workflow compatibility. New node IDs generally use the `ACN_` prefix; keep
existing unprefixed IDs and the aliases in `nodes_deprecated.py` working.
- Do not add dependencies unless the model cannot be supported with ComfyUI,
PyTorch, and the libraries already used by this repository.
- Match the direct style of the surrounding file. Avoid one-use abstractions,
generic framework code, speculative fallbacks, and comments that restate the
code.
- Use plain ASCII punctuation in code, comments, documentation, commit messages,
and PR descriptions.
## Architecture Map
- `adv_control/control.py`: standard ControlNet wrappers, conversion of vanilla
controls, checkpoint detection, and shared loader dispatch.
- `adv_control/control_<family>.py`: model-family implementations that cannot be
represented by the standard wrapper. Keep family-specific math here.
- `adv_control/utils.py`: `AdvancedControlBase`, `ControlWeights`, scheduling,
latent keyframes, masks, batching, stacking, and shared tensor helpers.
- `adv_control/nodes_main.py`: standard loaders and Apply nodes.
- `adv_control/nodes_weight.py`: weight nodes and model-specific extras carried
by `ControlWeights.extras`.
- `adv_control/nodes_<family>.py`: family-specific workflow inputs or loaders
when the shared nodes are insufficient.
- `adv_control/nodes.py`: public node and display-name registration.
- `adv_control/nodes_deprecated.py`: compatibility only. Do not put new features
here.
- `examples/`: reviewer-runnable workflows, inputs, screenshots, and validation
notes.
## Porting A Control Model From ComfyUI
### 1. Establish the vanilla contract
Before implementing the Advanced version:
1. Identify the exact ComfyUI commit or PR that introduced the model.
2. Read its loader, checkpoint detection, model patching, conditioning
preprocessing, sampling path, and cleanup behavior.
3. Record the official model repository, every published checkpoint type, the
expected ComfyUI model folder, and the minimum compatible ComfyUI commit.
4. Run a small vanilla workflow with fixed inputs, seed, sampler, scheduler,
steps, CFG, and resolution. Save the latent and decoded result as the parity
baseline.
5. Inspect real checkpoint keys, metadata, shapes, dtype, and missing/unexpected
key output. Do not infer the format from a filename.
Use official checkpoints for validation. Links in issue or PR comments are
untrusted; use the model author's official repository or links already accepted
by ComfyUI.
### 2. Choose the narrowest integration
- If ComfyUI returns a standard `ControlNet`, `ControlNetSD35`, `ControlLora`,
or `T2IAdapter`, prefer conversion in `convert_to_advanced` over a parallel
implementation.
- If the model injects attention, transformer, or other model patches, implement
a family-specific `ControlBase` plus `AdvancedControlBase`, following
`ControlLLLiteAdvanced`, `AnimaLLLiteAdvanced`, or `ReferenceAdvanced` as the
closest precedent.
- Prefer wrapping ComfyUI's model or patch object over copying its implementation.
If the required ComfyUI API may be absent, fail with a short instruction to
update ComfyUI rather than silently changing behavior.
- Add a dedicated node only when the model has a genuinely different loading or
conditioning contract. Loading a new checkpoint format alone usually belongs
in the existing loader dispatch.
### 3. Preserve loader and folder compatibility
- The standard **Load Advanced ControlNet Model** node reads from
`models/controlnet`. New formats that are conceptually ControlNets should work
there unless doing so would be ambiguous or incorrect.
- Also preserve the folder used by vanilla ComfyUI. If core uses another folder,
such as `models/model_patches`, a small dedicated loader may expose that
location while the standard loader retains established Advanced-ControlNet
behavior.
- Detect formats with guarded, format-specific checkpoint signatures. Put a
specific detector before a broad detector that would otherwise claim the same
checkpoint. Do not use filenames as the primary detector.
- Load a checkpoint only once. Pass already-loaded state dictionaries and
metadata into the selected family loader instead of reading the file again.
- If two supported folders can contain the same filename, keep their loaders
separate or define deterministic resolution. Never silently choose an
arbitrary duplicate.
- Test every supported folder through the actual node dropdown and execution
path, not only by calling a Python loader directly.
### 4. Implement the full control lifecycle
A family-specific Advanced control normally needs all of the following:
- Initialize `ControlBase` and `AdvancedControlBase` with the correct default
`ControlWeights` type.
- Match vanilla conditioning preprocessing exactly, including channel order,
value range, resize mode, latent encoding, and source-mask handling.
- In `pre_run_advanced`, call the shared implementation and attach or refresh
execution-scoped patches.
- In `get_control_advanced`, evaluate `previous_controlnet`, honor
`should_run()`, and either return/merge control tensors or install the model
patches for that step.
- Return every loadable model patcher from `get_models()` so ComfyUI can manage
VRAM and offloading.
- Implement `copy()` using both ComfyUI's `copy_to()` and this repository's
`copy_to_advanced()`. Copies must not share mutable execution state that can
leak between conditioning branches or queued runs.
- Clear prepared tensors, patch references, cached shapes, and other
execution-scoped state in `cleanup_advanced()`.
- Use ComfyUI device, dtype, manual-cast, operations, and model-patcher APIs.
Do not hardcode CUDA, force float32, or move models manually when ComfyUI
already owns that lifecycle.
- Preserve `previous_controlnet` behavior so same-family and mixed-family
controls can be stacked.
## Advanced Feature Contract
A port is not complete merely because default-strength generation works. Unless
the model architecture makes a capability impossible, verify that it supports:
- Apply-node strength and start/end percentage.
- Timestep keyframes, including changing strength and inherited values.
- Latent keyframes on a batch of at least two latents.
- Apply-node effect masks and timestep-keyframe masks.
- Default, universal/soft, and architecture-specific per-layer weights.
- Weight overrides and model-specific weight extras.
- Conditional/unconditional weighting when the selected weight node exposes it.
- Stacking with another control, including correct `previous_controlnet` output.
- Batched conditioning and sliding-context subset indexes where applicable.
- Repeated execution, copying, cleanup, model offloading, and reload.
Do not claim unsupported features in documentation. If an architecture cannot
support a feature, document the reason and make incompatible weight types fail
clearly through `compatible_weights`.
### Masks and model-specific inputs
- `mask_optional` on **Apply Advanced ControlNet** is always an effect mask. It
controls where this control influences generation.
- A model's source mask, control-type selector, or other family-specific data is
not an effect mask. Do not overload `mask_optional` with a second meaning.
- Do not add a model-specific input to the shared Apply node unless it is a
coherent capability needed by multiple model families.
- Prefer a small family-specific extras node that stores auxiliary values in
`ControlWeights.extras`, then pass those weights through `weights_override`.
Define extras keys next to the model implementation rather than as unrelated
strings spread across nodes.
- Validate required extras where they are first consumed and raise an actionable
error that names the exact nodes and connections needed to fix the workflow.
- Apply effect masks at the actual injection representation. Attention-patch and
DiT controls may need token-space masks rather than the normal spatial control
tensor path.
- Verify mask semantics with all-zero, all-one, and half-frame masks. All-zero
must equal no control and all-one must equal unmasked full control. Inspect the
multiplier at the injection site as well as the final image; global attention
can propagate influence outside directly controlled tokens.
### Per-layer weights
- Map custom weights to real architecture blocks in execution order. Confirm the
count from the loaded model, not from a model-card claim alone.
- Default weights must reproduce vanilla output exactly.
- Universal/soft weights must follow this repository's established progression
semantics. Implement a family-specific conversion only when the normal
`ControlWeights` layout does not represent the architecture.
- Ordinary example workflows should use default weights. Do not connect an
advanced custom-weight node merely to demonstrate that it exists.
## Required Validation
Python import or compile checks are necessary but are not model validation. Use
a real local ComfyUI installation, preferably managed by comfy-runner, with this
repository linked as the custom node.
### Vanilla parity
For every published control type and materially different checkpoint format:
1. Run vanilla ComfyUI and Advanced-ControlNet with identical model files,
conditioning, seed, sampler settings, and latent.
2. Compare latent tensors before decode and decoded pixel arrays.
3. Target maximum absolute latent difference `0.0` and identical pixels when
both paths implement the same math. If exact parity is impossible, explain
why and report a justified numerical tolerance plus image metrics.
4. Confirm strength zero matches no control and strength one matches vanilla.
5. Check logs for missing/unexpected keys, dtype/device errors, repeated model
loads, and cleanup failures.
### Advanced behavior
At minimum, execute focused workflows for:
- A nontrivial start/end schedule or two timestep keyframes.
- Batch size two with different latent-keyframe strengths.
- All-zero, all-one, and half-frame effect masks.
- Default weights and one nonuniform custom or soft-weight configuration.
- Conditional/unconditional weighting when supported.
- A stacked control path.
- Missing required model-specific extras and the resulting readable error.
- Both the vanilla model folder and any historical Advanced-ControlNet folder.
- Re-queueing the same workflow to exercise copy and cleanup behavior.
For model families with several control types, test every type. Do not assume
that lineart, depth, pose, inpainting, union, or channel-count variants share the
same conditioning contract.
### Basic checks
- Run `python -m compileall adv_control __init__.py` with the target ComfyUI
environment.
- Parse every added workflow JSON.
- Load each committed workflow in the real frontend, serialize it to an API
prompt, and execute that round-tripped prompt. This catches stale node IDs,
renamed inputs, invalid widgets, and missing model metadata.
- Run `git diff --check`.
There is currently no repository unit-test suite. Add focused tests when they
can exercise pure detection, shape, mask, or scheduling logic without building
a fake ComfyUI runtime. Do not add a large test framework solely for one port.
## Examples and Review Evidence
Every model-family port must be independently checkable by a reviewer:
- Add one simple workflow for every public control type. Include required input
images or masks when licensing permits.
- Keep ComfyUI's default node names. Use colored groups or regions to explain
branches; do not rename nodes, because reviewers need to identify their types.
- Keep the normal workflows simple. Leave custom per-layer weights disconnected
unless a workflow specifically validates those advanced weights.
- Include direct links to the official base model, encoder, VAE, and control
checkpoint repositories, plus the exact destination folder for each file.
- Include a workflow screenshot and labeled output comparison in the PR. For
parity tests, show vanilla, Advanced-ControlNet, and an absolute-difference
result when practical.
- Commit reusable workflows and small review images under `examples/<family>/`.
Do not commit model files, latent dumps, or large intermediate artifacts.
- Document exact seeds/settings, expected numerical results, known limitations,
and reproduction steps in the example README and PR description.
- If final-image interpretation is subtle, include the direct tensor-level
evidence needed to distinguish a real bug from model behavior.
## Definition Of Done For A Model Port
- [ ] The official checkpoint is detected without relying on its filename.
- [ ] Vanilla and historical Advanced-ControlNet model folders are preserved.
- [ ] Default output matches vanilla for every published control type.
- [ ] Strength scheduling, keyframes, masks, weights, batching, and stacking are
tested or an architectural limitation is documented.
- [ ] Effect masks are tested at the injection site and in decoded output.
- [ ] Model-specific inputs use a narrow family boundary, not shared Apply-node
expansion.
- [ ] `get_models`, `copy`, cleanup, dtype/device handling, and repeated runs are
verified.
- [ ] Missing required inputs produce actionable errors.
- [ ] Simple workflows, input assets, download links, screenshots, and exact
reproduction instructions are included.
- [ ] Real frontend/API execution, compile checks, JSON parsing, and
`git diff --check` pass.
+7 -1
View File
@@ -12,7 +12,7 @@ ControlNet preprocessors are available through [comfyui_controlnet_aux](https://
- Replicate ***"ControlNet is more important"*** feature from sd-webui-controlnet extension via ***uncond_multiplier*** on ***Soft Weights***
- uncond_multiplier=0.0 gives identical results of auto1111's feature, but values between 0.0 and 1.0 can be used without issue to granularly control the setting.
- ControlNet, T2IAdapter, and ControlLoRA support for sliding context windows
- ControlLLLite support
- ControlLLLite support, including Anima LLLite v2 and inpainting models
- ControlNet++ support
- CtrLoRA support
- Relevant models linked on [CtrLoRA github page](https://github.com/xyfJASON/ctrlora)
@@ -83,6 +83,12 @@ Loads a ControlNet model and converts it into an Advanced version that supports
### Outputs
- 🟪***CONTROL_NET***: loaded Advanced ControlNet
## Anima LLLite v2
Place Anima LLLite v2 files in `ComfyUI/models/model_patches` and load them with **Load Anima LLLite Model**. The regular Advanced ControlNet loader also recognizes these models when they are placed in `ComfyUI/models/controlnet`.
Use the loaded model with **Apply Advanced ControlNet**. For the 4-channel inpainting model, pass the source mask through **Anima LLLite Extras** into the `cn_extras` input of a weights node; `mask_optional` on the Apply node remains the Advanced-ControlNet effect mask. **ControlNet Custom Weights [Anima]** provides one weight for each of Anima's 28 transformer blocks. Timestep keyframes, latent keyframes, soft weights, CFG/unconditional weighting, effect masks, and stacked controls work the same as with other Advanced-ControlNet models.
## Timestep Keyframe
![image](https://github.com/Kosinkadink/ComfyUI-Advanced-ControlNet/assets/7365912/404f3cfe-5852-4eed-935b-37e32493d1b5)
+5 -6
View File
@@ -1,11 +1,10 @@
from .adv_control.nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
from .adv_control import documentation
from .adv_control.nodes import AdvancedControlNetExtension
from .adv_control.dinklink import init_dinklink
from .adv_control.sampling import prepare_dinklink_acn_wrapper
WEB_DIRECTORY = "./web"
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', "WEB_DIRECTORY"]
documentation.format_descriptions(NODE_CLASS_MAPPINGS)
init_dinklink()
prepare_dinklink_acn_wrapper()
async def comfy_entrypoint() -> AdvancedControlNetExtension:
return AdvancedControlNetExtension()
+9 -4
View File
@@ -13,7 +13,7 @@ from comfy.controlnet import ControlBase, ControlNet, ControlNetSD35, ControlLor
from comfy.model_patcher import ModelPatcher
from .control_sparsectrl import SparseControlNet, SparseSettings, SparseConst, InterfaceAnimateDiffModel, create_sparse_modelpatcher, load_sparsectrl_motionmodel
from .control_lllite import LLLiteModule, LLLitePatch, load_controllllite
from .control_lllite import LLLiteModule, LLLitePatch, load_anima_lllite, load_controllllite
from .control_svd import svd_unet_config_from_diffusers_unet, SVDControlNet, svd_unet_to_diffusers
from .utils import (AdvancedControlBase, TimestepKeyframeGroup, LatentKeyframeGroup, AbstractPreprocWrapper, ControlWeightType, ControlWeights, WeightTypeException, Extras,
manual_cast_clean_groupnorm, disable_weight_init_clean_groupnorm, WrapperConsts, prepare_mask_batch, get_properly_arranged_t2i_weights, load_torch_file_with_dict_factory,
@@ -510,7 +510,7 @@ class SparseCtrlAdvanced(ControlNetAdvanced):
def load_controlnet(ckpt_path, timestep_keyframe: TimestepKeyframeGroup=None, model=None):
controlnet_data = comfy.utils.load_torch_file(ckpt_path, safe_load=True)
controlnet_data, metadata = comfy.utils.load_torch_file(ckpt_path, safe_load=True, return_metadata=True)
# from pathlib import Path
# log_name = ckpt_path.split('\\')[-1]
# with open(Path(__file__).parent.parent.parent / rf"keys_{log_name}.txt", "w") as afile:
@@ -519,6 +519,10 @@ def load_controlnet(ckpt_path, timestep_keyframe: TimestepKeyframeGroup=None, mo
control = None
# check if a non-vanilla ControlNet
controlnet_type = ControlWeightType.DEFAULT
is_anima_lllite = (
"lllite_conditioning1.conv1.weight" in controlnet_data
and any(key.startswith("lllite_dit_blocks_") for key in controlnet_data)
)
has_controlnet_key = False
has_motion_modules_key = False
has_temporal_res_block_key = False
@@ -548,7 +552,9 @@ def load_controlnet(ckpt_path, timestep_keyframe: TimestepKeyframeGroup=None, mo
elif has_controlnet_key and has_temporal_res_block_key:
controlnet_type = ControlWeightType.SVD_CONTROLNET
if controlnet_type != ControlWeightType.DEFAULT:
if is_anima_lllite:
control = load_anima_lllite(ckpt_path, controlnet_data=controlnet_data, metadata=metadata, timestep_keyframe=timestep_keyframe)
elif controlnet_type != ControlWeightType.DEFAULT:
if controlnet_type == ControlWeightType.CONTROLLLLITE:
control = load_controllllite(ckpt_path, controlnet_data=controlnet_data, timestep_keyframe=timestep_keyframe)
elif controlnet_type == ControlWeightType.SPARSECTRL:
@@ -990,4 +996,3 @@ def load_svdcontrolnet(ckpt_path: str, controlnet_data: dict[str, Tensor]=None,
control = SVDControlNetAdvanced(control_model, timestep_keyframes=timestep_keyframe, global_average_pooling=global_average_pooling, load_device=load_device, manual_cast_dtype=manual_cast_dtype)
return control
+237
View File
@@ -10,14 +10,24 @@ import os
import comfy.utils
import comfy.ops
import comfy.model_management
import comfy.model_patcher
from comfy.model_patcher import ModelPatcher
from comfy.controlnet import ControlBase
try:
import comfy.ldm.anima.lllite as comfy_anima_lllite
except ImportError:
comfy_anima_lllite = None
from .logger import logger
from .utils import (AdvancedControlBase, TimestepKeyframeGroup, ControlWeights, broadcast_image_to_extend, extend_to_batch_size,
prepare_mask_batch)
class AnimaLLLiteConst:
INPAINT_MASK = "anima_lllite_inpaint_mask"
# based on set_model_patch code in comfy/model_patcher.py
def set_model_patch(transformer_options, patch, name):
to = transformer_options
@@ -371,6 +381,233 @@ class ControlLLLiteAdvanced(ControlBase, AdvancedControlBase):
return c
class AnimaLLLiteAdvancedPatch:
def __init__(self, model_patch, control: 'AnimaLLLiteAdvanced'=None):
self.model_patch = model_patch
self.control = control
def set_control(self, control: 'AnimaLLLiteAdvanced') -> 'AnimaLLLiteAdvancedPatch':
self.control = control
return self
def clone_with_control(self, control: 'AnimaLLLiteAdvanced') -> 'AnimaLLLiteAdvancedPatch':
return AnimaLLLiteAdvancedPatch(self.model_patch, control)
def __call__(self, args):
if not self.control.should_run():
return args
x = args["x"]
if x.shape[2] != 1:
raise ValueError(f"Anima LLLite only supports T=1, got T={x.shape[2]}")
target_height = x.shape[-2] * 8
target_width = x.shape[-1] * 8
image = self.control.prepare_batched_tensor(self.control.cond_hint_original, x.shape[0])[:, :3]
image = comfy.utils.common_upscale(image, target_width, target_height, "bicubic", crop="center").clamp(0.0, 1.0)
image = image.to(device=x.device, dtype=x.dtype) * 2.0 - 1.0
if self.model_patch.model.cond_in_channels == 4:
mask = self.control.weights.extras.get(AnimaLLLiteConst.INPAINT_MASK)
if mask is None:
raise ValueError(
"Anima LLLite inpainting models require an inpaint mask. Connect a MASK to Anima LLLite Extras, "
"connect its cn_extras output to Default Weights, then connect CN_WEIGHTS to weights_override on Apply Advanced ControlNet."
)
if mask.ndim == 3:
mask = mask.unsqueeze(1)
if mask.ndim != 4 or mask.shape[1] != 1:
raise ValueError(f"Anima LLLite mask must have one channel, got shape {tuple(mask.shape)}")
if image.shape[0] > 1:
mask = self.control.prepare_batched_tensor(mask, image.shape[0], except_one=False)
mask = comfy.utils.common_upscale(mask.float(), target_width, target_height, "nearest-exact", crop="center")
if mask.shape[0] != image.shape[0]:
if image.shape[0] % mask.shape[0] != 0:
raise ValueError(f"Anima LLLite mask batch {mask.shape[0]} cannot be broadcast to image batch {image.shape[0]}")
mask = mask.repeat(image.shape[0] // mask.shape[0], 1, 1, 1)
mask = (mask >= 0.5).to(device=x.device, dtype=x.dtype)
if self.model_patch.model.inpaint_masked_input:
image = image * (mask < 0.5).to(image.dtype)
image = torch.cat((image, mask * 2.0 - 1.0), dim=1)
cond_emb = self.model_patch.model.encode_conditioning(image)
multiplier, weight_mask = self.control.prepare_multiplier(args["img"])
args["transformer_options"]["model_patch_data"][self] = (cond_emb, multiplier, weight_mask)
return args
def to(self, device_or_dtype):
return self
def models(self):
return [self.model_patch]
class AnimaLLLiteAdvancedAttentionPatch:
def __init__(self, patch: AnimaLLLiteAdvancedPatch, targets):
self.patch = patch
self.targets = targets
def __call__(self, q, k, v, pe=None, attn_mask=None, extra_options=None):
patch_data = extra_options["model_patch_data"].get(self.patch)
if patch_data is None:
return {"q": q, "k": k, "v": v, "pe": pe, "attn_mask": attn_mask}
cond_emb, multiplier, weight_mask = patch_data
block_index = extra_options["block_index"]
strength = self.patch.control.get_block_strength(block_index, multiplier, weight_mask)
values = {"q": q, "k": k, "v": v}
for value_name, target in self.targets.items():
values[value_name] = self.patch.model_patch.model.apply(values[value_name], cond_emb, block_index, target, strength)
return {"q": values["q"], "k": values["k"], "v": values["v"], "pe": pe, "attn_mask": attn_mask}
class AnimaLLLiteAdvancedMLPPatch:
def __init__(self, patch: AnimaLLLiteAdvancedPatch):
self.patch = patch
def __call__(self, args):
patch_data = args["transformer_options"]["model_patch_data"].get(self.patch)
if patch_data is None:
return args
cond_emb, multiplier, weight_mask = patch_data
block_index = args["transformer_options"]["block_index"]
strength = self.patch.control.get_block_strength(block_index, multiplier, weight_mask)
args["x"] = self.patch.model_patch.model.apply(args["x"], cond_emb, block_index, "mlp_layer1", strength)
return args
class AnimaLLLiteAdvanced(ControlBase, AdvancedControlBase):
def __init__(self, model_patch, timestep_keyframes: TimestepKeyframeGroup):
ControlBase.__init__(self)
AdvancedControlBase.__init__(self, super(), timestep_keyframes=timestep_keyframes, weights_default=ControlWeights.controllllite())
self.model_patch = model_patch
self.patch = AnimaLLLiteAdvancedPatch(model_patch, self)
self.patch_attn1 = AnimaLLLiteAdvancedAttentionPatch(
self.patch,
{"q": "self_attn_q_proj", "k": "self_attn_k_proj", "v": "self_attn_v_proj"},
)
self.patch_attn2 = AnimaLLLiteAdvancedAttentionPatch(self.patch, {"q": "cross_attn_q_proj"})
self.patch_mlp = AnimaLLLiteAdvancedMLPPatch(self.patch)
def prepare_batched_tensor(self, tensor: Tensor, target_batch: int, except_one=True) -> Tensor:
if self.sub_idxs is not None:
if tensor.shape[0] < self.full_latent_length:
tensor = extend_to_batch_size(tensor, self.full_latent_length)
tensor = tensor[self.sub_idxs]
if tensor.shape[0] != target_batch:
tensor = broadcast_image_to_extend(tensor, target_batch, self.batched_number, except_one=except_one)
return tensor
def prepare_effect_mask(self, mask: Tensor, img: Tensor) -> Tensor:
mask = self.prepare_batched_tensor(mask, img.shape[0], except_one=False)
mask = torch.nn.functional.interpolate(
mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])).float(),
size=(img.shape[2], img.shape[3]),
mode="bilinear",
)
return mask.flatten(2).transpose(1, 2).to(device=img.device, dtype=img.dtype)
def prepare_multiplier(self, img: Tensor):
multiplier = self.strength * self._current_timestep_keyframe.strength
token_multiplier = None
masks = [self.mask_cond_hint_original, self._current_timestep_keyframe.mask_hint_orig]
for mask in masks:
if mask is not None:
mask_multiplier = self.prepare_effect_mask(mask, img)
token_multiplier = mask_multiplier if token_multiplier is None else token_multiplier * mask_multiplier
flat_img = img.flatten(1, 3)
if self.latent_keyframes is not None:
latent_multiplier = self.calc_latent_keyframe_mults(flat_img, self.batched_number)
token_multiplier = latent_multiplier if token_multiplier is None else token_multiplier * latent_multiplier
if self.weights.has_uncond_multiplier and self.cond_or_uncond is not None:
batch_multiplier = torch.ones((img.shape[0], 1, 1), dtype=img.dtype, device=img.device)
actual_length = img.shape[0] // self.batched_number
for idx, cond_type in enumerate(self.cond_or_uncond):
if cond_type == 1:
batch_multiplier[actual_length * idx:actual_length * (idx + 1)] *= self.weights.uncond_multiplier
token_multiplier = batch_multiplier if token_multiplier is None else token_multiplier * batch_multiplier
weight_mask = None
if self.weights.weight_mask is not None:
weight_mask = self.prepare_effect_mask(self.weights.weight_mask, img)
if token_multiplier is not None:
multiplier = token_multiplier * multiplier
return multiplier, weight_mask
def get_block_strength(self, block_index: int, multiplier, weight_mask):
block_weight = 1.0
if self.weights.weight_type == "universal":
exponent = self.model_patch.model.block_count - block_index
if weight_mask is not None:
block_weight = torch.pow(weight_mask, exponent)
else:
block_weight = self.weights.base_multiplier ** exponent
elif self.weights.weights_input is not None and block_index < len(self.weights.weights_input):
block_weight = self.weights.weights_input[block_index]
return multiplier * block_weight
def pre_run_advanced(self, *args, **kwargs):
AdvancedControlBase.pre_run_advanced(self, *args, **kwargs)
self.patch.set_control(self)
def get_control_advanced(self, x_noisy: Tensor, t, cond, batched_number: int, transformer_options: dict):
control_prev = None
if self.previous_controlnet is not None:
control_prev = self.previous_controlnet.get_control(x_noisy, t, cond, batched_number, transformer_options)
if not self.should_run():
return control_prev
set_model_patch(transformer_options, self.patch.set_control(self), "post_input")
set_model_attn1_patch(transformer_options, self.patch_attn1)
set_model_attn2_patch(transformer_options, self.patch_attn2)
set_model_patch(transformer_options, self.patch_mlp, "mlp_patch")
return control_prev
def get_models(self):
models = super().get_models()
models.append(self.model_patch)
return models
def copy(self):
copied = AnimaLLLiteAdvanced(self.model_patch, self.timestep_keyframes)
self.copy_to(copied)
self.copy_to_advanced(copied)
return copied
def load_anima_lllite(ckpt_path: str, controlnet_data: dict[str, Tensor]=None, metadata=None, timestep_keyframe: TimestepKeyframeGroup=None):
if comfy_anima_lllite is None:
raise RuntimeError("Anima LLLite requires a newer version of ComfyUI. Please update ComfyUI.")
if controlnet_data is None or metadata is None:
loaded_data, loaded_metadata = comfy.utils.load_torch_file(ckpt_path, safe_load=True, return_metadata=True)
if controlnet_data is None:
controlnet_data = loaded_data
metadata = loaded_metadata
dtype = comfy.utils.weight_dtype(controlnet_data)
model = comfy_anima_lllite.AnimaLLLite(
controlnet_data,
metadata,
device=comfy.model_management.unet_offload_device(),
dtype=dtype,
operations=comfy.ops.manual_cast,
)
patcher_type = getattr(comfy.model_patcher, "CoreModelPatcher", ModelPatcher)
model_patcher = patcher_type(
model,
load_device=comfy.model_management.get_torch_device(),
offload_device=comfy.model_management.unet_offload_device(),
)
is_dynamic = getattr(model_patcher, "is_dynamic", lambda: False)()
model.load_state_dict(controlnet_data, assign=is_dynamic)
return AnimaLLLiteAdvanced(model_patcher, timestep_keyframe)
def load_controllllite(ckpt_path: str, controlnet_data: dict[str, Tensor]=None, timestep_keyframe: TimestepKeyframeGroup=None):
if controlnet_data is None:
controlnet_data = comfy.utils.load_torch_file(ckpt_path, safe_load=True)
-47
View File
@@ -1,47 +0,0 @@
from .logger import logger
def image(src):
return f'<img src={src} style="width: 0px; min-width: 100%">'
def video(src):
return f'<video src={src} autoplay muted loop controls controlslist="nodownload noremoteplayback noplaybackrate" style="width: 0px; min-width: 100%" class="VHS_loopedvideo">'
def short_desc(desc):
return f'<div id=VHS_shortdesc style="font-size: .8em">{desc}</div>'
descriptions = {
}
sizes = ['1.4','1.2','1']
def as_html(entry, depth=0):
if isinstance(entry, dict):
size = 0.8 if depth < 2 else 1
html = ''
for k in entry:
if k == "collapsed":
continue
collapse_single = k.endswith("_collapsed")
if collapse_single:
name = k[:-len("_collapsed")]
else:
name = k
collapse_flag = ' VHS_precollapse' if entry.get("collapsed", False) or collapse_single else ''
html += f'<div vhs_title=\"{name}\" style=\"display: flex; font-size: {size}em\" class=\"VHS_collapse{collapse_flag}\"><div style=\"color: #AAA; height: 1.5em;\">[<span style=\"font-family: monospace\">-</span>]</div><div style=\"width: 100%\">{name}: {as_html(entry[k], depth=depth+1)}</div></div>'
return html
if isinstance(entry, list):
html = ''
for i in entry:
html += f'<div>{as_html(i, depth=depth)}</div>'
return html
return str(entry)
def format_descriptions(nodes):
for k in descriptions:
if k.endswith("_collapsed"):
k = k[:-len("_collapsed")]
nodes[k].DESCRIPTION = as_html(descriptions[k])
# undocumented_nodes = []
# for k in nodes:
# if not hasattr(nodes[k], "DESCRIPTION"):
# undocumented_nodes.append(k)
# if len(undocumented_nodes) > 0:
# logger.info(f"Undocumented nodes: {undocumented_nodes}")
+55 -118
View File
@@ -1,136 +1,73 @@
import comfy.sample
from comfy_api.latest import ComfyExtension, io
from .nodes_main import (ControlNetLoaderAdvanced, DiffControlNetLoaderAdvanced,
from .nodes_main import (ControlNetLoaderAdvanced, DiffControlNetLoaderAdvanced, AnimaLLLiteLoaderAdvanced,
AdvancedControlNetApply, AdvancedControlNetApplySingle)
from .nodes_weight import (DefaultWeights, ScaledSoftMaskedUniversalWeights, ScaledSoftUniversalWeights,
SoftControlNetWeightsSD15, CustomControlNetWeightsSD15, CustomControlNetWeightsFlux,
SoftT2IAdapterWeights, CustomT2IAdapterWeights, ExtrasMiddleMultNode)
CustomControlNetWeightsAnima, SoftT2IAdapterWeights, CustomT2IAdapterWeights, ExtrasMiddleMultNode,
AnimaLLLiteExtras)
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_ctrlora import CtrLoRALoader
from .nodes_loosecontrol import ControlNetLoaderWithLoraAdvanced
from .nodes_deprecated import (LoadImagesFromDirectory, ScaledSoftUniversalWeightsDeprecated,
SoftControlNetWeightsDeprecated, CustomControlNetWeightsDeprecated,
SoftT2IAdapterWeightsDeprecated, CustomT2IAdapterWeightsDeprecated,
AdvancedControlNetApplyDEPR, AdvancedControlNetApplySingleDEPR,
ControlNetLoaderAdvancedDEPR, DiffControlNetLoaderAdvancedDEPR)
from .logger import logger
# NODE MAPPING
NODE_CLASS_MAPPINGS = {
# Keyframes
"TimestepKeyframe": TimestepKeyframeNode,
"ACN_TimestepKeyframeInterpolation": TimestepKeyframeInterpolationNode,
"ACN_TimestepKeyframeFromStrengthList": TimestepKeyframeFromStrengthListNode,
"LatentKeyframe": LatentKeyframeNode,
"LatentKeyframeTiming": LatentKeyframeInterpolationNode,
"LatentKeyframeBatchedGroup": LatentKeyframeBatchedGroupNode,
"LatentKeyframeGroup": LatentKeyframeGroupNode,
# Conditioning
"ACN_AdvancedControlNetApply_v2": AdvancedControlNetApply,
"ACN_AdvancedControlNetApplySingle_v2": AdvancedControlNetApplySingle,
# Loaders
"ACN_ControlNetLoaderAdvanced": ControlNetLoaderAdvanced,
"ACN_DiffControlNetLoaderAdvanced": DiffControlNetLoaderAdvanced,
# Weights
"ACN_ScaledSoftControlNetWeights": ScaledSoftUniversalWeights,
"ScaledSoftMaskedUniversalWeights": ScaledSoftMaskedUniversalWeights,
"ACN_SoftControlNetWeightsSD15": SoftControlNetWeightsSD15,
"ACN_CustomControlNetWeightsSD15": CustomControlNetWeightsSD15,
"ACN_CustomControlNetWeightsFlux": CustomControlNetWeightsFlux,
"ACN_SoftT2IAdapterWeights": SoftT2IAdapterWeights,
"ACN_CustomT2IAdapterWeights": CustomT2IAdapterWeights,
"ACN_DefaultUniversalWeights": DefaultWeights,
"ACN_ExtrasMiddleMult": ExtrasMiddleMultNode,
# SparseCtrl
"ACN_SparseCtrlRGBPreprocessor": RgbSparseCtrlPreprocessor,
"ACN_SparseCtrlLoaderAdvanced": SparseCtrlLoaderAdvanced,
"ACN_SparseCtrlMergedLoaderAdvanced": SparseCtrlMergedLoaderAdvanced,
"ACN_SparseCtrlIndexMethodNode": SparseIndexMethodNode,
"ACN_SparseCtrlSpreadMethodNode": SparseSpreadMethodNode,
"ACN_SparseCtrlWeightExtras": SparseWeightExtras,
# ControlNet++
"ACN_ControlNet++LoaderSingle": PlusPlusLoaderSingle,
"ACN_ControlNet++LoaderAdvanced": PlusPlusLoaderAdvanced,
"ACN_ControlNet++InputNode": PlusPlusInputNode,
# CtrLoRA
"ACN_CtrLoRALoader": CtrLoRALoader,
# Reference
"ACN_ReferencePreprocessor": ReferencePreprocessorNode,
"ACN_ReferenceControlNet": ReferenceControlNetNode,
"ACN_ReferenceControlNetFinetune": ReferenceControlFinetune,
# LOOSEControl
#"ACN_ControlNetLoaderWithLoraAdvanced": ControlNetLoaderWithLoraAdvanced,
# Deprecated
"LoadImagesFromDirectory": LoadImagesFromDirectory,
"ScaledSoftControlNetWeights": ScaledSoftUniversalWeightsDeprecated,
"SoftControlNetWeights": SoftControlNetWeightsDeprecated,
"CustomControlNetWeights": CustomControlNetWeightsDeprecated,
"SoftT2IAdapterWeights": SoftT2IAdapterWeightsDeprecated,
"CustomT2IAdapterWeights": CustomT2IAdapterWeightsDeprecated,
"ACN_AdvancedControlNetApply": AdvancedControlNetApplyDEPR,
"ACN_AdvancedControlNetApplySingle": AdvancedControlNetApplySingleDEPR,
"ControlNetLoaderAdvanced": ControlNetLoaderAdvancedDEPR,
"DiffControlNetLoaderAdvanced": DiffControlNetLoaderAdvancedDEPR,
}
NODE_DISPLAY_NAME_MAPPINGS = {
# Keyframes
"TimestepKeyframe": "Timestep Keyframe 🛂🅐🅒🅝",
"ACN_TimestepKeyframeInterpolation": "Timestep Keyframe Interp. 🛂🅐🅒🅝",
"ACN_TimestepKeyframeFromStrengthList": "Timestep Keyframe From List 🛂🅐🅒🅝",
"LatentKeyframe": "Latent Keyframe 🛂🅐🅒🅝",
"LatentKeyframeTiming": "Latent Keyframe Interp. 🛂🅐🅒🅝",
"LatentKeyframeBatchedGroup": "Latent Keyframe From List 🛂🅐🅒🅝",
"LatentKeyframeGroup": "Latent Keyframe Group 🛂🅐🅒🅝",
# Conditioning
"ACN_AdvancedControlNetApply_v2": "Apply Advanced ControlNet 🛂🅐🅒🅝",
"ACN_AdvancedControlNetApplySingle_v2": "Apply Advanced ControlNet(1) 🛂🅐🅒🅝",
# Loaders
"ACN_ControlNetLoaderAdvanced": "Load Advanced ControlNet Model 🛂🅐🅒🅝",
"ACN_DiffControlNetLoaderAdvanced": "Load Advanced ControlNet Model (diff) 🛂🅐🅒🅝",
# Weights
"ACN_ScaledSoftControlNetWeights": "Scaled Soft Weights 🛂🅐🅒🅝",
"ScaledSoftMaskedUniversalWeights": "Scaled Soft Masked 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 🛂🅐🅒🅝",
"ACN_ExtrasMiddleMult": "Middle Weight Extras 🛂🅐🅒🅝",
# SparseCtrl
"ACN_SparseCtrlRGBPreprocessor": "RGB SparseCtrl 🛂🅐🅒🅝",
"ACN_SparseCtrlLoaderAdvanced": "Load SparseCtrl Model 🛂🅐🅒🅝",
"ACN_SparseCtrlMergedLoaderAdvanced": "🧪Load Merged SparseCtrl Model 🛂🅐🅒🅝",
"ACN_SparseCtrlIndexMethodNode": "SparseCtrl Index Method 🛂🅐🅒🅝",
"ACN_SparseCtrlSpreadMethodNode": "SparseCtrl Spread Method 🛂🅐🅒🅝",
"ACN_SparseCtrlWeightExtras": "SparseCtrl Weight Extras 🛂🅐🅒🅝",
# ControlNet++
"ACN_ControlNet++LoaderSingle": "Load ControlNet++ Model (Single) 🛂🅐🅒🅝",
"ACN_ControlNet++LoaderAdvanced": "Load ControlNet++ Model (Multi) 🛂🅐🅒🅝",
"ACN_ControlNet++InputNode": "ControlNet++ Input 🛂🅐🅒🅝",
# CtrLoRA
"ACN_CtrLoRALoader": "Load CtrLoRA Model 🛂🅐🅒🅝",
# Reference
"ACN_ReferencePreprocessor": "Reference Preproccessor 🛂🅐🅒🅝",
"ACN_ReferenceControlNet": "Reference ControlNet 🛂🅐🅒🅝",
"ACN_ReferenceControlNetFinetune": "Reference ControlNet (Finetune) 🛂🅐🅒🅝",
# LOOSEControl
#"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 🛂🅐🅒🅝",
"ACN_AdvancedControlNetApply": "Apply Advanced ControlNet 🛂🅐🅒🅝",
"ACN_AdvancedControlNetApplySingle": "Apply Advanced ControlNet(1) 🛂🅐🅒🅝",
"ControlNetLoaderAdvanced": "Load Advanced ControlNet Model 🛂🅐🅒🅝",
"DiffControlNetLoaderAdvanced": "Load Advanced ControlNet Model (diff) 🛂🅐🅒🅝",
}
class AdvancedControlNetExtension(ComfyExtension):
async def get_node_list(self) -> list[type[io.ComfyNode]]:
return [
TimestepKeyframeNode,
TimestepKeyframeInterpolationNode,
TimestepKeyframeFromStrengthListNode,
LatentKeyframeNode,
LatentKeyframeInterpolationNode,
LatentKeyframeBatchedGroupNode,
LatentKeyframeGroupNode,
AdvancedControlNetApply,
AdvancedControlNetApplySingle,
ControlNetLoaderAdvanced,
DiffControlNetLoaderAdvanced,
AnimaLLLiteLoaderAdvanced,
ScaledSoftUniversalWeights,
ScaledSoftMaskedUniversalWeights,
SoftControlNetWeightsSD15,
CustomControlNetWeightsSD15,
CustomControlNetWeightsFlux,
CustomControlNetWeightsAnima,
SoftT2IAdapterWeights,
CustomT2IAdapterWeights,
DefaultWeights,
ExtrasMiddleMultNode,
AnimaLLLiteExtras,
RgbSparseCtrlPreprocessor,
SparseCtrlLoaderAdvanced,
SparseCtrlMergedLoaderAdvanced,
SparseIndexMethodNode,
SparseSpreadMethodNode,
SparseWeightExtras,
PlusPlusLoaderSingle,
PlusPlusLoaderAdvanced,
PlusPlusInputNode,
CtrLoRALoader,
ReferencePreprocessorNode,
ReferenceControlNetNode,
ReferenceControlFinetune,
LoadImagesFromDirectory,
ScaledSoftUniversalWeightsDeprecated,
SoftControlNetWeightsDeprecated,
CustomControlNetWeightsDeprecated,
SoftT2IAdapterWeightsDeprecated,
CustomT2IAdapterWeightsDeprecated,
AdvancedControlNetApplyDEPR,
AdvancedControlNetApplySingleDEPR,
ControlNetLoaderAdvancedDEPR,
DiffControlNetLoaderAdvancedDEPR
]
+18 -15
View File
@@ -1,25 +1,28 @@
from comfy_api.latest import io
import folder_paths
from .control_ctrlora import load_ctrlora
class CtrLoRALoader:
class CtrLoRALoader(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"base": (folder_paths.get_filename_list("controlnet"), ),
"lora": (folder_paths.get_filename_list("controlnet"), ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_CtrLoRALoader',
display_name='Load CtrLoRA Model 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/CtrLoRA',
inputs=[
io.Combo.Input('base', options=folder_paths.get_filename_list("controlnet")),
io.Combo.Input('lora', options=folder_paths.get_filename_list("controlnet"))
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET",)
FUNCTION = "load_controlnet_plusplus"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/CtrLoRA"
def load_controlnet_plusplus(self, base: str, lora: str):
@classmethod
def execute(cls, base: str, lora: str):
base_path = folder_paths.get_full_path("controlnet", base)
lora_path = folder_paths.get_full_path("controlnet", lora)
controlnet = load_ctrlora(base_path, lora_path)
return (controlnet,)
return io.NodeOutput(controlnet,)
+257 -279
View File
@@ -1,3 +1,4 @@
from comfy_api.latest import io
import os
import torch
@@ -7,29 +8,31 @@ import numpy as np
from PIL import Image, ImageOps
from .control import load_controlnet, is_advanced_controlnet
from .nodes_main import AdvancedControlNetApply
from .utils import BIGMAX, ControlWeights, TimestepKeyframeGroup, TimestepKeyframe, get_properly_arranged_t2i_weights
from .logger import logger
from .utils import ControlWeights, TimestepKeyframeGroup, TimestepKeyframe, get_properly_arranged_t2i_weights
class LoadImagesFromDirectory:
class LoadImagesFromDirectory(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"directory": ("STRING", {"default": ""}),
},
"optional": {
"image_load_cap": ("INT", {"default": 0, "min": 0, "max": BIGMAX, "step": 1}),
"start_index": ("INT", {"default": 0, "min": 0, "max": BIGMAX, "step": 1}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='LoadImagesFromDirectory',
display_name='🚫Load Images [DEPRECATED] 🛂🅐🅒🅝',
category='',
inputs=[
io.String.Input('directory', default=''),
io.Int.Input('image_load_cap', optional=True, default=0, max=9007199254740991, min=0, step=1),
io.Int.Input('start_index', optional=True, default=0, max=9007199254740991, min=0, step=1)
],
outputs=[
io.Image.Output('IMAGE', is_output_list=False),
io.Mask.Output('MASK', is_output_list=False),
io.Int.Output('INT', is_output_list=False)
],
is_deprecated=True
)
RETURN_TYPES = ("IMAGE", "MASK", "INT")
FUNCTION = "load_images"
CATEGORY = ""
def load_images(self, directory: str, image_load_cap: int = 0, start_index: int = 0):
@classmethod
def execute(cls, directory: str, image_load_cap: int = 0, start_index: int = 0):
if not os.path.isdir(directory):
raise FileNotFoundError(f"Directory '{directory} cannot be found.'")
dir_files = os.listdir(directory)
@@ -71,306 +74,283 @@ class LoadImagesFromDirectory:
if len(images) == 0:
raise FileNotFoundError(f"No images could be loaded from directory '{directory}'.")
return (torch.cat(images, dim=0), torch.stack(masks, dim=0), image_count)
return io.NodeOutput(torch.cat(images, dim=0), torch.stack(masks, dim=0), image_count)
class ScaledSoftUniversalWeightsDeprecated:
class ScaledSoftUniversalWeightsDeprecated(io.ComfyNode):
@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",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ScaledSoftControlNetWeights',
display_name='Scaled Soft Weights 🛂🅐🅒🅝',
category='',
inputs=[
io.Float.Input('base_multiplier', default=0.825, max=1.0, min=0.0, step=0.001),
io.Boolean.Input('flip_weights', default=False),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
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]={}):
@classmethod
def execute(cls, 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)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class SoftControlNetWeightsDeprecated:
class SoftControlNetWeightsDeprecated(io.ComfyNode):
@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",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='SoftControlNetWeights',
display_name='ControlNet Soft Weights 🛂🅐🅒🅝',
category='',
inputs=[
io.Float.Input('weight_00', default=0.09941396206337118, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_01', default=0.12050177219802567, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_02', default=0.14606275417942507, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_03', default=0.17704576264172736, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_04', default=0.214600924414215, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_05', default=0.26012233262329093, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_06', default=0.3152997971191405, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_07', default=0.3821815722656249, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_08', default=0.4632503906249999, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_09', default=0.561515625, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_10', default=0.6806249999999999, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_11', default=0.825, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_12', default=1.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('flip_weights', default=False),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
],
is_deprecated=True
)
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,
@classmethod
def execute(cls, 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)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class CustomControlNetWeightsDeprecated:
class CustomControlNetWeightsDeprecated(io.ComfyNode):
@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",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='CustomControlNetWeights',
display_name='ControlNet Custom Weights 🛂🅐🅒🅝',
category='',
inputs=[
io.Float.Input('weight_00', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_01', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_02', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_03', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_04', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_05', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_06', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_07', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_08', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_09', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_10', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_11', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_12', default=1.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('flip_weights', default=False),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
],
is_deprecated=True
)
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,
@classmethod
def execute(cls, 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)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class SoftT2IAdapterWeightsDeprecated:
class SoftT2IAdapterWeightsDeprecated(io.ComfyNode):
@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",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='SoftT2IAdapterWeights',
display_name='T2IAdapter Soft Weights 🛂🅐🅒🅝',
category='',
inputs=[
io.Float.Input('weight_00', default=0.25, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_01', default=0.62, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_02', default=0.825, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_03', default=1.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('flip_weights', default=False),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
],
is_deprecated=True
)
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,
@classmethod
def execute(cls, 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)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class CustomT2IAdapterWeightsDeprecated:
class CustomT2IAdapterWeightsDeprecated(io.ComfyNode):
@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",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='CustomT2IAdapterWeights',
display_name='T2IAdapter Custom Weights 🛂🅐🅒🅝',
category='',
inputs=[
io.Float.Input('weight_00', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_01', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_02', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_03', default=1.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('flip_weights', default=False),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
],
is_deprecated=True
)
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,
@classmethod
def execute(cls, 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)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class AdvancedControlNetApplyDEPR:
class AdvancedControlNetApplyDEPR(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"positive": ("CONDITIONING", ),
"negative": ("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",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_AdvancedControlNetApply',
display_name='Apply Advanced ControlNet 🛂🅐🅒🅝',
category='',
inputs=[
io.Conditioning.Input('positive'),
io.Conditioning.Input('negative'),
io.ControlNet.Input('control_net'),
io.Image.Input('image'),
io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01),
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001),
io.Mask.Input('mask_optional', optional=True),
io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_kf_override', optional=True),
io.Custom('CONTROL_NET_WEIGHTS').Input('weights_override', optional=True),
io.Model.Input('model_optional', optional=True),
io.Vae.Input('vae_optional', optional=True)
],
outputs=[
io.Conditioning.Output('positive', is_output_list=False),
io.Conditioning.Output('negative', is_output_list=False),
io.Model.Output('model_opt', is_output_list=False)
],
is_deprecated=True
)
DEPRECATED = True
RETURN_TYPES = ("CONDITIONING","CONDITIONING","MODEL",)
RETURN_NAMES = ("positive", "negative", "model_opt")
FUNCTION = "apply_controlnet"
CATEGORY = ""
def apply_controlnet(self, positive, negative, control_net, image, strength, start_percent, end_percent,
@classmethod
def execute(cls, positive, negative, control_net, image, strength, start_percent, end_percent,
mask_optional=None, model_optional=None, vae_optional=None,
timestep_kf: TimestepKeyframeGroup=None, latent_kf_override=None,
weights_override: ControlWeights=None, control_apply_to_uncond=False):
new_positive, new_negative = AdvancedControlNetApply.apply_controlnet(self, positive=positive, negative=negative, control_net=control_net, image=image,
new_positive, new_negative = AdvancedControlNetApply.execute(positive=positive, negative=negative, control_net=control_net, image=image,
strength=strength, start_percent=start_percent, end_percent=end_percent,
mask_optional=mask_optional, vae_optional=vae_optional,
timestep_kf=timestep_kf, latent_kf_override=latent_kf_override, weights_override=weights_override,)
return (new_positive, new_negative, model_optional)
timestep_kf=timestep_kf, latent_kf_override=latent_kf_override, weights_override=weights_override,).args
return io.NodeOutput(new_positive, new_negative, model_optional)
class AdvancedControlNetApplySingleDEPR:
class AdvancedControlNetApplySingleDEPR(io.ComfyNode):
@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",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_AdvancedControlNetApplySingle',
display_name='Apply Advanced ControlNet(1) 🛂🅐🅒🅝',
category='',
inputs=[
io.Conditioning.Input('conditioning'),
io.ControlNet.Input('control_net'),
io.Image.Input('image'),
io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01),
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001),
io.Mask.Input('mask_optional', optional=True),
io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_kf_override', optional=True),
io.Custom('CONTROL_NET_WEIGHTS').Input('weights_override', optional=True),
io.Model.Input('model_optional', optional=True),
io.Vae.Input('vae_optional', optional=True)
],
outputs=[
io.Conditioning.Output('CONDITIONING', is_output_list=False),
io.Model.Output('model_opt', is_output_list=False)
],
is_deprecated=True
)
DEPRECATED = True
RETURN_TYPES = ("CONDITIONING","MODEL",)
RETURN_NAMES = ("CONDITIONING", "model_opt")
FUNCTION = "apply_controlnet"
CATEGORY = ""
def apply_controlnet(self, conditioning, control_net, image, strength, start_percent, end_percent,
@classmethod
def execute(cls, conditioning, control_net, image, strength, start_percent, end_percent,
mask_optional=None, model_optional=None, vae_optional=None,
timestep_kf: TimestepKeyframeGroup=None, latent_kf_override=None,
weights_override: ControlWeights=None):
values = AdvancedControlNetApply.apply_controlnet(self, positive=conditioning, negative=None, control_net=control_net, image=image,
values = AdvancedControlNetApply.execute(positive=conditioning, negative=None, control_net=control_net, image=image,
strength=strength, start_percent=start_percent, end_percent=end_percent,
mask_optional=mask_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], model_optional)
return io.NodeOutput(values.args[0], model_optional)
class ControlNetLoaderAdvancedDEPR:
class ControlNetLoaderAdvancedDEPR(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"control_net_name": (folder_paths.get_filename_list("controlnet"), ),
},
"optional": {
"tk_optional": ("TIMESTEP_KEYFRAME", ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ControlNetLoaderAdvanced',
display_name='Load Advanced ControlNet Model 🛂🅐🅒🅝',
category='',
inputs=[
io.Combo.Input('control_net_name', options=folder_paths.get_filename_list("controlnet")),
io.Custom('TIMESTEP_KEYFRAME').Input('tk_optional', optional=True)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
],
is_deprecated=True
)
DEPRECATED = True
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = ""
def load_controlnet(self, control_net_name,
@classmethod
def execute(cls, control_net_name,
tk_optional: TimestepKeyframeGroup=None,
timestep_keyframe: TimestepKeyframeGroup=None,
):
@@ -378,32 +358,30 @@ class ControlNetLoaderAdvancedDEPR:
tk_optional = timestep_keyframe
controlnet_path = folder_paths.get_full_path("controlnet", control_net_name)
controlnet = load_controlnet(controlnet_path, tk_optional)
return (controlnet,)
return io.NodeOutput(controlnet,)
class DiffControlNetLoaderAdvancedDEPR:
class DiffControlNetLoaderAdvancedDEPR(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model": ("MODEL",),
"control_net_name": (folder_paths.get_filename_list("controlnet"), )
},
"optional": {
"tk_optional": ("TIMESTEP_KEYFRAME", ),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='DiffControlNetLoaderAdvanced',
display_name='Load Advanced ControlNet Model (diff) 🛂🅐🅒🅝',
category='',
inputs=[
io.Model.Input('model'),
io.Combo.Input('control_net_name', options=folder_paths.get_filename_list("controlnet")),
io.Custom('TIMESTEP_KEYFRAME').Input('tk_optional', optional=True)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
],
is_deprecated=True
)
DEPRECATED = True
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = ""
def load_controlnet(self, control_net_name, model,
@classmethod
def execute(cls, control_net_name, model,
tk_optional: TimestepKeyframeGroup=None,
timestep_keyframe: TimestepKeyframeGroup=None
):
@@ -413,4 +391,4 @@ class DiffControlNetLoaderAdvancedDEPR:
controlnet = load_controlnet(controlnet_path, tk_optional, model)
if is_advanced_controlnet(controlnet):
controlnet.verify_all_weights()
return (controlnet,)
return io.NodeOutput(controlnet,)
+171 -192
View File
@@ -1,43 +1,40 @@
from comfy_api.latest import io
from typing import Union
import numpy as np
from collections.abc import Iterable
from .utils import ControlWeights, TimestepKeyframe, TimestepKeyframeGroup, LatentKeyframe, LatentKeyframeGroup, BIGMIN, BIGMAX
from .utils import ControlWeights, TimestepKeyframe, TimestepKeyframeGroup, LatentKeyframe, LatentKeyframeGroup
from .utils import StrengthInterpolation as SI
from .logger import logger
class TimestepKeyframeNode:
class TimestepKeyframeNode(io.ComfyNode):
OUTDATED_DUMMY = -39
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}, ),
},
"optional": {
"prev_timestep_kf": ("TIMESTEP_KEYFRAME", ),
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"cn_weights": ("CONTROL_NET_WEIGHTS", ),
"latent_keyframe": ("LATENT_KEYFRAME", ),
"null_latent_kf_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"inherit_missing": ("BOOLEAN", {"default": True}, ),
"guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}),
"mask_optional": ("MASK", ),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='TimestepKeyframe',
display_name='Timestep Keyframe 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Custom('TIMESTEP_KEYFRAME').Input('prev_timestep_kf', optional=True),
io.Float.Input('strength', optional=True, default=1.0, max=10.0, min=0.0, step=0.001),
io.Custom('CONTROL_NET_WEIGHTS').Input('cn_weights', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_keyframe', optional=True),
io.Float.Input('null_latent_kf_strength', optional=True, default=0.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('inherit_missing', optional=True, default=True),
io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0),
io.Mask.Input('mask_optional', optional=True)
],
outputs=[
io.Custom('TIMESTEP_KEYFRAME').Output('TIMESTEP_KF', is_output_list=False)
]
)
RETURN_NAMES = ("TIMESTEP_KF", )
RETURN_TYPES = ("TIMESTEP_KEYFRAME", )
FUNCTION = "load_keyframe"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/keyframes"
def load_keyframe(self,
@classmethod
def execute(cls,
start_percent: float,
strength: float=1.0,
cn_weights: ControlWeights=None, control_net_weights: ControlWeights=None, # old name
@@ -49,7 +46,7 @@ class TimestepKeyframeNode:
guarantee_usage=True, # old input
mask_optional=None,):
# if using outdated dummy value, means node on workflow is outdated and should appropriately convert behavior
if guarantee_steps == self.OUTDATED_DUMMY:
if guarantee_steps == cls.OUTDATED_DUMMY:
guarantee_steps = int(guarantee_usage)
control_net_weights = control_net_weights if control_net_weights else cn_weights
prev_timestep_keyframe = prev_timestep_keyframe if prev_timestep_keyframe else prev_timestep_kf
@@ -61,42 +58,39 @@ class TimestepKeyframeNode:
control_weights=control_net_weights, latent_keyframes=latent_keyframe, inherit_missing=inherit_missing,
guarantee_steps=guarantee_steps, mask_hint_orig=mask_optional)
prev_timestep_keyframe.add(keyframe)
return (prev_timestep_keyframe,)
return io.NodeOutput(prev_timestep_keyframe,)
class TimestepKeyframeInterpolationNode:
class TimestepKeyframeInterpolationNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"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}),
"strength_start": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001},),
"strength_end": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001},),
"interpolation": (SI._LIST, ),
"intervals": ("INT", {"default": 50, "min": 2, "max": 100, "step": 1}),
},
"optional": {
"prev_timestep_kf": ("TIMESTEP_KEYFRAME", ),
"cn_weights": ("CONTROL_NET_WEIGHTS", ),
"latent_keyframe": ("LATENT_KEYFRAME", ),
"null_latent_kf_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.001},),
"inherit_missing": ("BOOLEAN", {"default": True},),
"mask_optional": ("MASK", ),
"print_keyframes": ("BOOLEAN", {"default": False}),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_TimestepKeyframeInterpolation',
display_name='Timestep Keyframe Interp. 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('strength_start', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('strength_end', default=1.0, max=10.0, min=0.0, step=0.001),
io.Combo.Input('interpolation', options=['linear', 'ease-in', 'ease-out', 'ease-in-out']),
io.Int.Input('intervals', default=50, max=100, min=2, step=1),
io.Custom('TIMESTEP_KEYFRAME').Input('prev_timestep_kf', optional=True),
io.Custom('CONTROL_NET_WEIGHTS').Input('cn_weights', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_keyframe', optional=True),
io.Float.Input('null_latent_kf_strength', optional=True, default=0.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('inherit_missing', optional=True, default=True),
io.Mask.Input('mask_optional', optional=True),
io.Boolean.Input('print_keyframes', optional=True, default=False)
],
outputs=[
io.Custom('TIMESTEP_KEYFRAME').Output('TIMESTEP_KF', is_output_list=False)
]
)
RETURN_NAMES = ("TIMESTEP_KF", )
RETURN_TYPES = ("TIMESTEP_KEYFRAME", )
FUNCTION = "load_keyframe"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/keyframes"
def load_keyframe(self,
@classmethod
def execute(cls,
start_percent: float, end_percent: float,
strength_start: float, strength_end: float, interpolation: str, intervals: int,
cn_weights: ControlWeights=None,
@@ -125,39 +119,35 @@ class TimestepKeyframeInterpolationNode:
guarantee_steps=guarantee_steps, mask_hint_orig=mask_optional))
if print_keyframes:
logger.info(f"TimestepKeyframe - start_percent:{percent} = {strength}")
return (prev_timestep_kf,)
return io.NodeOutput(prev_timestep_kf,)
class TimestepKeyframeFromStrengthListNode:
class TimestepKeyframeFromStrengthListNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"float_strengths": ("FLOAT", {"default": -1, "min": -1, "step": 0.001, "forceInput": True}),
"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": {
"prev_timestep_kf": ("TIMESTEP_KEYFRAME", ),
"cn_weights": ("CONTROL_NET_WEIGHTS", ),
"latent_keyframe": ("LATENT_KEYFRAME", ),
"null_latent_kf_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.001},),
"inherit_missing": ("BOOLEAN", {"default": True},),
"mask_optional": ("MASK", ),
"print_keyframes": ("BOOLEAN", {"default": False}),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_TimestepKeyframeFromStrengthList',
display_name='Timestep Keyframe From List 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.Float.Input('float_strengths', default=-1, force_input=True, min=-1, step=0.001),
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001),
io.Custom('TIMESTEP_KEYFRAME').Input('prev_timestep_kf', optional=True),
io.Custom('CONTROL_NET_WEIGHTS').Input('cn_weights', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_keyframe', optional=True),
io.Float.Input('null_latent_kf_strength', optional=True, default=0.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('inherit_missing', optional=True, default=True),
io.Mask.Input('mask_optional', optional=True),
io.Boolean.Input('print_keyframes', optional=True, default=False)
],
outputs=[
io.Custom('TIMESTEP_KEYFRAME').Output('TIMESTEP_KF', is_output_list=False)
]
)
RETURN_NAMES = ("TIMESTEP_KF", )
RETURN_TYPES = ("TIMESTEP_KEYFRAME", )
FUNCTION = "load_keyframe"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/keyframes"
def load_keyframe(self,
@classmethod
def execute(cls,
start_percent: float, end_percent: float,
float_strengths: float,
cn_weights: ControlWeights=None,
@@ -191,32 +181,27 @@ class TimestepKeyframeFromStrengthListNode:
guarantee_steps=guarantee_steps, mask_hint_orig=mask_optional))
if print_keyframes:
logger.info(f"TimestepKeyframe - start_percent:{percent} = {strength}")
return (prev_timestep_kf,)
return io.NodeOutput(prev_timestep_kf,)
class LatentKeyframeNode:
class LatentKeyframeNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"batch_index": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX, "step": 1}),
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
},
"optional": {
"prev_latent_kf": ("LATENT_KEYFRAME", ),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='LatentKeyframe',
display_name='Latent Keyframe 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.Int.Input('batch_index', default=0, max=9007199254740991, min=-9007199254740991, step=1),
io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.001),
io.Custom('LATENT_KEYFRAME').Input('prev_latent_kf', optional=True)
],
outputs=[
io.Custom('LATENT_KEYFRAME').Output('LATENT_KF', is_output_list=False)
]
)
RETURN_NAMES = ("LATENT_KF", )
RETURN_TYPES = ("LATENT_KEYFRAME", )
FUNCTION = "load_keyframe"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/keyframes"
def load_keyframe(self,
@classmethod
def execute(cls,
batch_index: int,
strength: float,
prev_latent_kf: LatentKeyframeGroup=None,
@@ -229,33 +214,29 @@ class LatentKeyframeNode:
prev_latent_keyframe = prev_latent_keyframe.clone()
keyframe = LatentKeyframe(batch_index, strength)
prev_latent_keyframe.add(keyframe)
return (prev_latent_keyframe,)
return io.NodeOutput(prev_latent_keyframe,)
class LatentKeyframeGroupNode:
class LatentKeyframeGroupNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"index_strengths": ("STRING", {"multiline": True, "default": ""}),
},
"optional": {
"prev_latent_kf": ("LATENT_KEYFRAME", ),
"latent_optional": ("LATENT", ),
"print_keyframes": ("BOOLEAN", {"default": False}),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='LatentKeyframeGroup',
display_name='Latent Keyframe Group 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.String.Input('index_strengths', default='', multiline=True),
io.Custom('LATENT_KEYFRAME').Input('prev_latent_kf', optional=True),
io.Latent.Input('latent_optional', optional=True),
io.Boolean.Input('print_keyframes', optional=True, default=False)
],
outputs=[
io.Custom('LATENT_KEYFRAME').Output('LATENT_KF', is_output_list=False)
]
)
RETURN_NAMES = ("LATENT_KF", )
RETURN_TYPES = ("LATENT_KEYFRAME", )
FUNCTION = "load_keyframes"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/keyframes"
def validate_index(self, index: int, latent_count: int = 0, is_range: bool = False, allow_negative = False) -> int:
@staticmethod
def validate_index(index: int, latent_count: int = 0, is_range: bool = False, allow_negative = False) -> int:
# if part of range, do nothing
if is_range:
return index
@@ -273,13 +254,15 @@ class LatentKeyframeGroupNode:
index = conv_index
return index
def convert_to_index_int(self, raw_index: str, latent_count: int = 0, is_range: bool = False, allow_negative = False) -> int:
@classmethod
def convert_to_index_int(cls, raw_index: str, latent_count: int = 0, is_range: bool = False, allow_negative = False) -> int:
try:
return self.validate_index(int(raw_index), latent_count=latent_count, is_range=is_range, allow_negative=allow_negative)
return cls.validate_index(int(raw_index), latent_count=latent_count, is_range=is_range, allow_negative=allow_negative)
except ValueError as e:
raise ValueError(f"index '{raw_index}' must be an integer.", e)
def convert_to_latent_keyframes(self, latent_indeces: str, latent_count: int) -> set[LatentKeyframe]:
@classmethod
def convert_to_latent_keyframes(cls, latent_indeces: str, latent_count: int) -> set[LatentKeyframe]:
if not latent_indeces:
return set()
int_latent_indeces = [i for i in range(0, latent_count)]
@@ -304,8 +287,8 @@ class LatentKeyframeGroupNode:
if ':' in g:
index_range = g.split(":", 1)
index_range = [r.strip() for r in index_range]
start_index = self.convert_to_index_int(index_range[0], latent_count=latent_count, is_range=True, allow_negative=allow_negative)
end_index = self.convert_to_index_int(index_range[1], latent_count=latent_count, is_range=True, allow_negative=allow_negative)
start_index = cls.convert_to_index_int(index_range[0], latent_count=latent_count, is_range=True, allow_negative=allow_negative)
end_index = cls.convert_to_index_int(index_range[1], latent_count=latent_count, is_range=True, allow_negative=allow_negative)
# if latents were passed in, base indeces on known latent count
if len(int_latent_indeces) > 0:
for i in int_latent_indeces[start_index:end_index]:
@@ -316,14 +299,16 @@ class LatentKeyframeGroupNode:
chosen_indeces.add(LatentKeyframe(i, strength))
# parse individual indeces
else:
chosen_indeces.add(LatentKeyframe(self.convert_to_index_int(g, latent_count=latent_count, allow_negative=allow_negative), strength))
chosen_indeces.add(LatentKeyframe(cls.convert_to_index_int(g, latent_count=latent_count, allow_negative=allow_negative), strength))
return chosen_indeces
def load_keyframes(self,
@classmethod
def execute(cls,
index_strengths: str,
prev_latent_kf: LatentKeyframeGroup=None,
prev_latent_keyframe: LatentKeyframeGroup=None, # old name
latent_image_opt=None,
latent_optional=None,
latent_image_opt=None, # old name
print_keyframes=False):
prev_latent_keyframe = prev_latent_keyframe if prev_latent_keyframe else prev_latent_kf
if not prev_latent_keyframe:
@@ -332,10 +317,11 @@ class LatentKeyframeGroupNode:
prev_latent_keyframe = prev_latent_keyframe.clone()
curr_latent_keyframe = LatentKeyframeGroup()
latent_image_opt = latent_image_opt if latent_image_opt is not None else latent_optional
latent_count = -1
if latent_image_opt:
latent_count = latent_image_opt['samples'].size()[0]
latent_keyframes = self.convert_to_latent_keyframes(index_strengths, latent_count=latent_count)
latent_keyframes = cls.convert_to_latent_keyframes(index_strengths, latent_count=latent_count)
for latent_keyframe in latent_keyframes:
curr_latent_keyframe.add(latent_keyframe)
@@ -348,35 +334,32 @@ class LatentKeyframeGroupNode:
for latent_keyframe in prev_latent_keyframe.keyframes:
curr_latent_keyframe.add(latent_keyframe)
return (curr_latent_keyframe,)
return io.NodeOutput(curr_latent_keyframe,)
class LatentKeyframeInterpolationNode:
class LatentKeyframeInterpolationNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"batch_index_from": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX, "step": 1}),
"batch_index_to_excl": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX, "step": 1}),
"strength_from": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"strength_to": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"interpolation": (SI._LIST, ),
},
"optional": {
"prev_latent_kf": ("LATENT_KEYFRAME", ),
"print_keyframes": ("BOOLEAN", {"default": False}),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='LatentKeyframeTiming',
display_name='Latent Keyframe Interp. 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.Int.Input('batch_index_from', default=0, max=9007199254740991, min=-9007199254740991, step=1),
io.Int.Input('batch_index_to_excl', default=0, max=9007199254740991, min=-9007199254740991, step=1),
io.Float.Input('strength_from', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('strength_to', default=1.0, max=10.0, min=0.0, step=0.001),
io.Combo.Input('interpolation', options=['linear', 'ease-in', 'ease-out', 'ease-in-out']),
io.Custom('LATENT_KEYFRAME').Input('prev_latent_kf', optional=True),
io.Boolean.Input('print_keyframes', optional=True, default=False)
],
outputs=[
io.Custom('LATENT_KEYFRAME').Output('LATENT_KF', is_output_list=False)
]
)
RETURN_NAMES = ("LATENT_KF", )
RETURN_TYPES = ("LATENT_KEYFRAME", )
FUNCTION = "load_keyframe"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/keyframes"
def load_keyframe(self,
@classmethod
def execute(cls,
batch_index_from: int,
strength_from: float,
batch_index_to_excl: int,
@@ -425,31 +408,27 @@ class LatentKeyframeInterpolationNode:
for latent_keyframe in prev_latent_keyframe.keyframes:
curr_latent_keyframe.add(latent_keyframe)
return (curr_latent_keyframe,)
return io.NodeOutput(curr_latent_keyframe,)
class LatentKeyframeBatchedGroupNode:
class LatentKeyframeBatchedGroupNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"float_strengths": ("FLOAT", {"default": -1, "min": -1, "step": 0.001, "forceInput": True}),
},
"optional": {
"prev_latent_kf": ("LATENT_KEYFRAME", ),
"print_keyframes": ("BOOLEAN", {"default": False}),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='LatentKeyframeBatchedGroup',
display_name='Latent Keyframe From List 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.Float.Input('float_strengths', default=-1, force_input=True, min=-1, step=0.001),
io.Custom('LATENT_KEYFRAME').Input('prev_latent_kf', optional=True),
io.Boolean.Input('print_keyframes', optional=True, default=False)
],
outputs=[
io.Custom('LATENT_KEYFRAME').Output('LATENT_KF', is_output_list=False)
]
)
RETURN_NAMES = ("LATENT_KF", )
RETURN_TYPES = ("LATENT_KEYFRAME", )
FUNCTION = "load_keyframe"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/keyframes"
def load_keyframe(self, float_strengths: Union[float, list[float]],
@classmethod
def execute(cls, float_strengths: Union[float, list[float]],
prev_latent_kf: LatentKeyframeGroup=None,
prev_latent_keyframe: LatentKeyframeGroup=None, # old name
print_keyframes=False):
@@ -479,4 +458,4 @@ class LatentKeyframeBatchedGroupNode:
for latent_keyframe in prev_latent_keyframe.keyframes:
curr_latent_keyframe.add(latent_keyframe)
return (curr_latent_keyframe,)
return io.NodeOutput(curr_latent_keyframe,)
+114 -104
View File
@@ -1,107 +1,120 @@
from comfy_api.latest import io
from torch import Tensor
import folder_paths
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 .control_lllite import load_anima_lllite
from .utils import ControlWeights, LatentKeyframeGroup, TimestepKeyframeGroup, AbstractPreprocWrapper
from .logger import logger
class ControlNetLoaderAdvanced:
class ControlNetLoaderAdvanced(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"cnet": (folder_paths.get_filename_list("controlnet"), ),
},
"optional": {
"_tk_opt": ("TIMESTEP_KEYFRAME", ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ControlNetLoaderAdvanced',
display_name='Load Advanced ControlNet Model 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝',
inputs=[
io.Combo.Input('cnet', options=folder_paths.get_filename_list("controlnet")),
io.Custom('TIMESTEP_KEYFRAME').Input('_tk_opt', optional=True)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝"
def load_controlnet(self, cnet,
@classmethod
def execute(cls, cnet,
_tk_opt: TimestepKeyframeGroup=None,
):
controlnet_path = folder_paths.get_full_path("controlnet", cnet)
controlnet = load_controlnet(controlnet_path, _tk_opt)
return (controlnet,)
return io.NodeOutput(controlnet,)
class DiffControlNetLoaderAdvanced:
class DiffControlNetLoaderAdvanced(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model": ("MODEL",),
"cnet": (folder_paths.get_filename_list("controlnet"), )
},
"optional": {
"_tk_opt": ("TIMESTEP_KEYFRAME", ),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_DiffControlNetLoaderAdvanced',
display_name='Load Advanced ControlNet Model (diff) 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝',
inputs=[
io.Model.Input('model'),
io.Combo.Input('cnet', options=folder_paths.get_filename_list("controlnet")),
io.Custom('TIMESTEP_KEYFRAME').Input('_tk_opt', optional=True)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝"
def load_controlnet(self, cnet, model,
@classmethod
def execute(cls, cnet, model,
_tk_opt: TimestepKeyframeGroup=None,
):
controlnet_path = folder_paths.get_full_path("controlnet", cnet)
controlnet = load_controlnet(controlnet_path, _tk_opt, model)
if is_advanced_controlnet(controlnet):
controlnet.verify_all_weights()
return (controlnet,)
return io.NodeOutput(controlnet,)
class AdvancedControlNetApply:
class AnimaLLLiteLoaderAdvanced(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"positive": ("CONDITIONING", ),
"negative": ("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", ),
"vae_optional": ("VAE",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_AnimaLLLiteLoaderAdvanced',
display_name='Load Anima LLLite Model 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/loaders',
inputs=[
io.Combo.Input('model_patch', options=folder_paths.get_filename_list("model_patches")),
io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONDITIONING","CONDITIONING",)
RETURN_NAMES = ("positive", "negative")
FUNCTION = "apply_controlnet"
@classmethod
def execute(cls, model_patch, timestep_kf: TimestepKeyframeGroup=None):
model_patch_path = folder_paths.get_full_path_or_raise("model_patches", model_patch)
return io.NodeOutput(load_anima_lllite(model_patch_path, timestep_keyframe=timestep_kf),)
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝"
class AdvancedControlNetApply(io.ComfyNode):
@classmethod
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_AdvancedControlNetApply_v2',
display_name='Apply Advanced ControlNet 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝',
inputs=[
io.Conditioning.Input('positive'),
io.Conditioning.Input('negative'),
io.ControlNet.Input('control_net'),
io.Image.Input('image'),
io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01),
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001),
io.Mask.Input('mask_optional', optional=True),
io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_kf_override', optional=True),
io.Custom('CONTROL_NET_WEIGHTS').Input('weights_override', optional=True),
io.Vae.Input('vae_optional', optional=True)
],
outputs=[
io.Conditioning.Output('positive', is_output_list=False),
io.Conditioning.Output('negative', is_output_list=False)
]
)
def apply_controlnet(self, positive, negative, control_net, image, strength, start_percent, end_percent,
@classmethod
def execute(cls, positive, negative, control_net, image, strength, start_percent, end_percent,
mask_optional: Tensor=None, vae_optional=None,
timestep_kf: TimestepKeyframeGroup=None, latent_kf_override: LatentKeyframeGroup=None,
weights_override: ControlWeights=None, control_apply_to_uncond=False):
if strength == 0:
return (positive, negative)
return io.NodeOutput(positive, negative)
control_hint = image.movedim(-1,1)
cnets = {}
@@ -167,46 +180,43 @@ class AdvancedControlNetApply:
n = [t[0], d]
c.append(n)
out.append(c)
return (out[0], out[1])
return io.NodeOutput(out[0], out[1])
class AdvancedControlNetApplySingle:
class AdvancedControlNetApplySingle(io.ComfyNode):
@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", ),
"vae_optional": ("VAE",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_AdvancedControlNetApplySingle_v2',
display_name='Apply Advanced ControlNet(1) 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝',
inputs=[
io.Conditioning.Input('conditioning'),
io.ControlNet.Input('control_net'),
io.Image.Input('image'),
io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01),
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001),
io.Mask.Input('mask_optional', optional=True),
io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_kf_override', optional=True),
io.Custom('CONTROL_NET_WEIGHTS').Input('weights_override', optional=True),
io.Vae.Input('vae_optional', optional=True)
],
outputs=[
io.Conditioning.Output('CONDITIONING', is_output_list=False),
io.Model.Output('model_opt', is_output_list=False)
]
)
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,
@classmethod
def execute(cls, conditioning, control_net, image, strength, start_percent, end_percent,
mask_optional: Tensor=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,
values = AdvancedControlNetApply.execute(positive=conditioning, negative=None, control_net=control_net, image=image,
strength=strength, start_percent=start_percent, end_percent=end_percent,
mask_optional=mask_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],)
return io.NodeOutput(values.args[0], None)
+55 -54
View File
@@ -1,81 +1,82 @@
from comfy_api.latest import io
from torch import Tensor
import math
import folder_paths
from .control_plusplus import load_controlnetplusplus, PlusPlusType, PlusPlusInput, PlusPlusInputGroup, PlusPlusImageWrapper
from .utils import BIGMAX
from .control_plusplus import load_controlnetplusplus, PlusPlusInput, PlusPlusInputGroup, PlusPlusImageWrapper
class PlusPlusLoaderAdvanced:
class PlusPlusLoaderAdvanced(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"plus_input": ("PLUS_INPUT", ),
"name": (folder_paths.get_filename_list("controlnet"), ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ControlNet++LoaderAdvanced',
display_name='Load ControlNet++ Model (Multi) 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/ControlNet++',
inputs=[
io.Custom('PLUS_INPUT').Input('plus_input'),
io.Combo.Input('name', options=folder_paths.get_filename_list("controlnet"))
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False),
io.Image.Output('IMAGE', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", "IMAGE",)
FUNCTION = "load_controlnet_plusplus"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/ControlNet++"
def load_controlnet_plusplus(self, plus_input: PlusPlusInputGroup, name: str):
@classmethod
def execute(cls, plus_input: PlusPlusInputGroup, name: str):
controlnet_path = folder_paths.get_full_path("controlnet", name)
controlnet = load_controlnetplusplus(controlnet_path)
controlnet.verify_control_type(name, plus_input)
controlnet.allow_condhint_latents = True
return (controlnet, PlusPlusImageWrapper(plus_input),)
return io.NodeOutput(controlnet, PlusPlusImageWrapper(plus_input),)
class PlusPlusLoaderSingle:
class PlusPlusLoaderSingle(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"name": (folder_paths.get_filename_list("controlnet"), ),
"control_type": (PlusPlusType._LIST_WITH_NONE, {"default": PlusPlusType.NONE}, ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ControlNet++LoaderSingle',
display_name='Load ControlNet++ Model (Single) 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/ControlNet++',
inputs=[
io.Combo.Input('name', options=folder_paths.get_filename_list("controlnet")),
io.Combo.Input('control_type', options=['openpose', 'depth', 'hed/pidi/scribble/ted', 'canny/lineart/mlsd', 'normal', 'segment', 'tile', 'inpaint/outpaint', 'none'], default='none')
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET",)
FUNCTION = "load_controlnet_plusplus"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/ControlNet++"
def load_controlnet_plusplus(self, name: str, control_type: str):
@classmethod
def execute(cls, name: str, control_type: str):
controlnet_path = folder_paths.get_full_path("controlnet", name)
controlnet = load_controlnetplusplus(controlnet_path)
controlnet.single_control_type = control_type
controlnet.verify_control_type(name)
return (controlnet,)
return io.NodeOutput(controlnet,)
class PlusPlusInputNode:
class PlusPlusInputNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE",),
"control_type": (PlusPlusType._LIST,),
},
"optional": {
"prev_plus_input": ("PLUS_INPUT",),
#"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": BIGMAX, "step": 0.01}),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ControlNet++InputNode',
display_name='ControlNet++ Input 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/ControlNet++',
inputs=[
io.Image.Input('image'),
io.Combo.Input('control_type', options=['openpose', 'depth', 'hed/pidi/scribble/ted', 'canny/lineart/mlsd', 'normal', 'segment', 'tile', 'inpaint/outpaint']),
io.Custom('PLUS_INPUT').Input('prev_plus_input', optional=True)
],
outputs=[
io.Custom('PLUS_INPUT').Output('PLUS_INPUT', is_output_list=False)
]
)
RETURN_TYPES = ("PLUS_INPUT", )
FUNCTION = "wrap_images"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/ControlNet++"
def wrap_images(self, image: Tensor, control_type: str, strength=1.0, prev_plus_input: PlusPlusInputGroup=None):
@classmethod
def execute(cls, image: Tensor, control_type: str, strength=1.0, prev_plus_input: PlusPlusInputGroup=None):
if prev_plus_input is None:
prev_plus_input = PlusPlusInputGroup()
prev_plus_input = prev_plus_input.clone()
@@ -85,4 +86,4 @@ class PlusPlusInputNode:
pp_input = PlusPlusInput(image, control_type, strength)
prev_plus_input.add(pp_input)
return (prev_plus_input,)
return io.NodeOutput(prev_plus_input,)
+58 -53
View File
@@ -1,3 +1,4 @@
from comfy_api.latest import io
from torch import Tensor
from nodes import VAEEncode
@@ -6,77 +7,81 @@ from comfy.sd import VAE
from .control_reference import ReferenceAdvanced, ReferenceOptions, ReferenceType, ReferencePreprocWrapper
# node for ReferenceCN
class ReferenceControlNetNode:
class ReferenceControlNetNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"reference_type": (ReferenceType._LIST,),
"style_fidelity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"ref_weight": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ReferenceControlNet',
display_name='Reference ControlNet 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/Reference',
inputs=[
io.Combo.Input('reference_type', options=['reference_attn', 'reference_adain', 'reference_attn+adain']),
io.Float.Input('style_fidelity', default=0.5, max=1.0, min=0.0, step=0.01),
io.Float.Input('ref_weight', default=1.0, max=1.0, min=0.0, step=0.01)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/Reference"
def load_controlnet(self, reference_type: str, style_fidelity: float, ref_weight: float):
@classmethod
def execute(cls, reference_type: str, style_fidelity: float, ref_weight: float):
ref_opts = ReferenceOptions.create_combo(reference_type=reference_type, style_fidelity=style_fidelity, ref_weight=ref_weight)
controlnet = ReferenceAdvanced(ref_opts=ref_opts, timestep_keyframes=None)
return (controlnet,)
return io.NodeOutput(controlnet,)
class ReferenceControlFinetune:
class ReferenceControlFinetune(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"attn_style_fidelity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"attn_ref_weight": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"attn_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"adain_style_fidelity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"adain_ref_weight": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"adain_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ReferenceControlNetFinetune',
display_name='Reference ControlNet (Finetune) 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/Reference',
inputs=[
io.Float.Input('attn_style_fidelity', default=0.5, max=1.0, min=0.0, step=0.01),
io.Float.Input('attn_ref_weight', default=1.0, max=1.0, min=0.0, step=0.01),
io.Float.Input('attn_strength', default=1.0, max=1.0, min=0.0, step=0.01),
io.Float.Input('adain_style_fidelity', default=0.5, max=1.0, min=0.0, step=0.01),
io.Float.Input('adain_ref_weight', default=1.0, max=1.0, min=0.0, step=0.01),
io.Float.Input('adain_strength', default=1.0, max=1.0, min=0.0, step=0.01)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/Reference"
def load_controlnet(self,
@classmethod
def execute(cls,
attn_style_fidelity: float, attn_ref_weight: float, attn_strength: float,
adain_style_fidelity: float, adain_ref_weight: float, adain_strength: float):
ref_opts = ReferenceOptions(reference_type=ReferenceType.ATTN_ADAIN,
attn_style_fidelity=attn_style_fidelity, attn_ref_weight=attn_ref_weight, attn_strength=attn_strength,
adain_style_fidelity=adain_style_fidelity, adain_ref_weight=adain_ref_weight, adain_strength=adain_strength)
controlnet = ReferenceAdvanced(ref_opts=ref_opts, timestep_keyframes=None)
return (controlnet,)
return io.NodeOutput(controlnet,)
class ReferencePreprocessorNode:
class ReferencePreprocessorNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE", ),
"vae": ("VAE", ),
"latent_size": ("LATENT", ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ReferencePreprocessor',
display_name='Reference Preproccessor 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/Reference/preprocess',
inputs=[
io.Image.Input('image'),
io.Vae.Input('vae'),
io.Latent.Input('latent_size')
],
outputs=[
io.Image.Output('proc_IMAGE', is_output_list=False)
]
)
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("proc_IMAGE",)
FUNCTION = "preprocess_images"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/Reference/preprocess"
def preprocess_images(self, vae: VAE, image: Tensor, latent_size: Tensor):
@classmethod
def execute(cls, vae: VAE, image: Tensor, latent_size: Tensor):
# first, resize image to match latents
image = image.movedim(-1,1)
image = comfy.utils.common_upscale(image, latent_size["samples"].shape[3] * 8, latent_size["samples"].shape[2] * 8, 'nearest-exact', "center")
@@ -87,4 +92,4 @@ class ReferencePreprocessorNode:
except Exception:
image = VAEEncode.vae_encode_crop_pixels(image)
encoded = vae.encode(image[:,:,:,:3])
return (ReferencePreprocWrapper(condhint=encoded),)
return io.NodeOutput(ReferencePreprocWrapper(condhint=encoded),)
+118 -118
View File
@@ -1,3 +1,4 @@
from comfy_api.latest import io
from torch import Tensor
import folder_paths
@@ -7,68 +8,68 @@ from comfy.sd import VAE
from .utils import TimestepKeyframeGroup
from .control_sparsectrl import SparseMethod, SparseIndexMethod, SparseSettings, SparseSpreadMethod, PreprocSparseRGBWrapper, SparseConst, SparseContextAware, get_idx_list_from_str
from .control import load_sparsectrl, load_controlnet, ControlNetAdvanced, SparseCtrlAdvanced
from .control import load_sparsectrl, load_controlnet, ControlNetAdvanced
# node for SparseCtrl loading
class SparseCtrlLoaderAdvanced:
class SparseCtrlLoaderAdvanced(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"sparsectrl_name": (folder_paths.get_filename_list("controlnet"), ),
"use_motion": ("BOOLEAN", {"default": True}, ),
"motion_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
},
"optional": {
"sparse_method": ("SPARSE_METHOD", ),
"tk_optional": ("TIMESTEP_KEYFRAME", ),
"context_aware": (SparseContextAware.LIST, ),
"sparse_hint_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"sparse_nonhint_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"sparse_mask_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SparseCtrlLoaderAdvanced',
display_name='Load SparseCtrl Model 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl',
inputs=[
io.Combo.Input('sparsectrl_name', options=folder_paths.get_filename_list("controlnet")),
io.Boolean.Input('use_motion', default=True),
io.Float.Input('motion_strength', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('motion_scale', default=1.0, max=10.0, min=0.0, step=0.001),
io.Custom('SPARSE_METHOD').Input('sparse_method', optional=True),
io.Custom('TIMESTEP_KEYFRAME').Input('tk_optional', optional=True),
io.Combo.Input('context_aware', optional=True, options=['nearest_hint', 'off']),
io.Float.Input('sparse_hint_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('sparse_nonhint_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('sparse_mask_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl"
def load_controlnet(self, sparsectrl_name: str, use_motion: bool, motion_strength: float, motion_scale: float, sparse_method: SparseMethod=SparseSpreadMethod(), tk_optional: TimestepKeyframeGroup=None,
@classmethod
def execute(cls, sparsectrl_name: str, use_motion: bool, motion_strength: float, motion_scale: float, sparse_method: SparseMethod=SparseSpreadMethod(), tk_optional: TimestepKeyframeGroup=None,
context_aware=SparseContextAware.NEAREST_HINT, sparse_hint_mult=1.0, sparse_nonhint_mult=1.0, sparse_mask_mult=1.0):
sparsectrl_path = folder_paths.get_full_path("controlnet", sparsectrl_name)
sparse_settings = SparseSettings(sparse_method=sparse_method, use_motion=use_motion, motion_strength=motion_strength, motion_scale=motion_scale,
context_aware=context_aware,
sparse_mask_mult=sparse_mask_mult, sparse_hint_mult=sparse_hint_mult, sparse_nonhint_mult=sparse_nonhint_mult)
sparsectrl = load_sparsectrl(sparsectrl_path, timestep_keyframe=tk_optional, sparse_settings=sparse_settings)
return (sparsectrl,)
return io.NodeOutput(sparsectrl,)
class SparseCtrlMergedLoaderAdvanced:
class SparseCtrlMergedLoaderAdvanced(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"sparsectrl_name": (folder_paths.get_filename_list("controlnet"), ),
"control_net_name": (folder_paths.get_filename_list("controlnet"), ),
"use_motion": ("BOOLEAN", {"default": True}, ),
"motion_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
},
"optional": {
"sparse_method": ("SPARSE_METHOD", ),
"tk_optional": ("TIMESTEP_KEYFRAME", ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SparseCtrlMergedLoaderAdvanced',
display_name='🧪Load Merged SparseCtrl Model 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl/experimental',
inputs=[
io.Combo.Input('sparsectrl_name', options=folder_paths.get_filename_list("controlnet")),
io.Combo.Input('control_net_name', options=folder_paths.get_filename_list("controlnet")),
io.Boolean.Input('use_motion', default=True),
io.Float.Input('motion_strength', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('motion_scale', default=1.0, max=10.0, min=0.0, step=0.001),
io.Custom('SPARSE_METHOD').Input('sparse_method', optional=True),
io.Custom('TIMESTEP_KEYFRAME').Input('tk_optional', optional=True)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl/experimental"
def load_controlnet(self, sparsectrl_name: str, control_net_name: str, use_motion: bool, motion_strength: float, motion_scale: float, sparse_method: SparseMethod=SparseSpreadMethod(), tk_optional: TimestepKeyframeGroup=None):
@classmethod
def execute(cls, sparsectrl_name: str, control_net_name: str, use_motion: bool, motion_strength: float, motion_scale: float, sparse_method: SparseMethod=SparseSpreadMethod(), tk_optional: TimestepKeyframeGroup=None):
sparsectrl_path = folder_paths.get_full_path("controlnet", sparsectrl_name)
controlnet_path = folder_paths.get_full_path("controlnet", control_net_name)
sparse_settings = SparseSettings(sparse_method=sparse_method, use_motion=use_motion, motion_strength=motion_strength, motion_scale=motion_scale, merged=True)
@@ -85,67 +86,68 @@ class SparseCtrlMergedLoaderAdvanced:
new_state_dict[key] = value
# now, reload sparsectrl with real settings
sparsectrl = load_sparsectrl(sparsectrl_path, controlnet_data=new_state_dict, timestep_keyframe=tk_optional, sparse_settings=sparse_settings)
return (sparsectrl,)
return io.NodeOutput(sparsectrl,)
class SparseIndexMethodNode:
class SparseIndexMethodNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"indexes": ("STRING", {"default": "0"}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SparseCtrlIndexMethodNode',
display_name='SparseCtrl Index Method 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl',
inputs=[
io.String.Input('indexes', default='0')
],
outputs=[
io.Custom('SPARSE_METHOD').Output('SPARSE_METHOD', is_output_list=False)
]
)
RETURN_TYPES = ("SPARSE_METHOD",)
FUNCTION = "get_method"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl"
def get_method(self, indexes: str):
@classmethod
def execute(cls, indexes: str):
idxs = get_idx_list_from_str(indexes)
return (SparseIndexMethod(idxs),)
return io.NodeOutput(SparseIndexMethod(idxs),)
class SparseSpreadMethodNode:
class SparseSpreadMethodNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"spread": (SparseSpreadMethod.LIST,),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SparseCtrlSpreadMethodNode',
display_name='SparseCtrl Spread Method 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl',
inputs=[
io.Combo.Input('spread', options=['uniform', 'starting', 'ending', 'center'])
],
outputs=[
io.Custom('SPARSE_METHOD').Output('SPARSE_METHOD', is_output_list=False)
]
)
RETURN_TYPES = ("SPARSE_METHOD",)
FUNCTION = "get_method"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl"
def get_method(self, spread: str):
return (SparseSpreadMethod(spread=spread),)
class RgbSparseCtrlPreprocessor:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE", ),
"vae": ("VAE", ),
"latent_size": ("LATENT", ),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def execute(cls, spread: str):
return io.NodeOutput(SparseSpreadMethod(spread=spread),)
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("proc_IMAGE",)
FUNCTION = "preprocess_images"
class RgbSparseCtrlPreprocessor(io.ComfyNode):
@classmethod
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SparseCtrlRGBPreprocessor',
display_name='RGB SparseCtrl 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl/preprocess',
inputs=[
io.Image.Input('image'),
io.Vae.Input('vae'),
io.Latent.Input('latent_size')
],
outputs=[
io.Image.Output('proc_IMAGE', is_output_list=False)
]
)
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl/preprocess"
def preprocess_images(self, vae: VAE, image: Tensor, latent_size: Tensor):
@classmethod
def execute(cls, vae: VAE, image: Tensor, latent_size: Tensor):
# first, resize image to match latents
image = image.movedim(-1,1)
image = comfy.utils.common_upscale(image, latent_size["samples"].shape[3] * 8, latent_size["samples"].shape[2] * 8, 'nearest-exact', "center")
@@ -156,33 +158,31 @@ class RgbSparseCtrlPreprocessor:
except Exception:
image = VAEEncode.vae_encode_crop_pixels(image)
encoded = vae.encode(image[:,:,:,:3])
return (PreprocSparseRGBWrapper(condhint=encoded),)
return io.NodeOutput(PreprocSparseRGBWrapper(condhint=encoded),)
class SparseWeightExtras:
class SparseWeightExtras(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"optional": {
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
"sparse_hint_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"sparse_nonhint_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"sparse_mask_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SparseCtrlWeightExtras',
display_name='SparseCtrl Weight Extras 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl/extras',
inputs=[
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True),
io.Float.Input('sparse_hint_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('sparse_nonhint_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('sparse_mask_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001)
],
outputs=[
io.Custom('CN_WEIGHTS_EXTRAS').Output('cn_extras', is_output_list=False)
]
)
RETURN_TYPES = ("CN_WEIGHTS_EXTRAS", )
RETURN_NAMES = ("cn_extras", )
FUNCTION = "create_weight_extras"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl/extras"
def create_weight_extras(self, cn_extras: dict[str]={}, sparse_hint_mult=1.0, sparse_nonhint_mult=1.0, sparse_mask_mult=1.0):
@classmethod
def execute(cls, cn_extras: dict[str]={}, sparse_hint_mult=1.0, sparse_nonhint_mult=1.0, sparse_mask_mult=1.0):
cn_extras = cn_extras.copy()
cn_extras[SparseConst.HINT_MULT] = sparse_hint_mult
cn_extras[SparseConst.NONHINT_MULT] = sparse_nonhint_mult
cn_extras[SparseConst.MASK_MULT] = sparse_mask_mult
return (cn_extras, )
return io.NodeOutput(cn_extras, )
+296 -247
View File
@@ -1,62 +1,56 @@
from comfy_api.latest import io
from torch import Tensor
import torch
from .utils import TimestepKeyframe, TimestepKeyframeGroup, ControlWeights, Extras, get_properly_arranged_t2i_weights, linear_conversion
from .logger import logger
from .control_lllite import AnimaLLLiteConst
WEIGHTS_RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT")
class DefaultWeights:
class DefaultWeights(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"optional": {
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_DefaultUniversalWeights',
display_name='Default Weights 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights',
inputs=[
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights"
def load_weights(self, cn_extras: dict[str]={}):
@classmethod
def execute(cls, cn_extras: dict[str]={}):
weights = ControlWeights.default(extras=cn_extras)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class ScaledSoftMaskedUniversalWeights:
class ScaledSoftMaskedUniversalWeights(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"mask": ("MASK", ),
"min_base_multiplier": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}, ),
"max_base_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}, ),
#"lock_min": ("BOOLEAN", {"default": False}, ),
#"lock_max": ("BOOLEAN", {"default": False}, ),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ScaledSoftMaskedUniversalWeights',
display_name='Scaled Soft Masked Weights 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights',
inputs=[
io.Mask.Input('mask'),
io.Float.Input('min_base_multiplier', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('max_base_multiplier', default=1.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights"
def load_weights(self, mask: Tensor, min_base_multiplier: float, max_base_multiplier: float, lock_min=False, lock_max=False,
@classmethod
def execute(cls, mask: Tensor, min_base_multiplier: float, max_base_multiplier: float, lock_min=False, lock_max=False,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
# normalize mask
mask = mask.clone()
@@ -67,116 +61,107 @@ class ScaledSoftMaskedUniversalWeights:
else:
mask = linear_conversion(mask, x_min, x_max, min_base_multiplier, max_base_multiplier)
weights = ControlWeights.universal_mask(weight_mask=mask, uncond_multiplier=uncond_multiplier, extras=cn_extras)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class ScaledSoftUniversalWeights:
class ScaledSoftUniversalWeights(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"base_multiplier": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 1.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",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ScaledSoftControlNetWeights',
display_name='Scaled Soft Weights 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights',
inputs=[
io.Float.Input('base_multiplier', default=0.825, max=1.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights"
def load_weights(self, base_multiplier, uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
@classmethod
def execute(cls, 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)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class SoftControlNetWeightsSD15:
class SoftControlNetWeightsSD15(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"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",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SoftControlNetWeightsSD15',
display_name='ControlNet Soft Weights [SD1.5] 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet',
inputs=[
io.Float.Input('output_0', default=0.09941396206337118, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_1', default=0.12050177219802567, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_2', default=0.14606275417942507, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_3', default=0.17704576264172736, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_4', default=0.214600924414215, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_5', default=0.26012233262329093, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_6', default=0.3152997971191405, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_7', default=0.3821815722656249, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_8', default=0.4632503906249999, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_9', default=0.561515625, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_10', default=0.6806249999999999, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_11', default=0.825, max=10.0, min=0.0, step=0.001),
io.Float.Input('middle_0', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
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,
@classmethod
def execute(cls, 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]={}):
return CustomControlNetWeightsSD15.load_weights(self,
return CustomControlNetWeightsSD15.execute(
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:
class CustomControlNetWeightsSD15(io.ComfyNode):
@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",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_CustomControlNetWeightsSD15',
display_name='ControlNet Custom Weights [SD1.5] 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet',
inputs=[
io.Float.Input('output_0', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_1', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_2', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_3', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_4', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_5', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_6', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_7', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_8', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_9', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_10', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_11', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('middle_0', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
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,
@classmethod
def execute(cls, 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,
@@ -184,50 +169,47 @@ class CustomControlNetWeightsSD15:
weights_middle = [middle_0]
weights = ControlWeights.controlnet(weights_output=weights_output, weights_middle=weights_middle, uncond_multiplier=uncond_multiplier,
extras=cn_extras, disable_applied_to=True)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class CustomControlNetWeightsFlux:
class CustomControlNetWeightsFlux(io.ComfyNode):
@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",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_CustomControlNetWeightsFlux',
display_name='ControlNet Custom Weights [Flux] 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet',
inputs=[
io.Float.Input('input_0', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_1', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_2', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_3', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_4', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_5', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_6', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_7', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_8', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_9', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_10', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_11', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_12', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_13', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_14', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_15', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_16', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_17', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_18', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
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,
@classmethod
def execute(cls, 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]={}):
@@ -235,95 +217,162 @@ class CustomControlNetWeightsFlux:
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, disable_applied_to=True)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class SoftT2IAdapterWeights:
class CustomControlNetWeightsAnima(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"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",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_CustomControlNetWeightsAnima',
display_name='ControlNet Custom Weights [Anima] 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet',
inputs=[
io.Float.Input('block_0', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_1', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_2', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_3', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_4', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_5', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_6', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_7', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_8', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_9', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_10', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_11', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_12', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_13', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_14', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_15', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_16', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_17', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_18', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_19', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_20', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_21', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_22', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_23', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_24', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_25', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_26', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_27', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
@classmethod
def execute(cls, uncond_multiplier: float=1.0, cn_extras: dict[str]={}, **kwargs):
weights = [kwargs[f"block_{index}"] for index in range(28)]
control_weights = ControlWeights.controllllite(
weights_input=weights,
uncond_multiplier=uncond_multiplier,
extras=cn_extras,
)
return io.NodeOutput(control_weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=control_weights)))
class SoftT2IAdapterWeights(io.ComfyNode):
@classmethod
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SoftT2IAdapterWeights',
display_name='T2IAdapter Soft Weights 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/T2IAdapter',
inputs=[
io.Float.Input('input_0', default=0.25, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_1', default=0.62, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_2', default=0.825, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_3', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/T2IAdapter"
def load_weights(self, input_0, input_1, input_2, input_3,
@classmethod
def execute(cls, input_0, input_1, input_2, input_3,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
return CustomT2IAdapterWeights.load_weights(self, input_0=input_0, input_1=input_1, input_2=input_2, input_3=input_3,
return CustomT2IAdapterWeights.execute(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:
class CustomT2IAdapterWeights(io.ComfyNode):
@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}, ),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_CustomT2IAdapterWeights',
display_name='T2IAdapter Custom Weights 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/T2IAdapter',
inputs=[
io.Float.Input('input_0', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_1', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_2', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_3', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/T2IAdapter"
def load_weights(self, input_0, input_1, input_2, input_3,
@classmethod
def execute(cls, input_0, input_1, input_2, input_3,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
weights = [input_0, input_1, input_2, input_3]
weights = get_properly_arranged_t2i_weights(weights)
weights = ControlWeights.t2iadapter(weights_input=weights, uncond_multiplier=uncond_multiplier, extras=cn_extras, disable_applied_to=True)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class ExtrasMiddleMultNode:
class ExtrasMiddleMultNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"middle_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}),
},
"optional": {
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
"hidden": {
"autosize": ("ACNAUTOSIZE", {"padding": 0}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ExtrasMiddleMult',
display_name='Middle Weight Extras 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/extras',
inputs=[
io.Float.Input('middle_mult', default=1.0, max=10.0, min=0.0, step=0.001),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CN_WEIGHTS_EXTRAS').Output('cn_extras', is_output_list=False)
]
)
RETURN_TYPES = ("CN_WEIGHTS_EXTRAS",)
RETURN_NAMES = ("cn_extras",)
FUNCTION = "create_extras"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/extras"
def create_extras(self, middle_mult: float, cn_extras: dict[str]={}):
@classmethod
def execute(cls, middle_mult: float, cn_extras: dict[str]={}):
cn_extras = cn_extras.copy()
cn_extras[Extras.MIDDLE_MULT] = middle_mult
return (cn_extras,)
return io.NodeOutput(cn_extras,)
class AnimaLLLiteExtras(io.ComfyNode):
@classmethod
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_AnimaLLLiteExtras',
display_name='Anima LLLite Extras 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/extras',
inputs=[
io.Mask.Input('inpaint_mask'),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CN_WEIGHTS_EXTRAS').Output('cn_extras', is_output_list=False)
]
)
@classmethod
def execute(cls, inpaint_mask: Tensor, cn_extras: dict[str]={}):
cn_extras = cn_extras.copy()
cn_extras[AnimaLLLiteConst.INPAINT_MASK] = inpaint_mask.clone()
return io.NodeOutput(cn_extras,)
+144
View File
@@ -0,0 +1,144 @@
# Anima LLLite validation workflows
This folder contains simple Advanced-ControlNet workflows for the two Anima
LLLite v2 checkpoints: the five conditioning inputs documented for the v2
any-test-like model and the v2 inpainting model. It also includes vanilla parity
and effect-mask validation workflows.
![Simple workflows for all six v2 examples](https://ampcode.com/attachments/3ef84a8ad6947c624cd82b5ac6fb9c664b224678c774a5eba79f4f7e6e8d5c3f.jpeg)
![Control images and tested results for all six v2 examples](https://ampcode.com/attachments/addd1fdb85aa331cc622ef55a9397a996f34c5243e268a1b9bcc42ca39a25120.jpeg)
## Simple v2 workflows
| Type | Workflow | Input image | Checkpoint |
| --- | --- | --- | --- |
| Any - Grayscale A | [`anima_lllite_any_grayscale_a.json`](anima_lllite_any_grayscale_a.json) | [`anima_lllite_any_grayscale_a_control.png`](https://ampcode.com/attachments/911b31c414bcf5c9aeaa254356c89f04e1133ce2181d0414cd8b7e9a7fa7847c.png) | `anima-lllite-any-test-like-v2.safetensors` |
| Any - Grayscale B | [`anima_lllite_any_grayscale_b.json`](anima_lllite_any_grayscale_b.json) | [`anima_lllite_any_grayscale_b_control.png`](https://ampcode.com/attachments/6950b3c8a47ff62311cc8ac147e2c8aa6aad9176abe7046db3a1bd6cf97cf705.png) | `anima-lllite-any-test-like-v2.safetensors` |
| Any - Lineart | [`anima_lllite_any_lineart.json`](anima_lllite_any_lineart.json) | [`anima_lllite_any_lineart_control.png`](https://ampcode.com/attachments/338c2f07267d564ca9fb60bf79c229b5689509202734875f0cb674efb8d691b1.png) | `anima-lllite-any-test-like-v2.safetensors` |
| Any - HED scribble | [`anima_lllite_any_hed_scribble.json`](anima_lllite_any_hed_scribble.json) | [`anima_lllite_any_hed_scribble_control.png`](https://ampcode.com/attachments/a52217f09c5852652667152cb901b98c3472c2e4cbf86a6cae53a36ec7e84192.png) | `anima-lllite-any-test-like-v2.safetensors` |
| Any - PiDiNet scribble | [`anima_lllite_any_pidinet_scribble.json`](anima_lllite_any_pidinet_scribble.json) | [`anima_lllite_any_pidinet_scribble_control.png`](https://ampcode.com/attachments/57bc6b0d8cba0df17f92115917bfc6ccfce83107e56af624f0be1db32c402579.png) | `anima-lllite-any-test-like-v2.safetensors` |
| Inpainting | [`anima_lllite_inpainting.json`](anima_lllite_inpainting.json) | [`anima_lllite_v2_control.png`](https://ampcode.com/attachments/3220b2f533a014619e3c0422eca8a3343e8488eb113795af5bae5f05d38a8ada.png) | `anima-lllite-inpainting-v2.safetensors` |
Download the selected input image from the table and save it under the displayed
filename in `ComfyUI/input`. Load its workflow and queue it unchanged. Results
are saved under `ComfyUI/output/acn_anima_examples`. Binary inputs and
screenshots are linked externally instead of being committed to this repository.
These examples use default node names and do not connect the custom 28-layer
Anima weights node. The inpainting workflow uses Anima LLLite Extras and
Default Weights only because the model's source mask must be carried through
`cn_extras`. Strength, start/end scheduling, effect masks, timestep keyframes,
latent keyframes, and stacking remain available on the standard
Advanced-ControlNet nodes.
The model author trained the v2 any-test-like checkpoint on five conditioning
types: HED scribble, PiDiNet scribble, Grayscale A, Grayscale B, and lineart,
all with heavy augmentation. The examples above exercise each input type using
that one v2 checkpoint instead of the lower-quality Preview3 depth, pose,
lineart, and scribble checkpoints.
The HED and PiDiNet controls were prepared with the
[comfyui_controlnet_aux](https://github.com/Fannovel16/comfyui_controlnet_aux)
HED and Scribble PiDiNet preprocessors. The HED soft-edge output was thresholded
at 80 to make the documented white-on-black scribble representation. The model
card names two grayscale generation patterns but does not define their
individual construction; Grayscale A is the author's sample and Grayscale B
demonstrates the documented inversion, contrast, and blur augmentation.
## Requirements
Official Hugging Face repositories:
- [circlestone-labs/Anima](https://huggingface.co/circlestone-labs/Anima) - base model, text encoder, and VAE
- [kohya-ss/Anima-LLLite](https://huggingface.co/kohya-ss/Anima-LLLite) - Anima LLLite control models
Use ComfyUI commit `0f42ba514631` or later and place these files in the listed
model folders. These are direct downloads from the official model repositories:
| Download | ComfyUI model folder |
| --- | --- |
| [`anima-base-v1.0.safetensors`](https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/diffusion_models/anima-base-v1.0.safetensors?download=true) | `models/diffusion_models` |
| [`qwen_3_06b_base.safetensors`](https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/text_encoders/qwen_3_06b_base.safetensors?download=true) | `models/text_encoders` |
| [`qwen_image_vae.safetensors`](https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/vae/qwen_image_vae.safetensors?download=true) | `models/vae` |
| [`anima-lllite-inpainting-v2.safetensors`](https://huggingface.co/kohya-ss/Anima-LLLite/resolve/main/anima-lllite-inpainting-v2.safetensors?download=true) | `models/model_patches` |
| [`anima-lllite-any-test-like-v2.safetensors`](https://huggingface.co/kohya-ss/Anima-LLLite/resolve/main/anima-lllite-any-test-like-v2.safetensors?download=true) (3-channel any-type model) | `models/model_patches` |
The included workflows use **Load Anima LLLite Model**, so their LLLite files
belong in `models/model_patches`, matching vanilla ComfyUI. For compatibility
with existing Advanced-ControlNet LLLite workflows, the files may instead be
placed in `models/controlnet` and loaded with **Load Advanced ControlNet
Model**. The standard loader automatically distinguishes Anima checkpoints
from older SDXL LLLite checkpoints.
## Run the inpainting comparison
This validation workflow runs the inpainting model through vanilla ComfyUI and
Advanced-ControlNet with identical inputs and sampling settings. It saves both
decoded results, both latent tensors, and an absolute pixel-difference image.
![Workflow showing the vanilla and Advanced-ControlNet branches](https://ampcode.com/attachments/f056cae89132c45bc133f456a2832a81255fee9c0782968a24adce2095b2fe1b.jpeg)
![Bit-exact result comparison](https://ampcode.com/attachments/fcf073d1253ea721a2d46d34efde0e45ff6ccfb751fbd295a438ab459cc26037.jpeg)
1. Download [`anima_lllite_v2_control.png`](https://ampcode.com/attachments/3220b2f533a014619e3c0422eca8a3343e8488eb113795af5bae5f05d38a8ada.png)
to `ComfyUI/input`.
2. Load `anima_lllite_v2_inpaint_comparison.json` in ComfyUI.
3. Queue the workflow without changing its settings.
4. Inspect `ComfyUI/output/acn_anima_pr`.
The control PNG contains a transparent edit region. ComfyUI's Load Image node
provides that alpha channel as the source inpainting mask.
The vanilla branch passes the mask directly to Apply Anima LLLite. The
Advanced-ControlNet branch passes it through Anima LLLite Extras, the
`cn_extras` input on Default Weights, and `weights_override` on Apply Advanced
ControlNet.
If an inpainting checkpoint reaches sampling without that source mask,
Advanced-ControlNet raises an error that describes these connections instead
of silently substituting an empty mask.
With the included seed and settings, the expected results are:
- Identical latent tensors with maximum and mean absolute differences of `0.0`.
- Identical decoded PNG pixels.
- A completely black `absolute_difference` image.
## Run the any-type effect-mask comparison
This workflow verifies the official 3-channel any-type checkpoint against
vanilla ComfyUI and applies an Advanced-ControlNet effect mask to only the left
half of the image. Its nodes retain their default names; the colored regions
identify the comparison branches.
![Any-type effect-mask workflow](https://ampcode.com/attachments/3a547b4914a84f0d578b0223149d3ed14e9b22542093e4bc51b6501aeb18f29f.jpeg)
![Any-type parity and effect-mask results](https://ampcode.com/attachments/340c286b28d74d943f03a95d71208f2408c94934f0809fbb060cc774ae8c1b67.jpeg)
1. Download [`anima_lllite_v2_any_control.png`](https://ampcode.com/attachments/db18d41c476324bdf5a5b6117476ed117cc590f8f48e193f6316d08b959bc49c.png)
and [`anima_lllite_v2_left_half_mask.png`](https://ampcode.com/attachments/e4fe3a88415357f29315c9afb124ee975a48dee8abccad1fe1903dd5d21b91b6.png)
to `ComfyUI/input`.
2. Load `anima_lllite_v2_any_effect_mask.json` in ComfyUI.
3. Queue the workflow without changing its settings.
4. Inspect `ComfyUI/output/acn_anima_any_mask`.
The expected results are:
- Advanced-ControlNet full control and vanilla full control have identical
latent tensors and decoded pixels, with maximum absolute difference `0.0`.
- A completely black effect mask produces the no-control latent exactly.
- A completely white effect mask produces the unmasked full-control latent
exactly.
- At the model token resolution, the included half mask is exactly `1.0` on
the left and `0.0` on the right. Direct LLLite injection is therefore zero
for every right-side token.
- In the final image, the masked right side is closer to the no-control
baseline than full control: PSNR improves from `16.39` to `17.69`, and SSIM
improves from `0.554` to `0.600`.
The final right half is not pixel-identical to the no-control image. This model
patches self-attention Q, so controlled left-side tokens can influence
right-side tokens through global self-attention and later diffusion steps.
The effect mask guarantees local control injection, not hard image-space
isolation after attention.
@@ -0,0 +1,941 @@
{
"id": "00000000-0000-0000-0000-000000000000",
"revision": 0,
"last_node_id": 12,
"last_link_id": 26,
"nodes": [
{
"id": 1,
"type": "UNETLoader",
"pos": [
50,
100
],
"size": [
270,
82
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"localized_name": "unet_name",
"name": "unet_name",
"type": "COMBO",
"widget": {
"name": "unet_name"
},
"link": null
},
{
"localized_name": "weight_dtype",
"name": "weight_dtype",
"type": "COMBO",
"widget": {
"name": "weight_dtype"
},
"link": null
}
],
"outputs": [
{
"localized_name": "MODEL",
"name": "MODEL",
"type": "MODEL",
"links": [
20
]
}
],
"properties": {
"Node name for S&R": "UNETLoader",
"models": [
{
"name": "anima-base-v1.0.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/diffusion_models/anima-base-v1.0.safetensors",
"directory": "diffusion_models"
}
]
},
"widgets_values": [
"anima-base-v1.0.safetensors",
"default"
]
},
{
"id": 2,
"type": "CLIPLoader",
"pos": [
50,
250
],
"size": [
270,
106
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"localized_name": "clip_name",
"name": "clip_name",
"type": "COMBO",
"widget": {
"name": "clip_name"
},
"link": null
},
{
"localized_name": "type",
"name": "type",
"type": "COMBO",
"widget": {
"name": "type"
},
"link": null
},
{
"localized_name": "device",
"name": "device",
"shape": 7,
"type": "COMBO",
"widget": {
"name": "device"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CLIP",
"name": "CLIP",
"type": "CLIP",
"links": [
14,
15
]
}
],
"properties": {
"Node name for S&R": "CLIPLoader",
"models": [
{
"name": "qwen_3_06b_base.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/text_encoders/qwen_3_06b_base.safetensors",
"directory": "text_encoders"
}
]
},
"widgets_values": [
"qwen_3_06b_base.safetensors",
"qwen_image",
"default"
]
},
{
"id": 3,
"type": "CLIPTextEncode",
"pos": [
360,
190
],
"size": [
400,
200
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"localized_name": "clip",
"name": "clip",
"type": "CLIP",
"link": 14
},
{
"localized_name": "text",
"name": "text",
"type": "STRING",
"widget": {
"name": "text"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONDITIONING",
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
16
]
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"anime illustration of a female knight in ornate silver plate armor holding a sword, side profile in a dense forest, detailed"
]
},
{
"id": 4,
"type": "CLIPTextEncode",
"pos": [
360,
440
],
"size": [
400,
200
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"localized_name": "clip",
"name": "clip",
"type": "CLIP",
"link": 15
},
{
"localized_name": "text",
"name": "text",
"type": "STRING",
"widget": {
"name": "text"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONDITIONING",
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
17
]
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
""
]
},
{
"id": 5,
"type": "EmptySD3LatentImage",
"pos": [
50,
470
],
"size": [
270,
106
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"localized_name": "width",
"name": "width",
"type": "INT",
"widget": {
"name": "width"
},
"link": null
},
{
"localized_name": "height",
"name": "height",
"type": "INT",
"widget": {
"name": "height"
},
"link": null
},
{
"localized_name": "batch_size",
"name": "batch_size",
"type": "INT",
"widget": {
"name": "batch_size"
},
"link": null
}
],
"outputs": [
{
"localized_name": "LATENT",
"name": "LATENT",
"type": "LATENT",
"links": [
23
]
}
],
"properties": {
"Node name for S&R": "EmptySD3LatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 6,
"type": "LoadImage",
"pos": [
50,
650
],
"size": [
282.798828125,
314
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"localized_name": "image",
"name": "image",
"type": "COMBO",
"widget": {
"name": "image"
},
"link": null
},
{
"localized_name": "choose file to upload",
"name": "upload",
"type": "IMAGEUPLOAD",
"widget": {
"name": "upload"
},
"link": null
}
],
"outputs": [
{
"localized_name": "IMAGE",
"name": "IMAGE",
"type": "IMAGE",
"links": [
19
]
},
{
"localized_name": "MASK",
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"anima_lllite_any_grayscale_a_control.png",
"image"
]
},
{
"id": 7,
"type": "VAELoader",
"pos": [
360,
700
],
"size": [
270,
58
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"localized_name": "vae_name",
"name": "vae_name",
"type": "COMBO",
"widget": {
"name": "vae_name"
},
"link": null
}
],
"outputs": [
{
"localized_name": "VAE",
"name": "VAE",
"type": "VAE",
"links": [
25
]
}
],
"properties": {
"Node name for S&R": "VAELoader",
"models": [
{
"name": "qwen_image_vae.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/vae/qwen_image_vae.safetensors",
"directory": "vae"
}
]
},
"widgets_values": [
"qwen_image_vae.safetensors"
]
},
{
"id": 8,
"type": "ACN_AnimaLLLiteLoaderAdvanced",
"pos": [
860,
120
],
"size": [
315,
58
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"localized_name": "timestep_kf",
"name": "timestep_kf",
"shape": 7,
"type": "TIMESTEP_KEYFRAME",
"link": null
},
{
"localized_name": "model_patch",
"name": "model_patch",
"type": "COMBO",
"widget": {
"name": "model_patch"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONTROL_NET",
"name": "CONTROL_NET",
"type": "CONTROL_NET",
"links": [
18
]
}
],
"properties": {
"Node name for S&R": "ACN_AnimaLLLiteLoaderAdvanced",
"models": [
{
"name": "anima-lllite-any-test-like-v2.safetensors",
"url": "https://huggingface.co/kohya-ss/Anima-LLLite/resolve/main/anima-lllite-any-test-like-v2.safetensors",
"directory": "model_patches"
}
]
},
"widgets_values": [
"anima-lllite-any-test-like-v2.safetensors"
]
},
{
"id": 9,
"type": "ACN_AdvancedControlNetApply_v2",
"pos": [
1210,
130
],
"size": [
287.86183776855466,
266
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"link": 16
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"link": 17
},
{
"localized_name": "control_net",
"name": "control_net",
"type": "CONTROL_NET",
"link": 18
},
{
"localized_name": "image",
"name": "image",
"type": "IMAGE",
"link": 19
},
{
"localized_name": "mask_optional",
"name": "mask_optional",
"shape": 7,
"type": "MASK",
"link": null
},
{
"localized_name": "timestep_kf",
"name": "timestep_kf",
"shape": 7,
"type": "TIMESTEP_KEYFRAME",
"link": null
},
{
"localized_name": "latent_kf_override",
"name": "latent_kf_override",
"shape": 7,
"type": "LATENT_KEYFRAME",
"link": null
},
{
"localized_name": "weights_override",
"name": "weights_override",
"shape": 7,
"type": "CONTROL_NET_WEIGHTS",
"link": null
},
{
"localized_name": "vae_optional",
"name": "vae_optional",
"shape": 7,
"type": "VAE",
"link": null
},
{
"localized_name": "strength",
"name": "strength",
"type": "FLOAT",
"widget": {
"name": "strength"
},
"link": null
},
{
"localized_name": "start_percent",
"name": "start_percent",
"type": "FLOAT",
"widget": {
"name": "start_percent"
},
"link": null
},
{
"localized_name": "end_percent",
"name": "end_percent",
"type": "FLOAT",
"widget": {
"name": "end_percent"
},
"link": null
}
],
"outputs": [
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"links": [
21
]
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"links": [
22
]
}
],
"properties": {
"Node name for S&R": "ACN_AdvancedControlNetApply_v2"
},
"widgets_values": [
1,
0,
1
]
},
{
"id": 10,
"type": "KSampler",
"pos": [
1580,
110
],
"size": [
270,
262
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"localized_name": "model",
"name": "model",
"type": "MODEL",
"link": 20
},
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"link": 21
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"link": 22
},
{
"localized_name": "latent_image",
"name": "latent_image",
"type": "LATENT",
"link": 23
},
{
"localized_name": "seed",
"name": "seed",
"type": "INT",
"widget": {
"name": "seed"
},
"link": null
},
{
"localized_name": "steps",
"name": "steps",
"type": "INT",
"widget": {
"name": "steps"
},
"link": null
},
{
"localized_name": "cfg",
"name": "cfg",
"type": "FLOAT",
"widget": {
"name": "cfg"
},
"link": null
},
{
"localized_name": "sampler_name",
"name": "sampler_name",
"type": "COMBO",
"widget": {
"name": "sampler_name"
},
"link": null
},
{
"localized_name": "scheduler",
"name": "scheduler",
"type": "COMBO",
"widget": {
"name": "scheduler"
},
"link": null
},
{
"localized_name": "denoise",
"name": "denoise",
"type": "FLOAT",
"widget": {
"name": "denoise"
},
"link": null
}
],
"outputs": [
{
"localized_name": "LATENT",
"name": "LATENT",
"type": "LATENT",
"links": [
24
]
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
82000,
"fixed",
12,
1,
"euler",
"simple",
1
]
},
{
"id": 11,
"type": "VAEDecode",
"pos": [
1940,
140
],
"size": [
140,
46
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"localized_name": "samples",
"name": "samples",
"type": "LATENT",
"link": 24
},
{
"localized_name": "vae",
"name": "vae",
"type": "VAE",
"link": 25
}
],
"outputs": [
{
"localized_name": "IMAGE",
"name": "IMAGE",
"type": "IMAGE",
"links": [
26
]
}
],
"properties": {
"Node name for S&R": "VAEDecode"
}
},
{
"id": 12,
"type": "SaveImage",
"pos": [
2190,
110
],
"size": [
270,
58
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"link": 26
},
{
"localized_name": "filename_prefix",
"name": "filename_prefix",
"type": "STRING",
"widget": {
"name": "filename_prefix"
},
"link": null
}
],
"outputs": [
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"links": null
}
],
"properties": {},
"widgets_values": [
"acn_anima_examples/any_grayscale_a"
]
}
],
"links": [
[
14,
2,
0,
3,
0,
"CLIP"
],
[
15,
2,
0,
4,
0,
"CLIP"
],
[
16,
3,
0,
9,
0,
"CONDITIONING"
],
[
17,
4,
0,
9,
1,
"CONDITIONING"
],
[
18,
8,
0,
9,
2,
"CONTROL_NET"
],
[
19,
6,
0,
9,
3,
"IMAGE"
],
[
20,
1,
0,
10,
0,
"MODEL"
],
[
21,
9,
0,
10,
1,
"CONDITIONING"
],
[
22,
9,
1,
10,
2,
"CONDITIONING"
],
[
23,
5,
0,
10,
3,
"LATENT"
],
[
24,
10,
0,
11,
0,
"LATENT"
],
[
25,
7,
0,
11,
1,
"VAE"
],
[
26,
11,
0,
12,
0,
"IMAGE"
]
],
"groups": [
{
"id": 20,
"title": "SHARED INPUTS",
"bounding": [
10,
35,
740,
900
],
"color": "#3f789e",
"font_size": 32,
"flags": {}
},
{
"id": 21,
"title": "ANIMA LLLITE V2 ANY - GRAYSCALE A",
"bounding": [
810,
35,
1660,
900
],
"color": "#6f4d86",
"font_size": 32,
"flags": {}
}
],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
},
"info": {
"name": "Anima LLLite v2 any - grayscale A",
"author": "Kosinkadink",
"description": "Advanced-ControlNet example for Anima LLLite v2 any with grayscale A conditioning."
}
},
"version": 0.4
}
@@ -0,0 +1,941 @@
{
"id": "00000000-0000-0000-0000-000000000000",
"revision": 0,
"last_node_id": 12,
"last_link_id": 26,
"nodes": [
{
"id": 1,
"type": "UNETLoader",
"pos": [
50,
100
],
"size": [
270,
82
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"localized_name": "unet_name",
"name": "unet_name",
"type": "COMBO",
"widget": {
"name": "unet_name"
},
"link": null
},
{
"localized_name": "weight_dtype",
"name": "weight_dtype",
"type": "COMBO",
"widget": {
"name": "weight_dtype"
},
"link": null
}
],
"outputs": [
{
"localized_name": "MODEL",
"name": "MODEL",
"type": "MODEL",
"links": [
20
]
}
],
"properties": {
"Node name for S&R": "UNETLoader",
"models": [
{
"name": "anima-base-v1.0.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/diffusion_models/anima-base-v1.0.safetensors",
"directory": "diffusion_models"
}
]
},
"widgets_values": [
"anima-base-v1.0.safetensors",
"default"
]
},
{
"id": 2,
"type": "CLIPLoader",
"pos": [
50,
250
],
"size": [
270,
106
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"localized_name": "clip_name",
"name": "clip_name",
"type": "COMBO",
"widget": {
"name": "clip_name"
},
"link": null
},
{
"localized_name": "type",
"name": "type",
"type": "COMBO",
"widget": {
"name": "type"
},
"link": null
},
{
"localized_name": "device",
"name": "device",
"shape": 7,
"type": "COMBO",
"widget": {
"name": "device"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CLIP",
"name": "CLIP",
"type": "CLIP",
"links": [
14,
15
]
}
],
"properties": {
"Node name for S&R": "CLIPLoader",
"models": [
{
"name": "qwen_3_06b_base.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/text_encoders/qwen_3_06b_base.safetensors",
"directory": "text_encoders"
}
]
},
"widgets_values": [
"qwen_3_06b_base.safetensors",
"qwen_image",
"default"
]
},
{
"id": 3,
"type": "CLIPTextEncode",
"pos": [
360,
190
],
"size": [
400,
200
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"localized_name": "clip",
"name": "clip",
"type": "CLIP",
"link": 14
},
{
"localized_name": "text",
"name": "text",
"type": "STRING",
"widget": {
"name": "text"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONDITIONING",
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
16
]
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"anime illustration of a female knight in ornate silver plate armor holding a sword, side profile in a dense forest, detailed"
]
},
{
"id": 4,
"type": "CLIPTextEncode",
"pos": [
360,
440
],
"size": [
400,
200
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"localized_name": "clip",
"name": "clip",
"type": "CLIP",
"link": 15
},
{
"localized_name": "text",
"name": "text",
"type": "STRING",
"widget": {
"name": "text"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONDITIONING",
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
17
]
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
""
]
},
{
"id": 5,
"type": "EmptySD3LatentImage",
"pos": [
50,
470
],
"size": [
270,
106
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"localized_name": "width",
"name": "width",
"type": "INT",
"widget": {
"name": "width"
},
"link": null
},
{
"localized_name": "height",
"name": "height",
"type": "INT",
"widget": {
"name": "height"
},
"link": null
},
{
"localized_name": "batch_size",
"name": "batch_size",
"type": "INT",
"widget": {
"name": "batch_size"
},
"link": null
}
],
"outputs": [
{
"localized_name": "LATENT",
"name": "LATENT",
"type": "LATENT",
"links": [
23
]
}
],
"properties": {
"Node name for S&R": "EmptySD3LatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 6,
"type": "LoadImage",
"pos": [
50,
650
],
"size": [
282.798828125,
314
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"localized_name": "image",
"name": "image",
"type": "COMBO",
"widget": {
"name": "image"
},
"link": null
},
{
"localized_name": "choose file to upload",
"name": "upload",
"type": "IMAGEUPLOAD",
"widget": {
"name": "upload"
},
"link": null
}
],
"outputs": [
{
"localized_name": "IMAGE",
"name": "IMAGE",
"type": "IMAGE",
"links": [
19
]
},
{
"localized_name": "MASK",
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"anima_lllite_any_grayscale_b_control.png",
"image"
]
},
{
"id": 7,
"type": "VAELoader",
"pos": [
360,
700
],
"size": [
270,
58
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"localized_name": "vae_name",
"name": "vae_name",
"type": "COMBO",
"widget": {
"name": "vae_name"
},
"link": null
}
],
"outputs": [
{
"localized_name": "VAE",
"name": "VAE",
"type": "VAE",
"links": [
25
]
}
],
"properties": {
"Node name for S&R": "VAELoader",
"models": [
{
"name": "qwen_image_vae.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/vae/qwen_image_vae.safetensors",
"directory": "vae"
}
]
},
"widgets_values": [
"qwen_image_vae.safetensors"
]
},
{
"id": 8,
"type": "ACN_AnimaLLLiteLoaderAdvanced",
"pos": [
860,
120
],
"size": [
315,
58
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"localized_name": "timestep_kf",
"name": "timestep_kf",
"shape": 7,
"type": "TIMESTEP_KEYFRAME",
"link": null
},
{
"localized_name": "model_patch",
"name": "model_patch",
"type": "COMBO",
"widget": {
"name": "model_patch"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONTROL_NET",
"name": "CONTROL_NET",
"type": "CONTROL_NET",
"links": [
18
]
}
],
"properties": {
"Node name for S&R": "ACN_AnimaLLLiteLoaderAdvanced",
"models": [
{
"name": "anima-lllite-any-test-like-v2.safetensors",
"url": "https://huggingface.co/kohya-ss/Anima-LLLite/resolve/main/anima-lllite-any-test-like-v2.safetensors",
"directory": "model_patches"
}
]
},
"widgets_values": [
"anima-lllite-any-test-like-v2.safetensors"
]
},
{
"id": 9,
"type": "ACN_AdvancedControlNetApply_v2",
"pos": [
1210,
130
],
"size": [
287.86183776855466,
266
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"link": 16
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"link": 17
},
{
"localized_name": "control_net",
"name": "control_net",
"type": "CONTROL_NET",
"link": 18
},
{
"localized_name": "image",
"name": "image",
"type": "IMAGE",
"link": 19
},
{
"localized_name": "mask_optional",
"name": "mask_optional",
"shape": 7,
"type": "MASK",
"link": null
},
{
"localized_name": "timestep_kf",
"name": "timestep_kf",
"shape": 7,
"type": "TIMESTEP_KEYFRAME",
"link": null
},
{
"localized_name": "latent_kf_override",
"name": "latent_kf_override",
"shape": 7,
"type": "LATENT_KEYFRAME",
"link": null
},
{
"localized_name": "weights_override",
"name": "weights_override",
"shape": 7,
"type": "CONTROL_NET_WEIGHTS",
"link": null
},
{
"localized_name": "vae_optional",
"name": "vae_optional",
"shape": 7,
"type": "VAE",
"link": null
},
{
"localized_name": "strength",
"name": "strength",
"type": "FLOAT",
"widget": {
"name": "strength"
},
"link": null
},
{
"localized_name": "start_percent",
"name": "start_percent",
"type": "FLOAT",
"widget": {
"name": "start_percent"
},
"link": null
},
{
"localized_name": "end_percent",
"name": "end_percent",
"type": "FLOAT",
"widget": {
"name": "end_percent"
},
"link": null
}
],
"outputs": [
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"links": [
21
]
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"links": [
22
]
}
],
"properties": {
"Node name for S&R": "ACN_AdvancedControlNetApply_v2"
},
"widgets_values": [
1,
0,
1
]
},
{
"id": 10,
"type": "KSampler",
"pos": [
1580,
110
],
"size": [
270,
262
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"localized_name": "model",
"name": "model",
"type": "MODEL",
"link": 20
},
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"link": 21
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"link": 22
},
{
"localized_name": "latent_image",
"name": "latent_image",
"type": "LATENT",
"link": 23
},
{
"localized_name": "seed",
"name": "seed",
"type": "INT",
"widget": {
"name": "seed"
},
"link": null
},
{
"localized_name": "steps",
"name": "steps",
"type": "INT",
"widget": {
"name": "steps"
},
"link": null
},
{
"localized_name": "cfg",
"name": "cfg",
"type": "FLOAT",
"widget": {
"name": "cfg"
},
"link": null
},
{
"localized_name": "sampler_name",
"name": "sampler_name",
"type": "COMBO",
"widget": {
"name": "sampler_name"
},
"link": null
},
{
"localized_name": "scheduler",
"name": "scheduler",
"type": "COMBO",
"widget": {
"name": "scheduler"
},
"link": null
},
{
"localized_name": "denoise",
"name": "denoise",
"type": "FLOAT",
"widget": {
"name": "denoise"
},
"link": null
}
],
"outputs": [
{
"localized_name": "LATENT",
"name": "LATENT",
"type": "LATENT",
"links": [
24
]
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
82000,
"fixed",
12,
1,
"euler",
"simple",
1
]
},
{
"id": 11,
"type": "VAEDecode",
"pos": [
1940,
140
],
"size": [
140,
46
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"localized_name": "samples",
"name": "samples",
"type": "LATENT",
"link": 24
},
{
"localized_name": "vae",
"name": "vae",
"type": "VAE",
"link": 25
}
],
"outputs": [
{
"localized_name": "IMAGE",
"name": "IMAGE",
"type": "IMAGE",
"links": [
26
]
}
],
"properties": {
"Node name for S&R": "VAEDecode"
}
},
{
"id": 12,
"type": "SaveImage",
"pos": [
2190,
110
],
"size": [
270,
58
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"link": 26
},
{
"localized_name": "filename_prefix",
"name": "filename_prefix",
"type": "STRING",
"widget": {
"name": "filename_prefix"
},
"link": null
}
],
"outputs": [
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"links": null
}
],
"properties": {},
"widgets_values": [
"acn_anima_examples/any_grayscale_b"
]
}
],
"links": [
[
14,
2,
0,
3,
0,
"CLIP"
],
[
15,
2,
0,
4,
0,
"CLIP"
],
[
16,
3,
0,
9,
0,
"CONDITIONING"
],
[
17,
4,
0,
9,
1,
"CONDITIONING"
],
[
18,
8,
0,
9,
2,
"CONTROL_NET"
],
[
19,
6,
0,
9,
3,
"IMAGE"
],
[
20,
1,
0,
10,
0,
"MODEL"
],
[
21,
9,
0,
10,
1,
"CONDITIONING"
],
[
22,
9,
1,
10,
2,
"CONDITIONING"
],
[
23,
5,
0,
10,
3,
"LATENT"
],
[
24,
10,
0,
11,
0,
"LATENT"
],
[
25,
7,
0,
11,
1,
"VAE"
],
[
26,
11,
0,
12,
0,
"IMAGE"
]
],
"groups": [
{
"id": 20,
"title": "SHARED INPUTS",
"bounding": [
10,
35,
740,
900
],
"color": "#3f789e",
"font_size": 32,
"flags": {}
},
{
"id": 21,
"title": "ANIMA LLLITE V2 ANY - GRAYSCALE B",
"bounding": [
810,
35,
1660,
900
],
"color": "#6f4d86",
"font_size": 32,
"flags": {}
}
],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
},
"info": {
"name": "Anima LLLite v2 any - grayscale B",
"author": "Kosinkadink",
"description": "Advanced-ControlNet example for Anima LLLite v2 any with grayscale B conditioning."
}
},
"version": 0.4
}
@@ -0,0 +1,941 @@
{
"id": "00000000-0000-0000-0000-000000000000",
"revision": 0,
"last_node_id": 12,
"last_link_id": 26,
"nodes": [
{
"id": 1,
"type": "UNETLoader",
"pos": [
50,
100
],
"size": [
270,
82
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"localized_name": "unet_name",
"name": "unet_name",
"type": "COMBO",
"widget": {
"name": "unet_name"
},
"link": null
},
{
"localized_name": "weight_dtype",
"name": "weight_dtype",
"type": "COMBO",
"widget": {
"name": "weight_dtype"
},
"link": null
}
],
"outputs": [
{
"localized_name": "MODEL",
"name": "MODEL",
"type": "MODEL",
"links": [
20
]
}
],
"properties": {
"Node name for S&R": "UNETLoader",
"models": [
{
"name": "anima-base-v1.0.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/diffusion_models/anima-base-v1.0.safetensors",
"directory": "diffusion_models"
}
]
},
"widgets_values": [
"anima-base-v1.0.safetensors",
"default"
]
},
{
"id": 2,
"type": "CLIPLoader",
"pos": [
50,
250
],
"size": [
270,
106
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"localized_name": "clip_name",
"name": "clip_name",
"type": "COMBO",
"widget": {
"name": "clip_name"
},
"link": null
},
{
"localized_name": "type",
"name": "type",
"type": "COMBO",
"widget": {
"name": "type"
},
"link": null
},
{
"localized_name": "device",
"name": "device",
"shape": 7,
"type": "COMBO",
"widget": {
"name": "device"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CLIP",
"name": "CLIP",
"type": "CLIP",
"links": [
14,
15
]
}
],
"properties": {
"Node name for S&R": "CLIPLoader",
"models": [
{
"name": "qwen_3_06b_base.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/text_encoders/qwen_3_06b_base.safetensors",
"directory": "text_encoders"
}
]
},
"widgets_values": [
"qwen_3_06b_base.safetensors",
"qwen_image",
"default"
]
},
{
"id": 3,
"type": "CLIPTextEncode",
"pos": [
360,
190
],
"size": [
400,
200
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"localized_name": "clip",
"name": "clip",
"type": "CLIP",
"link": 14
},
{
"localized_name": "text",
"name": "text",
"type": "STRING",
"widget": {
"name": "text"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONDITIONING",
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
16
]
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"anime illustration of a female knight in ornate silver plate armor holding a sword, side profile in a dense forest, detailed"
]
},
{
"id": 4,
"type": "CLIPTextEncode",
"pos": [
360,
440
],
"size": [
400,
200
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"localized_name": "clip",
"name": "clip",
"type": "CLIP",
"link": 15
},
{
"localized_name": "text",
"name": "text",
"type": "STRING",
"widget": {
"name": "text"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONDITIONING",
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
17
]
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
""
]
},
{
"id": 5,
"type": "EmptySD3LatentImage",
"pos": [
50,
470
],
"size": [
270,
106
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"localized_name": "width",
"name": "width",
"type": "INT",
"widget": {
"name": "width"
},
"link": null
},
{
"localized_name": "height",
"name": "height",
"type": "INT",
"widget": {
"name": "height"
},
"link": null
},
{
"localized_name": "batch_size",
"name": "batch_size",
"type": "INT",
"widget": {
"name": "batch_size"
},
"link": null
}
],
"outputs": [
{
"localized_name": "LATENT",
"name": "LATENT",
"type": "LATENT",
"links": [
23
]
}
],
"properties": {
"Node name for S&R": "EmptySD3LatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 6,
"type": "LoadImage",
"pos": [
50,
650
],
"size": [
282.798828125,
314
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"localized_name": "image",
"name": "image",
"type": "COMBO",
"widget": {
"name": "image"
},
"link": null
},
{
"localized_name": "choose file to upload",
"name": "upload",
"type": "IMAGEUPLOAD",
"widget": {
"name": "upload"
},
"link": null
}
],
"outputs": [
{
"localized_name": "IMAGE",
"name": "IMAGE",
"type": "IMAGE",
"links": [
19
]
},
{
"localized_name": "MASK",
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"anima_lllite_any_hed_scribble_control.png",
"image"
]
},
{
"id": 7,
"type": "VAELoader",
"pos": [
360,
700
],
"size": [
270,
58
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"localized_name": "vae_name",
"name": "vae_name",
"type": "COMBO",
"widget": {
"name": "vae_name"
},
"link": null
}
],
"outputs": [
{
"localized_name": "VAE",
"name": "VAE",
"type": "VAE",
"links": [
25
]
}
],
"properties": {
"Node name for S&R": "VAELoader",
"models": [
{
"name": "qwen_image_vae.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/vae/qwen_image_vae.safetensors",
"directory": "vae"
}
]
},
"widgets_values": [
"qwen_image_vae.safetensors"
]
},
{
"id": 8,
"type": "ACN_AnimaLLLiteLoaderAdvanced",
"pos": [
860,
120
],
"size": [
315,
58
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"localized_name": "timestep_kf",
"name": "timestep_kf",
"shape": 7,
"type": "TIMESTEP_KEYFRAME",
"link": null
},
{
"localized_name": "model_patch",
"name": "model_patch",
"type": "COMBO",
"widget": {
"name": "model_patch"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONTROL_NET",
"name": "CONTROL_NET",
"type": "CONTROL_NET",
"links": [
18
]
}
],
"properties": {
"Node name for S&R": "ACN_AnimaLLLiteLoaderAdvanced",
"models": [
{
"name": "anima-lllite-any-test-like-v2.safetensors",
"url": "https://huggingface.co/kohya-ss/Anima-LLLite/resolve/main/anima-lllite-any-test-like-v2.safetensors",
"directory": "model_patches"
}
]
},
"widgets_values": [
"anima-lllite-any-test-like-v2.safetensors"
]
},
{
"id": 9,
"type": "ACN_AdvancedControlNetApply_v2",
"pos": [
1210,
130
],
"size": [
287.86183776855466,
266
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"link": 16
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"link": 17
},
{
"localized_name": "control_net",
"name": "control_net",
"type": "CONTROL_NET",
"link": 18
},
{
"localized_name": "image",
"name": "image",
"type": "IMAGE",
"link": 19
},
{
"localized_name": "mask_optional",
"name": "mask_optional",
"shape": 7,
"type": "MASK",
"link": null
},
{
"localized_name": "timestep_kf",
"name": "timestep_kf",
"shape": 7,
"type": "TIMESTEP_KEYFRAME",
"link": null
},
{
"localized_name": "latent_kf_override",
"name": "latent_kf_override",
"shape": 7,
"type": "LATENT_KEYFRAME",
"link": null
},
{
"localized_name": "weights_override",
"name": "weights_override",
"shape": 7,
"type": "CONTROL_NET_WEIGHTS",
"link": null
},
{
"localized_name": "vae_optional",
"name": "vae_optional",
"shape": 7,
"type": "VAE",
"link": null
},
{
"localized_name": "strength",
"name": "strength",
"type": "FLOAT",
"widget": {
"name": "strength"
},
"link": null
},
{
"localized_name": "start_percent",
"name": "start_percent",
"type": "FLOAT",
"widget": {
"name": "start_percent"
},
"link": null
},
{
"localized_name": "end_percent",
"name": "end_percent",
"type": "FLOAT",
"widget": {
"name": "end_percent"
},
"link": null
}
],
"outputs": [
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"links": [
21
]
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"links": [
22
]
}
],
"properties": {
"Node name for S&R": "ACN_AdvancedControlNetApply_v2"
},
"widgets_values": [
1,
0,
1
]
},
{
"id": 10,
"type": "KSampler",
"pos": [
1580,
110
],
"size": [
270,
262
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"localized_name": "model",
"name": "model",
"type": "MODEL",
"link": 20
},
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"link": 21
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"link": 22
},
{
"localized_name": "latent_image",
"name": "latent_image",
"type": "LATENT",
"link": 23
},
{
"localized_name": "seed",
"name": "seed",
"type": "INT",
"widget": {
"name": "seed"
},
"link": null
},
{
"localized_name": "steps",
"name": "steps",
"type": "INT",
"widget": {
"name": "steps"
},
"link": null
},
{
"localized_name": "cfg",
"name": "cfg",
"type": "FLOAT",
"widget": {
"name": "cfg"
},
"link": null
},
{
"localized_name": "sampler_name",
"name": "sampler_name",
"type": "COMBO",
"widget": {
"name": "sampler_name"
},
"link": null
},
{
"localized_name": "scheduler",
"name": "scheduler",
"type": "COMBO",
"widget": {
"name": "scheduler"
},
"link": null
},
{
"localized_name": "denoise",
"name": "denoise",
"type": "FLOAT",
"widget": {
"name": "denoise"
},
"link": null
}
],
"outputs": [
{
"localized_name": "LATENT",
"name": "LATENT",
"type": "LATENT",
"links": [
24
]
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
82000,
"fixed",
12,
1,
"euler",
"simple",
1
]
},
{
"id": 11,
"type": "VAEDecode",
"pos": [
1940,
140
],
"size": [
140,
46
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"localized_name": "samples",
"name": "samples",
"type": "LATENT",
"link": 24
},
{
"localized_name": "vae",
"name": "vae",
"type": "VAE",
"link": 25
}
],
"outputs": [
{
"localized_name": "IMAGE",
"name": "IMAGE",
"type": "IMAGE",
"links": [
26
]
}
],
"properties": {
"Node name for S&R": "VAEDecode"
}
},
{
"id": 12,
"type": "SaveImage",
"pos": [
2190,
110
],
"size": [
270,
58
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"link": 26
},
{
"localized_name": "filename_prefix",
"name": "filename_prefix",
"type": "STRING",
"widget": {
"name": "filename_prefix"
},
"link": null
}
],
"outputs": [
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"links": null
}
],
"properties": {},
"widgets_values": [
"acn_anima_examples/any_hed_scribble"
]
}
],
"links": [
[
14,
2,
0,
3,
0,
"CLIP"
],
[
15,
2,
0,
4,
0,
"CLIP"
],
[
16,
3,
0,
9,
0,
"CONDITIONING"
],
[
17,
4,
0,
9,
1,
"CONDITIONING"
],
[
18,
8,
0,
9,
2,
"CONTROL_NET"
],
[
19,
6,
0,
9,
3,
"IMAGE"
],
[
20,
1,
0,
10,
0,
"MODEL"
],
[
21,
9,
0,
10,
1,
"CONDITIONING"
],
[
22,
9,
1,
10,
2,
"CONDITIONING"
],
[
23,
5,
0,
10,
3,
"LATENT"
],
[
24,
10,
0,
11,
0,
"LATENT"
],
[
25,
7,
0,
11,
1,
"VAE"
],
[
26,
11,
0,
12,
0,
"IMAGE"
]
],
"groups": [
{
"id": 20,
"title": "SHARED INPUTS",
"bounding": [
10,
35,
740,
900
],
"color": "#3f789e",
"font_size": 32,
"flags": {}
},
{
"id": 21,
"title": "ANIMA LLLITE V2 ANY - HED SCRIBBLE",
"bounding": [
810,
35,
1660,
900
],
"color": "#6f4d86",
"font_size": 32,
"flags": {}
}
],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
},
"info": {
"name": "Anima LLLite v2 any - HED scribble",
"author": "Kosinkadink",
"description": "Advanced-ControlNet example for Anima LLLite v2 any with HED scribble conditioning."
}
},
"version": 0.4
}
@@ -0,0 +1,941 @@
{
"id": "00000000-0000-0000-0000-000000000000",
"revision": 0,
"last_node_id": 12,
"last_link_id": 26,
"nodes": [
{
"id": 1,
"type": "UNETLoader",
"pos": [
50,
100
],
"size": [
270,
82
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"localized_name": "unet_name",
"name": "unet_name",
"type": "COMBO",
"widget": {
"name": "unet_name"
},
"link": null
},
{
"localized_name": "weight_dtype",
"name": "weight_dtype",
"type": "COMBO",
"widget": {
"name": "weight_dtype"
},
"link": null
}
],
"outputs": [
{
"localized_name": "MODEL",
"name": "MODEL",
"type": "MODEL",
"links": [
20
]
}
],
"properties": {
"Node name for S&R": "UNETLoader",
"models": [
{
"name": "anima-base-v1.0.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/diffusion_models/anima-base-v1.0.safetensors",
"directory": "diffusion_models"
}
]
},
"widgets_values": [
"anima-base-v1.0.safetensors",
"default"
]
},
{
"id": 2,
"type": "CLIPLoader",
"pos": [
50,
250
],
"size": [
270,
106
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"localized_name": "clip_name",
"name": "clip_name",
"type": "COMBO",
"widget": {
"name": "clip_name"
},
"link": null
},
{
"localized_name": "type",
"name": "type",
"type": "COMBO",
"widget": {
"name": "type"
},
"link": null
},
{
"localized_name": "device",
"name": "device",
"shape": 7,
"type": "COMBO",
"widget": {
"name": "device"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CLIP",
"name": "CLIP",
"type": "CLIP",
"links": [
14,
15
]
}
],
"properties": {
"Node name for S&R": "CLIPLoader",
"models": [
{
"name": "qwen_3_06b_base.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/text_encoders/qwen_3_06b_base.safetensors",
"directory": "text_encoders"
}
]
},
"widgets_values": [
"qwen_3_06b_base.safetensors",
"qwen_image",
"default"
]
},
{
"id": 3,
"type": "CLIPTextEncode",
"pos": [
360,
190
],
"size": [
400,
200
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"localized_name": "clip",
"name": "clip",
"type": "CLIP",
"link": 14
},
{
"localized_name": "text",
"name": "text",
"type": "STRING",
"widget": {
"name": "text"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONDITIONING",
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
16
]
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"anime illustration of a female knight in ornate silver plate armor holding a sword, side profile in a dense forest, detailed"
]
},
{
"id": 4,
"type": "CLIPTextEncode",
"pos": [
360,
440
],
"size": [
400,
200
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"localized_name": "clip",
"name": "clip",
"type": "CLIP",
"link": 15
},
{
"localized_name": "text",
"name": "text",
"type": "STRING",
"widget": {
"name": "text"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONDITIONING",
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
17
]
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
""
]
},
{
"id": 5,
"type": "EmptySD3LatentImage",
"pos": [
50,
470
],
"size": [
270,
106
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"localized_name": "width",
"name": "width",
"type": "INT",
"widget": {
"name": "width"
},
"link": null
},
{
"localized_name": "height",
"name": "height",
"type": "INT",
"widget": {
"name": "height"
},
"link": null
},
{
"localized_name": "batch_size",
"name": "batch_size",
"type": "INT",
"widget": {
"name": "batch_size"
},
"link": null
}
],
"outputs": [
{
"localized_name": "LATENT",
"name": "LATENT",
"type": "LATENT",
"links": [
23
]
}
],
"properties": {
"Node name for S&R": "EmptySD3LatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 6,
"type": "LoadImage",
"pos": [
50,
650
],
"size": [
282.798828125,
314
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"localized_name": "image",
"name": "image",
"type": "COMBO",
"widget": {
"name": "image"
},
"link": null
},
{
"localized_name": "choose file to upload",
"name": "upload",
"type": "IMAGEUPLOAD",
"widget": {
"name": "upload"
},
"link": null
}
],
"outputs": [
{
"localized_name": "IMAGE",
"name": "IMAGE",
"type": "IMAGE",
"links": [
19
]
},
{
"localized_name": "MASK",
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"anima_lllite_any_lineart_control.png",
"image"
]
},
{
"id": 7,
"type": "VAELoader",
"pos": [
360,
700
],
"size": [
270,
58
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"localized_name": "vae_name",
"name": "vae_name",
"type": "COMBO",
"widget": {
"name": "vae_name"
},
"link": null
}
],
"outputs": [
{
"localized_name": "VAE",
"name": "VAE",
"type": "VAE",
"links": [
25
]
}
],
"properties": {
"Node name for S&R": "VAELoader",
"models": [
{
"name": "qwen_image_vae.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/vae/qwen_image_vae.safetensors",
"directory": "vae"
}
]
},
"widgets_values": [
"qwen_image_vae.safetensors"
]
},
{
"id": 8,
"type": "ACN_AnimaLLLiteLoaderAdvanced",
"pos": [
860,
120
],
"size": [
315,
58
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"localized_name": "timestep_kf",
"name": "timestep_kf",
"shape": 7,
"type": "TIMESTEP_KEYFRAME",
"link": null
},
{
"localized_name": "model_patch",
"name": "model_patch",
"type": "COMBO",
"widget": {
"name": "model_patch"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONTROL_NET",
"name": "CONTROL_NET",
"type": "CONTROL_NET",
"links": [
18
]
}
],
"properties": {
"Node name for S&R": "ACN_AnimaLLLiteLoaderAdvanced",
"models": [
{
"name": "anima-lllite-any-test-like-v2.safetensors",
"url": "https://huggingface.co/kohya-ss/Anima-LLLite/resolve/main/anima-lllite-any-test-like-v2.safetensors",
"directory": "model_patches"
}
]
},
"widgets_values": [
"anima-lllite-any-test-like-v2.safetensors"
]
},
{
"id": 9,
"type": "ACN_AdvancedControlNetApply_v2",
"pos": [
1210,
130
],
"size": [
287.86183776855466,
266
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"link": 16
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"link": 17
},
{
"localized_name": "control_net",
"name": "control_net",
"type": "CONTROL_NET",
"link": 18
},
{
"localized_name": "image",
"name": "image",
"type": "IMAGE",
"link": 19
},
{
"localized_name": "mask_optional",
"name": "mask_optional",
"shape": 7,
"type": "MASK",
"link": null
},
{
"localized_name": "timestep_kf",
"name": "timestep_kf",
"shape": 7,
"type": "TIMESTEP_KEYFRAME",
"link": null
},
{
"localized_name": "latent_kf_override",
"name": "latent_kf_override",
"shape": 7,
"type": "LATENT_KEYFRAME",
"link": null
},
{
"localized_name": "weights_override",
"name": "weights_override",
"shape": 7,
"type": "CONTROL_NET_WEIGHTS",
"link": null
},
{
"localized_name": "vae_optional",
"name": "vae_optional",
"shape": 7,
"type": "VAE",
"link": null
},
{
"localized_name": "strength",
"name": "strength",
"type": "FLOAT",
"widget": {
"name": "strength"
},
"link": null
},
{
"localized_name": "start_percent",
"name": "start_percent",
"type": "FLOAT",
"widget": {
"name": "start_percent"
},
"link": null
},
{
"localized_name": "end_percent",
"name": "end_percent",
"type": "FLOAT",
"widget": {
"name": "end_percent"
},
"link": null
}
],
"outputs": [
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"links": [
21
]
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"links": [
22
]
}
],
"properties": {
"Node name for S&R": "ACN_AdvancedControlNetApply_v2"
},
"widgets_values": [
1,
0,
1
]
},
{
"id": 10,
"type": "KSampler",
"pos": [
1580,
110
],
"size": [
270,
262
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"localized_name": "model",
"name": "model",
"type": "MODEL",
"link": 20
},
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"link": 21
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"link": 22
},
{
"localized_name": "latent_image",
"name": "latent_image",
"type": "LATENT",
"link": 23
},
{
"localized_name": "seed",
"name": "seed",
"type": "INT",
"widget": {
"name": "seed"
},
"link": null
},
{
"localized_name": "steps",
"name": "steps",
"type": "INT",
"widget": {
"name": "steps"
},
"link": null
},
{
"localized_name": "cfg",
"name": "cfg",
"type": "FLOAT",
"widget": {
"name": "cfg"
},
"link": null
},
{
"localized_name": "sampler_name",
"name": "sampler_name",
"type": "COMBO",
"widget": {
"name": "sampler_name"
},
"link": null
},
{
"localized_name": "scheduler",
"name": "scheduler",
"type": "COMBO",
"widget": {
"name": "scheduler"
},
"link": null
},
{
"localized_name": "denoise",
"name": "denoise",
"type": "FLOAT",
"widget": {
"name": "denoise"
},
"link": null
}
],
"outputs": [
{
"localized_name": "LATENT",
"name": "LATENT",
"type": "LATENT",
"links": [
24
]
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
82000,
"fixed",
12,
1,
"euler",
"simple",
1
]
},
{
"id": 11,
"type": "VAEDecode",
"pos": [
1940,
140
],
"size": [
140,
46
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"localized_name": "samples",
"name": "samples",
"type": "LATENT",
"link": 24
},
{
"localized_name": "vae",
"name": "vae",
"type": "VAE",
"link": 25
}
],
"outputs": [
{
"localized_name": "IMAGE",
"name": "IMAGE",
"type": "IMAGE",
"links": [
26
]
}
],
"properties": {
"Node name for S&R": "VAEDecode"
}
},
{
"id": 12,
"type": "SaveImage",
"pos": [
2190,
110
],
"size": [
270,
58
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"link": 26
},
{
"localized_name": "filename_prefix",
"name": "filename_prefix",
"type": "STRING",
"widget": {
"name": "filename_prefix"
},
"link": null
}
],
"outputs": [
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"links": null
}
],
"properties": {},
"widgets_values": [
"acn_anima_examples/any_lineart"
]
}
],
"links": [
[
14,
2,
0,
3,
0,
"CLIP"
],
[
15,
2,
0,
4,
0,
"CLIP"
],
[
16,
3,
0,
9,
0,
"CONDITIONING"
],
[
17,
4,
0,
9,
1,
"CONDITIONING"
],
[
18,
8,
0,
9,
2,
"CONTROL_NET"
],
[
19,
6,
0,
9,
3,
"IMAGE"
],
[
20,
1,
0,
10,
0,
"MODEL"
],
[
21,
9,
0,
10,
1,
"CONDITIONING"
],
[
22,
9,
1,
10,
2,
"CONDITIONING"
],
[
23,
5,
0,
10,
3,
"LATENT"
],
[
24,
10,
0,
11,
0,
"LATENT"
],
[
25,
7,
0,
11,
1,
"VAE"
],
[
26,
11,
0,
12,
0,
"IMAGE"
]
],
"groups": [
{
"id": 20,
"title": "SHARED INPUTS",
"bounding": [
10,
35,
740,
900
],
"color": "#3f789e",
"font_size": 32,
"flags": {}
},
{
"id": 21,
"title": "ANIMA LLLITE V2 ANY - LINEART",
"bounding": [
810,
35,
1660,
900
],
"color": "#6f4d86",
"font_size": 32,
"flags": {}
}
],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
},
"info": {
"name": "Anima LLLite v2 any - lineart",
"author": "Kosinkadink",
"description": "Advanced-ControlNet example for Anima LLLite v2 any with lineart conditioning."
}
},
"version": 0.4
}
@@ -0,0 +1,941 @@
{
"id": "00000000-0000-0000-0000-000000000000",
"revision": 0,
"last_node_id": 12,
"last_link_id": 26,
"nodes": [
{
"id": 1,
"type": "UNETLoader",
"pos": [
50,
100
],
"size": [
270,
82
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"localized_name": "unet_name",
"name": "unet_name",
"type": "COMBO",
"widget": {
"name": "unet_name"
},
"link": null
},
{
"localized_name": "weight_dtype",
"name": "weight_dtype",
"type": "COMBO",
"widget": {
"name": "weight_dtype"
},
"link": null
}
],
"outputs": [
{
"localized_name": "MODEL",
"name": "MODEL",
"type": "MODEL",
"links": [
20
]
}
],
"properties": {
"Node name for S&R": "UNETLoader",
"models": [
{
"name": "anima-base-v1.0.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/diffusion_models/anima-base-v1.0.safetensors",
"directory": "diffusion_models"
}
]
},
"widgets_values": [
"anima-base-v1.0.safetensors",
"default"
]
},
{
"id": 2,
"type": "CLIPLoader",
"pos": [
50,
250
],
"size": [
270,
106
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"localized_name": "clip_name",
"name": "clip_name",
"type": "COMBO",
"widget": {
"name": "clip_name"
},
"link": null
},
{
"localized_name": "type",
"name": "type",
"type": "COMBO",
"widget": {
"name": "type"
},
"link": null
},
{
"localized_name": "device",
"name": "device",
"shape": 7,
"type": "COMBO",
"widget": {
"name": "device"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CLIP",
"name": "CLIP",
"type": "CLIP",
"links": [
14,
15
]
}
],
"properties": {
"Node name for S&R": "CLIPLoader",
"models": [
{
"name": "qwen_3_06b_base.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/text_encoders/qwen_3_06b_base.safetensors",
"directory": "text_encoders"
}
]
},
"widgets_values": [
"qwen_3_06b_base.safetensors",
"qwen_image",
"default"
]
},
{
"id": 3,
"type": "CLIPTextEncode",
"pos": [
360,
190
],
"size": [
400,
200
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"localized_name": "clip",
"name": "clip",
"type": "CLIP",
"link": 14
},
{
"localized_name": "text",
"name": "text",
"type": "STRING",
"widget": {
"name": "text"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONDITIONING",
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
16
]
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"anime illustration of a female knight in ornate silver plate armor holding a sword, side profile in a dense forest, detailed"
]
},
{
"id": 4,
"type": "CLIPTextEncode",
"pos": [
360,
440
],
"size": [
400,
200
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"localized_name": "clip",
"name": "clip",
"type": "CLIP",
"link": 15
},
{
"localized_name": "text",
"name": "text",
"type": "STRING",
"widget": {
"name": "text"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONDITIONING",
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
17
]
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
""
]
},
{
"id": 5,
"type": "EmptySD3LatentImage",
"pos": [
50,
470
],
"size": [
270,
106
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"localized_name": "width",
"name": "width",
"type": "INT",
"widget": {
"name": "width"
},
"link": null
},
{
"localized_name": "height",
"name": "height",
"type": "INT",
"widget": {
"name": "height"
},
"link": null
},
{
"localized_name": "batch_size",
"name": "batch_size",
"type": "INT",
"widget": {
"name": "batch_size"
},
"link": null
}
],
"outputs": [
{
"localized_name": "LATENT",
"name": "LATENT",
"type": "LATENT",
"links": [
23
]
}
],
"properties": {
"Node name for S&R": "EmptySD3LatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 6,
"type": "LoadImage",
"pos": [
50,
650
],
"size": [
282.798828125,
314
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"localized_name": "image",
"name": "image",
"type": "COMBO",
"widget": {
"name": "image"
},
"link": null
},
{
"localized_name": "choose file to upload",
"name": "upload",
"type": "IMAGEUPLOAD",
"widget": {
"name": "upload"
},
"link": null
}
],
"outputs": [
{
"localized_name": "IMAGE",
"name": "IMAGE",
"type": "IMAGE",
"links": [
19
]
},
{
"localized_name": "MASK",
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"anima_lllite_any_pidinet_scribble_control.png",
"image"
]
},
{
"id": 7,
"type": "VAELoader",
"pos": [
360,
700
],
"size": [
270,
58
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"localized_name": "vae_name",
"name": "vae_name",
"type": "COMBO",
"widget": {
"name": "vae_name"
},
"link": null
}
],
"outputs": [
{
"localized_name": "VAE",
"name": "VAE",
"type": "VAE",
"links": [
25
]
}
],
"properties": {
"Node name for S&R": "VAELoader",
"models": [
{
"name": "qwen_image_vae.safetensors",
"url": "https://huggingface.co/circlestone-labs/Anima/resolve/main/split_files/vae/qwen_image_vae.safetensors",
"directory": "vae"
}
]
},
"widgets_values": [
"qwen_image_vae.safetensors"
]
},
{
"id": 8,
"type": "ACN_AnimaLLLiteLoaderAdvanced",
"pos": [
860,
120
],
"size": [
315,
58
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"localized_name": "timestep_kf",
"name": "timestep_kf",
"shape": 7,
"type": "TIMESTEP_KEYFRAME",
"link": null
},
{
"localized_name": "model_patch",
"name": "model_patch",
"type": "COMBO",
"widget": {
"name": "model_patch"
},
"link": null
}
],
"outputs": [
{
"localized_name": "CONTROL_NET",
"name": "CONTROL_NET",
"type": "CONTROL_NET",
"links": [
18
]
}
],
"properties": {
"Node name for S&R": "ACN_AnimaLLLiteLoaderAdvanced",
"models": [
{
"name": "anima-lllite-any-test-like-v2.safetensors",
"url": "https://huggingface.co/kohya-ss/Anima-LLLite/resolve/main/anima-lllite-any-test-like-v2.safetensors",
"directory": "model_patches"
}
]
},
"widgets_values": [
"anima-lllite-any-test-like-v2.safetensors"
]
},
{
"id": 9,
"type": "ACN_AdvancedControlNetApply_v2",
"pos": [
1210,
130
],
"size": [
287.86183776855466,
266
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"link": 16
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"link": 17
},
{
"localized_name": "control_net",
"name": "control_net",
"type": "CONTROL_NET",
"link": 18
},
{
"localized_name": "image",
"name": "image",
"type": "IMAGE",
"link": 19
},
{
"localized_name": "mask_optional",
"name": "mask_optional",
"shape": 7,
"type": "MASK",
"link": null
},
{
"localized_name": "timestep_kf",
"name": "timestep_kf",
"shape": 7,
"type": "TIMESTEP_KEYFRAME",
"link": null
},
{
"localized_name": "latent_kf_override",
"name": "latent_kf_override",
"shape": 7,
"type": "LATENT_KEYFRAME",
"link": null
},
{
"localized_name": "weights_override",
"name": "weights_override",
"shape": 7,
"type": "CONTROL_NET_WEIGHTS",
"link": null
},
{
"localized_name": "vae_optional",
"name": "vae_optional",
"shape": 7,
"type": "VAE",
"link": null
},
{
"localized_name": "strength",
"name": "strength",
"type": "FLOAT",
"widget": {
"name": "strength"
},
"link": null
},
{
"localized_name": "start_percent",
"name": "start_percent",
"type": "FLOAT",
"widget": {
"name": "start_percent"
},
"link": null
},
{
"localized_name": "end_percent",
"name": "end_percent",
"type": "FLOAT",
"widget": {
"name": "end_percent"
},
"link": null
}
],
"outputs": [
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"links": [
21
]
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"links": [
22
]
}
],
"properties": {
"Node name for S&R": "ACN_AdvancedControlNetApply_v2"
},
"widgets_values": [
1,
0,
1
]
},
{
"id": 10,
"type": "KSampler",
"pos": [
1580,
110
],
"size": [
270,
262
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"localized_name": "model",
"name": "model",
"type": "MODEL",
"link": 20
},
{
"localized_name": "positive",
"name": "positive",
"type": "CONDITIONING",
"link": 21
},
{
"localized_name": "negative",
"name": "negative",
"type": "CONDITIONING",
"link": 22
},
{
"localized_name": "latent_image",
"name": "latent_image",
"type": "LATENT",
"link": 23
},
{
"localized_name": "seed",
"name": "seed",
"type": "INT",
"widget": {
"name": "seed"
},
"link": null
},
{
"localized_name": "steps",
"name": "steps",
"type": "INT",
"widget": {
"name": "steps"
},
"link": null
},
{
"localized_name": "cfg",
"name": "cfg",
"type": "FLOAT",
"widget": {
"name": "cfg"
},
"link": null
},
{
"localized_name": "sampler_name",
"name": "sampler_name",
"type": "COMBO",
"widget": {
"name": "sampler_name"
},
"link": null
},
{
"localized_name": "scheduler",
"name": "scheduler",
"type": "COMBO",
"widget": {
"name": "scheduler"
},
"link": null
},
{
"localized_name": "denoise",
"name": "denoise",
"type": "FLOAT",
"widget": {
"name": "denoise"
},
"link": null
}
],
"outputs": [
{
"localized_name": "LATENT",
"name": "LATENT",
"type": "LATENT",
"links": [
24
]
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
82000,
"fixed",
12,
1,
"euler",
"simple",
1
]
},
{
"id": 11,
"type": "VAEDecode",
"pos": [
1940,
140
],
"size": [
140,
46
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"localized_name": "samples",
"name": "samples",
"type": "LATENT",
"link": 24
},
{
"localized_name": "vae",
"name": "vae",
"type": "VAE",
"link": 25
}
],
"outputs": [
{
"localized_name": "IMAGE",
"name": "IMAGE",
"type": "IMAGE",
"links": [
26
]
}
],
"properties": {
"Node name for S&R": "VAEDecode"
}
},
{
"id": 12,
"type": "SaveImage",
"pos": [
2190,
110
],
"size": [
270,
58
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"link": 26
},
{
"localized_name": "filename_prefix",
"name": "filename_prefix",
"type": "STRING",
"widget": {
"name": "filename_prefix"
},
"link": null
}
],
"outputs": [
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"links": null
}
],
"properties": {},
"widgets_values": [
"acn_anima_examples/any_pidinet_scribble"
]
}
],
"links": [
[
14,
2,
0,
3,
0,
"CLIP"
],
[
15,
2,
0,
4,
0,
"CLIP"
],
[
16,
3,
0,
9,
0,
"CONDITIONING"
],
[
17,
4,
0,
9,
1,
"CONDITIONING"
],
[
18,
8,
0,
9,
2,
"CONTROL_NET"
],
[
19,
6,
0,
9,
3,
"IMAGE"
],
[
20,
1,
0,
10,
0,
"MODEL"
],
[
21,
9,
0,
10,
1,
"CONDITIONING"
],
[
22,
9,
1,
10,
2,
"CONDITIONING"
],
[
23,
5,
0,
10,
3,
"LATENT"
],
[
24,
10,
0,
11,
0,
"LATENT"
],
[
25,
7,
0,
11,
1,
"VAE"
],
[
26,
11,
0,
12,
0,
"IMAGE"
]
],
"groups": [
{
"id": 20,
"title": "SHARED INPUTS",
"bounding": [
10,
35,
740,
900
],
"color": "#3f789e",
"font_size": 32,
"flags": {}
},
{
"id": 21,
"title": "ANIMA LLLITE V2 ANY - PIDINET SCRIBBLE",
"bounding": [
810,
35,
1660,
900
],
"color": "#6f4d86",
"font_size": 32,
"flags": {}
}
],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
},
"info": {
"name": "Anima LLLite v2 any - PiDiNet scribble",
"author": "Kosinkadink",
"description": "Advanced-ControlNet example for Anima LLLite v2 any with PiDiNet scribble conditioning."
}
},
"version": 0.4
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-53
View File
@@ -1,53 +0,0 @@
import { app } from '../../../scripts/app.js'
function addResizeHook(node, padding, useOldMin=false) {
let origOnCreated = node.onNodeCreated
node.onNodeCreated = function() {
let r = origOnCreated?.apply(this, arguments)
let size = this.computeSize();
size[0] += padding || 0;
if (useOldMin) {
//equal to LiteGraph.NODE_WIDTH*1.5*1.5
size[0] = Math.max(size[0], 315)
}
this.setSize(size);
return r
}
}
app.registerExtension({
name: "AdvancedControlNet.autosize",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
//since python_module is based off folder path,
//it could be changed by users and should only be used as fallback
if (nodeData?.name?.startsWith("ACN_")
|| nodeData.python_module == 'custom_nodes.ComfyUI-Advanced-ControlNet') {
if (nodeData?.input?.hidden?.autosize) {
addResizeHook(nodeType.prototype, nodeData.input.hidden.autosize[1]?.padding)
} else if (!nodeData?.input?.optional?.autosize) {
addResizeHook(nodeType.prototype, 0, true)
}
}
},
async getCustomWidgets() {
return {
ACNAUTOSIZE(node, inputName, inputData) {
let w = {
name : inputName,
type : "ACN.AUTOSIZE",
value : "",
options : {"serialize": false},
computeSize : function(width) {
return [0, -4];
}
}
if (!node.widgets) {
node.widgets = []
}
node.widgets.push(w)
addResizeHook(node, inputData[1].padding);
return w;
}
}
}
});
-293
View File
@@ -1,293 +0,0 @@
import { app } from '../../../scripts/app.js'
function chainCallback(object, property, callback) {
if (object == undefined) {
//This should not happen.
console.error("Tried to add callback to non-existant object")
return;
}
if (property in object && object[property]) {
const callback_orig = object[property]
object[property] = function () {
const r = callback_orig.apply(this, arguments);
callback.apply(this, arguments);
return r
};
} else {
object[property] = callback;
}
}
var helpDOM;
function initHelpDOM() {
let parentDOM = document.createElement("div");
document.body.appendChild(parentDOM)
parentDOM.appendChild(helpDOM)
helpDOM.className = "litegraph";
let scrollbarStyle = document.createElement('style');
scrollbarStyle.innerHTML = `
<style id="scroll-properties">
* {
scrollbar-width: 6px;
scrollbar-color: #0003 #0000;
}
::-webkit-scrollbar {
background: transparent;
width: 6px;
}
::-webkit-scrollbar-thumb {
background: #0005;
border-radius: 20px
}
::-webkit-scrollbar-button {
display: none;
}
.VHS_loopedvideo::-webkit-media-controls-mute-button {
display:none;
}
.VHS_loopedvideo::-webkit-media-controls-fullscreen-button {
display:none;
}
</style>
`
parentDOM.appendChild(scrollbarStyle)
chainCallback(app.canvas, "onDrawForeground", function (ctx, visible_rect){
let n = helpDOM.node
if (!n || !n?.graph) {
parentDOM.style['left'] = '-5000px'
return
}
//draw : function(ctx, node, widgetWidth, widgetY, height) {
//update widget position, even if off screen
const transform = ctx.getTransform();
const scale = app.canvas.ds.scale;//gets the litegraph zoom
//calculate coordinates with account for browser zoom
const bcr = app.canvas.canvas.getBoundingClientRect()
const x = transform.e*scale/transform.a + bcr.x;
const y = transform.f*scale/transform.a + bcr.y;
//TODO: text reflows at low zoom. investigate alternatives
Object.assign(parentDOM.style, {
left: (x+(n.pos[0] + n.size[0]+15)*scale) + "px",
top: (y+(n.pos[1]-LiteGraph.NODE_TITLE_HEIGHT)*scale) + "px",
width: "400px",
minHeight: "100px",
maxHeight: "600px",
overflowY: 'scroll',
transformOrigin: '0 0',
transform: 'scale(' + scale + ',' + scale +')',
fontSize: '18px',
backgroundColor: LiteGraph.NODE_DEFAULT_BGCOLOR,
boxShadow: '0 0 10px black',
borderRadius: '4px',
padding: '3px',
zIndex: 3,
position: "absolute",
display: 'inline',
});
});
function setCollapse(el, doCollapse) {
if (doCollapse) {
el.children[0].children[0].innerHTML = '+'
Object.assign(el.children[1].style, {
color: '#CCC',
overflowX: 'hidden',
width: '0px',
minWidth: 'calc(100% - 20px)',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
})
for (let child of el.children[1].children) {
if (child.style.display != 'none'){
child.origDisplay = child.style.display
}
child.style.display = 'none'
}
} else {
el.children[0].children[0].innerHTML = '-'
Object.assign(el.children[1].style, {
color: '',
overflowX: '',
width: '100%',
minWidth: '',
textOverflow: '',
whiteSpace: '',
})
for (let child of el.children[1].children) {
child.style.display = child.origDisplay
}
}
}
helpDOM.collapseOnClick = function() {
let doCollapse = this.children[0].innerHTML == '-'
setCollapse(this.parentElement, doCollapse)
}
helpDOM.selectHelp = function(name, value) {
//attempt to navigate to name in help
function collapseUnlessMatch(items,t) {
var match = items.querySelector('[vhs_title="' + t + '"]')
if (!match) {
for (let i of items.children) {
if (i.innerHTML.slice(0,t.length+5).includes(t)) {
match = i
break
}
}
}
if (!match) {
return null
}
//For longer documentation items with fewer collapsable elements,
//scroll to make sure the entirety of the selected item is visible
//This has the unfortunate side effect of trying to scroll the main
//window if the documentation windows is forcibly offscreen,
//but it's easy to simply scroll the main window back and seems to
//have no visual side effects
match.scrollIntoView(false)
window.scrollTo(0,0)
for (let i of items.querySelectorAll('.VHS_collapse')) {
if (i.contains(match)) {
setCollapse(i, false)
} else {
setCollapse(i, true)
}
}
return match
}
let target = collapseUnlessMatch(helpDOM, name)
if (target && value) {
collapseUnlessMatch(target, value)
}
}
helpDOM.addHelp = function(node, nodeType, description) {
if (!description) {
return
}
//Pad computed size for the clickable question mark
let originalComputeSize = node.computeSize
node.computeSize = function() {
let size = originalComputeSize.apply(this, arguments)
if (!this.title) {
return size
}
let title_width = this.title.length * 0.6 * LiteGraph.NODE_TEXT_SIZE
size[0] = Math.max(size[0], title_width + LiteGraph.NODE_TITLE_HEIGHT)
return size
}
node.description = description
chainCallback(node, "onDrawForeground", function (ctx) {
//draw question mark
ctx.save()
ctx.font = 'bold 20px Arial'
ctx.fillText("?", this.size[0]-17, -8)
ctx.restore()
})
chainCallback(node, "onMouseDown", function (e, pos, canvas) {
//On click would be preferred, but this'll be good enough
if (pos[1] < 0 && pos[0] + LiteGraph.NODE_TITLE_HEIGHT > this.size[0]) {
//corner question mark clicked
if (helpDOM.node == this) {
helpDOM.node = undefined
} else {
helpDOM.node = this;
helpDOM.innerHTML = this.description || "no help provided ".repeat(20)
for (let e of helpDOM.querySelectorAll('.VHS_collapse')) {
e.children[0].onclick = helpDOM.collapseOnClick
e.children[0].style.cursor = 'pointer'
}
for (let e of helpDOM.querySelectorAll('.VHS_precollapse')) {
setCollapse(e, true)
}
}
return true
}
})
let timeout = null
chainCallback(node, "onMouseMove", function (e, pos, canvas) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
if (helpDOM.node != this) {
return
}
timeout = setTimeout(() => {
let n = this
if (pos[0] > 0 && pos[0] < n.size[0]
&& pos[1] > 0 && pos[1] < n.size[1]) {
//TODO: provide help specific to element clicked
let inputRows = Math.max(n.inputs.length, n.outputs.length)
if (pos[1] < LiteGraph.NODE_SLOT_HEIGHT * inputRows) {
let row = Math.floor((pos[1] - 7) / LiteGraph.NODE_SLOT_HEIGHT)
if (pos[0] < n.size[0]/2) {
if (row < n.inputs.length) {
helpDOM.selectHelp(n.inputs[row].name)
}
} else {
if (row < n.outputs.length) {
helpDOM.selectHelp(n.outputs[row].name)
}
}
} else {
//probably widget, but widgets have variable height.
let basey = LiteGraph.NODE_SLOT_HEIGHT * inputRows + 6
for (let w of n.widgets) {
if (w.y) {
basey = w.y
}
let wheight = LiteGraph.NODE_WIDGET_HEIGHT+4
if (w.computeSize) {
wheight = w.computeSize(n.size[0])[1]
}
if (pos[1] < basey + wheight) {
helpDOM.selectHelp(w.name, w.value)
break
}
basey += wheight
}
}
}
}, 500)
})
chainCallback(node, "onMouseLeave", function (e, pos, canvas) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
});
}
}
app.registerExtension({
name: "AdvancedControlNet.documentation",
async init() {
if (app.VHSHelp) {
helpDOM = app.VHSHelp
} else {
helpDOM = document.createElement("div");
initHelpDOM()
app.VHSHelp = helpDOM
}
},
async beforeRegisterNodeDef(nodeType, nodeData, app) {
// NOTE: May need manual adjusting for the few non-namespaced nodes
if(nodeData?.name?.startsWith("ACN_") && nodeData.description) {
let description = nodeData.description
let el = document.createElement("div")
el.innerHTML = description
if (!el.children.length) {
//Is plaintext. Do minor convenience formatting
let chunks = description.split('\n')
nodeData.description = chunks[0]
description = chunks.join('<br>')
} else {
nodeData.description = el.querySelector('#VHS_shortdesc')?.innerHTML || el.children[1]?.firstChild?.innerHTML
}
chainCallback(nodeType.prototype, "onNodeCreated", function () {
helpDOM.addHelp(this, nodeType, description)
})
}
},
});