Text-to-Image
Diffusers
anima
lora
in-context
character-reference
ip-adapter-alternative
comfyui
anime
Instructions to use darask0/Anima-InContext-Character with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use darask0/Anima-InContext-Character with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("circlestone-labs/Anima", torch_dtype=torch.bfloat16, device_map="cuda") pipe.load_lora_weights("darask0/Anima-InContext-Character") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Draw Things
- DiffusionBee
File size: 6,782 Bytes
e545366 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | """ComfyUI nodes for Anima in-context character reference."""
import torch
import torch.nn.functional as F
from .incontext import apply_incontext_ref, _fit_latent
def _fit_pixels_white_pad(pixels, target_h, target_w):
"""Aspect-preserving resize of (B, H, W, C) pixels to the target
size, centered on a white canvas. White matches the background that
AnimaRefEncode composites masked subjects onto."""
b, h, w, c = pixels.shape
if (h, w) == (target_h, target_w):
return pixels
scale = min(target_h / h, target_w / w)
nh = max(1, min(target_h, round(h * scale)))
nw = max(1, min(target_w, round(w * scale)))
resized = F.interpolate(
pixels.permute(0, 3, 1, 2), size=(nh, nw), mode="bilinear", align_corners=False
).permute(0, 2, 3, 1)
canvas = torch.ones((b, target_h, target_w, c), device=pixels.device, dtype=pixels.dtype)
top = (target_h - nh) // 2
left = (target_w - nw) // 2
canvas[:, top:top + nh, left:left + nw] = resized
return canvas
class AnimaRefEncode:
"""Prepare a reference image for in-context conditioning.
Optionally composites the subject onto a white background using a
mask (recommended: character segmentation mask). Background removal
measurably improves appearance fidelity for anime subjects.
target_width / target_height (0 = keep): aspect-preserving resize
onto a white canvas before encoding. Set these to the generation
resolution to avoid any latent-space resampling at sampling time.
Multiple reference images can be batched; each becomes its own
reference frame.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"vae": ("VAE",),
"image": ("IMAGE",),
},
"optional": {
"mask": ("MASK",),
"target_width": ("INT", {"default": 0, "min": 0, "max": 8192, "step": 8}),
"target_height": ("INT", {"default": 0, "min": 0, "max": 8192, "step": 8}),
},
}
RETURN_TYPES = ("LATENT",)
FUNCTION = "encode"
CATEGORY = "anima/incontext"
def encode(self, vae, image, mask=None, target_width=0, target_height=0):
pixels = image
if mask is not None:
m = mask
if m.ndim == 2:
m = m.unsqueeze(0)
m = m.unsqueeze(-1) # (B, H, W, 1)
if m.shape[1:3] != pixels.shape[1:3]:
m = F.interpolate(
m.permute(0, 3, 1, 2), size=pixels.shape[1:3], mode="bilinear", align_corners=False
).permute(0, 2, 3, 1)
pixels = pixels * m + (1.0 - m) # subject on white
if target_width > 0 and target_height > 0:
pixels = _fit_pixels_white_pad(pixels, target_height, target_width)
latent = vae.encode(pixels[:, :, :, :3])
return ({"samples": latent},)
class AnimaRefLatentBatch:
"""Batch two reference latents into one multi-frame reference.
Reference latents of different resolutions are combined by fitting
the second latent to the first one's size (aspect-preserving pad by
default). Chain several of these to stack many references."""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"ref_latent_1": ("LATENT",),
"ref_latent_2": ("LATENT",),
"fit_mode": (["pad", "stretch", "crop"], {"default": "pad"}),
},
}
RETURN_TYPES = ("LATENT",)
FUNCTION = "batch"
CATEGORY = "anima/incontext"
def batch(self, ref_latent_1, ref_latent_2, fit_mode):
s1 = ref_latent_1["samples"]
s2 = ref_latent_2["samples"]
if s1.ndim == 5:
s1 = s1.squeeze(2)
if s2.ndim == 5:
s2 = s2.squeeze(2)
if s2.shape[-2:] != s1.shape[-2:]:
s2 = _fit_latent(s2, s1.shape[-2], s1.shape[-1], fit_mode)
return ({"samples": torch.cat([s1, s2], dim=0)},)
class AnimaInContextApply:
"""Attach in-context reference frames to an Anima model.
The reference latent is concatenated on the DiT's temporal axis as
clean (t=0) frames; the generated frame attends to it via shared
self-attention. Combine with an in-context reference LoRA trained
with the same contract for full effect.
strength: attention bias toward reference tokens.
1.0 = neutral, >1 stronger, 0 = off.
start_percent / end_percent: sampling step window where the
reference is attached.
cond_only: mask the reference for the uncond half of CFG. Matches
the training contract (reference dropped for the
unconditional distribution) — recommended on.
fit_mode: how a reference latent that does not match the generation
resolution is fitted (pad = aspect-preserving, default).
ref_timestep: timestep for reference frames. 0 = clean image
(the training contract). Small values (~0.05–0.1 of the
schedule) act as noise augmentation if a future LoRA is
trained that way.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"model": ("MODEL",),
"ref_latent": ("LATENT",),
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.05}),
"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": {
"cond_only": ("BOOLEAN", {"default": True}),
"fit_mode": (["pad", "stretch", "crop"], {"default": "pad"}),
"ref_timestep": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1000.0, "step": 0.5}),
},
}
RETURN_TYPES = ("MODEL",)
FUNCTION = "apply"
CATEGORY = "anima/incontext"
def apply(self, model, ref_latent, strength, start_percent, end_percent,
cond_only=True, fit_mode="pad", ref_timestep=0.0):
samples = ref_latent["samples"]
m = apply_incontext_ref(
model, samples, strength, start_percent, end_percent,
cond_only=cond_only, fit_mode=fit_mode, ref_timestep=ref_timestep,
)
return (m,)
NODE_CLASS_MAPPINGS = {
"AnimaRefEncode": AnimaRefEncode,
"AnimaRefLatentBatch": AnimaRefLatentBatch,
"AnimaInContextApply": AnimaInContextApply,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"AnimaRefEncode": "Anima Reference Encode (in-context)",
"AnimaRefLatentBatch": "Anima Reference Latent Batch",
"AnimaInContextApply": "Anima In-Context Reference Apply",
}
|