cv2.pyrUp
It doubles the pixels, it does not sharpen the image
- src
- dstsize
- result
Half the people searching for this node want an upscaler. It isn't one. pyrUp inserts zeros between pixels and blurs the result, which is the mathematically correct inverse of a Gaussian pyramid reduce step - and it is soft, because the information that was thrown away in the reduce step isn't coming back. If your goal is "make this 1024px image 2048px and make it look good", the honest answer is a proper resampler or one of the neural upscalers that upscaling.md covers. If your goal is "rebuild a pyramid level I just modified", you're in the right place and there's no substitute.
What it's for
Pyramid reconstruction, almost exclusively. The pattern is: reduce an image a few times with cv2.pyrDown, modify one of those small levels (blend it, filter it, mask it), then climb back up with pyrUp, adding the level's detail back at each step. That's how Laplacian pyramids - and multi-band blending, and a whole family of tone-mapping and compositing techniques - work. The pack's CV Multi-Band Blend (Laplacian pyramid) subgraph is the concrete example: a three-level blend along a soft seam.
The second legitimate use is "I need this array twice as big and I'm going to blur it anyway" - an intermediate step in a processing chain, where nobody will ever look at it.
Mechanism and gotchas
Two things to keep straight:
- Sizes double, with rounding. The output is nominally
2×the input. The optionaldstsizeis aCV_TUPLE-(width, height), typed in place or wired from CV Tuple - and OpenCV only accepts a value within a pixel or two of exactly double. Leave it at(0, 0)and let the function pick. borderTypeis a dropdown, and onlyBORDER_DEFAULTis supported on this function. The widget offers more, the docs say one, so don't go fiddling - you'll get an OpenCV error rather than a subtly different image.
Inputs and outputs
src accepts IMAGE, MASK or NPARRAY. An IMAGE is unwrapped to uint8 BGR, frame 0 of a batch - this function is in the pack's per-frame batch set, so a batch comes back as a batch. LATENT links work too, and here's the interesting part from the node's own tooltip: a latent is processed in latent space, frame 0 becoming a float32 [H, W, C] array with values untouched. So you can double the spatial size of a latent with a blur-based resampler, which is a legitimate alternative to latent upscale-via-VAE in some pipelines.
The output result echoes the input's format: IMAGE in, IMAGE out, MASK in, MASK out, NPARRAY stays NPARRAY. Wire it into CV Array → Image if you went the latent route, or straight into the next pyramid step.
Install
Part of ComfyUI CV (bmad4ever) - a large pack: ~470 auto-generated cv2.* wrappers plus curated nodes, all built on ComfyUI's V3 node API. ComfyUI Manager, search comfyui_cv, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Restart afterwards. Python ≥ 3.12 required. No models.
Common issues
"It's blurry." Correct. Zero-stuffing plus a Gaussian is a blur; the aliasing-free part is the point. If you wanted a sharp 2×, this is the wrong node - and if you wanted invented detail, no resampler will do it either; that's the neural upscaler's job.
Mismatched sizes in a pyramid round trip. pyrDown then pyrUp is not bit-exact on odd dimensions. If you're subtracting the reconstructed image from the original to get a Laplacian band, crop to even dimensions first or your arrays won't line up and the subtraction will throw.
The node offers border types that error. Only BORDER_DEFAULT is supported; the dropdown is generic machinery shared by every wrapper in the pack.
"It changed only one frame of my clip." Shouldn't happen here - pyrUp is batch-aware. If you see it, check whether the other node in the chain (a mean-shift pass, for instance) is the one collapsing the batch.
Missing contrib nodes. As with the whole pack: a non-contrib opencv-python wheel installed over the contrib build silently removes the contrib submodules. tools/repair_opencv_contrib.py --check diagnoses it.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| src | COMFY_MATCHTYPE_V3 | input image. The image output(s) echo this input's format. A LATENT link is processed in latent space: frame 0 becomes a float32 [H,W,C] array (any channel count), values untouched. Arithmetic ops (add, multiply, etc.) also accept a full LATENT batch ({samples: [B,C,H,W]}) — the whole batch flows through when both inputs have the same batch size. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. | |
| dstsizeopt | CV_TUPLE | 0,0 | size of the output image. One value with 2 components (w, h) - it travels as a whole, so it cannot arrive half-connected. Wire it from 'CV Tuple' or type the components in place. |
| borderTypeopt | COMBO | BORDER_DEFAULT | Pixel extrapolation method, see #BorderTypes (only #BORDER_DEFAULT is supported) |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| result | COMFY_MATCHTYPE_V3 | Echoes the 'src' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY. |