initial version
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from .predefined_resolution_node import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user