Nodes/Nifty Nodes for ComfyUI/Nifty Upscale Image
ComfyUI Node

Nifty Upscale Image

Loader and upscaler in one node, with an OOM parachute

By Stibo·Created 7 months ago·Updated 7 days ago· 11
Nifty Upscale Image
  • image
  • IMAGE
  • model_name
◄model▾►
◄tile_size512►
◄overlap32►

Upscaling is three jobs under one word, and the KB says it best: more pixels, more detail, or more pixels over time. This node is squarely the first one. It loads an ESRGAN-lineage model from your upscale_models folder and runs it over your image - the "more pixels, source already sharp, cannot hallucinate anything" rung that is still, by share of discussion, gaining rather than dying.

It replaces the usual UpscaleModelLoader → ImageUpscaleWithModel pair, and adds the two things that pair never exposed: tile size and overlap.

How it works

Under the hood it's the standard ComfyUI path. The checkpoint is loaded as a state dict, with the module. prefix stripped off for the models that ship it (module.layers.0.residual_group... - the classic ESRGAN leftovers), then handed to the spandrel model loader. It estimates the memory the run will need and asks ComfyUI to free that much, then moves the model to your compute device.

The actual upscale is comfy.utils.tiled_scale: your image is cut into tile_size squares with overlap pixels of padding, each tile is upscaled, and the overlapping regions are blended back together. Tiling is what lets an upscale work on a card that couldn't hold the whole tensor.

Then the parachute: if the run raises an out-of-memory error, the node halves the tile size and tries again, repeating until the tile drops below 128, at which point it gives up and raises. You lose speed, not the run. The upscaled result is clamped to 0–1 and moved off the GPU, and the model is pushed back to CPU at the end.

One thing you may not expect: there's no scale factor widget, because there's nothing to set. The multiplier is baked into the model - 2x, 4x, whatever it was trained at.

Inputs and outputs

  • image - one image or a batch. Batch means the tiling and OOM behavior apply per frame.
  • model - dropdown of files in upscale_models. This is the folder your Manager-installed upscalers land in; 4x-UltraSharp, Remacri and NMKD-Siax_200k are the usual general-purpose picks.
  • tile_size - default 512, range 64–4096 in steps of 32.
  • overlap - default 32, range 0–4096. Keep it small relative to the tile - the default is about 6% of the tile - or you're recomputing the same pixels over and over.

Outputs: IMAGE, the upscaled result and nothing else, plus model_name - the model's filename without its extension, as a string. That second one is quietly useful: drop it in a filename suffix and every output tells you which upscaler made it.

Installing it

ComfyUI Manager → search Nifty Nodes, or:

cd ComfyUI/custom_nodes
git clone https://github.com/Stibo/comfyui-nifty-nodes

Restart. Extra dependencies for the whole pack are color-matcher and imageio-ffmpeg; this node doesn't use either. If you have no upscale models yet, download a .pth/.safetensors upscaler into ComfyUI/models/upscale_models/ - they're small, which is the point of this rung of the upscaling hierarchy.

Written against ComfyUI's V3 node API (so no NODE_CLASS_MAPPINGS in the source), tested on frontend 1.39 per the README, and part of the 2.2.0 batch of additions.

Where people get burned

  • "Upscale model must be a single-image model." The node refuses anything spandrel doesn't see as an image upscaler. Video upscale models and some exotic architectures belong in different nodes.
  • A bad tile/overlap ratio is slow, not broken. If the tile is large and the overlap is large, you're processing a lot of shared pixels. If you're on a small card, drop the tile to 256 and the overlap to 16 first - the OOM retry will get there eventually, but it wastes a failed attempt each time.
  • Big factor × big batch = big tensor. A 4x model on an eight-frame batch at 1024² is 4096² × 8 in memory at the end. If you're upscaling frames, feed them one at a time from the batch loop rather than all at once.
  • This rung cannot add detail. If the source is soft, blurry or compressed, an ESRGAN model amplifies what's there - including the mush. The KB's upscaling doc puts generative restorers (SeedVR2, SUPIR) and tile-ControlNet on the detail rung, with the explicit warning that they rewrite faces.
  • It reloads the model every execution. The loader isn't cached the way a separate loader node's would be, so across a 200-image loop you pay the state-dict load each time. Fine for one image; noticeable in a long batch.
Categorynifty/media

Inputs (4)

NameTypeDefaultDescription
imageIMAGE—
modelCOMBO0 options:
tile_sizeINT51264–4096—
overlapINT320–4096—

Outputs (2)

NameTypeDescription
IMAGEIMAGE—
model_nameSTRING—