Image Resize
Resize with crop, pad or contain — and get the real numbers back
- image
- IMAGE
- WIDTH
- HEIGHT
Core's ImageScale does one thing: push an image to a width and height, aspect ratio be damned. That's fine until you need the image to fit a box without being squashed, and then you're doing mental arithmetic with ratios. 图像缩放 (QG_ImageResize) is the resizer for people who've been bitten by that - three fit modes, pixel or percentage measurement, and two integer outputs telling you what you actually ended up with.
How it works
Pick a measurement. On pixels, width and height are target pixel dimensions. On percentage, they're percentages of the original - 50 and 50 halves the image, 200 and 200 doubles it, and the two axes are computed independently, so mismatched percentages stretch.
Then fit decides how the image occupies the target box:
- crop - scale up until the image covers the box, then centre-crop the overflow. Output is exactly
width × height. This is the one you want for fixed-size model inputs where squashing would be visible. - pad - scale down until it fits entirely, then centre-pad the gaps. Output is exactly
width × height. Note the padding is black, not configurable - it's a zero tensor. - contain - scale to fit and stop there. Output can be smaller than the box on one axis, aspect intact. Nothing is cropped, nothing is padded.
Under the hood it calls ComfyUI's own common_upscale, so the resampling methods are the ones core exposes - nearest-exact, bilinear, area, bicubic, lanczos - and any improvement upstream makes to them shows up here without a pack update. Downscaling with area or lanczos is the sane default; nearest-exact is for pixel work where you want hard edges preserved.
Two shortcuts worth knowing because they're easy to misuse:
- Set either dimension to
0and it's derived from the other with the aspect ratio locked, which also forcescontain. Sowidth=1024, height=0means "1024 wide, however tall that makes it". - If both are
0, or if the requested size already matches the input, the image comes straight back untouched.
Inputs and outputs
image(required, IMAGE)measurement(required, enum:pixels/percentage)width,height(required, INT, 0–16384) - pixels or percent depending on measurement;0means derive.fit(required, enum:crop/pad/contain)method(required, enum) - resampling filter.- Outputs:
IMAGE, plusWIDTHandHEIGHTas INTs - the actual output dimensions, after everything.
Those two integers are the reason to prefer this over the core node in a big graph. Any subsequent step that needs to know the size - a blur radius that should scale, a crop that should centre, a paste that needs coordinates - can be wired instead of typed. The KB's plumbing notes are blunt about this being the whole point of value nodes: one source, fanned out, no silent mismatch when the image changes.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/live1617/ComfyUI-QG-tools
Full restart; the console prints [QG-tools] v0.9.2 加载完成 | 节点数 24. Manager search ComfyUI-QG-Tools covers the registry case. No pip dependencies - the requirements file is comments only, deliberately, so Manager runs no pip step.
Ported from rgthree-comfy (MIT), searchable as resize, scale, fit or 图像缩放.
Where people get burned
Black bars from pad. The fill is zeros. If you're padding to a 16:9 frame and expecting it to blend into a white banner, it won't. Pad first, then composite, or pad onto a colour yourself.
Byte-identical early return. If the requested size equals the input size the node skips the resize, so an "upscale to 1024" that's already 1024 does nothing at all - including not re-encoding the image through a filter. Usually what you want; occasionally the reason a chain looks like it skipped a step.
Percentages round. round(width * W / 100) on each axis. At small sizes, 33% of 500 is 165 and 33% of 501 is 165 too - two images from the same pipeline can land one pixel apart. If exact matching matters, use pixels.
Un-divisible output sizes. There's no rounding to multiples of 8 here. A contain-fit of an odd aspect ratio regularly produces sizes that a VAE will round for you on the way in, which is fine, but the WIDTH you're reading is the pre-VAE number. Don't feed it to a latent-shaping node and expect the sampler to agree.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | The image batch to resize. | |
| measurement | COMBO | pixels | pixels: the two numbers are the target size in pixels. percentage: they are a share of the source size (100 = unchanged). |
| width | INT | 00–16384 | Target width: pixels, or a percentage of the source when the measurement is percentage. 0 derives it from the height and keeps the aspect ratio. |
| height | INT | 00–16384 | Target height: pixels, or a percentage of the source when the measurement is percentage. 0 derives it from the width and keeps the aspect ratio. |
| fit | COMBO | crop | crop: scale up until the box is full, then trim the overflow evenly, so the output is exactly the target size. pad: scale down to fit inside, then paint the gaps black, so the output is exactly the target size. contain: scale down to fit inside and stop, so one side may end up smaller than the target. With either width or height at 0 the aspect ratio is locked and contain is what runs. |
| method | COMBO | Resampling filter, the same list core's Image Scale offers. nearest-exact keeps hard mask edges, lanczos is sharpest for downscale-to-upscale work, area is good for heavy shrinking. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | The resized images. |
| WIDTH | INT | The width the output actually came out at, after fitting. |
| HEIGHT | INT | The height the output actually came out at, after fitting. |