Skip to content

Commit 1f1ec37

Browse files
feat: add ResolutionSelector node for aspect ratio and megapixel-based resolution calculation (Comfy-Org#12199)
Amp-Thread-ID: https://ampcode.com/threads/T-019c179e-cd8c-768f-ae66-207c7a53c01d Co-authored-by: Jedrzej Kosinski <kosinkadink1@gmail.com>
1 parent 0a7f8e1 commit 1f1ec37

2 files changed

Lines changed: 83 additions & 0 deletions

File tree

comfy_extras/nodes_resolution.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from __future__ import annotations
2+
import math
3+
from enum import Enum
4+
from typing_extensions import override
5+
from comfy_api.latest import ComfyExtension, io
6+
7+
8+
class AspectRatio(str, Enum):
9+
SQUARE = "1:1 (Square)"
10+
PHOTO_H = "3:2 (Photo)"
11+
STANDARD_H = "4:3 (Standard)"
12+
WIDESCREEN_H = "16:9 (Widescreen)"
13+
ULTRAWIDE_H = "21:9 (Ultrawide)"
14+
PHOTO_V = "2:3 (Portrait Photo)"
15+
STANDARD_V = "3:4 (Portrait Standard)"
16+
WIDESCREEN_V = "9:16 (Portrait Widescreen)"
17+
18+
19+
ASPECT_RATIOS: dict[str, tuple[int, int]] = {
20+
"1:1 (Square)": (1, 1),
21+
"3:2 (Photo)": (3, 2),
22+
"4:3 (Standard)": (4, 3),
23+
"16:9 (Widescreen)": (16, 9),
24+
"21:9 (Ultrawide)": (21, 9),
25+
"2:3 (Portrait Photo)": (2, 3),
26+
"3:4 (Portrait Standard)": (3, 4),
27+
"9:16 (Portrait Widescreen)": (9, 16),
28+
}
29+
30+
31+
class ResolutionSelector(io.ComfyNode):
32+
"""Calculate width and height from aspect ratio and megapixel target."""
33+
34+
@classmethod
35+
def define_schema(cls):
36+
return io.Schema(
37+
node_id="ResolutionSelector",
38+
display_name="Resolution Selector",
39+
category="utils",
40+
description="Calculate width and height from aspect ratio and megapixel target. Useful for setting up Empty Latent Image dimensions.",
41+
inputs=[
42+
io.Combo.Input(
43+
"aspect_ratio",
44+
options=AspectRatio,
45+
default=AspectRatio.SQUARE,
46+
tooltip="The aspect ratio for the output dimensions.",
47+
),
48+
io.Float.Input(
49+
"megapixels",
50+
default=1.0,
51+
min=0.1,
52+
max=16.0,
53+
step=0.1,
54+
tooltip="Target total megapixels. 1.0 MP ≈ 1024×1024 for square.",
55+
),
56+
],
57+
outputs=[
58+
io.Int.Output("width", tooltip="Calculated width in pixels (multiple of 8)."),
59+
io.Int.Output("height", tooltip="Calculated height in pixels (multiple of 8)."),
60+
],
61+
)
62+
63+
@classmethod
64+
def execute(cls, aspect_ratio: str, megapixels: float) -> io.NodeOutput:
65+
w_ratio, h_ratio = ASPECT_RATIOS[aspect_ratio]
66+
total_pixels = megapixels * 1024 * 1024
67+
scale = math.sqrt(total_pixels / (w_ratio * h_ratio))
68+
width = round(w_ratio * scale / 8) * 8
69+
height = round(h_ratio * scale / 8) * 8
70+
return io.NodeOutput(width, height)
71+
72+
73+
class ResolutionExtension(ComfyExtension):
74+
@override
75+
async def get_node_list(self) -> list[type[io.ComfyNode]]:
76+
return [
77+
ResolutionSelector,
78+
]
79+
80+
81+
async def comfy_entrypoint() -> ResolutionExtension:
82+
return ResolutionExtension()

nodes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2435,6 +2435,7 @@ async def init_builtin_extra_nodes():
24352435
"nodes_audio_encoder.py",
24362436
"nodes_rope.py",
24372437
"nodes_logic.py",
2438+
"nodes_resolution.py",
24382439
"nodes_nop.py",
24392440
"nodes_kandinsky5.py",
24402441
"nodes_wanmove.py",

0 commit comments

Comments
 (0)