Nodes/comfyui-image-metrics/Image Quality Metrics (MSE/PSNR/SSIM/Hash)
ComfyUI Node

Image Quality Metrics (MSE/PSNR/SSIM/Hash)

Get real numbers on whether your workflow actually got better

By claudia2020shen·Created 6 months ago·Updated 6 months ago· 1
Image Quality Metrics (MSE/PSNR/SSIM/Hash)
  • image_original
  • image_generated
  • MSE
  • PSNR
  • SSIM
  • HASH_DISTANCE
  • REPORT_TEXT
hash_size8

You changed the CFG scale, bumped the hires upscaler, switched samplers - and now you're toggling between two images like a lab scientist holding up two color swatches, convinced one is better but unable to prove it to anyone, including yourself. This node is for that moment. It's an analysis node, not a generation one: feed it your original image and your generated image and it hands you four standard quality metrics plus a text report, all in one pass. MSE, PSNR, SSIM, and a perceptual hash distance. No more eyeballing; you get a number you can write down, log, and compare across runs.

It lives under Analysis/Image Metrics in the node menu, alongside the general ecosystem of "did that actually do anything" tools - the same spirit as rgthree's Image Comparer or a Power Puter that evaluates expressions mid-graph. ComfyUI already lets you chain anything; this is the piece that turns "which of these looks better" into "which of these scores better."

How it works

The mechanism is boring in the good way. The node takes both images as standard ComfyUI IMAGE tensors ([B, H, W, C], floats 0–1), converts them to 0–255 uint8, strips an alpha channel if present, and hands them to scikit-image. mean_squared_error gives you MSE, peak_signal_noise_ratio gives you PSNR in dB, and structural_similarity gives you SSIM - with a channel_axis/multichannel fallback so it works on both new and old scikit-image versions.

The hash is the part worth reading about. It runs imagehash.phash() on each image, then computes the Hamming distance with raw XOR plus bit-counting instead of relying on imagehash's hash1 - hash2. That's a deliberate fix, and a real one: the __sub__ operator has behaved inconsistently across imagehash versions, returning garbage or zero. Doing bin(xor).count('1') yourself is bulletproof. The kind of detail that tells you the author actually hit this bug.

The inputs and outputs that matter

There are only three inputs. image_original and image_generated are the two images to compare - wire one from a Load Image, the other from your sampler's VAE decode. The third, hash_size (default 8, range 4–64), controls the size of the perceptual hash. Default 8 means an 8×8 hash, so 64 bits, and HASH_DISTANCE runs 0–64. Smaller = faster and more forgiving; larger = more sensitive to small detail changes. You'll almost never touch it.

Outputs, all from the info_schema:

  • MSE (FLOAT) - mean squared error. 0 means pixel-identical; lower is better.
  • PSNR (FLOAT, dB) - higher is better; 30+ dB is the usual "fine for reconstruction" bar.
  • SSIM (FLOAT, 0–1) - closer to 1 is better; it tracks structure, brightness, and contrast rather than raw pixel error.
  • HASH_DISTANCE (INT) - bits that differ between the perceptual hashes. Lower means "looks more like the same image."
  • REPORT_TEXT (STRING) - a formatted report of all four, which you can wire into a Save Text node for a log you can grep later.

Installing it

ComfyUI Manager is the easy path - search for Image Quality Metrics (or just "image metrics") and install. Or manually:

cd ComfyUI/custom_nodes
git clone https://github.com/claudia2020shen/comfyui-image-metrics

then restart ComfyUI. On a Windows portable install, the pip step for any missing deps looks like:

.\python_embeded\python.exe -m pip install -r .\custom_nodes\comfyui-image-metrics\requirements.txt

The real dependencies are scikit-image, imagehash, numpy, and Pillow. requirements.txt also lists opencv-python-headless, but the source imports cv2 and never actually uses it - scikit-image and imagehash do all the work. If you already have opencv-python installed, adding opencv-python-headless into the same environment can cause a package-level clash, so if pip complains, install just scikit-image and imagehash and let it be. This is one of those rare packs where the README is accurate about everything that matters.

Where people get burned

Three gotchas, all real and all in the code. First: if the two images are pixel-identical, MSE is 0 and PSNR comes back as inf. Not a bug - that's the standard definition - but it looks alarming the first time you see it in a report. Second: the node only compares the first frame of each batch. If you feed it a batch of ten generated images, it silently measures only frame 0. Fine for single-image A/B; a trap if you were hoping for batch numbers. Third: if the images differ in resolution, it force-resizes the generated one to the original's size with bilinear interpolation. That makes the numbers meaningless for real upscaling comparisons - resize both sides outside the node first.

And the honest caveat: PSNR, SSIM, and even the perceptual hash measure fidelity to the original, not artistic quality. Two images generated from the same prompt with different seeds will score poorly against each other even if both look great, and a completely different but more beautiful image will score terribly. These metrics shine for reconstruction-style questions - did my hires pass preserve the source, did this upscaler mangle faces, is my img2img denoise still recognizable - not for "which seed is better art." For the former, this node is exactly right. For the latter, go back to squinting.

CategoryAnalysis/Image Metrics

Inputs (3)

NameTypeDefaultDescription
image_originalIMAGE
image_generatedIMAGE
hash_sizeINT84–64

Outputs (5)

NameTypeDescription
MSEFLOAT
PSNRFLOAT
SSIMFLOAT
HASH_DISTANCEINT
REPORT_TEXTSTRING