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