Skip to Content
New: ssim-native brings SSIM, MS-SSIM and Hitchhiker's to Node, and interpret-native ships diff interpretation on its own. Read more β†’
APIsblazediff-interpret

blazediff-interpret

Structured region analysis for image diffs. Given two images and a set of changed regions, it says what changed in each one β€” not just where.

Installation

# Cargo.toml [dependencies] blazediff-interpret = "5.4.0"

The crate name is blazediff-interpret; the library imports as blazediff_interpret.

Why it’s a separate crate

The classifier is deliberately independent of whatever found the regions. Three producers feed it:

ProducerChangeSourceHow it finds regions
blazediffDiffconnected components over a pixel-diff mask
blazediff-ssimScoreMapthresholding a local SSIM score map
your codeRegionsDOM rectangles, a JS-side diff, a crop list β€” anything

All three call the same function and get identical treatment; only the description of where differs. blazediff and blazediff-ssim are independent of each other, so a classifier living in either would be unreachable from the other. It sits below both, on blazediff-shared.

Usage

use blazediff_interpret::{interpret, ChangeSource}; // From a pixel diff β€” what `blazediff` does. let result = interpret(&expected, &actual, ChangeSource::Diff { output: &diff_image.data, diff_count: diff.diff_count, diff_percentage: diff.diff_percentage, })?; // From a similarity map β€” what `blazediff-ssim` does. let result = interpret(&expected, &actual, ChangeSource::ScoreMap { map: &outcome.map, width: outcome.map_width, height: outcome.map_height, floor: 0.99, })?; // From boxes you already have. let result = interpret(&expected, &actual, ChangeSource::Regions(&boxes))?; println!("{}", result.summary); for region in &result.regions { println!("{:?} at {} ({:.2}%)", region.change_type, region.position, region.percentage); }

Coarse regions are fine

A producer only has to know roughly where something changed. Before any statistic is computed, each supplied box is refined against the source pixels β€” every pixel whose YIQ delta falls below the noise floor is dropped β€” so shape, colour and gradient analysis stay per-pixel no matter how blocky the input was:

// An 8x8 change, described exactly and then quantized to a 16px grid. let exact = interpret(&a, &b, ChangeSource::Regions(&[BoundingBox { x: 16, y: 16, width: 8, height: 8 }]))?; let coarse = interpret(&a, &b, ChangeSource::Regions(&[BoundingBox { x: 16, y: 16, width: 16, height: 16 }]))?; assert_eq!(coarse.diff_count, exact.diff_count); // both 64

That is what makes an SSIM window map a usable region source: its grid is coarse, but the statistics derived from it are not.

diff_count therefore means the same thing on every path β€” actually-changed pixels, never windows. On a real fixture pair, SSIM-located regions report 792 changed pixels where the exact pixel diff reports 776.

API

ItemPurpose
interpretthe entry point: a ChangeSource in, a full InterpretResult out
ChangeSourceDiff (a pixel diff’s output + counts), ScoreMap (a similarity map), or Regions
regions_from_score_mapthreshold a lower-resolution score map into image-space regions
classify_region / classify_regionsclassify against a mask you already hold
detect_regionsconnected components over a boolean mask
extract_change_maskrecover a mask from an RGBA diff visualization
detect_shiftsthe shift-relabeling pass, for producers holding an exact mask
classify_severity, build_summarythe pooling steps, for custom pipelines

Regions arriving from a caller are validated: a box outside the image is an InterpretError::RegionOutOfBounds, not an out-of-bounds panic. That matters now that regions cross the wasm and N-API boundaries.

From JavaScript

One package wraps all of it β€” @blazediff/interpret-native:

import { interpret, interpretRegions } from "@blazediff/interpret-native"; // Regions from a pixel diff (default), or from a similarity map. const exact = await interpret("expected.png", "actual.png"); const loose = await interpret("expected.png", "actual.png", undefined, { source: "ms-ssim" }); // Regions you already know about. const given = await interpretRegions("expected.png", "actual.png", [ { x: 16, y: 16, width: 32, height: 32 }, ]);

What it classifies

Each region gets a change type, a shape, a position, a confidence, and the statistics behind them β€” colour delta, gradient/edge correlation, fill ratios, and the signals the classifier used. See INTERPRET.mdΒ  for the full algorithm.

Last updated on