From 520c8f50223a2685c5e861165eff42bf2d2a61c0 Mon Sep 17 00:00:00 2001 From: Alexander Tujicov Date: Wed, 9 Sep 2026 18:58:37 +0300 Subject: [PATCH] Maximize GPT 4K output dimensions --- README.md | 5 +++-- nano_seed.py | 61 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ccc5916..b4c3a4a 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ You must have a **Fal.ai API Key** to use this node. * **num_inference_steps**: Number of inference steps for supported models. * **seed**: Seed for reproducibility. * **aspect_ratio**: Set target aspect ratio (e.g., 16:9, 1:1). -* **resolution**: Sets the maximum output edge for GPT Image models while preserving the first input image's aspect ratio: `0.5K` = 512px, `1K` = 1024px, `2K` = 2048px, and `4K` = 3840px. +* **resolution**: Sets the GPT output tier while preserving the first input image's aspect ratio. `0.5K`, `1K`, and `2K` use 512px, 1024px, and 2048px maximum edges. `4K` uses the largest valid dimensions under fal's 3840px-edge and 8,294,400-pixel limits. * **quality**: GPT quality. GPT Image 2 supports `auto` through `high`; GPT Image 2.5 also supports `xhigh` and `max`. * **enable_web_search**: Enables web search context for supported models. * **thinking_level**: `off`, `minimal`, or `high` for supported models. @@ -89,7 +89,8 @@ Different models have different requirements implemented in the node: * Supports all 10 NanoSeed reference-image sockets and up to **10 output images**. * Supports `auto`, `low`, `medium`, `high`, `xhigh`, and `max` quality. Higher levels increase cost and latency. * Supports automatic, transparent, or opaque backgrounds and PNG, JPEG, or WebP output. - * `resolution` automatically calculates dimensions from the first input image: 1K, 2K, or 4K sets a 1024px, 2048px, or 3840px maximum edge. + * `resolution` automatically calculates the largest valid dimensions from the first input image. + * At `4K`, 16:9 becomes **3840x2160**, portrait 9:16 becomes **2160x3840**, square becomes **2880x2880**, and a **3678x4598** input becomes **2576x3216**. * Calculated dimensions are multiples of 16, and input aspect ratios beyond the API limit are clamped to 3:1. * `gpt_image_size` or non-zero `width` and `height` can override the aspect ratio, but not the selected resolution cap. * Supports the shared GPT `mask` socket. diff --git a/nano_seed.py b/nano_seed.py index e37d487..40717e5 100644 --- a/nano_seed.py +++ b/nano_seed.py @@ -31,6 +31,9 @@ GPT_RESOLUTION_MAX_EDGE = { "4K": 3840, } +GPT_MAX_OUTPUT_PIXELS = 8_294_400 +GPT_MAX_ASPECT_RATIO = 3.0 + GPT_IMAGE_SIZE_OPTIONS = [ "auto", "square_hd", @@ -222,22 +225,50 @@ def _resolve_gpt_image_size(image_tensor, resolution, width, height, image_size) if source_width <= 0 or source_height <= 0: raise ValueError("GPT image dimensions must be greater than zero.") - if source_width >= source_height: - aspect_ratio = min(source_width / source_height, 3.0) - output_width = max_edge - output_height = _round_to_multiple(output_width / aspect_ratio, 16) - output_height = max( - output_height, - math.ceil((output_width / 3.0) / 16) * 16, - ) + is_landscape = source_width >= source_height + long_edge = max(source_width, source_height) + short_edge = min(source_width, source_height) + aspect_ratio = min(long_edge / short_edge, GPT_MAX_ASPECT_RATIO) + ideal_long_edge = min( + max_edge, + math.sqrt(GPT_MAX_OUTPUT_PIXELS * aspect_ratio), + ) + ideal_short_edge = ideal_long_edge / aspect_ratio + + long_edge_candidates = { + max(16, math.floor(ideal_long_edge / 16) * 16), + max(16, math.ceil(ideal_long_edge / 16) * 16), + } + short_edge_candidates = { + max(16, math.floor(ideal_short_edge / 16) * 16), + max(16, math.ceil(ideal_short_edge / 16) * 16), + } + valid_sizes = [ + (candidate_long_edge, candidate_short_edge) + for candidate_long_edge in long_edge_candidates + for candidate_short_edge in short_edge_candidates + if candidate_long_edge <= max_edge + and candidate_short_edge <= max_edge + and candidate_long_edge * candidate_short_edge <= GPT_MAX_OUTPUT_PIXELS + and candidate_long_edge / candidate_short_edge <= GPT_MAX_ASPECT_RATIO + ] + if not valid_sizes: + raise ValueError("Unable to calculate a valid GPT image size.") + + output_long_edge, output_short_edge = max( + valid_sizes, + key=lambda size: ( + size[0] * size[1], + -abs((size[0] / size[1]) - aspect_ratio), + ), + ) + + if is_landscape: + output_width = output_long_edge + output_height = output_short_edge else: - aspect_ratio = min(source_height / source_width, 3.0) - output_height = max_edge - output_width = _round_to_multiple(output_height / aspect_ratio, 16) - output_width = max( - output_width, - math.ceil((output_height / 3.0) / 16) * 16, - ) + output_width = output_short_edge + output_height = output_long_edge return {"width": output_width, "height": output_height}