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 */
219static const l_int32 JB_ADDED_PIXELS = 6;
220
221 /* For pixHaustest(), pixRankHaustest() and pixCorrelationScore():
222 * choose these to be 2 or greater */
223static const l_int32 MAX_DIFF_WIDTH = 2; /* use at least 2 */
224static 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. */
233static const l_int32 MAX_CONN_COMP_WIDTH = 350; /* default max cc width */
234static const l_int32 MAX_CHAR_COMP_WIDTH = 350; /* default max char width */
235static const l_int32 MAX_WORD_COMP_WIDTH = 1000; /* default max word width */
236static 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};
249typedef struct JbFindTemplatesState JBFINDCTX;
250
251 /* Static initialization function */
252static 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 */
257static JBFINDCTX * findSimilarSizedTemplatesInit(JBCLASSER *classer, PIX *pixs);
258static l_int32 findSimilarSizedTemplatesNext(JBFINDCTX *context);
259static void findSimilarSizedTemplatesDestroy(JBFINDCTX **pcontext);
260static 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 *----------------------------------------------------------------------*/
285JBCLASSER *
286jbRankHausInit(l_int32 components,
287 l_int32 maxwidth,
288 l_int32 maxheight,
289 l_int32 size,
290 l_float32 rank)
291{
292JBCLASSER *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
346JBCLASSER *
347jbCorrelationInit(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
373JBCLASSER *
374jbCorrelationInitWithoutComponents(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
385static JBCLASSER *
386jbCorrelationInitInternal(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{
393JBCLASSER *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 *----------------------------------------------------------------------*/
446l_ok
447jbAddPages(JBCLASSER *classer,
448 SARRAY *safiles)
449{
450l_int32 i, nfiles;
451char *fname;
452PIX *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
488l_ok
489jbAddPage(JBCLASSER *classer,
490 PIX *pixs)
491{
492BOXA *boxas;
493PIXA *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
533l_ok
534jbAddPageComponents(JBCLASSER *classer,
535 PIX *pixs,
536 BOXA *boxas,
537 PIXA *pixas)
538{
539l_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 *----------------------------------------------------------------------*/
593l_ok
594jbClassifyRankHaus(JBCLASSER *classer,
595 BOXA *boxa,
596 PIXA *pixas)
597{
598l_int32 n, nt, i, wt, ht, iclass, size, found, testval;
599l_int32 npages, area1, area3;
600l_int32 *tab8;
601l_float32 rank, x1, y1, x2, y2;
602BOX *box;
603NUMA *naclass, *napage;
604NUMA *nafg; /* fg area of all instances */
605NUMA *nafgt; /* fg area of all templates */
606JBFINDCTX *findcontext;
607L_DNAHASH *dahash;
608PIX *pix, *pix1, *pix2, *pix3, *pix4;
609PIXA *pixa, *pixa1, *pixa2, *pixat, *pixatd;
610PIXAA *pixaa;
611PTA *pta, *ptac, *ptact;
612SEL *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
848l_int32
849pixHaustest(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{
858l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch;
859PIX *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
946l_int32
947pixRankHaustest(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{
960l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch;
961l_int32 thresh1, thresh3;
962PIX *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 *----------------------------------------------------------------------*/
1033l_ok
1034jbClassifyCorrelation(JBCLASSER *classer,
1035 BOXA *boxa,
1036 PIXA *pixas)
1037{
1038l_int32 n, nt, i, iclass, wt, ht, found, area, area1, area2, npages,
1039 overthreshold;
1040l_int32 *sumtab, *centtab;
1041l_uint32 *row, word;
1042l_float32 x1, y1, x2, y2, xsum, ysum;
1043l_float32 thresh, weight, threshold;
1044BOX *box;
1045NUMA *naclass, *napage;
1046NUMA *nafgt; /* fg area of all templates */
1047NUMA *naarea; /* w * h area of all templates */
1048JBFINDCTX *findcontext;
1049L_DNAHASH *dahash;
1050PIX *pix, *pix1, *pix2;
1051PIXA *pixa, *pixa1, *pixat;
1052PIXAA *pixaa;
1053PTA *pta, *ptac, *ptact;
1054l_int32 *pixcts; /* pixel counts of each pixa */
1055l_int32 **pixrowcts; /* row-by-row pixel counts of each pixa */
1056l_int32 x, y, rowcount, downcount, wpl;
1057l_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 *----------------------------------------------------------------------*/
1315l_ok
1316jbGetComponents(PIX *pixs,
1317 l_int32 components,
1318 l_int32 maxwidth,
1319 l_int32 maxheight,
1320 BOXA **pboxad,
1321 PIXA **ppixad)
1322{
1323l_int32 empty, res, redfactor;
1324BOXA *boxa;
1325PIX *pix1, *pix2, *pix3;
1326PIXA *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
1457l_ok
1458pixWordMaskByDilation(PIX *pixs,
1459 PIX **ppixm,
1460 l_int32 *psize,
1461 PIXA *pixadb)
1462{
1463l_int32 i, n, ndil, maxdiff, diff, ibest;
1464l_int32 check, count, total, xres;
1465l_int32 ncc[13]; /* max dilation + 1 */
1466l_int32 *diffa;
1467BOXA *boxa;
1468NUMA *nacc, *nadiff;
1469PIX *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
1589l_ok
1590pixWordBoxesByDilation(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{
1599BOXA *boxa1, *boxa2;
1600PIX *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 *----------------------------------------------------------------------*/
1651PIXA *
1652jbAccumulateComposites(PIXAA *pixaa,
1653 NUMA **pna,
1654 PTA **pptat)
1655{
1656l_int32 n, nt, i, j, d, minw, maxw, minh, maxh, xdiff, ydiff;
1657l_float32 x, y, xave, yave;
1658NUMA *na;
1659PIX *pix, *pixt1, *pixt2, *pixsum;
1660PIXA *pixa, *pixad;
1661PTA *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
1741PIXA *
1742jbTemplatesFromComposites(PIXA *pixac,
1743 NUMA *na)
1744{
1745l_int32 n, i;
1746l_float32 nt; /* number of samples in the composite; always an integer */
1747l_float32 factor;
1748PIX *pixsum; /* accumulated composite */
1749PIX *pixd;
1750PIXA *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 *----------------------------------------------------------------------*/
1786JBCLASSER *
1787jbClasserCreate(l_int32 method,
1788 l_int32 components)
1789{
1790JBCLASSER *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 */
1824void
1825jbClasserDestroy(JBCLASSER **pclasser)
1826{
1827JBCLASSER *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
1873JBDATA *
1874jbDataSave(JBCLASSER *classer)
1875{
1876l_int32 maxw, maxh;
1877JBDATA *data;
1878PIX *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 */
1913void
1914jbDataDestroy(JBDATA **pdata)
1915{
1916JBDATA *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
1944l_ok
1945jbDataWrite(const char *rootout,
1946 JBDATA *jbdata)
1947{
1948char buf[L_BUF_SIZE];
1949l_int32 w, h, nclass, npages, cellw, cellh, ncomp, i, x, y, iclass, ipage;
1950NUMA *naclass, *napage;
1951PTA *ptaul;
1952PIX *pixt;
1953FILE *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
2004JBDATA *
2005jbDataRead(const char *rootname)
2006{
2007char fname[L_BUF_SIZE];
2008char *linestr;
2009l_uint8 *data;
2010l_int32 nsa, i, w, h, cellw, cellh, x, y, iclass, ipage;
2011l_int32 npages, nclass, ncomp, ninit;
2012size_t size;
2013JBDATA *jbdata;
2014NUMA *naclass, *napage;
2015PIX *pixs;
2016PTA *ptaul;
2017SARRAY *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
2109PIXA *
2110jbDataRender(JBDATA *data,
2111 l_int32 debugflag)
2112{
2113l_int32 i, w, h, cellw, cellh, x, y, iclass, ipage;
2114l_int32 npages, nclass, ncomp, wp, hp;
2115BOX *box;
2116NUMA *naclass, *napage;
2117PIX *pixt, *pixt2, *pix, *pixd;
2118PIXA *pixat; /* pixa of templates */
2119PIXA *pixad; /* pixa of output images */
2120PIXCMAP *cmap;
2121PTA *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
2218l_ok
2219jbGetULCorners(JBCLASSER *classer,
2220 PIX *pixs,
2221 BOXA *boxa)
2222{
2223l_int32 i, baseindex, index, n, iclass, idelx, idely, x, y, dx, dy;
2224l_int32 *sumtab;
2225l_float32 x1, x2, y1, y2, delx, dely;
2226BOX *box;
2227NUMA *naclass;
2228PIX *pixt;
2229PTA *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
2310l_ok
2311jbGetLLCorners(JBCLASSER *classer)
2312{
2313l_int32 i, iclass, n, x1, y1, h;
2314NUMA *naclass;
2315PIX *pix;
2316PIXA *pixat;
2317PTA *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. */
2358static 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
2393static JBFINDCTX *
2394findSimilarSizedTemplatesInit(JBCLASSER *classer,
2395 PIX *pixs)
2396{
2397JBFINDCTX *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
2407static void
2408findSimilarSizedTemplatesDestroy(JBFINDCTX **pstate)
2409{
2410JBFINDCTX *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
2445static l_int32
2446findSimilarSizedTemplatesNext(JBFINDCTX *state)
2447{
2448l_int32 desiredh, desiredw, size, templ;
2449PIX *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
2512static l_int32
2513finalPositioningForAlignment(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{
2523l_int32 w, h, i, j, minx, miny, count, mincount;
2524PIX *pixi; /* clipped from source pixs */
2525PIX *pixr; /* temporary storage */
2526BOX *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 * pixReduceRankBinaryCascade(PIX *pixs, l_int32 level1, l_int32 level2, l_int32 level3, l_int32 level4)
pixReduceRankBinaryCascade()
Definition: binreduce.c:152
BOX * boxaGetBox(BOXA *boxa, l_int32 index, l_int32 accessflag)
boxaGetBox()
Definition: boxbasic.c:779
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:282
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:172
l_int32 boxaGetCount(BOXA *boxa)
boxaGetCount()
Definition: boxbasic.c:734
l_ok boxGetGeometry(BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:313
BOXA * boxaCreate(l_int32 n)
boxaCreate()
Definition: boxbasic.c:502
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:583
BOXA * boxaSelectBySize(BOXA *boxas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged)
boxaSelectBySize()
Definition: boxfunc4.c:220
#define L_BUF_SIZE
Definition: classapp.c:59
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:125
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:414
BOXA * pixConnComp(PIX *pixs, PIXA **ppixa, l_int32 connectivity)
pixConnComp()
Definition: conncomp.c:151
BOXA * pixConnCompBB(PIX *pixs, l_int32 connectivity)
pixConnCompBB()
Definition: conncomp.c:310
void l_dnaDestroy(L_DNA **pda)
l_dnaDestroy()
Definition: dnabasic.c:332
l_int32 l_dnaGetCount(L_DNA *da)
l_dnaGetCount()
Definition: dnabasic.c:631
L_DNAHASH * l_dnaHashCreate(l_int32 nbuckets, l_int32 initsize)
l_dnaHashCreate()
Definition: dnahash.c:69
void l_dnaHashDestroy(L_DNAHASH **pdahash)
l_dnaHashDestroy()
Definition: dnahash.c:106
L_DNA * l_dnaHashGetDna(L_DNAHASH *dahash, l_uint64 key, l_int32 copyflag)
l_dnaHashGetDna()
Definition: dnahash.c:141
l_ok l_dnaHashAdd(L_DNAHASH *dahash, l_uint64 key, l_float64 value)
l_dnaHashAdd()
Definition: dnahash.c:176
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
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 pixRenderBoxaArb(PIX *pix, BOXA *boxa, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval)
pixRenderBoxaArb()
Definition: graphics.c:1772
#define JB_TEMPLATE_EXT
Definition: jbclass.h:138
PIX * pixCloseBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseBrick()
Definition: morph.c:900
PIX * pixDilate(PIX *pixd, PIX *pixs, SEL *sel)
pixDilate()
Definition: morph.c:213
PTA * pixaCentroids(PIXA *pixa)
pixaCentroids()
Definition: morphapp.c:1478
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:137
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:478
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:719
l_int32 * numaGetIArray(NUMA *na)
numaGetIArray()
Definition: numabasic.c:847
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:366
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:658
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:754
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:194
NUMA * numaClone(NUMA *na)
numaClone()
Definition: numabasic.c:428
NUMA * numaMakeSequence(l_float32 startval, l_float32 increment, l_int32 size)
numaMakeSequence()
Definition: numafunc1.c:821
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1699
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1113
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:383
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:705
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
l_ok pixClearAll(PIX *pix)
pixClearAll()
Definition: pix2.c:789
PIX * pixAddBorderGeneral(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_uint32 val)
pixAddBorderGeneral()
Definition: pix2.c:1917
l_int32 * makePixelSumTab8(void)
makePixelSumTab8()
Definition: pix3.c:2411
NUMA * pixaCountPixels(PIXA *pixa)
pixaCountPixels()
Definition: pix3.c:1892
l_ok pixZero(PIX *pix, l_int32 *pempty)
pixZero()
Definition: pix3.c:1815
l_ok pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1937
l_int32 * makePixelCentroidTab8(void)
makePixelCentroidTab8()
Definition: pix3.c:2451
l_ok pixThresholdPixelSum(PIX *pix, l_int32 thresh, l_int32 *pabove, l_int32 *tab8)
pixThresholdPixelSum()
Definition: pix3.c:2339
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition: pix5.c:1026
#define PIX_DST
Definition: pix.h:331
@ L_SELECT_IF_LTE
Definition: pix.h:784
@ L_SELECT_IF_GTE
Definition: pix.h:785
@ L_SELECT_IF_BOTH
Definition: pix.h:806
@ L_COPY
Definition: pix.h:712
@ L_CLONE
Definition: pix.h:713
@ L_NOCOPY
Definition: pix.h:710
@ L_INSERT
Definition: pix.h:711
#define PIX_SRC
Definition: pix.h:330
#define PIX_NOT(op)
Definition: pix.h:332
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:506
PIXA * pixaCreateFromPix(PIX *pixs, l_int32 n, l_int32 cellw, l_int32 cellh)
pixaCreateFromPix()
Definition: pixabasic.c:206
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:412
l_ok pixaaAddPixa(PIXAA *paa, PIXA *pixa, l_int32 copyflag)
pixaaAddPixa()
Definition: pixabasic.c:1998
PIXA * pixaaGetPixa(PIXAA *paa, l_int32 index, l_int32 accesstype)
pixaaGetPixa()
Definition: pixabasic.c:2206
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:650
l_ok pixaAddBox(PIXA *pixa, BOX *box, l_int32 copyflag)
pixaAddBox()
Definition: pixabasic.c:555
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:691
void pixaaDestroy(PIXAA **ppaa)
pixaaDestroy()
Definition: pixabasic.c:1957
l_int32 pixaaGetCount(PIXAA *paa, NUMA **pna)
pixaaGetCount()
Definition: pixabasic.c:2157
PIXAA * pixaaCreate(l_int32 n)
pixaaCreate()
Definition: pixabasic.c:1852
l_ok pixaSizeRange(PIXA *pixa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh)
pixaSizeRange()
Definition: pixafunc1.c:2591
PIXA * pixaSelectBySize(PIXA *pixas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged)
pixaSelectBySize()
Definition: pixafunc1.c:306
PIXA * pixaClipToPix(PIXA *pixas, PIX *pixs)
pixaClipToPix()
Definition: pixafunc1.c:2661
PIX * pixaDisplayOnLattice(PIXA *pixa, l_int32 cellw, l_int32 cellh, l_int32 *pncols, BOXA **pboxa)
pixaDisplayOnLattice()
Definition: pixafunc2.c:440
l_ok pixMultConstAccumulate(PIX *pixs, l_float32 factor, l_uint32 offset)
pixMultConstAccumulate()
Definition: pixarith.c:916
PIX * pixInitAccumulate(l_int32 w, l_int32 h, l_uint32 offset)
pixInitAccumulate()
Definition: pixarith.c:649
PIX * pixFinalAccumulate(PIX *pixs, l_uint32 offset, l_int32 depth)
pixFinalAccumulate()
Definition: pixarith.c:683
l_ok pixAccumulate(PIX *pixd, PIX *pixs, l_int32 op)
pixAccumulate()
Definition: pixarith.c:817
PIX * pixConvert1To2Cmap(PIX *pixs)
pixConvert1To2Cmap()
Definition: pixconv.c:2115
PIX * pixUnpackBinary(PIX *pixs, l_int32 depth, l_int32 invert)
pixUnpackBinary()
Definition: pixconv.c:1913
l_ok ptaGetIPt(PTA *pta, l_int32 index, l_int32 *px, l_int32 *py)
ptaGetIPt()
Definition: ptabasic.c:578
PTA * ptaCreate(l_int32 n)
ptaCreate()
Definition: ptabasic.c:120
PTA * ptaClone(PTA *pta)
ptaClone()
Definition: ptabasic.c:297
l_ok ptaAddPt(PTA *pta, l_float32 x, l_float32 y)
ptaAddPt()
Definition: ptabasic.c:343
l_ok ptaGetPt(PTA *pta, l_int32 index, l_float32 *px, l_float32 *py)
ptaGetPt()
Definition: ptabasic.c:548
l_int32 ptaGetCount(PTA *pta)
ptaGetCount()
Definition: ptabasic.c:527
void ptaDestroy(PTA **ppta)
ptaDestroy()
Definition: ptabasic.c:195
l_ok ptaJoin(PTA *ptad, PTA *ptas, l_int32 istart, l_int32 iend)
ptaJoin()
Definition: ptafunc1.c:167
PIX * pixRead(const char *filename)
pixRead()
Definition: readfile.c:193
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
char * sarrayGetString(SARRAY *sa, l_int32 index, l_int32 copyflag)
sarrayGetString()
Definition: sarray1.c:703
l_int32 sarrayGetCount(SARRAY *sa)
sarrayGetCount()
Definition: sarray1.c:643
void sarrayDestroy(SARRAY **psa)
sarrayDestroy()
Definition: sarray1.c:362
SARRAY * sarrayCreateLinesFromString(const char *string, l_int32 blankflag)
sarrayCreateLinesFromString()
Definition: sarray1.c:283
SARRAY * sarrayCopy(SARRAY *sa)
sarrayCopy()
Definition: sarray1.c:398
PIX * pixScaleToSize(PIX *pixs, l_int32 wd, l_int32 hd)
pixScaleToSize()
Definition: scale1.c:323
PIX * pixExpandReplicate(PIX *pixs, l_int32 factor)
pixExpandReplicate()
Definition: scale2.c:872
void selDestroy(SEL **psel)
selDestroy()
Definition: sel1.c:340
SEL * selCreateBrick(l_int32 h, l_int32 w, l_int32 cy, l_int32 cx, l_int32 type)
selCreateBrick()
Definition: sel1.c:418
Definition: pix.h:481
Definition: pix.h:492
struct Pixa * pixat
Definition: jbclass.h:71
l_float32 thresh
Definition: jbclass.h:61
struct Pta * ptac
Definition: jbclass.h:78
l_int32 h
Definition: jbclass.h:67
struct Sarray * safiles
Definition: jbclass.h:49
struct Numa * naarea
Definition: jbclass.h:64
l_int32 npages
Definition: jbclass.h:55
l_float32 rankhaus
Definition: jbclass.h:60
struct Pta * ptall
Definition: jbclass.h:85
struct Numa * napage
Definition: jbclass.h:81
struct Numa * naclass
Definition: jbclass.h:80
l_int32 nclass
Definition: jbclass.h:68
struct Pta * ptaul
Definition: jbclass.h:82
l_int32 maxwidth
Definition: jbclass.h:53
l_float32 weightfactor
Definition: jbclass.h:62
struct Numa * nafgt
Definition: jbclass.h:76
struct Pixaa * pixaa
Definition: jbclass.h:70
l_int32 keep_pixaa
Definition: jbclass.h:69
struct L_DnaHash * dahash
Definition: jbclass.h:75
l_int32 sizehaus
Definition: jbclass.h:59
l_int32 w
Definition: jbclass.h:66
struct Numa * nacomps
Definition: jbclass.h:58
l_int32 components
Definition: jbclass.h:51
struct Pta * ptact
Definition: jbclass.h:79
l_int32 method
Definition: jbclass.h:50
l_int32 maxheight
Definition: jbclass.h:54
l_int32 baseindex
Definition: jbclass.h:56
struct Pixa * pixatd
Definition: jbclass.h:73
l_int32 latticew
Definition: jbclass.h:111
struct Numa * napage
Definition: jbclass.h:114
l_int32 latticeh
Definition: jbclass.h:112
l_int32 npages
Definition: jbclass.h:107
struct Numa * naclass
Definition: jbclass.h:113
l_int32 nclass
Definition: jbclass.h:110
struct Pix * pix
Definition: jbclass.h:106
struct Pta * ptaul
Definition: jbclass.h:115
l_int32 h
Definition: jbclass.h:109
l_int32 w
Definition: jbclass.h:108
Definition: array.h:95
l_float64 * array
Definition: array.h:101
Definition: array.h:71
Definition: pix.h:139
Definition: pix.h:456
Definition: pix.h:467
Definition: pix.h:517
Definition: array.h:127
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
FILE * fopenWriteStream(const char *filename, const char *modestring)
fopenWriteStream()
Definition: utils2.c:1975
l_uint8 * l_binaryRead(const char *filename, size_t *pnbytes)
l_binaryRead()
Definition: utils2.c:1352
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:2218