Files
budihartono-comfyui-aspect-…/nodes.py
T
Budi Hartono 36d1667eb0 Add model-filtered preset picker and per-model latent channel count
- EmptyLatentAspectPreset gains a "model" widget; web/aspect_ratio_filter.js
  filters the "preset" combo to that model client-side, parsed from each
  preset's label so presets.py stays the single source of truth.
- Fix latent channel count: SD1.5/SDXL use 4-channel latents, but
  Flux/HiDream/Krea/Qwen-Image use 16-channel latents (same family as
  ComfyUI's EmptySD3LatentImage) — was previously hardcoded to 4 for all
  models, breaking/corrupting generations on the newer models.
- Drop Ideogram and ERNIE from presets.py: both are hosted API models with
  no local LATENT/diffusion pipeline in ComfyUI, so listing them implied
  false compatibility.
2026-08-14 17:29:40 +07:00

116 lines
4.1 KiB
Python

import torch
from .presets import PRESETS
def _validate_dim(v: int):
if v <= 0 or v % 8 != 0:
raise ValueError("Dimension must be >0 and divisible by 8")
class EmptyLatentAspectPreset:
"""Creates a blank latent using one of the predefined presets."""
# Built once at import time and shared by all instances/calls.
PRESET_MAP = {
f"{w}x{h} - {lbl} - {model}": (w, h)
for model, lbl, w, h in PRESETS
}
# Unique models in first-appearance order; the "model" widget below is purely a
# client-side filter (see web/aspect_ratio_filter.js) for the "preset" dropdown,
# which already encodes the model in its label — parsed there, not duplicated.
MODELS = list(dict.fromkeys(model for model, _, _, _ in PRESETS))
# Latent channel count per model family. SD1.5/SDXL use the legacy 4-channel VAE;
# Flux/HiDream/Qwen-Image/Krea use a 16-channel VAE (same family as ComfyUI's own
# EmptySD3LatentImage) — feeding a 4-channel latent into those samplers fails or
# silently produces garbage.
LATENT_CHANNELS = {
"SD15": 4,
"SDXL": 4,
"HiDream": 16,
"Flux.1": 16,
"Flux.2": 16,
"Krea": 16,
"Qwen-Image": 16,
}
DEFAULT_LATENT_CHANNELS = 4
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"model": (cls.MODELS,),
"preset": (list(cls.PRESET_MAP.keys()),),
"batch_size": ("INT", {"default": 1, "min": 1})
}
}
RETURN_TYPES = ("LATENT", "INT", "INT")
RETURN_NAMES = ("LATENT", "width", "height")
FUNCTION = "generate"
CATEGORY = "latent" # moved into ComfyUI's built-in "latent" category
def generate(self, model: str, preset: str, batch_size: int):
if preset not in self.PRESET_MAP:
raise ValueError(f"Unknown preset: {preset}")
w, h = self.PRESET_MAP[preset]
_validate_dim(w)
_validate_dim(h)
channels = self.LATENT_CHANNELS.get(model, self.DEFAULT_LATENT_CHANNELS)
latent = torch.zeros([batch_size, channels, h // 8, w // 8], dtype=torch.float32)
return ({"samples": latent}, w, h)
class EmptyLatentAspectByAxis:
"""Creates a blank latent by fixing one axis and computing the other from an aspect ratio."""
ASPECT_CHOICES = [
("1:1 Square", (1, 1)),
("3:2 Landscape", (3, 2)),
("2:3 Portrait", (2, 3)),
("4:3 Landscape", (4, 3)),
("3:4 Portrait", (3, 4)),
("16:9 Landscape", (16, 9)),
("9:16 Portrait", (9, 16)),
("5:4 Landscape", (5, 4)),
("4:5 Portrait", (4, 5)),
("21:9 Widescreen",(21, 9)),
("9:21 Portrait", (9, 21)),
("7:5 Landscape", (7, 5)),
("5:7 Portrait", (5, 7)),
]
REFERENCE_CHOICES = ["Width", "Height"]
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"primary_dim": ("INT", {"default": 512, "min": 8}),
"reference": (cls.REFERENCE_CHOICES,),
"aspect_ratio": ([lbl for lbl,_ in cls.ASPECT_CHOICES],),
"batch_size": ("INT", {"default": 1, "min": 1})
}
}
RETURN_TYPES = ("LATENT", "INT", "INT")
RETURN_NAMES = ("LATENT", "width", "height")
FUNCTION = "generate"
CATEGORY = "latent" # now appears under the built-in latent category
RATIO_MAP = dict(ASPECT_CHOICES)
def generate(self, primary_dim: int, reference: str, aspect_ratio: str, batch_size: int):
if aspect_ratio not in self.RATIO_MAP:
raise ValueError(f"Unknown aspect ratio: {aspect_ratio}")
wr, hr = self.RATIO_MAP[aspect_ratio]
_validate_dim(primary_dim)
if reference == "Width":
w, h = primary_dim, round(primary_dim * hr / wr)
else:
h, w = primary_dim, round(primary_dim * wr / hr)
_validate_dim(w)
_validate_dim(h)
latent = torch.zeros([batch_size, 4, h // 8, w // 8], dtype=torch.float32)
return ({"samples": latent}, w, h)