Skip to Content
New: codec-png-native and codec-png-wasm bring the blazediff-png codec to Node and WebAssembly, faster than spng and jSquash. Read more β†’
APIsblazediff

blazediff (Python)

PyO3 bindings to the same Rust core that powers @blazediff/core-native. 3-4x faster than odiffΒ , shipped as abi3 wheels for CPython β‰₯ 3.8 across macOS, Linux, and Windows.

View Detailed BenchmarksΒ 

Installation

pip install blazediff

uv pip install blazediff and poetry add blazediff work identically. Pre-built wheels are published for:

  • macOS ARM64 (Apple Silicon) & x86_64 (Intel)
  • Linux ARM64 & x86_64 (manylinux 2.17)
  • Windows ARM64 & x86_64

A single abi3-py38 wheel per platform serves every CPython β‰₯ 3.8 - no separate wheel per minor version.

Features

  • PNG, JPEG & QOI support - auto-detected by file extension
  • Paths or bytes - pass file paths or encoded bytes in, blazediff handles decode + compare + encode
  • SIMD-accelerated - NEON on ARM, SSE4.1 on x86
  • Block-based optimization - skips unchanged regions
  • Interpret mode - region detection, classification, severity scoring, human-readable summaries

API Reference

diff(a, b, output=None, *, ...)

Compares two images and optionally writes a diff image. It has the same options and result as every other BlazeDiff surface, in snake_case. All keyword arguments are optional.

from blazediff import diff result = diff("expected.png", "actual.png", "diff.png", threshold=0.1)

Parameters

ParameterTypeDefaultDescription
a, bstr | bytes-A file path or encoded PNG, JPEG or QOI bytes
outputstr | NoneNonePath for the diff image. It is written only when the images differ
thresholdfloat0.1Color difference threshold (0.0-1.0). Lower is stricter
antialiasingboolTrueDetect anti-aliased pixels and leave them out of the count
metricstr | None"yiq"Color metric: "yiq" or "oklab" (same counts as pixelmatch 8)
maskbytes | NoneNoneOne byte per pixel (width Γ— height). Non-zero pixels are skipped
windowint | NoneNoneAlso report the NΓ—N window with the most diff pixels
alphafloat0.1Opacity of the faded background in the diff image
diff_color, diff_color_alt, aa_colortuple[int, int, int] | Nonered, diff_color, yellowColors of different, darker and anti-aliased pixels
backgroundstr | None"faded""faded" or "transparent"
compressionint0PNG compression level (0-9, 0 = fastest, 9 = smallest)
qualityint90JPEG quality (1-100)

Return value

diff() returns a DiffResult. Its kind decides which fields are set:

kindFields
"compared"count, percentage, identical, and window (x, y, count) when window is set
"dimension-mismatch"a and b, each with width and height
"io-error"path (when known) and message

Invalid options raise ValueError.

Threshold guidelines:

  • 0.0 - Exact match only
  • 0.05 - Strict comparison
  • 0.1 - Default balanced comparison
  • 0.2 - Lenient comparison

Usage

Basic comparison

from blazediff import diff result = diff("expected.png", "actual.png", "diff.png", threshold=0.1) if result.kind == "compared": if result.identical: print("Images are identical!") else: print(f"{result.count} pixels differ ({result.percentage:.2f}%)") elif result.kind == "dimension-mismatch": print("Images have different dimensions") else: print(f"Could not read {result.path}: {result.message}")

pytest assertion

from blazediff import diff def test_homepage_matches_baseline(): result = diff("baseline.png", "rendered.png", "diff.png", threshold=0.05) assert result.kind == "compared" and result.identical, ( f"{result.count} pixels differ ({result.percentage:.2f}%)" )

Mixed formats

diff() infers format from the file extension. Inputs and outputs can mix freely:

# PNG inputs, QOI diff output (12x smaller diff files) diff("expected.png", "actual.png", "diff.qoi") # JPEG comparison diff("expected.jpg", "actual.jpg", "diff.jpg", quality=85)
FormatExtensionsNotes
PNG.pngLossless, transparency
JPEG.jpg, .jpegLossy, smaller files
QOI.qoiFast lossless, ideal for diff outputs (~12x smaller than PNG)

Interpret mode

Structured analysis β€” change regions, classification (Addition, Deletion, Shift, ContentChange, ColorChange, RenderingNoise), severity scoring and human-readable summaries β€” is not part of the Python wheel. It lives in the blazediff-interpret crate, reachable from a shell with its blazediff-interpret CLI:

blazediff-interpret expected.png actual.png --json

See Interpret example β†’ for the interactive demo.

Last updated on