1 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /* Fuzzy pixel test matching.
7 * This is designed to compare two layout test images (RGB 800x600) and manage
8 * to ignore the noise caused by the font renderers choosing slightly different
23 * 1x3 3x1 Morphological openings
30 * The result is that three different pixels in a row (vertically or
31 * horizontally) will cause the match to fail and the binary exits with code 1.
32 * Otherwise the binary exists with code 0.
34 * This requires leptonica to be installed. On Ubuntu do
35 * # apt-get install libleptonica libleptonica-dev libtiff4-dev
38 * % gcc -o fuzzymatch fuzzymatch.c -llept -ljpeg -ltiff -lpng -lz -lm -Wall -O2
41 * --highlight: write highlight.png which is a copy of the first image
42 * argument where the differing areas (after noise removal) are ringed
44 * --no-ignore-scrollbars: usually the rightmost 15px of the image are
45 * ignored to account for scrollbars. Use this flag to include them in
51 #include <leptonica/allheaders.h>
54 usage(const char *argv0
) {
55 fprintf(stderr
, "Usage: %s [--highlight] [--no-ignore-scrollbars] "
56 "[--output filename] "
57 "<input a> <input b>\n", argv0
);
62 main(int argc
, char **argv
) {
64 return usage(argv
[0]);
67 char ignore_scrollbars
= 1;
68 /* Default output filename; can be overridden by command line. */
69 const char *output_filename
= "highlight.png";
73 for (; argi
< argc
; ++argi
) {
74 if (strcmp("--highlight", argv
[argi
]) == 0) {
76 } else if (strcmp("--no-ignore-scrollbars", argv
[argi
]) == 0) {
77 ignore_scrollbars
= 0;
78 } else if (strcmp("--output", argv
[argi
]) == 0) {
79 if (argi
+ 1 >= argc
) {
80 fprintf(stderr
, "missing argument to --output\n");
83 output_filename
= argv
[++argi
];
90 return usage(argv
[0]);
92 PIX
*a
= pixRead(argv
[argi
]);
93 PIX
*b
= pixRead(argv
[argi
+ 1]);
96 fprintf(stderr
, "Failed to open %s\n", argv
[argi
]);
101 fprintf(stderr
, "Failed to open %s\n", argv
[argi
+ 1]);
105 if (pixGetWidth(a
) != pixGetWidth(b
) ||
106 pixGetHeight(a
) != pixGetHeight(b
)) {
107 fprintf(stderr
, "Inputs are difference sizes\n");
111 PIX
*delta
= pixAbsDifference(a
, b
);
112 pixInvert(delta
, delta
);
117 PIX
*deltagray
= pixConvertRGBToGray(delta
, 0, 0, 0);
120 PIX
*deltabinary
= pixThresholdToBinary(deltagray
, 254);
121 PIX
*deltabinaryclipped
;
122 const int clipwidth
= pixGetWidth(deltabinary
) - 15;
123 const int clipheight
= pixGetHeight(deltabinary
) - 15;
125 if (ignore_scrollbars
&& clipwidth
> 0 && clipheight
> 0) {
126 BOX
*clip
= boxCreate(0, 0, clipwidth
, clipheight
);
128 deltabinaryclipped
= pixClipRectangle(deltabinary
, clip
, NULL
);
130 pixDestroy(&deltabinary
);
132 deltabinaryclipped
= deltabinary
;
136 PIX
*hopened
= pixOpenBrick(NULL
, deltabinaryclipped
, 3, 1);
137 PIX
*vopened
= pixOpenBrick(NULL
, deltabinaryclipped
, 1, 3);
138 pixDestroy(&deltabinaryclipped
);
140 PIX
*opened
= pixOr(NULL
, hopened
, vopened
);
141 pixDestroy(&hopened
);
142 pixDestroy(&vopened
);
145 pixCountPixels(opened
, &count
, NULL
);
146 fprintf(stderr
, "%d\n", count
);
148 if (count
&& highlight
) {
149 PIX
*d1
= pixDilateBrick(NULL
, opened
, 7, 7);
150 PIX
*d2
= pixDilateBrick(NULL
, opened
, 3, 3);
153 pixPaintThroughMask(a
, d1
, 0, 0, 0xff << 24);
154 pixWrite(output_filename
, a
, IFF_PNG
);