Nodes/ComfyUI CV/cv2.PSNR
ComfyUI Node

cv2.PSNR

One number for 'how close are these two images', and how to read it

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
cv2.PSNR
  • src1
  • src2
  • float
◄R255.0000►

PSNR is the oldest, bluntest image-quality metric there is: mean squared error, converted to decibels. It's also the one you'll actually use, because it's deterministic, instant, and needs no checkerboard, no model and no opinion. Compare a denoised or upscaled frame against a reference and you get a number. Sweep eight settings and you get eight numbers. That's the use case - parameter sweeps where your eyes are lying to you because you've been staring at the same crop for twenty minutes.

What it computes

cv2.PSNR(src1, src2, R) returns 10 * log10(R² / MSE) for the two arrays. Higher is better, and the scale is logarithmic, so a 3 dB gap is roughly a doubling of error - not a small deal. Rules of thumb for 8-bit data:

  • Above ~40 dB - you'd have to hunt for the difference.
  • 30–40 dB - clearly visible, often still acceptable for a denoise comparison.
  • Below 30 dB - visible in a thumbnail.
  • inf - the two arrays are identical. Some code paths print a huge number instead of infinity; either way, identical means your "different" settings didn't change anything.

R is the maximum pixel value of your data and it is not cosmetic. It defaults to 255, which is right for the uint8 path. If you're comparing float arrays in 0–1, pass 1.0; get this wrong and your dB numbers are shifted by a constant ~48 dB, which is exactly the kind of error that makes a sweep look flat for no reason.

Inputs and outputs

src1 and src2 are the two images. Both take an IMAGE or MASK link directly or an NPARRAY; images are unwrapped to uint8 BGR. The two must be the same size and channel count - cv2 asserts on this, and it's the number-one error people hit, usually when one branch of a graph resized and the other didn't. Optional R, as above.

The output is a single FLOAT, and that's the whole node. That number is the interesting part of these graphs: wire it into a Display Any-style output, or - more usefully in this pack - feed your candidates through CV Quality Compare Batch, the curated node that scores a whole IMAGE batch against one reference, reports the best index, and can also give you SSIM, MSE and GMSD. If you're evaluating a batch rather than a pair, start there; cv2.PSNR is the pair-at-a-time primitive under it.

The candidate that matters is usually registered alongside it: PSNR won't tell you where two images differ. CV absdiff plus a false-colour pass will, and the pack's docs call that pairing out because it's how you see zipper artifacts and moiré from an upscale instead of arguing about a scalar.

Install

cv2.PSNR is one of ~470 auto-generated wrappers in ComfyUI CV by bmad4ever. 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. Requires Python ≥ 3.12 and a recent ComfyUI on the V3 node API. No models, obviously.

Common issues

The number is absurdly high or low. Check R. 255 against float data (or the reverse) shifts the whole scale.

"Sizes must match." Resize or crop first. Note that comparing a reference to an output that went through a VAE round-trip is fine - same resolution - but comparing against a different-resolution original isn't a metric, it's a category error.

Your metric disagrees with your eyes. That's normal and well documented. PSNR is a pixel-error metric; it happily rewards a blurry result over a crisp one with slightly more error, which is why the curated quality node offers SSIM and GMSD too. Use PSNR for sweeps within one pipeline, never as a universal judge of "which upscaler is better".

It only scores the first frame. This function isn't batch-aware in the pack, so a whole IMAGE batch collapses to frame 0. For a batch, use CV Quality Compare Batch.

Dependency friction on install. OpenCV 5 pulls numpy 2.x, and installs that pin numpy 1.x (insightface-era packs are the classic) will fight it. Sort out which side owns numpy before installing - this is the most common real-world complaint about any OpenCV-based pack.

Categoryimage/CV/low-level/cv2 P

Inputs (3)

NameTypeDefaultDescription
src1NPARRAY,IMAGE,MASKfirst input array. 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.
src2NPARRAY,IMAGE,MASKsecond input array of the same size as src1. 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.
RoptFLOAT255.0000-1e+38–1e+38the maximum pixel value (255 by default) Preset to the OpenCV default (255.0).

Outputs (1)

NameTypeDescription
floatFLOAT—