Files
2025-08-03 16:35:40 +05:00

116 lines
8.5 KiB
Markdown

> "Do one thing and do it well." _— Peter H. Salus / Doug McIlroy, [core Unix principle](https://en.wikipedia.org/wiki/Unix_philosophy)_
> "Simple is better than complex." _— Zen of Python_
# `Best Resolution` ComfyUI nodes [UX/QoL]
A small modular pack — nodes for semi-automatic calculation of the best (most optimal) sampling resolution:
- ... model-agnostic — i.e., compatible with **ANY** model (from now **or** the future),
- ... accounting for upscale,
- ... and for (the essential) pixel-step.
[🔄 Updates ChangeLog](CHANGELOG.md)
> [!NOTE]
> Almost all the nodes from this pack do **nothing** with actual images on their own — they only calculate the optimal **values** for the resolution.
Read any further only if you feel you need to.
## The purpose
In case you didn't know, you can't just choose **any** arbitrary resolution for sampling. You need to ensure:
- both width and height are divisible by a latent scale factor, pre-defined by a model family _(`8` for both SD1.5 and SDXL)_ - and they **must** remain multiples of it both in initial generation **AND** after further upscales;
- the overall resolution _(aka megapixels, total pixel count)_ is as close as possible to the model's "view window" _(the resolution it was trained on)_ - so if you increase one side of the image, you need to proportionally decrease the other.
... and choosing the right resolution is your responsibility. 🤷🏻‍♂️
This pack lets you forget about crunching numbers and handles the calculation of optimal width/height for you, while still leaving control of the image size/proportions/orientation to you. It provides...
## 3 main nodes
![screenshot1](img/screenshot1.png)
### Simple
Just rounds width/height to the given step, ensuring the image **CAN** be converted to latent - both in initial generation and after some upscales.
### From Aspect-Ratio
As the name suggests, you control the resolution indirectly - by specifying the desired aspect ratio, one of the sides, and orientation (landscape/portrait).
The node detects the actual width/height to match those as close as posible.
### From Area
To my taste, **THE** way to select the optimal resolution for diffusion models.
All you need to know is a size (one side) of square images the model was trained on _(512 for SD1.5, 1024 for SDXL)_. The node handles the rest to meet all the restrictions:
- match the total resolution (i.e., image area, number of pixels) as close to the training one as possible,
- ... while still respecting aspect ratio, image orientation and step size.
## Helper node - `Upscale Image By (with Model)`
![screenshot_upscale_model_by](img/screenshot_upscale_model_by.png)
This is the **only** node in the pack which does more than some resolution calculation, and actually scales an image. But it's nothing more than a wrapper over the built-in `Upscale Image (using Model)` and `Upscale Image By` nodes used in sequence.
Like the built-in `Upscale Image (using Model)` node, this one scales with a specialized model, **but also** does it to a desired upscale factor, like `Upscale Image By` does. This factor should be below the model's native one. And, depending on the value you set this factor to, you get different results.
For a `x4` model, you could expect the following:
| Target `scale` factor | Resulting image |
|-----------------------|------------------------------------------------------------------------------------------------------------------------------------|
| Below `x0.5` | ❔ Perfect, crisp, but... 🤔 the model-upscale won't even be performed. Why using this node at all and not just `Upscale Image By`? |
| `x0.5+` - `x1.0` | 👍🏻 Perfect, downscale-only, but the result might be slightly different (and crisper) from using just `Upscale Image By`. |
| `x1.0+` - `x3.5` | 👍🏻 **Perfect. The upscale range this node was made for.** |
| `x3.5+` - `x4.0` | ⚠️ **Image starts getting blurry, but still better than just using `Upscale Image By`.** |
| `x4.0` exactly | 👍🏻 Perfect. Just the same result as from `Upscale Image (using Model)`. Second scaling isn't even performed. |
| `x4.0+` | ‼️ **VERY** blurry |
For a `x2` model, the middle two ranges (the most important ones, with **bold text**) would be, respectively:
- `x1.0+` - `x1.5`
- `x1.5+` - `x2.0`
So, to get the best result, your upscale factor should be between `1.0` and `(model_scale - 0.5)`. The node won't stop you from getting a subpar result, but it will warn you (if the `show_status` toggle is enabled).
## Advanced: Upscale support (aka "HD-fix")
![screenshot2](img/screenshot2.png)
`Best-Res` nodes have a special version _(currently, only `area` node has one)_, which also account for very first upscale aka HD-fix - to ensure the upscaled resolution is also divisible by the step value.
Additionally, they're accompanied by utility node to also auto-detect any necessary cropping/padding (for out-paint) to perform on the upscaled image before second KSampler (the "HD-fix" itself).
## Tooltips
Each parameter is self-documented in the shortest possible, yet exhaustive detail - just hover mouse over it. If you're new to Comfy and Stable Diffusion, this might be especially helpful.
## A note on `step` value to choose
This parameter has the highest priority. A desired resolution is slightly tweaked to **always** adhere to it **strictly**. The nodes do their best to match all other criteria, but no matter what, in the end both width and height **must** remain a multiple of this value. This means two things:
1. If you don't plan processing the image with diffusion model but otherwise like these nodes for image-size selection, you can still use them - just set `step` to `1` (effectively disabling any rounding).
2. The right `step` value can single-handedly save you from unnecessary cropping or out-painting later.
> In my workflows, I usually generate images like this:
> - initial draft generation, exactly in the resolution the model was trained on - only composition and overall silhouettes matter here.
> - "HD fix": immediate first 1.5x upscale in latent space, followed by sampling with high denoise (0.5+) - this becomes the actual base image to improve upon.
> - some tweaking/inpainting is done in this resolution.
> - at least one more upscale, still sampling the whole image at once if GPU has VRAM for that (with a lower denoise, though).
> - subsequent upscales with [USDU](https://github.com/ssitu/ComfyUI_UltimateSDUpscale).
So, this imposes a few restrictions on the initial gen if we want to avoid cropping/outpainting later:
- The initial image must be a multiple of `8`
- After 1.5x upscale it must also be a multiple of `8` - so a +0.5 of size must be divisible by 8, or the initial step must be `8*2`.
- After the first 1.5x upscale _(with the original `step` being `8*2`, or `16`)_ our image became divisible by `8*2*1.5`, or `8*3`, so the second upscale could be +1/3 aka 1.333x. And if we do so, we actually reach the total size of 2x of the original gen.
- However, if we do so, at the very next step our divisibility would be `8*3*1.333`, or `8*4`.
- This is good since the third upscale could be either 1.5x or 1.25x... but it can't be another +1/3.
- So, to workaround it, it's safer to include another multiple of `3` into the original `step` _(so, together it becomes `8*2*3`)_.
- With it, we can do the previous 1.5x > 1.333x, followed by either 1.5x/1.25x **or 1.333x**.
- If instead we did 1.5x > **2x** - then we end up with a triple of the original size **AND** we have another multiple of 3 built-in, so the image has become divisible by `9`. This is not at all necessary for "regular"-sized HD images, but it might become **very** handy for later ultra-upscales (6k+) with USDU.
Summarizing all of the above, our lowest common denominator to be safe during first few upscales is `8*2*3`.
**Thus, `48` by default.**
Additionally, if you prefer to always upscale by 1.5, you might look into `8*2*2` = `32`.
Or, to allow a few more of 1.5x upscales (or a 1.25x one), or to get the resolution closer to the ones we used to (1080p, 1440p, etc), you might want to increase it by a power of 2, getting `64`, `96`, `128`, `192`, `256`, etc.