Image Resize (Lanczos 3 non-separable) (rholdorf)
The downscale that doesn't leave diagonal ghosts
- image
- image
- width
- height
If you've ever downscaled a big image in ComfyUI and noticed subtle diagonal smearing or cross-hatch in the result, you've hit the classic separable-Lanczos artifact. Most resamplers - PIL, OpenCV, the default ComfyUI resizers - apply the Lanczos kernel twice, once along X and once along Y. That's fast, and it's fine for upscaling, but it gives edges a preferred direction. This node does Lanczos 3 differently: it evaluates the kernel on the true 2D radial distance sqrt(dx² + dy²), producing a circularly-symmetric filter with no diagonal bias. It's the same resampling mode Affinity Photo calls "Lanczos 3 (non-separable)", and it's visibly sharper on downscales.
How it works
The kernel is the radial form sinc(r) · sinc(r/a) with a = 3 lobes, summed over a 2D neighborhood around each output pixel. When downscaling, the kernel widens in input space - that's the anti-aliasing / band-limiting step that keeps you from getting moiré on fine texture. The implementation chunks output rows so memory stays bounded, and it runs in torch on whatever device your tensor is on, so no CPU/GPU shuffle.
The one rule to know before you use it: it never upscales. Give it an image that already fits inside max_width × max_height and you get it back untouched, with no resample happening.
Inputs and outputs
image- your input tensor.max_width/max_height- bounding box, both default 1024. The image scales to fit inside while preserving aspect ratio.
Outputs: image, plus width and height as INTs so you can feed the actual result size downstream.
Where it fits
This is the sharp downscale you reach for when the source is bigger than what your model generates at - prepping a 4K photo into a Flux workflow at 1MP, or sizing a reference image for img2img. The width/height outputs also make it a handy size-probe: connect them to the pack's Resolution Selector from Dimensions, which treats them as a reference pair and returns a clean generation resolution.
Honest trade-off: a radial kernel touches a lot more pixels than a separable one, so this is slower than the built-in ImageScale, especially on big sources. It's not the "drag a slider" node - it's the one you use when the last 10% of sharpness and artifact-free edges justify a few seconds of waiting. If you only need fast prep, the built-in resize is fine; if the output is going into a training set or a hero image, this one earns its keep.
Install
Part of the small comfyui_imgtools pack by rholdorf. Install once, get all 11 nodes:
cd ComfyUI/custom_nodes
git clone https://github.com/rholdorf/comfyui_imgtools
Restart ComfyUI afterwards. Or search "rholdorf" in ComfyUI Manager. No build step, no extra dependencies - it runs on the torch/numpy/Pillow ComfyUI already ships, so nothing to pip install. A young, low-star solo pack: MIT licensed, short readable code, no network calls, no model downloads. JS changes need only a browser refresh; Python changes need a restart.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| max_width | INT | 10241–16384 | — |
| max_height | INT | 10241–16384 | — |
| allow_upscale | BOOLEAN | false | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| width | INT | — |
| height | INT | — |