Minor improvements

This commit is contained in:
BRADSEC
2025-12-07 20:46:52 +10:00
parent b764f915c8
commit 480f15bb15
7 changed files with 89 additions and 72 deletions
+1
View File
@@ -122,3 +122,4 @@ Desktop.ini
# Claude Code files
.claude/
CLAUDE.md
+12 -10
View File
@@ -1,4 +1,4 @@
# Resolution Selector for ComfyUI
# Resolution Selector Plus for ComfyUI
Custom node for [ComfyUI](https://github.com/comfyanonymous/ComfyUI) providing easy resolution and empty latent selection for image generation workflows.
@@ -21,52 +21,54 @@ cd pathTo/ComfyUI
cd custom_nodes
# Clone the repo into custom_nodes
git clone https://github.com/bradsec/ComfyUI_ResolutionSelector.git
git clone https://github.com/bradsec/ComfyUI_ResolutionSelectorPlus.git
# Restart ComfyUI
```
## Usage
### Resolution Selector
### Resolution Selector Plus
**Add Node:** `Add Node > utils > Resolution Selector`
**Add Node:** `Add Node > utils > Resolution Selector Plus`
**Inputs:**
- `model` (dropdown) - Select your image generation model or "All" for all resolutions
- `resolution` (dropdown) - Choose from model-optimized presets with aspect ratios (e.g., "1920x1080 (16:9 Landscape)")
- `resolution_multiplier` (dropdown) - Multiply preset resolution (1x, 2x, 3x, 4x)
- `batch_size` (number) - Number of latent samples to generate (1-64, default: 1)
- `custom_width` (optional) - Override with custom width (0-4096, step 8)
- `custom_height` (optional) - Override with custom height (0-4096, step 8)
- `custom_multiplier` (optional) - Independent multiplier for custom dimensions (1x, 2x, 3x, 4x)
- `custom_batch` (optional) - Number of latent samples for custom resolution (1-64, default: 1)
**Outputs:**
- `width` (INT) - Preset resolution width in pixels
- `height` (INT) - Preset resolution height in pixels
- `latent` (LATENT) - Empty latent tensor for preset resolution
- `latent` (LATENT) - Empty latent tensor for preset resolution (batch_size samples)
- `custom_width` (INT) - Custom width (0 if not set)
- `custom_height` (INT) - Custom height (0 if not set)
- `custom_latent` (LATENT) - Empty latent tensor for custom resolution
- `custom_latent` (LATENT) - Empty latent tensor for custom resolution (custom_batch samples)
**Example Workflows:**
**Basic preset with multiplier:**
```
Resolution Selector (model: SDXL, resolution: "1024x1024 (1:1 Square)", multiplier: 2x)
Resolution Selector Plus (model: SDXL, resolution: "1024x1024 (1:1 Square)", multiplier: 2x)
→ width: 2048, height: 2048
→ latent → KSampler
```
**Custom dimensions with independent multiplier:**
```
Resolution Selector (custom_width: 1024, custom_height: 768, custom_multiplier: 2x)
Resolution Selector Plus (custom_width: 1024, custom_height: 768, custom_multiplier: 2x)
→ custom_width: 2048, custom_height: 1536
→ custom_latent → KSampler
```
**Using "All" model option:**
```
Resolution Selector (model: All, resolution: "1920x1080 (16:9 Landscape)")
→ Shows all 34 unique resolutions from all models
Resolution Selector Plus (model: All, resolution: "1920x1080 (16:9 Landscape)")
→ Shows all 74 unique resolutions from all models
```
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 84 KiB

+2 -2
View File
@@ -1,11 +1,11 @@
[project]
name = "comfyui_resolutionselectorplus"
description = "Custom node for ComfyUI providing easy resolution and empty latent selection for image generation workflows."
version = "2.0.4"
version = "2.0.5"
license = {file = "LICENSE"}
[project.urls]
Repository = "https://github.com/bradsec/ComfyUI_ResolutionSelector"
Repository = "https://github.com/bradsec/ComfyUI_ResolutionSelectorPlus"
# Used by Comfy Registry https://comfyregistry.org
[tool.comfy]
+44 -34
View File
@@ -8,35 +8,36 @@ except ImportError:
# Model-specific resolution presets with constraints
# Includes: model-optimized sizes + photo print (4x6, 5x7, 8x10) + digital/social + canvas art ratios
MODEL_RESOLUTIONS = {
"Flux": {
"square": [(512, 512), (768, 768), (1024, 1024), (1280, 1280), (1536, 1536), (1920, 1920), (2048, 2048)],
"portrait": [(768, 1344), (832, 1216), (896, 1152), (928, 1664), (1024, 1536), (1024, 1792), (1080, 1920), (1152, 2048)],
"landscape": [(1344, 768), (1216, 832), (1152, 896), (1536, 1024), (1664, 928), (1792, 1024), (1920, 1080), (2048, 1152)],
"square": [(512, 512), (768, 768), (1024, 1024), (1088, 1088), (1280, 1280), (1536, 1536), (1920, 1920), (2048, 2048)],
"portrait": [(688, 2048), (768, 1344), (832, 1216), (896, 1152), (928, 1664), (1024, 1536), (1024, 1792), (1024, 2048), (1088, 1920), (1152, 2048), (1200, 1792), (1360, 2048), (1456, 2048), (1536, 2048), (1616, 2048), (1632, 2048), (1712, 2048)],
"landscape": [(1280, 720), (1344, 768), (1216, 832), (1152, 896), (1536, 1024), (1664, 928), (1792, 1024), (1792, 1200), (1920, 1088), (2048, 688), (2048, 1024), (2048, 1152), (2048, 1360), (2048, 1456), (2048, 1536), (2048, 1616), (2048, 1632), (2048, 1712)],
"constraints": {"divisible_by": 16, "min": 256, "max": 2048}
},
"Qwen Image": {
"square": [(1024, 1024), (1280, 1280), (1328, 1328), (1536, 1536), (1920, 1920), (2048, 2048)],
"portrait": [(928, 1664), (1024, 1536), (1080, 1920), (1140, 1472), (1152, 2048)],
"landscape": [(1472, 1140), (1536, 1024), (1664, 928), (1920, 1080), (2048, 1152)],
"square": [(1024, 1024), (1080, 1080), (1280, 1280), (1328, 1328), (1536, 1536), (1920, 1920), (2048, 2048)],
"portrait": [(680, 2048), (928, 1664), (1024, 1536), (1024, 2048), (1080, 1920), (1140, 1472), (1152, 2048), (1200, 1800), (1368, 2048), (1464, 2048), (1536, 2048), (1608, 2048), (1640, 2048), (1704, 2048)],
"landscape": [(1280, 720), (1472, 1140), (1536, 1024), (1664, 928), (1800, 1200), (1920, 1080), (2048, 680), (2048, 1024), (2048, 1152), (2048, 1368), (2048, 1464), (2048, 1536), (2048, 1608), (2048, 1640), (2048, 1704)],
"constraints": {"divisible_by": 8, "min": 256, "max": 2048}
},
"Z-Image": {
"square": [(512, 512), (768, 768), (1024, 1024), (1280, 1280), (1536, 1536), (1920, 1920), (2048, 2048)],
"portrait": [(720, 1280), (768, 1024), (1080, 1920), (1152, 2048)],
"landscape": [(1024, 768), (1280, 720), (1920, 1080), (2048, 1152)],
"square": [(512, 512), (768, 768), (1024, 1024), (1080, 1080), (1280, 1280), (1536, 1536), (1920, 1920), (2048, 2048)],
"portrait": [(680, 2048), (720, 1280), (768, 1024), (1024, 2048), (1080, 1920), (1152, 2048), (1200, 1800), (1368, 2048), (1464, 2048), (1536, 2048), (1608, 2048), (1640, 2048), (1704, 2048)],
"landscape": [(1024, 768), (1280, 720), (1800, 1200), (1920, 1080), (2048, 680), (2048, 1024), (2048, 1152), (2048, 1368), (2048, 1464), (2048, 1536), (2048, 1608), (2048, 1640), (2048, 1704)],
"constraints": {"divisible_by": 8, "min": 256, "max": 2048}
},
"SD 1.5": {
"square": [(512, 512), (768, 768), (1024, 1024), (1280, 1280), (1536, 1536)],
"portrait": [(512, 768), (512, 682), (512, 1024), (768, 1024), (768, 1344), (1080, 1920)],
"landscape": [(768, 512), (1024, 512), (1024, 768), (1344, 768), (1536, 512), (1920, 1080)],
"square": [(512, 512), (768, 768), (1024, 1024), (1080, 1080), (1280, 1280), (1536, 1536)],
"portrait": [(512, 768), (512, 682), (512, 1024), (680, 2048), (768, 1024), (768, 1344), (1024, 2048), (1080, 1920), (1200, 1800), (1368, 2048), (1464, 2048), (1536, 2048), (1608, 2048), (1640, 2048), (1704, 2048)],
"landscape": [(768, 512), (1024, 512), (1024, 768), (1280, 720), (1344, 768), (1536, 512), (1800, 1200), (1920, 1080), (2048, 680), (2048, 1024), (2048, 1368), (2048, 1464), (2048, 1536), (2048, 1608), (2048, 1640), (2048, 1704)],
"constraints": {"divisible_by": 8, "min": 256, "max": 2048}
},
"SDXL": {
"square": [(1024, 1024), (1280, 1280), (1536, 1536), (1920, 1920), (2048, 2048)],
"portrait": [(640, 1536), (768, 1344), (832, 1216), (896, 1152), (1024, 1536), (1080, 1920), (1152, 2048)],
"landscape": [(1152, 896), (1216, 832), (1344, 768), (1536, 640), (1536, 1024), (1920, 1080), (2048, 1152)],
"square": [(1024, 1024), (1080, 1080), (1280, 1280), (1536, 1536), (1920, 1920), (2048, 2048)],
"portrait": [(640, 1536), (680, 2048), (768, 1344), (832, 1216), (896, 1152), (1024, 1536), (1024, 2048), (1080, 1920), (1152, 2048), (1200, 1800), (1368, 2048), (1464, 2048), (1536, 2048), (1608, 2048), (1640, 2048), (1704, 2048)],
"landscape": [(1152, 896), (1216, 832), (1280, 720), (1344, 768), (1536, 640), (1536, 1024), (1800, 1200), (1920, 1080), (2048, 680), (2048, 1024), (2048, 1152), (2048, 1368), (2048, 1464), (2048, 1536), (2048, 1608), (2048, 1640), (2048, 1704)],
"constraints": {"divisible_by": 8, "min": 256, "max": 2048}
}
}
@@ -60,7 +61,7 @@ def gcd(a, b):
def calculate_aspect_ratio(width, height):
"""
Calculate simplified aspect ratio from dimensions, rounding to common ratios.
Calculate simplified aspect ratio from dimensions, using nearest common ratio.
Args:
width (int): Width in pixels
@@ -91,19 +92,12 @@ def calculate_aspect_ratio(width, height):
(0.56, "9:16"), # 0.5625
(0.5, "1:2"), # 0.5
(0.42, "5:12"), # 0.4166...
(0.33, "1:3"), # 0.333... (panoramic)
]
# Find closest common ratio (within 5% tolerance)
tolerance = 0.05
for ratio_value, ratio_str in common_ratios:
if abs(actual_ratio - ratio_value) / ratio_value < tolerance:
return ratio_str
# If no close match, calculate GCD-based ratio
divisor = gcd(width, height)
ratio_width = width // divisor
ratio_height = height // divisor
return f"{ratio_width}:{ratio_height}"
# Find closest common ratio by absolute difference
closest_ratio = min(common_ratios, key=lambda r: abs(actual_ratio - r[0]))
return closest_ratio[1]
def format_resolution(width, height):
@@ -283,6 +277,13 @@ class ResolutionSelector:
"resolution_multiplier": (["1x", "2x", "3x", "4x"], {
"default": "1x"
}),
"batch_size": ("INT", {
"default": 1,
"min": 1,
"max": 64,
"step": 1,
"display": "number"
}),
},
"optional": {
"custom_width": ("INT", {
@@ -302,6 +303,13 @@ class ResolutionSelector:
"custom_multiplier": (["1x", "2x", "3x", "4x"], {
"default": "1x"
}),
"custom_batch": ("INT", {
"default": 1,
"min": 1,
"max": 64,
"step": 1,
"display": "number"
}),
}
}
@@ -310,7 +318,7 @@ class ResolutionSelector:
FUNCTION = "select_resolution"
CATEGORY = "utils"
def select_resolution(self, model, resolution, resolution_multiplier="1x", custom_width=0, custom_height=0, custom_multiplier="1x"):
def select_resolution(self, model, resolution, resolution_multiplier="1x", batch_size=1, custom_width=0, custom_height=0, custom_multiplier="1x", custom_batch=1):
"""
Select and validate resolution, generate outputs.
@@ -318,9 +326,11 @@ class ResolutionSelector:
model (str): Selected model name
resolution (str): Selected preset resolution string
resolution_multiplier (str): Multiplier for resolution (1x-4x)
batch_size (int): Number of latent samples for preset resolution (default: 1)
custom_width (int, optional): Custom width override
custom_height (int, optional): Custom height override
custom_multiplier (str, optional): Multiplier for custom dimensions (1x-4x)
custom_batch (int, optional): Number of latent samples for custom resolution (default: 1)
Returns:
tuple: (width: int, height: int, latent: dict, custom_width: int, custom_height: int, custom_latent: dict)
@@ -336,8 +346,8 @@ class ResolutionSelector:
width *= multiplier
height *= multiplier
# Generate latent for preset resolution
latent = self._generate_empty_latent(width, height)
# Generate latent for preset resolution with batch size
latent = self._generate_empty_latent(width, height, batch_size)
# Determine final custom dimensions
if custom_width > 0 and custom_height > 0:
@@ -349,13 +359,13 @@ class ResolutionSelector:
if model != "All":
self._validate_dimensions(model, final_custom_width, final_custom_height)
# Generate custom latent
custom_latent = self._generate_empty_latent(final_custom_width, final_custom_height)
# Generate custom latent with custom batch size
custom_latent = self._generate_empty_latent(final_custom_width, final_custom_height, custom_batch)
return (width, height, latent, final_custom_width, final_custom_height, custom_latent)
else:
# No custom dimensions, return zeros and minimal empty custom latent
custom_latent = self._generate_empty_latent(1, 1)
custom_latent = self._generate_empty_latent(1, 1, 1)
return (width, height, latent, 0, 0, custom_latent)
@@ -433,5 +443,5 @@ NODE_CLASS_MAPPINGS = {
}
NODE_DISPLAY_NAME_MAPPINGS = {
"ResolutionSelector": "Resolution Selector",
"ResolutionSelector": "Resolution Selector Plus",
}
+2 -1
View File
@@ -85,7 +85,8 @@ def test_model_resolutions():
flux_res = get_resolution_list("Flux")
print(f" Flux has {len(flux_res)} resolutions")
assert len(flux_res) > 0, "Flux should have resolutions"
assert any("1920x1080" in r for r in flux_res), "Flux should have 1920x1080"
# Flux uses 16-pixel divisibility, so 1920x1080 becomes 1920x1088
assert any("1920x1088" in r for r in flux_res), "Flux should have 1920x1088 (Full HD adapted to 16px divisibility)"
# Test All model
all_res = get_resolution_list("All")
+28 -25
View File
@@ -1,30 +1,32 @@
import { app } from "/scripts/app.js";
// Model-specific resolution presets with constraints
// Includes: model-optimized sizes + photo print (4x6, 5x7, 8x10) + digital/social + canvas art ratios
const MODEL_RESOLUTIONS = {
"Flux": {
"square": [[512, 512], [768, 768], [1024, 1024], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[768, 1344], [832, 1216], [896, 1152], [928, 1664], [1024, 1536], [1024, 1792], [1080, 1920], [1152, 2048]],
"landscape": [[1344, 768], [1216, 832], [1152, 896], [1536, 1024], [1664, 928], [1792, 1024], [1920, 1080], [2048, 1152]],
"square": [[512, 512], [768, 768], [1024, 1024], [1088, 1088], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[688, 2048], [768, 1344], [832, 1216], [896, 1152], [928, 1664], [1024, 1536], [1024, 1792], [1024, 2048], [1088, 1920], [1152, 2048], [1200, 1792], [1360, 2048], [1456, 2048], [1536, 2048], [1616, 2048], [1632, 2048], [1712, 2048]],
"landscape": [[1280, 720], [1344, 768], [1216, 832], [1152, 896], [1536, 1024], [1664, 928], [1792, 1024], [1792, 1200], [1920, 1088], [2048, 688], [2048, 1024], [2048, 1152], [2048, 1360], [2048, 1456], [2048, 1536], [2048, 1616], [2048, 1632], [2048, 1712]],
},
"Qwen Image": {
"square": [[1024, 1024], [1280, 1280], [1328, 1328], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[928, 1664], [1024, 1536], [1080, 1920], [1140, 1472], [1152, 2048]],
"landscape": [[1472, 1140], [1536, 1024], [1664, 928], [1920, 1080], [2048, 1152]],
"square": [[1024, 1024], [1080, 1080], [1280, 1280], [1328, 1328], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[680, 2048], [928, 1664], [1024, 1536], [1024, 2048], [1080, 1920], [1140, 1472], [1152, 2048], [1200, 1800], [1368, 2048], [1464, 2048], [1536, 2048], [1608, 2048], [1640, 2048], [1704, 2048]],
"landscape": [[1280, 720], [1472, 1140], [1536, 1024], [1664, 928], [1800, 1200], [1920, 1080], [2048, 680], [2048, 1024], [2048, 1152], [2048, 1368], [2048, 1464], [2048, 1536], [2048, 1608], [2048, 1640], [2048, 1704]],
},
"Z-Image": {
"square": [[512, 512], [768, 768], [1024, 1024], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[720, 1280], [768, 1024], [1080, 1920], [1152, 2048]],
"landscape": [[1024, 768], [1280, 720], [1920, 1080], [2048, 1152]],
"square": [[512, 512], [768, 768], [1024, 1024], [1080, 1080], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[680, 2048], [720, 1280], [768, 1024], [1024, 2048], [1080, 1920], [1152, 2048], [1200, 1800], [1368, 2048], [1464, 2048], [1536, 2048], [1608, 2048], [1640, 2048], [1704, 2048]],
"landscape": [[1024, 768], [1280, 720], [1800, 1200], [1920, 1080], [2048, 680], [2048, 1024], [2048, 1152], [2048, 1368], [2048, 1464], [2048, 1536], [2048, 1608], [2048, 1640], [2048, 1704]],
},
"SD 1.5": {
"square": [[512, 512], [768, 768], [1024, 1024], [1280, 1280], [1536, 1536]],
"portrait": [[512, 768], [512, 682], [512, 1024], [768, 1024], [768, 1344], [1080, 1920]],
"landscape": [[768, 512], [1024, 512], [1024, 768], [1344, 768], [1536, 512], [1920, 1080]],
"square": [[512, 512], [768, 768], [1024, 1024], [1080, 1080], [1280, 1280], [1536, 1536]],
"portrait": [[512, 768], [512, 682], [512, 1024], [680, 2048], [768, 1024], [768, 1344], [1024, 2048], [1080, 1920], [1200, 1800], [1368, 2048], [1464, 2048], [1536, 2048], [1608, 2048], [1640, 2048], [1704, 2048]],
"landscape": [[768, 512], [1024, 512], [1024, 768], [1280, 720], [1344, 768], [1536, 512], [1800, 1200], [1920, 1080], [2048, 680], [2048, 1024], [2048, 1368], [2048, 1464], [2048, 1536], [2048, 1608], [2048, 1640], [2048, 1704]],
},
"SDXL": {
"square": [[1024, 1024], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[640, 1536], [768, 1344], [832, 1216], [896, 1152], [1024, 1536], [1080, 1920], [1152, 2048]],
"landscape": [[1152, 896], [1216, 832], [1344, 768], [1536, 640], [1536, 1024], [1920, 1080], [2048, 1152]],
"square": [[1024, 1024], [1080, 1080], [1280, 1280], [1536, 1536], [1920, 1920], [2048, 2048]],
"portrait": [[640, 1536], [680, 2048], [768, 1344], [832, 1216], [896, 1152], [1024, 1536], [1024, 2048], [1080, 1920], [1152, 2048], [1200, 1800], [1368, 2048], [1464, 2048], [1536, 2048], [1608, 2048], [1640, 2048], [1704, 2048]],
"landscape": [[1152, 896], [1216, 832], [1280, 720], [1344, 768], [1536, 640], [1536, 1024], [1800, 1200], [1920, 1080], [2048, 680], [2048, 1024], [2048, 1152], [2048, 1368], [2048, 1464], [2048, 1536], [2048, 1608], [2048, 1640], [2048, 1704]],
}
};
@@ -60,21 +62,22 @@ function calculateAspectRatio(width, height) {
[0.56, "9:16"], // 0.5625
[0.5, "1:2"], // 0.5
[0.42, "5:12"], // 0.4166...
[0.33, "1:3"], // 0.333... (panoramic)
];
// Find closest common ratio (within 5% tolerance)
const tolerance = 0.05;
for (const [ratioValue, ratioStr] of commonRatios) {
if (Math.abs(actualRatio - ratioValue) / ratioValue < tolerance) {
return ratioStr;
// Find closest common ratio by absolute difference
let closestRatio = commonRatios[0];
let minDiff = Math.abs(actualRatio - closestRatio[0]);
for (const ratio of commonRatios) {
const diff = Math.abs(actualRatio - ratio[0]);
if (diff < minDiff) {
minDiff = diff;
closestRatio = ratio;
}
}
// If no close match, calculate GCD-based ratio
const divisor = gcd(width, height);
const ratioWidth = width / divisor;
const ratioHeight = height / divisor;
return `${ratioWidth}:${ratioHeight}`;
return closestRatio[1];
}
function formatResolution(width, height) {