The Video Upscaler Index
Nobody in this category publishes reproducible numbers. Every upscaler review, including the ones that rank first for the term, is a pair of screenshots and an opinion. This page is the opposite: a fixed set of measurements, the exact harness that produced them, and the sources they ran on — so you can disagree with us by running it yourself rather than by trusting a different screenshot.
It is published by the people who make Crisp, which is a reason to read the methodology before the results. So we have made that easy, and we have published the places Crisp comes off worse.
What is actually measured
Seven axes, computed on raw decoded frames with ffmpeg and pure Python — no reference library, no perceptual model, nothing that requires trusting a black box:
| Metric | What it means | Better |
|---|---|---|
sharpness | Mean variance-of-Laplacian over sampled grayscale frames — how much edge detail survives. | Higher |
levels | Distinct luma levels used, out of 256. A gradient that collapses to fewer levels is banding. | Higher |
flat_steps | Share of near-flat-region pixels sitting on a hard step — banding you can see in skies and walls. | Lower |
noise | Residual grain estimate on the output. | Context |
temporal | Median frame-to-frame luma change: the flicker floor. | Lower |
added_flicker | Output flicker minus source flicker. Shimmer the upscaler introduced. | Lower |
color_drift | Mean per-channel difference against the source. | Context |
added_flicker is the one that matters most and the one nobody reports. A
per-frame upscaler treats every frame independently, so it can invent slightly different
detail on each one — and a still frame looks superb while the moving clip crawls. That
temporal stability is Topaz's most-cited advantage. It is measurable, so we measure it.
Results — Crisp 0.1.284
M1 Ultra Mac Studio, 64GB, macOS 15. Each source processed for its first 3 seconds. Throughput is end-to-end frames per second including decode, upscale and encode.
| Source | Preset | Output | sharpness | levels | flat_steps | added_flicker | fps |
|---|---|---|---|---|---|---|---|
| Compressed 480p web video | Enhance | 2560×1440 | 428.5 | 223.8 | 6.70% | 0.00 | 5.3 |
| Compressed 480p web video | Low-light | 2560×1440 | 382.0 | 194.2 | 6.62% | 0.00 | 5.6 |
| Dark, noisy phone clip | Enhance | 3416×1920 | 161.9 | 141.8 | 14.78% | 0.00 | 3.7 |
| Dark, noisy phone clip | Low-light | 3416×1920 | 230.6 | 176.8 | 15.58% | +0.25 | 3.6 |
| Soft camcorder / DVD-era | Enhance | 2880×1920 | 417.0 | 218.6 | 13.61% | −0.50 | 4.4 |
| Soft camcorder / DVD-era | Low-light | 2880×1920 | 287.5 | 191.6 | 10.89% | −0.75 | 3.7 |
Where Crisp comes off worse
Three things in that table are not flattering, and they are the reason it is worth reading.
Low-light on already-noisy footage adds shimmer. On the dark, noisy phone
clip the Low-light preset scores +0.25 added flicker — it introduced temporal
instability that was not in the source. It buys real detail for that (sharpness 230.6 against
Enhance's 161.9), so it is a trade rather than a defect, but if your clip is noisy and
you care more about steadiness than detail, Enhance is the better choice on this evidence.
Banding is worse on the dark source. flat_steps is 14.78% and
15.58% on the noisy phone clip, against 6.7% on the compressed web video. Lifting shadows
stretches a small range of near-black values over a wider one, and the steps between them
become visible. That is inherent to brightening, not something we have solved.
Throughput is not fast. 3.6–5.6 fps means a 10-minute clip takes roughly an hour. Cloud services are faster because they are not running on your laptop. If you need a 90-minute film done this afternoon, this is the wrong tool.
Why there are no competitor columns
Because we have not run the competitors. Publishing a number for a tool we did not measure would make everything else here worthless.
The harness is built for head-to-head comparison — eval.py compare takes two
outputs of the same source and marks the per-axis winner plus their mutual SSIM. What it needs
is the other half: the same clip processed by Topaz Video AI, Aiarty, UniFab, HitPaw, AVCLabs,
or plain ffmpeg Lanczos. If you have a licence for one of those and are willing to run one
clip through it, send us the output and the source, and the result goes up here unchanged —
including if it beats Crisp. That is the whole point of keeping the method public.
Reproducing this
The harness is scripts/eval.py in the Crisp repository. It depends on ffmpeg
and the Python standard library, nothing else.
scripts/eval.py run SOURCE.mp4 --preset enhance --trim-end 3
scripts/eval.py score OUTPUT.mp4 --ref SOURCE.mp4
scripts/eval.py compare crisp_out.mp4 other_out.mp4 --ref SOURCE.mp4
Two honest caveats about the run above. The sources are synthesised —
ffmpeg test patterns degraded to imitate a compressed web video, a dark noisy phone clip and a
soft camcorder transfer — because they can be regenerated byte-for-byte by anyone, where our
own home footage cannot. Synthetic degradation is not the same as real degradation, and these
numbers should be read as an A/B instrument rather than as an absolute score. And every metric
here is content-dependent: comparing sharpness across two different sources tells
you about the sources, not the upscalers. Only same-source comparisons mean anything.
What this is for
Two things. It keeps us honest — a quality change that improves the picture in someone's
opinion but moves added_flicker the wrong way is a regression, and we can see
that before shipping it rather than after. And it gives anyone comparing upscalers something
better than screenshots to argue with.
It will be re-run and re-published as Crisp changes. Where a number gets worse, the number gets updated.