|
Packit |
df99a1 |
/* minidjvu - library for handling bilevel images with DjVuBitonal support
|
|
Packit |
df99a1 |
*
|
|
Packit |
df99a1 |
* classify.c - classifying patterns
|
|
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 |
|
|
Packit |
df99a1 |
#include "mdjvucfg.h"
|
|
Packit |
df99a1 |
#include "minidjvu.h"
|
|
Packit |
df99a1 |
#include <stdlib.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 |
/* Classes are single-linked lists with an additional pointer to the last node.
|
|
Packit |
df99a1 |
* This is an class item.
|
|
Packit |
df99a1 |
*/
|
|
Packit |
df99a1 |
typedef struct ClassNode
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
mdjvu_pattern_t ptr;
|
|
Packit |
df99a1 |
struct ClassNode *next; /* NULL if this node is the last one */
|
|
Packit |
df99a1 |
struct ClassNode *global_next; /* next among all nodes to classify */
|
|
Packit |
df99a1 |
int32 tag; /* filled before the final dumping */
|
|
Packit |
df99a1 |
} ClassNode;
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
/* Classes themselves are composed in double-linked list. */
|
|
Packit |
df99a1 |
typedef struct Class
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
ClassNode *first, *last;
|
|
Packit |
df99a1 |
struct Class *prev_class;
|
|
Packit |
df99a1 |
struct Class *next_class;
|
|
Packit |
df99a1 |
} Class;
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
typedef struct Classification
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
Class *first_class;
|
|
Packit |
df99a1 |
ClassNode *first_node, *last_node;
|
|
Packit |
df99a1 |
} Classification;
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
/* Creates an empty class and links it to the list of classes. */
|
|
Packit |
df99a1 |
static Class *new_class(Classification *cl)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
Class *c = MALLOC(Class);
|
|
Packit |
df99a1 |
c->first = c->last = NULL;
|
|
Packit |
df99a1 |
c->prev_class = NULL;
|
|
Packit |
df99a1 |
c->next_class = cl->first_class;
|
|
Packit |
df99a1 |
if (cl->first_class) cl->first_class->prev_class = c;
|
|
Packit |
df99a1 |
cl->first_class = c;
|
|
Packit |
df99a1 |
return c;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
/* Unlinks a class and deletes it. Its nodes are not deleted. */
|
|
Packit |
df99a1 |
static void delete_class(Classification *cl, Class *c)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
Class *prev = c->prev_class, *next = c->next_class;
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
if (prev)
|
|
Packit |
df99a1 |
prev->next_class = next;
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
cl->first_class = next;
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
if (next)
|
|
Packit |
df99a1 |
next->prev_class = prev;
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
FREE(c);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
/* Creates a new node and adds it to the given class. */
|
|
Packit |
df99a1 |
static ClassNode *new_node(Classification *cl, Class *c, mdjvu_pattern_t ptr)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
ClassNode *n = MALLOC(ClassNode);
|
|
Packit |
df99a1 |
n->ptr = ptr;
|
|
Packit |
df99a1 |
n->next = c->first;
|
|
Packit |
df99a1 |
c->first = n;
|
|
Packit |
df99a1 |
if (!c->last) c->last = n;
|
|
Packit |
df99a1 |
n->global_next = NULL;
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
if (cl->last_node)
|
|
Packit |
df99a1 |
cl->last_node->global_next = n;
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
cl->first_node = n;
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
cl->last_node = n;
|
|
Packit |
df99a1 |
return n;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
/* Merge two classes and delete one of them. */
|
|
Packit |
df99a1 |
static Class *merge(Classification *cl, Class *c1, Class *c2)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (!c1->first)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
delete_class(cl, c1);
|
|
Packit |
df99a1 |
return c2;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (c2->first)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
c1->last->next = c2->first;
|
|
Packit |
df99a1 |
c1->last = c2->last;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
delete_class(cl, c2);
|
|
Packit |
df99a1 |
return c1;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
/* Puts a tag on each node corresponding to its class. */
|
|
Packit |
df99a1 |
static unsigned put_tags(Classification *cl)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int32 tag = 1;
|
|
Packit |
df99a1 |
Class *c = cl->first_class;
|
|
Packit |
df99a1 |
while (c)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
ClassNode *n = c->first;
|
|
Packit |
df99a1 |
while (n)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
n->tag = tag;
|
|
Packit |
df99a1 |
n = n->next;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
c = c->next_class;
|
|
Packit |
df99a1 |
tag++;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
return tag - 1;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
/* Deletes all classes; nodes are untouched. */
|
|
Packit |
df99a1 |
static void delete_all_classes(Classification *cl)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
Class *c = cl->first_class;
|
|
Packit |
df99a1 |
while (c)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
Class *t = c;
|
|
Packit |
df99a1 |
c = c->next_class;
|
|
Packit |
df99a1 |
FREE(t);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
/* Compares p with nodes from c until a meaningful result. */
|
|
Packit |
df99a1 |
static int compare_to_class(mdjvu_pattern_t p, Class *c, int32 dpi,
|
|
Packit |
df99a1 |
mdjvu_matcher_options_t options)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int r = 0;
|
|
Packit |
df99a1 |
ClassNode *n = c->first;
|
|
Packit |
df99a1 |
while(n)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
r = mdjvu_match_patterns(p, n->ptr, dpi, options);
|
|
Packit |
df99a1 |
if (r) break;
|
|
Packit |
df99a1 |
n = n->next;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
return r;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
static void classify(Classification *cl, mdjvu_pattern_t p,
|
|
Packit |
df99a1 |
int32 dpi, mdjvu_matcher_options_t options)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
Class *class_of_this = NULL;
|
|
Packit |
df99a1 |
Class *c, *next_c = NULL;
|
|
Packit |
df99a1 |
for (c = cl->first_class; c; c = next_c)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
next_c = c->next_class; /* That's because c may be deleted in merging */
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
if (class_of_this == c) continue;
|
|
Packit |
df99a1 |
if (compare_to_class(p, c, dpi, options) != 1) continue;
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
if (class_of_this)
|
|
Packit |
df99a1 |
class_of_this = merge(cl, class_of_this, c);
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
class_of_this = c;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (!class_of_this) class_of_this = new_class(cl);
|
|
Packit |
df99a1 |
new_node(cl, class_of_this, p);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
MDJVU_IMPLEMENT int32 mdjvu_classify_patterns
|
|
Packit |
df99a1 |
(mdjvu_pattern_t *b, int32 *r, int32 n, int32 dpi,
|
|
Packit |
df99a1 |
mdjvu_matcher_options_t options)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int32 i, max_tag;
|
|
Packit |
df99a1 |
ClassNode *node;
|
|
Packit |
df99a1 |
Classification cl;
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
cl.first_class = NULL;
|
|
Packit |
df99a1 |
cl.first_node = cl.last_node = NULL;
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
for (i = 0; i < n; i++) if (b[i]) classify(&cl, b[i], dpi, options);
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
max_tag = put_tags(&cl);
|
|
Packit |
df99a1 |
delete_all_classes(&cl);
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
i = 0;
|
|
Packit |
df99a1 |
node = cl.first_node;
|
|
Packit |
df99a1 |
while (node)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
ClassNode *t;
|
|
Packit |
df99a1 |
while (!b[i]) r[i++] = 0;
|
|
Packit |
df99a1 |
r[i++] = node->tag;
|
|
Packit |
df99a1 |
t = node;
|
|
Packit |
df99a1 |
node = node->global_next;
|
|
Packit |
df99a1 |
FREE(t);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (i < n) while (i < n) r[i++] = 0;
|
|
Packit |
df99a1 |
return max_tag;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
#ifndef NO_MINIDJVU
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
MDJVU_IMPLEMENT int32 mdjvu_classify_bitmaps_in_image
|
|
Packit |
df99a1 |
(mdjvu_image_t image, int32 *result, mdjvu_matcher_options_t options)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int32 i, n = mdjvu_image_get_bitmap_count(image);
|
|
Packit |
df99a1 |
int32 dpi = mdjvu_image_get_resolution(image);
|
|
Packit |
df99a1 |
mdjvu_pattern_t *patterns = MALLOCV(mdjvu_pattern_t, n);
|
|
Packit |
df99a1 |
int32 max_tag;
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
for (i = 0; i < n; i++)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
mdjvu_bitmap_t bitmap = mdjvu_image_get_bitmap(image, i);
|
|
Packit |
df99a1 |
if (mdjvu_image_get_no_substitution_flag(image, bitmap))
|
|
Packit |
df99a1 |
patterns[i] = NULL;
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
patterns[i] = mdjvu_pattern_create(bitmap);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
max_tag = mdjvu_classify_patterns(patterns, result, n, dpi, options);
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
for (i = 0; i < n; i++)
|
|
Packit |
df99a1 |
if (patterns[i]) mdjvu_pattern_destroy(patterns[i]);
|
|
Packit |
df99a1 |
FREEV(patterns);
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
return max_tag;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
#endif /* NO_MINIDJVU */
|