Blame tools/jb2cmp/frames.cpp

Packit df99a1
/* minidjvu - library for handling bilevel images with DjVuBitonal support
Packit df99a1
 *
Packit df99a1
 * frames.c - extracting frameworks and calculating "importance rating"
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
/* Frameworks are funny things...
Packit df99a1
 * The algorithm is to be commented yet.
Packit df99a1
 * Here's a picture illustrating what is a frame
Packit df99a1
 * (view with monospace font):
Packit df99a1
 *
Packit df99a1
 *  Original letter:        Its framework:
Packit df99a1
 *
Packit df99a1
  .....@@@@@@@@........ .....................
Packit df99a1
  ...@@@@@@@@@@@@...... ......@@@@@..........
Packit df99a1
  ..@@@@@@@@@@@@@@..... .....@@...@@@........
Packit df99a1
  ..@@@@@...@@@@@@@.... ....@@......@@.......
Packit df99a1
  ..@@@@.....@@@@@@.... ....@........@.......
Packit df99a1
  .@@@@@.....@@@@@@.... ....@........@.......
Packit df99a1
  .@@@@@.....@@@@@@.... ....@........@.......
Packit df99a1
  ..@@@@.....@@@@@@.... ....@........@.......
Packit df99a1
  ..........@@@@@@@.... .............@.......
Packit df99a1
  .......@@@@@@@@@@.... .............@.......
Packit df99a1
  .....@@@@@@@@@@@@.... ........@@@@@@.......
Packit df99a1
  ...@@@@@@@@@@@@@@.... ......@@@....@.......
Packit df99a1
  ..@@@@@@@..@@@@@@.... .....@@......@.......
Packit df99a1
  .@@@@@@....@@@@@@.... ...@@@.......@.......
Packit df99a1
  .@@@@@.....@@@@@@.... ...@.........@.......
Packit df99a1
  @@@@@......@@@@@@.... ..@@.........@.......
Packit df99a1
  @@@@@......@@@@@@.... ..@..........@.......
Packit df99a1
  @@@@@.....@@@@@@@.... ..@..........@.......
Packit df99a1
  @@@@@@....@@@@@@@.@@@ ..@@.........@.......
Packit df99a1
  .@@@@@@@@@@@@@@@@@@@@ ...@@.....@@@@@......
Packit df99a1
  .@@@@@@@@@@@@@@@@@@@@ ....@@..@@@...@@@@@..
Packit df99a1
  ..@@@@@@@@@.@@@@@@@@. .....@@@@............
Packit df99a1
  ....@@@@@....@@@@@... .....................
Packit df99a1
Packit df99a1
 * A letter is converted into grayshades,
Packit df99a1
 *   and a frame is the set of its purely black pixels after the transformation.
Packit df99a1
 * In the grayshade version of a letter,
Packit df99a1
 *   all pixels that were white remain absolutely white,
Packit df99a1
 *   the frame is black and the blackness falls down from it to the border.
Packit df99a1
 */
Packit df99a1
Packit df99a1
/* Offtopic: I wonder if this thing could help OCRing because frameworks
Packit df99a1
 * perfectly retain readability while becoming essentially 1-dimensional.
Packit df99a1
 */
Packit df99a1
Packit df99a1
#include "mdjvucfg.h"
Packit df99a1
#include "minidjvu.h"
Packit df99a1
#include <stdlib.h>
Packit df99a1
#include <assert.h>
Packit df99a1
#include <string.h>
Packit df99a1
#include <math.h>
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
/* This determines the gray level of the border (ratio of black).
Packit df99a1
 * Setting it to 1 will effectively eliminate grayshading.
Packit df99a1
 */
Packit df99a1
#define BORDER_FALLOFF .7 /* this is the main constant in all the matcher... */
Packit df99a1
Packit df99a1
typedef unsigned char byte;
Packit df99a1
Packit df99a1
static int donut_connectivity_test(byte *upper, byte *row, byte *lower)/*{{{*/
Packit df99a1
{
Packit df99a1
    /*(on the pictures below 0 is white, 1 is black or gray)
Packit df99a1
     *
Packit df99a1
     * 01.
Packit df99a1
     * 1 . -> 1
Packit df99a1
     * ...
Packit df99a1
     *
Packit df99a1
     * .0.
Packit df99a1
     * 1 1 ->  1
Packit df99a1
     * .0.
Packit df99a1
     *
Packit df99a1
     * all others -> 0
Packit df99a1
     */
Packit df99a1
Packit df99a1
    int sum, l, u, d, r;
Packit df99a1
Packit df99a1
    sum = (u = *upper ? 1 : 0) + (d = *lower ? 1 : 0) +
Packit df99a1
          (l = row[-1] ? 1 : 0) + (r = row[1] ? 1 : 0);
Packit df99a1
Packit df99a1
    switch(sum)
Packit df99a1
    {
Packit df99a1
        case 3:/*{{{*/
Packit df99a1
        {
Packit df99a1
            int x = 6 - (u + (l << 1) + d + (d << 1));
Packit df99a1
            switch(x)
Packit df99a1
            {
Packit df99a1
                case 0: /* l */
Packit df99a1
                    return upper[-1] && lower[-1] ? 0 : 1;
Packit df99a1
                case 1: /* d */
Packit df99a1
                    return lower[-1] && lower[1] ? 0 : 1;
Packit df99a1
                case 2: /* r */
Packit df99a1
                    return upper[1] && lower[1] ? 0 : 1;
Packit df99a1
                case 3: /* u */
Packit df99a1
                    return upper[-1] && upper[1] ? 0 : 1;
Packit df99a1
                default: assert(0); return 0;
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
        break;/*}}}*/
Packit df99a1
        case 2:/*{{{*/
Packit df99a1
        {
Packit df99a1
            int s = l + r;
Packit df99a1
            if (s & 1)
Packit df99a1
            {
Packit df99a1
                /*   A1.
Packit df99a1
                 *   1 0 - should be !A (2x2 square extermination)
Packit df99a1
                 *   .0.
Packit df99a1
                 */
Packit df99a1
                if (l)
Packit df99a1
                {
Packit df99a1
                    if (u)
Packit df99a1
                        return upper[-1] ? 0 : 1;
Packit df99a1
                    else
Packit df99a1
                        return lower[-1] ? 0 : 1;
Packit df99a1
                }
Packit df99a1
                else /* r */
Packit df99a1
                {
Packit df99a1
                    if (u)
Packit df99a1
                        return upper[1] ? 0 : 1;
Packit df99a1
                    else
Packit df99a1
                        return lower[1] ? 0 : 1;
Packit df99a1
                }
Packit df99a1
            }
Packit df99a1
            else
Packit df99a1
            {
Packit df99a1
                /*   .0.
Packit df99a1
                 *   1 1 - surely should be 1 to preserve connection
Packit df99a1
                 *   .0.
Packit df99a1
                 */
Packit df99a1
                return 1;
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
        break;/*}}}*/
Packit df99a1
        case 0: case 4:
Packit df99a1
            return 1;
Packit df99a1
        case 1:
Packit df99a1
            return 0;
Packit df99a1
        default: assert(0); return 0;
Packit df99a1
    }
Packit df99a1
    assert(0); return 0;
Packit df99a1
}/*}}}*/
Packit df99a1
static byte donut_transform_pixel(byte *upper, byte *row, byte *lower)/*{{{*/
Packit df99a1
{
Packit df99a1
    /* (center pixel should be gray in order for this to work)
Packit df99a1
     * (on the pictures below 0 is white, 1 is black or gray)
Packit df99a1
     *
Packit df99a1
     * 01.
Packit df99a1
     * 1 . -> center will become 1
Packit df99a1
     * ...
Packit df99a1
     *
Packit df99a1
     * .0.
Packit df99a1
     * 1 1 -> center will become 1
Packit df99a1
     * .0.
Packit df99a1
     *
Packit df99a1
     * 00.
Packit df99a1
     * 1 0 -> center will become 1
Packit df99a1
     * .0.
Packit df99a1
     *
Packit df99a1
     * 1..
Packit df99a1
     * 1 0 -> center will become 0
Packit df99a1
     * 1..
Packit df99a1
     *
Packit df99a1
     * 11.
Packit df99a1
     * 1 0 -> center will become 0
Packit df99a1
     * .0.
Packit df99a1
     *
Packit df99a1
     * .A.
Packit df99a1
     * A A -> center will become 1
Packit df99a1
     * .A.
Packit df99a1
     */
Packit df99a1
Packit df99a1
    int sum, l, u, d, r;
Packit df99a1
    if (!*row) return 0;
Packit df99a1
Packit df99a1
    sum = (u = *upper ? 1 : 0) + (d = *lower ? 1 : 0) +
Packit df99a1
          (l = row[-1] ? 1 : 0) + (r = row[1] ? 1 : 0);
Packit df99a1
Packit df99a1
    switch(sum)
Packit df99a1
    {
Packit df99a1
        case 1: case 3:/*{{{*/
Packit df99a1
        {
Packit df99a1
            int x = u + (l << 1) + d + (d << 1);
Packit df99a1
            if (sum == 3) x = (6 - x) ^ 2;
Packit df99a1
            switch(x)
Packit df99a1
            {
Packit df99a1
                case 0: /* r */
Packit df99a1
                    return upper[1] && lower[1] ? 0 : 1;
Packit df99a1
                case 1: /* u */
Packit df99a1
                    return upper[-1] && upper[1] ? 0 : 1;
Packit df99a1
                case 2: /* l */
Packit df99a1
                    return upper[-1] && lower[-1] ? 0 : 1;
Packit df99a1
                case 3: /* d */
Packit df99a1
                    return lower[-1] && lower[1] ? 0 : 1;
Packit df99a1
                default: assert(0); return 0;
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
        break;/*}}}*/
Packit df99a1
        case 2:/*{{{*/
Packit df99a1
        {
Packit df99a1
            int s = l + r;
Packit df99a1
            if (s & 1)
Packit df99a1
            {
Packit df99a1
                /*   A1.
Packit df99a1
                 *   1 0 - should be !A (2x2 square extermination)
Packit df99a1
                 *   .0.
Packit df99a1
                 */
Packit df99a1
                if (l)
Packit df99a1
                {
Packit df99a1
                    if (u)
Packit df99a1
                        return upper[-1] ? 0 : 1;
Packit df99a1
                    else
Packit df99a1
                        return lower[-1] ? 0 : 1;
Packit df99a1
                }
Packit df99a1
                else /* r */
Packit df99a1
                {
Packit df99a1
                    if (u)
Packit df99a1
                        return upper[1] ? 0 : 1;
Packit df99a1
                    else
Packit df99a1
                        return lower[1] ? 0 : 1;
Packit df99a1
                }
Packit df99a1
            }
Packit df99a1
            else
Packit df99a1
            {
Packit df99a1
                /*   .0.
Packit df99a1
                 *   1 1 - surely should be 1 to preserve connection
Packit df99a1
                 *   .0.
Packit df99a1
                 */
Packit df99a1
                return 1;
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
        break;/*}}}*/
Packit df99a1
        case 0: case 4:
Packit df99a1
            return 1; /* lone pixels are NOT omitted */
Packit df99a1
        default: assert(0); return 0;
Packit df99a1
    }
Packit df99a1
    assert(0); return 0;
Packit df99a1
}/*}}}*/
Packit df99a1
Packit df99a1
/* `pixels' should have a margin of 1 pixel at each side
Packit df99a1
 * returns true if the image was changed
Packit df99a1
 */
Packit df99a1
static int flay(byte **pixels, int w, int h, int rank, int **ranks)
Packit df99a1
{
Packit df99a1
    int i, j, result = 0;
Packit df99a1
Packit df99a1
    byte *buf = MALLOCV(byte, w * h);
Packit df99a1
Packit df99a1
    assert(pixels);
Packit df99a1
    assert(w);
Packit df99a1
    assert(h);
Packit df99a1
Packit df99a1
    for (i = 0; i < h; i++) for (j = 0; j < w; j++)
Packit df99a1
    {
Packit df99a1
        buf[w * i + j] =
Packit df99a1
            donut_transform_pixel(pixels[i-1] + j, pixels[i] + j, pixels[i+1] + j);
Packit df99a1
    }
Packit df99a1
Packit df99a1
    for (i = 0; i < h; i++)
Packit df99a1
    {
Packit df99a1
        byte *up = pixels[i-1], *row = pixels[i], *dn = pixels[i+1];
Packit df99a1
        byte *buf_row = buf + w * i;
Packit df99a1
        int *rank_row = NULL;
Packit df99a1
        if (ranks) rank_row = ranks[i];
Packit df99a1
        for (j = 0; j < w; j++)
Packit df99a1
        {
Packit df99a1
            if (row[j] && !buf_row[j])
Packit df99a1
            {
Packit df99a1
                if (!donut_connectivity_test(up + j, row + j, dn + j))
Packit df99a1
                {
Packit df99a1
                    row[j] = buf_row[j];
Packit df99a1
                    if (rank) rank_row[j] = rank;
Packit df99a1
                    result = 1;
Packit df99a1
                }
Packit df99a1
            }
Packit df99a1
            else
Packit df99a1
                row[j] = buf_row[j];
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
Packit df99a1
    FREEV(buf);
Packit df99a1
    return result;
Packit df99a1
}
Packit df99a1
Packit df99a1
/* TODO: use less temporary buffers and silly copyings */
Packit df99a1
MDJVU_IMPLEMENT void mdjvu_soften_pattern(byte **result, byte **pixels, int32 w, int32 h)/*{{{*/
Packit df99a1
{
Packit df99a1
    byte *r = MALLOCV(byte, (w + 2) * (h + 2));
Packit df99a1
    byte **pointers = MALLOCV(byte *, h + 2);
Packit df99a1
    int *ranks_buf = MALLOCV(int, w * h);
Packit df99a1
    int **ranks = MALLOCV(int *, h);
Packit df99a1
Packit df99a1
    int i, j, passes = 1;
Packit df99a1
    double level = 1, falloff;
Packit df99a1
    byte *colors;
Packit df99a1
Packit df99a1
    memset(r, 0, (w + 2) * (h + 2));
Packit df99a1
    memset(ranks_buf, 0, w * h * sizeof(int));
Packit df99a1
Packit df99a1
    for (i = 0; i < h + 2; i++)
Packit df99a1
        pointers[i] = r + (w + 2) * i + 1;
Packit df99a1
Packit df99a1
    for (i = 0; i < h; i++)
Packit df99a1
        memcpy(pointers[i+1], pixels[i], w);
Packit df99a1
Packit df99a1
    for (i = 0; i < h; i++)
Packit df99a1
        ranks[i] = ranks_buf + w * i;
Packit df99a1
Packit df99a1
    while(flay(pointers + 1, w, h, passes, ranks)) passes++;
Packit df99a1
Packit df99a1
    colors = MALLOCV(byte, passes + 1);
Packit df99a1
Packit df99a1
    falloff = pow(BORDER_FALLOFF, 1./passes);
Packit df99a1
Packit df99a1
    for (i = 0; i < passes; i++)
Packit df99a1
    {
Packit df99a1
        colors[i] = (byte) (level * 255);
Packit df99a1
        level *= falloff;
Packit df99a1
    }
Packit df99a1
    /* TODO: test the next line */
Packit df99a1
    /* colors[passes - 1] = 50; pay less attention to border pixels */
Packit df99a1
    colors[passes] = 0;
Packit df99a1
Packit df99a1
    pointers++;
Packit df99a1
    for (i = 0; i < h; i++)
Packit df99a1
    {
Packit df99a1
        for (j = 0; j < w; j++)
Packit df99a1
        {
Packit df99a1
            if (pointers[i][j])
Packit df99a1
            {
Packit df99a1
                result[i][j] = 255;
Packit df99a1
            }
Packit df99a1
            else
Packit df99a1
            {
Packit df99a1
                result[i][j] = colors[passes - ranks[i][j]];
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
    pointers--;
Packit df99a1
Packit df99a1
    FREEV(colors);
Packit df99a1
    FREEV(ranks);
Packit df99a1
    FREEV(ranks_buf);
Packit df99a1
    FREEV(r);
Packit df99a1
    FREEV(pointers);
Packit df99a1
}/*}}}*/