Leptonica 1.68
C Image Processing Library

jbclass.c

Go to the documentation of this file.
00001 /*====================================================================*
00002  -  Copyright (C) 2001 Leptonica.  All rights reserved.
00003  -  This software is distributed in the hope that it will be
00004  -  useful, but with NO WARRANTY OF ANY KIND.
00005  -  No author or distributor accepts responsibility to anyone for the
00006  -  consequences of using this software, or for whether it serves any
00007  -  particular purpose or works at all, unless he or she says so in
00008  -  writing.  Everyone is granted permission to copy, modify and
00009  -  redistribute this source code, for commercial or non-commercial
00010  -  purposes, with the following restrictions: (1) the origin of this
00011  -  source code must not be misrepresented; (2) modified versions must
00012  -  be plainly marked as such; and (3) this notice may not be removed
00013  -  or altered from any source or modified source distribution.
00014  *====================================================================*/
00015 
00016 
00017 /*
00018  * jbclass.c
00019  *
00020  *     These are functions for unsupervised classification of
00021  *     collections of connected components -- either characters or
00022  *     words -- in binary images.  They can be used as image
00023  *     processing steps in jbig2 compression.
00024  *
00025  *     Initialization
00026  *
00027  *         JBCLASSER         *jbRankHausInit()      [rank hausdorff encoder]
00028  *         JBCLASSER         *jbCorrelationInit()   [correlation encoder]
00029  *         JBCLASSER         *jbCorrelationInitWithoutComponents()  [ditto]
00030  *         static JBCLASSER  *jbCorrelationInitInternal()
00031  *
00032  *     Classify the pages
00033  *
00034  *         l_int32     jbAddPages()
00035  *         l_int32     jbAddPage()
00036  *         l_int32     jbAddPageComponents()
00037  *
00038  *     Rank hausdorff classifier
00039  *
00040  *         l_int32     jbClassifyRankHaus()
00041  *         l_int32     pixHaustest()
00042  *         l_int32     pixRankHaustest()
00043  *
00044  *     Binary correlation classifier
00045  *
00046  *         l_int32     jbClassifyCorrelation()
00047  *
00048  *     Determine the image components we start with
00049  *
00050  *         l_int32     jbGetComponents()
00051  *         PIX        *pixWordMaskByDilation()
00052  *
00053  *     Build grayscale composites (templates)
00054  *
00055  *         PIXA       *jbAccumulateComposites
00056  *         PIXA       *jbTemplatesFromComposites
00057  *
00058  *     Utility functions for Classer
00059  *
00060  *         JBCLASSER  *jbClasserCreate()
00061  *         void        jbClasserDestroy()
00062  *
00063  *     Utility functions for Data
00064  *
00065  *         JBDATA     *jbDataSave()
00066  *         void        jbDataDestroy()
00067  *         l_int32     jbDataWrite()
00068  *         JBDATA     *jbDataRead()
00069  *         PIXA       *jbDataRender()
00070  *         l_int32     jbGetULCorners()
00071  *         l_int32     jbGetLLCorners()
00072  *
00073  *     Static helpers
00074  *
00075  *         static JBFINDCTX *findSimilarSizedTemplatesInit()
00076  *         static l_int32    findSimilarSizedTemplatesNext()
00077  *         static void       findSimilarSizedTemplatesDestroy()
00078  *         static l_int32    finalPositioningForAlignment()
00079  *
00080  *     Note: this is NOT an implementation of the JPEG jbig2
00081  *     proposed standard encoder, the specifications for which
00082  *     can be found at http://www.jpeg.org/jbigpt2.html.
00083  *     (See below for a full implementation.)
00084  *     It is an implementation of the lower-level part of an encoder that:
00085  *
00086  *        (1) identifies connected components that are going to be used
00087  *        (2) puts them in similarity classes (this is an unsupervised
00088  *            classifier), and
00089  *        (3) stores the result in a simple file format (2 files,
00090  *            one for templates and one for page/coordinate/template-index
00091  *            quartets).
00092  *
00093  *     An actual implementation of the official jbig2 encoder could
00094  *     start with parts (1) and (2), and would then compress the quartets
00095  *     according to the standards requirements (e.g., Huffman or
00096  *     arithmetic coding of coordinate differences and image templates).
00097  *
00098  *     The low-level part of the encoder provided here has the
00099  *     following useful features:
00100  *
00101  *         - It is accurate in the identification of templates
00102  *           and classes because it uses a windowed hausdorff
00103  *           distance metric.
00104  *         - It is accurate in the placement of the connected
00105  *           components, doing a two step process of first aligning
00106  *           the the centroids of the template with those of each instance,
00107  *           and then making a further correction of up to +- 1 pixel
00108  *           in each direction to best align the templates.
00109  *         - It is fast because it uses a morphologically based
00110  *           matching algorithm to implement the hausdorff criterion,
00111  *           and it selects the patterns that are possible matches
00112  *           based on their size.
00113  *
00114  *     We provide two different matching functions, one using Hausdorff
00115  *     distance and one using a simple image correlation.
00116  *     The Hausdorff method sometimes produces better results for the
00117  *     same number of classes, because it gives a relatively small
00118  *     effective weight to foreground pixels near the boundary,
00119  *     and a relatively  large weight to foreground pixels that are
00120  *     not near the boundary.  By effectively ignoring these boundary
00121  *     pixels, Hausdorff weighting corresponds better to the expected
00122  *     probabilities of the pixel values in a scanned image, where the
00123  *     variations in instances of the same printed character are much
00124  *     more likely to be in pixels near the boundary.  By contrast,
00125  *     the correlation method gives equal weight to all foreground pixels.
00126  *
00127  *     For best results, use the correlation method.  Correlation takes
00128  *     the number of fg pixels in the AND of instance and template,
00129  *     divided by the product of the number of fg pixels in instance
00130  *     and template.  It compares this with a threshold that, in
00131  *     general, depends on the fractional coverage of the template.
00132  *     For heavy text, the threshold is raised above that for light
00133  *     text,  By using both these parameters (basic threshold and
00134  *     adjustment factor for text weight), one has more flexibility
00135  *     and can arrive at the fewest substitution errors, although
00136  *     this comes at the price of more templates.
00137  *
00138  *     The strict Hausdorff scoring is not a rank weighting, because a
00139  *     single pixel beyond the given distance will cause a match
00140  *     failure.  A rank Hausdorff is more robust to non-boundary noise,
00141  *     but it is also more susceptible to confusing components that
00142  *     should be in different classes.  For implementing a jbig2
00143  *     application for visually lossless binary image compression,
00144  *     you have two choices:
00145  *
00146  *        (1) use a 3x3 structuring element (size = 3) and a strict
00147  *            Hausdorff comparison (rank = 1.0 in the rank Hausdorff
00148  *            function).  This will result in a minimal number of classes,
00149  *            but confusion of small characters, such as italic and
00150  *            non-italic lower-case 'o', can still occur.
00151  *        (2) use the correlation method with a threshold of 0.85
00152  *            and a weighting factor of about 0.7.  This will result in
00153  *            a larger number of classes, but should not be confused
00154  *            either by similar small characters or by extremely
00155  *            thick sans serif characters, such as in prog/cootoots.png.
00156  *
00157  *     As mentioned above, if visual substitution errors must be
00158  *     avoided, you should use the correlation method.
00159  *
00160  *     We provide executables that show how to do the encoding:
00161  *         prog/jbrankhaus.c
00162  *         prog/jbcorrelation.c
00163  *
00164  *     The basic flow for correlation classification goes as follows,
00165  *     where specific choices have been made for parameters (Hausdorff
00166  *     is the same except for initialization):
00167  *
00168  *             // Initialize and save data in the classer
00169  *         JBCLASSER *classer =
00170  *             jbCorrelationInit(JB_CONN_COMPS, 0, 0, 0.8, 0.7);
00171  *         SARRAY *safiles = getSortedPathnamesInDirectory(directory,
00172  *                                                         NULL, 0, 0);
00173  *         jbAddPages(classer, safiles);
00174  *
00175  *             // Save the data in a data structure for serialization,
00176  *             // and write it into two files.
00177  *         JBDATA *data = jbDataSave(classer);
00178  *         jbDataWrite(rootname, data);
00179  *
00180  *             // Reconstruct (render) the pages from the encoded data.
00181  *         PIXA *pixa = jbDataRender(data, FALSE);
00182  *
00183  *     Adam Langley has recently built a jbig2 standards-compliant
00184  *     encoder, the first one to appear in open source.  You can get
00185  *     this encoder at:
00186  *
00187  *          http://www.imperialviolet.org/jbig2.html
00188  *
00189  *     It uses arithmetic encoding throughout.  It encodes binary images
00190  *     losslessly with a single arithmetic coding over the full image.
00191  *     It also does both lossy and lossless encoding from connected
00192  *     components, using leptonica to generate the templates representing
00193  *     each cluster.
00194  */
00195 
00196 #include <string.h>
00197 #include <math.h>
00198 #include "allheaders.h"
00199 
00200     /* MSVC can't handle arrays dimensioned by static const integers */
00201 #define  L_BUF_SIZE  512
00202 
00203     /* For jbClassifyRankHaus(): size of border added around
00204      * pix of each c.c., to allow further processing.  This
00205      * should be at least the sum of the MAX_DIFF_HEIGHT
00206      * (or MAX_DIFF_WIDTH) and one-half the size of the Sel  */
00207 static const l_int32  JB_ADDED_PIXELS = 6;
00208 
00209     /* For pixHaustest(), pixRankHaustest() and pixCorrelationScore():
00210      * choose these to be 2 or greater */
00211 static const l_int32  MAX_DIFF_WIDTH = 2;  /* use at least 2 */
00212 static const l_int32  MAX_DIFF_HEIGHT = 2;  /* use at least 2 */
00213 
00214     /* In initialization, you have the option to discard components
00215      * (cc, characters or words) that have either width or height larger
00216      * than a given size.  This is convenient for jbDataSave(), because
00217      * the components are placed onto a regular lattice with cell
00218      * dimension equal to the maximum component size.  The default
00219      * values are given here.  If you want to save all components,
00220      * use a sufficiently large set of dimensions. */
00221 static const l_int32  MAX_CONN_COMP_WIDTH = 350;  /* default max cc width */
00222 static const l_int32  MAX_CHAR_COMP_WIDTH = 350;  /* default max char width */
00223 static const l_int32  MAX_WORD_COMP_WIDTH = 1000;  /* default max word width */
00224 static const l_int32  MAX_COMP_HEIGHT = 120;  /* default max component height */
00225 
00226     /* Max allowed dilation to merge characters into words */
00227 #define  MAX_ALLOWED_DILATION  14
00228 
00229     /* This stores the state of a state machine which fetches
00230      * similar sized templates */
00231 struct JbFindTemplatesState
00232 {
00233     JBCLASSER       *classer;    /* classer                               */
00234     l_int32          w;          /* desired width                         */
00235     l_int32          h;          /* desired height                        */
00236     l_int32          i;          /* index into two_by_two step array      */
00237     NUMA            *numa;       /* current number array                  */
00238     l_int32          n;          /* current element of numa               */
00239 };
00240 typedef struct JbFindTemplatesState JBFINDCTX;
00241 
00242 
00243     /* Static initialization function */
00244 static JBCLASSER * jbCorrelationInitInternal(l_int32 components,
00245                        l_int32 maxwidth, l_int32 maxheight, l_float32 thresh,
00246                        l_float32 weightfactor, l_int32 keep_components);
00247 
00248     /* Static helper functions */
00249 static JBFINDCTX * findSimilarSizedTemplatesInit(JBCLASSER *classer, PIX *pixs);
00250 static l_int32 findSimilarSizedTemplatesNext(JBFINDCTX *context);
00251 static void findSimilarSizedTemplatesDestroy(JBFINDCTX **pcontext);
00252 static l_int32 finalPositioningForAlignment(PIX *pixs, l_int32 x, l_int32 y,
00253                              l_int32 idelx, l_int32 idely, PIX *pixt,
00254                              l_int32 *sumtab, l_int32 *pdx, l_int32 *pdy);
00255 
00256 #ifndef NO_CONSOLE_IO
00257 #define  DEBUG_PLOT_CC             0
00258 #define  DEBUG_CORRELATION_SCORE   0
00259 #endif  /* ~NO_CONSOLE_IO */
00260 
00261 
00262 /*----------------------------------------------------------------------*
00263  *                            Initialization                            *
00264  *----------------------------------------------------------------------*/
00265 /*!
00266  *  jbRankHausInit()
00267  *
00268  *      Input:  components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS)
00269  *              maxwidth (of component; use 0 for default)
00270  *              maxheight (of component; use 0 for default)
00271  *              size  (of square structuring element; 2, representing
00272  *                     2x2 sel, is necessary for reasonable accuracy of
00273  *                     small components; combine this with rank ~ 0.97
00274  *                     to avoid undue class expansion)
00275  *              rank (rank val of match, each way; in [0.5 - 1.0];
00276  *                    when using size = 2, 0.97 is a reasonable value)
00277  *      Return: jbclasser if OK; NULL on error
00278  */
00279 JBCLASSER *
00280 jbRankHausInit(l_int32    components,
00281                l_int32    maxwidth,
00282                l_int32    maxheight,
00283                l_int32    size,
00284                l_float32  rank)
00285 {
00286 JBCLASSER  *classer;
00287 
00288     PROCNAME("jbRankHausInit");
00289 
00290     if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
00291         components != JB_WORDS)
00292         return (JBCLASSER *)ERROR_PTR("invalid components", procName, NULL);
00293     if (size < 1 || size > 10)
00294         return (JBCLASSER *)ERROR_PTR("size not reasonable", procName, NULL);
00295     if (rank < 0.5 || rank > 1.0)
00296         return (JBCLASSER *)ERROR_PTR("rank not in [0.5-1.0]", procName, NULL);
00297     if (maxwidth == 0) {
00298         if (components == JB_CONN_COMPS)
00299             maxwidth = MAX_CONN_COMP_WIDTH;
00300         else if (components == JB_CHARACTERS)
00301             maxwidth = MAX_CHAR_COMP_WIDTH;
00302         else  /* JB_WORDS */
00303             maxwidth = MAX_WORD_COMP_WIDTH;
00304     }
00305     if (maxheight == 0)
00306         maxheight = MAX_COMP_HEIGHT;
00307 
00308     if ((classer = jbClasserCreate(JB_RANKHAUS, components)) == NULL)
00309         return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL);
00310     classer->maxwidth = maxwidth;
00311     classer->maxheight = maxheight;
00312     classer->sizehaus = size;
00313     classer->rankhaus = rank;
00314     classer->nahash = numaHashCreate(5507, 4);  /* 5507 is prime */
00315     return classer;
00316 }
00317 
00318 
00319 /*!
00320  *  jbCorrelationInit()
00321  *
00322  *      Input:  components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS)
00323  *              maxwidth (of component; use 0 for default)
00324  *              maxheight (of component; use 0 for default)
00325  *              thresh (value for correlation score: in [0.4 - 0.98])
00326  *              weightfactor (corrects thresh for thick characters [0.0 - 1.0])
00327  *      Return: jbclasser if OK; NULL on error
00328  *
00329  *  Notes:
00330  *      (1) For scanned text, suggested input values are:
00331  *            thresh ~ [0.8 - 0.85]
00332  *            weightfactor ~ [0.5 - 0.6]
00333  *      (2) For electronically generated fonts (e.g., rasterized pdf),
00334  *          a very high thresh (e.g., 0.95) will not cause a significant
00335  *          increase in the number of classes.
00336  */
00337 JBCLASSER *
00338 jbCorrelationInit(l_int32    components,
00339                   l_int32    maxwidth,
00340                   l_int32    maxheight,
00341                   l_float32  thresh,
00342                   l_float32  weightfactor)
00343 {
00344     return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh,
00345                                      weightfactor, 1);
00346 }
00347 
00348 /*!
00349  *  jbCorrelationInitWithoutComponents()
00350  *
00351  *      Input:  same as jbCorrelationInit
00352  *      Output: same as jbCorrelationInit
00353  *
00354  *  Note: acts the same as jbCorrelationInit(), but the resulting
00355  *        object doesn't keep a list of all the components.
00356  */
00357 JBCLASSER *
00358 jbCorrelationInitWithoutComponents(l_int32    components,
00359                                    l_int32    maxwidth,
00360                                    l_int32    maxheight,
00361                                    l_float32  thresh,
00362                                    l_float32  weightfactor)
00363 {
00364     return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh,
00365                                      weightfactor, 0);
00366 }
00367 
00368 
00369 static JBCLASSER *
00370 jbCorrelationInitInternal(l_int32    components,
00371                           l_int32    maxwidth,
00372                           l_int32    maxheight,
00373                           l_float32  thresh,
00374                           l_float32  weightfactor,
00375                           l_int32    keep_components)
00376 {
00377 JBCLASSER  *classer;
00378 
00379     PROCNAME("jbCorrelationInitInternal");
00380 
00381     if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
00382         components != JB_WORDS)
00383         return (JBCLASSER *)ERROR_PTR("invalid components", procName, NULL);
00384     if (thresh < 0.4 || thresh > 0.98)
00385         return (JBCLASSER *)ERROR_PTR("thresh not in range [0.4 - 0.98]",
00386                 procName, NULL);
00387     if (weightfactor < 0.0 || weightfactor > 1.0)
00388         return (JBCLASSER *)ERROR_PTR("weightfactor not in range [0.0 - 1.0]",
00389                 procName, NULL);
00390     if (maxwidth == 0) {
00391         if (components == JB_CONN_COMPS)
00392             maxwidth = MAX_CONN_COMP_WIDTH;
00393         else if (components == JB_CHARACTERS)
00394             maxwidth = MAX_CHAR_COMP_WIDTH;
00395         else  /* JB_WORDS */
00396             maxwidth = MAX_WORD_COMP_WIDTH;
00397     }
00398     if (maxheight == 0)
00399         maxheight = MAX_COMP_HEIGHT;
00400 
00401 
00402     if ((classer = jbClasserCreate(JB_CORRELATION, components)) == NULL)
00403         return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL);
00404     classer->maxwidth = maxwidth;
00405     classer->maxheight = maxheight;
00406     classer->thresh = thresh;
00407     classer->weightfactor = weightfactor;
00408     classer->nahash = numaHashCreate(5507, 4);  /* 5507 is prime */
00409     classer->keep_pixaa = keep_components;
00410     return classer;
00411 }
00412 
00413 
00414 /*----------------------------------------------------------------------*
00415  *                       Classify the pages                             *
00416  *----------------------------------------------------------------------*/
00417 /*!
00418  *  jbAddPages()
00419  *
00420  *      Input:  jbclasser
00421  *              safiles (of page image file names)
00422  *      Return: 0 if OK; 1 on error
00423  *
00424  *  Note:
00425  *      (1) jbclasser makes a copy of the array of file names.
00426  *      (2) The caller is still responsible for destroying the input array.
00427  */
00428 l_int32
00429 jbAddPages(JBCLASSER  *classer,
00430            SARRAY     *safiles)
00431 {
00432 l_int32  i, nfiles;
00433 char    *fname;
00434 PIX     *pix;
00435 
00436     PROCNAME("jbAddPages");
00437 
00438     if (!classer)
00439         return ERROR_INT("classer not defined", procName, 1);
00440     if (!safiles)
00441         return ERROR_INT("safiles not defined", procName, 1);
00442 
00443     classer->safiles = sarrayCopy(safiles);
00444     nfiles = sarrayGetCount(safiles);
00445     for (i = 0; i < nfiles; i++) {
00446         fname = sarrayGetString(safiles, i, 0);
00447         if ((pix = pixRead(fname)) == NULL) {
00448             L_WARNING_INT("image file %d not read", procName, i);
00449             continue;
00450         }
00451         if (pixGetDepth(pix) != 1) {
00452             L_WARNING_INT("image file %d not 1 bpp", procName, i);
00453             continue;
00454         }
00455         jbAddPage(classer, pix);
00456         pixDestroy(&pix);
00457     }
00458 
00459     return 0;
00460 }
00461 
00462 
00463 /*!
00464  *  jbAddPage()
00465  *
00466  *      Input:  jbclasser
00467  *              pixs (of input page)
00468  *      Return: 0 if OK; 1 on error
00469  */
00470 l_int32
00471 jbAddPage(JBCLASSER  *classer,
00472           PIX        *pixs)
00473 {
00474 BOXA  *boxas;
00475 PIXA  *pixas;
00476 
00477     PROCNAME("jbAddPage");
00478 
00479     if (!classer)
00480         return ERROR_INT("classer not defined", procName, 1);
00481     if (!pixs || pixGetDepth(pixs) != 1)
00482         return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
00483 
00484     classer->w = pixGetWidth(pixs);
00485     classer->h = pixGetHeight(pixs);
00486 
00487         /* Get the appropriate components and their bounding boxes */
00488     if (jbGetComponents(pixs, classer->components, classer->maxwidth,
00489                         classer->maxheight, &boxas, &pixas)) {
00490         return ERROR_INT("components not made", procName, 1);
00491     }
00492 
00493     jbAddPageComponents(classer, pixs, boxas, pixas);
00494     boxaDestroy(&boxas);
00495     pixaDestroy(&pixas);
00496     return 0;
00497 }
00498     
00499 
00500 /*!
00501  *  jbAddPageComponents()
00502  *
00503  *      Input:  jbclasser
00504  *              pixs (of input page)
00505  *              boxas (b.b. of components for this page)
00506  *              pixas (components for this page)
00507  *      Return: 0 if OK; 1 on error
00508  *
00509  *  Notes:
00510  *      (1) If there are no components on the page, we don't require input
00511  *          of empty boxas or pixas, although that's the typical situation.
00512  */
00513 l_int32
00514 jbAddPageComponents(JBCLASSER  *classer,
00515                     PIX        *pixs,
00516                     BOXA       *boxas,
00517                     PIXA       *pixas)
00518 {
00519 l_int32  n;
00520 
00521     PROCNAME("jbAddPageComponents");
00522 
00523     if (!classer)
00524         return ERROR_INT("classer not defined", procName, 1);
00525     if (!pixs)
00526         return ERROR_INT("pix not defined", procName, 1);
00527 
00528         /* Test for no components on the current page.  Always update the
00529          * number of pages processed, even if nothing is on it. */
00530     if (!boxas || !pixas || (boxaGetCount(boxas) == 0)) {
00531         classer->npages++;
00532         return 0;
00533     }
00534 
00535         /* Get classes.  For hausdorff, it uses a specified size of
00536          * structuring element and specified rank.  For correlation,
00537          * it uses a specified threshold. */
00538     if (classer->method == JB_RANKHAUS) {
00539         if (jbClassifyRankHaus(classer, boxas, pixas))
00540             return ERROR_INT("rankhaus classification failed", procName, 1);
00541     }
00542     else {  /* classer->method == JB_CORRELATION */
00543         if (jbClassifyCorrelation(classer, boxas, pixas))
00544             return ERROR_INT("correlation classification failed", procName, 1);
00545     }
00546 
00547         /* Find the global UL corners, adjusted for each instance so
00548          * that the class template and instance will have their
00549          * centroids in the same place.  Then the template can be
00550          * used to replace the instance. */
00551     if (jbGetULCorners(classer, pixs, boxas))
00552         return ERROR_INT("UL corners not found", procName, 1);
00553 
00554         /* Update total component counts and number of pages processed. */
00555     n = boxaGetCount(boxas);
00556     classer->baseindex += n;
00557     numaAddNumber(classer->nacomps, n);
00558     classer->npages++;
00559 
00560     return 0;
00561 }
00562 
00563 
00564 /*----------------------------------------------------------------------*
00565  *         Classification using windowed rank hausdorff metric          *
00566  *----------------------------------------------------------------------*/
00567 /*!
00568  *  jbClassifyRankHaus()
00569  *
00570  *      Input:  jbclasser
00571  *              boxa (of new components for classification)
00572  *              pixas (of new components for classification)
00573  *      Return: 0 if OK; 1 on error
00574  */
00575 l_int32
00576 jbClassifyRankHaus(JBCLASSER  *classer,
00577                    BOXA       *boxa,
00578                    PIXA       *pixas)
00579 {
00580 l_int32     n, nt, i, wt, ht, iclass, size, found, testval;
00581 l_int32    *sumtab;
00582 l_int32     npages, area1, area3;
00583 l_int32    *tab8;
00584 l_float32   rank, x1, y1, x2, y2;
00585 BOX        *box;
00586 NUMA       *naclass, *napage;
00587 NUMA       *nafg;   /* fg area of all instances */
00588 NUMA       *nafgt;  /* fg area of all templates */
00589 JBFINDCTX  *findcontext;
00590 NUMAHASH   *nahash;
00591 PIX        *pix, *pix1, *pix2, *pix3, *pix4;
00592 PIXA       *pixa, *pixa1, *pixa2, *pixat, *pixatd;
00593 PIXAA      *pixaa;
00594 PTA        *pta, *ptac, *ptact;
00595 SEL        *sel;
00596 
00597     PROCNAME("jbClassifyRankHaus");
00598 
00599     if (!classer)
00600         return ERROR_INT("classer not found", procName, 1);
00601     if (!boxa)
00602         return ERROR_INT("boxa not found", procName, 1);
00603     if (!pixas)
00604         return ERROR_INT("pixas not found", procName, 1);
00605 
00606     npages = classer->npages;
00607     size = classer->sizehaus;
00608     sel = selCreateBrick(size, size, size / 2, size / 2, SEL_HIT);
00609 
00610         /* Generate the bordered pixa, with and without dilation.
00611          * pixa1 and pixa2 contain all the input components. */
00612     n = pixaGetCount(pixas);
00613     pixa1 = pixaCreate(n);
00614     pixa2 = pixaCreate(n);
00615     for (i = 0; i < n; i++) {
00616         pix = pixaGetPix(pixas, i, L_CLONE);
00617         pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS,
00618                     JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0);
00619         pix2 = pixDilate(NULL, pix1, sel);
00620         pixaAddPix(pixa1, pix1, L_INSERT);   /* un-dilated */
00621         pixaAddPix(pixa2, pix2, L_INSERT);   /* dilated */
00622         pixDestroy(&pix);
00623     }
00624 
00625         /* Get the centroids of all the bordered images.
00626          * These are relative to the UL corner of each (bordered) pix.  */
00627     pta = pixaCentroids(pixa1);  /* centroids for this page; use here */
00628     ptac = classer->ptac;  /* holds centroids of components up to this page */
00629     ptaJoin(ptac, pta, 0, 0);  /* save centroids of all components */
00630     ptact = classer->ptact;  /* holds centroids of templates */
00631         
00632         /* Use these to save the class and page of each component. */
00633     naclass = classer->naclass;
00634     napage = classer->napage;
00635     sumtab = makePixelSumTab8();
00636 
00637         /* Store the unbordered pix in a pixaa, in a hierarchical
00638          * set of arrays.  There is one pixa for each class,
00639          * and the pix in each pixa are all the instances found
00640          * of that class.  This is actually more than one would need
00641          * for a jbig2 encoder, but there are two reasons to keep
00642          * them around: (1) the set of instances for each class
00643          * can be used to make an improved binary (or, better,
00644          * a grayscale) template, rather than simply using the first
00645          * one in the set; (2) we can investigate the failures
00646          * of the classifier.  This pixaa grows as we process
00647          * successive pages. */
00648     pixaa = classer->pixaa;
00649 
00650         /* arrays to store class exemplars (templates) */
00651     pixat = classer->pixat;   /* un-dilated */
00652     pixatd = classer->pixatd;   /* dilated */
00653 
00654         /* Fill up the pixaa tree with the template exemplars as
00655          * the first pix in each pixa.  As we add each pix,
00656          * we also add the associated box to the pixa.
00657          * We also keep track of the centroid of each pix,
00658          * and use the difference between centroids (of the
00659          * pix with the exemplar we are checking it with)
00660          * to align the two when checking that the Hausdorff
00661          * distance does not exceed a threshold.
00662          * The threshold is set by the Sel used for dilating.
00663          * For example, a 3x3 brick, sel_3, corresponds to a
00664          * Hausdorff distance of 1.  In general, for an NxN brick,
00665          * with N odd, corresponds to a Hausdorff distance of (N - 1)/2.
00666          * It turns out that we actually need to use a sel of size 2x2
00667          * to avoid small bad components when there is a halftone image
00668          * from which components can be chosen.
00669          * The larger the Sel you use, the fewer the number of classes,
00670          * and the greater the likelihood of putting semantically
00671          * different objects in the same class.  For simplicity,
00672          * we do this separately for the case of rank == 1.0 (exact
00673          * match within the Hausdorff distance) and rank < 1.0.  */
00674     rank = classer->rankhaus;
00675     nahash = classer->nahash;
00676     if (rank == 1.0) {
00677         for (i = 0; i < n; i++) {
00678             pix1 = pixaGetPix(pixa1, i, L_CLONE);
00679             pix2 = pixaGetPix(pixa2, i, L_CLONE);
00680             ptaGetPt(pta, i, &x1, &y1);
00681             nt = pixaGetCount(pixat);  /* number of templates */
00682             found = FALSE;
00683             findcontext = findSimilarSizedTemplatesInit(classer, pix1);
00684             while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) {
00685                     /* Find score for this template */
00686                 pix3 = pixaGetPix(pixat, iclass, L_CLONE);
00687                 pix4 = pixaGetPix(pixatd, iclass, L_CLONE);
00688                 ptaGetPt(ptact, iclass, &x2, &y2);
00689                 testval = pixHaustest(pix1, pix2, pix3, pix4, x1 - x2, y1 - y2,
00690                                       MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT);
00691                 pixDestroy(&pix3);
00692                 pixDestroy(&pix4);
00693                 if (testval == 1) {
00694                     found = TRUE;
00695                     numaAddNumber(naclass, iclass);
00696                     numaAddNumber(napage, npages);
00697                     if (classer->keep_pixaa) {
00698                         pixa = pixaaGetPixa(pixaa, iclass, L_CLONE);
00699                         pix = pixaGetPix(pixas, i, L_CLONE);
00700                         pixaAddPix(pixa, pix, L_INSERT);
00701                         box = boxaGetBox(boxa, i, L_CLONE);
00702                         pixaAddBox(pixa, box, L_INSERT);
00703                         pixaDestroy(&pixa);
00704                     }
00705                     break;
00706                 }
00707             }
00708             findSimilarSizedTemplatesDestroy(&findcontext);
00709             if (found == FALSE) {  /* new class */
00710                 numaAddNumber(naclass, nt);
00711                 numaAddNumber(napage, npages);
00712                 pixa = pixaCreate(0);
00713                 pix = pixaGetPix(pixas, i, L_CLONE);  /* unbordered instance */
00714                 pixaAddPix(pixa, pix, L_INSERT);
00715                 wt = pixGetWidth(pix);
00716                 ht = pixGetHeight(pix);
00717                 numaHashAdd(nahash, ht * wt, nt);
00718                 box = boxaGetBox(boxa, i, L_CLONE);
00719                 pixaAddBox(pixa, box, L_INSERT);
00720                 pixaaAddPixa(pixaa, pixa, L_INSERT);  /* unbordered instance */
00721                 ptaAddPt(ptact, x1, y1);
00722                 pixaAddPix(pixat, pix1, L_INSERT);  /* bordered template */
00723                 pixaAddPix(pixatd, pix2, L_INSERT);  /* bordered dil template */
00724             }
00725             else {   /* don't save them */
00726                 pixDestroy(&pix1);
00727                 pixDestroy(&pix2);
00728             }
00729         }
00730     }
00731     else {  /* rank < 1.0 */
00732         if ((nafg = pixaCountPixels(pixas)) == NULL)  /* areas for this page */
00733             return ERROR_INT("nafg not made", procName, 1);
00734         nafgt = classer->nafgt;
00735         tab8 = makePixelSumTab8();
00736         for (i = 0; i < n; i++) {   /* all instances on this page */
00737             pix1 = pixaGetPix(pixa1, i, L_CLONE);
00738             numaGetIValue(nafg, i, &area1);
00739             pix2 = pixaGetPix(pixa2, i, L_CLONE);
00740             ptaGetPt(pta, i, &x1, &y1);   /* use pta for this page */
00741             nt = pixaGetCount(pixat);  /* number of templates */
00742             found = FALSE;
00743             findcontext = findSimilarSizedTemplatesInit(classer, pix1);
00744             while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) {
00745                     /* Find score for this template */
00746                 pix3 = pixaGetPix(pixat, iclass, L_CLONE);
00747                 numaGetIValue(nafgt, iclass, &area3);
00748                 pix4 = pixaGetPix(pixatd, iclass, L_CLONE);
00749                 ptaGetPt(ptact, iclass, &x2, &y2);
00750                 testval = pixRankHaustest(pix1, pix2, pix3, pix4,
00751                                           x1 - x2, y1 - y2,
00752                                           MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT,
00753                                           area1, area3, rank, tab8);
00754                 pixDestroy(&pix3);
00755                 pixDestroy(&pix4);
00756                 if (testval == 1) {  /* greedy match; take the first */
00757                     found = TRUE;
00758                     numaAddNumber(naclass, iclass);
00759                     numaAddNumber(napage, npages);
00760                     if (classer->keep_pixaa) {
00761                         pixa = pixaaGetPixa(pixaa, iclass, L_CLONE);
00762                         pix = pixaGetPix(pixas, i, L_CLONE);
00763                         pixaAddPix(pixa, pix, L_INSERT);
00764                         box = boxaGetBox(boxa, i, L_CLONE);
00765                         pixaAddBox(pixa, box, L_INSERT);
00766                         pixaDestroy(&pixa);
00767                     }
00768                     break;
00769                 }
00770             }
00771             findSimilarSizedTemplatesDestroy(&findcontext);
00772             if (found == FALSE) {  /* new class */
00773                 numaAddNumber(naclass, nt);
00774                 numaAddNumber(napage, npages);
00775                 pixa = pixaCreate(0);
00776                 pix = pixaGetPix(pixas, i, L_CLONE);  /* unbordered instance */
00777                 pixaAddPix(pixa, pix, L_INSERT);
00778                 wt = pixGetWidth(pix);
00779                 ht = pixGetHeight(pix);
00780                 numaHashAdd(nahash, ht * wt, nt);
00781                 box = boxaGetBox(boxa, i, L_CLONE);
00782                 pixaAddBox(pixa, box, L_INSERT);
00783                 pixaaAddPixa(pixaa, pixa, L_INSERT);  /* unbordered instance */
00784                 ptaAddPt(ptact, x1, y1);
00785                 pixaAddPix(pixat, pix1, L_INSERT);  /* bordered template */
00786                 pixaAddPix(pixatd, pix2, L_INSERT);  /* ditto */
00787                 numaAddNumber(nafgt, area1);
00788             }
00789             else {   /* don't save them */
00790                 pixDestroy(&pix1);
00791                 pixDestroy(&pix2);
00792             }
00793         }
00794         FREE(tab8);
00795         numaDestroy(&nafg);
00796     }
00797     classer->nclass = pixaGetCount(pixat);
00798 
00799     FREE(sumtab);
00800     ptaDestroy(&pta);
00801     pixaDestroy(&pixa1);
00802     pixaDestroy(&pixa2);
00803     selDestroy(&sel);
00804     return 0;
00805 }
00806 
00807 
00808 /*!
00809  *  pixHaustest()
00810  *
00811  *      Input:  pix1   (new pix, not dilated)
00812  *              pix2   (new pix, dilated)
00813  *              pix3   (exemplar pix, not dilated)
00814  *              pix4   (exemplar pix, dilated)
00815  *              delx   (x comp of centroid difference)
00816  *              dely   (y comp of centroid difference)
00817  *              maxdiffw (max width difference of pix1 and pix2)
00818  *              maxdiffh (max height difference of pix1 and pix2)
00819  *      Return: 0 (FALSE) if no match, 1 (TRUE) if the new
00820  *              pix is in the same class as the exemplar.
00821  *
00822  *  Note: we check first that the two pix are roughly
00823  *  the same size.  Only if they meet that criterion do
00824  *  we compare the bitmaps.  The Hausdorff is a 2-way
00825  *  check.  The centroid difference is used to align the two
00826  *  images to the nearest integer for each of the checks.
00827  *  These check that the dilated image of one contains
00828  *  ALL the pixels of the undilated image of the other.
00829  *  Checks are done in both direction.  A single pixel not
00830  *  contained in either direction results in failure of the test.
00831  */
00832 l_int32
00833 pixHaustest(PIX       *pix1,
00834             PIX       *pix2,
00835             PIX       *pix3,
00836             PIX       *pix4,
00837             l_float32  delx,   /* x(1) - x(3) */
00838             l_float32  dely,   /* y(1) - y(3) */
00839             l_int32    maxdiffw,
00840             l_int32    maxdiffh)
00841 {
00842 l_int32  wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch;
00843 PIX     *pixt;
00844 
00845         /* Eliminate possible matches based on size difference */
00846     wi = pixGetWidth(pix1);
00847     hi = pixGetHeight(pix1);
00848     wt = pixGetWidth(pix3);
00849     ht = pixGetHeight(pix3);
00850     delw = L_ABS(wi - wt);
00851     if (delw > maxdiffw)
00852         return FALSE;
00853     delh = L_ABS(hi - ht);
00854     if (delh > maxdiffh)
00855         return FALSE;
00856 
00857         /* Round difference in centroid location to nearest integer;
00858          * use this as a shift when doing the matching. */
00859     if (delx >= 0)
00860         idelx = (l_int32)(delx + 0.5);
00861     else
00862         idelx = (l_int32)(delx - 0.5);
00863     if (dely >= 0)
00864         idely = (l_int32)(dely + 0.5);
00865     else
00866         idely = (l_int32)(dely - 0.5);
00867 
00868         /*  Do 1-direction hausdorff, checking that every pixel in pix1
00869          *  is within a dilation distance of some pixel in pix3.  Namely,
00870          *  that pix4 entirely covers pix1:
00871          *       pixt = pixSubtract(NULL, pix1, pix4), including shift
00872          *  where pixt has no ON pixels.  */
00873     pixt = pixCreateTemplate(pix1);
00874     pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC, pix1, 0, 0);
00875     pixRasterop(pixt, idelx, idely, wi, hi, PIX_DST & PIX_NOT(PIX_SRC),
00876                 pix4, 0, 0);
00877     pixZero(pixt, &boolmatch);
00878     if (boolmatch == 0) {
00879         pixDestroy(&pixt);
00880         return FALSE;
00881     }
00882 
00883         /*  Do 1-direction hausdorff, checking that every pixel in pix3
00884          *  is within a dilation distance of some pixel in pix1.  Namely,
00885          *  that pix2 entirely covers pix3:
00886          *      pixSubtract(pixt, pix3, pix2), including shift
00887          *  where pixt has no ON pixels. */
00888     pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix3, 0, 0);
00889     pixRasterop(pixt, 0, 0, wt, ht, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0);
00890     pixZero(pixt, &boolmatch);
00891     pixDestroy(&pixt);
00892     return boolmatch;
00893 }
00894 
00895 
00896 /*!
00897  *  pixRankHaustest()
00898  *
00899  *      Input:  pix1   (new pix, not dilated)
00900  *              pix2   (new pix, dilated)
00901  *              pix3   (exemplar pix, not dilated)
00902  *              pix4   (exemplar pix, dilated)
00903  *              delx   (x comp of centroid difference)
00904  *              dely   (y comp of centroid difference)
00905  *              maxdiffw (max width difference of pix1 and pix2)
00906  *              maxdiffh (max height difference of pix1 and pix2)
00907  *              area1  (fg pixels in pix1)
00908  *              area3  (fg pixels in pix3)
00909  *              rank   (rank value of test, each way)
00910  *              tab8   (table of pixel sums for byte)
00911  *      Return: 0 (FALSE) if no match, 1 (TRUE) if the new
00912  *                 pix is in the same class as the exemplar.
00913  *
00914  *  Note: we check first that the two pix are roughly
00915  *  the same size.  Only if they meet that criterion do
00916  *  we compare the bitmaps.  We convert the rank value to
00917  *  a number of pixels by multiplying the rank fraction by the number
00918  *  of pixels in the undilated image.  The Hausdorff is a 2-way
00919  *  check.  The centroid difference is used to align the two
00920  *  images to the nearest integer for each of the checks.
00921  *  The rank hausdorff checks that the dilated image of one
00922  *  contains the rank fraction of the pixels of the undilated
00923  *  image of the other.   Checks are done in both direction. 
00924  *  Failure of the test in either direction results in failure
00925  *  of the test.
00926  */
00927 l_int32
00928 pixRankHaustest(PIX       *pix1,
00929                 PIX       *pix2,
00930                 PIX       *pix3,
00931                 PIX       *pix4,
00932                 l_float32  delx,   /* x(1) - x(3) */
00933                 l_float32  dely,   /* y(1) - y(3) */
00934                 l_int32    maxdiffw,
00935                 l_int32    maxdiffh,
00936                 l_int32    area1,
00937                 l_int32    area3,
00938                 l_float32  rank,
00939                 l_int32   *tab8)
00940 {
00941 l_int32  wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch;
00942 l_int32  thresh1, thresh3;
00943 PIX     *pixt;
00944 
00945         /* Eliminate possible matches based on size difference */
00946     wi = pixGetWidth(pix1);
00947     hi = pixGetHeight(pix1);
00948     wt = pixGetWidth(pix3);
00949     ht = pixGetHeight(pix3);
00950     delw = L_ABS(wi - wt);
00951     if (delw > maxdiffw)
00952         return FALSE;
00953     delh = L_ABS(hi - ht);
00954     if (delh > maxdiffh)
00955         return FALSE;
00956 
00957         /* Upper bounds in remaining pixels for allowable match */
00958     thresh1 = (l_int32)(area1 * (1. - rank) + 0.5);
00959     thresh3 = (l_int32)(area3 * (1. - rank) + 0.5);
00960 
00961         /* Round difference in centroid location to nearest integer;
00962          * use this as a shift when doing the matching. */
00963     if (delx >= 0)
00964         idelx = (l_int32)(delx + 0.5);
00965     else
00966         idelx = (l_int32)(delx - 0.5);
00967     if (dely >= 0)
00968         idely = (l_int32)(dely + 0.5);
00969     else
00970         idely = (l_int32)(dely - 0.5);
00971 
00972         /*  Do 1-direction hausdorff, checking that every pixel in pix1
00973          *  is within a dilation distance of some pixel in pix3.  Namely,
00974          *  that pix4 entirely covers pix1:
00975          *       pixt = pixSubtract(NULL, pix1, pix4), including shift
00976          *  where pixt has no ON pixels.  */
00977     pixt = pixCreateTemplate(pix1);
00978     pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC, pix1, 0, 0);
00979     pixRasterop(pixt, idelx, idely, wi, hi, PIX_DST & PIX_NOT(PIX_SRC),
00980                 pix4, 0, 0);
00981     pixThresholdPixelSum(pixt, thresh1, &boolmatch, tab8);
00982     if (boolmatch == 1) { /* above thresh1 */
00983         pixDestroy(&pixt);
00984         return FALSE;
00985     }
00986 
00987         /*  Do 1-direction hausdorff, checking that every pixel in pix3
00988          *  is within a dilation distance of some pixel in pix1.  Namely,
00989          *  that pix2 entirely covers pix3:
00990          *      pixSubtract(pixt, pix3, pix2), including shift
00991          *  where pixt has no ON pixels. */
00992     pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix3, 0, 0);
00993     pixRasterop(pixt, 0, 0, wt, ht, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0);
00994     pixThresholdPixelSum(pixt, thresh3, &boolmatch, tab8);
00995     pixDestroy(&pixt);
00996     if (boolmatch == 1)  /* above thresh3 */
00997         return FALSE;
00998     else
00999         return TRUE;
01000 }
01001 
01002 
01003 /*----------------------------------------------------------------------*
01004  *            Classification using windowed correlation score           *
01005  *----------------------------------------------------------------------*/
01006 /*!
01007  *  jbClassifyCorrelation()
01008  *
01009  *      Input:  jbclasser
01010  *              boxa (of new components for classification)
01011  *              pixas (of new components for classification)
01012  *      Return: 0 if OK; 1 on error
01013  */
01014 l_int32
01015 jbClassifyCorrelation(JBCLASSER  *classer,
01016                       BOXA       *boxa,
01017                       PIXA       *pixas)
01018 {
01019 l_int32     n, nt, i, iclass, wt, ht, found, area, area1, area2, npages,
01020             overthreshold;
01021 l_int32    *sumtab, *centtab;
01022 l_uint32   *row, word;
01023 l_float32   x1, y1, x2, y2, xsum, ysum;
01024 l_float32   thresh, weight, threshold;
01025 BOX        *box;
01026 NUMA       *naclass, *napage;
01027 NUMA       *nafgt;   /* fg area of all templates */
01028 NUMA       *naarea;   /* w * h area of all templates */
01029 JBFINDCTX  *findcontext;
01030 NUMAHASH   *nahash;
01031 PIX        *pix, *pix1, *pix2;
01032 PIXA       *pixa, *pixa1, *pixat;
01033 PIXAA      *pixaa;
01034 PTA        *pta, *ptac, *ptact;
01035 l_int32    *pixcts;  /* pixel counts of each pixa */
01036 l_int32   **pixrowcts;  /* row-by-row pixel counts of each pixa */
01037 l_int32     x, y, rowcount, downcount, wpl;
01038 l_uint8     byte;
01039 
01040     PROCNAME("jbClassifyCorrelation");
01041 
01042     if (!classer)
01043         return ERROR_INT("classer not found", procName, 1);
01044     if (!boxa)
01045         return ERROR_INT("boxa not found", procName, 1);
01046     if (!pixas)
01047         return ERROR_INT("pixas not found", procName, 1);
01048 
01049     npages = classer->npages;
01050 
01051         /* Generate the bordered pixa, which contains all the the
01052          * input components.  This will not be saved.   */
01053     n = pixaGetCount(pixas);
01054     pixa1 = pixaCreate(n);
01055     for (i = 0; i < n; i++) {
01056         pix = pixaGetPix(pixas, i, L_CLONE);
01057         pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS,
01058                     JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0);
01059         pixaAddPix(pixa1, pix1, L_INSERT);
01060         pixDestroy(&pix);
01061     }
01062 
01063         /* Use these to save the class and page of each component. */
01064     naclass = classer->naclass;
01065     napage = classer->napage;
01066 
01067         /* Get the number of fg pixels in each component.  */
01068     nafgt = classer->nafgt;    /* holds fg areas of the templates */
01069     sumtab = makePixelSumTab8();
01070 
01071     pixcts = (l_int32 *)CALLOC(n, sizeof(*pixcts));
01072     pixrowcts = (l_int32 **)CALLOC(n, sizeof(*pixrowcts));
01073     centtab = makePixelCentroidTab8();
01074     if (!pixcts || !pixrowcts || !centtab)
01075         return ERROR_INT("calloc fail in pix*cts or centtab", procName, 1);
01076 
01077         /* Count the "1" pixels in each row of the pix in pixa1; this
01078          * allows pixCorrelationScoreThresholded to abort early if a match
01079          * is impossible.  This loop merges three calculations: the total
01080          * number of "1" pixels, the number of "1" pixels in each row, and
01081          * the centroid.  The centroids are relative to the UL corner of
01082          * each (bordered) pix.  The pixrowcts[i][y] are the total number
01083          * of fg pixels in pixa[i] below row y. */
01084     pta = ptaCreate(n);
01085     for (i = 0; i < n; i++) {
01086         pix = pixaGetPix(pixa1, i, L_CLONE);
01087         pixrowcts[i] = (l_int32 *)CALLOC(pixGetHeight(pix),
01088                                          sizeof(**pixrowcts));
01089         xsum = 0;
01090         ysum = 0;
01091         wpl = pixGetWpl(pix);
01092         row = pixGetData(pix) + (pixGetHeight(pix) - 1) * wpl;
01093         downcount = 0;
01094         for (y = pixGetHeight(pix) - 1; y >= 0; y--, row -= wpl) {
01095             pixrowcts[i][y] = downcount;
01096             rowcount = 0;
01097             for (x = 0; x < wpl; x++) {
01098                 word = row[x];
01099                 byte = word & 0xff;
01100                 rowcount += sumtab[byte];
01101                 xsum += centtab[byte] + (x * 32 + 24) * sumtab[byte];
01102                 byte = (word >> 8) & 0xff;
01103                 rowcount += sumtab[byte];
01104                 xsum += centtab[byte] + (x * 32 + 16) * sumtab[byte];
01105                 byte = (word >> 16) & 0xff;
01106                 rowcount += sumtab[byte];
01107                 xsum += centtab[byte] + (x * 32 + 8) * sumtab[byte];
01108                 byte = (word >> 24) & 0xff;
01109                 rowcount += sumtab[byte];
01110                 xsum += centtab[byte] + x * 32 * sumtab[byte];
01111             }
01112             downcount += rowcount;
01113             ysum += rowcount * y;
01114         }
01115         pixcts[i] = downcount;
01116         ptaAddPt(pta,
01117                  xsum / (l_float32)downcount, ysum / (l_float32)downcount);
01118         pixDestroy(&pix);
01119     }
01120 
01121     ptac = classer->ptac;  /* holds centroids of components up to this page */
01122     ptaJoin(ptac, pta, 0, 0);  /* save centroids of all components */
01123     ptact = classer->ptact;  /* holds centroids of templates */
01124 
01125     /* Store the unbordered pix in a pixaa, in a hierarchical
01126      * set of arrays.  There is one pixa for each class,
01127      * and the pix in each pixa are all the instances found
01128      * of that class.  This is actually more than one would need
01129      * for a jbig2 encoder, but there are two reasons to keep
01130      * them around: (1) the set of instances for each class
01131      * can be used to make an improved binary (or, better,
01132      * a grayscale) template, rather than simply using the first
01133      * one in the set; (2) we can investigate the failures
01134      * of the classifier.  This pixaa grows as we process
01135      * successive pages. */
01136     pixaa = classer->pixaa;
01137 
01138         /* Array to store class exemplars */
01139     pixat = classer->pixat;
01140 
01141         /* Fill up the pixaa tree with the template exemplars as
01142          * the first pix in each pixa.  As we add each pix,
01143          * we also add the associated box to the pixa.
01144          * We also keep track of the centroid of each pix,
01145          * and use the difference between centroids (of the
01146          * pix with the exemplar we are checking it with)
01147          * to align the two when checking that the correlation
01148          * score exceeds a threshold.  The correlation score
01149          * is given by the square of the area of the AND
01150          * between aligned instance and template, divided by
01151          * the product of areas of each image.  For identical
01152          * template and instance, the score is 1.0.
01153          * If the threshold is too small, non-equivalent instances
01154          * will be placed in the same class; if too large, there will
01155          * be an unnecessary division of classes representing the
01156          * same character.  The weightfactor adds in some of the
01157          * difference (1.0 - thresh), depending on the heaviness
01158          * of the template (measured as the fraction of fg pixels). */
01159     thresh = classer->thresh;
01160     weight = classer->weightfactor;
01161     naarea = classer->naarea;
01162     nahash = classer->nahash;
01163     for (i = 0; i < n; i++) {
01164         pix1 = pixaGetPix(pixa1, i, L_CLONE);
01165         area1 = pixcts[i];
01166         ptaGetPt(pta, i, &x1, &y1);  /* centroid for this instance */
01167         nt = pixaGetCount(pixat);
01168         found = FALSE;
01169         findcontext = findSimilarSizedTemplatesInit(classer, pix1);
01170         while ( (iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) {
01171                 /* Get the template */
01172             pix2 = pixaGetPix(pixat, iclass, L_CLONE);
01173             numaGetIValue(nafgt, iclass, &area2);
01174             ptaGetPt(ptact, iclass, &x2, &y2);  /* template centroid */
01175 
01176                 /* Find threshold for this template */
01177             if (weight > 0.0) {
01178                 numaGetIValue(naarea, iclass, &area);
01179                 threshold = thresh + (1. - thresh) * weight * area2 / area;
01180             }
01181             else
01182                 threshold = thresh;
01183 
01184                 /* Find score for this template */
01185             overthreshold = pixCorrelationScoreThresholded(pix1, pix2,
01186                                                            area1, area2,
01187                                                            x1 - x2, y1 - y2,
01188                                                            MAX_DIFF_WIDTH,
01189                                                            MAX_DIFF_HEIGHT,
01190                                                            sumtab,
01191                                                            pixrowcts[i],
01192                                                            threshold);
01193 #if DEBUG_CORRELATION_SCORE
01194             {
01195                 l_float32 score, testscore;
01196                 l_int32 count, testcount;
01197                 score = pixCorrelationScore(pix1, pix2, area1, area2,
01198                                             x1 - x2, y1 - y2,
01199                                             MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT,
01200                                             sumtab);
01201 
01202                 testscore = pixCorrelationScoreSimple(pix1, pix2, area1, area2,
01203                                   x1 - x2, y1 - y2, MAX_DIFF_WIDTH,
01204                                   MAX_DIFF_HEIGHT, sumtab);
01205                 count = (l_int32)rint(sqrt(score * area1 * area2));
01206                 testcount = (l_int32)rint(sqrt(testscore * area1 * area2));
01207                 if ((score >= threshold) != (testscore >= threshold)) {
01208                     fprintf(stderr, "Correlation score mismatch: %d(%g,%d) vs %d(%g,%d) (%g)\n",
01209                             count, score, score >= threshold,
01210                             testcount, testscore, testscore >= threshold,
01211                             score - testscore);
01212                 }
01213 
01214                 if ((score >= threshold) != overthreshold) {
01215                     fprintf(stderr, "Mismatch between correlation/threshold comparison: %g(%g,%d) >= %g(%g) vs %s\n",
01216                             score, score*area1*area2, count, threshold, threshold*area1*area2, (overthreshold ? "true" : "false"));
01217                 }
01218             }
01219 #endif  /* DEBUG_CORRELATION_SCORE */
01220             pixDestroy(&pix2);
01221 
01222             if (overthreshold) {  /* greedy match */
01223                 found = TRUE;
01224                 numaAddNumber(naclass, iclass);
01225                 numaAddNumber(napage, npages);
01226                 if (classer->keep_pixaa) {
01227                         /* We are keeping a record of all components */
01228                     pixa = pixaaGetPixa(pixaa, iclass, L_CLONE);
01229                     pix = pixaGetPix(pixas, i, L_CLONE);
01230                     pixaAddPix(pixa, pix, L_INSERT);
01231                     box = boxaGetBox(boxa, i, L_CLONE);
01232                     pixaAddBox(pixa, box, L_INSERT);
01233                     pixaDestroy(&pixa);
01234                 }
01235                 break;
01236             }
01237         }
01238         findSimilarSizedTemplatesDestroy(&findcontext);
01239         if (found == FALSE) {  /* new class */
01240             numaAddNumber(naclass, nt);
01241             numaAddNumber(napage, npages);
01242             pixa = pixaCreate(0);
01243             pix = pixaGetPix(pixas, i, L_CLONE);  /* unbordered instance */
01244             pixaAddPix(pixa, pix, L_INSERT);
01245             wt = pixGetWidth(pix);
01246             ht = pixGetHeight(pix);
01247             numaHashAdd(nahash, ht * wt, nt);
01248             box = boxaGetBox(boxa, i, L_CLONE);
01249             pixaAddBox(pixa, box, L_INSERT);
01250             pixaaAddPixa(pixaa, pixa, L_INSERT);  /* unbordered instance */
01251             ptaAddPt(ptact, x1, y1);
01252             numaAddNumber(nafgt, area1);
01253             pixaAddPix(pixat, pix1, L_INSERT);   /* bordered template */
01254             area = (pixGetWidth(pix1) - 2 * JB_ADDED_PIXELS) *
01255                    (pixGetHeight(pix1) - 2 * JB_ADDED_PIXELS);
01256             numaAddNumber(naarea, area);
01257         }
01258         else {   /* don't save it */
01259             pixDestroy(&pix1);
01260         }
01261     }
01262     classer->nclass = pixaGetCount(pixat);
01263 
01264     FREE(pixcts);
01265     FREE(centtab);
01266     for (i = 0; i < n; i++) {
01267         FREE(pixrowcts[i]);
01268     }
01269     FREE(pixrowcts);
01270 
01271     FREE(sumtab);
01272     ptaDestroy(&pta);
01273     pixaDestroy(&pixa1);
01274     return 0;
01275 }
01276 
01277 
01278 /*----------------------------------------------------------------------*
01279  *             Determine the image components we start with             *
01280  *----------------------------------------------------------------------*/
01281 /*!
01282  *  jbGetComponents()
01283  *
01284  *      Input:  pixs (1 bpp)
01285  *              components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS)
01286  *              maxwidth, maxheight (of saved components; larger are discarded)
01287  *              &pboxa (<return> b.b. of component items)
01288  *              &ppixa (<return> component items)
01289  *      Return: 0 if OK, 1 on error
01290  */
01291 l_int32
01292 jbGetComponents(PIX     *pixs,
01293                 l_int32  components,
01294                 l_int32  maxwidth,
01295                 l_int32  maxheight,
01296                 BOXA   **pboxad,
01297                 PIXA   **ppixad)
01298 {
01299 l_int32    empty, res, redfactor;
01300 BOXA      *boxa;
01301 PIX       *pixt1, *pixt2, *pixt3;
01302 PIXA      *pixa, *pixat;
01303 
01304     PROCNAME("jbGetComponents");
01305 
01306     if (!pboxad)
01307         return ERROR_INT("&boxad not defined", procName, 1);
01308     *pboxad = NULL;
01309     if (!ppixad)
01310         return ERROR_INT("&pixad not defined", procName, 1);
01311     *ppixad = NULL;
01312     if (!pixs)
01313         return ERROR_INT("pixs not defined", procName, 1);
01314     if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
01315         components != JB_WORDS)
01316         return ERROR_INT("invalid components", procName, 1);
01317 
01318     pixZero(pixs, &empty);
01319     if (empty) {
01320         *pboxad = boxaCreate(0);
01321         *ppixad = pixaCreate(0);
01322         return 0;
01323     }
01324 
01325         /* If required, preprocess input pixs.  The method for both
01326          * characters and words is to generate a connected component
01327          * mask over the units that we want to aggregrate, which are,
01328          * in general, sets of related connected components in pixs.
01329          * For characters, we want to include the dots with
01330          * 'i', 'j' and '!', so we do a small vertical closing to
01331          * generate the mask.  For words, we make a mask over all
01332          * characters in each word.  This is a bit more tricky, because
01333          * the spacing between words is difficult to predict a priori,
01334          * and words can be typeset with variable spacing that can
01335          * in some cases be barely larger than the space between
01336          * characters.  The first step is to generate the mask and
01337          * identify each of its connected components.  */
01338     if (components == JB_CONN_COMPS) {  /* no preprocessing */
01339         boxa = pixConnComp(pixs, &pixa, 8);
01340     } 
01341     else if (components == JB_CHARACTERS) {
01342         pixt1 = pixMorphSequence(pixs, "c1.6", 0);
01343         boxa = pixConnComp(pixt1, &pixat, 8);
01344         pixa = pixaClipToPix(pixat, pixs);
01345         pixDestroy(&pixt1);
01346         pixaDestroy(&pixat);
01347     } 
01348     else {  /* components == JB_WORDS */
01349 
01350             /* Do the operations at about 150 ppi resolution.
01351              * It is much faster at 75 ppi, but the results are
01352              * more accurate at 150 ppi.  This will segment the
01353              * words in body text.  It can be expected that relatively
01354              * infrequent words in a larger font will be split. */
01355         res = pixGetXRes(pixs);
01356         if (res <= 200) {
01357             redfactor = 1;
01358             pixt1 = pixClone(pixs);
01359         }
01360         else if (res <= 400) {
01361             redfactor = 2;
01362             pixt1 = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0);
01363         }
01364         else {
01365             redfactor = 4;
01366             pixt1 = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0);
01367         }
01368 
01369         pixt2 = pixWordMaskByDilation(pixt1, 0, NULL);
01370 
01371             /* Expand the optimally dilated word mask to full res. */
01372         pixt3 = pixExpandReplicate(pixt2, redfactor);
01373 
01374             /* Pull out the pixels in pixs corresponding to the mask
01375              * components in pixt3.  Note that above we used threshold
01376              * levels in the reduction of 1 to insure that the resulting
01377              * mask fully covers the input pixs.  The downside of using
01378              * a threshold of 1 is that very close characters from adjacent
01379              * lines can be joined.  But with a level of 2 or greater,
01380              * it is necessary to use a seedfill, followed by a pixOr():
01381              *       pixt4 = pixSeedfillBinary(NULL, pixt3, pixs, 8);
01382              *       pixOr(pixt3, pixt3, pixt4);
01383              * to insure that the mask coverage is complete over pixs.  */
01384         boxa = pixConnComp(pixt3, &pixat, 4);
01385         pixa = pixaClipToPix(pixat, pixs);
01386         pixaDestroy(&pixat);
01387         pixDestroy(&pixt1);
01388         pixDestroy(&pixt2);
01389         pixDestroy(&pixt3);
01390     }
01391 
01392         /* Remove large components, and save the results.  */
01393     *ppixad = pixaSelectBySize(pixa, maxwidth, maxheight, L_SELECT_IF_BOTH,
01394                                L_SELECT_IF_LTE, NULL);
01395     *pboxad = boxaSelectBySize(boxa, maxwidth, maxheight, L_SELECT_IF_BOTH,
01396                                L_SELECT_IF_LTE, NULL);
01397     pixaDestroy(&pixa);
01398     boxaDestroy(&boxa);
01399 
01400     return 0;
01401 }
01402 
01403 
01404 /*!
01405  *  pixWordMaskByDilation()
01406  *
01407  *      Input:  pixs (1 bpp; typ. at 75 to 150 ppi)
01408  *              maxsize (use 0 for default; not to exceed 14)
01409  *              &size (<optional return> size of optimal horiz Sel)
01410  *      Return: pixd (dilated word mask), or null on error
01411  *
01412  *  Notes:
01413  *      (1) For 75 to 150 ppi, the optimal dilation should not exceed 7.
01414  *          This is the default size chosen if maxsize <= 0.
01415  *      (2) To run this on images at resolution between 200 and 300, it
01416  *          is advisable to use a larger maxsize, say between 10 and 14.
01417  *      (3) The best size for dilating to get word masks is optionally returned.
01418  */
01419 PIX *
01420 pixWordMaskByDilation(PIX      *pixs,
01421                       l_int32   maxsize,
01422                       l_int32  *psize)
01423 {
01424 l_int32  i, diffmin, ndiff, imin;
01425 l_int32  ncc[MAX_ALLOWED_DILATION + 1];
01426 BOXA    *boxa;
01427 NUMA    *nacc;
01428 PIX     *pixt1, *pixt2, *pixt3;
01429 PIXA    *pixa;
01430 SEL     *sel;
01431 
01432     PROCNAME("pixWordMaskbyDilation");
01433 
01434     if (!pixs)
01435         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01436 
01437         /* Find the optimal dilation to create the word mask.
01438          * Look for successively increasing dilations where the
01439          * number of connected components doesn't decrease.
01440          * This is the situation where the components in the
01441          * word mask should properly cover each word.  Use of
01442          * 4 cc slightly reduces the likelihood that words from
01443          * different lines are joined.  */
01444     diffmin = 1000000;
01445     pixa = pixaCreate(8);
01446     pixt1 = pixCopy(NULL, pixs);
01447     pixaAddPix(pixa, pixt1, L_COPY); 
01448 
01449     if (maxsize <= 0)
01450         maxsize = 7;  /* default */
01451     if (maxsize > MAX_ALLOWED_DILATION)
01452         maxsize = MAX_ALLOWED_DILATION;
01453     nacc = numaCreate(maxsize);
01454     for (i = 0; i <= maxsize; i++) {
01455         if (i == 0)     /* first one not dilated */
01456             pixt2 = pixCopy(NULL, pixt1); 
01457         else    /* successive dilation by sel_2h */
01458             pixt2 = pixMorphSequence(pixt1, "d2.1", 0);
01459         boxa = pixConnCompBB(pixt2, 4);
01460         ncc[i] = boxaGetCount(boxa);
01461         numaAddNumber(nacc, ncc[i]);
01462         if (i > 0) {
01463             ndiff = ncc[i - 1] - ncc[i];
01464 #if  DEBUG_PLOT_CC
01465             fprintf(stderr, "ndiff[%d] = %d\n", i - 1, ndiff);
01466 #endif  /* DEBUG_PLOT_CC */
01467             if (ndiff < diffmin) {
01468                 imin = i;
01469                 diffmin = ndiff;
01470             }
01471         }
01472         pixaAddPix(pixa, pixt2, L_COPY);
01473         pixDestroy(&pixt1);
01474         pixt1 = pixt2;
01475         boxaDestroy(&boxa);
01476     }
01477     pixDestroy(&pixt1);
01478 
01479 #if  DEBUG_PLOT_CC
01480     {GPLOT *gplot;
01481      NUMA  *naseq;
01482         naseq = numaMakeSequence(1, 1, numaGetCount(nacc));
01483         gplot = gplotCreate("/tmp/cc_output", GPLOT_PNG,
01484                             "Number of cc vs. horizontal dilation",
01485                             "Sel horiz", "Number of cc");
01486         gplotAddPlot(gplot, naseq, nacc, GPLOT_LINES, "");
01487         gplotMakeOutput(gplot);
01488         gplotDestroy(&gplot);
01489         numaDestroy(&naseq);
01490     }
01491 #endif  /* DEBUG_PLOT_CC */
01492 
01493         /* Save the result of the optimal dilation */
01494     pixt2 = pixaGetPix(pixa, imin, L_CLONE);
01495     sel = selCreateBrick(1, imin, 0, imin - 1, SEL_HIT);
01496     pixt3 = pixErode(NULL, pixt2, sel);  /* remove effect of dilation */
01497     selDestroy(&sel);
01498     pixDestroy(&pixt2);
01499     pixaDestroy(&pixa);
01500     if (psize)
01501         *psize = imin + 1;
01502 
01503     numaDestroy(&nacc);
01504     return pixt3;
01505 }
01506 
01507 
01508 /*----------------------------------------------------------------------*
01509  *                 Build grayscale composites (templates)               *
01510  *----------------------------------------------------------------------*/
01511 /*!
01512  *  jbAccumulateComposites()
01513  *
01514  *      Input:  pixaa (one pixa for each class)
01515  *              &pna (<return> number of samples used to build each composite)
01516  *              &ptat (<return> centroids of bordered composites)
01517  *      Return: pixad (accumulated sum of samples in each class),
01518  *                     or null on error
01519  *
01520  */
01521 PIXA *
01522 jbAccumulateComposites(PIXAA  *pixaa,
01523                        NUMA  **pna,
01524                        PTA   **pptat)
01525 {
01526 l_int32    n, nt, i, j, d, minw, maxw, minh, maxh, xdiff, ydiff;
01527 l_float32  x, y, xave, yave;
01528 NUMA      *na;
01529 PIX       *pix, *pixt1, *pixt2, *pixsum;
01530 PIXA      *pixa, *pixad;
01531 PTA       *ptat, *pta;
01532 
01533     PROCNAME("jbAccumulateComposites");
01534 
01535     if (!pptat)
01536         return (PIXA *)ERROR_PTR("&ptat not defined", procName, NULL);
01537     *pptat = NULL;
01538     if (!pna)
01539         return (PIXA *)ERROR_PTR("&na not defined", procName, NULL);
01540     *pna = NULL;
01541     if (!pixaa)
01542         return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL);
01543 
01544     n = pixaaGetCount(pixaa);
01545     if ((ptat = ptaCreate(n)) == NULL)
01546         return (PIXA *)ERROR_PTR("ptat not made", procName, NULL);
01547     *pptat = ptat;
01548     pixad = pixaCreate(n);
01549     na = numaCreate(n);
01550     *pna = na;
01551 
01552     for (i = 0; i < n; i++) {
01553         pixa = pixaaGetPixa(pixaa, i, L_CLONE);
01554         nt = pixaGetCount(pixa);
01555         numaAddNumber(na, nt);
01556         if (nt == 0) {
01557             L_WARNING("empty pixa found!", procName);
01558             pixaDestroy(&pixa);
01559             continue;
01560         }
01561         pixaSizeRange(pixa, &minw, &minh, &maxw, &maxh);
01562         pix = pixaGetPix(pixa, 0, L_CLONE);
01563         d = pixGetDepth(pix);
01564         pixDestroy(&pix);
01565         pixt1 = pixCreate(maxw, maxh, d);
01566         pixsum = pixInitAccumulate(maxw, maxh, 0);
01567         pta = pixaCentroids(pixa);
01568 
01569             /* Find the average value of the centroids ... */
01570         xave = yave = 0;
01571         for (j = 0; j < nt; j++) {
01572             ptaGetPt(pta, j, &x, &y);
01573             xave += x;
01574             yave += y;
01575         }
01576         xave = xave / (l_float32)nt;
01577         yave = yave / (l_float32)nt;
01578 
01579             /* and place all centroids at their average value */
01580         for (j = 0; j < nt; j++) {
01581             pixt2 = pixaGetPix(pixa, j, L_CLONE);
01582             ptaGetPt(pta, j, &x, &y);
01583             xdiff = (l_int32)(x - xave);
01584             ydiff = (l_int32)(y - yave);
01585             pixClearAll(pixt1);
01586             pixRasterop(pixt1, xdiff, ydiff, maxw, maxh, PIX_SRC,
01587                         pixt2, 0, 0);
01588             pixAccumulate(pixsum, pixt1, L_ARITH_ADD);
01589             pixDestroy(&pixt2);
01590         }
01591         pixaAddPix(pixad, pixsum, L_INSERT);
01592         ptaAddPt(ptat, xave, yave);
01593 
01594         pixaDestroy(&pixa);
01595         pixDestroy(&pixt1);
01596         ptaDestroy(&pta);
01597     }
01598 
01599     return pixad;
01600 }
01601 
01602 
01603 /*!
01604  *  jbTemplatesFromComposites()
01605  *
01606  *      Input:  pixac (one pix of composites for each class)
01607  *              na (number of samples used for each class composite)
01608  *      Return: pixad (8 bpp templates for each class), or null on error
01609  *
01610  */
01611 PIXA *
01612 jbTemplatesFromComposites(PIXA  *pixac,
01613                           NUMA  *na)
01614 {
01615 l_int32    n, i;
01616 l_float32  nt;  /* number of samples in the composite; always an integer */
01617 l_float32  factor;
01618 PIX       *pixsum;   /* accumulated composite */
01619 PIX       *pixd;
01620 PIXA      *pixad;
01621 
01622     PROCNAME("jbTemplatesFromComposites");
01623 
01624     if (!pixac)
01625         return (PIXA *)ERROR_PTR("pixac not defined", procName, NULL);
01626     if (!na)
01627         return (PIXA *)ERROR_PTR("na not defined", procName, NULL);
01628 
01629     n = pixaGetCount(pixac);
01630     pixad = pixaCreate(n);
01631     for (i = 0; i < n; i++) {
01632         pixsum = pixaGetPix(pixac, i, L_COPY);  /* changed internally */
01633         numaGetFValue(na, i, &nt);
01634         factor = 255. / nt;
01635         pixMultConstAccumulate(pixsum, factor, 0);  /* changes pixsum */
01636         pixd = pixFinalAccumulate(pixsum, 0, 8);
01637         pixaAddPix(pixad, pixd, L_INSERT);
01638         pixDestroy(&pixsum);
01639     }
01640 
01641     return pixad;
01642 }
01643 
01644 
01645 
01646 /*----------------------------------------------------------------------*
01647  *                       jbig2 utility routines                         *
01648  *----------------------------------------------------------------------*/
01649 /*!
01650  *  jbClasserCreate()
01651  *
01652  *      Input:  method (JB_RANKHAUS, JB_CORRELATION) 
01653  *              components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS)
01654  *      Return: jbclasser, or null on error
01655  */
01656 JBCLASSER *
01657 jbClasserCreate(l_int32  method,
01658                 l_int32  components)
01659 {
01660 JBCLASSER  *classer;
01661 
01662     PROCNAME("jbClasserCreate");
01663 
01664     if ((classer = (JBCLASSER *)CALLOC(1, sizeof(JBCLASSER))) == NULL)
01665         return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL);
01666     if (method != JB_RANKHAUS && method != JB_CORRELATION)
01667         return (JBCLASSER *)ERROR_PTR("invalid type", procName, NULL);
01668     if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
01669         components != JB_WORDS)
01670         return (JBCLASSER *)ERROR_PTR("invalid type", procName, NULL);
01671 
01672     classer->method = method;
01673     classer->components = components;
01674     classer->nacomps = numaCreate(0);
01675     classer->pixaa = pixaaCreate(0);
01676     classer->pixat = pixaCreate(0);
01677     classer->pixatd = pixaCreate(0);
01678     classer->nafgt = numaCreate(0);
01679     classer->naarea = numaCreate(0);
01680     classer->ptac = ptaCreate(0);
01681     classer->ptact = ptaCreate(0);
01682     classer->naclass = numaCreate(0);
01683     classer->napage = numaCreate(0);
01684     classer->ptaul = ptaCreate(0);
01685 
01686     return classer;
01687 }
01688 
01689 
01690 /*
01691  *  jbClasserDestroy()
01692  *
01693  *      Input: &classer (<to be nulled>)
01694  *      Return: void
01695  */
01696 void
01697 jbClasserDestroy(JBCLASSER  **pclasser)
01698 {
01699 JBCLASSER  *classer;
01700 
01701     if (!pclasser)
01702         return;
01703     if ((classer = *pclasser) == NULL)
01704         return;
01705 
01706     sarrayDestroy(&classer->safiles);
01707     numaDestroy(&classer->nacomps);
01708     pixaaDestroy(&classer->pixaa);
01709     pixaDestroy(&classer->pixat);
01710     pixaDestroy(&classer->pixatd);
01711     numaHashDestroy(&classer->nahash);
01712     numaDestroy(&classer->nafgt);
01713     numaDestroy(&classer->naarea);
01714     ptaDestroy(&classer->ptac);
01715     ptaDestroy(&classer->ptact);
01716     numaDestroy(&classer->naclass);
01717     numaDestroy(&classer->napage);
01718     ptaDestroy(&classer->ptaul);
01719     ptaDestroy(&classer->ptall);
01720     FREE(classer);
01721     *pclasser = NULL;
01722     return;
01723 }
01724     
01725 
01726 /*!
01727  *  jbDataSave()
01728  *
01729  *      Input:  jbclasser
01730  *              latticew, latticeh (cell size used to store each
01731  *                  connected component in the composite)
01732  *      Return: jbdata, or null on error
01733  *
01734  *  Notes:
01735  *      (1) This routine stores the jbig2-type data required for
01736  *          generating a lossy jbig2 version of the image.
01737  *          It can be losslessly written to (and read from) two files.
01738  *      (2) It generates and stores the mosaic of templates.
01739  *      (3) It clones the Numa and Pta arrays, so these must all
01740  *          be destroyed by the caller.
01741  *      (4) Input 0 to use the default values for latticew and/or latticeh,
01742  */
01743 JBDATA *
01744 jbDataSave(JBCLASSER  *classer)
01745 {
01746 l_int32  maxw, maxh;
01747 JBDATA  *data;
01748 PIX     *pix;
01749 
01750     PROCNAME("jbDataSave");
01751 
01752     if (!classer)
01753         return (JBDATA *)ERROR_PTR("classer not defined", procName, NULL);
01754 
01755         /* Write the templates into an array. */
01756     pixaSizeRange(classer->pixat, NULL, NULL, &maxw, &maxh);
01757     if ((pix = pixaDisplayOnLattice(classer->pixat, maxw + 1, maxh + 1))
01758             == NULL)
01759         return (JBDATA *)ERROR_PTR("data not made", procName, NULL);
01760 
01761     if ((data = (JBDATA *)CALLOC(1, sizeof(JBDATA))) == NULL)
01762         return (JBDATA *)ERROR_PTR("data not made", procName, NULL);
01763     data->pix = pix;
01764     data->npages = classer->npages;
01765     data->w = classer->w;
01766     data->h = classer->h;
01767     data->nclass = classer->nclass;
01768     data->latticew = maxw + 1;
01769     data->latticeh = maxh + 1;
01770     data->naclass = numaClone(classer->naclass);
01771     data->napage = numaClone(classer->napage);
01772     data->ptaul = ptaClone(classer->ptaul);
01773 
01774     return data;
01775 }
01776 
01777 
01778 /*
01779  *  jbDataDestroy()
01780  *
01781  *      Input: &data (<to be nulled>)
01782  *      Return: void
01783  */
01784 void
01785 jbDataDestroy(JBDATA  **pdata)
01786 {
01787 JBDATA  *data;
01788 
01789     if (!pdata)
01790         return;
01791     if ((data = *pdata) == NULL)
01792         return;
01793 
01794     pixDestroy(&data->pix);
01795     numaDestroy(&data->naclass);
01796     numaDestroy(&data->napage);
01797     ptaDestroy(&data->ptaul);
01798     FREE(data);
01799     *pdata = NULL;
01800     return;
01801 }
01802     
01803 
01804 /*!
01805  *  jbDataWrite()
01806  *
01807  *      Input:  rootname (for output files; everything but the extension)
01808  *              jbdata
01809  *      Return: 0 if OK, 1 on error
01810  *
01811  *  Notes:
01812  *      (1) Serialization function that writes data in jbdata to file.
01813  */
01814 l_int32
01815 jbDataWrite(const char  *rootout,
01816             JBDATA      *jbdata)
01817 {
01818 char     buf[L_BUF_SIZE];
01819 l_int32  w, h, nclass, npages, cellw, cellh, ncomp, i, x, y, iclass, ipage;
01820 NUMA    *naclass, *napage;
01821 PTA     *ptaul;
01822 PIX     *pixt;
01823 FILE    *fp;
01824     
01825     PROCNAME("jbDataWrite");
01826 
01827     if (!rootout)
01828         return ERROR_INT("no rootout", procName, 1);
01829     if (!jbdata)
01830         return ERROR_INT("no jbdata", procName, 1);
01831 
01832     npages = jbdata->npages;
01833     w = jbdata->w;
01834     h = jbdata->h;
01835     pixt = jbdata->pix;
01836     nclass = jbdata->nclass;
01837     cellw = jbdata->latticew;
01838     cellh = jbdata->latticeh;
01839     naclass = jbdata->naclass;
01840     napage = jbdata->napage;
01841     ptaul = jbdata->ptaul;
01842 
01843     snprintf(buf, L_BUF_SIZE, "%s%s", rootout, JB_TEMPLATE_EXT); 
01844     pixWrite(buf, pixt, IFF_PNG);
01845 
01846     snprintf(buf, L_BUF_SIZE, "%s%s", rootout, JB_DATA_EXT); 
01847     if ((fp = fopenWriteStream(buf, "wb")) == NULL)
01848         return ERROR_INT("stream not opened", procName, 1);
01849     ncomp = ptaGetCount(ptaul);
01850     fprintf(fp, "jb data file\n");
01851     fprintf(fp, "num pages = %d\n", npages);
01852     fprintf(fp, "page size: w = %d, h = %d\n", w, h);
01853     fprintf(fp, "num components = %d\n", ncomp);
01854     fprintf(fp, "num classes = %d\n", nclass);
01855     fprintf(fp, "template lattice size: w = %d, h = %d\n", cellw, cellh);
01856     for (i = 0; i < ncomp; i++) {
01857         numaGetIValue(napage, i, &ipage);
01858         numaGetIValue(naclass, i, &iclass);
01859         ptaGetIPt(ptaul, i, &x, &y);
01860         fprintf(fp, "%d %d %d %d\n", ipage, iclass, x, y);
01861     }
01862     fclose(fp);
01863 
01864     return 0;
01865 }
01866 
01867 
01868 /*!
01869  *  jbDataRead()
01870  *
01871  *      Input:  rootname (for template and data files)
01872  *      Return: jbdata, or NULL on error
01873  */
01874 JBDATA *
01875 jbDataRead(const char  *rootname)
01876 {
01877 char      fname[L_BUF_SIZE];
01878 char     *linestr;
01879 l_uint8  *data;
01880 l_int32   nsa, i, w, h, cellw, cellh, x, y, iclass, ipage;
01881 l_int32   npages, nclass, ncomp;
01882 size_t    size;
01883 JBDATA   *jbdata;
01884 NUMA     *naclass, *napage;
01885 PIX      *pixs;
01886 PTA      *ptaul;
01887 SARRAY   *sa;
01888     
01889     PROCNAME("jbDataRead");
01890 
01891     if (!rootname)
01892         return (JBDATA *)ERROR_PTR("rootname not defined", procName, NULL);
01893 
01894     snprintf(fname, L_BUF_SIZE, "%s%s", rootname, JB_TEMPLATE_EXT);
01895     if ((pixs = pixRead(fname)) == NULL)
01896         return (JBDATA *)ERROR_PTR("pix not read", procName, NULL);
01897 
01898     snprintf(fname, L_BUF_SIZE, "%s%s", rootname, JB_DATA_EXT);
01899     if ((data = l_binaryRead(fname, &size)) == NULL)
01900         return (JBDATA *)ERROR_PTR("data not read", procName, NULL);
01901 
01902     if ((sa = sarrayCreateLinesFromString((char *)data, 0)) == NULL)
01903         return (JBDATA *)ERROR_PTR("sa not made", procName, NULL);
01904     nsa = sarrayGetCount(sa);   /* number of cc + 6 */
01905     linestr = sarrayGetString(sa, 0, 0);
01906     if (strcmp(linestr, "jb data file"))
01907         return (JBDATA *)ERROR_PTR("invalid jb data file", procName, NULL);
01908     linestr = sarrayGetString(sa, 1, 0);
01909     sscanf(linestr, "num pages = %d", &npages);
01910     linestr = sarrayGetString(sa, 2, 0);
01911     sscanf(linestr, "page size: w = %d, h = %d", &w, &h);
01912     linestr = sarrayGetString(sa, 3, 0);
01913     sscanf(linestr, "num components = %d", &ncomp);
01914     linestr = sarrayGetString(sa, 4, 0);
01915     sscanf(linestr, "num classes = %d\n", &nclass);
01916     linestr = sarrayGetString(sa, 5, 0);
01917     sscanf(linestr, "template lattice size: w = %d, h = %d\n", &cellw, &cellh);
01918 
01919 #if 1
01920     fprintf(stderr, "num pages = %d\n", npages);
01921     fprintf(stderr, "page size: w = %d, h = %d\n", w, h);
01922     fprintf(stderr, "num components = %d\n", ncomp);
01923     fprintf(stderr, "num classes = %d\n", nclass);
01924     fprintf(stderr, "template lattice size: w = %d, h = %d\n", cellw, cellh);
01925 #endif
01926 
01927     if ((naclass = numaCreate(ncomp)) == NULL)
01928         return (JBDATA *)ERROR_PTR("naclass not made", procName, NULL);
01929     if ((napage = numaCreate(ncomp)) == NULL)
01930         return (JBDATA *)ERROR_PTR("napage not made", procName, NULL);
01931     if ((ptaul = ptaCreate(ncomp)) == NULL)
01932         return (JBDATA *)ERROR_PTR("pta not made", procName, NULL);
01933     for (i = 6; i < nsa; i++) {
01934         linestr = sarrayGetString(sa, i, 0);
01935         sscanf(linestr, "%d %d %d %d\n", &ipage, &iclass, &x, &y);
01936         numaAddNumber(napage, ipage);
01937         numaAddNumber(naclass, iclass);
01938         ptaAddPt(ptaul, x, y);
01939     }
01940 
01941     if ((jbdata = (JBDATA *)CALLOC(1, sizeof(JBDATA))) == NULL)
01942         return (JBDATA *)ERROR_PTR("data not made", procName, NULL);
01943     jbdata->pix = pixs;
01944     jbdata->npages = npages;
01945     jbdata->w = w;
01946     jbdata->h = h;
01947     jbdata->nclass = nclass;
01948     jbdata->latticew = cellw;
01949     jbdata->latticeh = cellh;
01950     jbdata->naclass = naclass;
01951     jbdata->napage = napage;
01952     jbdata->ptaul = ptaul;
01953 
01954     FREE(data);
01955     sarrayDestroy(&sa);
01956     return jbdata;
01957 }
01958 
01959 
01960 /*!
01961  *  jbDataRender()
01962  *
01963  *      Input:  jbdata
01964  *              debugflag (if TRUE, writes into 2 bpp pix and adds 
01965  *                         component outlines in color)
01966  *      Return: pixa (reconstruction of original images, using templates) or
01967  *              null on error
01968  */
01969 PIXA *
01970 jbDataRender(JBDATA  *data,
01971              l_int32  debugflag)
01972 {
01973 l_int32   i, w, h, cellw, cellh, x, y, iclass, ipage;
01974 l_int32   npages, nclass, ncomp, wp, hp;
01975 BOX      *box;
01976 NUMA     *naclass, *napage;
01977 PIX      *pixt, *pixt2, *pix, *pixd;
01978 PIXA     *pixat;   /* pixa of templates */
01979 PIXA     *pixad;   /* pixa of output images */
01980 PIXCMAP  *cmap;
01981 PTA      *ptaul;
01982     
01983     PROCNAME("jbDataRender");
01984 
01985     if (!data)
01986         return (PIXA *)ERROR_PTR("data not defined", procName, NULL);
01987 
01988     npages = data->npages;
01989     w = data->w;
01990     h = data->h;
01991     pixt = data->pix;
01992     nclass = data->nclass;
01993     cellw = data->latticew;
01994     cellh = data->latticeh;
01995     naclass = data->naclass;
01996     napage = data->napage;
01997     ptaul = data->ptaul;
01998     ncomp = numaGetCount(naclass);
01999     
02000         /* Reconstruct the original set of images from the templates
02001          * and the data associated with each component.  First,
02002          * generate the output pixa as a set of empty pix. */
02003     if ((pixad = pixaCreate(npages)) == NULL)
02004         return (PIXA *)ERROR_PTR("pixad not made", procName, NULL);
02005     for (i = 0; i < npages; i++) {
02006         if (debugflag == FALSE)
02007             pix = pixCreate(w, h, 1);
02008         else {
02009             pix = pixCreate(w, h, 2);
02010             cmap = pixcmapCreate(2);
02011             pixcmapAddColor(cmap, 255, 255, 255);
02012             pixcmapAddColor(cmap, 0, 0, 0);
02013             pixcmapAddColor(cmap, 255, 0, 0);  /* for box outlines */
02014             pixSetColormap(pix, cmap);
02015         }
02016         pixaAddPix(pixad, pix, L_INSERT);
02017     }
02018     
02019         /* Put the class templates into a pixa. */
02020     if ((pixat = pixaCreateFromPix(pixt, nclass, cellw, cellh)) == NULL)
02021         return (PIXA *)ERROR_PTR("pixat not made", procName, NULL);
02022 
02023         /* Place each component in the right location on its page. */
02024     for (i = 0; i < ncomp; i++) {
02025         numaGetIValue(napage, i, &ipage);
02026         numaGetIValue(naclass, i, &iclass);
02027         pix = pixaGetPix(pixat, iclass, L_CLONE);  /* the template */
02028         wp = pixGetWidth(pix);
02029         hp = pixGetHeight(pix);
02030         ptaGetIPt(ptaul, i, &x, &y);
02031         pixd = pixaGetPix(pixad, ipage, L_CLONE);   /* the output page */
02032         if (debugflag == FALSE)
02033             pixRasterop(pixd, x, y, wp, hp, PIX_SRC | PIX_DST, pix, 0, 0);
02034         else {
02035             pixt2 = pixConvert1To2Cmap(pix);
02036             pixRasterop(pixd, x, y, wp, hp, PIX_SRC | PIX_DST, pixt2, 0, 0);
02037             box = boxCreate(x, y, wp, hp);
02038             pixRenderBoxArb(pixd, box, 1, 255, 0, 0);
02039             pixDestroy(&pixt2);
02040             boxDestroy(&box);
02041         }
02042         pixDestroy(&pix);   /* the clone only */
02043         pixDestroy(&pixd);  /* the clone only */
02044     }
02045 
02046     pixaDestroy(&pixat);
02047     return pixad;
02048 }
02049 
02050 
02051 /*!
02052  *  jbGetULCorners()
02053  *
02054  *      Input:  jbclasser
02055  *              pixs (full res image)
02056  *              boxa (of c.c. bounding rectangles for this page)
02057  *      Return: 0 if OK, 1 on error
02058  *
02059  *  Notes:
02060  *      (1) This computes the ptaul field, which has the global UL corners,
02061  *          adjusted for each specific component, so that each component
02062  *          can be replaced by the template for its class and have the
02063  *          centroid in the template in the same position as the
02064  *          centroid of the original connected component.  It is important
02065  *          that this be done properly to avoid a wavy baseline in the
02066  *          result.
02067  *      (2) The array fields ptac and ptact give the centroids of
02068  *          those components relative to the UL corner of each component.
02069  *          Here, we compute the difference in each component, round to
02070  *          nearest integer, and correct the box->x and box->y by
02071  *          the appropriate integral difference.
02072  *      (3) The templates and stored instances are all bordered.
02073  */
02074 l_int32
02075 jbGetULCorners(JBCLASSER  *classer,
02076                PIX        *pixs,
02077                BOXA       *boxa)
02078 {
02079 l_int32    i, baseindex, index, n, iclass, idelx, idely, x, y, dx, dy;
02080 l_int32   *sumtab;
02081 l_float32  x1, x2, y1, y2, delx, dely;
02082 BOX       *box;
02083 NUMA      *naclass;
02084 PIX       *pixt;
02085 PTA       *ptac, *ptact, *ptaul;
02086 
02087     PROCNAME("jbGetULCorners");
02088 
02089     if (!classer)
02090         return ERROR_INT("classer not defined", procName, 1);
02091     if (!pixs)
02092         return ERROR_INT("pixs not defined", procName, 1);
02093     if (!boxa)
02094         return ERROR_INT("boxa not defined", procName, 1);
02095 
02096     n = boxaGetCount(boxa);
02097     ptaul = classer->ptaul;
02098     naclass = classer->naclass;
02099     ptac = classer->ptac;
02100     ptact = classer->ptact;
02101     baseindex = classer->baseindex;  /* num components before this page */
02102     sumtab = makePixelSumTab8();
02103     for (i = 0; i < n; i++) {
02104         index = baseindex + i;
02105         ptaGetPt(ptac, index, &x1, &y1);
02106         numaGetIValue(naclass, index, &iclass);
02107         ptaGetPt(ptact, iclass, &x2, &y2);
02108         delx = x2 - x1;
02109         dely = y2 - y1;
02110         if (delx >= 0)
02111             idelx = (l_int32)(delx + 0.5);
02112         else
02113             idelx = (l_int32)(delx - 0.5);
02114         if (dely >= 0)
02115             idely = (l_int32)(dely + 0.5);
02116         else
02117             idely = (l_int32)(dely - 0.5);
02118         if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL)
02119             return ERROR_INT("box not found", procName, 1);
02120         boxGetGeometry(box, &x, &y, NULL, NULL);
02121 
02122             /* Get final increments dx and dy for best alignment */
02123         pixt = pixaGetPix(classer->pixat, iclass, L_CLONE);
02124         finalPositioningForAlignment(pixs, x, y, idelx, idely,
02125                                      pixt, sumtab, &dx, &dy);
02126 /*        if (i % 20 == 0)
02127             fprintf(stderr, "dx = %d, dy = %d\n", dx, dy); */
02128         ptaAddPt(ptaul, x - idelx + dx, y - idely + dy);
02129         boxDestroy(&box);
02130         pixDestroy(&pixt);
02131     }
02132 
02133     FREE(sumtab);
02134     return 0;
02135 }
02136 
02137 
02138 /*!
02139  *  jbGetLLCorners()
02140  *
02141  *      Input:  jbclasser
02142  *      Return: 0 if OK, 1 on error
02143  *
02144  *  Notes:
02145  *      (1) This computes the ptall field, which has the global LL corners,
02146  *          adjusted for each specific component, so that each component
02147  *          can be replaced by the template for its class and have the
02148  *          centroid in the template in the same position as the
02149  *          centroid of the original connected component. It is important
02150  *          that this be done properly to avoid a wavy baseline in the result.
02151  *      (2) It is computed here from the corresponding UL corners, where
02152  *          the input templates and stored instances are all bordered.
02153  *          This should be done after all pages have been processed.
02154  *      (3) For proper substitution, the templates whose LL corners are
02155  *          placed in these locations must be UN-bordered.
02156  *          This is available for a realistic jbig2 encoder, which would
02157  *          (1) encode each template without a border, and (2) encode
02158  *          the position using the LL corner (rather than the UL
02159  *          corner) because the difference between y-values
02160  *          of successive instances is typically close to zero.
02161  */
02162 l_int32
02163 jbGetLLCorners(JBCLASSER  *classer)
02164 {
02165 l_int32    i, iclass, n, x1, y1, h;
02166 NUMA      *naclass;
02167 PIX       *pix;
02168 PIXA      *pixat;
02169 PTA       *ptaul, *ptall;
02170 
02171     PROCNAME("jbGetLLCorners");
02172 
02173     if (!classer)
02174         return ERROR_INT("classer not defined", procName, 1);
02175 
02176     ptaul = classer->ptaul;
02177     naclass = classer->naclass;
02178     pixat = classer->pixat;
02179 
02180     ptaDestroy(&classer->ptall);
02181     n = ptaGetCount(ptaul);
02182     ptall = ptaCreate(n);
02183     classer->ptall = ptall;
02184 
02185         /* If the templates were bordered, we would add h - 1 to the UL
02186          * corner y-value.  However, because the templates to be used
02187          * here have their borders removed, and the borders are
02188          * JB_ADDED_PIXELS on each side, we add h - 1 - 2 * JB_ADDED_PIXELS
02189          * to the UL corner y-value.  */
02190     for (i = 0; i < n; i++) {
02191         ptaGetIPt(ptaul, i, &x1, &y1);
02192         numaGetIValue(naclass, i, &iclass);
02193         pix = pixaGetPix(pixat, iclass, L_CLONE);
02194         h = pixGetHeight(pix);
02195         ptaAddPt(ptall, x1, y1 + h - 1 - 2 * JB_ADDED_PIXELS);
02196         pixDestroy(&pix);
02197     }
02198 
02199     return 0;
02200 }
02201 
02202 
02203 /*----------------------------------------------------------------------*
02204  *                              Static helpers                          *
02205  *----------------------------------------------------------------------*/
02206 /* When looking for similar matches we check templates whose size is +/- 2 in
02207  * each direction. This involves 25 possible sizes. This array contains the
02208  * offsets for each of those positions in a spiral pattern. There are 25 pairs
02209  * of numbers in this array: even positions are x values. */
02210 static int two_by_two_walk[50] = {
02211   0, 0,
02212   0, 1,
02213   -1, 0,
02214   0, -1,
02215   1, 0,
02216   -1, 1,
02217   1, 1,
02218   -1, -1,
02219   1, -1,
02220   0, -2,
02221   2, 0,
02222   0, 2,
02223   -2, 0,
02224   -1, -2,
02225   1, -2,
02226   2, -1,
02227   2, 1,
02228   1, 2,
02229   -1, 2,
02230   -2, 1,
02231   -2, -1,
02232   -2, -2,
02233   2, -2,
02234   2, 2,
02235   -2, 2};
02236 
02237 
02238 /*!
02239  *  findSimilarSizedTemplatesInit()
02240  *
02241  *      Input:  classer
02242  *              pixs (instance to be matched)
02243  *      Return: Allocated context to be used with findSimilar*
02244  */
02245 static JBFINDCTX *
02246 findSimilarSizedTemplatesInit(JBCLASSER  *classer,
02247                               PIX        *pixs)
02248 {
02249 JBFINDCTX  *state;
02250 
02251     state = (JBFINDCTX *)CALLOC(1, sizeof(JBFINDCTX));
02252     state->w = pixGetWidth(pixs) - 2 * JB_ADDED_PIXELS;
02253     state->h = pixGetHeight(pixs) - 2 * JB_ADDED_PIXELS;
02254     state->classer = classer;
02255 
02256     return state;
02257 }
02258 
02259 
02260 static void
02261 findSimilarSizedTemplatesDestroy(JBFINDCTX  **pstate)
02262 {
02263 JBFINDCTX  *state;
02264 
02265     PROCNAME("findSimilarSizedTemplatesDestroy");
02266 
02267     if (pstate == NULL) {
02268         L_WARNING("ptr address is null", procName);
02269         return;
02270     }
02271     if ((state = *pstate) == NULL)
02272         return;
02273 
02274     numaDestroy(&state->numa);
02275     FREE(state);
02276     *pstate = NULL;
02277     return;
02278 }
02279 
02280 
02281 /*!
02282  *  findSimilarSizedTemplatesNext()
02283  *
02284  *      Input:  state (from findSimilarSizedTemplatesInit)
02285  *      Return: Next template number, or -1 when finished
02286  *
02287  *  We have a hash table mapping template area to a list of template
02288  *  numbers with that area.  We wish to find similar sized templates,
02289  *  so we first look for templates with the same width and height, and
02290  *  then with width + 1, etc.  This walk is guided by the
02291  *  two_by_two_walk array, above.
02292  *
02293  *  We don't want to have to collect the whole list of templates first because
02294  *  (we hope) to find it quickly.  So we keep the context for this walk in an
02295  *  explictit state structure and this function acts like a generator.
02296  */
02297 static l_int32
02298 findSimilarSizedTemplatesNext(JBFINDCTX  *state)
02299 {
02300 l_int32  desiredh, desiredw, size, templ;
02301 PIX     *pixt;
02302 
02303     while(1) {  /* Continue the walk over step 'i' */
02304         if (state->i >= 25) {  /* all done */
02305             return -1;
02306         }
02307 
02308         desiredw = state->w + two_by_two_walk[2 * state->i];
02309         desiredh = state->h + two_by_two_walk[2 * state->i + 1];
02310         if (desiredh < 1 || desiredw < 1) {  /* invalid size */
02311             state->i++;
02312             continue;
02313         }
02314 
02315         if (!state->numa) {
02316                 /* We have yet to start walking the array for the step 'i' */
02317             state->numa = numaHashGetNuma(state->classer->nahash,
02318                                           desiredh * desiredw);
02319             if (!state->numa) {  /* nothing there */
02320                 state->i++;
02321                 continue;
02322             }
02323 
02324             state->n = 0;  /* OK, we got a numa. */
02325         }
02326 
02327             /* Continue working on this numa */
02328         size = numaGetCount(state->numa);
02329         for ( ; state->n < size; ) {
02330             templ = (l_int32)(state->numa->array[state->n++] + 0.5);
02331             pixt = pixaGetPix(state->classer->pixat, templ, L_CLONE);
02332             if (pixGetWidth(pixt) - 2 * JB_ADDED_PIXELS == desiredw &&
02333                 pixGetHeight(pixt) - 2 * JB_ADDED_PIXELS == desiredh) {
02334                 pixDestroy(&pixt);
02335                 return templ;
02336             }
02337             pixDestroy(&pixt);
02338         }
02339 
02340             /* Exhausted the numa; take another step and try again */
02341         state->i++;
02342         numaDestroy(&state->numa);
02343         continue;
02344     }
02345 }
02346 
02347 
02348 /*!
02349  *  finalPositioningForAlignment()
02350  *
02351  *      Input:  pixs (input page image)
02352  *              x, y (location of UL corner of bb of component in pixs)
02353  *              idelx, idely (compensation to match centroids of component
02354  *                            and template)
02355  *              pixt (template, with JB_ADDED_PIXELS of padding on all sides)
02356  *              sumtab (for summing fg pixels in an image)
02357  *              &dx, &dy (return delta on position for best match; each
02358  *                        one is in the set {-1, 0, 1})
02359  *      Return: 0 if OK, 1 on error
02360  *
02361  */
02362 static l_int32
02363 finalPositioningForAlignment(PIX      *pixs,
02364                              l_int32   x,
02365                              l_int32   y,
02366                              l_int32   idelx,
02367                              l_int32   idely,
02368                              PIX      *pixt,
02369                              l_int32  *sumtab,
02370                              l_int32  *pdx,
02371                              l_int32  *pdy)
02372 {
02373 l_int32  w, h, i, j, minx, miny, count, mincount;
02374 PIX     *pixi;  /* clipped from source pixs */
02375 PIX     *pixr;  /* temporary storage */
02376 BOX     *box;
02377 
02378     PROCNAME("finalPositioningForAlignment");
02379 
02380     if (!pixs)
02381         return ERROR_INT("pixs not defined", procName, 1);
02382     if (!pixt)
02383         return ERROR_INT("pixt not defined", procName, 1);
02384     if (!pdx || !pdy)
02385         return ERROR_INT("&dx and &dy not both defined", procName, 1);
02386     if (!sumtab)
02387         return ERROR_INT("sumtab not defined", procName, 1);
02388     *pdx = *pdy = 0;
02389 
02390         /* Use JB_ADDED_PIXELS pixels padding on each side */
02391     w = pixGetWidth(pixt);
02392     h = pixGetHeight(pixt);
02393     box = boxCreate(x - idelx - JB_ADDED_PIXELS,
02394                     y - idely - JB_ADDED_PIXELS, w, h);
02395     pixi = pixClipRectangle(pixs, box, NULL);
02396     boxDestroy(&box);
02397     if (!pixi)
02398         return ERROR_INT("pixi not made", procName, 1);
02399 
02400     pixr = pixCreate(pixGetWidth(pixi), pixGetHeight(pixi), 1);
02401     mincount = 0x7fffffff;
02402     for (i = -1; i <= 1; i++) {
02403         for (j = -1; j <= 1; j++) {
02404             pixCopy(pixr, pixi);
02405             pixRasterop(pixr, j, i, w, h, PIX_SRC ^ PIX_DST, pixt, 0, 0);
02406             pixCountPixels(pixr, &count, sumtab);
02407             if (count < mincount) {
02408                 minx = j;
02409                 miny = i;
02410                 mincount = count;
02411             }
02412         }
02413     }
02414     pixDestroy(&pixi);
02415     pixDestroy(&pixr);
02416 
02417     *pdx = minx;
02418     *pdy = miny;
02419     return 0;
02420 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines