From db12e23cfd635b8be8a747b1a9093202ccd7349f Mon Sep 17 00:00:00 2001 From: Kieran Bicheno Date: Wed, 11 Jun 2025 10:52:24 +1000 Subject: [PATCH] feat: Add ComfyUI Image Size Tool v0.1.0-alpha MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement resolution calculator nodes for AI image generation models: - SD 1.5 Resolution Node: 8 optimized presets (512×512 to 912×512) - SDXL Resolution Node: 13 bucket-trained presets (~1MP, div by 64) - Flux.1 Dev Resolution Node: 11 practical presets (1024×1024 to 2560×1440) Features: - Zero external dependencies for maximum compatibility - Model-specific validation (div by 8/64 constraints) - Bucket-aware resolution selection for optimal quality - Aspect ratio text output for reference - SDXL includes pixel count display Based on technical specifications and training bucket systems. Addresses the gap in reliable, constraint-aware resolution tools. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .gitignore | 4 +- .python-version | 1 + CLAUDE.md | 45 +++++ PROCEDURES.md | 1 + __init__.py | 28 +++ node_pack.md | 7 + nodes/__init__.py | 1 + nodes/flux_resolution_node.py | 50 ++++++ nodes/sd15_resolution_node.py | 49 ++++++ nodes/sdxl_resolution_node.py | 51 ++++++ pyproject.toml | 27 +++ requirements.txt | 3 + res_reference.md | 317 ++++++++++++++++++++++++++++++++++ utils/__init__.py | 1 + utils/resolution_data.py | 88 ++++++++++ 15 files changed, 672 insertions(+), 1 deletion(-) create mode 100644 .python-version create mode 100644 CLAUDE.md create mode 120000 PROCEDURES.md create mode 100644 __init__.py create mode 100644 node_pack.md create mode 100644 nodes/__init__.py create mode 100644 nodes/flux_resolution_node.py create mode 100644 nodes/sd15_resolution_node.py create mode 100644 nodes/sdxl_resolution_node.py create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 res_reference.md create mode 100644 utils/__init__.py create mode 100644 utils/resolution_data.py diff --git a/.gitignore b/.gitignore index 7b004e5..0e1ef7a 100644 --- a/.gitignore +++ b/.gitignore @@ -191,4 +191,6 @@ cython_debug/ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data # refer to https://docs.cursor.com/context/ignore-files .cursorignore -.cursorindexingignore \ No newline at end of file +.cursorindexingignore + +estimates/ diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..99b565f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,45 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is a ComfyUI node for setting image sizes based on AI model specifications. The project aims to create a resolution calculator that understands the technical constraints and optimal resolutions for different AI image generation models (SD 1.5, SDXL, Flux.1, HiDream, Wan2.1, Illustrious XL, SD3). + +## Key Technical Concepts + +### Model-Specific Resolution Constraints +- **SD 1.5**: Dimensions must be divisible by 8, native 512×512 +- **SDXL**: Dimensions must be divisible by 64, uses bucket system targeting ~1 megapixel +- **Flux.1 Dev**: No strict divisibility requirements, native 1024×1024 +- **SD3**: Dimensions must be divisible by 64, optimal around 1 megapixel +- **Illustrious XL**: Native 1536×1536, follows SDXL constraints + +### Architecture Understanding +The tool needs to implement resolution validation and suggestion algorithms that: +1. Validate against model-specific divisibility factors +2. Suggest closest trained bucket resolutions for optimal quality +3. Calculate VRAM requirements based on resolution and model size +4. Handle aspect ratio preservation with proper rounding + +## Development Commands + +```bash +# Run the main application +python main.py + +# Install dependencies (when they exist) +pip install -e . +``` + +## Critical Implementation Notes + +- Resolution calculations must account for VAE downsampling factors +- SDXL uses a sophisticated 43-bucket resolution system - don't calculate arbitrary resolutions +- Always validate divisibility constraints before suggesting resolutions +- Consider VRAM scaling (roughly quadratic with resolution increase) +- Bucket systems solve the cropping problem by training on natural aspect ratios + +## Reference Documentation + +The `res_reference.md` file contains comprehensive technical specifications for all supported models, including mathematical formulas, bucket algorithms, and hardware requirements. This is the authoritative source for resolution constraints and optimal settings. \ No newline at end of file diff --git a/PROCEDURES.md b/PROCEDURES.md new file mode 120000 index 0000000..760cdb7 --- /dev/null +++ b/PROCEDURES.md @@ -0,0 +1 @@ +/home/websinthe/code/Central-Projections/PROCEDURES.md \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..012347e --- /dev/null +++ b/__init__.py @@ -0,0 +1,28 @@ +""" +ComfyUI Image Size Tool + +A node pack providing resolution calculators for AI image generation models. +Includes optimized presets for SD 1.5, SDXL, and Flux.1 Dev with proper +validation and bucket-aware resolution selection. +""" + +from .nodes.sd15_resolution_node import SD15ResolutionNode +from .nodes.sdxl_resolution_node import SDXLResolutionNode +from .nodes.flux_resolution_node import FluxResolutionNode + +# Combine all node class mappings +NODE_CLASS_MAPPINGS = { + "SD15ResolutionNode": SD15ResolutionNode, + "SDXLResolutionNode": SDXLResolutionNode, + "FluxResolutionNode": FluxResolutionNode, +} + +# Combine all display name mappings +NODE_DISPLAY_NAME_MAPPINGS = { + "SD15ResolutionNode": "SD 1.5 Resolution", + "SDXLResolutionNode": "SDXL Resolution", + "FluxResolutionNode": "Flux.1 Dev Resolution", +} + +# Export for ComfyUI +__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] \ No newline at end of file diff --git a/node_pack.md b/node_pack.md new file mode 100644 index 0000000..22b94a0 --- /dev/null +++ b/node_pack.md @@ -0,0 +1,7 @@ +# Nodes + +1. SD 1.5 image sizes +2. SDXL image sizes +3. Flux.1 Dev image sizes +4. AIO simple # User sets the model type +5. AIO advanced # Can detect model type (will probably be harder than it sounds) diff --git a/nodes/__init__.py b/nodes/__init__.py new file mode 100644 index 0000000..7cffb56 --- /dev/null +++ b/nodes/__init__.py @@ -0,0 +1 @@ +# Nodes package for ComfyUI Image Size Tool \ No newline at end of file diff --git a/nodes/flux_resolution_node.py b/nodes/flux_resolution_node.py new file mode 100644 index 0000000..2f1fe7c --- /dev/null +++ b/nodes/flux_resolution_node.py @@ -0,0 +1,50 @@ +import sys +import os +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from utils.resolution_data import FLUX_RESOLUTIONS, validate_resolution, get_aspect_ratio_text + +class FluxResolutionNode: + """ + Flux.1 Dev Resolution Calculator + + Provides practical resolution presets for Flux.1 Dev models. + No strict divisibility requirements, focused on hardware compatibility + and common use cases from 1024x1024 to 2560x1440. + """ + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "resolution": (list(FLUX_RESOLUTIONS.keys()),), + }, + } + + RETURN_TYPES = ("INT", "INT", "STRING") + RETURN_NAMES = ("width", "height", "aspect_ratio") + FUNCTION = "get_resolution" + CATEGORY = "Image Size Tool" + + def get_resolution(self, resolution): + """Get width, height, and aspect ratio for selected Flux.1 Dev resolution""" + res_data = FLUX_RESOLUTIONS[resolution] + width = res_data["width"] + height = res_data["height"] + aspect_ratio = res_data["ratio"] + + # Validate the resolution (Flux is more flexible) + is_valid, message = validate_resolution(width, height, "FLUX") + if not is_valid: + print(f"Warning: Invalid resolution {width}×{height}: {message}") + + return (width, height, aspect_ratio) + +# Node registration will be handled in __init__.py +NODE_CLASS_MAPPINGS = { + "FluxResolutionNode": FluxResolutionNode +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "FluxResolutionNode": "Flux.1 Dev Resolution" +} \ No newline at end of file diff --git a/nodes/sd15_resolution_node.py b/nodes/sd15_resolution_node.py new file mode 100644 index 0000000..180bd97 --- /dev/null +++ b/nodes/sd15_resolution_node.py @@ -0,0 +1,49 @@ +import sys +import os +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from utils.resolution_data import SD15_RESOLUTIONS, validate_resolution, get_aspect_ratio_text + +class SD15ResolutionNode: + """ + SD 1.5 Resolution Calculator + + Provides optimal resolution presets for Stable Diffusion 1.5 models. + All resolutions are validated to be divisible by 8 (VAE constraint). + """ + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "resolution": (list(SD15_RESOLUTIONS.keys()),), + }, + } + + RETURN_TYPES = ("INT", "INT", "STRING") + RETURN_NAMES = ("width", "height", "aspect_ratio") + FUNCTION = "get_resolution" + CATEGORY = "Image Size Tool" + + def get_resolution(self, resolution): + """Get width, height, and aspect ratio for selected SD 1.5 resolution""" + res_data = SD15_RESOLUTIONS[resolution] + width = res_data["width"] + height = res_data["height"] + aspect_ratio = res_data["ratio"] + + # Validate the resolution (should always pass for our presets) + is_valid, message = validate_resolution(width, height, "SD15") + if not is_valid: + print(f"Warning: Invalid resolution {width}×{height}: {message}") + + return (width, height, aspect_ratio) + +# Node registration will be handled in __init__.py +NODE_CLASS_MAPPINGS = { + "SD15ResolutionNode": SD15ResolutionNode +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "SD15ResolutionNode": "SD 1.5 Resolution" +} \ No newline at end of file diff --git a/nodes/sdxl_resolution_node.py b/nodes/sdxl_resolution_node.py new file mode 100644 index 0000000..2626785 --- /dev/null +++ b/nodes/sdxl_resolution_node.py @@ -0,0 +1,51 @@ +import sys +import os +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from utils.resolution_data import SDXL_RESOLUTIONS, validate_resolution, get_aspect_ratio_text + +class SDXLResolutionNode: + """ + SDXL Resolution Calculator + + Provides bucket-trained resolution presets for SDXL models. + All resolutions target ~1 megapixel and are divisible by 64. + Based on the 43-bucket training system for optimal quality. + """ + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "resolution": (list(SDXL_RESOLUTIONS.keys()),), + }, + } + + RETURN_TYPES = ("INT", "INT", "STRING", "INT") + RETURN_NAMES = ("width", "height", "aspect_ratio", "total_pixels") + FUNCTION = "get_resolution" + CATEGORY = "Image Size Tool" + + def get_resolution(self, resolution): + """Get width, height, aspect ratio, and pixel count for selected SDXL resolution""" + res_data = SDXL_RESOLUTIONS[resolution] + width = res_data["width"] + height = res_data["height"] + aspect_ratio = res_data["ratio"] + total_pixels = res_data["pixels"] + + # Validate the resolution (should always pass for our presets) + is_valid, message = validate_resolution(width, height, "SDXL") + if not is_valid: + print(f"Warning: Invalid resolution {width}×{height}: {message}") + + return (width, height, aspect_ratio, total_pixels) + +# Node registration will be handled in __init__.py +NODE_CLASS_MAPPINGS = { + "SDXLResolutionNode": SDXLResolutionNode +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "SDXLResolutionNode": "SDXL Resolution" +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..43008ad --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "comfyui-image-size-tool" +version = "0.1.0-alpha" +description = "Resolution calculator nodes for ComfyUI with model-specific constraints and optimal bucket resolutions" +readme = "README.md" +license = {file = "LICENSE"} +requires-python = ">=3.8" +dependencies = [] +keywords = ["comfyui", "stable-diffusion", "image-generation", "resolution", "sdxl", "flux"] +authors = [ + {name = "websinthe"} +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Topic :: Multimedia :: Graphics", + "Topic :: Scientific/Engineering :: Artificial Intelligence" +] + +[project.urls] +Homepage = "https://github.com/websinthe/ComfyUI-Image-Size-Tool" +Repository = "https://github.com/websinthe/ComfyUI-Image-Size-Tool" +Issues = "https://github.com/websinthe/ComfyUI-Image-Size-Tool/issues" + +[build-system] +requires = ["setuptools>=45", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ef24146 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +# ComfyUI Image Size Tool - Zero Dependencies +# This package intentionally has no external dependencies to ensure maximum compatibility +# and avoid the dependency conflicts that plague many ComfyUI custom nodes. \ No newline at end of file diff --git a/res_reference.md b/res_reference.md new file mode 100644 index 0000000..f3960fd --- /dev/null +++ b/res_reference.md @@ -0,0 +1,317 @@ +# Technical Specifications for AI Image/Video Generation Models: Resolution and Aspect Ratio Guide + +## Stable Diffusion 1.5 + +### Training specifications and constraints +- **Native Training Resolution**: 512×512 pixels +- **VAE Downsampling Factor**: 8x (reduces images to latent space of H/8 × W/8) +- **Resolution Constraint**: All dimensions must be divisible by 8 +- **Mathematical Formula**: `(width % 8 == 0) and (height % 8 == 0)` + +### Recommended resolutions for optimal quality +| Aspect Ratio | Resolution | Use Case | +|--------------|------------|----------| +| 1:1 | 512×512, 768×768 | Square images | +| 2:3 | 512×768 | Portrait | +| 3:2 | 768×512 | Landscape | +| 4:3 | 768×576 | Standard format | +| 16:9 | 912×512 | Widescreen | +| 9:16 | 512×912 | Mobile/tall | + +### Technical constraints +- Model performs best at native 512×512 resolution +- Quality significantly degrades when deviating from training resolution +- 1.3 billion parameters with CLIP ViT-L/14 text encoder +- Minimum VRAM: 4GB (limited), Recommended: 8GB + +## SDXL (Stable Diffusion XL) + +### Advanced bucket resolution system +SDXL uses a sophisticated multi-aspect ratio training approach with 43 official bucket resolutions ranging from 512×2048 to 2048×512, all targeting approximately 1 megapixel (1,048,576 pixels). + +### Key technical specifications +- **Native Resolution**: 1024×1024 pixels +- **Resolution Constraint**: Dimensions must be divisible by 64 +- **Mathematical Formula**: `(width % 64 == 0) and (height % 64 == 0)` +- **Pixel Target**: Total pixels ≈ 1,048,576 +- **Aspect Ratio Range**: 0.25 ≤ aspect_ratio ≤ 4.0 + +### Resolution calculation algorithm + +```python +def calculate_sdxl_resolution(aspect_ratio): + target_pixels = 1048576 # ~1 megapixel + center_point = 1024 + + width = int(center_point * (aspect_ratio**0.5)) + height = int(target_pixels / width) + width = width - (width % 64) # Round to nearest 64 + height = height - (height % 64) + return width, height +``` + +### Most commonly used resolutions + +| Aspect Ratio | Resolution | Pixels | +|--------------|------------|--------| +| 1:1 | 1024×1024 | 1,048,576 | +| 7:9 | 896×1152 | 1,032,192 | +| 9:7 | 1152×896 | 1,032,192 | +| 7:4 | 1344×768 | 1,032,192 | +| 4:7 | 768×1344 | 1,032,192 | + +### Performance specifications + +- 3.5 billion parameters (3x larger than SD 1.5) +- Dual text encoders: OpenCLIP-ViT/G + CLIP-ViT/L +- Minimum VRAM: 4GB (extremely limited), Recommended: 12GB+ +- Novel conditioning: size, crop, and aesthetic parameters + +## Flux.1 Dev + +### Flexible architecture specifications + +- **Native Resolution**: 1024×1024 pixels +- **Training Range**: 0.2 to 2 megapixels +- **Architecture**: 12 billion parameter rectified flow transformer +- **Key Feature**: No specific divisibility requirements (more flexible than SD models) + +### Recommended resolutions + +- 1024×1024 (standard, 20-second render) +- 1920×1080 (Full HD, optimal balance) +- 1536×640 (ultrawide/banner format) +- 1600×1600 (high detail, 35-40 second render) + +### Technical considerations + +- Effective resolution range: 512×512 to 2560×1440 (hardware dependent) +- RTX 3090: Best at 1920×1080 or lower +- RTX 4090: Can handle up to 2560×1440 +- 4× slower than SDXL for equivalent resolutions +- Uses rotary positional embeddings for hardware efficiency + +## HiDream-I1 + +### Model specifications + +- **Native Resolution**: 1024×1024 pixels +- **Architecture**: 17 billion parameter Mixture of Experts (MoE) with Sparse Diffusion Transformer +- **Standard Output**: 1024×1024 pixels +- **Resolution Flexibility**: Follows SDXL-style constraints + +### Hardware requirements + +- Requires NVIDIA Ampere or newer (RTX 3090+, A100, H100) +- Full model: ~60GB VRAM +- Dev model: ~24GB VRAM +- Quantized versions available (FP16, FP8, NF4) +- Multiple text encoders: OpenCLIP ViT-bigG, CLIP ViT-L, T5-XXL, Llama-3.1-8B + +## Wan2.1 + +### Video generation specifications + +- **Model Variants**: 1.3B and 14B parameters +- **Architecture**: Flow Matching framework with Diffusion Transformers +- **Primary Resolutions**: + - 480P: 832×480 pixels + - 720P: 1280×720 pixels +- **VAE Capability**: Can encode/decode 1080P videos + +### Technical parameters + +- Video output: 5-second clips at 16 FPS +- Flow_shift parameter: 5.0 for 720P, 3.0 for 480P +- 1.3B model: Only 8.19GB VRAM required +- 14B model: 24GB+ VRAM recommended +- Supports both Chinese and English text in videos + +## Illustrious XL + +### Enhanced SDXL variant specifications + +- **Base Architecture**: SDXL without modifications +- **Native Resolution**: 1536×1536 pixels (v1.0+) +- **Resolution Range**: 512×512 to 1536×1536 pixels +- **Future Support**: v2.0 targeting 2048×2048 and 20MP+ generation + +### Technical requirements + +- Minimum resolution: 512×512 (lower causes errors) +- ClipSkip: Must be set to 2 +- CFG Scale: 3-7 recommended (4.5-5 optimal) +- Sampling Steps: 20-28 steps +- Euler a sampler recommended +- Can extend to 3744×3744 experimentally + +## SD3 (Stable Diffusion 3) + +### Multi-resolution architecture + +- **Native Resolution**: 1024×1024 pixels (optimal) +- **Architecture**: MMDiT-X (Multimodal Diffusion Transformer) +- **Resolution Constraint**: Dimensions must be divisible by 64 +- **Training Range**: 0.25 to 2 megapixels +- **VAE**: 16-channel (improved from 4-channel) + +### Recommended resolutions +Same divisibility-by-64 constraint as SDXL, with optimal performance around 1 megapixel: + +- Square: 1024×1024, 1152×1152 +- Landscape: 1344×768, 1280×768, 1216×832 +- Portrait: 768×1344, 832×1216, 896×1152 + +### Technical limitations + +- T5 token limit: Artifacts may occur when exceeding 256 tokens +- Poor upscaling performance beyond native resolution +- SD3 Medium: 9.9GB VRAM (excluding text encoders) +- SD3 Large: 24GB VRAM requirement + +## Mathematical Formulas for Resolution Calculation + +### General validation formula + +```python +def validate_resolution(width, height, vae_factor, min_size=256, max_size=2048): + # Divisibility check + if width % vae_factor != 0 or height % vae_factor != 0: + return False + + # Size bounds check + if min(width, height) < min_size or max(width, height) > max_size: + return False + + return True +``` + +### Aspect ratio preservation formula + +```python +def preserve_aspect_ratio(original_width, original_height, target_pixels, factor): + aspect_ratio = original_width / original_height + + # Calculate dimensions based on target pixel count + optimal_height = int((target_pixels / aspect_ratio) ** 0.5) + optimal_width = int(optimal_height * aspect_ratio) + + # Round to valid dimensions + valid_width = (optimal_width // factor) * factor + valid_height = (optimal_height // factor) * factor + + return valid_width, valid_height +``` + +### VRAM requirements formula + +```python +def calculate_vram_gb(width, height, model_size_gb, batch_size=1): + # Activation memory approximation + pixels = width * height * batch_size + activation_memory_gb = pixels * 8 / 1e9 + + # Total VRAM = model + activations + total_vram = model_size_gb + activation_memory_gb + return total_vram +``` + +## Implementation Guide for Resolution Calculator + +### Model-specific divisibility factors + +- **SD 1.5**: 8 +- **SDXL**: 64 +- **Flux.1 Dev**: No strict requirement (recommend 8 for compatibility) +- **HiDream**: 64 (follows SDXL) +- **Wan2.1**: Model-specific for 480P/720P +- **Illustrious**: 64 (SDXL-based) +- **SD3**: 64 + +### Performance scaling considerations + +1. **Memory scaling**: Roughly quadratic with resolution increase +2. **Time scaling**: Non-linear due to attention mechanisms (~O(n^1.2) where n is pixel count) +3. **Quality considerations**: Each model has an optimal resolution range where training data density is highest + +### Key implementation notes + +1. Always validate resolutions against model-specific constraints before generation +2. Consider using bucket systems for models trained with multi-aspect ratios +3. Implement safety margins for VRAM calculations (use 80% of available VRAM) +4. Cache commonly used resolutions to avoid recalculation +5. For video models (Wan2.1), consider temporal dimension in memory calculations + +The bucket system is a crucial training technique that solves a fundamental problem in AI image generation. Let me break it down: + +## The Core Problem + +Traditional diffusion models like early Stable Diffusion were trained only on square images (512×512). When you trained these models on non-square images, you had two bad options: + +1. **Center crop** everything to squares → cuts off heads, feet, important parts +2. **Pad** images to squares → wastes computation on empty space + +This is why early AI models generated images with "unnatural crops" - people without heads or feet, swords that were just straight lines extending beyond the frame. + +## What Bucket Systems Solve + +Aspect ratio bucketing allows the creation of batches where every item in the batch has the same size, but the image size of batches may differ. This means the model can learn from images in their natural aspect ratios without destructive cropping. + +## How Bucketing Works Technically + +### 1. **Bucket Generation Algorithm** (NovelAI's approach): + +```python +# Simplified version of the algorithm +buckets = [] +width = 256 +max_dimension = 1024 +max_pixels = 512 * 768 # Memory constraint + +while width <= max_dimension: + max_height = min(max_dimension, max_pixels // width) + buckets.append((width, max_height)) + width += 64 + +# Repeat with width/height swapped +# Remove duplicates, add 512×512 +``` + +This generates buckets like: + +- 256×1024 (aspect ratio 0.25) +- 320×1024 (aspect ratio 0.3125) +- 384×1024 (aspect ratio 0.375) +- 512×768 (aspect ratio 0.67) +- 512×512 (aspect ratio 1.0) +- 768×512 (aspect ratio 1.5) +- 1024×384 (aspect ratio 2.67) + +### 2. **Image Assignment**: + +For each image in the dataset, we retrieve its resolution and calculate the aspect ratio. The image aspect ratio is subtracted from the array of bucket aspect ratios, allowing us to efficiently select the closest bucket according to the absolute value of the difference between aspect ratios + +### 3. **Batch Creation**: + +- All images in a single batch must be the same resolution +- Different batches can have different resolutions +- When a batch is requested, we randomly draw a bucket from a weighted distribution. The bucket weights are set as the size of the bucket divided by the size of all remaining buckets + +### 4. **Image Processing**: + +The image is scaled, while preserving its aspect ratio, in such a way that it either fits the bucket resolution exactly if the aspect ratio happens to match or it extends past the bucket resolution on one dimension while fitting it exactly on the other. In the latter case, a random crop is applied + +## Why This Matters for Your Tool + +**For SDXL**: Uses 43 different bucket resolutions, all targeting ~1 megapixel. Your tool should validate that user-requested resolutions match these trained buckets for optimal quality. + +**For SD 1.5**: Doesn't use sophisticated bucketing, so stick to divisible-by-8 constraints near 512×512. + +**For Implementation**: When someone requests an arbitrary aspect ratio, your tool should: + +1. Find the closest trained bucket resolution for that model +2. Suggest that resolution instead of arbitrary calculations +3. Warn if the requested ratio is far from any trained bucket + +The bucket system is why SDXL can generate clean 896×1152 portraits or 1344×768 landscapes without cropping artifacts, while SD 1.5 struggles with anything far from square. + diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..72fe78e --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1 @@ +# Utils package for ComfyUI Image Size Tool \ No newline at end of file diff --git a/utils/resolution_data.py b/utils/resolution_data.py new file mode 100644 index 0000000..5d5522e --- /dev/null +++ b/utils/resolution_data.py @@ -0,0 +1,88 @@ +# Resolution data extracted from technical specifications +# Based on model training constraints and optimal quality buckets + +# SD 1.5 - All dimensions must be divisible by 8, native 512x512 +SD15_RESOLUTIONS = { + "512×512 (1:1) - Native": {"width": 512, "height": 512, "ratio": "1:1"}, + "768×512 (3:2) - Landscape": {"width": 768, "height": 512, "ratio": "3:2"}, + "512×768 (2:3) - Portrait": {"width": 512, "height": 768, "ratio": "2:3"}, + "768×576 (4:3) - Standard": {"width": 768, "height": 576, "ratio": "4:3"}, + "576×768 (3:4) - Standard Portrait": {"width": 576, "height": 768, "ratio": "3:4"}, + "912×512 (16:9) - Widescreen": {"width": 912, "height": 512, "ratio": "16:9"}, + "512×912 (9:16) - Mobile": {"width": 512, "height": 912, "ratio": "9:16"}, + "768×768 (1:1) - Large Square": {"width": 768, "height": 768, "ratio": "1:1"}, +} + +# SDXL - Dimensions must be divisible by 64, targeting ~1 megapixel +# Using most commonly used bucket resolutions from the 43-bucket system +SDXL_RESOLUTIONS = { + "1024×1024 (1:1) - Native": {"width": 1024, "height": 1024, "ratio": "1:1", "pixels": 1048576}, + "1152×896 (9:7) - Landscape": {"width": 1152, "height": 896, "ratio": "9:7", "pixels": 1032192}, + "896×1152 (7:9) - Portrait": {"width": 896, "height": 1152, "ratio": "7:9", "pixels": 1032192}, + "1344×768 (7:4) - Wide": {"width": 1344, "height": 768, "ratio": "7:4", "pixels": 1032192}, + "768×1344 (4:7) - Tall": {"width": 768, "height": 1344, "ratio": "4:7", "pixels": 1032192}, + "1216×832 (19:13) - Cinema": {"width": 1216, "height": 832, "ratio": "19:13", "pixels": 1011712}, + "832×1216 (13:19) - Tall Cinema": {"width": 832, "height": 1216, "ratio": "13:19", "pixels": 1011712}, + "1280×768 (5:3) - Ultrawide": {"width": 1280, "height": 768, "ratio": "5:3", "pixels": 983040}, + "768×1280 (3:5) - Ultra Portrait": {"width": 768, "height": 1280, "ratio": "3:5", "pixels": 983040}, + "1536×640 (12:5) - Banner": {"width": 1536, "height": 640, "ratio": "12:5", "pixels": 983040}, + "640×1536 (5:12) - Skyscraper": {"width": 640, "height": 1536, "ratio": "5:12", "pixels": 983040}, + "1600×640 (5:2) - Extreme Wide": {"width": 1600, "height": 640, "ratio": "5:2", "pixels": 1024000}, + "640×1600 (2:5) - Extreme Tall": {"width": 640, "height": 1600, "ratio": "2:5", "pixels": 1024000}, +} + +# Flux.1 Dev - No strict divisibility requirements, more flexible +# Focus on practical resolutions with good hardware compatibility +FLUX_RESOLUTIONS = { + "1024×1024 (1:1) - Native": {"width": 1024, "height": 1024, "ratio": "1:1"}, + "1920×1080 (16:9) - Full HD": {"width": 1920, "height": 1080, "ratio": "16:9"}, + "1080×1920 (9:16) - Vertical HD": {"width": 1080, "height": 1920, "ratio": "9:16"}, + "1536×640 (12:5) - Ultrawide": {"width": 1536, "height": 640, "ratio": "12:5"}, + "640×1536 (5:12) - Ultra Portrait": {"width": 640, "height": 1536, "ratio": "5:12"}, + "1600×1600 (1:1) - High Detail": {"width": 1600, "height": 1600, "ratio": "1:1"}, + "1280×720 (16:9) - HD": {"width": 1280, "height": 720, "ratio": "16:9"}, + "720×1280 (9:16) - Vertical HD": {"width": 720, "height": 1280, "ratio": "9:16"}, + "1366×768 (16:9) - Laptop": {"width": 1366, "height": 768, "ratio": "16:9"}, + "768×1366 (9:16) - Vertical Laptop": {"width": 768, "height": 1366, "ratio": "9:16"}, + "2560×1440 (16:9) - 2K": {"width": 2560, "height": 1440, "ratio": "16:9"}, +} + +# Model-specific validation constraints +MODEL_CONSTRAINTS = { + "SD15": { + "divisible_by": 8, + "native_resolution": (512, 512), + "recommended_max": 1024, + }, + "SDXL": { + "divisible_by": 64, + "native_resolution": (1024, 1024), + "target_pixels": 1048576, # ~1 megapixel + }, + "FLUX": { + "divisible_by": None, # No strict requirement + "native_resolution": (1024, 1024), + "recommended_range": (512, 2560), + }, +} + +def validate_resolution(width, height, model_type): + """Validate resolution against model-specific constraints""" + constraints = MODEL_CONSTRAINTS.get(model_type) + if not constraints: + return True, "Unknown model type" + + if constraints["divisible_by"]: + factor = constraints["divisible_by"] + if width % factor != 0 or height % factor != 0: + return False, f"Dimensions must be divisible by {factor}" + + return True, "Valid resolution" + +def get_aspect_ratio_text(width, height): + """Calculate and return human-readable aspect ratio""" + from math import gcd + common_divisor = gcd(width, height) + ratio_w = width // common_divisor + ratio_h = height // common_divisor + return f"{ratio_w}:{ratio_h}" \ No newline at end of file