Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27a67fee80 | ||
|
|
d5c6a5ec8f | ||
|
|
516906affe | ||
|
|
ff45185d3b | ||
|
|
ffa6350020 | ||
|
|
9e60d8a9c8 | ||
|
|
b5764c344f | ||
|
|
42cdfe6c88 | ||
|
|
2f9dd25d93 | ||
|
|
0e70221a05 | ||
|
|
a4f01ba9cc | ||
|
|
d25dbc8fc3 | ||
|
|
0a0c10b25b | ||
|
|
4b37dbd421 | ||
|
|
84cbeab2e6 | ||
|
|
d508fe9027 | ||
|
|
f900f14b0b | ||
|
|
e701735c50 | ||
|
|
a0563a3fa0 | ||
|
|
ba0795aaaa | ||
|
|
9538b054cd | ||
|
|
71b536c47f | ||
|
|
b03791e456 |
@@ -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.
|
||||
@@ -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
|
||||

|
||||
|
||||
|
||||
+5
-6
@@ -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()
|
||||
|
||||
+31
-13
@@ -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,
|
||||
@@ -64,22 +64,22 @@ class ControlNetAdvanced(ControlNet, AdvancedControlBase):
|
||||
|
||||
# make cond_hint appropriate dimensions
|
||||
# TODO: change this to not require cond_hint upscaling every step when self.sub_idxs are present
|
||||
if self.sub_idxs is not None or self.cond_hint is None or x_noisy.shape[2] * self.real_compression_ratio != self.cond_hint.shape[2] or x_noisy.shape[3] * self.real_compression_ratio != self.cond_hint.shape[3]:
|
||||
if self.sub_idxs is not None or self.cond_hint is None or x_noisy.shape[-2] * self.real_compression_ratio != self.cond_hint.shape[-2] or x_noisy.shape[-1] * self.real_compression_ratio != self.cond_hint.shape[-1]:
|
||||
if self.cond_hint is not None:
|
||||
del self.cond_hint
|
||||
self.cond_hint = None
|
||||
self.real_compression_ratio = self.compression_ratio
|
||||
compression_ratio = self.compression_ratio
|
||||
if self.vae is not None and self.mult_by_ratio_when_vae:
|
||||
compression_ratio *= self.vae.downscale_ratio
|
||||
compression_ratio *= self.vae.spacial_compression_encode()
|
||||
# if self.cond_hint_original length greater or equal to real latent count, subdivide it before scaling
|
||||
if self.sub_idxs is not None:
|
||||
actual_cond_hint_orig = self.cond_hint_original
|
||||
if self.cond_hint_original.size(0) < self.full_latent_length:
|
||||
actual_cond_hint_orig = extend_to_batch_size(tensor=actual_cond_hint_orig, batch_size=self.full_latent_length)
|
||||
self.cond_hint = comfy.utils.common_upscale(actual_cond_hint_orig[self.sub_idxs], x_noisy.shape[3] * compression_ratio, x_noisy.shape[2] * compression_ratio, self.upscale_algorithm, "center")
|
||||
self.cond_hint = comfy.utils.common_upscale(actual_cond_hint_orig[self.sub_idxs], x_noisy.shape[-1] * compression_ratio, x_noisy.shape[-2] * compression_ratio, self.upscale_algorithm, "center")
|
||||
else:
|
||||
self.cond_hint = comfy.utils.common_upscale(self.cond_hint_original, x_noisy.shape[3] * compression_ratio, x_noisy.shape[2] * compression_ratio, self.upscale_algorithm, "center")
|
||||
self.cond_hint = comfy.utils.common_upscale(self.cond_hint_original, x_noisy.shape[-1] * compression_ratio, x_noisy.shape[-2] * compression_ratio, self.upscale_algorithm, "center")
|
||||
self.cond_hint = self.preprocess_image(self.cond_hint)
|
||||
if self.vae is not None:
|
||||
loaded_models = comfy.model_management.loaded_models(only_currently_used=True)
|
||||
@@ -93,7 +93,10 @@ class ControlNetAdvanced(ControlNet, AdvancedControlBase):
|
||||
to_concat = []
|
||||
for c in self.extra_concat_orig:
|
||||
c = c.to(self.cond_hint.device)
|
||||
c = comfy.utils.common_upscale(c, self.cond_hint.shape[3], self.cond_hint.shape[2], self.upscale_algorithm, "center")
|
||||
c = comfy.utils.common_upscale(c, self.cond_hint.shape[-1], self.cond_hint.shape[-2], self.upscale_algorithm, "center")
|
||||
if c.ndim < self.cond_hint.ndim:
|
||||
c = c.unsqueeze(2)
|
||||
c = comfy.utils.repeat_to_batch_size(c, self.cond_hint.shape[2], dim=2)
|
||||
to_concat.append(comfy.utils.repeat_to_batch_size(c, self.cond_hint.shape[0]))
|
||||
self.cond_hint = torch.cat([self.cond_hint] + to_concat, dim=1)
|
||||
|
||||
@@ -123,7 +126,7 @@ class ControlNetAdvanced(ControlNet, AdvancedControlBase):
|
||||
return super().pre_run_advanced(*args, **kwargs)
|
||||
|
||||
def apply_advanced_strengths_and_masks(self, x: Tensor, batched_number: int, flux_shape=None):
|
||||
if self.is_flux:
|
||||
if self.is_flux or x.ndim == 3:
|
||||
flux_shape = self.x_noisy_shape
|
||||
return super().apply_advanced_strengths_and_masks(x, batched_number, flux_shape)
|
||||
|
||||
@@ -216,7 +219,7 @@ class T2IAdapterAdvanced(T2IAdapter, AdvancedControlBase):
|
||||
del self.cond_hint
|
||||
self.cond_hint = None
|
||||
if full_cond_hint_original.size(0) < self.full_latent_length:
|
||||
actual_cond_hint_orig = extend_to_batch_size(tensor=full_cond_hint_original, batch_size=full_cond_hint_original.size(0))
|
||||
actual_cond_hint_orig = extend_to_batch_size(tensor=full_cond_hint_original, batch_size=self.full_latent_length)
|
||||
self.cond_hint_original = actual_cond_hint_orig[self.sub_idxs]
|
||||
# mask hints
|
||||
self.prepare_mask_cond_hint(x_noisy=x_noisy, t=t, cond=cond, batched_number=batched_number)
|
||||
@@ -510,7 +513,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 +522,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 +555,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:
|
||||
@@ -804,13 +813,14 @@ def load_sparsectrl(ckpt_path: str, controlnet_data: dict[str, Tensor]=None, tim
|
||||
|
||||
if controlnet_config is None:
|
||||
unet_dtype = comfy.model_management.unet_dtype()
|
||||
controlnet_config = comfy.model_detection.model_config_from_unet(controlnet_data, prefix, unet_dtype, True).unet_config
|
||||
controlnet_config = comfy.model_detection.model_config_from_unet(controlnet_data, prefix, use_base_if_no_match=True).unet_config
|
||||
load_device = comfy.model_management.get_torch_device()
|
||||
manual_cast_dtype = comfy.model_management.unet_manual_cast(unet_dtype, load_device)
|
||||
if manual_cast_dtype is not None:
|
||||
controlnet_config["operations"] = manual_cast_clean_groupnorm
|
||||
else:
|
||||
controlnet_config["operations"] = disable_weight_init_clean_groupnorm
|
||||
controlnet_config["dtype"] = unet_dtype
|
||||
controlnet_config.pop("out_channels")
|
||||
# get proper hint channels
|
||||
if use_simplified_conditioning_embedding:
|
||||
@@ -845,6 +855,10 @@ def load_sparsectrl(ckpt_path: str, controlnet_data: dict[str, Tensor]=None, tim
|
||||
missing, unexpected = control_model.load_state_dict(controlnet_data, strict=False)
|
||||
if len(missing) > 0 or len(unexpected) > 0:
|
||||
logger.info(f"SparseCtrl ControlNet: {missing}, {unexpected}")
|
||||
# cast control_model to the intended dtype; load_state_dict can leave weights
|
||||
# in their on-disk dtype (e.g. comfy's lazy/zero-copy state dict loading), which
|
||||
# would otherwise mismatch the activations at runtime
|
||||
control_model = control_model.to(unet_dtype)
|
||||
|
||||
global_average_pooling = False
|
||||
filename = os.path.splitext(ckpt_path)[0]
|
||||
@@ -939,11 +953,12 @@ def load_svdcontrolnet(ckpt_path: str, controlnet_data: dict[str, Tensor]=None,
|
||||
|
||||
if controlnet_config is None:
|
||||
unet_dtype = comfy.model_management.unet_dtype()
|
||||
controlnet_config = comfy.model_detection.model_config_from_unet(controlnet_data, prefix, unet_dtype, True).unet_config
|
||||
controlnet_config = comfy.model_detection.model_config_from_unet(controlnet_data, prefix, use_base_if_no_match=True).unet_config
|
||||
load_device = comfy.model_management.get_torch_device()
|
||||
manual_cast_dtype = comfy.model_management.unet_manual_cast(unet_dtype, load_device)
|
||||
if manual_cast_dtype is not None:
|
||||
controlnet_config["operations"] = comfy.ops.manual_cast
|
||||
controlnet_config["dtype"] = unet_dtype
|
||||
controlnet_config.pop("out_channels")
|
||||
controlnet_config["hint_channels"] = controlnet_data["{}input_hint_block.0.weight".format(prefix)].shape[1]
|
||||
control_model = SVDControlNet(**controlnet_config)
|
||||
@@ -972,6 +987,10 @@ def load_svdcontrolnet(ckpt_path: str, controlnet_data: dict[str, Tensor]=None,
|
||||
missing, unexpected = control_model.load_state_dict(controlnet_data, strict=False)
|
||||
if len(missing) > 0 or len(unexpected) > 0:
|
||||
logger.info(f"SVD-ControlNet: {missing}, {unexpected}")
|
||||
# cast control_model to the intended dtype; load_state_dict can leave weights
|
||||
# in their on-disk dtype (e.g. comfy's lazy/zero-copy state dict loading), which
|
||||
# would otherwise mismatch the activations at runtime
|
||||
control_model = control_model.to(unet_dtype)
|
||||
|
||||
global_average_pooling = False
|
||||
filename = os.path.splitext(ckpt_path)[0]
|
||||
@@ -980,4 +999,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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -223,7 +233,7 @@ class LLLiteModule(torch.nn.Module):
|
||||
mask = prepare_mask_batch(control.mask_cond_hint, (1, 1, h, w)).to(cx.dtype)
|
||||
mask = mask.view(mask.shape[0], 1, h * w).permute(0, 2, 1)
|
||||
if control.tk_mask_cond_hint is not None:
|
||||
mask_tk = prepare_mask_batch(control.mask_cond_hint, (1, 1, h, w)).to(cx.dtype)
|
||||
mask_tk = prepare_mask_batch(control.tk_mask_cond_hint, (1, 1, h, w)).to(cx.dtype)
|
||||
mask_tk = mask_tk.view(mask_tk.shape[0], 1, h * w).permute(0, 2, 1)
|
||||
|
||||
# x in uncond/cond doubles batch size
|
||||
@@ -240,7 +250,7 @@ class LLLiteModule(torch.nn.Module):
|
||||
|
||||
if mask is None:
|
||||
mask = 1.0
|
||||
elif mask_tk is not None:
|
||||
if mask_tk is not None:
|
||||
mask = mask * mask_tk
|
||||
|
||||
#logger.info(f"cs: {cx.shape}, x: {x.shape}, is_conv2d: {self.is_conv2d}")
|
||||
@@ -250,7 +260,7 @@ class LLLiteModule(torch.nn.Module):
|
||||
if control.latent_keyframes is not None:
|
||||
cx = cx * control.calc_latent_keyframe_mults(x=cx, batched_number=control.batched_number)
|
||||
if control.weights is not None and control.weights.has_uncond_multiplier:
|
||||
cond_or_uncond = control.batched_number.cond_or_uncond
|
||||
cond_or_uncond = control.cond_or_uncond
|
||||
actual_length = cx.size(0) // control.batched_number
|
||||
for idx, cond_type in enumerate(cond_or_uncond):
|
||||
# if uncond, set to weight's uncond_multiplier
|
||||
@@ -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)
|
||||
|
||||
@@ -773,6 +773,9 @@ def refcn_diffusion_model_wrapper_factory(reference_injections: ReferenceInjecti
|
||||
# if nothing related to reference controlnets, do nothing special
|
||||
if len(ref_controlnets) == 0 and len(context_controlnets) == 0:
|
||||
return executor(x, *args, **kwargs)
|
||||
adain_controlnets = []
|
||||
context_adain_controlnets = []
|
||||
orig_forward_timestep_embed = None
|
||||
try:
|
||||
# assign cond and uncond idxs
|
||||
batched_number = len(transformer_options["cond_or_uncond"])
|
||||
@@ -784,14 +787,12 @@ def refcn_diffusion_model_wrapper_factory(reference_injections: ReferenceInjecti
|
||||
transformer_options[REF_COND_IDXS] = [i for i, z in enumerate(indiv_conds) if z == 0]
|
||||
# check which controlnets do which thing
|
||||
attn_controlnets = []
|
||||
adain_controlnets = []
|
||||
for control in ref_controlnets:
|
||||
if ReferenceType.is_attn(control.ref_opts.reference_type):
|
||||
attn_controlnets.append(control)
|
||||
if ReferenceType.is_adain(control.ref_opts.reference_type):
|
||||
adain_controlnets.append(control)
|
||||
context_attn_controlnets = []
|
||||
context_adain_controlnets = []
|
||||
# for ease of access, store current contextref_cond_idx value
|
||||
if len(context_controlnets) == 0:
|
||||
transformer_options[CONTEXTREF_TEMP_COND_IDX] = -1
|
||||
@@ -877,7 +878,7 @@ def refcn_diffusion_model_wrapper_factory(reference_injections: ReferenceInjecti
|
||||
finally:
|
||||
# make sure ref banks are cleared no matter what happens - otherwise, RIP VRAM
|
||||
reference_injections.clean_ref_module_mem()
|
||||
if len(adain_controlnets) > 0 or len(context_adain_controlnets) > 0:
|
||||
if orig_forward_timestep_embed is not None:
|
||||
openaimodel.forward_timestep_embed = orig_forward_timestep_embed
|
||||
return refcn_diffusion_model_wrapper
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
]
|
||||
|
||||
@@ -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
@@ -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', display_name='effect_mask', 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', display_name='model', optional=True),
|
||||
io.Vae.Input('vae_optional', display_name='vae', 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', display_name='effect_mask', 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', display_name='model', optional=True),
|
||||
io.Vae.Input('vae_optional', display_name='vae', 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', display_name='timestep_kf', 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', display_name='timestep_kf', 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
@@ -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', display_name='mask', 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', display_name='mask', 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', display_name='mask', 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', display_name='latent', 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,)
|
||||
|
||||
+132
-111
@@ -1,107 +1,130 @@
|
||||
from comfy_api.latest import io
|
||||
from torch import Tensor
|
||||
|
||||
import folder_paths
|
||||
from comfy.model_patcher import ModelPatcher
|
||||
import comfy.utils
|
||||
|
||||
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', display_name='timestep_kf', 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', display_name='timestep_kf', 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', display_name='effect_mask', 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', display_name='vae', optional=True),
|
||||
io.Mask.Input('inpaint_mask', 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)
|
||||
weights_override: ControlWeights=None, control_apply_to_uncond=False,
|
||||
inpaint_mask: Tensor=None):
|
||||
if strength == 0 or (mask_optional is not None and mask_optional.count_nonzero().item() == 0):
|
||||
return io.NodeOutput(positive, negative)
|
||||
|
||||
extra_concat = []
|
||||
if inpaint_mask is not None and getattr(control_net, "concat_mask", False):
|
||||
source_mask = 1.0 - inpaint_mask.reshape((-1, 1, inpaint_mask.shape[-2], inpaint_mask.shape[-1]))
|
||||
mask_apply = comfy.utils.common_upscale(source_mask, image.shape[2], image.shape[1], "bilinear", "center").round()
|
||||
image = image * mask_apply.movedim(1, -1).repeat(1, 1, 1, image.shape[3])
|
||||
extra_concat = [source_mask]
|
||||
|
||||
control_hint = image.movedim(-1,1)
|
||||
cnets = {}
|
||||
@@ -121,7 +144,7 @@ class AdvancedControlNetApply:
|
||||
if control_net is None:
|
||||
raise Exception("Passed in control_net is None; something must have went wrong when loading it from a Load ControlNet node.")
|
||||
# copy, convert to advanced if needed, and set cond
|
||||
c_net = convert_to_advanced(control_net.copy()).set_cond_hint(control_hint, strength, (start_percent, end_percent), vae_optional)
|
||||
c_net = convert_to_advanced(control_net.copy()).set_cond_hint(control_hint, strength, (start_percent, end_percent), vae_optional, extra_concat)
|
||||
if is_advanced_controlnet(c_net):
|
||||
# disarm node check
|
||||
c_net.disarm()
|
||||
@@ -140,9 +163,9 @@ class AdvancedControlNetApply:
|
||||
elif not vae_optional:
|
||||
# make sure SD3 ControlNet will get a special message instead of generic type mention
|
||||
if is_sd3_advanced_controlnet(c_net):
|
||||
raise Exception(f"SD3 ControlNet requires vae_optional input, but got None.")
|
||||
raise Exception(f"SD3 ControlNet requires vae input, but got None.")
|
||||
else:
|
||||
raise Exception(f"Type '{type(c_net).__name__}' requires vae_optional input, but got None.")
|
||||
raise Exception(f"Type '{type(c_net).__name__}' requires vae input, but got None.")
|
||||
# apply optional parameters and overrides, if provided
|
||||
if timestep_kf is not None:
|
||||
c_net.set_timestep_keyframes(timestep_kf)
|
||||
@@ -167,46 +190,44 @@ 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', display_name='effect_mask', 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', display_name='vae', optional=True),
|
||||
io.Mask.Input('inpaint_mask', 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,
|
||||
weights_override: ControlWeights=None, inpaint_mask: Tensor=None):
|
||||
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],)
|
||||
control_apply_to_uncond=True, inpaint_mask=inpaint_mask)
|
||||
return io.NodeOutput(values.args[0], None)
|
||||
|
||||
@@ -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,)
|
||||
|
||||
@@ -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
@@ -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', display_name='timestep_kf', 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', display_name='timestep_kf', 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
@@ -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,)
|
||||
|
||||
@@ -359,7 +359,7 @@ def prepare_mask_batch(mask: Tensor, shape: Tensor, multiplier: int=1, match_dim
|
||||
mask = mask.clone()
|
||||
if flux_shape is not None:
|
||||
multiplier = multiplier * 0.5
|
||||
mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(round(flux_shape[-2]*multiplier), round(flux_shape[-1]*multiplier)), mode="bilinear")
|
||||
mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(math.ceil(flux_shape[-2]*multiplier), math.ceil(flux_shape[-1]*multiplier)), mode="bilinear")
|
||||
mask = rearrange(mask, "b c h w -> b (h w) c")
|
||||
else:
|
||||
mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(round(shape[-2]*multiplier), round(shape[-1]*multiplier)), mode="bilinear")
|
||||
|
||||
@@ -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 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.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
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
@@ -0,0 +1,83 @@
|
||||
# Qwen Image ControlNet inpainting
|
||||
|
||||
This reviewer example adapts the active inpainting branch of ComfyUI's official
|
||||
Qwen Image workflow. It uses **Load Advanced ControlNet Model** and **Apply
|
||||
Advanced ControlNet**, while retaining the official Qwen base pipeline and the
|
||||
bypassed optional Lightning LoRA. Node titles are left at their ComfyUI
|
||||
defaults; the workflow stores no node title overrides.
|
||||
|
||||
## Inputs and models
|
||||
|
||||
Download the official inputs to `ComfyUI/input` with these exact names:
|
||||
|
||||
- [`acn_qwen_inpaint_source.png`](https://huggingface.co/InstantX/Qwen-Image-ControlNet-Inpainting/resolve/main/assets/images/image1.png)
|
||||
- [`acn_qwen_inpaint_mask.png`](https://huggingface.co/InstantX/Qwen-Image-ControlNet-Inpainting/resolve/main/assets/masks/mask1.png)
|
||||
|
||||
The model author's repository is
|
||||
[`InstantX/Qwen-Image-ControlNet-Inpainting`](https://huggingface.co/InstantX/Qwen-Image-ControlNet-Inpainting).
|
||||
Download every model below to the listed folder under `ComfyUI/models`:
|
||||
|
||||
| File and exact download | Folder |
|
||||
| --- | --- |
|
||||
| [`qwen_image_fp8_e4m3fn.safetensors`](https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/diffusion_models/qwen_image_fp8_e4m3fn.safetensors) | `diffusion_models` |
|
||||
| [`qwen_2.5_vl_7b_fp8_scaled.safetensors`](https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/text_encoders/qwen_2.5_vl_7b_fp8_scaled.safetensors) | `text_encoders` |
|
||||
| [`qwen_image_vae.safetensors`](https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/vae/qwen_image_vae.safetensors) | `vae` |
|
||||
| [`Qwen-Image-InstantX-ControlNet-Inpainting.safetensors`](https://huggingface.co/Comfy-Org/Qwen-Image-InstantX-ControlNets/resolve/main/split_files/controlnet/Qwen-Image-InstantX-ControlNet-Inpainting.safetensors) | `controlnet` |
|
||||
| [`Qwen-Image-Lightning-4steps-V1.0.safetensors`](https://huggingface.co/lightx2v/Qwen-Image-Lightning/resolve/main/Qwen-Image-Lightning-4steps-V1.0.safetensors) | `loras` (optional and bypassed) |
|
||||
|
||||
## Run
|
||||
|
||||
1. Download the two inputs and five model files to the folders above.
|
||||
2. Load `qwen_image_inpainting.json` in ComfyUI.
|
||||
3. Queue the workflow unchanged.
|
||||
|
||||
For command-line input reproduction:
|
||||
|
||||
```sh
|
||||
curl -L https://huggingface.co/InstantX/Qwen-Image-ControlNet-Inpainting/resolve/main/assets/images/image1.png -o ComfyUI/input/acn_qwen_inpaint_source.png
|
||||
curl -L https://huggingface.co/InstantX/Qwen-Image-ControlNet-Inpainting/resolve/main/assets/masks/mask1.png -o ComfyUI/input/acn_qwen_inpaint_mask.png
|
||||
```
|
||||
|
||||
The unchanged example uses seed `134554158057228` (fixed), 20 steps, CFG 2.5,
|
||||
Euler, the simple scheduler, denoise 1.0, model shift 3.1, control strength 1.0,
|
||||
and control start/end 0.0/1.0. Its prompt is `The Queen, on a throne,
|
||||
surrounded by Knights, HD, Realistic, Octane Render, Unreal engine`; the
|
||||
negative prompt is one space. The source is scaled with area interpolation to a
|
||||
maximum dimension of 1536. The optional 4-step LoRA remains bypassed; enabling
|
||||
it requires changing the sampler settings appropriately.
|
||||
|
||||
The two native **Load Image** nodes are intentionally separate. **Image To
|
||||
Mask** reads the red channel of the mask PNG. That source `inpaint_mask` defines
|
||||
the region supplied to the inpainting ControlNet and the latent noise mask. It
|
||||
is not the Advanced-ControlNet effect mask. `effect_mask` is left unconnected
|
||||
and independently limits where control is injected. The Apply node also exposes
|
||||
unconnected timestep keyframe, latent keyframe, and weights ports for focused
|
||||
reviewer experiments.
|
||||
|
||||
## Measured validation evidence
|
||||
|
||||
These results were measured with fixed inputs and settings; they are recorded
|
||||
here rather than inferred from the example image:
|
||||
|
||||
- A fresh isolated vanilla-versus-Advanced run had latent maximum/mean absolute
|
||||
differences `0/0`, pixel maximum/mean differences `0/0`, and 0 changed
|
||||
pixels.
|
||||
- An all-one effect mask exactly equaled unmasked Advanced output at latent and
|
||||
pixel level. An all-zero effect mask exactly equaled no ControlNet at latent
|
||||
and pixel level.
|
||||
- For right-half token-mask injection, relative to full control the left latent
|
||||
mean delta was `0` and the right was `0.1332103`; relative to no control the
|
||||
left was `0` and the right was `0.1835042`.
|
||||
- In a per-latent batch, the sample with strength 0 exactly equaled no control
|
||||
at latent and pixel level.
|
||||
- Soft weights, timestep scheduling, and two-control stacking each executed
|
||||
successfully.
|
||||
- The existing Anima real workflow rerun retained exact before/after latent and
|
||||
pixel equality.
|
||||
|
||||
Frontend and API validation confirms that the normal Apply node exposes the
|
||||
optional source mask as `inpaint_mask`. When connected to a ControlNet without
|
||||
source-mask support, that input is ignored and the normal control path is used.
|
||||
|
||||
Workflow and result screenshots are linked from the PR instead of stored here
|
||||
to avoid repository growth.
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-advanced-controlnet"
|
||||
description = "Nodes for scheduling ControlNet strength across timesteps and batched latents, as well as applying custom weights and attention masks."
|
||||
version = "1.5.7"
|
||||
version = "1.6.0"
|
||||
license = { file = "LICENSE" }
|
||||
dependencies = []
|
||||
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import Mock, patch, sentinel
|
||||
|
||||
comfyui_path = os.environ.get("COMFYUI_PATH")
|
||||
if comfyui_path:
|
||||
sys.path.insert(0, comfyui_path)
|
||||
|
||||
import torch
|
||||
|
||||
from comfy.controlnet import T2IAdapter
|
||||
|
||||
from adv_control.control import ControlNetAdvanced, T2IAdapterAdvanced
|
||||
from adv_control.nodes_main import AdvancedControlNetApply
|
||||
from adv_control.utils import ControlWeights
|
||||
|
||||
|
||||
class StopControlModel(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ControlModel:
|
||||
dtype = torch.float32
|
||||
|
||||
def __init__(self):
|
||||
self.hint = None
|
||||
|
||||
def __call__(self, x, hint, timesteps, context, **kwargs):
|
||||
self.hint = hint
|
||||
raise StopControlModel
|
||||
|
||||
|
||||
class VideoVAE:
|
||||
downscale_ratio = (4, 8, 8)
|
||||
|
||||
def __init__(self):
|
||||
self.encoded_shape = None
|
||||
|
||||
def spacial_compression_encode(self):
|
||||
return 8
|
||||
|
||||
def encode(self, image):
|
||||
self.encoded_shape = image.shape
|
||||
return torch.ones((image.shape[0], 4, 2, 2, 2))
|
||||
|
||||
|
||||
class ModernControlPreprocessingTests(unittest.TestCase):
|
||||
def test_effect_mask_is_resized_to_qwen_tokens(self):
|
||||
control = ControlNetAdvanced(ControlModel(), None)
|
||||
control.x_noisy_shape = (1, 16, 4, 6)
|
||||
control.mask_cond_hint = torch.tensor(
|
||||
[[[[0.0, 0.0, 0.0, 1.0, 1.0, 1.0]] * 4]]
|
||||
)
|
||||
control.tk_mask_cond_hint = None
|
||||
control.weights = SimpleNamespace(has_uncond_multiplier=False, has_uncond_mask=False)
|
||||
control.latent_keyframes = None
|
||||
control._current_timestep_keyframe = SimpleNamespace(strength=1.0)
|
||||
|
||||
output = torch.ones((1, 6, 4))
|
||||
control.apply_advanced_strengths_and_masks(output, batched_number=1)
|
||||
|
||||
expected = torch.tensor(
|
||||
[[[0.0] * 4, [0.5] * 4, [1.0] * 4, [0.0] * 4, [0.5] * 4, [1.0] * 4]]
|
||||
)
|
||||
torch.testing.assert_close(output, expected)
|
||||
|
||||
def test_effect_mask_matches_padded_flux_tokens_for_odd_latent_size(self):
|
||||
control = ControlNetAdvanced(ControlModel(), None)
|
||||
control.x_noisy_shape = (1, 16, 5, 7)
|
||||
control.mask_cond_hint = torch.ones((1, 1, 5, 7))
|
||||
control.tk_mask_cond_hint = None
|
||||
control.weights = SimpleNamespace(has_uncond_multiplier=False, has_uncond_mask=False)
|
||||
control.latent_keyframes = None
|
||||
control._current_timestep_keyframe = SimpleNamespace(strength=1.0)
|
||||
|
||||
output = torch.ones((1, 12, 4))
|
||||
control.apply_advanced_strengths_and_masks(output, batched_number=1)
|
||||
|
||||
torch.testing.assert_close(output, torch.ones_like(output))
|
||||
|
||||
def test_vae_compression_and_source_mask_match_5d_hint(self):
|
||||
control_model = ControlModel()
|
||||
vae = VideoVAE()
|
||||
control = ControlNetAdvanced(control_model, None, compression_ratio=1, latent_format=SimpleNamespace(process_in=lambda value: value))
|
||||
control.real_compression_ratio = 1
|
||||
control.cond_hint_original = torch.ones((1, 3, 16, 16))
|
||||
control.cond_hint = None
|
||||
control.vae = vae
|
||||
control.extra_concat_orig = [torch.zeros((1, 1, 16, 16))]
|
||||
control.sub_idxs = None
|
||||
control.model_sampling_current = SimpleNamespace(timestep=lambda value: value, calculate_input=lambda timestep, value: value)
|
||||
control.prepare_mask_cond_hint = lambda **kwargs: None
|
||||
|
||||
with self.assertRaises(StopControlModel):
|
||||
control.sliding_get_control(
|
||||
torch.ones((1, 4, 2, 2, 2)),
|
||||
torch.ones(1),
|
||||
{"c_crossattn": torch.ones((1, 1, 1))},
|
||||
1,
|
||||
{},
|
||||
)
|
||||
|
||||
self.assertEqual(tuple(vae.encoded_shape), (1, 16, 16, 3))
|
||||
self.assertEqual(tuple(control_model.hint.shape), (1, 5, 2, 2, 2))
|
||||
|
||||
|
||||
class T2IAdapterTests(unittest.TestCase):
|
||||
def test_effect_masks_are_applied_to_adapter_features(self):
|
||||
control = T2IAdapterAdvanced(SimpleNamespace(), None, channels_in=3)
|
||||
control.weights = ControlWeights.t2iadapter()
|
||||
control.latent_keyframes = None
|
||||
control.tk_mask_cond_hint = None
|
||||
control._current_timestep_keyframe = SimpleNamespace(strength=1.0)
|
||||
|
||||
masks = {
|
||||
"zero": torch.zeros((1, 1, 8, 8)),
|
||||
"one": torch.ones((1, 1, 8, 8)),
|
||||
"half": torch.cat((torch.zeros((1, 1, 8, 4)), torch.ones((1, 1, 8, 4))), dim=3),
|
||||
}
|
||||
for name, mask in masks.items():
|
||||
with self.subTest(name=name):
|
||||
features = torch.ones((1, 4, 8, 8))
|
||||
control.mask_cond_hint = mask
|
||||
control.apply_advanced_strengths_and_masks(features, batched_number=1)
|
||||
torch.testing.assert_close(features, mask.expand_as(features))
|
||||
|
||||
def test_sliding_context_extends_single_hint_to_full_latent_length(self):
|
||||
control = T2IAdapterAdvanced(SimpleNamespace(), None, channels_in=3)
|
||||
original_hint = torch.ones((1, 3, 8, 8))
|
||||
control.cond_hint_original = original_hint
|
||||
control.cond_hint = None
|
||||
control.sub_idxs = [2, 3]
|
||||
control.full_latent_length = 4
|
||||
control.prepare_mask_cond_hint = lambda **kwargs: None
|
||||
selected_hint = None
|
||||
|
||||
def get_control(adapter, *args, **kwargs):
|
||||
nonlocal selected_hint
|
||||
selected_hint = adapter.cond_hint_original.clone()
|
||||
return sentinel.output
|
||||
|
||||
with patch.object(T2IAdapter, "get_control", get_control):
|
||||
result = control.get_control_advanced(
|
||||
torch.ones((2, 4, 8, 8)),
|
||||
torch.ones(2),
|
||||
{},
|
||||
1,
|
||||
{},
|
||||
)
|
||||
|
||||
self.assertIs(result, sentinel.output)
|
||||
self.assertEqual(tuple(selected_hint.shape), (2, 3, 8, 8))
|
||||
torch.testing.assert_close(selected_hint, original_hint.repeat(2, 1, 1, 1))
|
||||
self.assertIs(control.cond_hint_original, original_hint)
|
||||
|
||||
|
||||
class AdvancedControlNetApplyTests(unittest.TestCase):
|
||||
def apply_control(self, concat_mask, image, inpaint_mask, effect_mask=None):
|
||||
control_net = SimpleNamespace(concat_mask=concat_mask, copy=Mock(return_value=sentinel.control_copy))
|
||||
applied_control = SimpleNamespace(
|
||||
allow_condhint_latents=False,
|
||||
require_vae=False,
|
||||
postpone_condhint_latents_check=False,
|
||||
disarm=Mock(),
|
||||
set_cond_hint=Mock(),
|
||||
set_cond_hint_mask=Mock(),
|
||||
set_previous_controlnet=Mock(),
|
||||
verify_all_weights=Mock(),
|
||||
)
|
||||
applied_control.set_cond_hint.return_value = applied_control
|
||||
positive = [[sentinel.positive_tensor, {}]]
|
||||
|
||||
with patch("adv_control.nodes_main.convert_to_advanced", return_value=applied_control), \
|
||||
patch("adv_control.nodes_main.is_advanced_controlnet", return_value=True):
|
||||
AdvancedControlNetApply.execute(
|
||||
positive=positive,
|
||||
negative=[],
|
||||
control_net=control_net,
|
||||
image=image,
|
||||
strength=1.0,
|
||||
start_percent=0.0,
|
||||
end_percent=1.0,
|
||||
mask_optional=effect_mask,
|
||||
vae_optional=sentinel.vae,
|
||||
inpaint_mask=inpaint_mask,
|
||||
)
|
||||
|
||||
return applied_control
|
||||
|
||||
def test_all_zero_effect_mask_returns_original_conditioning(self):
|
||||
positive = [[sentinel.positive_tensor, {"name": "positive"}]]
|
||||
negative = [[sentinel.negative_tensor, {"name": "negative"}]]
|
||||
|
||||
result = AdvancedControlNetApply.execute(
|
||||
positive=positive,
|
||||
negative=negative,
|
||||
control_net=sentinel.control_net,
|
||||
image=torch.ones((1, 8, 8, 3)),
|
||||
strength=1.0,
|
||||
start_percent=0.0,
|
||||
end_percent=1.0,
|
||||
mask_optional=torch.zeros((1, 8, 8)),
|
||||
)
|
||||
|
||||
self.assertIs(result.args[0], positive)
|
||||
self.assertIs(result.args[1], negative)
|
||||
|
||||
def test_source_mask_and_effect_mask_stay_independent(self):
|
||||
image = torch.ones((1, 2, 2, 3))
|
||||
inpaint_mask = torch.tensor([[[1.0, 0.0], [1.0, 0.0]]])
|
||||
effect_mask = torch.full((1, 2, 2), 0.25)
|
||||
applied_control = self.apply_control(True, image, inpaint_mask, effect_mask)
|
||||
|
||||
inputs = applied_control.set_cond_hint.call_args.args
|
||||
source_mask = 1.0 - inpaint_mask.unsqueeze(1)
|
||||
torch.testing.assert_close(inputs[0], (image * source_mask.movedim(1, -1)).movedim(-1, 1))
|
||||
torch.testing.assert_close(inputs[4][0], source_mask)
|
||||
torch.testing.assert_close(applied_control.set_cond_hint_mask.call_args.args[0], effect_mask)
|
||||
|
||||
def test_inpaint_mask_is_ignored_for_other_controlnets(self):
|
||||
image = torch.ones((1, 2, 2, 3))
|
||||
inpaint_mask = torch.tensor([[[1.0, 0.0], [1.0, 0.0]]])
|
||||
applied_control = self.apply_control(False, image, inpaint_mask)
|
||||
|
||||
inputs = applied_control.set_cond_hint.call_args.args
|
||||
torch.testing.assert_close(
|
||||
inputs[0],
|
||||
image.movedim(-1, 1),
|
||||
)
|
||||
self.assertEqual(inputs[4], [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,104 @@
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
comfyui_path = os.environ.get("COMFYUI_PATH")
|
||||
if comfyui_path:
|
||||
sys.path.insert(0, comfyui_path)
|
||||
|
||||
import torch
|
||||
|
||||
from comfy.controlnet import T2IAdapter
|
||||
|
||||
from adv_control.control import T2IAdapterAdvanced
|
||||
from adv_control.control_lllite import LLLiteModule
|
||||
from adv_control.control_reference import REF_CONTROL_LIST_ALL, RefConst, refcn_diffusion_model_wrapper_factory
|
||||
|
||||
|
||||
class LLLiteRegressionTests(unittest.TestCase):
|
||||
def create_module(self):
|
||||
torch.manual_seed(1)
|
||||
return LLLiteModule("test", False, 2, 1, 2, 2)
|
||||
|
||||
def create_control(self, effect_mask=None, timestep_mask=None, uncond_multiplier=1.0):
|
||||
return SimpleNamespace(
|
||||
sub_idxs=None,
|
||||
cond_hint=torch.ones((1, 3, 8, 8)),
|
||||
latent_dims_div2=None,
|
||||
latent_dims_div4=None,
|
||||
mask_cond_hint=effect_mask,
|
||||
tk_mask_cond_hint=timestep_mask,
|
||||
latent_keyframes=None,
|
||||
weights=SimpleNamespace(
|
||||
has_uncond_multiplier=uncond_multiplier != 1.0,
|
||||
uncond_multiplier=uncond_multiplier,
|
||||
),
|
||||
batched_number=2,
|
||||
cond_or_uncond=[0, 1],
|
||||
strength=1.0,
|
||||
_current_timestep_keyframe=SimpleNamespace(strength=1.0),
|
||||
)
|
||||
|
||||
def test_unconditional_multiplier_uses_sampling_condition_order(self):
|
||||
control = self.create_control(uncond_multiplier=0.25)
|
||||
output = self.create_module()(torch.ones((2, 1, 2)), control)
|
||||
|
||||
torch.testing.assert_close(output[1], output[0] * 0.25)
|
||||
|
||||
def test_timestep_mask_applies_without_effect_mask(self):
|
||||
control = self.create_control(timestep_mask=torch.zeros((1, 8, 8)))
|
||||
output = self.create_module()(torch.ones((2, 1, 2)), control)
|
||||
|
||||
torch.testing.assert_close(output, torch.zeros_like(output))
|
||||
|
||||
def test_effect_and_timestep_masks_are_combined(self):
|
||||
control = self.create_control(
|
||||
effect_mask=torch.ones((1, 8, 8)),
|
||||
timestep_mask=torch.zeros((1, 8, 8)),
|
||||
)
|
||||
output = self.create_module()(torch.ones((2, 1, 2)), control)
|
||||
|
||||
torch.testing.assert_close(output, torch.zeros_like(output))
|
||||
|
||||
|
||||
class T2IAdapterRegressionTests(unittest.TestCase):
|
||||
def test_sliding_context_extends_hint_to_full_latent_length(self):
|
||||
adapter = object.__new__(T2IAdapterAdvanced)
|
||||
adapter.sub_idxs = [2, 3]
|
||||
adapter.full_latent_length = 4
|
||||
adapter.cond_hint_original = torch.tensor([[[[7.0]]]])
|
||||
adapter.cond_hint = None
|
||||
adapter.prepare_mask_cond_hint = lambda **kwargs: None
|
||||
|
||||
with patch.object(T2IAdapter, "get_control", lambda self, *args, **kwargs: self.cond_hint_original.clone()):
|
||||
output = adapter.get_control_advanced(torch.empty((2, 4, 1, 1)), None, None, 1, {})
|
||||
|
||||
self.assertEqual(output.flatten().tolist(), [7.0, 7.0])
|
||||
self.assertEqual(adapter.cond_hint_original.flatten().tolist(), [7.0])
|
||||
|
||||
|
||||
class ReferenceRegressionTests(unittest.TestCase):
|
||||
def test_cleanup_does_not_hide_original_exception(self):
|
||||
class ReferenceInjections:
|
||||
cleaned = False
|
||||
|
||||
def clean_ref_module_mem(self):
|
||||
self.cleaned = True
|
||||
|
||||
reference_injections = ReferenceInjections()
|
||||
wrapper = refcn_diffusion_model_wrapper_factory(reference_injections)
|
||||
transformer_options = {
|
||||
REF_CONTROL_LIST_ALL: [SimpleNamespace(should_run=lambda: True)],
|
||||
RefConst.REFCN_PRESENT_IN_CONDS: True,
|
||||
}
|
||||
|
||||
with self.assertRaisesRegex(KeyError, "cond_or_uncond"):
|
||||
wrapper(lambda *args, **kwargs: None, torch.zeros(1), None, None, None, None, transformer_options)
|
||||
|
||||
self.assertTrue(reference_injections.cleaned)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user