ScaleToTargetMegapixels
Never do upscale-area math by hand again
- image
- upscale
- downscale
- upscale_if_need
- downscale_if_need
The name is half a lie: this node doesn't scale anything. It's a calculator - a tiny one - that answers the question every upscaling workflow eventually asks: what scale factor gets me to N megapixels? You feed it an image, tell it your target in megapixels, and it hands back the exact multiplier to plug into an ImageScaleBy or LatentUpscaleBy. That's the whole job, and it's a job worth outsourcing.
Why this exists
The pixel math is a trap, and it bites everyone once. Doubling both sides of an image quadruples the pixels, because area grows with the square. So going from 0.5MP to 1MP is a ~1.41x per-side scale, not 2x - and a naive "let's 2x it" lands you at 4x what you wanted. When your inputs are a batch of mixed-size images - video frames, img2img sources pulled off disk - a fixed multiplier like 1.5x is a dice roll: it overshoots some and undershoots others. Thinking in target megapixels instead of factors fixes that.
It also matches how the models actually work. SDXL wants roughly its ~1MP training band, Flux sits near 1MP, and the 2026 crop (Z-Image, Flux 2) are happiest in a 1–2MP band. Aiming at an absolute pixel count instead of a guess ratio is the sane way to run a hi-res fix second pass without blowing past what the model can render cleanly.
How it works
The math is two lines. It takes your image's height and width, computes current_megapixels = (H × W) / 1,000,000, then:
upscale = sqrt(target / current)- multiply each side by this to land on targetdownscale = sqrt(current / target)- the same number turned around
The square root is the entire point of the node: you're solving for a linear factor given an area goal, which is the part people get wrong in their heads.
The inputs and outputs
One knob: megapixels (default 1.0, range 0.1–100). You feed in any image tensor; it only reads the dimensions. That's it.
The four FLOAT outputs are where it gets interesting:
upscaleanddownscale- the pair you'll actually use. Pick whichever direction you're headed and wire it into thescale_byinput of anImageScaleByorLatentUpscaleBy.upscale_if_needanddownscale_if_need- supposed to be "the factor, or 1.0 if the image doesn't need it," which would be genuinely handy for batches of mixed sizes.
Here's the catch: as shipped, the if_need logic is inverted. When your image is smaller than the target - the one case where upscaling is needed - both if_need outputs return 1.0. When it's larger, they return the raw factors. If that reads backwards to you, you're reading it right. Treat upscale/downscale as ground truth and test the if_need pair before you trust them in a real workflow.
The node is registered as an output node, so it always executes and prints its answer as text right on the canvas - you can eyeball the factors before wiring anything. (It still routes its float outputs downstream fine.)
Installing it
cd ComfyUI/custom_nodes
git clone https://github.com/troyxmccall/ComfyUI-ScaleToTargetMegapixels
then restart ComfyUI. Or search "ScaleToTargetMegapixels" in ComfyUI Manager. No dependencies beyond torch, which ComfyUI already ships, and no model downloads - the whole pack is a single __init__.py. This is about as frictionless as a custom node gets.
Gotchas worth knowing
- Don't confuse the multiplier outputs with an
ImageScale's absolute width/height inputs.scale_byinputs take a multiplier; this node gives you one. megapixelsis a target, not a factor. Feeding a 256px source and asking for 1MP computes a ~4x upscale, which may be more than you want from a single pass - pair it with a real upscaler or work on the latent side.- The
if_needinversion above. That's the one rough edge; the core math is correct.
Verdict: a dependency-free utility that does one thing right and saves you from doing area math at 2am. If you batch-upscale or run hi-res fixes, that's worth a five-second install.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| megapixels | FLOAT | 1.00.1–100 | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| upscale | FLOAT | — |
| downscale | FLOAT | — |
| upscale_if_need | FLOAT | — |
| downscale_if_need | FLOAT | — |