BlazeDiff vs OpenCV
The blazediff Python wheel is ~3.3x faster
than reading two files with OpenCV and running cv2.absdiff, and it answers the
question a visual test asks: how many pixels differ, and where. OpenCV gives you
a difference array, and the counting, thresholding and anti-aliasing handling are
up to you.
cv2.absdiff subtracts two arrays per channel and returns the absolute values.
That is a building block, not a test assertion: it has no perceptual threshold
and no anti-aliasing detection, so one-pixel font rendering changes count as
differences. blazediff.compare decodes both files, runs a YIQ color delta with
optional anti-aliasing detection, and can write a diff image.
At a glance
OpenCV (opencv-python) | BlazeDiff (blazediff) | |
|---|---|---|
| License | Apache-2.0 (wheels bundle LGPL FFmpeg and Qt) | MIT |
| Dependencies | numpy | None |
| Input | numpy arrays, after cv2.imread | File paths (PNG, JPEG, QOI) |
| Output | Per-channel difference array | match, diff_count, diff_percentage, diff image |
| Perceptual threshold | No, you threshold the array | Yes, YIQ delta with threshold |
| Anti-aliasing | No | Optional detection |
| Scope | Full computer vision library | Image diffing |
Benchmarks
The OpenCV side reads both files as grayscale, runs cv2.absdiff, and counts the
non-zero pixels. The BlazeDiff side reads both files in color and runs
compare. Decode is included for both.
vs opencv-python (cv2.absdiff)
TOTAL TIME Β· 34 FIXTURES Β· MEAN OF 25 RUNS Β· IO INCLUDED
Apple M1 Max Β· darwin/arm64 Β· Node v22.14.0 Β· rustc 1.97.1 (8bab26f4f 2026-07-14) Β· Python 3.11.14 Β· 2026-10-22 Β· 234b300-dirty Β· pnpm benchmark python-opencv
Per-fixture results (34)
| Fixture | OpenCV absdiff | BlazeDiff | BlazeDiff vs OpenCV absdiff |
|---|---|---|---|
| 4k/1 | 617ms | 222ms | 2.77x |
| 4k/1 (identical) | 782ms | 197ms | 3.97x |
| 4k/2 | 710ms | 263ms | 2.69x |
| 4k/2 (identical) | 695ms | 233ms | 2.98x |
| 4k/3 | 820ms | 275ms | 2.98x |
| 4k/3 (identical) | 790ms | 264ms | 2.99x |
| blazediff/1 | 4.94ms | 1.09ms | 4.52x |
| blazediff/1 (identical) | 4.50ms | 0.83ms | 5.40x |
| blazediff/2 | 4.14ms | 1.13ms | 3.67x |
| blazediff/2 (identical) | 4.01ms | 1.06ms | 3.80x |
| blazediff/3 | 44.59ms | 15.84ms | 2.81x |
| blazediff/3 (identical) | 44.49ms | 15.85ms | 2.81x |
| blazediff/4 | 30.53ms | 5.13ms | 5.95x |
| blazediff/4 (identical) | 30.23ms | 5.03ms | 6.01x |
| page/1 | 1.06s | 310ms | 3.43x |
| page/1 (identical) | 1.21s | 269ms | 4.48x |
| page/2 | 306ms | 109ms | 2.81x |
| page/2 (identical) | 288ms | 54.88ms | 5.25x |
| pixelmatch/1 | 3.46ms | 1.29ms | 2.68x |
| pixelmatch/1 (identical) | 4.40ms | 1.08ms | 4.06x |
| pixelmatch/2 | 1.16ms | 0.75ms | 1.55x |
| pixelmatch/2 (identical) | 1.07ms | 0.50ms | 2.16x |
| pixelmatch/3 | 3.40ms | 1.03ms | 3.29x |
| pixelmatch/3 (identical) | 3.96ms | 0.92ms | 4.29x |
| pixelmatch/4 | 7.76ms | 2.88ms | 2.69x |
| pixelmatch/4 (identical) | 6.00ms | 2.17ms | 2.76x |
| pixelmatch/5 | 1.84ms | 0.57ms | 3.22x |
| pixelmatch/5 (identical) | 1.89ms | 0.57ms | 3.29x |
| pixelmatch/6 | 2.64ms | 1.20ms | 2.21x |
| pixelmatch/6 (identical) | 4.48ms | 0.88ms | 5.10x |
| pixelmatch/7 | 1.89ms | 0.75ms | 2.52x |
| pixelmatch/7 (identical) | 2.06ms | 0.62ms | 3.33x |
| same/1 | 18.29ms | 3.60ms | 5.08x |
| same/1 (identical) | 19.38ms | 3.15ms | 6.16x |
| TOTAL | 7.53s | 2.27s | 3.32x |
Switching
# Before
import cv2
import numpy as np
a = cv2.imread("baseline.png", cv2.IMREAD_GRAYSCALE)
b = cv2.imread("current.png", cv2.IMREAD_GRAYSCALE)
changed = np.count_nonzero(cv2.absdiff(a, b))
# After
from blazediff import compare
result = compare("baseline.png", "current.png", "diff.png", threshold=0.1, antialiasing=True)
if not result.match and result.reason == "pixel-diff":
print(f"{result.diff_count} pixels differ")compare returns reason == "layout-diff" when the two images differ in size,
where cv2.absdiff raises an error.
When to keep OpenCV
- The diff is one step in a larger vision pipeline: alignment, contours, morphology or feature matching.
- You need formats that only OpenCV reads, such as TIFF, WebP or HDR.
- You need GPU processing.
Related
- Python API
- BlazeDiff vs pixelmatch, which covers the PyPI pixelmatch port
- Image difference analysis for regions instead of a count
- All benchmarks