15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2008 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Fuzzy pixel test matching. 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * This is designed to compare two layout test images (RGB 800x600) and manage 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * to ignore the noise caused by the font renderers choosing slightly different 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * pixels. 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * A B 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * | | 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * |--->delta<---| 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * | 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * V 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * gray 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * | 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * V 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * binary 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * | 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * |-------------| 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * | | 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1x3 3x1 Morphological openings 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * | | 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * |-----OR------| 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * | 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * V 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * count pixels 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The result is that three different pixels in a row (vertically or 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * horizontally) will cause the match to fail and the binary exits with code 1. 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Otherwise the binary exists with code 0. 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * This requires leptonica to be installed. On Ubuntu do 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * # apt-get install libleptonica libleptonica-dev libtiff4-dev 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Build with: 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * % gcc -o fuzzymatch fuzzymatch.c -llept -ljpeg -ltiff -lpng -lz -lm -Wall -O2 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Options: 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * --highlight: write highlight.png which is a copy of the first image 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * argument where the differing areas (after noise removal) are ringed 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * in red 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * --no-ignore-scrollbars: usually the rightmost 15px of the image are 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * ignored to account for scrollbars. Use this flag to include them in 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * consideration 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <unistd.h> 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h> 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <leptonica/allheaders.h> 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static int 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)usage(const char *argv0) { 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fprintf(stderr, "Usage: %s [--highlight] [--no-ignore-scrollbars] " 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "[--output filename] " 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "<input a> <input b>\n", argv0); 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 1; 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)main(int argc, char **argv) { 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (argc < 3) 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return usage(argv[0]); 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) char highlight = 0; 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) char ignore_scrollbars = 1; 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /* Default output filename; can be overridden by command line. */ 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const char *output_filename = "highlight.png"; 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int argi = 1; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (; argi < argc; ++argi) { 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (strcmp("--highlight", argv[argi]) == 0) { 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) highlight = 1; 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (strcmp("--no-ignore-scrollbars", argv[argi]) == 0) { 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ignore_scrollbars = 0; 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (strcmp("--output", argv[argi]) == 0) { 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (argi + 1 >= argc) { 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fprintf(stderr, "missing argument to --output\n"); 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 1; 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) output_filename = argv[++argi]; 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (argc - argi < 2) 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return usage(argv[0]); 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PIX *a = pixRead(argv[argi]); 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PIX *b = pixRead(argv[argi + 1]); 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!a) { 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fprintf(stderr, "Failed to open %s\n", argv[argi]); 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 1; 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!b) { 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fprintf(stderr, "Failed to open %s\n", argv[argi + 1]); 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 1; 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (pixGetWidth(a) != pixGetWidth(b) || 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixGetHeight(a) != pixGetHeight(b)) { 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fprintf(stderr, "Inputs are difference sizes\n"); 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 1; 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PIX *delta = pixAbsDifference(a, b); 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixInvert(delta, delta); 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!highlight) 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixDestroy(&a); 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixDestroy(&b); 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PIX *deltagray = pixConvertRGBToGray(delta, 0, 0, 0); 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixDestroy(&delta); 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PIX *deltabinary = pixThresholdToBinary(deltagray, 254); 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PIX *deltabinaryclipped; 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const int clipwidth = pixGetWidth(deltabinary) - 15; 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const int clipheight = pixGetHeight(deltabinary) - 15; 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (ignore_scrollbars && clipwidth > 0 && clipheight > 0) { 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOX *clip = boxCreate(0, 0, clipwidth, clipheight); 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) deltabinaryclipped = pixClipRectangle(deltabinary, clip, NULL); 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) boxDestroy(&clip); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixDestroy(&deltabinary); 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) deltabinaryclipped = deltabinary; 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) deltabinary = NULL; 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PIX *hopened = pixOpenBrick(NULL, deltabinaryclipped, 3, 1); 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PIX *vopened = pixOpenBrick(NULL, deltabinaryclipped, 1, 3); 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixDestroy(&deltabinaryclipped); 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PIX *opened = pixOr(NULL, hopened, vopened); 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixDestroy(&hopened); 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixDestroy(&vopened); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) l_int32 count; 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixCountPixels(opened, &count, NULL); 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fprintf(stderr, "%d\n", count); 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (count && highlight) { 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PIX *d1 = pixDilateBrick(NULL, opened, 7, 7); 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PIX *d2 = pixDilateBrick(NULL, opened, 3, 3); 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixInvert(d2, d2); 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixAnd(d1, d1, d2); 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixPaintThroughMask(a, d1, 0, 0, 0xff << 24); 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixWrite(output_filename, a, IFF_PNG); 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return count > 0; 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 159