Documentation ¶
Overview ¶
Package pixmatch is a pixel-level image comparison tool. Heavily inspired by [Pixelmatch.js], but rewritten in idiomatic Go to speed up images comparison.
Go pixmatch has support for PNG, GIF and JPEG formats. This tool also accurately detects anti-aliasing and may (or may not) count it as a difference. [Pixelmatch.js]: https://github.com/mapbox/pixelmatch
Author: Dmitri Smirnov (https://www.whoop.ee/)
License: MIT 2023
Example ¶
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) } options := NewOptions() options.SetThreshold(0.05) options.SetAlpha(0.5) options.SetDiffColor(color.RGBA{0, 255, 127, 255}) // etc... diff, err := img1.Compare(img2, options) if err != nil { log.Fatalln(err) } fmt.Println(diff)
Output: 4933
Index ¶
- Constants
- Variables
- func GetVersion() string
- func HexStringToColor(hexstr string) (*color.RGBA, error)
- type Color
- func (c Color) Blend(a float64) *Color
- func (c Color) BlendToGray(a float64) color.Color
- func (c Color) Equals(c2 *Color) bool
- func (c Color) I() float64
- func (c Color) Q() float64
- func (c Color) RGBA() (r, g, b, a uint32)
- func (c Color) String() string
- func (c Color) Y() float64
- func (c Color) YIQ() (float64, float64, float64)
- type Image
- func (img *Image) Antialiased(img2 *Image, pt image.Point) bool
- func (img *Image) Bytes() []byte
- func (img *Image) BytesPerColor() int
- func (img *Image) ColorDelta(img2 *Image, m, n int, onlyY bool) float64
- func (img *Image) Compare(img2 *Image, opts *Options) (int, error)
- func (img *Image) DimensionsEqual(img2 *Image) bool
- func (img *Image) Empty() bool
- func (img *Image) Identical(img2 *Image) bool
- func (img *Image) Load(rd io.Reader) (err error)
- func (img *Image) Position(p image.Point) int
- func (img *Image) SameNeighbors(pt image.Point, n int) bool
- func (img *Image) Save(wr io.Writer) (err error)
- func (img *Image) Size() int
- func (img *Image) Stride() int
- func (img *Image) Uint32() []uint32
- type Options
- func (opts *Options) SetAAColor(v color.Color) *Options
- func (opts *Options) SetAlpha(v float64) *Options
- func (opts *Options) SetDiffColor(v color.Color) *Options
- func (opts *Options) SetDiffColorAlt(v color.Color) *Options
- func (opts *Options) SetDiffMask(v bool) *Options
- func (opts *Options) SetIncludeAA(v bool) *Options
- func (opts *Options) SetKeepEmptyDiff(v bool) *Options
- func (opts *Options) SetOutput(v io.Writer) *Options
- func (opts *Options) SetThreshold(v float64) *Options
- type Version
Examples ¶
Constants ¶
const ( // ExitOk when the program exited successfully. ExitOk = 0 // ExitFSFail occurs when there is a problem with the file system. ExitFSFail = 100 // ExitEmptyImage occurs when the image (or both) are empty. ExitEmptyImage = 101 // ExitDimensionsNotEqual occurs when the images dimensions are not equal. ExitDimensionsNotEqual = 102 // ExitInvalidInput input parameters and/or flags are invalid. // Check usage. ExitInvalidInput = 103 // ErrMissingImage one or both images are missing. ExitMissingImage = 104 // ExitUnknownFormat if format of the image is not supported. ExitUnknownFormat = 105 // ExitUnknown all other failings. ExitUnknown = 199 )
Exit codes that are not defined in the BSD and Linux specifications.
const ( // YIQDeltaMax is the value of 35215. This is the maximum possible value // for the YIQ difference metric. // Read more about YIQ NTSC https://en.wikipedia.org/wiki/YIQ YIQDeltaMax = 35215 // DefaultFormat is used if format is not specified. DefaultFormat = FormatPNG FormatPNG = "png" FormatGIF = "gif" FormatJPEG = "jpeg" )
Common constants for pixmatch package.
Variables ¶
var ( //ErrDimensionsDoNotMatch represents an error when the dimensions of two //images do not match. ErrDimensionsDoNotMatch = errors.New("images dimensions do not match") // ErrImageIsEmpty occurs when one of the images, or both of them, // are empty. ErrImageIsEmpty = errors.New("one or both images are empty") // ErrCorruptedImage occurs when the data for the image is corrupted and // cannot be read or decoded. ErrCorruptedImage = errors.New("image data is corrupted") // ErrCannotWriteOutputDiff occurs when output cannot be written. ErrCannotWriteOutputDiff = errors.New("cannot write diff output") // ErrUnknownFormat occurs when the image format is not supported or // unknown. ErrUnknownFormat = errors.New("unknown image format") // ErrInvalidColorFormat occurs when user enter invalid color format. ErrInvalidColorFormat = errors.New("invalid color format") // ErrMissingImage occurs when one or both images are missing. ErrMissingImage = errors.New("one or both images are missing") )
Functions ¶
func HexStringToColor ¶
HexStringToColor converts hexadecimal string RRGGBBAA of color representation to image/color.RGBA. Input string are case-insensitive. Also strings can be prefixed with '0x' or '0X'.
Examples values are:
- FF000099
- ff00ff00
- 0xff00ff00
- #ffFF00ff00
Types ¶
type Color ¶
type Color struct {
R, G, B, A uint32
}
Color represents color structure and its components (R)ed, (G)reen, (B)lue, (Alpha). This is similar to image/color.Color from the standard library.
func (Color) Blend ¶
Blend is the procedure of blending the color with the alpha factor is known as blending.
func (Color) BlendToGray ¶
BlendToGray draws gray-scaled color with gray-scaled blending.
func (Color) RGBA ¶
RGBA returns Red, Green, Blue, Alpha channels similar to image/color.RGBA from the standard library, which always returns values as uint32 type.
type Image ¶
type Image struct { // Path to the image in file system. Path string // Format as a string like (PNG, JEPG, GIF). Format string // PixData contains data for colors as uint32 numbers. PixData []uint32 // BPC is the number of bytes per color. BPC int // Image is an embedded [image.Image] from the standard library. image.Image }
Image represents the image structure.
func NewImageFromPath ¶
NewImageFromPath creates a new image instance from the file system path.
Example ¶
img, err := NewImageFromPath("./samples/form-b.png") if err != nil { log.Fatalln(err) } fmt.Printf("%T\n", img)
Output: *pixmatch.Image
func (*Image) Antialiased ¶
Antialiased checks that the point is anti-aliased.
NOTE Probably, better algorithms are required here.
func (*Image) Bytes ¶
Bytes are the raw bytes of the pixel data. Reflection is never clear (https://go-proverbs.github.io/)
func (*Image) BytesPerColor ¶
BytesPerColor resolves the count of the bytes per color: 1, 2, 4, or 8.
func (*Image) ColorDelta ¶
ColorDelta is the squared YUV distance between colors at the pixel's position, returns a negative value if the img2 pixel is darker, and vice versa. If the argument onlyY is true, the only brightness level will be returned (Y component of the YIQ color space).
func (*Image) Compare ¶
Compare returns the number of different pixels between two comparable images. Zero is returned if no difference found.Returns negative values if something went wrong but in this case error also returned.
Looks like process row of the pixel in a single goroutine is the most performant way to do this, but I can mistake here.
Example ¶
options := NewOptions() options.SetThreshold(0.25) img1, _ := NewImageFromPath("./samples/form-a.png") img2, _ := NewImageFromPath("./samples/form-b.png") diff, _ := img1.Compare(img2, options) fmt.Println(diff)
Output: 1626
func (*Image) DimensionsEqual ¶
DimensionsEqual checks that the dimensions of the two images are equal.
Example ¶
img1, _ := NewImageFromPath("./samples/bird-a.jpg") img2, _ := NewImageFromPath("./samples/bird-c-small.jpg") samplesult := img1.DimensionsEqual(img2) fmt.Println(samplesult)
Output: false
func (*Image) Empty ¶
Empty checks that the image is empty or has a theoretical size of 0 pixels.
Example ¶
img, _ := NewImageFromPath("./samples/form-a.png") samplesult := img.Empty() fmt.Println(samplesult)
Output: false
func (*Image) Identical ¶
Identical determines whether or not images are identical at the byte level. This means that all the bytes of both images are the same.
Alternative possible ways to compare:
loops - the slowest (faster for smaller images) reflect.DeepEqual() - slower bytes.Compare() - better bytes.Equal() - even better
Example ¶
img1, _ := NewImageFromPath("./samples/form-a.png") img2, _ := NewImageFromPath("./samples/form-a.png") samplesult := img1.Identical(img2) fmt.Println(samplesult)
Output: true
func (*Image) Load ¶
Load reads data from the reader.
Example ¶
img := NewImage(0, 0, FormatGIF) img.Path = "./samples/gray16-b.png" fp, err := os.Open(img.Path) if err != nil { log.Fatalln(err) } defer fp.Close() if err := img.Load(fp); err != nil { log.Fatalln(err) } fmt.Printf("%T\n", img)
Output: *pixmatch.Image
func (*Image) Position ¶
Position is the position of the pixel in the array of bytes.
Formula
(y2-y1)*Stride + (x2-x1)*BPC
func (*Image) SameNeighbors ¶
SameNeighbors determines whether a pixel has n+ adjacent pixels that are the same color.
func (*Image) Size ¶
Size gives the total size of the image in pixels.
Example ¶
img, _ := NewImageFromPath("./samples/form-a.png") samplesult := img.Size() fmt.Println(samplesult)
Output: 51200
func (*Image) Stride ¶
Stride gets the stride from the image. The default value is 1. Reflection is never clear (https://go-proverbs.github.io/)
type Options ¶
type Options struct { // Output is structure where final image will be written. Output io.Writer // Threshold is the threshold of the maximum color delta. // Values range [0, 1.0]. Threshold float64 // Alpha is the alpha channel factor (multiplier). Values range [0, 1.0]. // NOTE it is interesting to experiment with overflow and underflow // ranges. Alpha float64 // IncludeAA sets anti-aliasing pixels as difference counts. IncludeAA bool // AAColor is the color to mark anti-aliasing pixels. AAColor color.Color // DiffColor is the color to highlight the differences. DiffColor color.Color // DiffColorAlt is the alternative difference color. Used to detect dark // and light differences between two images and set an alternative color if // required. DiffColorAlt color.Color // DiffMask sets to use mask, renders the differences without the original // image. DiffMask bool // KeepEmptyDiff removes empty diff files. KeepEmptyDiff bool }
Options is the structure that stores the settings for common comparisons.
func NewOptions ¶
func NewOptions() *Options
NewOptions creates a new Options instance. It is possible to use https://github.com/imdario/mergo in this case. Personally, I try to avoid dependencies whenever possible.
func (*Options) SetAAColor ¶
SetAAColor sets anti-aliased color to the options.
func (*Options) SetDiffColor ¶
SetDiffColor sets color of differences to the options.
func (*Options) SetDiffColorAlt ¶
SetDiffColorAlt sets color of alternative difference to the options.
func (*Options) SetDiffMask ¶
SetDiffMask sets difference mask to the options.
func (*Options) SetIncludeAA ¶
SetIncludeAA sets anti-aliasing to the options to counts anti-aliased pixels as differences.
func (*Options) SetKeepEmptyDiff ¶
SetKeepEmptyDiff sets difference mask to the options.
func (*Options) SetThreshold ¶
SetThreshold sets threshold to the options.
type Version ¶
Version is extremely simple semantic version structure. Personally, I wouldn't like to use external (especially heavy) dependencies to manage versions.
Check out https://pkg.go.dev/github.com/hashicorp/go-version for monstrous version management.