• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 /* Fuzzy pixel test matching.
6  *
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
9  * pixels.
10  *
11  *    A             B
12  *    |             |
13  *    |--->delta<---|
14  *           |
15  *           V
16  *          gray
17  *           |
18  *           V
19  *         binary
20  *           |
21  *    |-------------|
22  *    |             |
23  *   1x3           3x1   Morphological openings
24  *    |             |
25  *    |-----OR------|
26  *           |
27  *           V
28  *     count pixels
29  *
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.
33  *
34  * This requires leptonica to be installed. On Ubuntu do
35  *   # apt-get install libleptonica libleptonica-dev libtiff4-dev
36  *
37  * Build with:
38  *   % gcc -o fuzzymatch fuzzymatch.c -llept -ljpeg -ltiff -lpng -lz -lm -Wall -O2
39  *
40  * Options:
41  *   --highlight: write highlight.png which is a copy of the first image
42  *     argument where the differing areas (after noise removal) are ringed
43  *     in red
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
46  *     consideration
47  */
48 
49 #include <unistd.h>
50 #include <stdio.h>
51 #include <leptonica/allheaders.h>
52 
53 static int
usage(const char * argv0)54 usage(const char *argv0) {
55   fprintf(stderr, "Usage: %s [--highlight] [--no-ignore-scrollbars] "
56                   "[--output filename] "
57                   "<input a> <input b>\n", argv0);
58   return 1;
59 }
60 
61 int
main(int argc,char ** argv)62 main(int argc, char **argv) {
63   if (argc < 3)
64     return usage(argv[0]);
65 
66   char highlight = 0;
67   char ignore_scrollbars = 1;
68   /* Default output filename; can be overridden by command line. */
69   const char *output_filename = "highlight.png";
70 
71   int argi = 1;
72 
73   for (; argi < argc; ++argi) {
74     if (strcmp("--highlight", argv[argi]) == 0) {
75       highlight = 1;
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");
81         return 1;
82       }
83       output_filename = argv[++argi];
84     } else {
85       break;
86     }
87   }
88 
89   if (argc - argi < 2)
90     return usage(argv[0]);
91 
92   PIX *a = pixRead(argv[argi]);
93   PIX *b = pixRead(argv[argi + 1]);
94 
95   if (!a) {
96     fprintf(stderr, "Failed to open %s\n", argv[argi]);
97     return 1;
98   }
99 
100   if (!b) {
101     fprintf(stderr, "Failed to open %s\n", argv[argi + 1]);
102     return 1;
103   }
104 
105   if (pixGetWidth(a) != pixGetWidth(b) ||
106       pixGetHeight(a) != pixGetHeight(b)) {
107     fprintf(stderr, "Inputs are difference sizes\n");
108     return 1;
109   }
110 
111   PIX *delta = pixAbsDifference(a, b);
112   pixInvert(delta, delta);
113   if (!highlight)
114     pixDestroy(&a);
115   pixDestroy(&b);
116 
117   PIX *deltagray = pixConvertRGBToGray(delta, 0, 0, 0);
118   pixDestroy(&delta);
119 
120   PIX *deltabinary = pixThresholdToBinary(deltagray, 254);
121   PIX *deltabinaryclipped;
122   const int clipwidth = pixGetWidth(deltabinary) - 15;
123   const int clipheight = pixGetHeight(deltabinary) - 15;
124 
125   if (ignore_scrollbars && clipwidth > 0 && clipheight > 0) {
126     BOX *clip = boxCreate(0, 0, clipwidth, clipheight);
127 
128     deltabinaryclipped = pixClipRectangle(deltabinary, clip, NULL);
129     boxDestroy(&clip);
130     pixDestroy(&deltabinary);
131   } else {
132     deltabinaryclipped = deltabinary;
133     deltabinary = NULL;
134   }
135 
136   PIX *hopened = pixOpenBrick(NULL, deltabinaryclipped, 3, 1);
137   PIX *vopened = pixOpenBrick(NULL, deltabinaryclipped, 1, 3);
138   pixDestroy(&deltabinaryclipped);
139 
140   PIX *opened = pixOr(NULL, hopened, vopened);
141   pixDestroy(&hopened);
142   pixDestroy(&vopened);
143 
144   l_int32 count;
145   pixCountPixels(opened, &count, NULL);
146   fprintf(stderr, "%d\n", count);
147 
148   if (count && highlight) {
149     PIX *d1 = pixDilateBrick(NULL, opened, 7, 7);
150     PIX *d2 = pixDilateBrick(NULL, opened, 3, 3);
151     pixInvert(d2, d2);
152     pixAnd(d1, d1, d2);
153     pixPaintThroughMask(a, d1, 0, 0, 0xff << 24);
154     pixWrite(output_filename, a, IFF_PNG);
155   }
156 
157   return count > 0;
158 }
159