feat: mode-based image/video presets, multiple profiles, and compatibility fixes
This commit is contained in:
+117
@@ -0,0 +1,117 @@
|
||||
MODES = ["Image", "Video"]
|
||||
|
||||
IMAGE_PRESETS = {
|
||||
"SD (512)": 512,
|
||||
"HD (768)": 768,
|
||||
"1K (1024)": 1024,
|
||||
"2K (2048)": 2048,
|
||||
"3K (3072)": 3072,
|
||||
"4K (4096)": 4096,
|
||||
}
|
||||
|
||||
VIDEO_PRESETS = {
|
||||
"520p (960x520)": 960,
|
||||
"720p (1280x720)": 1280,
|
||||
"1080p (1920x1080) - Premium": 1920,
|
||||
}
|
||||
|
||||
MULTIPLE_PROFILES = {
|
||||
"Auto (Image=64, Video=32)": "auto",
|
||||
"Flux / SDXL (64)": 64,
|
||||
"LTX (32)": 32,
|
||||
"Wan (16)": 16,
|
||||
"SD1.5 (8)": 8,
|
||||
}
|
||||
|
||||
IMAGE_PRESET_KEYS = list(IMAGE_PRESETS.keys())
|
||||
VIDEO_PRESET_KEYS = list(VIDEO_PRESETS.keys())
|
||||
MULTIPLE_PROFILE_KEYS = list(MULTIPLE_PROFILES.keys())
|
||||
|
||||
LEGACY_PRESETS = {
|
||||
"SD-512px": 512,
|
||||
"HD-1280px": 1280,
|
||||
"FullHD-1920px": 1920,
|
||||
"2K-2048px": 2048,
|
||||
"QHD-2560px": 2560,
|
||||
"4K-3840px": 3840,
|
||||
"8K-7680px": 7680,
|
||||
"FullHD_1920x1080": 1920,
|
||||
}
|
||||
|
||||
ASPECT_RATIOS = {
|
||||
# Square
|
||||
"1:1-AlbumArt": (1, 1),
|
||||
|
||||
# Portrait Formats
|
||||
"4:5-InstagramPortrait": (4, 5),
|
||||
"2:3-ClassicPortrait": (2, 3),
|
||||
"3:4-EditorialPortrait": (3, 4),
|
||||
|
||||
# Vertical Video
|
||||
"9:16-TikTokReel": (9, 16),
|
||||
|
||||
# Landscape / Video
|
||||
"4:3-ClassicVideo": (4, 3),
|
||||
"16:9-Widescreen": (16, 9),
|
||||
|
||||
# Cinematic Aspect
|
||||
"21:9-CinemaScope": (21, 9),
|
||||
|
||||
# Ultra Wide
|
||||
"32:9-UltraWideBanner": (32, 9),
|
||||
}
|
||||
|
||||
def snap_to_multiple(x, multiple):
|
||||
return max(multiple, int(round(x / float(multiple))) * multiple)
|
||||
|
||||
|
||||
def resolve_multiple(mode, multiple_profile):
|
||||
value = MULTIPLE_PROFILES.get(multiple_profile, "auto")
|
||||
if value == "auto":
|
||||
return 32 if mode == "Video" else 64
|
||||
return int(value)
|
||||
|
||||
|
||||
def normalize_aspect_ratio(aspect_ratio):
|
||||
# Allow old broken saved values too
|
||||
if aspect_ratio == "16:9_Widescreen":
|
||||
return "16:9-Widescreen"
|
||||
if aspect_ratio in ASPECT_RATIOS:
|
||||
return aspect_ratio
|
||||
return "16:9-Widescreen"
|
||||
|
||||
|
||||
def nearest_image_preset(long_side):
|
||||
return min(IMAGE_PRESETS, key=lambda k: abs(IMAGE_PRESETS[k] - long_side))
|
||||
|
||||
|
||||
def nearest_video_preset(long_side):
|
||||
return min(VIDEO_PRESETS, key=lambda k: abs(VIDEO_PRESETS[k] - long_side))
|
||||
|
||||
|
||||
def parse_mode_presets(mode, image_preset, video_preset, multiple_profile, resolution_preset=""):
|
||||
if mode not in MODES:
|
||||
mode = "Image"
|
||||
|
||||
# Migrate old workflows carrying resolution_preset only
|
||||
if resolution_preset in LEGACY_PRESETS:
|
||||
legacy_long = LEGACY_PRESETS[resolution_preset]
|
||||
if mode == "Video":
|
||||
video_preset = nearest_video_preset(legacy_long)
|
||||
else:
|
||||
image_preset = nearest_image_preset(legacy_long)
|
||||
|
||||
if image_preset not in IMAGE_PRESETS:
|
||||
image_preset = "1K (1024)"
|
||||
if video_preset not in VIDEO_PRESETS:
|
||||
video_preset = "720p (1280x720)"
|
||||
if multiple_profile not in MULTIPLE_PROFILES:
|
||||
multiple_profile = "Auto (Image=64, Video=32)"
|
||||
|
||||
return mode, image_preset, video_preset, multiple_profile
|
||||
|
||||
|
||||
def get_long_side(mode, image_preset, video_preset):
|
||||
if mode == "Video":
|
||||
return VIDEO_PRESETS[video_preset]
|
||||
return IMAGE_PRESETS[image_preset]
|
||||
@@ -0,0 +1,51 @@
|
||||
import torch
|
||||
from .latent_utils import (
|
||||
MODES,
|
||||
IMAGE_PRESET_KEYS,
|
||||
VIDEO_PRESET_KEYS,
|
||||
MULTIPLE_PROFILE_KEYS,
|
||||
ASPECT_RATIOS,
|
||||
snap_to_multiple,
|
||||
resolve_multiple,
|
||||
normalize_aspect_ratio,
|
||||
parse_mode_presets,
|
||||
get_long_side,
|
||||
)
|
||||
|
||||
class SmartLatentGenerator:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"mode": (MODES, {"default": "Image"}),
|
||||
"image_preset": (IMAGE_PRESET_KEYS, {"default": "1K (1024)"}),
|
||||
"video_preset": (VIDEO_PRESET_KEYS, {"default": "720p (1280x720)"}),
|
||||
"multiple_profile": (MULTIPLE_PROFILE_KEYS, {"default": "Auto (Image=64, Video=32)"}),
|
||||
"aspect_ratio": (list(ASPECT_RATIOS.keys()), {"default": "16:9-Widescreen"}),
|
||||
"batch_size": ("INT", {"default": 1, "min": 1, "max": 16}),
|
||||
},
|
||||
"optional": {
|
||||
"resolution_preset": ("STRING", {"default": "HD-1280px"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("LATENT",)
|
||||
RETURN_NAMES = ("LATENT",)
|
||||
FUNCTION = "generate"
|
||||
CATEGORY = "latent/generator"
|
||||
|
||||
def generate(self, mode, image_preset, video_preset, multiple_profile, aspect_ratio, batch_size, resolution_preset=""):
|
||||
mode, image_preset, video_preset, multiple_profile = parse_mode_presets(
|
||||
mode, image_preset, video_preset, multiple_profile, resolution_preset
|
||||
)
|
||||
aspect_ratio = normalize_aspect_ratio(aspect_ratio)
|
||||
long_side = get_long_side(mode, image_preset, video_preset)
|
||||
multiple = resolve_multiple(mode, multiple_profile)
|
||||
ar_w, ar_h = ASPECT_RATIOS[aspect_ratio]
|
||||
|
||||
scale = long_side / float(max(ar_w, ar_h))
|
||||
width = snap_to_multiple(ar_w * scale, multiple)
|
||||
height = snap_to_multiple(ar_h * scale, multiple)
|
||||
|
||||
latent = torch.zeros([batch_size, 4, height // 8, width // 8])
|
||||
return ({"samples": latent},)
|
||||
@@ -0,0 +1,53 @@
|
||||
from .latent_utils import (
|
||||
MODES,
|
||||
IMAGE_PRESET_KEYS,
|
||||
VIDEO_PRESET_KEYS,
|
||||
MULTIPLE_PROFILE_KEYS,
|
||||
ASPECT_RATIOS,
|
||||
snap_to_multiple,
|
||||
resolve_multiple,
|
||||
normalize_aspect_ratio,
|
||||
parse_mode_presets,
|
||||
get_long_side,
|
||||
)
|
||||
|
||||
class SmartResolutionPicker:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"mode": (MODES, {"default": "Image"}),
|
||||
"image_preset": (IMAGE_PRESET_KEYS, {"default": "1K (1024)"}),
|
||||
"video_preset": (VIDEO_PRESET_KEYS, {"default": "720p (1280x720)"}),
|
||||
"multiple_profile": (MULTIPLE_PROFILE_KEYS, {"default": "Auto (Image=64, Video=32)"}),
|
||||
"aspect_ratio": (list(ASPECT_RATIOS.keys()), {"default": "16:9-Widescreen"}),
|
||||
},
|
||||
"optional": {
|
||||
# Backward compatibility with older saved workflows
|
||||
"resolution_preset": ("STRING", {"default": "HD-1280px"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT", "STRING")
|
||||
RETURN_NAMES = ("width", "height", "resolution_info")
|
||||
FUNCTION = "calculate"
|
||||
CATEGORY = "utils/latent"
|
||||
|
||||
def calculate(self, mode, image_preset, video_preset, multiple_profile, aspect_ratio, resolution_preset=""):
|
||||
mode, image_preset, video_preset, multiple_profile = parse_mode_presets(
|
||||
mode, image_preset, video_preset, multiple_profile, resolution_preset
|
||||
)
|
||||
aspect_ratio = normalize_aspect_ratio(aspect_ratio)
|
||||
long_side = get_long_side(mode, image_preset, video_preset)
|
||||
multiple = resolve_multiple(mode, multiple_profile)
|
||||
ar_w, ar_h = ASPECT_RATIOS[aspect_ratio]
|
||||
|
||||
scale = long_side / float(max(ar_w, ar_h))
|
||||
width = snap_to_multiple(ar_w * scale, multiple)
|
||||
height = snap_to_multiple(ar_h * scale, multiple)
|
||||
|
||||
info = f"{width}x{height} | {mode} | {aspect_ratio} | mul={multiple}"
|
||||
return {
|
||||
"ui": {"text": [info]},
|
||||
"result": (width, height, info),
|
||||
}
|
||||
Reference in New Issue
Block a user