1 /*====================================================================*
2  -  Copyright (C) 2001 Leptonica.  All rights reserved.
3  -  This software is distributed in the hope that it will be
4  -  useful, but with NO WARRANTY OF ANY KIND.
5  -  No author or distributor accepts responsibility to anyone for the
6  -  consequences of using this software, or for whether it serves any
7  -  particular purpose or works at all, unless he or she says so in
8  -  writing.  Everyone is granted permission to copy, modify and
9  -  redistribute this source code, for commercial or non-commercial
10  -  purposes, with the following restrictions: (1) the origin of this
11  -  source code must not be misrepresented; (2) modified versions must
12  -  be plainly marked as such; and (3) this notice may not be removed
13  -  or altered from any source or modified source distribution.
14  *====================================================================*/
15 
16 
17 /*
18  * jbclass.c
19  *
20  *     These are functions for unsupervised classification of
21  *     collections of connected components -- either characters or
22  *     words -- in binary images.  They can be used as image
23  *     processing steps in jbig2 compression.
24  *
25  *     Initialization
26  *
27  *         JBCLASSER         *jbRankHausInit()      [rank hausdorff encoder]
28  *         JBCLASSER         *jbCorrelationInit()   [correlation encoder]
29  *         JBCLASSER         *jbCorrelationInitWithoutComponents()  [ditto]
30  *         static JBCLASSER  *jbCorrelationInitInternal()
31  *
32  *     Classify the pages
33  *
34  *         l_int32     jbAddPages()
35  *         l_int32     jbAddPage()
36  *         l_int32     jbAddPageComponents()
37  *
38  *     Rank hausdorff classifier
39  *
40  *         l_int32     jbClassifyRankHaus()
41  *         l_int32     pixHaustest()
42  *         l_int32     pixRankHaustest()
43  *
44  *     Binary correlation classifier
45  *
46  *         l_int32     jbClassifyCorrelation()
47  *
48  *     Determine the image components we start with
49  *
50  *         l_int32     jbGetComponents()
51  *         PIX        *pixWordMaskByDilation()
52  *
53  *     Build grayscale composites (templates)
54  *
55  *         PIXA       *jbAccumulateComposites
56  *         PIXA       *jbTemplatesFromComposites
57  *
58  *     Utility functions for Classer
59  *
60  *         JBCLASSER  *jbClasserCreate()
61  *         void        jbClasserDestroy()
62  *
63  *     Utility functions for Data
64  *
65  *         JBDATA     *jbDataSave()
66  *         void        jbDataDestroy()
67  *         l_int32     jbDataWrite()
68  *         JBDATA     *jbDataRead()
69  *         PIXA       *jbDataRender()
70  *         l_int32     jbGetULCorners()
71  *         l_int32     jbGetLLCorners()
72  *
73  *     Static helpers
74  *
75  *         static JBFINDCTX *findSimilarSizedTemplatesInit()
76  *         static l_int32    findSimilarSizedTemplatesNext()
77  *         static void       findSimilarSizedTemplatesDestroy()
78  *         static l_int32    finalPositioningForAlignment()
79  *
80  *     Note: this is NOT an implementation of the JPEG jbig2
81  *     proposed standard encoder, the specifications for which
82  *     can be found at http://www.jpeg.org/jbigpt2.html.
83  *     (See below for a full implementation.)
84  *     It is an implementation of the lower-level part of an encoder that:
85  *
86  *        (1) identifies connected components that are going to be used
87  *        (2) puts them in similarity classes (this is an unsupervised
88  *            classifier), and
89  *        (3) stores the result in a simple file format (2 files,
90  *            one for templates and one for page/coordinate/template-index
91  *            quartets).
92  *
93  *     An actual implementation of the official jbig2 encoder could
94  *     start with parts (1) and (2), and would then compress the quartets
95  *     according to the standards requirements (e.g., Huffman or
96  *     arithmetic coding of coordinate differences and image templates).
97  *
98  *     The low-level part of the encoder provided here has the
99  *     following useful features:
100  *
101  *         - It is accurate in the identification of templates
102  *           and classes because it uses a windowed hausdorff
103  *           distance metric.
104  *         - It is accurate in the placement of the connected
105  *           components, doing a two step process of first aligning
106  *           the the centroids of the template with those of each instance,
107  *           and then making a further correction of up to +- 1 pixel
108  *           in each direction to best align the templates.
109  *         - It is fast because it uses a morphologically based
110  *           matching algorithm to implement the hausdorff criterion,
111  *           and it selects the patterns that are possible matches
112  *           based on their size.
113  *
114  *     We provide two different matching functions, one using Hausdorff
115  *     distance and one using a simple image correlation.
116  *     The Hausdorff method sometimes produces better results for the
117  *     same number of classes, because it gives a relatively small
118  *     effective weight to foreground pixels near the boundary,
119  *     and a relatively  large weight to foreground pixels that are
120  *     not near the boundary.  By effectively ignoring these boundary
121  *     pixels, Hausdorff weighting corresponds better to the expected
122  *     probabilities of the pixel values in a scanned image, where the
123  *     variations in instances of the same printed character are much
124  *     more likely to be in pixels near the boundary.  By contrast,
125  *     the correlation method gives equal weight to all foreground pixels.
126  *
127  *     For best results, use the correlation method.  Correlation takes
128  *     the number of fg pixels in the AND of instance and template,
129  *     divided by the product of the number of fg pixels in instance
130  *     and template.  It compares this with a threshold that, in
131  *     general, depends on the fractional coverage of the template.
132  *     For heavy text, the threshold is raised above that for light
133  *     text,  By using both these parameters (basic threshold and
134  *     adjustment factor for text weight), one has more flexibility
135  *     and can arrive at the fewest substitution errors, although
136  *     this comes at the price of more templates.
137  *
138  *     The strict Hausdorff scoring is not a rank weighting, because a
139  *     single pixel beyond the given distance will cause a match
140  *     failure.  A rank Hausdorff is more robust to non-boundary noise,
141  *     but it is also more susceptible to confusing components that
142  *     should be in different classes.  For implementing a jbig2
143  *     application for visually lossless binary image compression,
144  *     you have two choices:
145  *
146  *        (1) use a 3x3 structuring element (size = 3) and a strict
147  *            Hausdorff comparison (rank = 1.0 in the rank Hausdorff
148  *            function).  This will result in a minimal number of classes,
149  *            but confusion of small characters, such as italic and
150  *            non-italic lower-case 'o', can still occur.
151  *        (2) use the correlation method with a threshold of 0.85
152  *            and a weighting factor of about 0.7.  This will result in
153  *            a larger number of classes, but should not be confused
154  *            either by similar small characters or by extremely
155  *            thick sans serif characters, such as in prog/cootoots.png.
156  *
157  *     As mentioned above, if visual substitution errors must be
158  *     avoided, you should use the correlation method.
159  *
160  *     We provide executables that show how to do the encoding:
161  *         prog/jbrankhaus.c
162  *         prog/jbcorrelation.c
163  *
164  *     The basic flow for correlation classification goes as follows,
165  *     where specific choices have been made for parameters (Hausdorff
166  *     is the same except for initialization):
167  *
168  *             // Initialize and save data in the classer
169  *         JBCLASSER *classer =
170  *             jbCorrelationInit(JB_CONN_COMPS, 0, 0, 0.8, 0.7);
171  *         SARRAY *safiles = getSortedPathnamesInDirectory(directory,
172  *                                                         NULL, 0, 0);
173  *         jbAddPages(classer, safiles);
174  *
175  *             // Save the data in a data structure for serialization,
176  *             // and write it into two files.
177  *         JBDATA *data = jbDataSave(classer);
178  *         jbDataWrite(rootname, data);
179  *
180  *             // Reconstruct (render) the pages from the encoded data.
181  *         PIXA *pixa = jbDataRender(data, FALSE);
182  *
183  *     Adam Langley has recently built a jbig2 standards-compliant
184  *     encoder, the first one to appear in open source.  You can get
185  *     this encoder at:
186  *
187  *          http://www.imperialviolet.org/jbig2.html
188  *
189  *     It uses arithmetic encoding throughout.  It encodes binary images
190  *     losslessly with a single arithmetic coding over the full image.
191  *     It also does both lossy and lossless encoding from connected
192  *     components, using leptonica to generate the templates representing
193  *     each cluster.
194  */
195 
196 #include <stdio.h>
197 #include <stdlib.h>
198 #include <string.h>
199 #include <math.h>
200 #include "allheaders.h"
201 
202     /* MSVC can't handle arrays dimensioned by static const integers */
203 #define  L_BUF_SIZE  512
204 
205     /* For jbClassifyRankHaus(): size of border added around
206      * pix of each c.c., to allow further processing.  This
207      * should be at least the sum of the MAX_DIFF_HEIGHT
208      * (or MAX_DIFF_WIDTH) and one-half the size of the Sel  */
209 static const l_int32  JB_ADDED_PIXELS = 6;
210 
211     /* For pixHaustest(), pixRankHaustest() and pixCorrelationScore():
212      * choose these to be 2 or greater */
213 static const l_int32  MAX_DIFF_WIDTH = 2;  /* use at least 2 */
214 static const l_int32  MAX_DIFF_HEIGHT = 2;  /* use at least 2 */
215 
216     /* In initialization, you have the option to discard components
217      * (cc, characters or words) that have either width or height larger
218      * than a given size.  This is convenient for jbDataSave(), because
219      * the components are placed onto a regular lattice with cell
220      * dimension equal to the maximum component size.  The default
221      * values are given here.  If you want to save all components,
222      * use a sufficiently large set of dimensions. */
223 static const l_int32  MAX_CONN_COMP_WIDTH = 350;  /* default max cc width */
224 static const l_int32  MAX_CHAR_COMP_WIDTH = 350;  /* default max char width */
225 static const l_int32  MAX_WORD_COMP_WIDTH = 1000;  /* default max word width */
226 static const l_int32  MAX_COMP_HEIGHT = 120;  /* default max component height */
227 
228     /* Max allowed dilation to merge characters into words */
229 #define  MAX_ALLOWED_DILATION  14
230 
231     /* This stores the state of a state machine which fetches
232      * similar sized templates */
233 struct JbFindTemplatesState
234 {
235     JBCLASSER       *classer;    /* classer                               */
236     l_int32          w;          /* desired width                         */
237     l_int32          h;          /* desired height                        */
238     l_int32          i;          /* index into two_by_two step array      */
239     NUMA            *numa;       /* current number array                  */
240     l_int32          n;          /* current element of numa               */
241 };
242 typedef struct JbFindTemplatesState JBFINDCTX;
243 
244 
245     /* Static initialization function */
246 static JBCLASSER * jbCorrelationInitInternal(l_int32 components,
247                        l_int32 maxwidth, l_int32 maxheight, l_float32 thresh,
248                        l_float32 weightfactor, l_int32 keep_components);
249 
250     /* Static helper functions */
251 static JBFINDCTX * findSimilarSizedTemplatesInit(JBCLASSER *classer, PIX *pixs);
252 static l_int32 findSimilarSizedTemplatesNext(JBFINDCTX *context);
253 static void findSimilarSizedTemplatesDestroy(JBFINDCTX **pcontext);
254 static l_int32 finalPositioningForAlignment(PIX *pixs, l_int32 x, l_int32 y,
255                              l_int32 idelx, l_int32 idely, PIX *pixt,
256                              l_int32 *sumtab, l_int32 *pdx, l_int32 *pdy);
257 
258 #ifndef NO_CONSOLE_IO
259 #define  DEBUG_PLOT_CC             0
260 #define  DEBUG_CORRELATION_SCORE   0
261 #endif  /* ~NO_CONSOLE_IO */
262 
263 
264 /*----------------------------------------------------------------------*
265  *                            Initialization                            *
266  *----------------------------------------------------------------------*/
267 /*!
268  *  jbRankHausInit()
269  *
270  *      Input:  components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS)
271  *              maxwidth (of component; use 0 for default)
272  *              maxheight (of component; use 0 for default)
273  *              size  (of square structuring element; 2, representing
274  *                     2x2 sel, is necessary for reasonable accuracy of
275  *                     small components; combine this with rank ~ 0.97
276  *                     to avoid undue class expansion)
277  *              rank (rank val of match, each way; in [0.5 - 1.0];
278  *                    when using size = 2, 0.97 is a reasonable value)
279  *      Return: jbclasser if OK; NULL on error
280  */
281 JBCLASSER *
jbRankHausInit(l_int32 components,l_int32 maxwidth,l_int32 maxheight,l_int32 size,l_float32 rank)282 jbRankHausInit(l_int32    components,
283                l_int32    maxwidth,
284                l_int32    maxheight,
285                l_int32    size,
286                l_float32  rank)
287 {
288 JBCLASSER  *classer;
289 
290     PROCNAME("jbRankHausInit");
291 
292     if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
293         components != JB_WORDS)
294         return (JBCLASSER *)ERROR_PTR("invalid components", procName, NULL);
295     if (size < 1 || size > 10)
296         return (JBCLASSER *)ERROR_PTR("size not reasonable", procName, NULL);
297     if (rank < 0.5 || rank > 1.0)
298         return (JBCLASSER *)ERROR_PTR("rank not in [0.5-1.0]", procName, NULL);
299     if (maxwidth == 0) {
300         if (components == JB_CONN_COMPS)
301             maxwidth = MAX_CONN_COMP_WIDTH;
302         else if (components == JB_CHARACTERS)
303             maxwidth = MAX_CHAR_COMP_WIDTH;
304         else  /* JB_WORDS */
305             maxwidth = MAX_WORD_COMP_WIDTH;
306     }
307     if (maxheight == 0)
308         maxheight = MAX_COMP_HEIGHT;
309 
310     if ((classer = jbClasserCreate(JB_RANKHAUS, components)) == NULL)
311         return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL);
312     classer->maxwidth = maxwidth;
313     classer->maxheight = maxheight;
314     classer->sizehaus = size;
315     classer->rankhaus = rank;
316     classer->nahash = numaHashCreate(5507, 4);  /* 5507 is prime */
317     return classer;
318 }
319 
320 
321 /*!
322  *  jbCorrelationInit()
323  *
324  *      Input:  components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS)
325  *              maxwidth (of component; use 0 for default)
326  *              maxheight (of component; use 0 for default)
327  *              thresh (value for correlation score: in [0.4 - 0.9])
328  *              weightfactor (corrects thresh for thick characters [0.0 - 1.0])
329  *      Return: jbclasser if OK; NULL on error
330  */
331 JBCLASSER *
jbCorrelationInit(l_int32 components,l_int32 maxwidth,l_int32 maxheight,l_float32 thresh,l_float32 weightfactor)332 jbCorrelationInit(l_int32    components,
333                   l_int32    maxwidth,
334                   l_int32    maxheight,
335                   l_float32  thresh,
336                   l_float32  weightfactor)
337 {
338     return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh,
339                                      weightfactor, 1);
340 }
341 
342 /*!
343  *  jbCorrelationInitWithoutComponents()
344  *
345  *      Input:  same as jbCorrelationInit
346  *      Output: same as jbCorrelationInit
347  *
348  *  Note: acts the same as jbCorrelationInit(), but the resulting
349  *        object doesn't keep a list of all the components.
350  */
351 JBCLASSER *
jbCorrelationInitWithoutComponents(l_int32 components,l_int32 maxwidth,l_int32 maxheight,l_float32 thresh,l_float32 weightfactor)352 jbCorrelationInitWithoutComponents(l_int32    components,
353                                    l_int32    maxwidth,
354                                    l_int32    maxheight,
355                                    l_float32  thresh,
356                                    l_float32  weightfactor)
357 {
358     return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh,
359                                      weightfactor, 0);
360 }
361 
362 
363 static JBCLASSER *
jbCorrelationInitInternal(l_int32 components,l_int32 maxwidth,l_int32 maxheight,l_float32 thresh,l_float32 weightfactor,l_int32 keep_components)364 jbCorrelationInitInternal(l_int32    components,
365                           l_int32    maxwidth,
366                           l_int32    maxheight,
367                           l_float32  thresh,
368                           l_float32  weightfactor,
369                           l_int32    keep_components)
370 {
371 JBCLASSER  *classer;
372 
373     PROCNAME("jbCorrelationInitInternal");
374 
375     if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
376         components != JB_WORDS)
377         return (JBCLASSER *)ERROR_PTR("invalid components", procName, NULL);
378     if (thresh < 0.4 || thresh > 0.9)
379         return (JBCLASSER *)ERROR_PTR("thresh not in range [0.4 - 0.9]",
380                 procName, NULL);
381     if (weightfactor < 0.0 || weightfactor > 1.0)
382         return (JBCLASSER *)ERROR_PTR("weightfactor not in range [0.0 - 1.0]",
383                 procName, NULL);
384     if (maxwidth == 0) {
385         if (components == JB_CONN_COMPS)
386             maxwidth = MAX_CONN_COMP_WIDTH;
387         else if (components == JB_CHARACTERS)
388             maxwidth = MAX_CHAR_COMP_WIDTH;
389         else  /* JB_WORDS */
390             maxwidth = MAX_WORD_COMP_WIDTH;
391     }
392     if (maxheight == 0)
393         maxheight = MAX_COMP_HEIGHT;
394 
395 
396     if ((classer = jbClasserCreate(JB_CORRELATION, components)) == NULL)
397         return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL);
398     classer->maxwidth = maxwidth;
399     classer->maxheight = maxheight;
400     classer->thresh = thresh;
401     classer->weightfactor = weightfactor;
402     classer->nahash = numaHashCreate(5507, 4);  /* 5507 is prime */
403     classer->keep_pixaa = keep_components;
404     return classer;
405 }
406 
407 
408 /*----------------------------------------------------------------------*
409  *                       Classify the pages                             *
410  *----------------------------------------------------------------------*/
411 /*!
412  *  jbAddPages()
413  *
414  *      Input:  jbclasser
415  *              safiles (of page image file names)
416  *      Return: 0 if OK; 1 on error
417  *
418  *  Note:
419  *      (1) jbclasser makes a copy of the array of file names.
420  *      (2) The caller is still responsible for destroying the input array.
421  */
422 l_int32
jbAddPages(JBCLASSER * classer,SARRAY * safiles)423 jbAddPages(JBCLASSER  *classer,
424            SARRAY     *safiles)
425 {
426 l_int32  i, nfiles;
427 char    *fname;
428 PIX     *pix;
429 
430     PROCNAME("jbAddPages");
431 
432     if (!classer)
433         return ERROR_INT("classer not defined", procName, 1);
434     if (!safiles)
435         return ERROR_INT("safiles not defined", procName, 1);
436 
437     classer->safiles = sarrayCopy(safiles);
438     nfiles = sarrayGetCount(safiles);
439     for (i = 0; i < nfiles; i++) {
440         fname = sarrayGetString(safiles, i, 0);
441         if ((pix = pixRead(fname)) == NULL) {
442             L_WARNING_INT("image file %d not read", procName, i);
443             continue;
444         }
445         jbAddPage(classer, pix);
446         pixDestroy(&pix);
447     }
448 
449     return 0;
450 }
451 
452 
453 /*!
454  *  jbAddPage()
455  *
456  *      Input:  jbclasser
457  *              pixs (of input page)
458  *      Return: 0 if OK; 1 on error
459  */
460 l_int32
jbAddPage(JBCLASSER * classer,PIX * pixs)461 jbAddPage(JBCLASSER  *classer,
462           PIX        *pixs)
463 {
464 BOXA  *boxas;
465 PIXA  *pixas;
466 
467     PROCNAME("jbAddPage");
468 
469     if (!classer)
470         return ERROR_INT("classer not defined", procName, 1);
471     if (!pixs)
472         return ERROR_INT("pix not defined", procName, 1);
473 
474     classer->w = pixGetWidth(pixs);
475     classer->h = pixGetHeight(pixs);
476 
477         /* Get the appropriate components and their bounding boxes */
478     if (jbGetComponents(pixs, classer->components, classer->maxwidth,
479                         classer->maxheight, &boxas, &pixas)) {
480         return ERROR_INT("components not made", procName, 1);
481     }
482 
483     jbAddPageComponents(classer, pixs, boxas, pixas);
484     boxaDestroy(&boxas);
485     pixaDestroy(&pixas);
486     return 0;
487 }
488 
489 
490 /*!
491  *  jbAddPageComponents()
492  *
493  *      Input:  jbclasser
494  *              pixs (of input page)
495  *              boxas (b.b. of components for this page)
496  *              pixas (components for this page)
497  *      Return: 0 if OK; 1 on error
498  *
499  *  Notes:
500  *      (1) If there are no components on the page, we don't require input
501  *          of empty boxas or pixas, although that's the typical situation.
502  */
503 l_int32
jbAddPageComponents(JBCLASSER * classer,PIX * pixs,BOXA * boxas,PIXA * pixas)504 jbAddPageComponents(JBCLASSER  *classer,
505                     PIX        *pixs,
506                     BOXA       *boxas,
507                     PIXA       *pixas)
508 {
509 l_int32  n;
510 
511     PROCNAME("jbAddPageComponents");
512 
513     if (!classer)
514         return ERROR_INT("classer not defined", procName, 1);
515     if (!pixs)
516         return ERROR_INT("pix not defined", procName, 1);
517 
518         /* Test for no components on the current page.  Always update the
519          * number of pages processed, even if nothing is on it. */
520     if (!boxas || !pixas || (boxaGetCount(boxas) == 0)) {
521         classer->npages++;
522         return 0;
523     }
524 
525         /* Get classes.  For hausdorff, it uses a specified size of
526          * structuring element and specified rank.  For correlation,
527          * it uses a specified threshold. */
528     if (classer->method == JB_RANKHAUS) {
529         if (jbClassifyRankHaus(classer, boxas, pixas))
530             return ERROR_INT("rankhaus classification failed", procName, 1);
531     }
532     else {  /* classer->method == JB_CORRELATION */
533         if (jbClassifyCorrelation(classer, boxas, pixas))
534             return ERROR_INT("correlation classification failed", procName, 1);
535     }
536 
537         /* Find the global UL corners, adjusted for each instance so
538          * that the class template and instance will have their
539          * centroids in the same place.  Then the template can be
540          * used to replace the instance. */
541     if (jbGetULCorners(classer, pixs, boxas))
542         return ERROR_INT("UL corners not found", procName, 1);
543 
544         /* Update total component counts and number of pages processed. */
545     n = boxaGetCount(boxas);
546     classer->baseindex += n;
547     numaAddNumber(classer->nacomps, n);
548     classer->npages++;
549 
550     return 0;
551 }
552 
553 
554 /*----------------------------------------------------------------------*
555  *         Classification using windowed rank hausdorff metric          *
556  *----------------------------------------------------------------------*/
557 /*!
558  *  jbClassifyRankHaus()
559  *
560  *      Input:  jbclasser
561  *              boxa (of new components for classification)
562  *              pixas (of new components for classification)
563  *      Return: 0 if OK; 1 on error
564  */
565 l_int32
jbClassifyRankHaus(JBCLASSER * classer,BOXA * boxa,PIXA * pixas)566 jbClassifyRankHaus(JBCLASSER  *classer,
567                    BOXA       *boxa,
568                    PIXA       *pixas)
569 {
570 l_int32     n, nt, i, wt, ht, iclass, size, found, testval;
571 l_int32    *sumtab;
572 l_int32     npages, area1, area3;
573 l_int32    *tab8;
574 l_float32   rank, x1, y1, x2, y2;
575 BOX        *box;
576 NUMA       *naclass, *napage;
577 NUMA       *nafg;   /* fg area of all instances */
578 NUMA       *nafgt;  /* fg area of all templates */
579 JBFINDCTX  *findcontext;
580 NUMAHASH   *nahash;
581 PIX        *pix, *pix1, *pix2, *pix3, *pix4;
582 PIXA       *pixa, *pixa1, *pixa2, *pixat, *pixatd;
583 PIXAA      *pixaa;
584 PTA        *pta, *ptac, *ptact;
585 SEL        *sel;
586 
587     PROCNAME("jbClassifyRankHaus");
588 
589     if (!classer)
590         return ERROR_INT("classer not found", procName, 1);
591     if (!boxa)
592         return ERROR_INT("boxa not found", procName, 1);
593     if (!pixas)
594         return ERROR_INT("pixas not found", procName, 1);
595 
596     npages = classer->npages;
597     size = classer->sizehaus;
598     sel = selCreateBrick(size, size, size / 2, size / 2, SEL_HIT);
599 
600         /* Generate the bordered pixa, with and without dilation.
601          * pixa1 and pixa2 contain all the input components. */
602     n = pixaGetCount(pixas);
603     pixa1 = pixaCreate(n);
604     pixa2 = pixaCreate(n);
605     for (i = 0; i < n; i++) {
606         pix = pixaGetPix(pixas, i, L_CLONE);
607         pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS,
608                     JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0);
609         pix2 = pixDilate(NULL, pix1, sel);
610         pixaAddPix(pixa1, pix1, L_INSERT);   /* un-dilated */
611         pixaAddPix(pixa2, pix2, L_INSERT);   /* dilated */
612         pixDestroy(&pix);
613     }
614 
615         /* Get the centroids of all the bordered images.
616          * These are relative to the UL corner of each (bordered) pix.  */
617     pta = pixaCentroids(pixa1);  /* centroids for this page; use here */
618     ptac = classer->ptac;  /* holds centroids of components up to this page */
619     ptaJoin(ptac, pta, 0, 0);  /* save centroids of all components */
620     ptact = classer->ptact;  /* holds centroids of templates */
621 
622         /* Use these to save the class and page of each component. */
623     naclass = classer->naclass;
624     napage = classer->napage;
625     sumtab = makePixelSumTab8();
626 
627         /* Store the unbordered pix in a pixaa, in a hierarchical
628          * set of arrays.  There is one pixa for each class,
629          * and the pix in each pixa are all the instances found
630          * of that class.  This is actually more than one would need
631          * for a jbig2 encoder, but there are two reasons to keep
632          * them around: (1) the set of instances for each class
633          * can be used to make an improved binary (or, better,
634          * a grayscale) template, rather than simply using the first
635          * one in the set; (2) we can investigate the failures
636          * of the classifier.  This pixaa grows as we process
637          * successive pages. */
638     pixaa = classer->pixaa;
639 
640         /* arrays to store class exemplars (templates) */
641     pixat = classer->pixat;   /* un-dilated */
642     pixatd = classer->pixatd;   /* dilated */
643 
644         /* Fill up the pixaa tree with the template exemplars as
645          * the first pix in each pixa.  As we add each pix,
646          * we also add the associated box to the pixa.
647          * We also keep track of the centroid of each pix,
648          * and use the difference between centroids (of the
649          * pix with the exemplar we are checking it with)
650          * to align the two when checking that the Hausdorff
651          * distance does not exceed a threshold.
652          * The threshold is set by the Sel used for dilating.
653          * For example, a 3x3 brick, sel_3, corresponds to a
654          * Hausdorff distance of 1.  In general, for an NxN brick,
655          * with N odd, corresponds to a Hausdorff distance of (N - 1)/2.
656          * It turns out that we actually need to use a sel of size 2x2
657          * to avoid small bad components when there is a halftone image
658          * from which components can be chosen.
659          * The larger the Sel you use, the fewer the number of classes,
660          * and the greater the likelihood of putting semantically
661          * different objects in the same class.  For simplicity,
662          * we do this separately for the case of rank == 1.0 (exact
663          * match within the Hausdorff distance) and rank < 1.0.  */
664     rank = classer->rankhaus;
665     nahash = classer->nahash;
666     if (rank == 1.0) {
667         for (i = 0; i < n; i++) {
668             pix1 = pixaGetPix(pixa1, i, L_CLONE);
669             pix2 = pixaGetPix(pixa2, i, L_CLONE);
670             ptaGetPt(pta, i, &x1, &y1);
671             nt = pixaGetCount(pixat);  /* number of templates */
672             found = FALSE;
673             findcontext = findSimilarSizedTemplatesInit(classer, pix1);
674             while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) {
675                     /* Find score for this template */
676                 pix3 = pixaGetPix(pixat, iclass, L_CLONE);
677                 pix4 = pixaGetPix(pixatd, iclass, L_CLONE);
678                 ptaGetPt(ptact, iclass, &x2, &y2);
679                 testval = pixHaustest(pix1, pix2, pix3, pix4, x1 - x2, y1 - y2,
680                                       MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT);
681                 pixDestroy(&pix3);
682                 pixDestroy(&pix4);
683                 if (testval == 1) {
684                     found = TRUE;
685                     numaAddNumber(naclass, iclass);
686                     numaAddNumber(napage, npages);
687                     if (classer->keep_pixaa) {
688                         pixa = pixaaGetPixa(pixaa, iclass, L_CLONE);
689                         pix = pixaGetPix(pixas, i, L_CLONE);
690                         pixaAddPix(pixa, pix, L_INSERT);
691                         box = boxaGetBox(boxa, i, L_CLONE);
692                         pixaAddBox(pixa, box, L_INSERT);
693                         pixaDestroy(&pixa);
694                     }
695                     break;
696                 }
697             }
698             findSimilarSizedTemplatesDestroy(&findcontext);
699             if (found == FALSE) {  /* new class */
700                 numaAddNumber(naclass, nt);
701                 numaAddNumber(napage, npages);
702                 pixa = pixaCreate(0);
703                 pix = pixaGetPix(pixas, i, L_CLONE);  /* unbordered instance */
704                 pixaAddPix(pixa, pix, L_INSERT);
705                 wt = pixGetWidth(pix);
706                 ht = pixGetHeight(pix);
707                 numaHashAdd(nahash, ht * wt, nt);
708                 box = boxaGetBox(boxa, i, L_CLONE);
709                 pixaAddBox(pixa, box, L_INSERT);
710                 pixaaAddPixa(pixaa, pixa, L_INSERT);  /* unbordered instance */
711                 ptaAddPt(ptact, x1, y1);
712                 pixaAddPix(pixat, pix1, L_INSERT);  /* bordered template */
713                 pixaAddPix(pixatd, pix2, L_INSERT);  /* bordered dil template */
714             }
715             else {   /* don't save them */
716                 pixDestroy(&pix1);
717                 pixDestroy(&pix2);
718             }
719         }
720     }
721     else {  /* rank < 1.0 */
722         if ((nafg = pixaCountPixels(pixas)) == NULL)  /* areas for this page */
723             return ERROR_INT("nafg not made", procName, 1);
724         nafgt = classer->nafgt;
725         tab8 = makePixelSumTab8();
726         for (i = 0; i < n; i++) {   /* all instances on this page */
727             pix1 = pixaGetPix(pixa1, i, L_CLONE);
728             numaGetIValue(nafg, i, &area1);
729             pix2 = pixaGetPix(pixa2, i, L_CLONE);
730             ptaGetPt(pta, i, &x1, &y1);   /* use pta for this page */
731             nt = pixaGetCount(pixat);  /* number of templates */
732             found = FALSE;
733             findcontext = findSimilarSizedTemplatesInit(classer, pix1);
734             while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) {
735                     /* Find score for this template */
736                 pix3 = pixaGetPix(pixat, iclass, L_CLONE);
737                 numaGetIValue(nafgt, iclass, &area3);
738                 pix4 = pixaGetPix(pixatd, iclass, L_CLONE);
739                 ptaGetPt(ptact, iclass, &x2, &y2);
740                 testval = pixRankHaustest(pix1, pix2, pix3, pix4,
741                                           x1 - x2, y1 - y2,
742                                           MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT,
743                                           area1, area3, rank, tab8);
744                 pixDestroy(&pix3);
745                 pixDestroy(&pix4);
746                 if (testval == 1) {  /* greedy match; take the first */
747                     found = TRUE;
748                     numaAddNumber(naclass, iclass);
749                     numaAddNumber(napage, npages);
750                     if (classer->keep_pixaa) {
751                         pixa = pixaaGetPixa(pixaa, iclass, L_CLONE);
752                         pix = pixaGetPix(pixas, i, L_CLONE);
753                         pixaAddPix(pixa, pix, L_INSERT);
754                         box = boxaGetBox(boxa, i, L_CLONE);
755                         pixaAddBox(pixa, box, L_INSERT);
756                         pixaDestroy(&pixa);
757                     }
758                     break;
759                 }
760             }
761             findSimilarSizedTemplatesDestroy(&findcontext);
762             if (found == FALSE) {  /* new class */
763                 numaAddNumber(naclass, nt);
764                 numaAddNumber(napage, npages);
765                 pixa = pixaCreate(0);
766                 pix = pixaGetPix(pixas, i, L_CLONE);  /* unbordered instance */
767                 pixaAddPix(pixa, pix, L_INSERT);
768                 wt = pixGetWidth(pix);
769                 ht = pixGetHeight(pix);
770                 numaHashAdd(nahash, ht * wt, nt);
771                 box = boxaGetBox(boxa, i, L_CLONE);
772                 pixaAddBox(pixa, box, L_INSERT);
773                 pixaaAddPixa(pixaa, pixa, L_INSERT);  /* unbordered instance */
774                 ptaAddPt(ptact, x1, y1);
775                 pixaAddPix(pixat, pix1, L_INSERT);  /* bordered template */
776                 pixaAddPix(pixatd, pix2, L_INSERT);  /* ditto */
777                 numaAddNumber(nafgt, area1);
778             }
779             else {   /* don't save them */
780                 pixDestroy(&pix1);
781                 pixDestroy(&pix2);
782             }
783         }
784         FREE(tab8);
785         numaDestroy(&nafg);
786     }
787     classer->nclass = pixaGetCount(pixat);
788 
789     FREE(sumtab);
790     ptaDestroy(&pta);
791     pixaDestroy(&pixa1);
792     pixaDestroy(&pixa2);
793     selDestroy(&sel);
794     return 0;
795 }
796 
797 
798 /*!
799  *  pixHaustest()
800  *
801  *      Input:  pix1   (new pix, not dilated)
802  *              pix2   (new pix, dilated)
803  *              pix3   (exemplar pix, not dilated)
804  *              pix4   (exemplar pix, dilated)
805  *              delx   (x comp of centroid difference)
806  *              dely   (y comp of centroid difference)
807  *              maxdiffw (max width difference of pix1 and pix2)
808  *              maxdiffh (max height difference of pix1 and pix2)
809  *      Return: 0 (FALSE) if no match, 1 (TRUE) if the new
810  *              pix is in the same class as the exemplar.
811  *
812  *  Note: we check first that the two pix are roughly
813  *  the same size.  Only if they meet that criterion do
814  *  we compare the bitmaps.  The Hausdorff is a 2-way
815  *  check.  The centroid difference is used to align the two
816  *  images to the nearest integer for each of the checks.
817  *  These check that the dilated image of one contains
818  *  ALL the pixels of the undilated image of the other.
819  *  Checks are done in both direction.  A single pixel not
820  *  contained in either direction results in failure of the test.
821  */
822 l_int32
pixHaustest(PIX * pix1,PIX * pix2,PIX * pix3,PIX * pix4,l_float32 delx,l_float32 dely,l_int32 maxdiffw,l_int32 maxdiffh)823 pixHaustest(PIX       *pix1,
824             PIX       *pix2,
825             PIX       *pix3,
826             PIX       *pix4,
827             l_float32  delx,   /* x(1) - x(3) */
828             l_float32  dely,   /* y(1) - y(3) */
829             l_int32    maxdiffw,
830             l_int32    maxdiffh)
831 {
832 l_int32  wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch;
833 PIX     *pixt;
834 
835         /* Eliminate possible matches based on size difference */
836     wi = pixGetWidth(pix1);
837     hi = pixGetHeight(pix1);
838     wt = pixGetWidth(pix3);
839     ht = pixGetHeight(pix3);
840     delw = L_ABS(wi - wt);
841     if (delw > maxdiffw)
842         return FALSE;
843     delh = L_ABS(hi - ht);
844     if (delh > maxdiffh)
845         return FALSE;
846 
847         /* Round difference in centroid location to nearest integer;
848          * use this as a shift when doing the matching. */
849     if (delx >= 0)
850         idelx = (l_int32)(delx + 0.5);
851     else
852         idelx = (l_int32)(delx - 0.5);
853     if (dely >= 0)
854         idely = (l_int32)(dely + 0.5);
855     else
856         idely = (l_int32)(dely - 0.5);
857 
858         /*  Do 1-direction hausdorff, checking that every pixel in pix1
859          *  is within a dilation distance of some pixel in pix3.  Namely,
860          *  that pix4 entirely covers pix1:
861          *       pixt = pixSubtract(NULL, pix1, pix4), including shift
862          *  where pixt has no ON pixels.  */
863     pixt = pixCreateTemplate(pix1);
864     pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC, pix1, 0, 0);
865     pixRasterop(pixt, idelx, idely, wi, hi, PIX_DST & PIX_NOT(PIX_SRC),
866                 pix4, 0, 0);
867     pixZero(pixt, &boolmatch);
868     if (boolmatch == 0) {
869         pixDestroy(&pixt);
870         return FALSE;
871     }
872 
873         /*  Do 1-direction hausdorff, checking that every pixel in pix3
874          *  is within a dilation distance of some pixel in pix1.  Namely,
875          *  that pix2 entirely covers pix3:
876          *      pixSubtract(pixt, pix3, pix2), including shift
877          *  where pixt has no ON pixels. */
878     pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix3, 0, 0);
879     pixRasterop(pixt, 0, 0, wt, ht, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0);
880     pixZero(pixt, &boolmatch);
881     pixDestroy(&pixt);
882     return boolmatch;
883 }
884 
885 
886 /*!
887  *  pixRankHaustest()
888  *
889  *      Input:  pix1   (new pix, not dilated)
890  *              pix2   (new pix, dilated)
891  *              pix3   (exemplar pix, not dilated)
892  *              pix4   (exemplar pix, dilated)
893  *              delx   (x comp of centroid difference)
894  *              dely   (y comp of centroid difference)
895  *              maxdiffw (max width difference of pix1 and pix2)
896  *              maxdiffh (max height difference of pix1 and pix2)
897  *              area1  (fg pixels in pix1)
898  *              area3  (fg pixels in pix3)
899  *              rank   (rank value of test, each way)
900  *              tab8   (table of pixel sums for byte)
901  *      Return: 0 (FALSE) if no match, 1 (TRUE) if the new
902  *                 pix is in the same class as the exemplar.
903  *
904  *  Note: we check first that the two pix are roughly
905  *  the same size.  Only if they meet that criterion do
906  *  we compare the bitmaps.  We convert the rank value to
907  *  a number of pixels by multiplying the rank fraction by the number
908  *  of pixels in the undilated image.  The Hausdorff is a 2-way
909  *  check.  The centroid difference is used to align the two
910  *  images to the nearest integer for each of the checks.
911  *  The rank hausdorff checks that the dilated image of one
912  *  contains the rank fraction of the pixels of the undilated
913  *  image of the other.   Checks are done in both direction.
914  *  Failure of the test in either direction results in failure
915  *  of the test.
916  */
917 l_int32
pixRankHaustest(PIX * pix1,PIX * pix2,PIX * pix3,PIX * pix4,l_float32 delx,l_float32 dely,l_int32 maxdiffw,l_int32 maxdiffh,l_int32 area1,l_int32 area3,l_float32 rank,l_int32 * tab8)918 pixRankHaustest(PIX       *pix1,
919                 PIX       *pix2,
920                 PIX       *pix3,
921                 PIX       *pix4,
922                 l_float32  delx,   /* x(1) - x(3) */
923                 l_float32  dely,   /* y(1) - y(3) */
924                 l_int32    maxdiffw,
925                 l_int32    maxdiffh,
926                 l_int32    area1,
927                 l_int32    area3,
928                 l_float32  rank,
929                 l_int32   *tab8)
930 {
931 l_int32  wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch;
932 l_int32  thresh1, thresh3;
933 PIX     *pixt;
934 
935         /* Eliminate possible matches based on size difference */
936     wi = pixGetWidth(pix1);
937     hi = pixGetHeight(pix1);
938     wt = pixGetWidth(pix3);
939     ht = pixGetHeight(pix3);
940     delw = L_ABS(wi - wt);
941     if (delw > maxdiffw)
942         return FALSE;
943     delh = L_ABS(hi - ht);
944     if (delh > maxdiffh)
945         return FALSE;
946 
947         /* Upper bounds in remaining pixels for allowable match */
948     thresh1 = (l_int32)(area1 * (1. - rank) + 0.5);
949     thresh3 = (l_int32)(area3 * (1. - rank) + 0.5);
950 
951         /* Round difference in centroid location to nearest integer;
952          * use this as a shift when doing the matching. */
953     if (delx >= 0)
954         idelx = (l_int32)(delx + 0.5);
955     else
956         idelx = (l_int32)(delx - 0.5);
957     if (dely >= 0)
958         idely = (l_int32)(dely + 0.5);
959     else
960         idely = (l_int32)(dely - 0.5);
961 
962         /*  Do 1-direction hausdorff, checking that every pixel in pix1
963          *  is within a dilation distance of some pixel in pix3.  Namely,
964          *  that pix4 entirely covers pix1:
965          *       pixt = pixSubtract(NULL, pix1, pix4), including shift
966          *  where pixt has no ON pixels.  */
967     pixt = pixCreateTemplate(pix1);
968     pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC, pix1, 0, 0);
969     pixRasterop(pixt, idelx, idely, wi, hi, PIX_DST & PIX_NOT(PIX_SRC),
970                 pix4, 0, 0);
971     pixThresholdPixelSum(pixt, thresh1, &boolmatch, tab8);
972     if (boolmatch == 1) { /* above thresh1 */
973         pixDestroy(&pixt);
974         return FALSE;
975     }
976 
977         /*  Do 1-direction hausdorff, checking that every pixel in pix3
978          *  is within a dilation distance of some pixel in pix1.  Namely,
979          *  that pix2 entirely covers pix3:
980          *      pixSubtract(pixt, pix3, pix2), including shift
981          *  where pixt has no ON pixels. */
982     pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix3, 0, 0);
983     pixRasterop(pixt, 0, 0, wt, ht, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0);
984     pixThresholdPixelSum(pixt, thresh3, &boolmatch, tab8);
985     pixDestroy(&pixt);
986     if (boolmatch == 1)  /* above thresh3 */
987         return FALSE;
988     else
989         return TRUE;
990 }
991 
992 
993 /*----------------------------------------------------------------------*
994  *            Classification using windowed correlation score           *
995  *----------------------------------------------------------------------*/
996 /*!
997  *  jbClassifyCorrelation()
998  *
999  *      Input:  jbclasser
1000  *              boxa (of new components for classification)
1001  *              pixas (of new components for classification)
1002  *      Return: 0 if OK; 1 on error
1003  */
1004 l_int32
jbClassifyCorrelation(JBCLASSER * classer,BOXA * boxa,PIXA * pixas)1005 jbClassifyCorrelation(JBCLASSER  *classer,
1006                       BOXA       *boxa,
1007                       PIXA       *pixas)
1008 {
1009 l_int32     n, nt, i, iclass, wt, ht, found, area, area1, area2, npages,
1010             overthreshold;
1011 l_int32    *sumtab, *centtab;
1012 l_uint32   *row, word;
1013 l_float32   x1, y1, x2, y2, xsum, ysum;
1014 l_float32   thresh, weight, threshold;
1015 BOX        *box;
1016 NUMA       *naclass, *napage;
1017 NUMA       *nafgt;   /* fg area of all templates */
1018 NUMA       *naarea;   /* w * h area of all templates */
1019 JBFINDCTX  *findcontext;
1020 NUMAHASH   *nahash;
1021 PIX        *pix, *pix1, *pix2;
1022 PIXA       *pixa, *pixa1, *pixat;
1023 PIXAA      *pixaa;
1024 PTA        *pta, *ptac, *ptact;
1025 l_int32    *pixcts;  /* pixel counts of each pixa */
1026 l_int32   **pixrowcts;  /* row-by-row pixel counts of each pixa */
1027 l_int32     x, y, rowcount, downcount, wpl;
1028 l_uint8     byte;
1029 
1030     PROCNAME("jbClassifyCorrelation");
1031 
1032     if (!classer)
1033         return ERROR_INT("classer not found", procName, 1);
1034     if (!boxa)
1035         return ERROR_INT("boxa not found", procName, 1);
1036     if (!pixas)
1037         return ERROR_INT("pixas not found", procName, 1);
1038 
1039     npages = classer->npages;
1040 
1041         /* Generate the bordered pixa, which contains all the the
1042          * input components.  This will not be saved.   */
1043     n = pixaGetCount(pixas);
1044     pixa1 = pixaCreate(n);
1045     for (i = 0; i < n; i++) {
1046         pix = pixaGetPix(pixas, i, L_CLONE);
1047         pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS,
1048                     JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0);
1049         pixaAddPix(pixa1, pix1, L_INSERT);
1050         pixDestroy(&pix);
1051     }
1052 
1053         /* Use these to save the class and page of each component. */
1054     naclass = classer->naclass;
1055     napage = classer->napage;
1056 
1057         /* Get the number of fg pixels in each component.  */
1058     nafgt = classer->nafgt;    /* holds fg areas of the templates */
1059     sumtab = makePixelSumTab8();
1060 
1061     pixcts = (l_int32 *)CALLOC(n, sizeof(*pixcts));
1062     pixrowcts = (l_int32 **)CALLOC(n, sizeof(*pixrowcts));
1063     centtab = makePixelCentroidTab8();
1064     if (!pixcts || !pixrowcts || !centtab)
1065         return ERROR_INT("calloc fail in pix*cts or centtab", procName, 1);
1066 
1067         /* Count the "1" pixels in each row of the pix in pixa1; this
1068          * allows pixCorrelationScoreThresholded to abort early if a match
1069          * is impossible.  This loop merges three calculations: the total
1070          * number of "1" pixels, the number of "1" pixels in each row, and
1071          * the centroid.  The centroids are relative to the UL corner of
1072          * each (bordered) pix.  The pixrowcts[i][y] are the total number
1073          * of fg pixels in pixa[i] below row y. */
1074     pta = ptaCreate(n);
1075     for (i = 0; i < n; i++) {
1076         pix = pixaGetPix(pixa1, i, L_CLONE);
1077         pixrowcts[i] = (l_int32 *)CALLOC(pixGetHeight(pix),
1078                                          sizeof(**pixrowcts));
1079         xsum = 0;
1080         ysum = 0;
1081         wpl = pixGetWpl(pix);
1082         row = pixGetData(pix) + (pixGetHeight(pix) - 1) * wpl;
1083         downcount = 0;
1084         for (y = pixGetHeight(pix) - 1; y >= 0; y--, row -= wpl) {
1085             pixrowcts[i][y] = downcount;
1086             rowcount = 0;
1087             for (x = 0; x < wpl; x++) {
1088                 word = row[x];
1089                 byte = word & 0xff;
1090                 rowcount += sumtab[byte];
1091                 xsum += centtab[byte] + (x * 32 + 24) * sumtab[byte];
1092                 byte = (word >> 8) & 0xff;
1093                 rowcount += sumtab[byte];
1094                 xsum += centtab[byte] + (x * 32 + 16) * sumtab[byte];
1095                 byte = (word >> 16) & 0xff;
1096                 rowcount += sumtab[byte];
1097                 xsum += centtab[byte] + (x * 32 + 8) * sumtab[byte];
1098                 byte = (word >> 24) & 0xff;
1099                 rowcount += sumtab[byte];
1100                 xsum += centtab[byte] + x * 32 * sumtab[byte];
1101             }
1102             downcount += rowcount;
1103             ysum += rowcount * y;
1104         }
1105         pixcts[i] = downcount;
1106         ptaAddPt(pta,
1107                  xsum / (l_float32)downcount, ysum / (l_float32)downcount);
1108         pixDestroy(&pix);
1109     }
1110 
1111     ptac = classer->ptac;  /* holds centroids of components up to this page */
1112     ptaJoin(ptac, pta, 0, 0);  /* save centroids of all components */
1113     ptact = classer->ptact;  /* holds centroids of templates */
1114 
1115     /* Store the unbordered pix in a pixaa, in a hierarchical
1116      * set of arrays.  There is one pixa for each class,
1117      * and the pix in each pixa are all the instances found
1118      * of that class.  This is actually more than one would need
1119      * for a jbig2 encoder, but there are two reasons to keep
1120      * them around: (1) the set of instances for each class
1121      * can be used to make an improved binary (or, better,
1122      * a grayscale) template, rather than simply using the first
1123      * one in the set; (2) we can investigate the failures
1124      * of the classifier.  This pixaa grows as we process
1125      * successive pages. */
1126     pixaa = classer->pixaa;
1127 
1128         /* Array to store class exemplars */
1129     pixat = classer->pixat;
1130 
1131         /* Fill up the pixaa tree with the template exemplars as
1132          * the first pix in each pixa.  As we add each pix,
1133          * we also add the associated box to the pixa.
1134          * We also keep track of the centroid of each pix,
1135          * and use the difference between centroids (of the
1136          * pix with the exemplar we are checking it with)
1137          * to align the two when checking that the correlation
1138          * score exceeds a threshold.  The correlation score
1139          * is given by the square of the area of the AND
1140          * between aligned instance and template, divided by
1141          * the product of areas of each image.  For identical
1142          * template and instance, the score is 1.0.
1143          * If the threshold is too small, non-equivalent instances
1144          * will be placed in the same class; if too large, there will
1145          * be an unnecessary division of classes representing the
1146          * same character.  The weightfactor adds in some of the
1147          * difference (1.0 - thresh), depending on the heaviness
1148          * of the template (measured as the fraction of fg pixels). */
1149     thresh = classer->thresh;
1150     weight = classer->weightfactor;
1151     naarea = classer->naarea;
1152     nahash = classer->nahash;
1153     for (i = 0; i < n; i++) {
1154         pix1 = pixaGetPix(pixa1, i, L_CLONE);
1155         area1 = pixcts[i];
1156         ptaGetPt(pta, i, &x1, &y1);  /* centroid for this instance */
1157         nt = pixaGetCount(pixat);
1158         found = FALSE;
1159         findcontext = findSimilarSizedTemplatesInit(classer, pix1);
1160         while ( (iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) {
1161                 /* Get the template */
1162             pix2 = pixaGetPix(pixat, iclass, L_CLONE);
1163             numaGetIValue(nafgt, iclass, &area2);
1164             ptaGetPt(ptact, iclass, &x2, &y2);  /* template centroid */
1165 
1166                 /* Find threshold for this template */
1167             if (weight > 0.0) {
1168                 numaGetIValue(naarea, iclass, &area);
1169                 threshold = thresh + (1. - thresh) * weight * area2 / area;
1170             }
1171             else
1172                 threshold = thresh;
1173 
1174                 /* Find score for this template */
1175             overthreshold = pixCorrelationScoreThresholded(pix1, pix2,
1176                                                            area1, area2,
1177                                                            x1 - x2, y1 - y2,
1178                                                            MAX_DIFF_WIDTH,
1179                                                            MAX_DIFF_HEIGHT,
1180                                                            sumtab,
1181                                                            pixrowcts[i],
1182                                                            threshold);
1183 #if DEBUG_CORRELATION_SCORE
1184             {
1185                 l_float32 score, testscore;
1186                 l_int32 count, testcount;
1187                 score = pixCorrelationScore(pix1, pix2, area1, area2,
1188                                             x1 - x2, y1 - y2,
1189                                             MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT,
1190                                             sumtab);
1191 
1192                 testscore = pixCorrelationScoreSimple(pix1, pix2, area1, area2,
1193                                   x1 - x2, y1 - y2, MAX_DIFF_WIDTH,
1194                                   MAX_DIFF_HEIGHT, sumtab);
1195                 count = (l_int32)rint(sqrt(score * area1 * area2));
1196                 testcount = (l_int32)rint(sqrt(testscore * area1 * area2));
1197                 if ((score >= threshold) != (testscore >= threshold)) {
1198                     fprintf(stderr, "Correlation score mismatch: %d(%g,%d) vs %d(%g,%d) (%g)\n",
1199                             count, score, score >= threshold,
1200                             testcount, testscore, testscore >= threshold,
1201                             score - testscore);
1202                 }
1203 
1204                 if ((score >= threshold) != overthreshold) {
1205                     fprintf(stderr, "Mismatch between correlation/threshold comparison: %g(%g,%d) >= %g(%g) vs %s\n",
1206                             score, score*area1*area2, count, threshold, threshold*area1*area2, (overthreshold ? "true" : "false"));
1207                 }
1208             }
1209 #endif  /* DEBUG_CORRELATION_SCORE */
1210             pixDestroy(&pix2);
1211 
1212             if (overthreshold) {  /* greedy match */
1213                 found = TRUE;
1214                 numaAddNumber(naclass, iclass);
1215                 numaAddNumber(napage, npages);
1216                 if (classer->keep_pixaa) {
1217                         /* We are keeping a record of all components */
1218                     pixa = pixaaGetPixa(pixaa, iclass, L_CLONE);
1219                     pix = pixaGetPix(pixas, i, L_CLONE);
1220                     pixaAddPix(pixa, pix, L_INSERT);
1221                     box = boxaGetBox(boxa, i, L_CLONE);
1222                     pixaAddBox(pixa, box, L_INSERT);
1223                     pixaDestroy(&pixa);
1224                 }
1225                 break;
1226             }
1227         }
1228         findSimilarSizedTemplatesDestroy(&findcontext);
1229         if (found == FALSE) {  /* new class */
1230             numaAddNumber(naclass, nt);
1231             numaAddNumber(napage, npages);
1232             pixa = pixaCreate(0);
1233             pix = pixaGetPix(pixas, i, L_CLONE);  /* unbordered instance */
1234             pixaAddPix(pixa, pix, L_INSERT);
1235             wt = pixGetWidth(pix);
1236             ht = pixGetHeight(pix);
1237             numaHashAdd(nahash, ht * wt, nt);
1238             box = boxaGetBox(boxa, i, L_CLONE);
1239             pixaAddBox(pixa, box, L_INSERT);
1240             pixaaAddPixa(pixaa, pixa, L_INSERT);  /* unbordered instance */
1241             ptaAddPt(ptact, x1, y1);
1242             numaAddNumber(nafgt, area1);
1243             pixaAddPix(pixat, pix1, L_INSERT);   /* bordered template */
1244             area = (pixGetWidth(pix1) - 2 * JB_ADDED_PIXELS) *
1245                    (pixGetHeight(pix1) - 2 * JB_ADDED_PIXELS);
1246             numaAddNumber(naarea, area);
1247         }
1248         else {   /* don't save it */
1249             pixDestroy(&pix1);
1250         }
1251     }
1252     classer->nclass = pixaGetCount(pixat);
1253 
1254     FREE(pixcts);
1255     FREE(centtab);
1256     for (i = 0; i < n; i++) {
1257         FREE(pixrowcts[i]);
1258     }
1259     FREE(pixrowcts);
1260 
1261     FREE(sumtab);
1262     ptaDestroy(&pta);
1263     pixaDestroy(&pixa1);
1264     return 0;
1265 }
1266 
1267 
1268 /*----------------------------------------------------------------------*
1269  *             Determine the image components we start with             *
1270  *----------------------------------------------------------------------*/
1271 /*!
1272  *  jbGetComponents()
1273  *
1274  *      Input:  pixs (1 bpp)
1275  *              components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS)
1276  *              maxwidth, maxheight (of saved components; larger are discarded)
1277  *              &pboxa (<return> b.b. of component items)
1278  *              &ppixa (<return> component items)
1279  *      Return: 0 if OK, 1 on error
1280  */
1281 l_int32
jbGetComponents(PIX * pixs,l_int32 components,l_int32 maxwidth,l_int32 maxheight,BOXA ** pboxad,PIXA ** ppixad)1282 jbGetComponents(PIX     *pixs,
1283                 l_int32  components,
1284                 l_int32  maxwidth,
1285                 l_int32  maxheight,
1286                 BOXA   **pboxad,
1287                 PIXA   **ppixad)
1288 {
1289 l_int32    empty, res, redfactor;
1290 BOXA      *boxa;
1291 PIX       *pixt1, *pixt2, *pixt3;
1292 PIXA      *pixa, *pixat;
1293 
1294     PROCNAME("jbGetComponents");
1295 
1296     if (!pboxad)
1297         return ERROR_INT("&boxad not defined", procName, 1);
1298     *pboxad = NULL;
1299     if (!ppixad)
1300         return ERROR_INT("&pixad not defined", procName, 1);
1301     *ppixad = NULL;
1302     if (!pixs)
1303         return ERROR_INT("pixs not defined", procName, 1);
1304     if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
1305         components != JB_WORDS)
1306         return ERROR_INT("invalid components", procName, 1);
1307 
1308     pixZero(pixs, &empty);
1309     if (empty) {
1310         *pboxad = boxaCreate(0);
1311         *ppixad = pixaCreate(0);
1312         return 0;
1313     }
1314 
1315         /* If required, preprocess input pixs.  The method for both
1316          * characters and words is to generate a connected component
1317          * mask over the units that we want to aggregrate, which are,
1318          * in general, sets of related connected components in pixs.
1319          * For characters, we want to include the dots with
1320          * 'i', 'j' and '!', so we do a small vertical closing to
1321          * generate the mask.  For words, we make a mask over all
1322          * characters in each word.  This is a bit more tricky, because
1323          * the spacing between words is difficult to predict a priori,
1324          * and words can be typeset with variable spacing that can
1325          * in some cases be barely larger than the space between
1326          * characters.  The first step is to generate the mask and
1327          * identify each of its connected components.  */
1328     if (components == JB_CONN_COMPS) {  /* no preprocessing */
1329         boxa = pixConnComp(pixs, &pixa, 8);
1330     }
1331     else if (components == JB_CHARACTERS) {
1332         pixt1 = pixMorphSequence(pixs, "c1.6", 0);
1333         boxa = pixConnComp(pixt1, &pixat, 8);
1334         pixa = pixaClipToPix(pixat, pixs);
1335         pixDestroy(&pixt1);
1336         pixaDestroy(&pixat);
1337     }
1338     else {  /* components == JB_WORDS */
1339 
1340             /* Do the operations at about 150 ppi resolution.
1341              * It is much faster at 75 ppi, but the results are
1342              * more accurate at 150 ppi.  This will segment the
1343              * words in body text.  It can be expected that relatively
1344              * infrequent words in a larger font will be split. */
1345         res = pixGetXRes(pixs);
1346         if (res <= 200) {
1347             redfactor = 1;
1348             pixt1 = pixClone(pixs);
1349         }
1350         else if (res <= 400) {
1351             redfactor = 2;
1352             pixt1 = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0);
1353         }
1354         else {
1355             redfactor = 4;
1356             pixt1 = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0);
1357         }
1358 
1359         pixt2 = pixWordMaskByDilation(pixt1, 0, NULL);
1360 
1361             /* Expand the optimally dilated word mask to full res. */
1362         pixt3 = pixExpandReplicate(pixt2, redfactor);
1363 
1364             /* Pull out the pixels in pixs corresponding to the mask
1365              * components in pixt3.  Note that above we used threshold
1366              * levels in the reduction of 1 to insure that the resulting
1367              * mask fully covers the input pixs.  The downside of using
1368              * a threshold of 1 is that very close characters from adjacent
1369              * lines can be joined.  But with a level of 2 or greater,
1370              * it is necessary to use a seedfill, followed by a pixOr():
1371              *       pixt4 = pixSeedfillBinary(NULL, pixt3, pixs, 8);
1372              *       pixOr(pixt3, pixt3, pixt4);
1373              * to insure that the mask coverage is complete over pixs.  */
1374         boxa = pixConnComp(pixt3, &pixat, 4);
1375         pixa = pixaClipToPix(pixat, pixs);
1376         pixaDestroy(&pixat);
1377         pixDestroy(&pixt1);
1378         pixDestroy(&pixt2);
1379         pixDestroy(&pixt3);
1380     }
1381 
1382         /* Remove large components, and save the results.  */
1383     *ppixad = pixaSelectBySize(pixa, maxwidth, maxheight, L_SELECT_IF_BOTH,
1384                                L_SELECT_IF_LTE, NULL);
1385     *pboxad = boxaSelectBySize(boxa, maxwidth, maxheight, L_SELECT_IF_BOTH,
1386                                L_SELECT_IF_LTE, NULL);
1387     pixaDestroy(&pixa);
1388     boxaDestroy(&boxa);
1389 
1390     return 0;
1391 }
1392 
1393 
1394 /*!
1395  *  pixWordMaskByDilation()
1396  *
1397  *      Input:  pixs (1 bpp; typ. at 75 to 150 ppi)
1398  *              maxsize (use 0 for default; not to exceed 14)
1399  *              &size (<optional return> size of optimal horiz Sel)
1400  *      Return: pixd (dilated word mask), or null on error
1401  *
1402  *  Notes:
1403  *      (1) For 75 to 150 ppi, the optimal dilation should not exceed 7.
1404  *          This is the default size chosen if maxsize <= 0.
1405  *      (2) To run this on images at resolution between 200 and 300, it
1406  *          is advisable to use a larger maxsize, say between 10 and 14.
1407  *      (3) The best size for dilating to get word masks is optionally returned.
1408  */
1409 PIX *
pixWordMaskByDilation(PIX * pixs,l_int32 maxsize,l_int32 * psize)1410 pixWordMaskByDilation(PIX      *pixs,
1411                       l_int32   maxsize,
1412                       l_int32  *psize)
1413 {
1414 l_int32  i, diffmin, ndiff, imin;
1415 l_int32  ncc[MAX_ALLOWED_DILATION + 1];
1416 BOXA    *boxa;
1417 NUMA    *nacc;
1418 PIX     *pixt1, *pixt2, *pixt3;
1419 PIXA    *pixa;
1420 SEL     *sel;
1421 
1422     PROCNAME("pixWordMaskbyDilation");
1423 
1424     if (!pixs)
1425         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1426 
1427         /* Find the optimal dilation to create the word mask.
1428          * Look for successively increasing dilations where the
1429          * number of connected components doesn't decrease.
1430          * This is the situation where the components in the
1431          * word mask should properly cover each word.  Use of
1432          * 4 cc slightly reduces the likelihood that words from
1433          * different lines are joined.  */
1434     diffmin = 1000000;
1435     pixa = pixaCreate(8);
1436     pixt1 = pixCopy(NULL, pixs);
1437     pixaAddPix(pixa, pixt1, L_COPY);
1438 
1439     if (maxsize <= 0)
1440         maxsize = 7;  /* default */
1441     if (maxsize > MAX_ALLOWED_DILATION)
1442         maxsize = MAX_ALLOWED_DILATION;
1443     nacc = numaCreate(maxsize);
1444     for (i = 0; i <= maxsize; i++) {
1445         if (i == 0)     /* first one not dilated */
1446             pixt2 = pixCopy(NULL, pixt1);
1447         else    /* successive dilation by sel_2h */
1448             pixt2 = pixMorphSequence(pixt1, "d2.1", 0);
1449         boxa = pixConnCompBB(pixt2, 4);
1450         ncc[i] = boxaGetCount(boxa);
1451         numaAddNumber(nacc, ncc[i]);
1452         if (i > 0) {
1453             ndiff = ncc[i - 1] - ncc[i];
1454 #if  DEBUG_PLOT_CC
1455             fprintf(stderr, "ndiff[%d] = %d\n", i - 1, ndiff);
1456 #endif  /* DEBUG_PLOT_CC */
1457             if (ndiff < diffmin) {
1458                 imin = i;
1459                 diffmin = ndiff;
1460             }
1461         }
1462         pixaAddPix(pixa, pixt2, L_COPY);
1463         pixDestroy(&pixt1);
1464         pixt1 = pixt2;
1465         boxaDestroy(&boxa);
1466     }
1467     pixDestroy(&pixt1);
1468 
1469 #if  DEBUG_PLOT_CC
1470     {GPLOT *gplot;
1471      NUMA  *naseq;
1472         naseq = numaMakeSequence(1, 1, numaGetCount(nacc));
1473         gplot = gplotCreate("junk_cc_output", GPLOT_PNG,
1474                             "Number of cc vs. horizontal dilation",
1475                             "Sel horiz", "Number of cc");
1476         gplotAddPlot(gplot, naseq, nacc, GPLOT_LINES, "");
1477         gplotMakeOutput(gplot);
1478         gplotDestroy(&gplot);
1479         numaDestroy(&naseq);
1480     }
1481 #endif  /* DEBUG_PLOT_CC */
1482 
1483         /* Save the result of the optimal dilation */
1484     pixt2 = pixaGetPix(pixa, imin, L_CLONE);
1485     sel = selCreateBrick(1, imin, 0, imin - 1, SEL_HIT);
1486     pixt3 = pixErode(NULL, pixt2, sel);  /* remove effect of dilation */
1487     selDestroy(&sel);
1488     pixDestroy(&pixt2);
1489     pixaDestroy(&pixa);
1490     if (psize)
1491         *psize = imin + 1;
1492 
1493     numaDestroy(&nacc);
1494     return pixt3;
1495 }
1496 
1497 
1498 /*----------------------------------------------------------------------*
1499  *                 Build grayscale composites (templates)               *
1500  *----------------------------------------------------------------------*/
1501 /*!
1502  *  jbAccumulateComposites()
1503  *
1504  *      Input:  pixaa (one pixa for each class)
1505  *              &pna (<return> number of samples used to build each composite)
1506  *              &ptat (<return> centroids of bordered composites)
1507  *      Return: pixad (accumulated sum of samples in each class),
1508  *                     or null on error
1509  *
1510  */
1511 PIXA *
jbAccumulateComposites(PIXAA * pixaa,NUMA ** pna,PTA ** pptat)1512 jbAccumulateComposites(PIXAA  *pixaa,
1513                        NUMA  **pna,
1514                        PTA   **pptat)
1515 {
1516 l_int32    n, nt, i, j, d, minw, maxw, minh, maxh, xdiff, ydiff;
1517 l_float32  x, y, xave, yave;
1518 NUMA      *na;
1519 PIX       *pix, *pixt1, *pixt2, *pixsum;
1520 PIXA      *pixa, *pixad;
1521 PTA       *ptat, *pta;
1522 
1523     PROCNAME("jbAccumulateComposites");
1524 
1525     if (!pptat)
1526         return (PIXA *)ERROR_PTR("&ptat not defined", procName, NULL);
1527     *pptat = NULL;
1528     if (!pna)
1529         return (PIXA *)ERROR_PTR("&na not defined", procName, NULL);
1530     *pna = NULL;
1531     if (!pixaa)
1532         return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL);
1533 
1534     n = pixaaGetCount(pixaa);
1535     if ((ptat = ptaCreate(n)) == NULL)
1536         return (PIXA *)ERROR_PTR("ptat not made", procName, NULL);
1537     *pptat = ptat;
1538     pixad = pixaCreate(n);
1539     na = numaCreate(n);
1540     *pna = na;
1541 
1542     for (i = 0; i < n; i++) {
1543         pixa = pixaaGetPixa(pixaa, i, L_CLONE);
1544         nt = pixaGetCount(pixa);
1545         numaAddNumber(na, nt);
1546         if (nt == 0) {
1547             L_WARNING("empty pixa found!", procName);
1548             pixaDestroy(&pixa);
1549             continue;
1550         }
1551         pixaSizeRange(pixa, &minw, &minh, &maxw, &maxh);
1552         pix = pixaGetPix(pixa, 0, L_CLONE);
1553         d = pixGetDepth(pix);
1554         pixDestroy(&pix);
1555         pixt1 = pixCreate(maxw, maxh, d);
1556         pixsum = pixInitAccumulate(maxw, maxh, 0);
1557         pta = pixaCentroids(pixa);
1558 
1559             /* Find the average value of the centroids ... */
1560         xave = yave = 0;
1561         for (j = 0; j < nt; j++) {
1562             ptaGetPt(pta, j, &x, &y);
1563             xave += x;
1564             yave += y;
1565         }
1566         xave = xave / (l_float32)nt;
1567         yave = yave / (l_float32)nt;
1568 
1569             /* and place all centroids at their average value */
1570         for (j = 0; j < nt; j++) {
1571             pixt2 = pixaGetPix(pixa, j, L_CLONE);
1572             ptaGetPt(pta, j, &x, &y);
1573             xdiff = (l_int32)(x - xave);
1574             ydiff = (l_int32)(y - yave);
1575             pixClearAll(pixt1);
1576             pixRasterop(pixt1, xdiff, ydiff, maxw, maxh, PIX_SRC,
1577                         pixt2, 0, 0);
1578             pixAccumulate(pixsum, pixt1, L_ARITH_ADD);
1579             pixDestroy(&pixt2);
1580         }
1581         pixaAddPix(pixad, pixsum, L_INSERT);
1582         ptaAddPt(ptat, xave, yave);
1583 
1584         pixaDestroy(&pixa);
1585         pixDestroy(&pixt1);
1586         ptaDestroy(&pta);
1587     }
1588 
1589     return pixad;
1590 }
1591 
1592 
1593 /*!
1594  *  jbTemplatesFromComposites()
1595  *
1596  *      Input:  pixac (one pix of composites for each class)
1597  *              na (number of samples used for each class composite)
1598  *      Return: pixad (8 bpp templates for each class), or null on error
1599  *
1600  */
1601 PIXA *
jbTemplatesFromComposites(PIXA * pixac,NUMA * na)1602 jbTemplatesFromComposites(PIXA  *pixac,
1603                           NUMA  *na)
1604 {
1605 l_int32    n, i;
1606 l_float32  nt;  /* number of samples in the composite; always an integer */
1607 l_float32  factor;
1608 PIX       *pixsum;   /* accumulated composite */
1609 PIX       *pixd;
1610 PIXA      *pixad;
1611 
1612     PROCNAME("jbTemplatesFromComposites");
1613 
1614     if (!pixac)
1615         return (PIXA *)ERROR_PTR("pixac not defined", procName, NULL);
1616     if (!na)
1617         return (PIXA *)ERROR_PTR("na not defined", procName, NULL);
1618 
1619     n = pixaGetCount(pixac);
1620     pixad = pixaCreate(n);
1621     for (i = 0; i < n; i++) {
1622         pixsum = pixaGetPix(pixac, i, L_COPY);  /* changed internally */
1623         numaGetFValue(na, i, &nt);
1624         factor = 255. / nt;
1625         pixMultConstAccumulate(pixsum, factor, 0);  /* changes pixsum */
1626         pixd = pixFinalAccumulate(pixsum, 0, 8);
1627         pixaAddPix(pixad, pixd, L_INSERT);
1628         pixDestroy(&pixsum);
1629     }
1630 
1631     return pixad;
1632 }
1633 
1634 
1635 
1636 /*----------------------------------------------------------------------*
1637  *                       jbig2 utility routines                         *
1638  *----------------------------------------------------------------------*/
1639 /*!
1640  *  jbClasserCreate()
1641  *
1642  *      Input:  method (JB_RANKHAUS, JB_CORRELATION)
1643  *              components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS)
1644  *      Return: jbclasser, or null on error
1645  */
1646 JBCLASSER *
jbClasserCreate(l_int32 method,l_int32 components)1647 jbClasserCreate(l_int32  method,
1648                 l_int32  components)
1649 {
1650 JBCLASSER  *classer;
1651 
1652     PROCNAME("jbClasserCreate");
1653 
1654     if ((classer = (JBCLASSER *)CALLOC(1, sizeof(JBCLASSER))) == NULL)
1655         return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL);
1656     if (method != JB_RANKHAUS && method != JB_CORRELATION)
1657         return (JBCLASSER *)ERROR_PTR("invalid type", procName, NULL);
1658     if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
1659         components != JB_WORDS)
1660         return (JBCLASSER *)ERROR_PTR("invalid type", procName, NULL);
1661 
1662     classer->method = method;
1663     classer->components = components;
1664     classer->nacomps = numaCreate(0);
1665     classer->pixaa = pixaaCreate(0);
1666     classer->pixat = pixaCreate(0);
1667     classer->pixatd = pixaCreate(0);
1668     classer->nafgt = numaCreate(0);
1669     classer->naarea = numaCreate(0);
1670     classer->ptac = ptaCreate(0);
1671     classer->ptact = ptaCreate(0);
1672     classer->naclass = numaCreate(0);
1673     classer->napage = numaCreate(0);
1674     classer->ptaul = ptaCreate(0);
1675 
1676     return classer;
1677 }
1678 
1679 
1680 /*
1681  *  jbClasserDestroy()
1682  *
1683  *      Input: &classer (<to be nulled>)
1684  *      Return: void
1685  */
1686 void
jbClasserDestroy(JBCLASSER ** pclasser)1687 jbClasserDestroy(JBCLASSER  **pclasser)
1688 {
1689 JBCLASSER  *classer;
1690 
1691     if (!pclasser)
1692         return;
1693     if ((classer = *pclasser) == NULL)
1694         return;
1695 
1696     sarrayDestroy(&classer->safiles);
1697     numaDestroy(&classer->nacomps);
1698     pixaaDestroy(&classer->pixaa);
1699     pixaDestroy(&classer->pixat);
1700     pixaDestroy(&classer->pixatd);
1701     numaHashDestroy(&classer->nahash);
1702     numaDestroy(&classer->nafgt);
1703     numaDestroy(&classer->naarea);
1704     ptaDestroy(&classer->ptac);
1705     ptaDestroy(&classer->ptact);
1706     numaDestroy(&classer->naclass);
1707     numaDestroy(&classer->napage);
1708     ptaDestroy(&classer->ptaul);
1709     ptaDestroy(&classer->ptall);
1710     FREE(classer);
1711     *pclasser = NULL;
1712     return;
1713 }
1714 
1715 
1716 /*!
1717  *  jbDataSave()
1718  *
1719  *      Input:  jbclasser
1720  *              latticew, latticeh (cell size used to store each
1721  *                  connected component in the composite)
1722  *      Return: jbdata, or null on error
1723  *
1724  *  Notes:
1725  *      (1) This routine stores the jbig2-type data required for
1726  *          generating a lossy jbig2 version of the image.
1727  *          It can be losslessly written to (and read from) two files.
1728  *      (2) It generates and stores the mosaic of templates.
1729  *      (3) It clones the Numa and Pta arrays, so these must all
1730  *          be destroyed by the caller.
1731  *      (4) Input 0 to use the default values for latticew and/or latticeh,
1732  */
1733 JBDATA *
jbDataSave(JBCLASSER * classer)1734 jbDataSave(JBCLASSER  *classer)
1735 {
1736 l_int32  maxw, maxh;
1737 JBDATA  *data;
1738 PIX     *pix;
1739 
1740     PROCNAME("jbDataSave");
1741 
1742     if (!classer)
1743         return (JBDATA *)ERROR_PTR("classer not defined", procName, NULL);
1744 
1745         /* Write the templates into an array. */
1746     pixaSizeRange(classer->pixat, NULL, NULL, &maxw, &maxh);
1747     if ((pix = pixaDisplayOnLattice(classer->pixat, maxw + 1, maxh + 1))
1748             == NULL)
1749         return (JBDATA *)ERROR_PTR("data not made", procName, NULL);
1750 
1751     if ((data = (JBDATA *)CALLOC(1, sizeof(JBDATA))) == NULL)
1752         return (JBDATA *)ERROR_PTR("data not made", procName, NULL);
1753     data->pix = pix;
1754     data->npages = classer->npages;
1755     data->w = classer->w;
1756     data->h = classer->h;
1757     data->nclass = classer->nclass;
1758     data->latticew = maxw + 1;
1759     data->latticeh = maxh + 1;
1760     data->naclass = numaClone(classer->naclass);
1761     data->napage = numaClone(classer->napage);
1762     data->ptaul = ptaClone(classer->ptaul);
1763 
1764     return data;
1765 }
1766 
1767 
1768 /*
1769  *  jbDataDestroy()
1770  *
1771  *      Input: &data (<to be nulled>)
1772  *      Return: void
1773  */
1774 void
jbDataDestroy(JBDATA ** pdata)1775 jbDataDestroy(JBDATA  **pdata)
1776 {
1777 JBDATA  *data;
1778 
1779     if (!pdata)
1780         return;
1781     if ((data = *pdata) == NULL)
1782         return;
1783 
1784     pixDestroy(&data->pix);
1785     numaDestroy(&data->naclass);
1786     numaDestroy(&data->napage);
1787     ptaDestroy(&data->ptaul);
1788     FREE(data);
1789     *pdata = NULL;
1790     return;
1791 }
1792 
1793 
1794 /*!
1795  *  jbDataWrite()
1796  *
1797  *      Input:  rootname (for output files; everything but the extension)
1798  *              jbdata
1799  *      Return: 0 if OK, 1 on error
1800  *
1801  *  Notes:
1802  *      (1) Serialization function that writes data in jbdata to file.
1803  */
1804 l_int32
jbDataWrite(const char * rootout,JBDATA * jbdata)1805 jbDataWrite(const char  *rootout,
1806             JBDATA      *jbdata)
1807 {
1808 char     buf[L_BUF_SIZE];
1809 l_int32  w, h, nclass, npages, cellw, cellh, ncomp, i, x, y, iclass, ipage;
1810 NUMA    *naclass, *napage;
1811 PTA     *ptaul;
1812 PIX     *pixt;
1813 FILE    *fp;
1814 
1815     PROCNAME("jbDataWrite");
1816 
1817     if (!rootout)
1818         return ERROR_INT("no rootout", procName, 1);
1819     if (!jbdata)
1820         return ERROR_INT("no jbdata", procName, 1);
1821 
1822     npages = jbdata->npages;
1823     w = jbdata->w;
1824     h = jbdata->h;
1825     pixt = jbdata->pix;
1826     nclass = jbdata->nclass;
1827     cellw = jbdata->latticew;
1828     cellh = jbdata->latticeh;
1829     naclass = jbdata->naclass;
1830     napage = jbdata->napage;
1831     ptaul = jbdata->ptaul;
1832 
1833     snprintf(buf, L_BUF_SIZE, "%s%s", rootout, JB_TEMPLATE_EXT);
1834     pixWrite(buf, pixt, IFF_PNG);
1835 
1836     snprintf(buf, L_BUF_SIZE, "%s%s", rootout, JB_DATA_EXT);
1837     if ((fp = fopen(buf, "wb")) == NULL)
1838         return ERROR_INT("stream not opened", procName, 1);
1839     ncomp = ptaGetCount(ptaul);
1840     fprintf(fp, "jb data file\n");
1841     fprintf(fp, "num pages = %d\n", npages);
1842     fprintf(fp, "page size: w = %d, h = %d\n", w, h);
1843     fprintf(fp, "num components = %d\n", ncomp);
1844     fprintf(fp, "num classes = %d\n", nclass);
1845     fprintf(fp, "template lattice size: w = %d, h = %d\n", cellw, cellh);
1846     for (i = 0; i < ncomp; i++) {
1847         numaGetIValue(napage, i, &ipage);
1848         numaGetIValue(naclass, i, &iclass);
1849         ptaGetIPt(ptaul, i, &x, &y);
1850         fprintf(fp, "%d %d %d %d\n", ipage, iclass, x, y);
1851     }
1852     fclose(fp);
1853 
1854     return 0;
1855 }
1856 
1857 
1858 /*!
1859  *  jbDataRead()
1860  *
1861  *      Input:  rootname (for template and data files)
1862  *      Return: jbdata, or NULL on error
1863  */
1864 JBDATA *
jbDataRead(const char * rootname)1865 jbDataRead(const char  *rootname)
1866 {
1867 char      fname[L_BUF_SIZE];
1868 char     *linestr;
1869 l_uint8  *data;
1870 l_int32   nsa, i, w, h, cellw, cellh, x, y, iclass, ipage;
1871 l_int32   nbytes, npages, nclass, ncomp;
1872 JBDATA   *jbdata;
1873 NUMA     *naclass, *napage;
1874 PIX      *pixs;
1875 PTA      *ptaul;
1876 SARRAY   *sa;
1877 
1878     PROCNAME("jbDataRead");
1879 
1880     if (!rootname)
1881         return (JBDATA *)ERROR_PTR("rootname not defined", procName, NULL);
1882 
1883     snprintf(fname, L_BUF_SIZE, "%s%s", rootname, JB_TEMPLATE_EXT);
1884     if ((pixs = pixRead(fname)) == NULL)
1885         return (JBDATA *)ERROR_PTR("pix not read", procName, NULL);
1886 
1887     snprintf(fname, L_BUF_SIZE, "%s%s", rootname, JB_DATA_EXT);
1888     if ((data = arrayRead(fname, &nbytes)) == NULL)
1889         return (JBDATA *)ERROR_PTR("data not read", procName, NULL);
1890 
1891     if ((sa = sarrayCreateLinesFromString((char *)data, 0)) == NULL)
1892         return (JBDATA *)ERROR_PTR("sa not made", procName, NULL);
1893     nsa = sarrayGetCount(sa);   /* number of cc + 6 */
1894     linestr = sarrayGetString(sa, 0, 0);
1895     if (strcmp(linestr, "jb data file"))
1896         return (JBDATA *)ERROR_PTR("invalid jb data file", procName, NULL);
1897     linestr = sarrayGetString(sa, 1, 0);
1898     sscanf(linestr, "num pages = %d", &npages);
1899     linestr = sarrayGetString(sa, 2, 0);
1900     sscanf(linestr, "page size: w = %d, h = %d", &w, &h);
1901     linestr = sarrayGetString(sa, 3, 0);
1902     sscanf(linestr, "num components = %d", &ncomp);
1903     linestr = sarrayGetString(sa, 4, 0);
1904     sscanf(linestr, "num classes = %d\n", &nclass);
1905     linestr = sarrayGetString(sa, 5, 0);
1906     sscanf(linestr, "template lattice size: w = %d, h = %d\n", &cellw, &cellh);
1907 
1908 #if 1
1909     fprintf(stderr, "num pages = %d\n", npages);
1910     fprintf(stderr, "page size: w = %d, h = %d\n", w, h);
1911     fprintf(stderr, "num components = %d\n", ncomp);
1912     fprintf(stderr, "num classes = %d\n", nclass);
1913     fprintf(stderr, "template lattice size: w = %d, h = %d\n", cellw, cellh);
1914 #endif
1915 
1916     if ((naclass = numaCreate(ncomp)) == NULL)
1917         return (JBDATA *)ERROR_PTR("naclass not made", procName, NULL);
1918     if ((napage = numaCreate(ncomp)) == NULL)
1919         return (JBDATA *)ERROR_PTR("napage not made", procName, NULL);
1920     if ((ptaul = ptaCreate(ncomp)) == NULL)
1921         return (JBDATA *)ERROR_PTR("pta not made", procName, NULL);
1922     for (i = 6; i < nsa; i++) {
1923         linestr = sarrayGetString(sa, i, 0);
1924         sscanf(linestr, "%d %d %d %d\n", &ipage, &iclass, &x, &y);
1925         numaAddNumber(napage, ipage);
1926         numaAddNumber(naclass, iclass);
1927         ptaAddPt(ptaul, x, y);
1928     }
1929 
1930     if ((jbdata = (JBDATA *)CALLOC(1, sizeof(JBDATA))) == NULL)
1931         return (JBDATA *)ERROR_PTR("data not made", procName, NULL);
1932     jbdata->pix = pixs;
1933     jbdata->npages = npages;
1934     jbdata->w = w;
1935     jbdata->h = h;
1936     jbdata->nclass = nclass;
1937     jbdata->latticew = cellw;
1938     jbdata->latticeh = cellh;
1939     jbdata->naclass = naclass;
1940     jbdata->napage = napage;
1941     jbdata->ptaul = ptaul;
1942 
1943     FREE(data);
1944     sarrayDestroy(&sa);
1945     return jbdata;
1946 }
1947 
1948 
1949 /*!
1950  *  jbDataRender()
1951  *
1952  *      Input:  jbdata
1953  *              debugflag (if TRUE, writes into 2 bpp pix and adds
1954  *                         component outlines in color)
1955  *      Return: pixa (reconstruction of original images, using templates) or
1956  *              null on error
1957  */
1958 PIXA *
jbDataRender(JBDATA * data,l_int32 debugflag)1959 jbDataRender(JBDATA  *data,
1960              l_int32  debugflag)
1961 {
1962 l_int32   i, w, h, cellw, cellh, x, y, iclass, ipage;
1963 l_int32   npages, nclass, ncomp, wp, hp;
1964 BOX      *box;
1965 NUMA     *naclass, *napage;
1966 PIX      *pixt, *pixt2, *pix, *pixd;
1967 PIXA     *pixat;   /* pixa of templates */
1968 PIXA     *pixad;   /* pixa of output images */
1969 PIXCMAP  *cmap;
1970 PTA      *ptaul;
1971 
1972     PROCNAME("jbDataRender");
1973 
1974     if (!data)
1975         return (PIXA *)ERROR_PTR("data not defined", procName, NULL);
1976 
1977     npages = data->npages;
1978     w = data->w;
1979     h = data->h;
1980     pixt = data->pix;
1981     nclass = data->nclass;
1982     cellw = data->latticew;
1983     cellh = data->latticeh;
1984     naclass = data->naclass;
1985     napage = data->napage;
1986     ptaul = data->ptaul;
1987     ncomp = numaGetCount(naclass);
1988 
1989         /* Reconstruct the original set of images from the templates
1990          * and the data associated with each component.  First,
1991          * generate the output pixa as a set of empty pix. */
1992     if ((pixad = pixaCreate(npages)) == NULL)
1993         return (PIXA *)ERROR_PTR("pixad not made", procName, NULL);
1994     for (i = 0; i < npages; i++) {
1995         if (debugflag == FALSE)
1996             pix = pixCreate(w, h, 1);
1997         else {
1998             pix = pixCreate(w, h, 2);
1999             cmap = pixcmapCreate(2);
2000             pixcmapAddColor(cmap, 255, 255, 255);
2001             pixcmapAddColor(cmap, 0, 0, 0);
2002             pixcmapAddColor(cmap, 255, 0, 0);  /* for box outlines */
2003             pixSetColormap(pix, cmap);
2004         }
2005         pixaAddPix(pixad, pix, L_INSERT);
2006     }
2007 
2008         /* Put the class templates into a pixa. */
2009     if ((pixat = pixaCreateFromPix(pixt, nclass, cellw, cellh)) == NULL)
2010         return (PIXA *)ERROR_PTR("pixat not made", procName, NULL);
2011 
2012         /* Place each component in the right location on its page. */
2013     for (i = 0; i < ncomp; i++) {
2014         numaGetIValue(napage, i, &ipage);
2015         numaGetIValue(naclass, i, &iclass);
2016         pix = pixaGetPix(pixat, iclass, L_CLONE);  /* the template */
2017         wp = pixGetWidth(pix);
2018         hp = pixGetHeight(pix);
2019         ptaGetIPt(ptaul, i, &x, &y);
2020         pixd = pixaGetPix(pixad, ipage, L_CLONE);   /* the output page */
2021         if (debugflag == FALSE)
2022             pixRasterop(pixd, x, y, wp, hp, PIX_SRC | PIX_DST, pix, 0, 0);
2023         else {
2024             pixt2 = pixConvert1To2Cmap(pix);
2025             pixRasterop(pixd, x, y, wp, hp, PIX_SRC | PIX_DST, pixt2, 0, 0);
2026             box = boxCreate(x, y, wp, hp);
2027             pixRenderBoxArb(pixd, box, 1, 255, 0, 0);
2028             pixDestroy(&pixt2);
2029             boxDestroy(&box);
2030         }
2031         pixDestroy(&pix);   /* the clone only */
2032         pixDestroy(&pixd);  /* the clone only */
2033     }
2034 
2035     pixaDestroy(&pixat);
2036     return pixad;
2037 }
2038 
2039 
2040 /*!
2041  *  jbGetULCorners()
2042  *
2043  *      Input:  jbclasser
2044  *              pixs (full res image)
2045  *              boxa (of c.c. bounding rectangles for this page)
2046  *      Return: 0 if OK, 1 on error
2047  *
2048  *  Notes:
2049  *      (1) This computes the ptaul field, which has the global UL corners,
2050  *          adjusted for each specific component, so that each component
2051  *          can be replaced by the template for its class and have the
2052  *          centroid in the template in the same position as the
2053  *          centroid of the original connected component.  It is important
2054  *          that this be done properly to avoid a wavy baseline in the
2055  *          result.
2056  *      (2) The array fields ptac and ptact give the centroids of
2057  *          those components relative to the UL corner of each component.
2058  *          Here, we compute the difference in each component, round to
2059  *          nearest integer, and correct the box->x and box->y by
2060  *          the appropriate integral difference.
2061  *      (3) The templates and stored instances are all bordered.
2062  */
2063 l_int32
jbGetULCorners(JBCLASSER * classer,PIX * pixs,BOXA * boxa)2064 jbGetULCorners(JBCLASSER  *classer,
2065                PIX        *pixs,
2066                BOXA       *boxa)
2067 {
2068 l_int32    i, baseindex, index, n, iclass, idelx, idely, x, y, dx, dy;
2069 l_int32   *sumtab;
2070 l_float32  x1, x2, y1, y2, delx, dely;
2071 BOX       *box;
2072 NUMA      *naclass;
2073 PIX       *pixt;
2074 PTA       *ptac, *ptact, *ptaul;
2075 
2076     PROCNAME("jbGetULCorners");
2077 
2078     if (!classer)
2079         return ERROR_INT("classer not defined", procName, 1);
2080     if (!pixs)
2081         return ERROR_INT("pixs not defined", procName, 1);
2082     if (!boxa)
2083         return ERROR_INT("boxa not defined", procName, 1);
2084 
2085     n = boxaGetCount(boxa);
2086     ptaul = classer->ptaul;
2087     naclass = classer->naclass;
2088     ptac = classer->ptac;
2089     ptact = classer->ptact;
2090     baseindex = classer->baseindex;  /* num components before this page */
2091     sumtab = makePixelSumTab8();
2092     for (i = 0; i < n; i++) {
2093         index = baseindex + i;
2094         ptaGetPt(ptac, index, &x1, &y1);
2095         numaGetIValue(naclass, index, &iclass);
2096         ptaGetPt(ptact, iclass, &x2, &y2);
2097         delx = x2 - x1;
2098         dely = y2 - y1;
2099         if (delx >= 0)
2100             idelx = (l_int32)(delx + 0.5);
2101         else
2102             idelx = (l_int32)(delx - 0.5);
2103         if (dely >= 0)
2104             idely = (l_int32)(dely + 0.5);
2105         else
2106             idely = (l_int32)(dely - 0.5);
2107         if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL)
2108             return ERROR_INT("box not found", procName, 1);
2109         boxGetGeometry(box, &x, &y, NULL, NULL);
2110 
2111             /* Get final increments dx and dy for best alignment */
2112         pixt = pixaGetPix(classer->pixat, iclass, L_CLONE);
2113         finalPositioningForAlignment(pixs, x, y, idelx, idely,
2114                                      pixt, sumtab, &dx, &dy);
2115 /*        if (i % 20 == 0)
2116             fprintf(stderr, "dx = %d, dy = %d\n", dx, dy); */
2117         ptaAddPt(ptaul, x - idelx + dx, y - idely + dy);
2118         boxDestroy(&box);
2119         pixDestroy(&pixt);
2120     }
2121 
2122     FREE(sumtab);
2123     return 0;
2124 }
2125 
2126 
2127 /*!
2128  *  jbGetLLCorners()
2129  *
2130  *      Input:  jbclasser
2131  *      Return: 0 if OK, 1 on error
2132  *
2133  *  Notes:
2134  *      (1) This computes the ptall field, which has the global LL corners,
2135  *          adjusted for each specific component, so that each component
2136  *          can be replaced by the template for its class and have the
2137  *          centroid in the template in the same position as the
2138  *          centroid of the original connected component. It is important
2139  *          that this be done properly to avoid a wavy baseline in the result.
2140  *      (2) It is computed here from the corresponding UL corners, where
2141  *          the input templates and stored instances are all bordered.
2142  *          This should be done after all pages have been processed.
2143  *      (3) For proper substitution, the templates whose LL corners are
2144  *          placed in these locations must be UN-bordered.
2145  *          This is available for a realistic jbig2 encoder, which would
2146  *          (1) encode each template without a border, and (2) encode
2147  *          the position using the LL corner (rather than the UL
2148  *          corner) because the difference between y-values
2149  *          of successive instances is typically close to zero.
2150  */
2151 l_int32
jbGetLLCorners(JBCLASSER * classer)2152 jbGetLLCorners(JBCLASSER  *classer)
2153 {
2154 l_int32    i, iclass, n, x1, y1, h;
2155 NUMA      *naclass;
2156 PIX       *pix;
2157 PIXA      *pixat;
2158 PTA       *ptaul, *ptall;
2159 
2160     PROCNAME("jbGetLLCorners");
2161 
2162     if (!classer)
2163         return ERROR_INT("classer not defined", procName, 1);
2164 
2165     ptaul = classer->ptaul;
2166     naclass = classer->naclass;
2167     pixat = classer->pixat;
2168 
2169     ptaDestroy(&classer->ptall);
2170     n = ptaGetCount(ptaul);
2171     ptall = ptaCreate(n);
2172     classer->ptall = ptall;
2173 
2174         /* If the templates were bordered, we would add h - 1 to the UL
2175          * corner y-value.  However, because the templates to be used
2176          * here have their borders removed, and the borders are
2177          * JB_ADDED_PIXELS on each side, we add h - 1 - 2 * JB_ADDED_PIXELS
2178          * to the UL corner y-value.  */
2179     for (i = 0; i < n; i++) {
2180         ptaGetIPt(ptaul, i, &x1, &y1);
2181         numaGetIValue(naclass, i, &iclass);
2182         pix = pixaGetPix(pixat, iclass, L_CLONE);
2183         h = pixGetHeight(pix);
2184         ptaAddPt(ptall, x1, y1 + h - 1 - 2 * JB_ADDED_PIXELS);
2185         pixDestroy(&pix);
2186     }
2187 
2188     return 0;
2189 }
2190 
2191 
2192 /*----------------------------------------------------------------------*
2193  *                              Static helpers                          *
2194  *----------------------------------------------------------------------*/
2195 /* When looking for similar matches we check templates whose size is +/- 2 in
2196  * each direction. This involves 25 possible sizes. This array contains the
2197  * offsets for each of those positions in a spiral pattern. There are 25 pairs
2198  * of numbers in this array: even positions are x values. */
2199 static int two_by_two_walk[50] = {
2200   0, 0,
2201   0, 1,
2202   -1, 0,
2203   0, -1,
2204   1, 0,
2205   -1, 1,
2206   1, 1,
2207   -1, -1,
2208   1, -1,
2209   0, -2,
2210   2, 0,
2211   0, 2,
2212   -2, 0,
2213   -1, -2,
2214   1, -2,
2215   2, -1,
2216   2, 1,
2217   1, 2,
2218   -1, 2,
2219   -2, 1,
2220   -2, -1,
2221   -2, -2,
2222   2, -2,
2223   2, 2,
2224   -2, 2};
2225 
2226 
2227 /*!
2228  *  findSimilarSizedTemplatesInit()
2229  *
2230  *      Input:  classer
2231  *              pixs (instance to be matched)
2232  *      Return: Allocated context to be used with findSimilar*
2233  */
2234 static JBFINDCTX *
findSimilarSizedTemplatesInit(JBCLASSER * classer,PIX * pixs)2235 findSimilarSizedTemplatesInit(JBCLASSER  *classer,
2236                               PIX        *pixs)
2237 {
2238 JBFINDCTX  *state;
2239 
2240     state = (JBFINDCTX *)CALLOC(1, sizeof(JBFINDCTX));
2241     state->w = pixGetWidth(pixs) - 2 * JB_ADDED_PIXELS;
2242     state->h = pixGetHeight(pixs) - 2 * JB_ADDED_PIXELS;
2243     state->classer = classer;
2244 
2245     return state;
2246 }
2247 
2248 
2249 static void
findSimilarSizedTemplatesDestroy(JBFINDCTX ** pstate)2250 findSimilarSizedTemplatesDestroy(JBFINDCTX  **pstate)
2251 {
2252 JBFINDCTX  *state;
2253 
2254     PROCNAME("findSimilarSizedTemplatesDestroy");
2255 
2256     if (pstate == NULL) {
2257         L_WARNING("ptr address is null", procName);
2258         return;
2259     }
2260     if ((state = *pstate) == NULL)
2261         return;
2262 
2263     numaDestroy(&state->numa);
2264     FREE(state);
2265     *pstate = NULL;
2266     return;
2267 }
2268 
2269 
2270 /*!
2271  *  findSimilarSizedTemplatesNext()
2272  *
2273  *      Input:  state (from findSimilarSizedTemplatesInit)
2274  *      Return: Next template number, or -1 when finished
2275  *
2276  *  We have a hash table mapping template area to a list of template
2277  *  numbers with that area.  We wish to find similar sized templates,
2278  *  so we first look for templates with the same width and height, and
2279  *  then with width + 1, etc.  This walk is guided by the
2280  *  two_by_two_walk array, above.
2281  *
2282  *  We don't want to have to collect the whole list of templates first because
2283  *  (we hope) to find it quickly.  So we keep the context for this walk in an
2284  *  explictit state structure and this function acts like a generator.
2285  */
2286 static l_int32
findSimilarSizedTemplatesNext(JBFINDCTX * state)2287 findSimilarSizedTemplatesNext(JBFINDCTX  *state)
2288 {
2289 l_int32  desiredh, desiredw, size, templ;
2290 PIX     *pixt;
2291 
2292     while(1) {  /* Continue the walk over step 'i' */
2293         if (state->i >= 25) {  /* all done */
2294             return -1;
2295         }
2296 
2297         desiredw = state->w + two_by_two_walk[2 * state->i];
2298         desiredh = state->h + two_by_two_walk[2 * state->i + 1];
2299         if (desiredh < 1 || desiredw < 1) {  /* invalid size */
2300             state->i++;
2301             continue;
2302         }
2303 
2304         if (!state->numa) {
2305                 /* We have yet to start walking the array for the step 'i' */
2306             state->numa = numaHashGetNuma(state->classer->nahash,
2307                                           desiredh * desiredw);
2308             if (!state->numa) {  /* nothing there */
2309                 state->i++;
2310                 continue;
2311             }
2312 
2313             state->n = 0;  /* OK, we got a numa. */
2314         }
2315 
2316             /* Continue working on this numa */
2317         size = numaGetCount(state->numa);
2318         for ( ; state->n < size; ) {
2319             templ = (l_int32)(state->numa->array[state->n++] + 0.5);
2320             pixt = pixaGetPix(state->classer->pixat, templ, L_CLONE);
2321             if (pixGetWidth(pixt) - 2 * JB_ADDED_PIXELS == desiredw &&
2322                 pixGetHeight(pixt) - 2 * JB_ADDED_PIXELS == desiredh) {
2323                 pixDestroy(&pixt);
2324                 return templ;
2325             }
2326             pixDestroy(&pixt);
2327         }
2328 
2329             /* Exhausted the numa; take another step and try again */
2330         state->i++;
2331         numaDestroy(&state->numa);
2332         continue;
2333     }
2334 }
2335 
2336 
2337 /*!
2338  *  finalPositioningForAlignment()
2339  *
2340  *      Input:  pixs (input page image)
2341  *              x, y (location of UL corner of bb of component in pixs)
2342  *              idelx, idely (compensation to match centroids of component
2343  *                            and template)
2344  *              pixt (template, with JB_ADDED_PIXELS of padding on all sides)
2345  *              sumtab (for summing fg pixels in an image)
2346  *              &dx, &dy (return delta on position for best match; each
2347  *                        one is in the set {-1, 0, 1})
2348  *      Return: 0 if OK, 1 on error
2349  *
2350  */
2351 static l_int32
finalPositioningForAlignment(PIX * pixs,l_int32 x,l_int32 y,l_int32 idelx,l_int32 idely,PIX * pixt,l_int32 * sumtab,l_int32 * pdx,l_int32 * pdy)2352 finalPositioningForAlignment(PIX      *pixs,
2353                              l_int32   x,
2354                              l_int32   y,
2355                              l_int32   idelx,
2356                              l_int32   idely,
2357                              PIX      *pixt,
2358                              l_int32  *sumtab,
2359                              l_int32  *pdx,
2360                              l_int32  *pdy)
2361 {
2362 l_int32  w, h, i, j, minx, miny, count, mincount;
2363 PIX     *pixi;  /* clipped from source pixs */
2364 PIX     *pixr;  /* temporary storage */
2365 BOX     *box;
2366 
2367     PROCNAME("finalPositioningForAlignment");
2368 
2369     if (!pixs)
2370         return ERROR_INT("pixs not defined", procName, 1);
2371     if (!pixt)
2372         return ERROR_INT("pixt not defined", procName, 1);
2373     if (!pdx || !pdy)
2374         return ERROR_INT("&dx and &dy not both defined", procName, 1);
2375     if (!sumtab)
2376         return ERROR_INT("sumtab not defined", procName, 1);
2377     *pdx = *pdy = 0;
2378 
2379         /* Use JB_ADDED_PIXELS pixels padding on each side */
2380     w = pixGetWidth(pixt);
2381     h = pixGetHeight(pixt);
2382     box = boxCreate(x - idelx - JB_ADDED_PIXELS,
2383                     y - idely - JB_ADDED_PIXELS, w, h);
2384     pixi = pixClipRectangle(pixs, box, NULL);
2385     boxDestroy(&box);
2386     if (!pixi)
2387         return ERROR_INT("pixi not made", procName, 1);
2388 
2389     pixr = pixCreate(pixGetWidth(pixi), pixGetHeight(pixi), 1);
2390     mincount = 0x7fffffff;
2391     for (i = -1; i <= 1; i++) {
2392         for (j = -1; j <= 1; j++) {
2393             pixCopy(pixr, pixi);
2394             pixRasterop(pixr, j, i, w, h, PIX_SRC ^ PIX_DST, pixt, 0, 0);
2395             pixCountPixels(pixr, &count, sumtab);
2396             if (count < mincount) {
2397                 minx = j;
2398                 miny = i;
2399                 mincount = count;
2400             }
2401         }
2402     }
2403     pixDestroy(&pixi);
2404     pixDestroy(&pixr);
2405 
2406     *pdx = minx;
2407     *pdy = miny;
2408     return 0;
2409 }
2410