Files
TuonoMindCode-ComfyUI-Resol…/tuono_resolution_suggest_downscale.py

339 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# tuono_resolution_suggest_downscale.py
#
# Resolution Suggest & Downscale
#
# - Chooses multiple_of from a model profile (8 / 16 / 32 / 64).
# - Either:
# * Downscales image by a percentage preset, OR
# * Uses "Resolution suggestion 1/2/3/4" based on:
# - static table (1–3)
# - dynamic very low VRAM (4)
# - Preserves aspect ratio.
# - Snaps width/height to the chosen multiple_of.
# - Info output shows:
# * multiple_of: N (Profile name)
# * scale_preset + reduction/scale
# * input image: WxH
# * result: raw WxH -> snapped WxH
# * suggested/common resolutions + less common lower resolution
#
# Typical flow:
# Load Image ─────────────┐
# │
# (image) ▼
# Resolution Suggest & Downscale ─► width/height ─► Resize Image (V2)
# │
# └─► info (STRING)
class TuonoResolutionSuggestDownscale:
"""
Resolution Suggest & Downscale
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
"model_profile": (
[
"multiple_of: 8 (SD / General (8))",
"multiple_of: 16 (WAN 2.2 / Strict (16))",
"multiple_of: 32 (Video / Advanced (32))",
"multiple_of: 64 (Legacy / Extra Safe (64))",
],
{
"default": "multiple_of: 16 (WAN 2.2 / Strict (16))",
},
),
"scale_preset": (
[
"0% smaller (keep original) (1920x1080→1920x1080, 1280x720→1280x720)",
"10% smaller (1920x1080→1728x972, 1280x720→1152x648)",
"20% smaller (1920x1080→1536x864, 1280x720→1024x576)",
"30% smaller (1920x1080→1344x756, 1280x720→896x504)",
"40% smaller (1920x1080→1152x648, 1280x720→768x432)",
"50% smaller (1920x1080→960x540, 1280x720→640x360)",
"60% smaller (1920x1080→768x432, 1280x720→512x288)",
"Resolution suggestion 1 (highest from table)",
"Resolution suggestion 2 (lower from table)",
"Resolution suggestion 3 (lowest from table)",
"Resolution suggestion 4 (very low VRAM)",
],
{
"default": "30% smaller (1920x1080→1344x756, 1280x720→896x504)",
},
),
},
}
RETURN_TYPES = ("IMAGE", "INT", "INT", "STRING")
RETURN_NAMES = ("image", "width", "height", "info")
FUNCTION = "calc"
CATEGORY = "Resolution/Utilities"
# --- Helpers: core ---
def _snap_dim(self, value, multiple):
if value <= 0:
return multiple
v = int(value)
snapped = (v // multiple) * multiple
if snapped < multiple:
snapped = multiple
return snapped
def _profile_to_multiple(self, model_profile: str):
"""
Map dropdown string to (multiple_of, profile_label)
profile_label används bara i info-texten.
"""
if model_profile.startswith("multiple_of: 8"):
return 8, "SD / General (8)"
if model_profile.startswith("multiple_of: 16"):
return 16, "WAN 2.2 / Strict (16)"
if model_profile.startswith("multiple_of: 32"):
return 32, "Video / Advanced (32)"
if model_profile.startswith("multiple_of: 64"):
return 64, "Legacy / Extra Safe (64)"
# fallback
return 8, "SD / General (8)"
# --- Helpers: percentage presets ---
def _scale_from_percentage_preset(self, scale_preset: str):
"""
Convert percentage scale presets to (reduction_percent, scale).
"""
if scale_preset.startswith("0%"):
reduction = 0.0
elif scale_preset.startswith("10%"):
reduction = 10.0
elif scale_preset.startswith("20%"):
reduction = 20.0
elif scale_preset.startswith("30%"):
reduction = 30.0
elif scale_preset.startswith("40%"):
reduction = 40.0
elif scale_preset.startswith("50%"):
reduction = 50.0
elif scale_preset.startswith("60%"):
reduction = 60.0
else:
reduction = 0.0
scale = 1.0 - (reduction / 100.0)
if scale < 0.1:
scale = 0.1
return reduction, scale
def _scale_and_snap(self, w, h, scale, multiple_of):
raw_w = w * scale
raw_h = h * scale
snapped_w = self._snap_dim(round(raw_w), multiple_of)
snapped_h = self._snap_dim(round(raw_h), multiple_of)
if snapped_w > w:
snapped_w = self._snap_dim(w, multiple_of)
if snapped_h > h:
snapped_h = self._snap_dim(h, multiple_of)
return int(snapped_w), int(snapped_h), int(round(raw_w)), int(round(raw_h))
# --- Helpers: common resolutions table ---
def _get_common_resolution_table(self):
return {
# 4K and high resolutions
(3840, 2160): "2560x1440, 1920x1080, 1280x720, 960x540, 854x480",
(3440, 1440): "2560x1080, 2560x1440, 1920x810, 1600x720, 1280x540",
(2560, 1440): "1920x1080, 1600x900, 1280x720, 1024x576, 960x540",
(2560, 1080): "1920x810, 1920x800, 1600x900, 1280x720, 1024x576",
(2048, 1152): "1920x1080, 1600x900, 1280x720, 1024x576, 960x540",
# SDXL / Civitai-style portrait & landscape
(1024, 1024): "896x896, 768x768, 640x640",
(1152, 896): "1024x800, 960x768, 896x736",
(896, 1152): "768x992, 672x864, 576x736",
(1216, 832): "1152x768, 1024x704, 896x640",
(832, 1216): "768x1120, 704x1024, 640x928",
(1344, 768): "1216x704, 1152x672, 1024x576",
(768, 1344): "704x1216, 672x1152, 576x1024",
(1536, 640): "1344x576, 1216x512, 1024x448",
(640, 1536): "576x1344, 512x1216, 448x1024",
(640, 960): "576x864, 512x768, 480x720",
(768, 1152): "704x1056, 640x960, 576x864",
(800, 1200): "704x1056, 640x960, 576x864",
(832, 1216): "768x1120, 704x1024, 640x928",
# Classic 16:9 landscape
(1920, 1080): "1600x900, 1536x864, 1280x720, 1024x576, 960x540",
(1720, 720): "1600x720, 1280x720, 1024x576, 960x540, 854x480",
(1680, 1050): "1600x900, 1440x900, 1280x800, 1280x720, 1024x640",
(1600, 900): "1440x810, 1280x720, 1024x576",
(1536, 864): "1440x810, 1280x720, 1024x576",
(1366, 768): "1280x720, 1152x648, 1024x576",
(1360, 768): "1280x720, 1152x648, 1024x576",
(1280, 720): "1152x648, 1024x576, 960x540",
(1270, 720): "1152x648, 1024x576, 960x540",
(1024, 576): "960x540, 854x480, 768x432",
(960, 540): "854x480, 848x480, 800x450",
(854, 480): "800x450, 768x432, 640x360",
(848, 480): "800x450, 768x432, 640x360",
(800, 450): "768x432, 720x405, 640x360",
(720, 405): "640x360, 576x324, 512x288",
(640, 360): "576x324, 512x288, 426x240",
(426, 240): "400x225, 384x216, 320x180",
# Square / near-square
(2048, 2048): "1536x1536, 1024x1024, 768x768",
(1536, 1536): "1280x1280, 1024x1024, 768x768",
(1280, 1280): "1024x1024, 896x896, 768x768",
(1024, 1024): "896x896, 768x768, 640x640",
(896, 896): "768x768, 704x704, 640x640",
(768, 768): "640x640, 576x576, 512x512",
(640, 640): "576x576, 512x512, 480x480",
(512, 512): "448x448, 384x384, 320x320",
# Portrait
(1080, 1920): "900x1600, 720x1280, 540x960",
(720, 1280): "640x1138, 540x960, 480x853",
(640, 1136): "576x1024, 540x960, 480x853",
# 1152x896 variant
(1152, 896): "1024x800, 960x768, 896x672",
}
def _parse_res_list(self, text):
results = []
parts = text.split(",")
for p in parts:
p = p.strip()
if "x" not in p:
continue
w_str, h_str = p.split("x", 1)
try:
w = int(w_str.strip())
h = int(h_str.strip())
results.append((w, h))
except Exception:
continue
return results
def _static_resolution_suggestions(self, current_w, current_h, multiple_of):
table = self._get_common_resolution_table()
key = (current_w, current_h)
if key in table:
base = f"suggested/common resolutions: {table[key]}"
else:
base = "suggested/common resolutions: (no static entries for this input size)"
low_w, low_h, _, _ = self._scale_and_snap(current_w, current_h, 0.5, multiple_of)
low = f"less common lower resolution (same aspect ratio): {low_w}x{low_h}"
return f"{base} | {low}"
# --- Helpers: resolution suggestion presets ---
def _get_suggestion_target(self, w, h, multiple_of, scale_preset: str):
preset_lower = scale_preset.lower()
# Suggestion 4: always dynamic low VRAM (~50%)
if "suggestion 4" in preset_lower or "very low vram" in preset_lower:
new_w, new_h, raw_w, raw_h = self._scale_and_snap(w, h, 0.5, multiple_of)
return new_w, new_h, raw_w, raw_h
# For suggestion 1/2/3 we try static table first
table = self._get_common_resolution_table()
key = (w, h)
suggestions = []
if key in table:
suggestions = self._parse_res_list(table[key])
if "suggestion 1" in preset_lower:
idx = 0
elif "suggestion 2" in preset_lower:
idx = 1
elif "suggestion 3" in preset_lower:
idx = 2
else:
idx = None
if suggestions and idx is not None:
if 0 <= idx < len(suggestions):
target_w, target_h = suggestions[idx]
else:
target_w, target_h = suggestions[-1]
snapped_w = self._snap_dim(target_w, multiple_of)
snapped_h = self._snap_dim(target_h, multiple_of)
if snapped_w > w:
snapped_w = self._snap_dim(w, multiple_of)
if snapped_h > h:
snapped_h = self._snap_dim(h, multiple_of)
raw_w = target_w
raw_h = target_h
return snapped_w, snapped_h, raw_w, raw_h
# No static suggestions: dynamic fallbacks
if "suggestion 1" in preset_lower:
scale = 0.8
elif "suggestion 2" in preset_lower:
scale = 0.7
elif "suggestion 3" in preset_lower:
scale = 0.6
else:
scale = 0.5
new_w, new_h, raw_w, raw_h = self._scale_and_snap(w, h, scale, multiple_of)
return new_w, new_h, raw_w, raw_h
# --- main ---
def calc(self, image, model_profile, scale_preset):
h = image.shape[1]
w = image.shape[2]
if h <= 0 or w <= 0:
info = "Invalid image size – cannot calculate."
return (image, int(w), int(h), info)
multiple_of, profile_label = self._profile_to_multiple(model_profile)
is_suggestion = scale_preset.startswith("Resolution suggestion")
if is_suggestion:
new_w, new_h, raw_w, raw_h = self._get_suggestion_target(
w, h, multiple_of, scale_preset
)
if w > 0:
scale = new_w / float(w)
else:
scale = 1.0
reduction_percent = (1.0 - scale) * 100.0
else:
reduction_percent, scale = self._scale_from_percentage_preset(scale_preset)
new_w, new_h, raw_w, raw_h = self._scale_and_snap(w, h, scale, multiple_of)
simple_preset = scale_preset.split(" (")[0]
suggestions_info = self._static_resolution_suggestions(w, h, multiple_of)
lines = []
lines.append(f"multiple_of: {multiple_of} ({profile_label})")
lines.append(
f"scale_preset={simple_preset}, reduction≈{reduction_percent:.1f}% (scale≈{scale:.4f})"
)
lines.append(f"input image: {w}x{h}")
lines.append(f"result: raw {raw_w}x{raw_h} -> snapped {new_w}x{new_h}")
lines.append(suggestions_info)
info = " | ".join(lines)
return (image, int(new_w), int(new_h), info)
NODE_CLASS_MAPPINGS = {
"TuonoResolutionSuggestDownscale": TuonoResolutionSuggestDownscale,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"TuonoResolutionSuggestDownscale": "Resolution Suggest & Downscale",
}