/go-pixmatch

The simpliest and smallest pixel-level image comparator.

Primary LanguageGoMIT LicenseMIT

pixmatch

Tests Go Report Card Go Reference

pixmatch is a pixel-level image comparison tool. Heavily inspired by pixelmatch, but rewritten in idiomatic Go, with zero dependencies, to speed up images comparison. Go pixmatch has support for PNG, GIF and JPEG formats. This tool also accurately detects anti-aliasing and may count it as a difference.

Example output

Format Expected Actual Difference
JPEG Hummingbird Hummingbird Hummingbird
GIF Landscape Landscape Landscape
PNG Form Form Form

Install

Library:

go get -u github.com/dknight/go-pixmatch

CLI:

go install github.com/dknight/go-pixmatch/cmd/pixmatch

Library usage

img1, err := NewImageFromPath("./samples/form-a.png")
if err != nil {
    log.Fatalln(err)
}
img2, err := NewImageFromPath("./samples/form-b.png")
if err != nil {
    log.Fatalln(err)
}

// Set some options.
options := NewOptions()
options.SetThreshold(0.05)
options.SetAlpha(0.5)
options.SetDiffColor(color.RGBA{0, 255, 128, 255})
// etc...

diff, err := img1.Compare(img2, options)
if err != nil {
    log.Fatalln(err)
}

fmt.Println(diff)

CLI usage

Usage:

pixmatch [options] image1.png image2.png

Run pixmatch -h for the list of supported options.

Example command:

pixmatch -o diff.png -aa -aacolor=00ffffff -mask ./samples/form-a.png ./samples/form-b.png

Compile binaries

Here is included simple script to compile binaries for some architectures. If you need something special, you can easily adopt it for your needs.

./scripts/makebin.sh

Testing and benchmark

Simple tests:

go test

Tests with update diff images:

UPDATEDIFFS=1 go test

Tests with full coverage:

# Terminal output
UPDATEDIFFS=1 go test -cover

# HTML output
UPDATEDIFFS=1 go test -coverprofile c.out && go tool cover -html c.out

Benchmark scripts (outputFile will be written in logs/ directory):

./scripts/benchmark.sh <outputFile> [iterations=10]

Later, it is easier to analyze it with a cool benchstat tool.

Using with WASM

WASI Preview 1 is very unstable, everything will be changed. This is an rough example how it might work with golang <= 1.12.6.

Compile the WASM file.

cd cmd/pixmatch
GOOS=wasip1 GOARCH=wasm go build -o pixmatch.wasm main.go

Node.js exanoke file (index.js)

const fs = require('node:fs');
const {WASI} = require('node:wasi');
const imgs = ['form-a.png', 'form-b.png'];
const virtulPathWasiPath = '';
const wasi = new WASI({
  version: 'preview1',
  args: [virtulPathWasiPath, '-mask', '-o', 'diff.png', ...imgs],
  preopens: {
    '/': __dirname,
  },
});

const wasmBuffer = fs.readFileSync('./pixmatch.wasm');
WebAssembly.instantiate(wasmBuffer, wasi.getImportObject()).then(
  (wasmModule) => {
    wasi.start(wasmModule.instance);
  }
);

Run node script:

NODE_NO_WARNINGS=1 node index.js

Known issues, bugs, flaws

  • Anti-aliasing detection algorithm can be improved (help appreciated).
  • Because of the nature of the JPEG format, comparing them is not a good idea or play with threshold parameter.
  • I have not tested this tool for 64-bit color images.

Credits

  • To provide 100% compatibility with pixelmatch. Original test files are borrowed from fixtures.
  • Hummingbird is taken from Wiki commons by San Diego Zoo.
  • Someone on the Pixilart platform created this pixel art girl.
  • Form screenshots are made using PureCSS framework.

Contribution

Any help is appreciated. Found a bug, typo, inaccuracy, etc.? Please do not hesitate to make a pull request or file an issue.

License

MIT 2023