initial version

This commit is contained in:
dongxiaoman
2026-03-09 20:58:37 -07:00
parent a73df600d4
commit 4bc0311b6d
3 changed files with 254 additions and 1 deletions
+67 -1
View File
@@ -1,2 +1,68 @@
# ComfyUI-PredefinedResolution
The ComfyUI node for selecting closes resolution that matches the user input
A ComfyUI custom node that automatically snaps input dimensions to predefined resolutions and aspect ratios.
## Features
- **Automatic Ratio Detection**: Analyzes input width/height and snaps to the closest standard aspect ratio (21:9, 16:9, 1:1, 9:16, 9:21)
- **Predefined Output Resolutions**: Choose from 720p, 1080p, 2K, or 4K output resolutions
- **Custom Resolution Support**: Define your own resolution presets for each aspect ratio
- **Ratio Outputs**: Returns both width/height and height/width ratios for downstream nodes
## Installation
1. Clone this repository into your ComfyUI custom_nodes folder:
```bash
cd ComfyUI/custom_nodes
git clone https://github.com/pipelet-ai/ComfyUI-PredefinedResolution.git
```
2. Restart ComfyUI
## Usage
### Inputs
**Required:**
- `width` (INT): Input image width (64-8192)
- `height` (INT): Input image height (64-8192)
- `output_resolution`: Select from "720p", "1080p", "2K", "4K", or "Custom"
- `target_ratio`: Target aspect ratio (21:9, 16:9, 1:1, 9:16, 9:21)
**Optional:**
- `width_height_scale` (FLOAT): Override the calculated ratio with a manual value (0.0 = use calculated ratio)
- `custom_21_9` (STRING): Custom resolution for 21:9 ratio (format: "width,height", e.g., "2560,1080")
- `custom_16_9` (STRING): Custom resolution for 16:9 ratio (default: "1920,1080")
- `custom_1_1` (STRING): Custom resolution for 1:1 ratio (default: "1080,1080")
- `custom_9_16` (STRING): Custom resolution for 9:16 ratio (default: "608,1080")
- `custom_9_21` (STRING): Custom resolution for 9:21 ratio (default: "464,1080")
### Outputs
- `width` (INT): Output width
- `height` (INT): Output height
- `width_height_ratio` (FLOAT): Width divided by height
- `height_width_ratio` (FLOAT): Height divided by width
### Example
**Input:** 720 x 800 (from an uploaded image)
- Detected ratio: Closest to 1:1
- Output resolution: 1080p
- **Output:** 1080 x 1080, ratio: 1.0, 1.0
**Input:** 1920 x 800
- Detected ratio: Closest to 21:9
- Output resolution: 2K
- **Output:** 4608 x 2048, ratio: 2.25, 0.44
## How It Works
1. The node calculates the aspect ratio from input width/height (or uses the optional `width_height_scale` if provided)
2. It finds the closest matching standard aspect ratio (21:9, 16:9, 1:1, 9:16, 9:21)
3. Based on the selected output resolution preset, it calculates the appropriate dimensions
4. Returns the final width, height, and both ratio values
## License
See LICENSE file for details.
+3
View File
@@ -0,0 +1,3 @@
from .predefined_resolution_node import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
+184
View File
@@ -0,0 +1,184 @@
import math
class PredefinedResolutionNode:
RATIO_PRESETS = {
"21:9": 21/9,
"16:9": 16/9,
"1:1": 1.0,
"9:16": 9/16,
"9:21": 9/21
}
RESOLUTION_PRESETS = {
"720p": 720,
"1080p": 1080,
"2K": 2048,
"4K": 3840
}
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"width": ("INT", {
"default": 512,
"min": 64,
"max": 8192,
"step": 1
}),
"height": ("INT", {
"default": 512,
"min": 64,
"max": 8192,
"step": 1
}),
"output_resolution": (["720p", "1080p", "2K", "4K", "Custom"], {
"default": "1080p"
}),
},
"optional": {
"enable_21_9": ("BOOLEAN", {
"default": True
}),
"custom_21_9": ("STRING", {
"default": "2560x1080",
"multiline": False
}),
"enable_16_9": ("BOOLEAN", {
"default": True
}),
"custom_16_9": ("STRING", {
"default": "1920x1080",
"multiline": False
}),
"enable_1_1": ("BOOLEAN", {
"default": True
}),
"custom_1_1": ("STRING", {
"default": "1080x1080",
"multiline": False
}),
"enable_9_16": ("BOOLEAN", {
"default": True
}),
"custom_9_16": ("STRING", {
"default": "1080x1920",
"multiline": False
}),
"enable_9_21": ("BOOLEAN", {
"default": True
}),
"custom_9_21": ("STRING", {
"default": "1080x2560",
"multiline": False
}),
}
}
RETURN_TYPES = ("INT", "INT", "FLOAT", "FLOAT")
RETURN_NAMES = ("width", "height", "width_height_ratio", "height_width_ratio")
FUNCTION = "calculate_resolution"
CATEGORY = "utils"
def parse_custom_resolution(self, custom_string):
try:
parts = custom_string.strip().lower().split('x')
if len(parts) == 2:
width = int(parts[0].strip())
height = int(parts[1].strip())
return width, height
except:
pass
return None, None
def get_resolution_for_ratio(self, ratio_name, output_resolution,
custom_21_9, custom_16_9, custom_1_1,
custom_9_16, custom_9_21):
if output_resolution == "Custom":
custom_map = {
"21:9": custom_21_9,
"16:9": custom_16_9,
"1:1": custom_1_1,
"9:16": custom_9_16,
"9:21": custom_9_21
}
width, height = self.parse_custom_resolution(custom_map[ratio_name])
if width is not None and height is not None:
return width, height
target_ratio = self.RATIO_PRESETS[ratio_name]
base_resolution = self.RESOLUTION_PRESETS.get(output_resolution, 1080)
if ratio_name == "1:1":
width = height = base_resolution
elif target_ratio > 1.0:
min_dimension = base_resolution
height = min_dimension
width = int(round(height * target_ratio))
else:
min_dimension = base_resolution
width = min_dimension
height = int(round(width / target_ratio))
return width, height
def find_closest_ratio(self, input_ratio, enabled_ratios):
min_diff = float('inf')
closest_ratio = None
for ratio_name, ratio_value in self.RATIO_PRESETS.items():
if ratio_name not in enabled_ratios:
continue
diff = abs(input_ratio - ratio_value)
if diff < min_diff:
min_diff = diff
closest_ratio = ratio_name
if closest_ratio is None:
closest_ratio = "1:1"
return closest_ratio
def calculate_resolution(self, width, height, output_resolution,
enable_21_9=True, custom_21_9="2560x1080",
enable_16_9=True, custom_16_9="1920x1080",
enable_1_1=True, custom_1_1="1080x1080",
enable_9_16=True, custom_9_16="1080x1920",
enable_9_21=True, custom_9_21="1080x2560"):
input_ratio = width / height if height > 0 else 1.0
enabled_ratios = set()
if enable_21_9:
enabled_ratios.add("21:9")
if enable_16_9:
enabled_ratios.add("16:9")
if enable_1_1:
enabled_ratios.add("1:1")
if enable_9_16:
enabled_ratios.add("9:16")
if enable_9_21:
enabled_ratios.add("9:21")
if not enabled_ratios:
enabled_ratios.add("1:1")
snapped_ratio = self.find_closest_ratio(input_ratio, enabled_ratios)
output_width, output_height = self.get_resolution_for_ratio(
snapped_ratio, output_resolution,
custom_21_9, custom_16_9, custom_1_1, custom_9_16, custom_9_21
)
width_height_ratio = output_width / output_height if output_height > 0 else 1.0
height_width_ratio = output_height / output_width if output_width > 0 else 1.0
return (output_width, output_height, width_height_ratio, height_width_ratio)
NODE_CLASS_MAPPINGS = {
"PredefinedResolution": PredefinedResolutionNode
}
NODE_DISPLAY_NAME_MAPPINGS = {
"PredefinedResolution": "Predefined Resolution"
}