Downscale Image to Megapixels (obvpm)
The cap that never upscales and never resamples what fits
- image
- image
A 4096×4096 phone screenshot going into a face-swap node that wanted 1 megapixel is how you turn a two-second job into a driver timeout. Most workflows don't want "bigger" or "smaller" - they want a ceiling: whatever comes in, don't hand anything downstream larger than this.
That's what this node is. It scales an image down so its total pixel count fits within a megapixel budget, keeps the aspect ratio, and - the part that matters - does nothing at all to images already under the budget. No resample, no re-encode, no quiet softening of an image that was already fine.
How it computes the resize
The maths is deliberate and worth knowing because you can reproduce it elsewhere: the target is megapixels × 1024 × 1024 pixels, and 1.0 means exactly 1024×1024 - the same convention as ComfyUI core's ImageScaleToTotalPixels, so the two numbers mean the same thing. If the source's width × height is at or under that, the tensor goes straight through untouched. Otherwise the scale factor is sqrt(target / current), applied to both sides, so a 4:3 source stays 4:3 and a 2000×1000 crop stays 2:1.
The resample itself goes through comfy.utils.common_upscale with the crop mode disabled, using whichever filter you picked.
The settings
megapixels- the maximum output size,0.01to128, default1.0. Larger images get scaled down to fit; smaller ones pass through.method- the resampling filter:lanczos(default),area,bicubic,bilinear,nearest-exact.image- optionalIMAGE. Leave it unconnected and the node outputsNone.
One output: image, the downscaled image (or None).
Which filter? lanczos is the sane default - it's the same family of plain interpolation that the community keeps falling back to for "I want fewer pixels, not different pixels", and it holds up against generative options whenever no new detail is wanted. area is the one to reach for when you're doing a large reduction and want the cleanest average (think 4x down). nearest-exact is for when you genuinely want hard edges and no blending - pixel-art-ish workflows, mattes, anything where an interpolated halo would be worse than aliasing.
Where it earns its place
- Front of a workflow. Cap incoming user images before a captioner, a segmentation model or a ControlNet preprocessor sees them. Preprocessors in particular get slow on ridiculous inputs and don't get better.
- Before an upscaler. The counterintuitive-but-documented move: Comfy Org's own upscaling handbook recommends dropping a soft source to around 0.35 MP before a SeedVR2 pass, on the reasoning that if there's no real detail at full resolution you should match the resolution to the actual sharpness and let the model rebuild. This node is the cheap way to do it inside an existing wire, without switching to a different scaling node type.
- As a conditioning gate. Wire it between a Load Image and the rest of the graph, so a 12 MP photo and a 1 MP screenshot both arrive at the same place at a sane size.
It pairs naturally with the pack's own Load Image & Crop, which has its own max_megapixels field doing the same job for the cropped output - use that one if you're already cropping, this one if you have an image from anywhere else.
Install
ComfyUI Manager → search comfyui-obvpm (the pack title), or:
cd ComfyUI/custom_nodes
git clone https://github.com/chanon/comfyui-obvpm
Restart. No extra Python dependencies, no model downloads. Category is obvpm/image. Every node id in the pack ends in (obvpm) as of 0.2.0, so a node-menu search for obvpm shows the lot.
Two things that catch people out
The image input is optional, and unconnected means None, not an error. If you build a workflow where the cap sits on a bypassed branch, you'll get a None downstream rather than a loud failure - that's intentional (it lets gates and switches route around it), but it's also a silent-empty-image bug if you didn't expect it.
This is a cap, not a target. A 512×512 image and a megapixels of 2.0 still outputs 512×512. It will not stretch anything up, ever, which is exactly the right behaviour and occasionally surprises someone who wanted ImageScaleToTotalPixels-with-upscaling semantics.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| megapixels | FLOAT | 1.000.01–128 | Maximum output size in megapixels (1.0 = 1024x1024 pixels). Larger images are scaled down to fit; smaller ones pass through untouched. |
| method | COMBO | lanczos | Resampling filter used when downscaling. |
| imageopt | IMAGE | The image to downscale. Leave unconnected to output None (bypass). |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | The image, scaled down if it exceeded the megapixel target. None when no image is connected. |