Blame tools/jb2cmp/patterns.cpp

Packit df99a1
/* minidjvu - library for handling bilevel images with DjVuBitonal support
Packit df99a1
 *
Packit df99a1
 * patterns.c - pattern matching algorithm
Packit df99a1
 *
Packit df99a1
 * Copyright (C) 2005  Ilya Mezhirov
Packit df99a1
 *
Packit df99a1
 * This program is free software; you can redistribute it and/or modify
Packit df99a1
 * it under the terms of the GNU General Public License as published by
Packit df99a1
 * the Free Software Foundation; either version 2 of the License, or
Packit df99a1
 * (at your option) any later version.
Packit df99a1
 *
Packit df99a1
 * This program is distributed in the hope that it will be useful,
Packit df99a1
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit df99a1
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit df99a1
 * GNU General Public License for more details.
Packit df99a1
 *
Packit df99a1
 * You should have received a copy of the GNU General Public License
Packit df99a1
 * along with this program; if not, write to the Free Software
Packit df99a1
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Packit df99a1
 *
Packit df99a1
 * 
Packit df99a1
 * minidjvu is derived from DjVuLibre (http://djvu.sourceforge.net)
Packit df99a1
 * All over DjVuLibre there is a patent alert from LizardTech
Packit df99a1
 * which I guess I should reproduce (don't ask me what does this mean):
Packit df99a1
 * 
Packit df99a1
 *  ------------------------------------------------------------------
Packit df99a1
 * | DjVu (r) Reference Library (v. 3.5)
Packit df99a1
 * | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
Packit df99a1
 * | The DjVu Reference Library is protected by U.S. Pat. No.
Packit df99a1
 * | 6,058,214 and patents pending.
Packit df99a1
 * |
Packit df99a1
 * | This software is subject to, and may be distributed under, the
Packit df99a1
 * | GNU General Public License, either Version 2 of the license,
Packit df99a1
 * | or (at your option) any later version. The license should have
Packit df99a1
 * | accompanied the software or you may obtain a copy of the license
Packit df99a1
 * | from the Free Software Foundation at http://www.fsf.org .
Packit df99a1
 * |
Packit df99a1
 * | The computer code originally released by LizardTech under this
Packit df99a1
 * | license and unmodified by other parties is deemed "the LIZARDTECH
Packit df99a1
 * | ORIGINAL CODE."  Subject to any third party intellectual property
Packit df99a1
 * | claims, LizardTech grants recipient a worldwide, royalty-free, 
Packit df99a1
 * | non-exclusive license to make, use, sell, or otherwise dispose of 
Packit df99a1
 * | the LIZARDTECH ORIGINAL CODE or of programs derived from the 
Packit df99a1
 * | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU 
Packit df99a1
 * | General Public License.   This grant only confers the right to 
Packit df99a1
 * | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to 
Packit df99a1
 * | the extent such infringement is reasonably necessary to enable 
Packit df99a1
 * | recipient to make, have made, practice, sell, or otherwise dispose 
Packit df99a1
 * | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to 
Packit df99a1
 * | any greater extent that may be necessary to utilize further 
Packit df99a1
 * | modifications or combinations.
Packit df99a1
 * |
Packit df99a1
 * | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
Packit df99a1
 * | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
Packit df99a1
 * | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
Packit df99a1
 * | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Packit df99a1
 * +------------------------------------------------------------------
Packit df99a1
 */
Packit df99a1
Packit df99a1
/* This is `patterns.c', the unit that handles pattern matching.
Packit df99a1
 * Its task is only to compare pairs of images, not to classify a set of them.
Packit df99a1
 * And this has absolutely nothing to do with choosing a cross-coding prototype.
Packit df99a1
 */
Packit df99a1
Packit df99a1
#include "mdjvucfg.h"
Packit df99a1
#include "minidjvu.h"
Packit df99a1
#include <stdlib.h>
Packit df99a1
#include <string.h>
Packit df99a1
#include <assert.h>
Packit df99a1
Packit df99a1
Packit df99a1
/* Stuff for not using malloc in C++
Packit df99a1
 * (made by Leon Bottou; has no use in minidjvu,
Packit df99a1
 * but left here for potential DjVuLibre compatibility)
Packit df99a1
 */
Packit df99a1
#ifdef __cplusplus
Packit df99a1
# define MALLOC(Type)    new Type
Packit df99a1
# define FREE(p)         delete p
Packit df99a1
# define MALLOCV(Type,n) new Type[n]
Packit df99a1
# define FREEV(p)        delete [] p
Packit df99a1
#else
Packit df99a1
# define MALLOC(Type)    ((Type*)malloc(sizeof(Type)))
Packit df99a1
# define FREE(p)         do{if(p)free(p);}while(0)
Packit df99a1
# define MALLOCV(Type,n) ((Type*)malloc(sizeof(Type)*(n)))
Packit df99a1
# define FREEV(p)        do{if(p)free(p);}while(0)
Packit df99a1
#endif
Packit df99a1
Packit df99a1
Packit df99a1
#define SIGNATURE_SIZE 32
Packit df99a1
Packit df99a1
/* Mass center coordinates are stored in (1/MASS_CENTER_QUANT) pixels.
Packit df99a1
 * This leads to more precise alignment then using whole pixels.
Packit df99a1
 */
Packit df99a1
#define MASS_CENTER_QUANT 8
Packit df99a1
Packit df99a1
Packit df99a1
/* These are hand-tweaked parameters of this classifier. */
Packit df99a1
Packit df99a1
typedef struct
Packit df99a1
{
Packit df99a1
    double pithdiff_threshold;
Packit df99a1
    double softdiff_threshold;
Packit df99a1
    double shiftdiff1_threshold;
Packit df99a1
    double shiftdiff2_threshold;
Packit df99a1
    double shiftdiff3_threshold;
Packit df99a1
} Options;
Packit df99a1
Packit df99a1
static const double pithdiff_veto_threshold        = 23;
Packit df99a1
static const double softdiff_veto_threshold        = 100; /* that means off */
Packit df99a1
static const double shiftdiff1_veto_threshold      = 1000;
Packit df99a1
static const double shiftdiff2_veto_threshold      = 1500;
Packit df99a1
static const double shiftdiff3_veto_threshold      = 2000;
Packit df99a1
Packit df99a1
static const double size_difference_threshold = 10;
Packit df99a1
static const double mass_difference_threshold = 15;
Packit df99a1
Packit df99a1
static const double shiftdiff1_falloff        = .9;
Packit df99a1
static const double shiftdiff2_falloff        = 1;
Packit df99a1
static const double shiftdiff3_falloff        = 1.15;
Packit df99a1
Packit df99a1
static void interpolate(Options *opt, const double *v1, const double *v2,
Packit df99a1
                        int l, int r, int x)
Packit df99a1
{
Packit df99a1
    double w1 = ((double)(r - x)) / (r - l); /* weights */
Packit df99a1
    double w2 = 1 - w1;
Packit df99a1
    opt->pithdiff_threshold   = v1[0] * w1 + v2[0] * w2;
Packit df99a1
    opt->softdiff_threshold   = v1[1] * w1 + v2[1] * w2;
Packit df99a1
    opt->shiftdiff1_threshold = v1[2] * w1 + v2[2] * w2;
Packit df99a1
    opt->shiftdiff2_threshold = v1[3] * w1 + v2[3] * w2;
Packit df99a1
    opt->shiftdiff3_threshold = v1[4] * w1 + v2[4] * w2;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
/* Sets `aggression' for pattern matching.
Packit df99a1
 * Lower values are safer, bigger values produce smaller files.
Packit df99a1
 */
Packit df99a1
Packit df99a1
MDJVU_IMPLEMENT void mdjvu_set_aggression(mdjvu_matcher_options_t opt, int level)
Packit df99a1
{
Packit df99a1
    const double set200[5] = {7, 15, 60,  80, 170};
Packit df99a1
    const double set150[5] = {5, 13, 50,  70, 160};
Packit df99a1
    const double   set0[5] = {0,  0,  0,   0,   0};
Packit df99a1
Packit df99a1
    if (level < 0) level = 0;
Packit df99a1
Packit df99a1
    if (level > 150)
Packit df99a1
        interpolate((Options *) opt, set150, set200, 150, 200, level);
Packit df99a1
    else
Packit df99a1
        interpolate((Options *) opt, set0, set150, 0, 150, level);
Packit df99a1
}
Packit df99a1
Packit df99a1
/* ========================================================================== */
Packit df99a1
Packit df99a1
MDJVU_IMPLEMENT mdjvu_matcher_options_t mdjvu_matcher_options_create(void)
Packit df99a1
{
Packit df99a1
    mdjvu_matcher_options_t options = (mdjvu_matcher_options_t) MALLOC(Options);
Packit df99a1
    mdjvu_set_aggression(options, 100);
Packit df99a1
    return options;
Packit df99a1
}
Packit df99a1
Packit df99a1
MDJVU_IMPLEMENT void mdjvu_matcher_options_destroy(mdjvu_matcher_options_t opt)
Packit df99a1
{
Packit df99a1
    FREE((Options *) opt);
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
/* FIXME: maxint is maxint32 */
Packit df99a1
static const int32 maxint = ~(1 << (sizeof(int32) * 8 - 1));
Packit df99a1
typedef unsigned char byte;
Packit df99a1
Packit df99a1
typedef struct ComparableImageData
Packit df99a1
{
Packit df99a1
    byte **pixels; /* 0 - purely white, 255 - purely black (inverse to PGM!) */
Packit df99a1
    int32 width, height, mass;
Packit df99a1
    int32 mass_center_x, mass_center_y;
Packit df99a1
    byte signature[SIGNATURE_SIZE];  /* for shiftdiff 1 and 3 tests */
Packit df99a1
    byte signature2[SIGNATURE_SIZE]; /* for shiftdiff 2 test */
Packit df99a1
} Image;
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
/* Each image pair undergoes simple tests (dimensions and mass)
Packit df99a1
 * and at most five more advanced tests.
Packit df99a1
 * Each test may end up with three outcomes: veto (-1), doubt (0) and match(1).
Packit df99a1
 * Images are equivalent if and only if
Packit df99a1
 *     there was no `veto'
Packit df99a1
 *     and there was at least one `match'.
Packit df99a1
 */
Packit df99a1
Packit df99a1
Packit df99a1
/* We check whether images' dimensions are different
Packit df99a1
 *     no more than by size_difference_threshold percent.
Packit df99a1
 * Return value is usual: veto (-1) or doubt (0).
Packit df99a1
 * Mass checking was introduced by Leon Bottou.
Packit df99a1
 */
Packit df99a1
Packit df99a1
static int simple_tests(Image *i1, Image *i2)
Packit df99a1
{
Packit df99a1
    int32 w1 = i1->width, h1 = i1->height, m1 = i1->mass;
Packit df99a1
    int32 w2 = i2->width, h2 = i2->height, m2 = i2->mass;
Packit df99a1
Packit df99a1
    if (100.* w1 > (100.+ size_difference_threshold) * w2) return -1;
Packit df99a1
    if (100.* w2 > (100.+ size_difference_threshold) * w1) return -1;
Packit df99a1
    if (100.* h1 > (100.+ size_difference_threshold) * h2) return -1;
Packit df99a1
    if (100.* h2 > (100.+ size_difference_threshold) * h1) return -1;
Packit df99a1
    if (100.* m1 > (100.+ mass_difference_threshold) * m2) return -1;
Packit df99a1
    if (100.* m2 > (100.+ mass_difference_threshold) * m1) return -1;
Packit df99a1
Packit df99a1
    return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
#define USE_PITHDIFF 1
Packit df99a1
#define USE_SOFTDIFF 1
Packit df99a1
#define USE_SHIFTDIFF_1 1
Packit df99a1
#define USE_SHIFTDIFF_2 1
Packit df99a1
#define USE_SHIFTDIFF_3 1
Packit df99a1
Packit df99a1
Packit df99a1
/* Computing distance by comparing pixels {{{ */
Packit df99a1
Packit df99a1
#if USE_PITHDIFF || USE_SOFTDIFF
Packit df99a1
Packit df99a1
/* This function compares two images pixel by pixel.
Packit df99a1
 * The exact way to compare pixels is defined by two functions,
Packit df99a1
 *     compare_row and compare_with_white.
Packit df99a1
 * Both functions take pointers to byte rows and their length.
Packit df99a1
 *
Packit df99a1
 * Now images are aligned by mass centers.
Packit df99a1
 * Code needs some clarification, yes...
Packit df99a1
 */
Packit df99a1
static int32 distance_by_pixeldiff_functions_by_shift(Image *i1, Image *i2,
Packit df99a1
    int32 (*compare_row)(byte *, byte *, int32),
Packit df99a1
    int32 (*compare_with_white)(byte *, int32), int32 ceiling,
Packit df99a1
    int32 shift_x, int32 shift_y) /* of i1's coordinate system with respect to i2 */
Packit df99a1
{
Packit df99a1
    int32 w1 = i1->width, w2 = i2->width, h1 = i1->height, h2 = i2->height;
Packit df99a1
    int32 min_y = shift_y < 0 ? shift_y : 0;
Packit df99a1
    int32 right1 = shift_x + w1;
Packit df99a1
    int32 max_y_plus_1 = h2 > shift_y + h1 ? h2 : shift_y + h1;
Packit df99a1
    int32 i;
Packit df99a1
    int32 min_overlap_x = shift_x > 0 ? shift_x : 0;
Packit df99a1
    int32 max_overlap_x_plus_1 = w2 < right1 ? w2 : right1;
Packit df99a1
    int32 min_overlap_x_for_i1 = min_overlap_x - shift_x;
Packit df99a1
    int32 max_overlap_x_plus_1_for_i1 = max_overlap_x_plus_1 - shift_x;
Packit df99a1
    int32 overlap_length = max_overlap_x_plus_1 - min_overlap_x;
Packit df99a1
    int32 score = 0;
Packit df99a1
Packit df99a1
    if (overlap_length <= 0) return maxint;
Packit df99a1
Packit df99a1
    for (i = min_y; i < max_y_plus_1; i++)
Packit df99a1
    {
Packit df99a1
        int32 y1 = i - shift_y;
Packit df99a1
Packit df99a1
        /* calculate difference in the i-th line */
Packit df99a1
Packit df99a1
        if (i < 0 || i >= h2)
Packit df99a1
        {
Packit df99a1
            /* calculate difference of i1 with white */
Packit df99a1
            score += compare_with_white(i1->pixels[y1], w1);
Packit df99a1
        }
Packit df99a1
        else if (i < shift_y || i >= shift_y + h1)
Packit df99a1
        {
Packit df99a1
            /* calculate difference of i2 with white */
Packit df99a1
            score += compare_with_white(i2->pixels[i], w2);
Packit df99a1
        }
Packit df99a1
        else
Packit df99a1
        {
Packit df99a1
            /* calculate difference in a line where the bitmaps overlap */
Packit df99a1
            score += compare_row(i1->pixels[y1] + min_overlap_x_for_i1,
Packit df99a1
                                 i2->pixels[i] + min_overlap_x,
Packit df99a1
                                 overlap_length);
Packit df99a1
Packit df99a1
Packit df99a1
            /* calculate penalty for the left margin */
Packit df99a1
            if (min_overlap_x > 0)
Packit df99a1
                score += compare_with_white(i2->pixels[i], min_overlap_x);
Packit df99a1
            else
Packit df99a1
                score += compare_with_white(i1->pixels[y1], min_overlap_x_for_i1);
Packit df99a1
Packit df99a1
            /* calculate penalty for the right margin */
Packit df99a1
            if (max_overlap_x_plus_1 < w2)
Packit df99a1
            {
Packit df99a1
                score += compare_with_white(
Packit df99a1
                    i2->pixels[i] + max_overlap_x_plus_1,
Packit df99a1
                    w2 - max_overlap_x_plus_1);
Packit df99a1
            }
Packit df99a1
            else
Packit df99a1
            {
Packit df99a1
                score += compare_with_white(
Packit df99a1
                     i1->pixels[y1] + max_overlap_x_plus_1_for_i1,
Packit df99a1
                     w1 - max_overlap_x_plus_1_for_i1);
Packit df99a1
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
Packit df99a1
        if (score >= ceiling) return maxint;
Packit df99a1
    }
Packit df99a1
    return score;
Packit df99a1
}
Packit df99a1
Packit df99a1
static int32 distance_by_pixeldiff_functions(Image *i1, Image *i2,
Packit df99a1
    int32 (*compare_row)(byte *, byte *, int32),
Packit df99a1
    int32 (*compare_with_white)(byte *, int32), int32 ceiling)
Packit df99a1
{
Packit df99a1
    int32 w1, w2, h1, h2;
Packit df99a1
    int32 shift_x, shift_y; /* of i1's coordinate system with respect to i2 */
Packit df99a1
Packit df99a1
    /* make i1 to be narrower than i2 */
Packit df99a1
    if (i1->width > i2->width)
Packit df99a1
    {
Packit df99a1
        Image *img = i1;
Packit df99a1
        i1 = i2;
Packit df99a1
        i2 = img;
Packit df99a1
    }
Packit df99a1
Packit df99a1
    w1 = i1->width; h1 = i1->height;
Packit df99a1
    w2 = i2->width; h2 = i2->height; 
Packit df99a1
Packit df99a1
    /* (shift_x, shift_y) */
Packit df99a1
    /*     is what should be added to i1's coordinates to get i2's coordinates. */
Packit df99a1
    shift_x = (w2 - w2/2) - (w1 - w1/2); /* center favors right */
Packit df99a1
    shift_y = h2/2 - h1/2;               /* center favors top */
Packit df99a1
Packit df99a1
    shift_x = i2->mass_center_x - i1->mass_center_x;
Packit df99a1
    if (shift_x < 0)
Packit df99a1
        shift_x = (shift_x - MASS_CENTER_QUANT / 2) / MASS_CENTER_QUANT;
Packit df99a1
    else
Packit df99a1
        shift_x = (shift_x + MASS_CENTER_QUANT / 2) / MASS_CENTER_QUANT;
Packit df99a1
Packit df99a1
    shift_y = i2->mass_center_y - i1->mass_center_y;
Packit df99a1
    if (shift_y < 0)
Packit df99a1
        shift_y = (shift_y - MASS_CENTER_QUANT / 2) / MASS_CENTER_QUANT;
Packit df99a1
    else
Packit df99a1
        shift_y = (shift_y + MASS_CENTER_QUANT / 2) / MASS_CENTER_QUANT;
Packit df99a1
Packit df99a1
    return distance_by_pixeldiff_functions_by_shift(
Packit df99a1
        i1, i2, compare_row, compare_with_white, ceiling, shift_x, shift_y);
Packit df99a1
}
Packit df99a1
Packit df99a1
#endif
Packit df99a1
Packit df99a1
/* Computing distance by comparing pixels }}} */
Packit df99a1
/* inscribed framework penalty counting {{{ */
Packit df99a1
Packit df99a1
/* (Look at `frames.c' to see what it's all about) */
Packit df99a1
Packit df99a1
#if USE_PITHDIFF
Packit df99a1
Packit df99a1
/* If the framework of one letter is inscribed into another and vice versa,
Packit df99a1
 *     then those letters are probably equivalent.
Packit df99a1
 * That's the idea...
Packit df99a1
 * Counting penalty points here for any pixel
Packit df99a1
 *     that's framework in one image and white in the other.
Packit df99a1
 */
Packit df99a1
Packit df99a1
static int32 pithdiff_compare_row(byte *row1, byte *row2, int32 n)
Packit df99a1
{
Packit df99a1
    int32 i, s = 0;
Packit df99a1
    for (i = 0; i < n; i++)
Packit df99a1
    {
Packit df99a1
        int32 k = row1[i], l = row2[i];
Packit df99a1
        if (k == 255)
Packit df99a1
            s += 255 - l;
Packit df99a1
        else if (l == 255)
Packit df99a1
            s += 255 - k;
Packit df99a1
    }
Packit df99a1
    return s;
Packit df99a1
}
Packit df99a1
Packit df99a1
static int32 pithdiff_compare_with_white(byte *row, int32 n)
Packit df99a1
{
Packit df99a1
    int32 i, s = 0;
Packit df99a1
    for (i = 0; i < n; i++) if (row[i] == 255) s += 255;
Packit df99a1
    return s;
Packit df99a1
}
Packit df99a1
Packit df99a1
static int32 pithdiff_distance(Image *i1, Image *i2, int32 ceiling)
Packit df99a1
{
Packit df99a1
    return distance_by_pixeldiff_functions(i1, i2,
Packit df99a1
            &pithdiff_compare_row, &pithdiff_compare_with_white, ceiling);
Packit df99a1
}
Packit df99a1
Packit df99a1
static int pithdiff_equivalence(Image *i1, Image *i2, double threshold, int32 dpi)
Packit df99a1
{
Packit df99a1
    int32 perimeter = i1->width + i1->height + i2->width + i2->height;
Packit df99a1
    double ceiling = pithdiff_veto_threshold * dpi * perimeter / 100;
Packit df99a1
    int32 d = pithdiff_distance(i1, i2, (int32) ceiling);
Packit df99a1
    if (d == maxint) return -1;
Packit df99a1
    if (d < threshold * dpi * perimeter / 100) return 1;
Packit df99a1
    return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
#endif /* if USE_PITHDIFF */
Packit df99a1
Packit df99a1
/* inscribed framework penalty counting }}} */
Packit df99a1
/* soft penalty counting {{{ */
Packit df99a1
Packit df99a1
#if USE_SOFTDIFF
Packit df99a1
Packit df99a1
/* This test scores penalty points for pixels that are different in both images.
Packit df99a1
 * Since every black pixel has a rating of importance,
Packit df99a1
 *     the penalty for a pair of corresponding pixels, one black, one white,
Packit df99a1
 *         is equal to the rating of the black pixel.
Packit df99a1
 */
Packit df99a1
Packit df99a1
static int32 softdiff_compare_row(byte *row1, byte *row2, int32 n)
Packit df99a1
{
Packit df99a1
    int32 i, s = 0;
Packit df99a1
    for (i = 0; i < n; i++)
Packit df99a1
    {
Packit df99a1
        if (!row1[i])
Packit df99a1
            s += row2[i];
Packit df99a1
        else if (!row2[i])
Packit df99a1
            s += row1[i];
Packit df99a1
    }
Packit df99a1
    return s;
Packit df99a1
}
Packit df99a1
Packit df99a1
static int32 softdiff_compare_with_white(byte *row, int32 n)
Packit df99a1
{
Packit df99a1
    int32 i, s = 0;
Packit df99a1
    for (i = 0; i < n; i++) s += row[i];
Packit df99a1
    return s;
Packit df99a1
}
Packit df99a1
Packit df99a1
static int32 softdiff_distance(Image *i1, Image *i2, int32 ceiling)
Packit df99a1
{
Packit df99a1
    return distance_by_pixeldiff_functions(i1, i2,
Packit df99a1
            &softdiff_compare_row, &softdiff_compare_with_white, ceiling);
Packit df99a1
}
Packit df99a1
Packit df99a1
static int softdiff_equivalence(Image *i1, Image *i2, double threshold, int32 dpi)
Packit df99a1
{
Packit df99a1
    int32 perimeter = i1->width + i1->height + i2->width + i2->height;
Packit df99a1
    double ceiling = softdiff_veto_threshold * dpi * perimeter / 100;
Packit df99a1
    int32 d = softdiff_distance(i1, i2, (int32) ceiling);
Packit df99a1
    if (d == maxint) return -1;
Packit df99a1
    if (d < threshold * dpi * perimeter / 100) return 1;
Packit df99a1
    return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
#endif /* if USE_SOFTDIFF */
Packit df99a1
Packit df99a1
/* soft penalty counting }}} */
Packit df99a1
/* shift signature comparison {{{ */
Packit df99a1
Packit df99a1
/* Just finding the square of a normal Euclidean distance between vectors
Packit df99a1
 * (but with falloff)
Packit df99a1
 */
Packit df99a1
Packit df99a1
#if USE_SHIFTDIFF_1 || USE_SHIFTDIFF_2 || USE_SHIFTDIFF_3
Packit df99a1
static int shiftdiff_equivalence(byte *s1, byte *s2, double falloff, double veto, double threshold)
Packit df99a1
{
Packit df99a1
    int i, delay_before_falloff = 1, delay_counter = 1;
Packit df99a1
    double penalty = 0;
Packit df99a1
    double weight = 1;
Packit df99a1
Packit df99a1
    for (i = 1; i < SIGNATURE_SIZE; i++) /* kluge: ignores the first byte */
Packit df99a1
    {
Packit df99a1
        int difference = s1[i] - s2[i];
Packit df99a1
        penalty += difference * difference * weight;
Packit df99a1
        if (!--delay_counter)
Packit df99a1
        {
Packit df99a1
            weight *= falloff;
Packit df99a1
            delay_counter = delay_before_falloff <<= 1;
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
Packit df99a1
    if (penalty >= veto * SIGNATURE_SIZE) return -1;
Packit df99a1
    if (penalty <= threshold * SIGNATURE_SIZE) return 1;
Packit df99a1
    return 0;
Packit df99a1
}
Packit df99a1
#endif
Packit df99a1
/* shift signature comparison }}} */
Packit df99a1
Packit df99a1
#ifndef NO_MINIDJVU
Packit df99a1
mdjvu_pattern_t mdjvu_pattern_create(mdjvu_bitmap_t bitmap)
Packit df99a1
{
Packit df99a1
    int32 w = mdjvu_bitmap_get_width(bitmap);
Packit df99a1
    int32 h = mdjvu_bitmap_get_height(bitmap);
Packit df99a1
    mdjvu_pattern_t pattern;
Packit df99a1
    byte **pixels = mdjvu_create_2d_array(w, h);
Packit df99a1
    mdjvu_bitmap_unpack_all(bitmap, pixels);
Packit df99a1
    pattern = mdjvu_pattern_create_from_array(pixels, w, h);
Packit df99a1
    mdjvu_destroy_2d_array(pixels);
Packit df99a1
    return pattern;
Packit df99a1
}
Packit df99a1
#endif
Packit df99a1
Packit df99a1
/* Finding mass center {{{ */
Packit df99a1
Packit df99a1
static void get_mass_center(unsigned char **pixels, int32 w, int32 h,
Packit df99a1
                     int32 *pmass_center_x, int32 *pmass_center_y)
Packit df99a1
{
Packit df99a1
    double x_sum = 0, y_sum = 0, mass = 0;
Packit df99a1
    int32 i, j;
Packit df99a1
Packit df99a1
    for (i = 0; i < h; i++)
Packit df99a1
    {
Packit df99a1
        unsigned char *row = pixels[i];
Packit df99a1
        for (j = 0; j < w; j++)
Packit df99a1
        {
Packit df99a1
            unsigned char pixel = row[j];
Packit df99a1
            x_sum += pixel * j;
Packit df99a1
            y_sum += pixel * i;
Packit df99a1
            mass  += pixel;
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
Packit df99a1
    *pmass_center_x = (int32) (x_sum * MASS_CENTER_QUANT / mass);
Packit df99a1
    *pmass_center_y = (int32) (y_sum * MASS_CENTER_QUANT / mass);
Packit df99a1
}
Packit df99a1
Packit df99a1
/* Finding mass center }}} */
Packit df99a1
Packit df99a1
Packit df99a1
MDJVU_IMPLEMENT mdjvu_pattern_t mdjvu_pattern_create_from_array(byte **pixels, int32 w, int32 h)/*{{{*/
Packit df99a1
{
Packit df99a1
    int32 i, mass;
Packit df99a1
    Image *img = MALLOC(Image);
Packit df99a1
    byte *pool = MALLOCV(byte, w * h);
Packit df99a1
    memset(pool, 0, w * h);
Packit df99a1
Packit df99a1
    img->width = w;
Packit df99a1
    img->height = h;
Packit df99a1
Packit df99a1
    img->pixels = MALLOCV(byte *, h);
Packit df99a1
    for (i = 0; i < h; i++)
Packit df99a1
        img->pixels[i] = pool + i * w;
Packit df99a1
Packit df99a1
    mass = 0;
Packit df99a1
    for (i = 0; i < h; i++)
Packit df99a1
    {
Packit df99a1
        int32 j;
Packit df99a1
        for (j = 0; j < w; j++)
Packit df99a1
            if (pixels[i][j])
Packit df99a1
            {
Packit df99a1
                img->pixels[i][j] = 255; /* i don't remember what for */
Packit df99a1
                mass += 1;
Packit df99a1
            }
Packit df99a1
    }
Packit df99a1
    img->mass = mass;
Packit df99a1
Packit df99a1
    mdjvu_soften_pattern(img->pixels, img->pixels, w, h);
Packit df99a1
    get_mass_center(img->pixels, w, h,
Packit df99a1
                    &img->mass_center_x, &img->mass_center_y);
Packit df99a1
    mdjvu_get_gray_signature(img->pixels, w, h,
Packit df99a1
                             img->signature, SIGNATURE_SIZE);
Packit df99a1
    mdjvu_get_black_and_white_signature(img->pixels, w, h,
Packit df99a1
                                        img->signature2, SIGNATURE_SIZE);
Packit df99a1
    return (mdjvu_pattern_t) img;
Packit df99a1
}/*}}}*/
Packit df99a1
Packit df99a1
/* Requires `opt' to be non-NULL */
Packit df99a1
static int compare_patterns(mdjvu_pattern_t ptr1, mdjvu_pattern_t ptr2,/*{{{*/
Packit df99a1
                            int32 dpi, Options *opt)
Packit df99a1
Packit df99a1
{
Packit df99a1
    Image *i1 = (Image *) ptr1, *i2 = (Image *) ptr2;
Packit df99a1
    int i, state = 0; /* 0 - unsure, 1 - equal unless veto */
Packit df99a1
Packit df99a1
    if (simple_tests(i1, i2)) return -1;
Packit df99a1
Packit df99a1
    #if USE_SHIFTDIFF_1
Packit df99a1
        i = shiftdiff_equivalence(i1->signature, i2->signature,
Packit df99a1
            shiftdiff1_falloff, shiftdiff1_veto_threshold, opt->shiftdiff1_threshold);
Packit df99a1
        if (i == -1) return -1;
Packit df99a1
        state |= i;
Packit df99a1
    #endif
Packit df99a1
Packit df99a1
    #if USE_SHIFTDIFF_2
Packit df99a1
        i = shiftdiff_equivalence(i1->signature2, i2->signature2,
Packit df99a1
            shiftdiff2_falloff, shiftdiff2_veto_threshold, opt->shiftdiff2_threshold);
Packit df99a1
        if (i == -1) return -1;
Packit df99a1
        state |= i;
Packit df99a1
    #endif
Packit df99a1
Packit df99a1
    #if USE_SHIFTDIFF_3
Packit df99a1
        i = shiftdiff_equivalence(i1->signature, i2->signature,
Packit df99a1
            shiftdiff3_falloff, shiftdiff3_veto_threshold, opt->shiftdiff3_threshold);
Packit df99a1
        if (i == -1) return -1;
Packit df99a1
        state |= i;
Packit df99a1
    #endif
Packit df99a1
Packit df99a1
    #if USE_PITHDIFF
Packit df99a1
        i = pithdiff_equivalence(i1, i2, opt->pithdiff_threshold, dpi);
Packit df99a1
        if (i == -1) return 0; /* pithdiff has no right to veto at upper level */
Packit df99a1
        state |= i;
Packit df99a1
    #endif
Packit df99a1
Packit df99a1
    #if USE_SOFTDIFF
Packit df99a1
        i = softdiff_equivalence(i1, i2, opt->softdiff_threshold, dpi);
Packit df99a1
        if (i == -1) return 0;  /* softdiff has no right to veto at upper level */
Packit df99a1
        state |= i;
Packit df99a1
    #endif
Packit df99a1
Packit df99a1
    return state;
Packit df99a1
}/*}}}*/
Packit df99a1
Packit df99a1
MDJVU_IMPLEMENT int mdjvu_match_patterns(mdjvu_pattern_t ptr1, mdjvu_pattern_t ptr2,
Packit df99a1
                    int32 dpi, mdjvu_matcher_options_t options)
Packit df99a1
{
Packit df99a1
    Options *opt;
Packit df99a1
    int result;
Packit df99a1
    if (options)
Packit df99a1
        opt = (Options *) options;
Packit df99a1
    else
Packit df99a1
        opt = (Options *) mdjvu_matcher_options_create();
Packit df99a1
Packit df99a1
    result = compare_patterns(ptr1, ptr2, dpi, opt);
Packit df99a1
Packit df99a1
    if (!options)
Packit df99a1
        mdjvu_matcher_options_destroy((mdjvu_matcher_options_t) opt);
Packit df99a1
Packit df99a1
    return result;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
MDJVU_IMPLEMENT void mdjvu_pattern_destroy(mdjvu_pattern_t p)/*{{{*/
Packit df99a1
{
Packit df99a1
    Image *img = (Image *) p;
Packit df99a1
    FREEV(img->pixels[0]);
Packit df99a1
    FREEV(img->pixels);
Packit df99a1
    FREE(img);
Packit df99a1
}/*}}}*/