VQ Full-Reference Metrics (PSNR/SSIM)
The old-school trio that still catches things ML metrics miss
- images
- reference
- psnr
- ssim
- ciede2000
- summary
Every video codec engineer on Earth has used PSNR and SSIM since the 90s, and they're still the fastest way to answer a very specific question: "did my pipeline preserve the input?" This node bundles them with CIEDE2000 (a proper perceptual color-difference metric) into one box that compares your output against a reference and hands you three floats and a summary.
These are full-reference metrics - you need a ground truth, which makes them the right tool for upscaling, video-to-video, or motion-transfer workflows where a source video exists and you want to know how faithfully it survived. They're the wrong tool for text-to-video (there's no reference), and they're also not "quality" in the aesthetic sense: a pixel-perfect re-render scores great even if it's boring. What they measure is fidelity.
How it works
- PSNR derives from mean squared error between the two tensors - the log ratio of peak signal to the noise floor. Simple, brutal, and extremely sensitive to misalignment.
- SSIM compares local luminance, contrast, and structure through a sliding Gaussian window, which lines up much better with how humans judge similarity.
- CIEDE2000 converts both inputs to CIELAB color space and computes the perceptually-weighted color difference (ΔE), catching hue and saturation drift that PSNR/SSIM shrug at.
All three are computed across every frame and averaged. On a [T, H, W, 3] video input you get a single averaged number per metric.
Inputs and outputs
Required:
images(IMAGE) - your generated/processed frames.reference(IMAGE) - the ground truth.
The shapes must match exactly - same frame count, same resolution, same channels. If they don't, the node returns zeros and an error string in summary instead of a number. Alignment is on you.
Outputs:
psnr(FLOAT) - dB. Higher is better.ssim(FLOAT) - 0–1, higher is better.ciede2000(FLOAT) - perceptual color difference. Lower is better.summary(STRING) - one-line report of all three, handy for a text display.
Pack calibration: PSNR over 35 dB is good, 25–30 fair, under 25 poor. SSIM above 0.95 excellent, under 0.85 noticeable degradation. ΔE under 1.0 is imperceptible; over 5.0 is "skin turned green" territory.
Installing
It's one node of the ComfyUI-VideoQuality-Metrics pack:
cd ComfyUI/custom_nodes
git clone https://github.com/jajos12/ComfyUI-VideoQuality-Metrics
pip install -r ComfyUI-VideoQuality-Metrics/requirements.txt
Restart ComfyUI; it appears under the VideoQuality category. ComfyUI Manager can install the pack too.
Common issues
- PSNR tanks but the video looks fine. The classic trap: PSNR is hypersensitive to spatial shifts. A one-pixel misalignment can drop it 10 dB while your eyes see nothing wrong. If your upscaler or video-to-video pass shifts the frame even slightly, PSNR will punish it - read SSIM instead, it's far more forgiving and closer to human judgment.
- "Error: Image dimensions do not match." This is the shape-mismatch guard. Resize or trim frames so both inputs are identical, or crop the output to the reference's resolution.
- All three metrics are averages, so a single glitchy frame gets diluted. That's by design - use the temporal nodes for per-frame problems.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| reference | IMAGE | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| psnr | FLOAT | — |
| ssim | FLOAT | — |
| ciede2000 | FLOAT | — |
| summary | STRING | — |