diff --git a/README.md b/README.md index 6d7d646..dd946ab 100644 --- a/README.md +++ b/README.md @@ -46,16 +46,17 @@ LoadImage ──► IMAGE ──┘ ▲ ``` - **`image`** — the conditioning image (the grayscale / lineart page for - colorization). It is resized to `target_megapixels` (aspect-preserving, + colorization). It is resized to at most `target_megapixels` (aspect-preserving, VAE/patch-snapped) and VAE-encoded once; the returned `LATENT` is sized to that same grid, so the cond stream and the sampled target share a resolution. - **`strength`** — scales the trained cond effect. `1.0` = as trained. Raise to push harder toward the reference, lower to loosen. -- **`target_megapixels`** (optional) — output size in megapixels for the returned - latent and the cond encode. `1.0` (default) keeps generation on the ~1MP - distribution Anima was trained at; `0` keeps the input's native resolution - (still snapped to the VAE/patch grid). Feed the `LATENT` output into the - KSampler's `latent_image`. +- **`target_megapixels`** (optional) — max output size in megapixels for the + returned latent and the cond encode. `1.0` (default) keeps generation on the + ~1MP distribution Anima was trained at by downscaling anything larger; a source + already below the cap is left at its native resolution (never upscaled). `0` + keeps the input's native resolution (still snapped to the VAE/patch grid). Feed + the `LATENT` output into the KSampler's `latent_image`. - **`cond_scale_override`** (optional) — replace the checkpoint's trained `cond_scale` outright (before `strength`); `0` = keep the trained value. diff --git a/nodes.py b/nodes.py index a5bc51e..974448c 100644 --- a/nodes.py +++ b/nodes.py @@ -32,9 +32,12 @@ def _vae_spatial(vae): def _resize_to_megapixels(image, vae, target_mp): - """Resize an IMAGE ``[B,H,W,C]`` (in [0,1]) to ~``target_mp`` megapixels, - aspect-preserving, snapping each side to ``vae_spatial * patch(2)`` so the - resulting latent has even dims (cosmos ``patch_spatial=2``). + """Resize an IMAGE ``[B,H,W,C]`` (in [0,1]) to at most ``target_mp`` + megapixels, aspect-preserving, snapping each side to ``vae_spatial * + patch(2)`` so the resulting latent has even dims (cosmos ``patch_spatial=2``). + + ``target_mp`` is a MAX cap: a source already below it is left at its native + resolution (only snapped) rather than upscaled. ``target_mp <= 0`` means keep the native resolution (still snapped, so the VAE/patch grid is clean). Returns the resized image ``[B,H,W,C]``. @@ -44,7 +47,9 @@ def _resize_to_megapixels(image, vae, target_mp): mult_h, mult_w = sh * 2, sw * 2 if target_mp and target_mp > 0: - scale = (target_mp * 1_000_000 / float(h * w)) ** 0.5 + # target_mp is a MAX cap: only downscale when the source exceeds it, + # never upscale a smaller source (clamp scale to <= 1.0). + scale = min(1.0, (target_mp * 1_000_000 / float(h * w)) ** 0.5) new_h, new_w = h * scale, w * scale else: new_h, new_w = float(h), float(w) diff --git a/pyproject.toml b/pyproject.toml index 3fa35eb..25a0e5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui-easycontrol-ksamplercompat" description = "KSampler-compatible Anima EasyControl image conditioning for ComfyUI. A single model-patch node (MODEL + VAE + IMAGE -> MODEL) that feeds a reference image into the Anima DiT's frozen extended self-attention stream and returns a patched MODEL for the stock KSampler — no dedicated sampler node. Reimplements anima_lora's EasyControl inference path against ComfyUI's native Anima/Cosmos DiT backbone (split q/k/v projections). EasyControl checkpoints go in models/loras/. Typical use: grayscale/lineart -> color (colorization)." -version = "0.1.4" +version = "0.1.5" license = { file = "LICENSE" } dependencies = []