Leptonica  1.82.0
Image processing and image analysis suite
jbclass.c
1 /*====================================================================*
2  - Copyright (C) 2001 Leptonica. All rights reserved.
3  -
4  - Redistribution and use in source and binary forms, with or without
5  - modification, are permitted provided that the following conditions
6  - are met:
7  - 1. Redistributions of source code must retain the above copyright
8  - notice, this list of conditions and the following disclaimer.
9  - 2. Redistributions in binary form must reproduce the above
10  - copyright notice, this list of conditions and the following
11  - disclaimer in the documentation and/or other materials
12  - provided with the distribution.
13  -
14  - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18  - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *====================================================================*/
26 
27 /*
28  * jbclass.c
29  *
30  * These are functions for unsupervised classification of
31  * collections of connected components -- either characters or
32  * words -- in binary images. They can be used as image
33  * processing steps in jbig2 compression.
34  *
35  * Initialization
36  *
37  * JBCLASSER *jbRankHausInit() [rank hausdorff encoder]
38  * JBCLASSER *jbCorrelationInit() [correlation encoder]
39  * JBCLASSER *jbCorrelationInitWithoutComponents() [ditto]
40  * static JBCLASSER *jbCorrelationInitInternal()
41  *
42  * Classify the pages
43  *
44  * l_int32 jbAddPages()
45  * l_int32 jbAddPage()
46  * l_int32 jbAddPageComponents()
47  *
48  * Rank hausdorff classifier
49  *
50  * l_int32 jbClassifyRankHaus()
51  * l_int32 pixHaustest()
52  * l_int32 pixRankHaustest()
53  *
54  * Binary correlation classifier
55  *
56  * l_int32 jbClassifyCorrelation()
57  *
58  * Determine the image components we start with
59  *
60  * l_int32 jbGetComponents()
61  * l_int32 pixWordMaskByDilation()
62  * l_int32 pixWordBoxesByDilation()
63  *
64  * Build grayscale composites (templates)
65  *
66  * PIXA *jbAccumulateComposites
67  * PIXA *jbTemplatesFromComposites
68  *
69  * Utility functions for Classer
70  *
71  * JBCLASSER *jbClasserCreate()
72  * void jbClasserDestroy()
73  *
74  * Utility functions for Data
75  *
76  * JBDATA *jbDataSave()
77  * void jbDataDestroy()
78  * l_int32 jbDataWrite()
79  * JBDATA *jbDataRead()
80  * PIXA *jbDataRender()
81  * l_int32 jbGetULCorners()
82  * l_int32 jbGetLLCorners()
83  *
84  * Static helpers
85  *
86  * static JBFINDCTX *findSimilarSizedTemplatesInit()
87  * static l_int32 findSimilarSizedTemplatesNext()
88  * static void findSimilarSizedTemplatesDestroy()
89  * static l_int32 finalPositioningForAlignment()
90  *
91  * Note: this is NOT an implementation of the JPEG jbig2
92  * proposed standard encoder, the specifications for which
93  * can be found at http://www.jpeg.org/jbigpt2.html.
94  * (See below for a full implementation.)
95  * It is an implementation of the lower-level part of an encoder that:
96  *
97  * (1) identifies connected components that are going to be used
98  * (2) puts them in similarity classes (this is an unsupervised
99  * classifier), and
100  * (3) stores the result in a simple file format (2 files,
101  * one for templates and one for page/coordinate/template-index
102  * quartets).
103  *
104  * An actual implementation of the official jbig2 encoder could
105  * start with parts (1) and (2), and would then compress the quartets
106  * according to the standards requirements (e.g., Huffman or
107  * arithmetic coding of coordinate differences and image templates).
108  *
109  * The low-level part of the encoder provided here has the
110  * following useful features:
111  *
112  * ~ It is accurate in the identification of templates
113  * and classes because it uses a windowed hausdorff
114  * distance metric.
115  * ~ It is accurate in the placement of the connected
116  * components, doing a two step process of first aligning
117  * the the centroids of the template with those of each instance,
118  * and then making a further correction of up to +- 1 pixel
119  * in each direction to best align the templates.
120  * ~ It is fast because it uses a morphologically based
121  * matching algorithm to implement the hausdorff criterion,
122  * and it selects the patterns that are possible matches
123  * based on their size.
124  *
125  * We provide two different matching functions, one using Hausdorff
126  * distance and one using a simple image correlation.
127  * The Hausdorff method sometimes produces better results for the
128  * same number of classes, because it gives a relatively small
129  * effective weight to foreground pixels near the boundary,
130  * and a relatively large weight to foreground pixels that are
131  * not near the boundary. By effectively ignoring these boundary
132  * pixels, Hausdorff weighting corresponds better to the expected
133  * probabilities of the pixel values in a scanned image, where the
134  * variations in instances of the same printed character are much
135  * more likely to be in pixels near the boundary. By contrast,
136  * the correlation method gives equal weight to all foreground pixels.
137  *
138  * For best results, use the correlation method. Correlation takes
139  * the number of fg pixels in the AND of instance and template,
140  * divided by the product of the number of fg pixels in instance
141  * and template. It compares this with a threshold that, in
142  * general, depends on the fractional coverage of the template.
143  * For heavy text, the threshold is raised above that for light
144  * text, By using both these parameters (basic threshold and
145  * adjustment factor for text weight), one has more flexibility
146  * and can arrive at the fewest substitution errors, although
147  * this comes at the price of more templates.
148  *
149  * The strict Hausdorff scoring is not a rank weighting, because a
150  * single pixel beyond the given distance will cause a match
151  * failure. A rank Hausdorff is more robust to non-boundary noise,
152  * but it is also more susceptible to confusing components that
153  * should be in different classes. For implementing a jbig2
154  * application for visually lossless binary image compression,
155  * you have two choices:
156  *
157  * (1) use a 3x3 structuring element (size = 3) and a strict
158  * Hausdorff comparison (rank = 1.0 in the rank Hausdorff
159  * function). This will result in a minimal number of classes,
160  * but confusion of small characters, such as italic and
161  * non-italic lower-case 'o', can still occur.
162  * (2) use the correlation method with a threshold of 0.85
163  * and a weighting factor of about 0.7. This will result in
164  * a larger number of classes, but should not be confused
165  * either by similar small characters or by extremely
166  * thick sans serif characters, such as in prog/cootoots.png.
167  *
168  * As mentioned above, if visual substitution errors must be
169  * avoided, you should use the correlation method.
170  *
171  * We provide executables that show how to do the encoding:
172  * prog/jbrankhaus.c
173  * prog/jbcorrelation.c
174  *
175  * The basic flow for correlation classification goes as follows,
176  * where specific choices have been made for parameters (Hausdorff
177  * is the same except for initialization):
178  *
179  * // Initialize and save data in the classer
180  * JBCLASSER *classer =
181  * jbCorrelationInit(JB_CONN_COMPS, 0, 0, 0.8, 0.7);
182  * SARRAY *safiles = getSortedPathnamesInDirectory(directory,
183  * NULL, 0, 0);
184  * jbAddPages(classer, safiles);
185  *
186  * // Save the data in a data structure for serialization,
187  * // and write it into two files.
188  * JBDATA *data = jbDataSave(classer);
189  * jbDataWrite(rootname, data);
190  *
191  * // Reconstruct (render) the pages from the encoded data.
192  * PIXA *pixa = jbDataRender(data, FALSE);
193  *
194  * Adam Langley has built a jbig2 standards-compliant encoder, the
195  * first one to appear in open source. You can get this encoder at:
196  * http://www.imperialviolet.org/jbig2.html
197  *
198  * It uses arithmetic encoding throughout. It encodes binary images
199  * losslessly with a single arithmetic coding over the full image.
200  * It also does both lossy and lossless encoding from connected
201  * components, using leptonica to generate the templates representing
202  * each cluster.
203  */
204 
205 #ifdef HAVE_CONFIG_H
206 #include <config_auto.h>
207 #endif /* HAVE_CONFIG_H */
208 
209 #include <string.h>
210 #include <math.h>
211 #include "allheaders.h"
212 
213 #define L_BUF_SIZE 512
214 
215  /* For jbClassifyRankHaus(): size of border added around
216  * pix of each c.c., to allow further processing. This
217  * should be at least the sum of the MAX_DIFF_HEIGHT
218  * (or MAX_DIFF_WIDTH) and one-half the size of the Sel */
219 static const l_int32 JB_ADDED_PIXELS = 6;
220 
221  /* For pixHaustest(), pixRankHaustest() and pixCorrelationScore():
222  * choose these to be 2 or greater */
223 static const l_int32 MAX_DIFF_WIDTH = 2; /* use at least 2 */
224 static const l_int32 MAX_DIFF_HEIGHT = 2; /* use at least 2 */
225 
226  /* In initialization, you have the option to discard components
227  * (cc, characters or words) that have either width or height larger
228  * than a given size. This is convenient for jbDataSave(), because
229  * the components are placed onto a regular lattice with cell
230  * dimension equal to the maximum component size. The default
231  * values are given here. If you want to save all components,
232  * use a sufficiently large set of dimensions. */
233 static const l_int32 MAX_CONN_COMP_WIDTH = 350; /* default max cc width */
234 static const l_int32 MAX_CHAR_COMP_WIDTH = 350; /* default max char width */
235 static const l_int32 MAX_WORD_COMP_WIDTH = 1000; /* default max word width */
236 static const l_int32 MAX_COMP_HEIGHT = 120; /* default max component height */
237 
238  /* This stores the state of a state machine which fetches
239  * similar sized templates */
241 {
242  JBCLASSER *classer; /* classer */
243  l_int32 w; /* desired width */
244  l_int32 h; /* desired height */
245  l_int32 i; /* index into two_by_two step array */
246  L_DNA *dna; /* current number array */
247  l_int32 n; /* current element of dna */
248 };
249 typedef struct JbFindTemplatesState JBFINDCTX;
250 
251  /* Static initialization function */
252 static JBCLASSER * jbCorrelationInitInternal(l_int32 components,
253  l_int32 maxwidth, l_int32 maxheight, l_float32 thresh,
254  l_float32 weightfactor, l_int32 keep_components);
255 
256  /* Static helper functions */
257 static JBFINDCTX * findSimilarSizedTemplatesInit(JBCLASSER *classer, PIX *pixs);
258 static l_int32 findSimilarSizedTemplatesNext(JBFINDCTX *context);
259 static void findSimilarSizedTemplatesDestroy(JBFINDCTX **pcontext);
260 static l_int32 finalPositioningForAlignment(PIX *pixs, l_int32 x, l_int32 y,
261  l_int32 idelx, l_int32 idely, PIX *pixt,
262  l_int32 *sumtab, l_int32 *pdx, l_int32 *pdy);
263 
264 #ifndef NO_CONSOLE_IO
265 #define DEBUG_CORRELATION_SCORE 0
266 #endif /* ~NO_CONSOLE_IO */
267 
268 /*----------------------------------------------------------------------*
269  * Initialization *
270  *----------------------------------------------------------------------*/
285 JBCLASSER *
286 jbRankHausInit(l_int32 components,
287  l_int32 maxwidth,
288  l_int32 maxheight,
289  l_int32 size,
290  l_float32 rank)
291 {
292 JBCLASSER *classer;
293 
294  PROCNAME("jbRankHausInit");
295 
296  if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
297  components != JB_WORDS)
298  return (JBCLASSER *)ERROR_PTR("invalid components", procName, NULL);
299  if (size < 1 || size > 10)
300  return (JBCLASSER *)ERROR_PTR("size not reasonable", procName, NULL);
301  if (rank < 0.5 || rank > 1.0)
302  return (JBCLASSER *)ERROR_PTR("rank not in [0.5-1.0]", procName, NULL);
303  if (maxwidth == 0) {
304  if (components == JB_CONN_COMPS)
305  maxwidth = MAX_CONN_COMP_WIDTH;
306  else if (components == JB_CHARACTERS)
307  maxwidth = MAX_CHAR_COMP_WIDTH;
308  else /* JB_WORDS */
309  maxwidth = MAX_WORD_COMP_WIDTH;
310  }
311  if (maxheight == 0)
312  maxheight = MAX_COMP_HEIGHT;
313 
314  if ((classer = jbClasserCreate(JB_RANKHAUS, components)) == NULL)
315  return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL);
316  classer->maxwidth = maxwidth;
317  classer->maxheight = maxheight;
318  classer->sizehaus = size;
319  classer->rankhaus = rank;
320  classer->dahash = l_dnaHashCreate(5507, 4); /* 5507 is prime */
321  classer->keep_pixaa = 1; /* keep all components in pixaa */
322  return classer;
323 }
324 
325 
346 JBCLASSER *
347 jbCorrelationInit(l_int32 components,
348  l_int32 maxwidth,
349  l_int32 maxheight,
350  l_float32 thresh,
351  l_float32 weightfactor)
352 {
353  return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh,
354  weightfactor, 1);
355 }
356 
373 JBCLASSER *
374 jbCorrelationInitWithoutComponents(l_int32 components,
375  l_int32 maxwidth,
376  l_int32 maxheight,
377  l_float32 thresh,
378  l_float32 weightfactor)
379 {
380  return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh,
381  weightfactor, 0);
382 }
383 
384 
385 static JBCLASSER *
386 jbCorrelationInitInternal(l_int32 components,
387  l_int32 maxwidth,
388  l_int32 maxheight,
389  l_float32 thresh,
390  l_float32 weightfactor,
391  l_int32 keep_components)
392 {
393 JBCLASSER *classer;
394 
395  PROCNAME("jbCorrelationInitInternal");
396 
397  if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
398  components != JB_WORDS)
399  return (JBCLASSER *)ERROR_PTR("invalid components", procName, NULL);
400  if (thresh < 0.4 || thresh > 0.98)
401  return (JBCLASSER *)ERROR_PTR("thresh not in range [0.4 - 0.98]",
402  procName, NULL);
403  if (weightfactor < 0.0 || weightfactor > 1.0)
404  return (JBCLASSER *)ERROR_PTR("weightfactor not in range [0.0 - 1.0]",
405  procName, NULL);
406  if (maxwidth == 0) {
407  if (components == JB_CONN_COMPS)
408  maxwidth = MAX_CONN_COMP_WIDTH;
409  else if (components == JB_CHARACTERS)
410  maxwidth = MAX_CHAR_COMP_WIDTH;
411  else /* JB_WORDS */
412  maxwidth = MAX_WORD_COMP_WIDTH;
413  }
414  if (maxheight == 0)
415  maxheight = MAX_COMP_HEIGHT;
416 
417 
418  if ((classer = jbClasserCreate(JB_CORRELATION, components)) == NULL)
419  return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL);
420  classer->maxwidth = maxwidth;
421  classer->maxheight = maxheight;
422  classer->thresh = thresh;
423  classer->weightfactor = weightfactor;
424  classer->dahash = l_dnaHashCreate(5507, 4); /* 5507 is prime */
425  classer->keep_pixaa = keep_components;
426  return classer;
427 }
428 
429 
430 /*----------------------------------------------------------------------*
431  * Classify the pages *
432  *----------------------------------------------------------------------*/
446 l_ok
447 jbAddPages(JBCLASSER *classer,
448  SARRAY *safiles)
449 {
450 l_int32 i, nfiles;
451 char *fname;
452 PIX *pix;
453 
454  PROCNAME("jbAddPages");
455 
456  if (!classer)
457  return ERROR_INT("classer not defined", procName, 1);
458  if (!safiles)
459  return ERROR_INT("safiles not defined", procName, 1);
460 
461  classer->safiles = sarrayCopy(safiles);
462  nfiles = sarrayGetCount(safiles);
463  for (i = 0; i < nfiles; i++) {
464  fname = sarrayGetString(safiles, i, L_NOCOPY);
465  if ((pix = pixRead(fname)) == NULL) {
466  L_WARNING("image file %d not read\n", procName, i);
467  continue;
468  }
469  if (pixGetDepth(pix) != 1) {
470  L_WARNING("image file %d not 1 bpp\n", procName, i);
471  continue;
472  }
473  jbAddPage(classer, pix);
474  pixDestroy(&pix);
475  }
476 
477  return 0;
478 }
479 
480 
488 l_ok
489 jbAddPage(JBCLASSER *classer,
490  PIX *pixs)
491 {
492 BOXA *boxas;
493 PIXA *pixas;
494 
495  PROCNAME("jbAddPage");
496 
497  if (!classer)
498  return ERROR_INT("classer not defined", procName, 1);
499  if (!pixs || pixGetDepth(pixs) != 1)
500  return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
501 
502  classer->w = pixGetWidth(pixs);
503  classer->h = pixGetHeight(pixs);
504 
505  /* Get the appropriate components and their bounding boxes */
506  if (jbGetComponents(pixs, classer->components, classer->maxwidth,
507  classer->maxheight, &boxas, &pixas)) {
508  return ERROR_INT("components not made", procName, 1);
509  }
510 
511  jbAddPageComponents(classer, pixs, boxas, pixas);
512  boxaDestroy(&boxas);
513  pixaDestroy(&pixas);
514  return 0;
515 }
516 
517 
533 l_ok
534 jbAddPageComponents(JBCLASSER *classer,
535  PIX *pixs,
536  BOXA *boxas,
537  PIXA *pixas)
538 {
539 l_int32 n;
540 
541  PROCNAME("jbAddPageComponents");
542 
543  if (!classer)
544  return ERROR_INT("classer not defined", procName, 1);
545  if (!pixs)
546  return ERROR_INT("pix not defined", procName, 1);
547 
548  /* Test for no components on the current page. Always update the
549  * number of pages processed, even if nothing is on it. */
550  if (!boxas || !pixas || (boxaGetCount(boxas) == 0)) {
551  classer->npages++;
552  return 0;
553  }
554 
555  /* Get classes. For hausdorff, it uses a specified size of
556  * structuring element and specified rank. For correlation,
557  * it uses a specified threshold. */
558  if (classer->method == JB_RANKHAUS) {
559  if (jbClassifyRankHaus(classer, boxas, pixas))
560  return ERROR_INT("rankhaus classification failed", procName, 1);
561  } else { /* classer->method == JB_CORRELATION */
562  if (jbClassifyCorrelation(classer, boxas, pixas))
563  return ERROR_INT("correlation classification failed", procName, 1);
564  }
565 
566  /* Find the global UL corners, adjusted for each instance so
567  * that the class template and instance will have their
568  * centroids in the same place. Then the template can be
569  * used to replace the instance. */
570  if (jbGetULCorners(classer, pixs, boxas))
571  return ERROR_INT("UL corners not found", procName, 1);
572 
573  /* Update total component counts and number of pages processed. */
574  n = boxaGetCount(boxas);
575  classer->baseindex += n;
576  numaAddNumber(classer->nacomps, n);
577  classer->npages++;
578  return 0;
579 }
580 
581 
582 /*----------------------------------------------------------------------*
583  * Classification using windowed rank hausdorff metric *
584  *----------------------------------------------------------------------*/
593 l_ok
594 jbClassifyRankHaus(JBCLASSER *classer,
595  BOXA *boxa,
596  PIXA *pixas)
597 {
598 l_int32 n, nt, i, wt, ht, iclass, size, found, testval;
599 l_int32 npages, area1, area3;
600 l_int32 *tab8;
601 l_float32 rank, x1, y1, x2, y2;
602 BOX *box;
603 NUMA *naclass, *napage;
604 NUMA *nafg; /* fg area of all instances */
605 NUMA *nafgt; /* fg area of all templates */
606 JBFINDCTX *findcontext;
607 L_DNAHASH *dahash;
608 PIX *pix, *pix1, *pix2, *pix3, *pix4;
609 PIXA *pixa, *pixa1, *pixa2, *pixat, *pixatd;
610 PIXAA *pixaa;
611 PTA *pta, *ptac, *ptact;
612 SEL *sel;
613 
614  PROCNAME("jbClassifyRankHaus");
615 
616  if (!classer)
617  return ERROR_INT("classer not defined", procName, 1);
618  if (!boxa)
619  return ERROR_INT("boxa not defined", procName, 1);
620  if (!pixas)
621  return ERROR_INT("pixas not defined", procName, 1);
622  if ((n = pixaGetCount(pixas)) == 0)
623  return ERROR_INT("pixas is empty", procName, 1);
624  if ((nafg = pixaCountPixels(pixas)) == NULL) /* areas for this page */
625  return ERROR_INT("fg counting failed", procName, 1);
626 
627  npages = classer->npages;
628  size = classer->sizehaus;
629  sel = selCreateBrick(size, size, size / 2, size / 2, SEL_HIT);
630 
631  /* Generate the bordered pixa, with and without dilation.
632  * pixa1 and pixa2 contain all the input components. */
633  pixa1 = pixaCreate(n);
634  pixa2 = pixaCreate(n);
635  for (i = 0; i < n; i++) {
636  pix = pixaGetPix(pixas, i, L_CLONE);
637  pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS,
638  JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0);
639  pix2 = pixDilate(NULL, pix1, sel);
640  pixaAddPix(pixa1, pix1, L_INSERT); /* un-dilated */
641  pixaAddPix(pixa2, pix2, L_INSERT); /* dilated */
642  pixDestroy(&pix);
643  }
644 
645  /* Get the centroids of all the bordered images.
646  * These are relative to the UL corner of each (bordered) pix. */
647  pta = pixaCentroids(pixa1); /* centroids for this page; use here */
648  ptac = classer->ptac; /* holds centroids of components up to this page */
649  ptaJoin(ptac, pta, 0, -1); /* save centroids of all components */
650  ptact = classer->ptact; /* holds centroids of templates */
651 
652  /* Use these to save the class and page of each component. */
653  naclass = classer->naclass;
654  napage = classer->napage;
655 
656  /* Store the unbordered pix in a pixaa, in a hierarchical
657  * set of arrays. There is one pixa for each class,
658  * and the pix in each pixa are all the instances found
659  * of that class. This is actually more than one would need
660  * for a jbig2 encoder, but there are two reasons to keep
661  * them around: (1) the set of instances for each class
662  * can be used to make an improved binary (or, better,
663  * a grayscale) template, rather than simply using the first
664  * one in the set; (2) we can investigate the failures
665  * of the classifier. This pixaa grows as we process
666  * successive pages. */
667  pixaa = classer->pixaa;
668 
669  /* arrays to store class exemplars (templates) */
670  pixat = classer->pixat; /* un-dilated */
671  pixatd = classer->pixatd; /* dilated */
672 
673  /* Fill up the pixaa tree with the template exemplars as
674  * the first pix in each pixa. As we add each pix,
675  * we also add the associated box to the pixa.
676  * We also keep track of the centroid of each pix,
677  * and use the difference between centroids (of the
678  * pix with the exemplar we are checking it with)
679  * to align the two when checking that the Hausdorff
680  * distance does not exceed a threshold.
681  * The threshold is set by the Sel used for dilating.
682  * For example, a 3x3 brick, sel_3, corresponds to a
683  * Hausdorff distance of 1. In general, for an NxN brick,
684  * with N odd, corresponds to a Hausdorff distance of (N - 1)/2.
685  * It turns out that we actually need to use a sel of size 2x2
686  * to avoid small bad components when there is a halftone image
687  * from which components can be chosen.
688  * The larger the Sel you use, the fewer the number of classes,
689  * and the greater the likelihood of putting semantically
690  * different objects in the same class. For simplicity,
691  * we do this separately for the case of rank == 1.0 (exact
692  * match within the Hausdorff distance) and rank < 1.0. */
693  rank = classer->rankhaus;
694  dahash = classer->dahash;
695  if (rank == 1.0) {
696  for (i = 0; i < n; i++) {
697  pix1 = pixaGetPix(pixa1, i, L_CLONE);
698  pix2 = pixaGetPix(pixa2, i, L_CLONE);
699  ptaGetPt(pta, i, &x1, &y1);
700  nt = pixaGetCount(pixat); /* number of templates */
701  found = FALSE;
702  findcontext = findSimilarSizedTemplatesInit(classer, pix1);
703  while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) {
704  /* Find score for this template */
705  pix3 = pixaGetPix(pixat, iclass, L_CLONE);
706  pix4 = pixaGetPix(pixatd, iclass, L_CLONE);
707  ptaGetPt(ptact, iclass, &x2, &y2);
708  testval = pixHaustest(pix1, pix2, pix3, pix4, x1 - x2, y1 - y2,
709  MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT);
710  pixDestroy(&pix3);
711  pixDestroy(&pix4);
712  if (testval == 1) {
713  found = TRUE;
714  numaAddNumber(naclass, iclass);
715  numaAddNumber(napage, npages);
716  if (classer->keep_pixaa) {
717  pixa = pixaaGetPixa(pixaa, iclass, L_CLONE);
718  pix = pixaGetPix(pixas, i, L_CLONE);
719  pixaAddPix(pixa, pix, L_INSERT);
720  box = boxaGetBox(boxa, i, L_CLONE);
721  pixaAddBox(pixa, box, L_INSERT);
722  pixaDestroy(&pixa);
723  }
724  break;
725  }
726  }
727  findSimilarSizedTemplatesDestroy(&findcontext);
728  if (found == FALSE) { /* new class */
729  numaAddNumber(naclass, nt);
730  numaAddNumber(napage, npages);
731  pixa = pixaCreate(0);
732  pix = pixaGetPix(pixas, i, L_CLONE); /* unbordered instance */
733  pixaAddPix(pixa, pix, L_INSERT);
734  wt = pixGetWidth(pix);
735  ht = pixGetHeight(pix);
736  l_dnaHashAdd(dahash, (l_uint64)ht * wt, nt);
737  box = boxaGetBox(boxa, i, L_CLONE);
738  pixaAddBox(pixa, box, L_INSERT);
739  pixaaAddPixa(pixaa, pixa, L_INSERT); /* unbordered instance */
740  ptaAddPt(ptact, x1, y1);
741  pixaAddPix(pixat, pix1, L_INSERT); /* bordered template */
742  pixaAddPix(pixatd, pix2, L_INSERT); /* bordered dil template */
743  } else { /* don't save them */
744  pixDestroy(&pix1);
745  pixDestroy(&pix2);
746  }
747  }
748  } else { /* rank < 1.0 */
749  nafgt = classer->nafgt;
750  tab8 = makePixelSumTab8();
751  for (i = 0; i < n; i++) { /* all instances on this page */
752  pix1 = pixaGetPix(pixa1, i, L_CLONE);
753  numaGetIValue(nafg, i, &area1);
754  pix2 = pixaGetPix(pixa2, i, L_CLONE);
755  ptaGetPt(pta, i, &x1, &y1); /* use pta for this page */
756  nt = pixaGetCount(pixat); /* number of templates */
757  found = FALSE;
758  findcontext = findSimilarSizedTemplatesInit(classer, pix1);
759  while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) {
760  /* Find score for this template */
761  pix3 = pixaGetPix(pixat, iclass, L_CLONE);
762  numaGetIValue(nafgt, iclass, &area3);
763  pix4 = pixaGetPix(pixatd, iclass, L_CLONE);
764  ptaGetPt(ptact, iclass, &x2, &y2);
765  testval = pixRankHaustest(pix1, pix2, pix3, pix4,
766  x1 - x2, y1 - y2,
767  MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT,
768  area1, area3, rank, tab8);
769  pixDestroy(&pix3);
770  pixDestroy(&pix4);
771  if (testval == 1) { /* greedy match; take the first */
772  found = TRUE;
773  numaAddNumber(naclass, iclass);
774  numaAddNumber(napage, npages);
775  if (classer->keep_pixaa) {
776  pixa = pixaaGetPixa(pixaa, iclass, L_CLONE);
777  pix = pixaGetPix(pixas, i, L_CLONE);
778  pixaAddPix(pixa, pix, L_INSERT);
779  box = boxaGetBox(boxa, i, L_CLONE);
780  pixaAddBox(pixa, box, L_INSERT);
781  pixaDestroy(&pixa);
782  }
783  break;
784  }
785  }
786  findSimilarSizedTemplatesDestroy(&findcontext);
787  if (found == FALSE) { /* new class */
788  numaAddNumber(naclass, nt);
789  numaAddNumber(napage, npages);
790  pixa = pixaCreate(0);
791  pix = pixaGetPix(pixas, i, L_CLONE); /* unbordered instance */
792  pixaAddPix(pixa, pix, L_INSERT);
793  wt = pixGetWidth(pix);
794  ht = pixGetHeight(pix);
795  l_dnaHashAdd(dahash, (l_uint64)ht * wt, nt);
796  box = boxaGetBox(boxa, i, L_CLONE);
797  pixaAddBox(pixa, box, L_INSERT);
798  pixaaAddPixa(pixaa, pixa, L_INSERT); /* unbordered instance */
799  ptaAddPt(ptact, x1, y1);
800  pixaAddPix(pixat, pix1, L_INSERT); /* bordered template */
801  pixaAddPix(pixatd, pix2, L_INSERT); /* ditto */
802  numaAddNumber(nafgt, area1);
803  } else { /* don't save them */
804  pixDestroy(&pix1);
805  pixDestroy(&pix2);
806  }
807  }
808  LEPT_FREE(tab8);
809  }
810  classer->nclass = pixaGetCount(pixat);
811 
812  numaDestroy(&nafg);
813  ptaDestroy(&pta);
814  pixaDestroy(&pixa1);
815  pixaDestroy(&pixa2);
816  selDestroy(&sel);
817  return 0;
818 }
819 
820 
848 l_int32
849 pixHaustest(PIX *pix1,
850  PIX *pix2,
851  PIX *pix3,
852  PIX *pix4,
853  l_float32 delx, /* x(1) - x(3) */
854  l_float32 dely, /* y(1) - y(3) */
855  l_int32 maxdiffw,
856  l_int32 maxdiffh)
857 {
858 l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch;
859 PIX *pixt;
860 
861  /* Eliminate possible matches based on size difference */
862  wi = pixGetWidth(pix1);
863  hi = pixGetHeight(pix1);
864  wt = pixGetWidth(pix3);
865  ht = pixGetHeight(pix3);
866  delw = L_ABS(wi - wt);
867  if (delw > maxdiffw)
868  return FALSE;
869  delh = L_ABS(hi - ht);
870  if (delh > maxdiffh)
871  return FALSE;
872 
873  /* Round difference in centroid location to nearest integer;
874  * use this as a shift when doing the matching. */
875  if (delx >= 0)
876  idelx = (l_int32)(delx + 0.5);
877  else
878  idelx = (l_int32)(delx - 0.5);
879  if (dely >= 0)
880  idely = (l_int32)(dely + 0.5);
881  else
882  idely = (l_int32)(dely - 0.5);
883 
884  /* Do 1-direction hausdorff, checking that every pixel in pix1
885  * is within a dilation distance of some pixel in pix3. Namely,
886  * that pix4 entirely covers pix1:
887  * pixt = pixSubtract(NULL, pix1, pix4), including shift
888  * where pixt has no ON pixels. */
889  pixt = pixCreateTemplate(pix1);
890  pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC, pix1, 0, 0);
891  pixRasterop(pixt, idelx, idely, wi, hi, PIX_DST & PIX_NOT(PIX_SRC),
892  pix4, 0, 0);
893  pixZero(pixt, &boolmatch);
894  if (boolmatch == 0) {
895  pixDestroy(&pixt);
896  return FALSE;
897  }
898 
899  /* Do 1-direction hausdorff, checking that every pixel in pix3
900  * is within a dilation distance of some pixel in pix1. Namely,
901  * that pix2 entirely covers pix3:
902  * pixSubtract(pixt, pix3, pix2), including shift
903  * where pixt has no ON pixels. */
904  pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix3, 0, 0);
905  pixRasterop(pixt, 0, 0, wt, ht, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0);
906  pixZero(pixt, &boolmatch);
907  pixDestroy(&pixt);
908  return boolmatch;
909 }
910 
911 
946 l_int32
947 pixRankHaustest(PIX *pix1,
948  PIX *pix2,
949  PIX *pix3,
950  PIX *pix4,
951  l_float32 delx, /* x(1) - x(3) */
952  l_float32 dely, /* y(1) - y(3) */
953  l_int32 maxdiffw,
954  l_int32 maxdiffh,
955  l_int32 area1,
956  l_int32 area3,
957  l_float32 rank,
958  l_int32 *tab8)
959 {
960 l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch;
961 l_int32 thresh1, thresh3;
962 PIX *pixt;
963 
964  /* Eliminate possible matches based on size difference */
965  wi = pixGetWidth(pix1);
966  hi = pixGetHeight(pix1);
967  wt = pixGetWidth(pix3);
968  ht = pixGetHeight(pix3);
969  delw = L_ABS(wi - wt);
970  if (delw > maxdiffw)
971  return FALSE;
972  delh = L_ABS(hi - ht);
973  if (delh > maxdiffh)
974  return FALSE;
975 
976  /* Upper bounds in remaining pixels for allowable match */
977  thresh1 = (l_int32)(area1 * (1. - rank) + 0.5);
978  thresh3 = (l_int32)(area3 * (1. - rank) + 0.5);
979 
980  /* Round difference in centroid location to nearest integer;
981  * use this as a shift when doing the matching. */
982  if (delx >= 0)
983  idelx = (l_int32)(delx + 0.5);
984  else
985  idelx = (l_int32)(delx - 0.5);
986  if (dely >= 0)
987  idely = (l_int32)(dely + 0.5);
988  else
989  idely = (l_int32)(dely - 0.5);
990 
991  /* Do 1-direction hausdorff, checking that every pixel in pix1
992  * is within a dilation distance of some pixel in pix3. Namely,
993  * that pix4 entirely covers pix1:
994  * pixt = pixSubtract(NULL, pix1, pix4), including shift
995  * where pixt has no ON pixels. */
996  pixt = pixCreateTemplate(pix1);
997  pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC, pix1, 0, 0);
998  pixRasterop(pixt, idelx, idely, wi, hi, PIX_DST & PIX_NOT(PIX_SRC),
999  pix4, 0, 0);
1000  pixThresholdPixelSum(pixt, thresh1, &boolmatch, tab8);
1001  if (boolmatch == 1) { /* above thresh1 */
1002  pixDestroy(&pixt);
1003  return FALSE;
1004  }
1005 
1006  /* Do 1-direction hausdorff, checking that every pixel in pix3
1007  * is within a dilation distance of some pixel in pix1. Namely,
1008  * that pix2 entirely covers pix3:
1009  * pixSubtract(pixt, pix3, pix2), including shift
1010  * where pixt has no ON pixels. */
1011  pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix3, 0, 0);
1012  pixRasterop(pixt, 0, 0, wt, ht, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0);
1013  pixThresholdPixelSum(pixt, thresh3, &boolmatch, tab8);
1014  pixDestroy(&pixt);
1015  if (boolmatch == 1) /* above thresh3 */
1016  return FALSE;
1017  else
1018  return TRUE;
1019 }
1020 
1021 
1022 /*----------------------------------------------------------------------*
1023  * Classification using windowed correlation score *
1024  *----------------------------------------------------------------------*/
1033 l_ok
1034 jbClassifyCorrelation(JBCLASSER *classer,
1035  BOXA *boxa,
1036  PIXA *pixas)
1037 {
1038 l_int32 n, nt, i, iclass, wt, ht, found, area, area1, area2, npages,
1039  overthreshold;
1040 l_int32 *sumtab, *centtab;
1041 l_uint32 *row, word;
1042 l_float32 x1, y1, x2, y2, xsum, ysum;
1043 l_float32 thresh, weight, threshold;
1044 BOX *box;
1045 NUMA *naclass, *napage;
1046 NUMA *nafgt; /* fg area of all templates */
1047 NUMA *naarea; /* w * h area of all templates */
1048 JBFINDCTX *findcontext;
1049 L_DNAHASH *dahash;
1050 PIX *pix, *pix1, *pix2;
1051 PIXA *pixa, *pixa1, *pixat;
1052 PIXAA *pixaa;
1053 PTA *pta, *ptac, *ptact;
1054 l_int32 *pixcts; /* pixel counts of each pixa */
1055 l_int32 **pixrowcts; /* row-by-row pixel counts of each pixa */
1056 l_int32 x, y, rowcount, downcount, wpl;
1057 l_uint8 byte;
1058 
1059  PROCNAME("jbClassifyCorrelation");
1060 
1061  if (!classer)
1062  return ERROR_INT("classer not found", procName, 1);
1063  if (!boxa)
1064  return ERROR_INT("boxa not found", procName, 1);
1065  if (!pixas)
1066  return ERROR_INT("pixas not found", procName, 1);
1067 
1068  npages = classer->npages;
1069 
1070  /* Generate the bordered pixa, which contains all the the
1071  * input components. This will not be saved. */
1072  if ((n = pixaGetCount(pixas)) == 0) {
1073  L_WARNING("pixas is empty\n", procName);
1074  return 0;
1075  }
1076  pixa1 = pixaCreate(n);
1077  for (i = 0; i < n; i++) {
1078  pix = pixaGetPix(pixas, i, L_CLONE);
1079  pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS,
1080  JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0);
1081  pixaAddPix(pixa1, pix1, L_INSERT);
1082  pixDestroy(&pix);
1083  }
1084 
1085  /* Use these to save the class and page of each component. */
1086  naclass = classer->naclass;
1087  napage = classer->napage;
1088 
1089  /* Get the number of fg pixels in each component. */
1090  nafgt = classer->nafgt; /* holds fg areas of the templates */
1091  sumtab = makePixelSumTab8();
1092 
1093  pixcts = (l_int32 *)LEPT_CALLOC(n, sizeof(*pixcts));
1094  pixrowcts = (l_int32 **)LEPT_CALLOC(n, sizeof(*pixrowcts));
1095  centtab = makePixelCentroidTab8();
1096 
1097  /* Count the "1" pixels in each row of the pix in pixa1; this
1098  * allows pixCorrelationScoreThresholded to abort early if a match
1099  * is impossible. This loop merges three calculations: the total
1100  * number of "1" pixels, the number of "1" pixels in each row, and
1101  * the centroid. The centroids are relative to the UL corner of
1102  * each (bordered) pix. The pixrowcts[i][y] are the total number
1103  * of fg pixels in pixa[i] below row y. */
1104  pta = ptaCreate(n);
1105  for (i = 0; i < n; i++) {
1106  pix = pixaGetPix(pixa1, i, L_CLONE);
1107  pixrowcts[i] = (l_int32 *)LEPT_CALLOC(pixGetHeight(pix),
1108  sizeof(**pixrowcts));
1109  xsum = 0;
1110  ysum = 0;
1111  wpl = pixGetWpl(pix);
1112  row = pixGetData(pix) + (pixGetHeight(pix) - 1) * wpl;
1113  downcount = 0;
1114  for (y = pixGetHeight(pix) - 1; y >= 0; y--, row -= wpl) {
1115  pixrowcts[i][y] = downcount;
1116  rowcount = 0;
1117  for (x = 0; x < wpl; x++) {
1118  word = row[x];
1119  byte = word & 0xff;
1120  rowcount += sumtab[byte];
1121  xsum += centtab[byte] + (x * 32 + 24) * sumtab[byte];
1122  byte = (word >> 8) & 0xff;
1123  rowcount += sumtab[byte];
1124  xsum += centtab[byte] + (x * 32 + 16) * sumtab[byte];
1125  byte = (word >> 16) & 0xff;
1126  rowcount += sumtab[byte];
1127  xsum += centtab[byte] + (x * 32 + 8) * sumtab[byte];
1128  byte = (word >> 24) & 0xff;
1129  rowcount += sumtab[byte];
1130  xsum += centtab[byte] + x * 32 * sumtab[byte];
1131  }
1132  downcount += rowcount;
1133  ysum += rowcount * y;
1134  }
1135  pixcts[i] = downcount;
1136  if (downcount > 0) {
1137  ptaAddPt(pta,
1138  xsum / (l_float32)downcount, ysum / (l_float32)downcount);
1139  } else { /* no pixels; shouldn't happen */
1140  L_ERROR("downcount == 0 !\n", procName);
1141  ptaAddPt(pta, pixGetWidth(pix) / 2, pixGetHeight(pix) / 2);
1142  }
1143  pixDestroy(&pix);
1144  }
1145 
1146  ptac = classer->ptac; /* holds centroids of components up to this page */
1147  ptaJoin(ptac, pta, 0, -1); /* save centroids of all components */
1148  ptact = classer->ptact; /* holds centroids of templates */
1149 
1150  /* Store the unbordered pix in a pixaa, in a hierarchical
1151  * set of arrays. There is one pixa for each class,
1152  * and the pix in each pixa are all the instances found
1153  * of that class. This is actually more than one would need
1154  * for a jbig2 encoder, but there are two reasons to keep
1155  * them around: (1) the set of instances for each class
1156  * can be used to make an improved binary (or, better,
1157  * a grayscale) template, rather than simply using the first
1158  * one in the set; (2) we can investigate the failures
1159  * of the classifier. This pixaa grows as we process
1160  * successive pages. */
1161  pixaa = classer->pixaa;
1162 
1163  /* Array to store class exemplars */
1164  pixat = classer->pixat;
1165 
1166  /* Fill up the pixaa tree with the template exemplars as
1167  * the first pix in each pixa. As we add each pix,
1168  * we also add the associated box to the pixa.
1169  * We also keep track of the centroid of each pix,
1170  * and use the difference between centroids (of the
1171  * pix with the exemplar we are checking it with)
1172  * to align the two when checking that the correlation
1173  * score exceeds a threshold. The correlation score
1174  * is given by the square of the area of the AND
1175  * between aligned instance and template, divided by
1176  * the product of areas of each image. For identical
1177  * template and instance, the score is 1.0.
1178  * If the threshold is too small, non-equivalent instances
1179  * will be placed in the same class; if too large, there will
1180  * be an unnecessary division of classes representing the
1181  * same character. The weightfactor adds in some of the
1182  * difference (1.0 - thresh), depending on the heaviness
1183  * of the template (measured as the fraction of fg pixels). */
1184  thresh = classer->thresh;
1185  weight = classer->weightfactor;
1186  naarea = classer->naarea;
1187  dahash = classer->dahash;
1188  for (i = 0; i < n; i++) {
1189  pix1 = pixaGetPix(pixa1, i, L_CLONE);
1190  area1 = pixcts[i];
1191  ptaGetPt(pta, i, &x1, &y1); /* centroid for this instance */
1192  nt = pixaGetCount(pixat);
1193  found = FALSE;
1194  findcontext = findSimilarSizedTemplatesInit(classer, pix1);
1195  while ( (iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) {
1196  /* Get the template */
1197  pix2 = pixaGetPix(pixat, iclass, L_CLONE);
1198  numaGetIValue(nafgt, iclass, &area2);
1199  ptaGetPt(ptact, iclass, &x2, &y2); /* template centroid */
1200 
1201  /* Find threshold for this template */
1202  if (weight > 0.0) {
1203  numaGetIValue(naarea, iclass, &area);
1204  threshold = thresh + (1. - thresh) * weight * area2 / area;
1205  } else {
1206  threshold = thresh;
1207  }
1208 
1209  /* Find score for this template */
1210  overthreshold = pixCorrelationScoreThresholded(pix1, pix2,
1211  area1, area2, x1 - x2, y1 - y2,
1212  MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT,
1213  sumtab, pixrowcts[i], threshold);
1214 #if DEBUG_CORRELATION_SCORE
1215  {
1216  l_float32 score, testscore;
1217  l_int32 count, testcount;
1218  pixCorrelationScore(pix1, pix2, area1, area2, x1 - x2, y1 - y2,
1219  MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT,
1220  sumtab, &score);
1221 
1222  pixCorrelationScoreSimple(pix1, pix2, area1, area2,
1223  x1 - x2, y1 - y2, MAX_DIFF_WIDTH,
1224  MAX_DIFF_HEIGHT, sumtab, &testscore);
1225  count = (l_int32)rint(sqrt(score * area1 * area2));
1226  testcount = (l_int32)rint(sqrt(testscore * area1 * area2));
1227  if ((score >= threshold) != (testscore >= threshold)) {
1228  lept_stderr("Correlation score mismatch: "
1229  "%d(%g,%d) vs %d(%g,%d) (%g)\n",
1230  count, score, score >= threshold,
1231  testcount, testscore, testscore >= threshold,
1232  score - testscore);
1233  }
1234 
1235  if ((score >= threshold) != overthreshold) {
1236  lept_stderr("Mismatch between correlation/threshold "
1237  "comparison: %g(%g,%d) >= %g(%g) vs %s\n",
1238  score, score*area1*area2, count, threshold,
1239  threshold*area1*area2,
1240  (overthreshold ? "true" : "false"));
1241  }
1242  }
1243 #endif /* DEBUG_CORRELATION_SCORE */
1244  pixDestroy(&pix2);
1245 
1246  if (overthreshold) { /* greedy match */
1247  found = TRUE;
1248  numaAddNumber(naclass, iclass);
1249  numaAddNumber(napage, npages);
1250  if (classer->keep_pixaa) {
1251  /* We are keeping a record of all components */
1252  pixa = pixaaGetPixa(pixaa, iclass, L_CLONE);
1253  pix = pixaGetPix(pixas, i, L_CLONE);
1254  pixaAddPix(pixa, pix, L_INSERT);
1255  box = boxaGetBox(boxa, i, L_CLONE);
1256  pixaAddBox(pixa, box, L_INSERT);
1257  pixaDestroy(&pixa);
1258  }
1259  break;
1260  }
1261  }
1262  findSimilarSizedTemplatesDestroy(&findcontext);
1263  if (found == FALSE) { /* new class */
1264  numaAddNumber(naclass, nt);
1265  numaAddNumber(napage, npages);
1266  pixa = pixaCreate(0);
1267  pix = pixaGetPix(pixas, i, L_CLONE); /* unbordered instance */
1268  pixaAddPix(pixa, pix, L_INSERT);
1269  wt = pixGetWidth(pix);
1270  ht = pixGetHeight(pix);
1271  l_dnaHashAdd(dahash, (l_uint64)ht * wt, nt);
1272  box = boxaGetBox(boxa, i, L_CLONE);
1273  pixaAddBox(pixa, box, L_INSERT);
1274  pixaaAddPixa(pixaa, pixa, L_INSERT); /* unbordered instance */
1275  ptaAddPt(ptact, x1, y1);
1276  numaAddNumber(nafgt, area1);
1277  pixaAddPix(pixat, pix1, L_INSERT); /* bordered template */
1278  area = (pixGetWidth(pix1) - 2 * JB_ADDED_PIXELS) *
1279  (pixGetHeight(pix1) - 2 * JB_ADDED_PIXELS);
1280  numaAddNumber(naarea, area);
1281  } else { /* don't save it */
1282  pixDestroy(&pix1);
1283  }
1284  }
1285  classer->nclass = pixaGetCount(pixat);
1286 
1287  LEPT_FREE(pixcts);
1288  LEPT_FREE(centtab);
1289  for (i = 0; i < n; i++) {
1290  LEPT_FREE(pixrowcts[i]);
1291  }
1292  LEPT_FREE(pixrowcts);
1293 
1294  LEPT_FREE(sumtab);
1295  ptaDestroy(&pta);
1296  pixaDestroy(&pixa1);
1297  return 0;
1298 }
1299 
1300 
1301 /*----------------------------------------------------------------------*
1302  * Determine the image components we start with *
1303  *----------------------------------------------------------------------*/
1315 l_ok
1316 jbGetComponents(PIX *pixs,
1317  l_int32 components,
1318  l_int32 maxwidth,
1319  l_int32 maxheight,
1320  BOXA **pboxad,
1321  PIXA **ppixad)
1322 {
1323 l_int32 empty, res, redfactor;
1324 BOXA *boxa;
1325 PIX *pix1, *pix2, *pix3;
1326 PIXA *pixa, *pixat;
1327 
1328  PROCNAME("jbGetComponents");
1329 
1330  if (!pboxad)
1331  return ERROR_INT("&boxad not defined", procName, 1);
1332  *pboxad = NULL;
1333  if (!ppixad)
1334  return ERROR_INT("&pixad not defined", procName, 1);
1335  *ppixad = NULL;
1336  if (!pixs)
1337  return ERROR_INT("pixs not defined", procName, 1);
1338  if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
1339  components != JB_WORDS)
1340  return ERROR_INT("invalid components", procName, 1);
1341 
1342  pixZero(pixs, &empty);
1343  if (empty) {
1344  *pboxad = boxaCreate(0);
1345  *ppixad = pixaCreate(0);
1346  return 0;
1347  }
1348 
1349  /* If required, preprocess input pixs. The method for both
1350  * characters and words is to generate a connected component
1351  * mask over the units that we want to aggregrate, which are,
1352  * in general, sets of related connected components in pixs.
1353  * For characters, we want to include the dots with
1354  * 'i', 'j' and '!', so we do a small vertical closing to
1355  * generate the mask. For words, we make a mask over all
1356  * characters in each word. This is a bit more tricky, because
1357  * the spacing between words is difficult to predict a priori,
1358  * and words can be typeset with variable spacing that can
1359  * in some cases be barely larger than the space between
1360  * characters. The first step is to generate the mask and
1361  * identify each of its connected components. */
1362  if (components == JB_CONN_COMPS) { /* no preprocessing */
1363  boxa = pixConnComp(pixs, &pixa, 8);
1364  } else if (components == JB_CHARACTERS) {
1365  pix1 = pixMorphSequence(pixs, "c1.6", 0);
1366  boxa = pixConnComp(pix1, &pixat, 8);
1367  pixa = pixaClipToPix(pixat, pixs);
1368  pixDestroy(&pix1);
1369  pixaDestroy(&pixat);
1370  } else { /* components == JB_WORDS */
1371 
1372  /* Do the operations at about 150 ppi resolution.
1373  * It is much faster at 75 ppi, but the results are
1374  * more accurate at 150 ppi. This will segment the
1375  * words in body text. It can be expected that relatively
1376  * infrequent words in a larger font will be split. */
1377  res = pixGetXRes(pixs);
1378  if (res <= 200) {
1379  redfactor = 1;
1380  pix1 = pixClone(pixs);
1381  } else if (res <= 400) {
1382  redfactor = 2;
1383  pix1 = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0);
1384  } else {
1385  redfactor = 4;
1386  pix1 = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0);
1387  }
1388 
1389  /* Estimate the word mask, at approximately 150 ppi.
1390  * This has both very large and very small components left in. */
1391  pixWordMaskByDilation(pix1, &pix2, NULL, NULL);
1392 
1393  /* Expand the optimally dilated word mask to full res. */
1394  pix3 = pixExpandReplicate(pix2, redfactor);
1395 
1396  /* Pull out the pixels in pixs corresponding to the mask
1397  * components in pix3. Note that above we used threshold
1398  * levels in the reduction of 1 to insure that the resulting
1399  * mask fully covers the input pixs. The downside of using
1400  * a threshold of 1 is that very close characters from adjacent
1401  * lines can be joined. But with a level of 2 or greater,
1402  * it is necessary to use a seedfill, followed by a pixOr():
1403  * pixt4 = pixSeedfillBinary(NULL, pix3, pixs, 8);
1404  * pixOr(pix3, pix3, pixt4);
1405  * to insure that the mask coverage is complete over pixs. */
1406  boxa = pixConnComp(pix3, &pixat, 4);
1407  pixa = pixaClipToPix(pixat, pixs);
1408  pixaDestroy(&pixat);
1409  pixDestroy(&pix1);
1410  pixDestroy(&pix2);
1411  pixDestroy(&pix3);
1412  }
1413 
1414  /* Remove large components, and save the results. */
1415  *ppixad = pixaSelectBySize(pixa, maxwidth, maxheight, L_SELECT_IF_BOTH,
1416  L_SELECT_IF_LTE, NULL);
1417  *pboxad = boxaSelectBySize(boxa, maxwidth, maxheight, L_SELECT_IF_BOTH,
1418  L_SELECT_IF_LTE, NULL);
1419  pixaDestroy(&pixa);
1420  boxaDestroy(&boxa);
1421 
1422  return 0;
1423 }
1424 
1425 
1457 l_ok
1458 pixWordMaskByDilation(PIX *pixs,
1459  PIX **ppixm,
1460  l_int32 *psize,
1461  PIXA *pixadb)
1462 {
1463 l_int32 i, n, ndil, maxdiff, diff, ibest;
1464 l_int32 check, count, total, xres;
1465 l_int32 ncc[13]; /* max dilation + 1 */
1466 l_int32 *diffa;
1467 BOXA *boxa;
1468 NUMA *nacc, *nadiff;
1469 PIX *pix1, *pix2;
1470 
1471  PROCNAME("pixWordMaskByDilation");
1472 
1473  if (ppixm) *ppixm = NULL;
1474  if (psize) *psize = 0;
1475  if (!pixs || pixGetDepth(pixs) != 1)
1476  return ERROR_INT("pixs undefined or not 1 bpp", procName, 1);
1477  if (!ppixm && !psize)
1478  return ERROR_INT("no output requested", procName, 1);
1479 
1480  /* Find a good dilation to create the word mask, by successively
1481  * increasing dilation size and counting the connected components. */
1482  pix1 = pixCopy(NULL, pixs);
1483  ndil = 12; /* appropriate for 75 to 150 ppi */
1484  nacc = numaCreate(ndil + 1);
1485  nadiff = numaCreate(ndil + 1);
1486  for (i = 0; i <= ndil; i++) {
1487  if (i == 0) /* first one not dilated */
1488  pix2 = pixCopy(NULL, pix1);
1489  else /* successive dilation by sel_2h */
1490  pix2 = pixMorphSequence(pix1, "d2.1", 0);
1491  boxa = pixConnCompBB(pix2, 4);
1492  ncc[i] = boxaGetCount(boxa);
1493  numaAddNumber(nacc, ncc[i]);
1494  if (i == 0) total = ncc[0];
1495  if (i > 0) {
1496  diff = ncc[i - 1] - ncc[i];
1497  numaAddNumber(nadiff, diff);
1498  }
1499  pixDestroy(&pix1);
1500  pix1 = pix2;
1501  boxaDestroy(&boxa);
1502  }
1503  pixDestroy(&pix1);
1504 
1505  /* Find the dilation at which the c.c. count has reduced
1506  * to 30% of the initial value. Although 30% seems high,
1507  * it seems better to use this but add one to ibest. */
1508  diffa = numaGetIArray(nadiff);
1509  n = numaGetCount(nadiff);
1510  maxdiff = 0;
1511  check = TRUE;
1512  ibest = 2;
1513  for (i = 1; i < n; i++) {
1514  numaGetIValue(nacc, i, &count);
1515  if (check && count < 0.3 * total) {
1516  ibest = i + 1;
1517  check = FALSE;
1518  }
1519  diff = diffa[i];
1520  if (diff > maxdiff)
1521  maxdiff = diff;
1522  }
1523  LEPT_FREE(diffa);
1524 
1525  /* Add small compensation for higher resolution */
1526  xres = pixGetXRes(pixs);
1527  if (xres == 0) xres = 150;
1528  if (xres > 110) ibest++;
1529  if (ibest < 2) {
1530  L_INFO("setting ibest to minimum allowed value of 2\n", procName);
1531  ibest = 2;
1532  }
1533 
1534  if (pixadb) {
1535  lept_mkdir("lept/jb");
1536  {NUMA *naseq;
1537  PIX *pix3, *pix4;
1538  L_INFO("Best dilation: %d\n", procName, L_MAX(3, ibest + 1));
1539  naseq = numaMakeSequence(1, 1, numaGetCount(nacc));
1540  pix3 = gplotGeneralPix2(naseq, nacc, GPLOT_LINES,
1541  "/tmp/lept/jb/numcc",
1542  "Number of cc vs. horizontal dilation",
1543  "Sel horiz", "Number of cc");
1544  pixaAddPix(pixadb, pix3, L_INSERT);
1545  numaDestroy(&naseq);
1546  naseq = numaMakeSequence(1, 1, numaGetCount(nadiff));
1547  pix3 = gplotGeneralPix2(naseq, nadiff, GPLOT_LINES,
1548  "/tmp/lept/jb/diffcc",
1549  "Diff count of cc vs. horizontal dilation",
1550  "Sel horiz", "Diff in cc");
1551  pixaAddPix(pixadb, pix3, L_INSERT);
1552  numaDestroy(&naseq);
1553  pix3 = pixCloseBrick(NULL, pixs, ibest + 1, 1);
1554  pix4 = pixScaleToSize(pix3, 600, 0);
1555  pixaAddPix(pixadb, pix4, L_INSERT);
1556  pixDestroy(&pix3);
1557  }
1558  }
1559 
1560  if (psize) *psize = ibest + 1;
1561  if (ppixm)
1562  *ppixm = pixCloseBrick(NULL, pixs, ibest + 1, 1);
1563 
1564  numaDestroy(&nacc);
1565  numaDestroy(&nadiff);
1566  return 0;
1567 }
1568 
1569 
1589 l_ok
1590 pixWordBoxesByDilation(PIX *pixs,
1591  l_int32 minwidth,
1592  l_int32 minheight,
1593  l_int32 maxwidth,
1594  l_int32 maxheight,
1595  BOXA **pboxa,
1596  l_int32 *psize,
1597  PIXA *pixadb)
1598 {
1599 BOXA *boxa1, *boxa2;
1600 PIX *pix1, *pix2;
1601 
1602  PROCNAME("pixWordBoxesByDilation");
1603 
1604  if (psize) *psize = 0;
1605  if (!pixs || pixGetDepth(pixs) != 1)
1606  return ERROR_INT("pixs undefined or not 1 bpp", procName, 1);
1607  if (!pboxa)
1608  return ERROR_INT("&boxa not defined", procName, 1);
1609  *pboxa = NULL;
1610 
1611  /* Make a first estimate of the word mask */
1612  if (pixWordMaskByDilation(pixs, &pix1, psize, pixadb))
1613  return ERROR_INT("pixWordMaskByDilation() failed", procName, 1);
1614 
1615  /* Prune the word mask. Get the bounding boxes of the words.
1616  * Remove the small ones, which can be due to punctuation
1617  * that was not joined to a word. Also remove the large ones,
1618  * which are not likely to be words. */
1619  boxa1 = pixConnComp(pix1, NULL, 8);
1620  boxa2 = boxaSelectBySize(boxa1, minwidth, minheight, L_SELECT_IF_BOTH,
1621  L_SELECT_IF_GTE, NULL);
1622  *pboxa = boxaSelectBySize(boxa2, maxwidth, maxheight, L_SELECT_IF_BOTH,
1623  L_SELECT_IF_LTE, NULL);
1624  if (pixadb) {
1625  pix2 = pixUnpackBinary(pixs, 32, 1);
1626  pixRenderBoxaArb(pix2, boxa1, 2, 255, 0, 0);
1627  pixaAddPix(pixadb, pix2, L_INSERT);
1628  pix2 = pixUnpackBinary(pixs, 32, 1);
1629  pixRenderBoxaArb(pix2, boxa2, 2, 0, 255, 0);
1630  pixaAddPix(pixadb, pix2, L_INSERT);
1631  }
1632  boxaDestroy(&boxa1);
1633  boxaDestroy(&boxa2);
1634  pixDestroy(&pix1);
1635  return 0;
1636 }
1637 
1638 
1639 /*----------------------------------------------------------------------*
1640  * Build grayscale composites (templates) *
1641  *----------------------------------------------------------------------*/
1651 PIXA *
1652 jbAccumulateComposites(PIXAA *pixaa,
1653  NUMA **pna,
1654  PTA **pptat)
1655 {
1656 l_int32 n, nt, i, j, d, minw, maxw, minh, maxh, xdiff, ydiff;
1657 l_float32 x, y, xave, yave;
1658 NUMA *na;
1659 PIX *pix, *pixt1, *pixt2, *pixsum;
1660 PIXA *pixa, *pixad;
1661 PTA *ptat, *pta;
1662 
1663  PROCNAME("jbAccumulateComposites");
1664 
1665  if (!pptat)
1666  return (PIXA *)ERROR_PTR("&ptat not defined", procName, NULL);
1667  *pptat = NULL;
1668  if (!pna)
1669  return (PIXA *)ERROR_PTR("&na not defined", procName, NULL);
1670  *pna = NULL;
1671  if (!pixaa)
1672  return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL);
1673 
1674  n = pixaaGetCount(pixaa, NULL);
1675  if ((ptat = ptaCreate(n)) == NULL)
1676  return (PIXA *)ERROR_PTR("ptat not made", procName, NULL);
1677  *pptat = ptat;
1678  pixad = pixaCreate(n);
1679  na = numaCreate(n);
1680  *pna = na;
1681 
1682  for (i = 0; i < n; i++) {
1683  pixa = pixaaGetPixa(pixaa, i, L_CLONE);
1684  nt = pixaGetCount(pixa);
1685  numaAddNumber(na, nt);
1686  if (nt == 0) {
1687  L_WARNING("empty pixa found!\n", procName);
1688  pixaDestroy(&pixa);
1689  continue;
1690  }
1691  pixaSizeRange(pixa, &minw, &minh, &maxw, &maxh);
1692  pix = pixaGetPix(pixa, 0, L_CLONE);
1693  d = pixGetDepth(pix);
1694  pixDestroy(&pix);
1695  pixt1 = pixCreate(maxw, maxh, d);
1696  pixsum = pixInitAccumulate(maxw, maxh, 0);
1697  pta = pixaCentroids(pixa);
1698 
1699  /* Find the average value of the centroids ... */
1700  xave = yave = 0;
1701  for (j = 0; j < nt; j++) {
1702  ptaGetPt(pta, j, &x, &y);
1703  xave += x;
1704  yave += y;
1705  }
1706  xave = xave / (l_float32)nt;
1707  yave = yave / (l_float32)nt;
1708 
1709  /* and place all centroids at their average value */
1710  for (j = 0; j < nt; j++) {
1711  pixt2 = pixaGetPix(pixa, j, L_CLONE);
1712  ptaGetPt(pta, j, &x, &y);
1713  xdiff = (l_int32)(x - xave);
1714  ydiff = (l_int32)(y - yave);
1715  pixClearAll(pixt1);
1716  pixRasterop(pixt1, xdiff, ydiff, maxw, maxh, PIX_SRC,
1717  pixt2, 0, 0);
1718  pixAccumulate(pixsum, pixt1, L_ARITH_ADD);
1719  pixDestroy(&pixt2);
1720  }
1721  pixaAddPix(pixad, pixsum, L_INSERT);
1722  ptaAddPt(ptat, xave, yave);
1723 
1724  pixaDestroy(&pixa);
1725  pixDestroy(&pixt1);
1726  ptaDestroy(&pta);
1727  }
1728 
1729  return pixad;
1730 }
1731 
1732 
1741 PIXA *
1742 jbTemplatesFromComposites(PIXA *pixac,
1743  NUMA *na)
1744 {
1745 l_int32 n, i;
1746 l_float32 nt; /* number of samples in the composite; always an integer */
1747 l_float32 factor;
1748 PIX *pixsum; /* accumulated composite */
1749 PIX *pixd;
1750 PIXA *pixad;
1751 
1752  PROCNAME("jbTemplatesFromComposites");
1753 
1754  if (!pixac)
1755  return (PIXA *)ERROR_PTR("pixac not defined", procName, NULL);
1756  if (!na)
1757  return (PIXA *)ERROR_PTR("na not defined", procName, NULL);
1758 
1759  n = pixaGetCount(pixac);
1760  pixad = pixaCreate(n);
1761  for (i = 0; i < n; i++) {
1762  pixsum = pixaGetPix(pixac, i, L_COPY); /* changed internally */
1763  numaGetFValue(na, i, &nt);
1764  factor = 255. / nt;
1765  pixMultConstAccumulate(pixsum, factor, 0); /* changes pixsum */
1766  pixd = pixFinalAccumulate(pixsum, 0, 8);
1767  pixaAddPix(pixad, pixd, L_INSERT);
1768  pixDestroy(&pixsum);
1769  }
1770 
1771  return pixad;
1772 }
1773 
1774 
1775 
1776 /*----------------------------------------------------------------------*
1777  * jbig2 utility routines *
1778  *----------------------------------------------------------------------*/
1786 JBCLASSER *
1787 jbClasserCreate(l_int32 method,
1788  l_int32 components)
1789 {
1790 JBCLASSER *classer;
1791 
1792  PROCNAME("jbClasserCreate");
1793 
1794  if (method != JB_RANKHAUS && method != JB_CORRELATION)
1795  return (JBCLASSER *)ERROR_PTR("invalid method", procName, NULL);
1796  if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
1797  components != JB_WORDS)
1798  return (JBCLASSER *)ERROR_PTR("invalid component", procName, NULL);
1799 
1800  classer = (JBCLASSER *)LEPT_CALLOC(1, sizeof(JBCLASSER));
1801  classer->method = method;
1802  classer->components = components;
1803  classer->nacomps = numaCreate(0);
1804  classer->pixaa = pixaaCreate(0);
1805  classer->pixat = pixaCreate(0);
1806  classer->pixatd = pixaCreate(0);
1807  classer->nafgt = numaCreate(0);
1808  classer->naarea = numaCreate(0);
1809  classer->ptac = ptaCreate(0);
1810  classer->ptact = ptaCreate(0);
1811  classer->naclass = numaCreate(0);
1812  classer->napage = numaCreate(0);
1813  classer->ptaul = ptaCreate(0);
1814  return classer;
1815 }
1816 
1817 
1818 /*
1819  * \brief jbClasserDestroy()
1820  *
1821  * \param[in,out] pclasser will be set to null before returning
1822  * \return void
1823  */
1824 void
1825 jbClasserDestroy(JBCLASSER **pclasser)
1826 {
1827 JBCLASSER *classer;
1828 
1829  if (!pclasser)
1830  return;
1831  if ((classer = *pclasser) == NULL)
1832  return;
1833 
1834  sarrayDestroy(&classer->safiles);
1835  numaDestroy(&classer->nacomps);
1836  pixaaDestroy(&classer->pixaa);
1837  pixaDestroy(&classer->pixat);
1838  pixaDestroy(&classer->pixatd);
1839  l_dnaHashDestroy(&classer->dahash);
1840  numaDestroy(&classer->nafgt);
1841  numaDestroy(&classer->naarea);
1842  ptaDestroy(&classer->ptac);
1843  ptaDestroy(&classer->ptact);
1844  numaDestroy(&classer->naclass);
1845  numaDestroy(&classer->napage);
1846  ptaDestroy(&classer->ptaul);
1847  ptaDestroy(&classer->ptall);
1848  LEPT_FREE(classer);
1849  *pclasser = NULL;
1850 }
1851 
1852 
1873 JBDATA *
1874 jbDataSave(JBCLASSER *classer)
1875 {
1876 l_int32 maxw, maxh;
1877 JBDATA *data;
1878 PIX *pix;
1879 
1880  PROCNAME("jbDataSave");
1881 
1882  if (!classer)
1883  return (JBDATA *)ERROR_PTR("classer not defined", procName, NULL);
1884 
1885  /* Write the templates into an array. */
1886  pixaSizeRange(classer->pixat, NULL, NULL, &maxw, &maxh);
1887  pix = pixaDisplayOnLattice(classer->pixat, maxw + 1, maxh + 1,
1888  NULL, NULL);
1889  if (!pix)
1890  return (JBDATA *)ERROR_PTR("data not made", procName, NULL);
1891 
1892  data = (JBDATA *)LEPT_CALLOC(1, sizeof(JBDATA));
1893  data->pix = pix;
1894  data->npages = classer->npages;
1895  data->w = classer->w;
1896  data->h = classer->h;
1897  data->nclass = classer->nclass;
1898  data->latticew = maxw + 1;
1899  data->latticeh = maxh + 1;
1900  data->naclass = numaClone(classer->naclass);
1901  data->napage = numaClone(classer->napage);
1902  data->ptaul = ptaClone(classer->ptaul);
1903  return data;
1904 }
1905 
1906 
1907 /*
1908  * \brief jbDataDestroy()
1909  *
1910  * \param[in,out] pdata will be set to null before returning
1911  * \return void
1912  */
1913 void
1914 jbDataDestroy(JBDATA **pdata)
1915 {
1916 JBDATA *data;
1917 
1918  if (!pdata)
1919  return;
1920  if ((data = *pdata) == NULL)
1921  return;
1922 
1923  pixDestroy(&data->pix);
1924  numaDestroy(&data->naclass);
1925  numaDestroy(&data->napage);
1926  ptaDestroy(&data->ptaul);
1927  LEPT_FREE(data);
1928  *pdata = NULL;
1929 }
1930 
1931 
1944 l_ok
1945 jbDataWrite(const char *rootout,
1946  JBDATA *jbdata)
1947 {
1948 char buf[L_BUF_SIZE];
1949 l_int32 w, h, nclass, npages, cellw, cellh, ncomp, i, x, y, iclass, ipage;
1950 NUMA *naclass, *napage;
1951 PTA *ptaul;
1952 PIX *pixt;
1953 FILE *fp;
1954 
1955  PROCNAME("jbDataWrite");
1956 
1957  if (!rootout)
1958  return ERROR_INT("no rootout", procName, 1);
1959  if (!jbdata)
1960  return ERROR_INT("no jbdata", procName, 1);
1961 
1962  npages = jbdata->npages;
1963  w = jbdata->w;
1964  h = jbdata->h;
1965  pixt = jbdata->pix;
1966  nclass = jbdata->nclass;
1967  cellw = jbdata->latticew;
1968  cellh = jbdata->latticeh;
1969  naclass = jbdata->naclass;
1970  napage = jbdata->napage;
1971  ptaul = jbdata->ptaul;
1972 
1973  snprintf(buf, L_BUF_SIZE, "%s%s", rootout, JB_TEMPLATE_EXT);
1974  pixWrite(buf, pixt, IFF_PNG);
1975 
1976  snprintf(buf, L_BUF_SIZE, "%s%s", rootout, JB_DATA_EXT);
1977  if ((fp = fopenWriteStream(buf, "wb")) == NULL)
1978  return ERROR_INT("stream not opened", procName, 1);
1979  ncomp = ptaGetCount(ptaul);
1980  fprintf(fp, "jb data file\n");
1981  fprintf(fp, "num pages = %d\n", npages);
1982  fprintf(fp, "page size: w = %d, h = %d\n", w, h);
1983  fprintf(fp, "num components = %d\n", ncomp);
1984  fprintf(fp, "num classes = %d\n", nclass);
1985  fprintf(fp, "template lattice size: w = %d, h = %d\n", cellw, cellh);
1986  for (i = 0; i < ncomp; i++) {
1987  numaGetIValue(napage, i, &ipage);
1988  numaGetIValue(naclass, i, &iclass);
1989  ptaGetIPt(ptaul, i, &x, &y);
1990  fprintf(fp, "%d %d %d %d\n", ipage, iclass, x, y);
1991  }
1992  fclose(fp);
1993 
1994  return 0;
1995 }
1996 
1997 
2004 JBDATA *
2005 jbDataRead(const char *rootname)
2006 {
2007 char fname[L_BUF_SIZE];
2008 char *linestr;
2009 l_uint8 *data;
2010 l_int32 nsa, i, w, h, cellw, cellh, x, y, iclass, ipage;
2011 l_int32 npages, nclass, ncomp, ninit;
2012 size_t size;
2013 JBDATA *jbdata;
2014 NUMA *naclass, *napage;
2015 PIX *pixs;
2016 PTA *ptaul;
2017 SARRAY *sa;
2018 
2019  PROCNAME("jbDataRead");
2020 
2021  if (!rootname)
2022  return (JBDATA *)ERROR_PTR("rootname not defined", procName, NULL);
2023 
2024  snprintf(fname, L_BUF_SIZE, "%s%s", rootname, JB_TEMPLATE_EXT);
2025  if ((pixs = pixRead(fname)) == NULL)
2026  return (JBDATA *)ERROR_PTR("pix not read", procName, NULL);
2027 
2028  snprintf(fname, L_BUF_SIZE, "%s%s", rootname, JB_DATA_EXT);
2029  if ((data = l_binaryRead(fname, &size)) == NULL) {
2030  pixDestroy(&pixs);
2031  return (JBDATA *)ERROR_PTR("data not read", procName, NULL);
2032  }
2033 
2034  if ((sa = sarrayCreateLinesFromString((char *)data, 0)) == NULL) {
2035  pixDestroy(&pixs);
2036  LEPT_FREE(data);
2037  return (JBDATA *)ERROR_PTR("sa not made", procName, NULL);
2038  }
2039  nsa = sarrayGetCount(sa); /* number of cc + 6 */
2040  linestr = sarrayGetString(sa, 0, L_NOCOPY);
2041  if (strcmp(linestr, "jb data file") != 0) {
2042  pixDestroy(&pixs);
2043  LEPT_FREE(data);
2044  sarrayDestroy(&sa);
2045  return (JBDATA *)ERROR_PTR("invalid jb data file", procName, NULL);
2046  }
2047  linestr = sarrayGetString(sa, 1, L_NOCOPY);
2048  sscanf(linestr, "num pages = %d", &npages);
2049  linestr = sarrayGetString(sa, 2, L_NOCOPY);
2050  sscanf(linestr, "page size: w = %d, h = %d", &w, &h);
2051  linestr = sarrayGetString(sa, 3, L_NOCOPY);
2052  sscanf(linestr, "num components = %d", &ncomp);
2053  linestr = sarrayGetString(sa, 4, L_NOCOPY);
2054  sscanf(linestr, "num classes = %d\n", &nclass);
2055  linestr = sarrayGetString(sa, 5, L_NOCOPY);
2056  sscanf(linestr, "template lattice size: w = %d, h = %d\n", &cellw, &cellh);
2057 
2058 #if 1
2059  lept_stderr("num pages = %d\n", npages);
2060  lept_stderr("page size: w = %d, h = %d\n", w, h);
2061  lept_stderr("num components = %d\n", ncomp);
2062  lept_stderr("num classes = %d\n", nclass);
2063  lept_stderr("template lattice size: w = %d, h = %d\n", cellw, cellh);
2064 #endif
2065 
2066  ninit = ncomp;
2067  if (ncomp > 1000000) { /* fuzz protection */
2068  L_WARNING("ncomp > 1M\n", procName);
2069  ninit = 1000000;
2070  }
2071  naclass = numaCreate(ninit);
2072  napage = numaCreate(ninit);
2073  ptaul = ptaCreate(ninit);
2074  for (i = 6; i < nsa; i++) {
2075  linestr = sarrayGetString(sa, i, L_NOCOPY);
2076  sscanf(linestr, "%d %d %d %d\n", &ipage, &iclass, &x, &y);
2077  numaAddNumber(napage, ipage);
2078  numaAddNumber(naclass, iclass);
2079  ptaAddPt(ptaul, x, y);
2080  }
2081 
2082  jbdata = (JBDATA *)LEPT_CALLOC(1, sizeof(JBDATA));
2083  jbdata->pix = pixs;
2084  jbdata->npages = npages;
2085  jbdata->w = w;
2086  jbdata->h = h;
2087  jbdata->nclass = nclass;
2088  jbdata->latticew = cellw;
2089  jbdata->latticeh = cellh;
2090  jbdata->naclass = naclass;
2091  jbdata->napage = napage;
2092  jbdata->ptaul = ptaul;
2093 
2094  LEPT_FREE(data);
2095  sarrayDestroy(&sa);
2096  return jbdata;
2097 }
2098 
2099 
2109 PIXA *
2110 jbDataRender(JBDATA *data,
2111  l_int32 debugflag)
2112 {
2113 l_int32 i, w, h, cellw, cellh, x, y, iclass, ipage;
2114 l_int32 npages, nclass, ncomp, wp, hp;
2115 BOX *box;
2116 NUMA *naclass, *napage;
2117 PIX *pixt, *pixt2, *pix, *pixd;
2118 PIXA *pixat; /* pixa of templates */
2119 PIXA *pixad; /* pixa of output images */
2120 PIXCMAP *cmap;
2121 PTA *ptaul;
2122 
2123  PROCNAME("jbDataRender");
2124 
2125  if (!data)
2126  return (PIXA *)ERROR_PTR("data not defined", procName, NULL);
2127 
2128  npages = data->npages;
2129  w = data->w;
2130  h = data->h;
2131  pixt = data->pix;
2132  nclass = data->nclass;
2133  cellw = data->latticew;
2134  cellh = data->latticeh;
2135  naclass = data->naclass;
2136  napage = data->napage;
2137  ptaul = data->ptaul;
2138  ncomp = numaGetCount(naclass);
2139 
2140  /* Reconstruct the original set of images from the templates
2141  * and the data associated with each component. First,
2142  * generate the output pixa as a set of empty pix. */
2143  if ((pixad = pixaCreate(npages)) == NULL)
2144  return (PIXA *)ERROR_PTR("pixad not made", procName, NULL);
2145  for (i = 0; i < npages; i++) {
2146  if (debugflag == FALSE) {
2147  pix = pixCreate(w, h, 1);
2148  } else {
2149  pix = pixCreate(w, h, 2);
2150  cmap = pixcmapCreate(2);
2151  pixcmapAddColor(cmap, 255, 255, 255);
2152  pixcmapAddColor(cmap, 0, 0, 0);
2153  pixcmapAddColor(cmap, 255, 0, 0); /* for box outlines */
2154  pixSetColormap(pix, cmap);
2155  }
2156  pixaAddPix(pixad, pix, L_INSERT);
2157  }
2158 
2159  /* Put the class templates into a pixa. */
2160  if ((pixat = pixaCreateFromPix(pixt, nclass, cellw, cellh)) == NULL) {
2161  pixaDestroy(&pixad);
2162  return (PIXA *)ERROR_PTR("pixat not made", procName, NULL);
2163  }
2164 
2165  /* Place each component in the right location on its page. */
2166  for (i = 0; i < ncomp; i++) {
2167  numaGetIValue(napage, i, &ipage);
2168  numaGetIValue(naclass, i, &iclass);
2169  pix = pixaGetPix(pixat, iclass, L_CLONE); /* the template */
2170  wp = pixGetWidth(pix);
2171  hp = pixGetHeight(pix);
2172  ptaGetIPt(ptaul, i, &x, &y);
2173  pixd = pixaGetPix(pixad, ipage, L_CLONE); /* the output page */
2174  if (debugflag == FALSE) {
2175  pixRasterop(pixd, x, y, wp, hp, PIX_SRC | PIX_DST, pix, 0, 0);
2176  } else {
2177  pixt2 = pixConvert1To2Cmap(pix);
2178  pixRasterop(pixd, x, y, wp, hp, PIX_SRC | PIX_DST, pixt2, 0, 0);
2179  box = boxCreate(x, y, wp, hp);
2180  pixRenderBoxArb(pixd, box, 1, 255, 0, 0);
2181  pixDestroy(&pixt2);
2182  boxDestroy(&box);
2183  }
2184  pixDestroy(&pix); /* the clone only */
2185  pixDestroy(&pixd); /* the clone only */
2186  }
2187 
2188  pixaDestroy(&pixat);
2189  return pixad;
2190 }
2191 
2192 
2218 l_ok
2219 jbGetULCorners(JBCLASSER *classer,
2220  PIX *pixs,
2221  BOXA *boxa)
2222 {
2223 l_int32 i, baseindex, index, n, iclass, idelx, idely, x, y, dx, dy;
2224 l_int32 *sumtab;
2225 l_float32 x1, x2, y1, y2, delx, dely;
2226 BOX *box;
2227 NUMA *naclass;
2228 PIX *pixt;
2229 PTA *ptac, *ptact, *ptaul;
2230 
2231  PROCNAME("jbGetULCorners");
2232 
2233  if (!classer)
2234  return ERROR_INT("classer not defined", procName, 1);
2235  if (!pixs)
2236  return ERROR_INT("pixs not defined", procName, 1);
2237  if (!boxa)
2238  return ERROR_INT("boxa not defined", procName, 1);
2239 
2240  n = boxaGetCount(boxa);
2241  ptaul = classer->ptaul;
2242  naclass = classer->naclass;
2243  ptac = classer->ptac;
2244  ptact = classer->ptact;
2245  baseindex = classer->baseindex; /* num components before this page */
2246  sumtab = makePixelSumTab8();
2247  for (i = 0; i < n; i++) {
2248  index = baseindex + i;
2249  ptaGetPt(ptac, index, &x1, &y1);
2250  numaGetIValue(naclass, index, &iclass);
2251  ptaGetPt(ptact, iclass, &x2, &y2);
2252  delx = x2 - x1;
2253  dely = y2 - y1;
2254  if (delx >= 0)
2255  idelx = (l_int32)(delx + 0.5);
2256  else
2257  idelx = (l_int32)(delx - 0.5);
2258  if (dely >= 0)
2259  idely = (l_int32)(dely + 0.5);
2260  else
2261  idely = (l_int32)(dely - 0.5);
2262  if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL) {
2263  LEPT_FREE(sumtab);
2264  return ERROR_INT("box not found", procName, 1);
2265  }
2266  boxGetGeometry(box, &x, &y, NULL, NULL);
2267 
2268  /* Get final increments dx and dy for best alignment */
2269  pixt = pixaGetPix(classer->pixat, iclass, L_CLONE);
2270  finalPositioningForAlignment(pixs, x, y, idelx, idely,
2271  pixt, sumtab, &dx, &dy);
2272 /* if (i % 20 == 0)
2273  lept_stderr("dx = %d, dy = %d\n", dx, dy); */
2274  ptaAddPt(ptaul, x - idelx + dx, y - idely + dy);
2275  boxDestroy(&box);
2276  pixDestroy(&pixt);
2277  }
2278 
2279  LEPT_FREE(sumtab);
2280  return 0;
2281 }
2282 
2283 
2310 l_ok
2311 jbGetLLCorners(JBCLASSER *classer)
2312 {
2313 l_int32 i, iclass, n, x1, y1, h;
2314 NUMA *naclass;
2315 PIX *pix;
2316 PIXA *pixat;
2317 PTA *ptaul, *ptall;
2318 
2319  PROCNAME("jbGetLLCorners");
2320 
2321  if (!classer)
2322  return ERROR_INT("classer not defined", procName, 1);
2323 
2324  ptaul = classer->ptaul;
2325  naclass = classer->naclass;
2326  pixat = classer->pixat;
2327 
2328  ptaDestroy(&classer->ptall);
2329  n = ptaGetCount(ptaul);
2330  ptall = ptaCreate(n);
2331  classer->ptall = ptall;
2332 
2333  /* If the templates were bordered, we would add h - 1 to the UL
2334  * corner y-value. However, because the templates to be used
2335  * here have their borders removed, and the borders are
2336  * JB_ADDED_PIXELS on each side, we add h - 1 - 2 * JB_ADDED_PIXELS
2337  * to the UL corner y-value. */
2338  for (i = 0; i < n; i++) {
2339  ptaGetIPt(ptaul, i, &x1, &y1);
2340  numaGetIValue(naclass, i, &iclass);
2341  pix = pixaGetPix(pixat, iclass, L_CLONE);
2342  h = pixGetHeight(pix);
2343  ptaAddPt(ptall, x1, y1 + h - 1 - 2 * JB_ADDED_PIXELS);
2344  pixDestroy(&pix);
2345  }
2346 
2347  return 0;
2348 }
2349 
2350 
2351 /*----------------------------------------------------------------------*
2352  * Static helpers *
2353  *----------------------------------------------------------------------*/
2354 /* When looking for similar matches we check templates whose size is +/- 2 in
2355  * each direction. This involves 25 possible sizes. This array contains the
2356  * offsets for each of those positions in a spiral pattern. There are 25 pairs
2357  * of numbers in this array: even positions are x values. */
2358 static int two_by_two_walk[50] = {
2359  0, 0,
2360  0, 1,
2361  -1, 0,
2362  0, -1,
2363  1, 0,
2364  -1, 1,
2365  1, 1,
2366  -1, -1,
2367  1, -1,
2368  0, -2,
2369  2, 0,
2370  0, 2,
2371  -2, 0,
2372  -1, -2,
2373  1, -2,
2374  2, -1,
2375  2, 1,
2376  1, 2,
2377  -1, 2,
2378  -2, 1,
2379  -2, -1,
2380  -2, -2,
2381  2, -2,
2382  2, 2,
2383  -2, 2};
2384 
2385 
2393 static JBFINDCTX *
2394 findSimilarSizedTemplatesInit(JBCLASSER *classer,
2395  PIX *pixs)
2396 {
2397 JBFINDCTX *state;
2398 
2399  state = (JBFINDCTX *)LEPT_CALLOC(1, sizeof(JBFINDCTX));
2400  state->w = pixGetWidth(pixs) - 2 * JB_ADDED_PIXELS;
2401  state->h = pixGetHeight(pixs) - 2 * JB_ADDED_PIXELS;
2402  state->classer = classer;
2403  return state;
2404 }
2405 
2406 
2407 static void
2408 findSimilarSizedTemplatesDestroy(JBFINDCTX **pstate)
2409 {
2410 JBFINDCTX *state;
2411 
2412  PROCNAME("findSimilarSizedTemplatesDestroy");
2413 
2414  if (pstate == NULL) {
2415  L_WARNING("ptr address is null\n", procName);
2416  return;
2417  }
2418  if ((state = *pstate) == NULL)
2419  return;
2420 
2421  l_dnaDestroy(&state->dna);
2422  LEPT_FREE(state);
2423  *pstate = NULL;
2424  return;
2425 }
2426 
2427 
2445 static l_int32
2446 findSimilarSizedTemplatesNext(JBFINDCTX *state)
2447 {
2448 l_int32 desiredh, desiredw, size, templ;
2449 PIX *pixt;
2450 
2451  while(1) { /* Continue the walk over step 'i' */
2452  if (state->i >= 25) { /* all done; didn't find a good match */
2453  return -1;
2454  }
2455 
2456  desiredw = state->w + two_by_two_walk[2 * state->i];
2457  desiredh = state->h + two_by_two_walk[2 * state->i + 1];
2458  if (desiredh < 1 || desiredw < 1) { /* invalid size */
2459  state->i++;
2460  continue;
2461  }
2462 
2463  if (!state->dna) {
2464  /* We have yet to start walking the array for the step 'i' */
2465  state->dna = l_dnaHashGetDna(state->classer->dahash,
2466  (l_uint64)desiredh * desiredw, L_CLONE);
2467  if (!state->dna) { /* nothing there */
2468  state->i++;
2469  continue;
2470  }
2471 
2472  state->n = 0; /* OK, we got a dna. */
2473  }
2474 
2475  /* Continue working on this dna */
2476  size = l_dnaGetCount(state->dna);
2477  for ( ; state->n < size; ) {
2478  templ = (l_int32)(state->dna->array[state->n++] + 0.5);
2479  pixt = pixaGetPix(state->classer->pixat, templ, L_CLONE);
2480  if (pixGetWidth(pixt) - 2 * JB_ADDED_PIXELS == desiredw &&
2481  pixGetHeight(pixt) - 2 * JB_ADDED_PIXELS == desiredh) {
2482  pixDestroy(&pixt);
2483  return templ;
2484  }
2485  pixDestroy(&pixt);
2486  }
2487 
2488  /* Exhausted the dna (no match found); take another step and
2489  * try again. */
2490  state->i++;
2491  l_dnaDestroy(&state->dna);
2492  continue;
2493  }
2494 }
2495 
2496 
2512 static l_int32
2513 finalPositioningForAlignment(PIX *pixs,
2514  l_int32 x,
2515  l_int32 y,
2516  l_int32 idelx,
2517  l_int32 idely,
2518  PIX *pixt,
2519  l_int32 *sumtab,
2520  l_int32 *pdx,
2521  l_int32 *pdy)
2522 {
2523 l_int32 w, h, i, j, minx, miny, count, mincount;
2524 PIX *pixi; /* clipped from source pixs */
2525 PIX *pixr; /* temporary storage */
2526 BOX *box;
2527 
2528  PROCNAME("finalPositioningForAlignment");
2529 
2530  if (!pixs)
2531  return ERROR_INT("pixs not defined", procName, 1);
2532  if (!pixt)
2533  return ERROR_INT("pixt not defined", procName, 1);
2534  if (!pdx || !pdy)
2535  return ERROR_INT("&dx and &dy not both defined", procName, 1);
2536  if (!sumtab)
2537  return ERROR_INT("sumtab not defined", procName, 1);
2538  *pdx = *pdy = 0;
2539 
2540  /* Use JB_ADDED_PIXELS pixels padding on each side */
2541  pixGetDimensions(pixt, &w, &h, NULL);
2542  box = boxCreate(x - idelx - JB_ADDED_PIXELS,
2543  y - idely - JB_ADDED_PIXELS, w, h);
2544  pixi = pixClipRectangle(pixs, box, NULL);
2545  boxDestroy(&box);
2546  if (!pixi)
2547  return ERROR_INT("pixi not made", procName, 1);
2548 
2549  pixr = pixCreate(pixGetWidth(pixi), pixGetHeight(pixi), 1);
2550  mincount = 0x7fffffff;
2551  for (i = -1; i <= 1; i++) {
2552  for (j = -1; j <= 1; j++) {
2553  pixCopy(pixr, pixi);
2554  pixRasterop(pixr, j, i, w, h, PIX_SRC ^ PIX_DST, pixt, 0, 0);
2555  pixCountPixels(pixr, &count, sumtab);
2556  if (count < mincount) {
2557  minx = j;
2558  miny = i;
2559  mincount = count;
2560  }
2561  }
2562  }
2563  pixDestroy(&pixi);
2564  pixDestroy(&pixr);
2565 
2566  *pdx = minx;
2567  *pdy = miny;
2568  return 0;
2569 }
PIX * pixUnpackBinary(PIX *pixs, l_int32 depth, l_int32 invert)
pixUnpackBinary()
Definition: pixconv.c:1913
l_int32 latticew
Definition: jbclass.h:111
void pixaaDestroy(PIXAA **ppaa)
pixaaDestroy()
Definition: pixabasic.c:1957
l_int32 keep_pixaa
Definition: jbclass.h:69
PIXAA * pixaaCreate(l_int32 n)
pixaaCreate()
Definition: pixabasic.c:1852
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:719
l_float32 rankhaus
Definition: jbclass.h:60
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:2218
L_DNA * l_dnaHashGetDna(L_DNAHASH *dahash, l_uint64 key, l_int32 copyflag)
l_dnaHashGetDna()
Definition: dnahash.c:141
l_int32 l_dnaGetCount(L_DNA *da)
l_dnaGetCount()
Definition: dnabasic.c:631
SARRAY * sarrayCopy(SARRAY *sa)
sarrayCopy()
Definition: sarray1.c:398
l_int32 * makePixelCentroidTab8(void)
makePixelCentroidTab8()
Definition: pix3.c:2451
struct Pta * ptaul
Definition: jbclass.h:115
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:383
l_ok pixMultConstAccumulate(PIX *pixs, l_float32 factor, l_uint32 offset)
pixMultConstAccumulate()
Definition: pixarith.c:916
l_int32 w
Definition: jbclass.h:66
PIX * gplotGeneralPix2(NUMA *na1, NUMA *na2, l_int32 plotstyle, const char *rootname, const char *title, const char *xlabel, const char *ylabel)
gplotGeneralPix2()
Definition: gplot.c:1137
struct L_DnaHash * dahash
Definition: jbclass.h:75
PIX * pixCloseBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseBrick()
Definition: morph.c:900
l_ok ptaAddPt(PTA *pta, l_float32 x, l_float32 y)
ptaAddPt()
Definition: ptabasic.c:343
Definition: pix.h:713
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:478
PTA * pixaCentroids(PIXA *pixa)
pixaCentroids()
Definition: morphapp.c:1478
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
void l_dnaDestroy(L_DNA **pda)
l_dnaDestroy()
Definition: dnabasic.c:332
void l_dnaHashDestroy(L_DNAHASH **pdahash)
l_dnaHashDestroy()
Definition: dnahash.c:106
struct Numa * nafgt
Definition: jbclass.h:76
Definition: pix.h:712
struct Numa * napage
Definition: jbclass.h:114
BOXA * boxaSelectBySize(BOXA *boxas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged)
boxaSelectBySize()
Definition: boxfunc4.c:220
l_ok pixRasterop(PIX *pixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, PIX *pixs, l_int32 sx, l_int32 sy)
pixRasterop()
Definition: rop.c:204
PTA * ptaCreate(l_int32 n)
ptaCreate()
Definition: ptabasic.c:120
PIXA * pixaaGetPixa(PIXAA *paa, l_int32 index, l_int32 accesstype)
pixaaGetPixa()
Definition: pixabasic.c:2206
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:705
l_int32 pixaaGetCount(PIXAA *paa, NUMA **pna)
pixaaGetCount()
Definition: pixabasic.c:2157
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
Definition: pix.h:710
PIX * pixaDisplayOnLattice(PIXA *pixa, l_int32 cellw, l_int32 cellh, l_int32 *pncols, BOXA **pboxa)
pixaDisplayOnLattice()
Definition: pixafunc2.c:440
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
l_int32 maxheight
Definition: jbclass.h:54
struct Numa * naclass
Definition: jbclass.h:80
Definition: array.h:94
struct Pixaa * pixaa
Definition: jbclass.h:70
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:194
l_int32 nclass
Definition: jbclass.h:110
l_int32 ptaGetCount(PTA *pta)
ptaGetCount()
Definition: ptabasic.c:527
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:583
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
struct Pixa * pixat
Definition: jbclass.h:71
struct Pta * ptac
Definition: jbclass.h:78
#define JB_TEMPLATE_EXT
Definition: jbclass.h:138
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition: pix5.c:1026
l_int32 nclass
Definition: jbclass.h:68
l_ok pixThresholdPixelSum(PIX *pix, l_int32 thresh, l_int32 *pabove, l_int32 *tab8)
pixThresholdPixelSum()
Definition: pix3.c:2339
Definition: pix.h:491
struct Pixa * pixatd
Definition: jbclass.h:73
l_int32 h
Definition: jbclass.h:67
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1699
l_ok ptaJoin(PTA *ptad, PTA *ptas, l_int32 istart, l_int32 iend)
ptaJoin()
Definition: ptafunc1.c:167
Definition: array.h:126
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:125
struct Numa * naarea
Definition: jbclass.h:64
l_int32 * numaGetIArray(NUMA *na)
numaGetIArray()
Definition: numabasic.c:847
l_ok l_dnaHashAdd(L_DNAHASH *dahash, l_uint64 key, l_float64 value)
l_dnaHashAdd()
Definition: dnahash.c:176
l_uint8 * l_binaryRead(const char *filename, size_t *pnbytes)
l_binaryRead()
Definition: utils2.c:1352
BOXA * pixConnComp(PIX *pixs, PIXA **ppixa, l_int32 connectivity)
pixConnComp()
Definition: conncomp.c:151
l_float32 thresh
Definition: jbclass.h:61
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:754
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:506
Definition: array.h:70
struct Numa * napage
Definition: jbclass.h:81
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:658
PIX * pixConvert1To2Cmap(PIX *pixs)
pixConvert1To2Cmap()
Definition: pixconv.c:2115
l_ok pixClearAll(PIX *pix)
pixClearAll()
Definition: pix2.c:789
PTA * ptaClone(PTA *pta)
ptaClone()
Definition: ptabasic.c:297
void selDestroy(SEL **psel)
selDestroy()
Definition: sel1.c:340
BOXA * pixConnCompBB(PIX *pixs, l_int32 connectivity)
pixConnCompBB()
Definition: conncomp.c:310
PIXA * pixaCreateFromPix(PIX *pixs, l_int32 n, l_int32 cellw, l_int32 cellh)
pixaCreateFromPix()
Definition: pixabasic.c:206
PIX * pixInitAccumulate(l_int32 w, l_int32 h, l_uint32 offset)
pixInitAccumulate()
Definition: pixarith.c:649
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:137
l_ok pixAccumulate(PIX *pixd, PIX *pixs, l_int32 op)
pixAccumulate()
Definition: pixarith.c:817
l_ok pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1937
PIXA * pixaSelectBySize(PIXA *pixas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged)
pixaSelectBySize()
Definition: pixafunc1.c:306
l_ok ptaGetPt(PTA *pta, l_int32 index, l_float32 *px, l_float32 *py)
ptaGetPt()
Definition: ptabasic.c:548
l_int32 * makePixelSumTab8(void)
makePixelSumTab8()
Definition: pix3.c:2411
struct Pta * ptaul
Definition: jbclass.h:82
l_int32 latticeh
Definition: jbclass.h:112
SEL * selCreateBrick(l_int32 h, l_int32 w, l_int32 cy, l_int32 cx, l_int32 type)
selCreateBrick()
Definition: sel1.c:418
l_float32 weightfactor
Definition: jbclass.h:62
char * sarrayGetString(SARRAY *sa, l_int32 index, l_int32 copyflag)
sarrayGetString()
Definition: sarray1.c:703
struct Pix * pix
Definition: jbclass.h:106
struct Pta * ptall
Definition: jbclass.h:85
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
PIX * pixScaleToSize(PIX *pixs, l_int32 wd, l_int32 hd)
pixScaleToSize()
Definition: scale1.c:323
PIX * pixFinalAccumulate(PIX *pixs, l_uint32 offset, l_int32 depth)
pixFinalAccumulate()
Definition: pixarith.c:683
l_int32 w
Definition: jbclass.h:108
struct Numa * naclass
Definition: jbclass.h:113
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
NUMA * numaMakeSequence(l_float32 startval, l_float32 increment, l_int32 size)
numaMakeSequence()
Definition: numafunc1.c:821
BOX * boxaGetBox(BOXA *boxa, l_int32 index, l_int32 accessflag)
boxaGetBox()
Definition: boxbasic.c:779
Definition: pix.h:711
SARRAY * sarrayCreateLinesFromString(const char *string, l_int32 blankflag)
sarrayCreateLinesFromString()
Definition: sarray1.c:283
Definition: pix.h:455
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:366
Definition: pix.h:466
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1113
FILE * fopenWriteStream(const char *filename, const char *modestring)
fopenWriteStream()
Definition: utils2.c:1975
NUMA * pixaCountPixels(PIXA *pixa)
pixaCountPixels()
Definition: pix3.c:1892
l_ok pixaSizeRange(PIXA *pixa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh)
pixaSizeRange()
Definition: pixafunc1.c:2591
l_int32 sarrayGetCount(SARRAY *sa)
sarrayGetCount()
Definition: sarray1.c:643
PIX * pixRead(const char *filename)
pixRead()
Definition: readfile.c:193
l_int32 npages
Definition: jbclass.h:107
l_int32 h
Definition: jbclass.h:109
PIX * pixExpandReplicate(PIX *pixs, l_int32 factor)
pixExpandReplicate()
Definition: scale2.c:872
l_ok pixRenderBoxaArb(PIX *pix, BOXA *boxa, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval)
pixRenderBoxaArb()
Definition: graphics.c:1772
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:691
l_ok pixRenderBoxArb(PIX *pix, BOX *box, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval)
pixRenderBoxArb()
Definition: graphics.c:1655
l_ok pixaaAddPixa(PIXAA *paa, PIXA *pixa, l_int32 copyflag)
pixaaAddPixa()
Definition: pixabasic.c:1998
#define PIX_NOT(op)
Definition: pix.h:332
l_int32 npages
Definition: jbclass.h:55
l_int32 maxwidth
Definition: jbclass.h:53
Definition: pix.h:138
void ptaDestroy(PTA **ppta)
ptaDestroy()
Definition: ptabasic.c:195
l_ok pixZero(PIX *pix, l_int32 *pempty)
pixZero()
Definition: pix3.c:1815
struct Numa * nacomps
Definition: jbclass.h:58
BOXA * boxaCreate(l_int32 n)
boxaCreate()
Definition: boxbasic.c:502
struct Pta * ptact
Definition: jbclass.h:79
#define PIX_SRC
Definition: pix.h:330
PIXA * pixaClipToPix(PIXA *pixas, PIX *pixs)
pixaClipToPix()
Definition: pixafunc1.c:2661
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:282
NUMA * numaClone(NUMA *na)
numaClone()
Definition: numabasic.c:428
#define L_BUF_SIZE
Definition: classapp.c:59
l_int32 boxaGetCount(BOXA *boxa)
boxaGetCount()
Definition: boxbasic.c:734
struct Sarray * safiles
Definition: jbclass.h:49
l_ok ptaGetIPt(PTA *pta, l_int32 index, l_int32 *px, l_int32 *py)
ptaGetIPt()
Definition: ptabasic.c:578
PIX * pixDilate(PIX *pixd, PIX *pixs, SEL *sel)
pixDilate()
Definition: morph.c:213
l_int32 baseindex
Definition: jbclass.h:56
L_DNAHASH * l_dnaHashCreate(l_int32 nbuckets, l_int32 initsize)
l_dnaHashCreate()
Definition: dnahash.c:69
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:414
l_ok pixaAddBox(PIXA *pixa, BOX *box, l_int32 copyflag)
pixaAddBox()
Definition: pixabasic.c:555
l_ok boxGetGeometry(BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:313
Definition: pix.h:480
PIX * pixReduceRankBinaryCascade(PIX *pixs, l_int32 level1, l_int32 level2, l_int32 level3, l_int32 level4)
pixReduceRankBinaryCascade()
Definition: binreduce.c:152
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:412
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:172
l_int32 components
Definition: jbclass.h:51
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:650
l_int32 method
Definition: jbclass.h:50
l_int32 sizehaus
Definition: jbclass.h:59
PIX * pixAddBorderGeneral(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_uint32 val)
pixAddBorderGeneral()
Definition: pix2.c:1917
Definition: pix.h:516
#define PIX_DST
Definition: pix.h:331
l_float64 * array
Definition: array.h:101
void sarrayDestroy(SARRAY **psa)
sarrayDestroy()
Definition: sarray1.c:362