Leptonica  1.82.0
Image processing and image analysis suite
recogident.c
Go to the documentation of this file.
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 
92 #ifdef HAVE_CONFIG_H
93 #include <config_auto.h>
94 #endif /* HAVE_CONFIG_H */
95 
96 #include <string.h>
97 #include "allheaders.h"
98 
99  /* There are two methods for splitting characters: DID and greedy.
100  * The default method is DID. */
101 #define SPLIT_WITH_DID 1
102 
103  /* Padding on pix1: added before correlations and removed from result */
104 static const l_int32 LeftRightPadding = 32;
105 
106  /* Parameters for filtering and sorting connected components in splitter */
107 static const l_float32 MinFillFactor = 0.10;
108 static const l_int32 DefaultMinHeight = 15; /* min unscaled height */
109 static const l_int32 MinOverlap1 = 6; /* in pass 1 of boxaSort2d() */
110 static const l_int32 MinOverlap2 = 6; /* in pass 2 of boxaSort2d() */
111 static const l_int32 MinHeightPass1 = 5; /* min height to start pass 1 */
112 
113 
114 static l_int32 pixCorrelationBestShift(PIX *pix1, PIX *pix2, NUMA *nasum1,
115  NUMA *namoment1, l_int32 area2,
116  l_int32 ycent2, l_int32 maxyshift,
117  l_int32 *tab8, l_int32 *pdelx,
118  l_int32 *pdely, l_float32 *pscore,
119  l_int32 debugflag );
120 static L_RCH *rchCreate(l_int32 index, l_float32 score, char *text,
121  l_int32 sample, l_int32 xloc, l_int32 yloc,
122  l_int32 width);
123 static L_RCHA *rchaCreate();
124 static l_int32 transferRchToRcha(L_RCH *rch, L_RCHA *rcha);
125 static PIX *recogPreSplittingFilter(L_RECOG *recog, PIX *pixs, l_int32 minh,
126  l_float32 minaf, l_int32 debug);
127 static l_int32 recogSplittingFilter(L_RECOG *recog, PIX *pixs, l_int32 min,
128  l_float32 minaf, l_int32 *premove,
129  l_int32 debug);
130 static void l_showIndicatorSplitValues(NUMA *na1, NUMA *na2, NUMA *na3,
131  NUMA *na4, NUMA *na5, NUMA *na6);
132 
133 /*------------------------------------------------------------------------*
134  * Identification
135  *------------------------------------------------------------------------*/
161 l_ok
163  PIX *pixs,
164  l_int32 minh,
165  l_int32 skipsplit,
166  BOXA **pboxa,
167  PIXA **ppixa,
168  PIX **ppixdb,
169  l_int32 debugsplit)
170 {
171 l_int32 n;
172 BOXA *boxa;
173 PIX *pixb;
174 PIXA *pixa;
175 
176  PROCNAME("recogIdentifyMultiple");
177 
178  if (pboxa) *pboxa = NULL;
179  if (ppixa) *ppixa = NULL;
180  if (ppixdb) *ppixdb = NULL;
181  if (!recog)
182  return ERROR_INT("recog not defined", procName, 2);
183  if (!recog->train_done)
184  return ERROR_INT("training not finished", procName, 2);
185  if (!pixs)
186  return ERROR_INT("pixs not defined", procName, 2);
187 
188  /* Binarize if necessary */
189  if (pixGetDepth(pixs) > 1)
190  pixb = pixConvertTo1(pixs, recog->threshold);
191  else
192  pixb = pixClone(pixs);
193 
194  /* Noise removal and splitting of touching characters */
195  recogSplitIntoCharacters(recog, pixb, minh, skipsplit, &boxa, &pixa,
196  debugsplit);
197  pixDestroy(&pixb);
198  if (!pixa || (n = pixaGetCount(pixa)) == 0) {
199  pixaDestroy(&pixa);
200  boxaDestroy(&boxa);
201  L_WARNING("nothing found\n", procName);
202  return 1;
203  }
204 
205  recogIdentifyPixa(recog, pixa, ppixdb);
206  if (pboxa)
207  *pboxa = boxa;
208  else
209  boxaDestroy(&boxa);
210  if (ppixa)
211  *ppixa = pixa;
212  else
213  pixaDestroy(&pixa);
214  return 0;
215 }
216 
217 
218 /*------------------------------------------------------------------------*
219  * Segmentation and noise removal *
220  *------------------------------------------------------------------------*/
249 l_ok
251  PIX *pixs,
252  l_int32 minh,
253  l_int32 skipsplit,
254  BOXA **pboxa,
255  PIXA **ppixa,
256  l_int32 debug)
257 {
258 static l_int32 ind = 0;
259 char buf[32];
260 l_int32 i, xoff, yoff, empty, maxw, bw, ncomp, scaling;
261 BOX *box;
262 BOXA *boxa1, *boxa2, *boxa3, *boxa4, *boxad;
263 BOXAA *baa;
264 PIX *pix, *pix1, *pix2, *pix3;
265 PIXA *pixa;
266 
267  PROCNAME("recogSplitIntoCharacters");
268 
269  lept_mkdir("lept/recog");
270 
271  if (pboxa) *pboxa = NULL;
272  if (ppixa) *ppixa = NULL;
273  if (!pboxa || !ppixa)
274  return ERROR_INT("&boxa and &pixa not defined", procName, 1);
275  if (!recog)
276  return ERROR_INT("recog not defined", procName, 1);
277  if (!recog->train_done)
278  return ERROR_INT("training not finished", procName, 1);
279  if (!pixs || pixGetDepth(pixs) != 1)
280  return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
281  if (minh <= 0) minh = DefaultMinHeight;
282  pixZero(pixs, &empty);
283  if (empty) return 1;
284 
285  /* Small vertical close for consolidation. Don't do a horizontal
286  * closing, because it might join separate characters. */
287  pix1 = pixMorphSequence(pixs, "c1.3", 0);
288 
289  /* Carefully filter out noise */
290  pix2 = recogPreSplittingFilter(recog, pix1, minh, MinFillFactor, debug);
291  pixDestroy(&pix1);
292 
293  /* Get the 8-connected components to be split/identified */
294  boxa1 = pixConnComp(pix2, NULL, 8);
295  pixDestroy(&pix2);
296  ncomp = boxaGetCount(boxa1);
297  if (ncomp == 0) {
298  boxaDestroy(&boxa1);
299  L_WARNING("all components removed\n", procName);
300  return 1;
301  }
302 
303  /* Save everything and split the large components */
304  boxa2 = boxaCreate(ncomp);
305  maxw = recog->maxwidth_u + 5;
306  scaling = (recog->scalew > 0 || recog->scaleh > 0) ? TRUE : FALSE;
307  pixa = (debug) ? pixaCreate(ncomp) : NULL;
308  for (i = 0; i < ncomp; i++) {
309  box = boxaGetBox(boxa1, i, L_CLONE);
310  boxGetGeometry(box, &xoff, &yoff, &bw, NULL);
311  /* Treat as one character if it is small, if the images
312  * have been scaled, or if splitting is not to be run. */
313  if (bw <= maxw || scaling || skipsplit) {
314  boxaAddBox(boxa2, box, L_INSERT);
315  } else {
316  pix = pixClipRectangle(pixs, box, NULL);
317 #if SPLIT_WITH_DID
318  if (!debug) {
319  boxa3 = recogDecode(recog, pix, 2, NULL);
320  } else {
321  boxa3 = recogDecode(recog, pix, 2, &pix2);
322  pixaAddPix(pixa, pix2, L_INSERT);
323  }
324 #else /* use greedy splitting */
325  recogCorrelationBestRow(recog, pix, &boxa3, NULL, NULL,
326  NULL, debug);
327  if (debug) {
328  pix2 = pixConvertTo32(pix);
329  pixRenderBoxaArb(pix2, boxa3, 2, 255, 0, 0);
330  pixaAddPix(pixa, pix2, L_INSERT);
331  }
332 #endif /* SPLIT_WITH_DID */
333  pixDestroy(&pix);
334  boxDestroy(&box);
335  if (!boxa3) {
336  L_ERROR("boxa3 not found for component %d\n", procName, i);
337  } else {
338  boxa4 = boxaTransform(boxa3, xoff, yoff, 1.0, 1.0);
339  boxaJoin(boxa2, boxa4, 0, -1);
340  boxaDestroy(&boxa3);
341  boxaDestroy(&boxa4);
342  }
343  }
344  }
345  boxaDestroy(&boxa1);
346  if (pixa) { /* debug */
347  pix3 = pixaDisplayTiledInColumns(pixa, 1, 1.0, 20, 2);
348  snprintf(buf, sizeof(buf), "/tmp/lept/recog/decode-%d.png", ind++);
349  pixWrite(buf, pix3, IFF_PNG);
350  pixaDestroy(&pixa);
351  pixDestroy(&pix3);
352  }
353 
354  /* Do a 2D sort on the bounding boxes, and flatten the result to 1D.
355  * For the 2D sort, to add a box to an existing boxa, we require
356  * specified minimum vertical overlaps for the first two passes
357  * of the 2D sort. In pass 1, only components with sufficient
358  * height can start a new boxa. */
359  baa = boxaSort2d(boxa2, NULL, MinOverlap1, MinOverlap2, MinHeightPass1);
360  boxa3 = boxaaFlattenToBoxa(baa, NULL, L_CLONE);
361  boxaaDestroy(&baa);
362  boxaDestroy(&boxa2);
363 
364  /* Remove smaller components of overlapping pairs.
365  * We only remove the small component if the overlap is
366  * at least half its area and if its area is no more
367  * than 30% of the area of the large component. Because the
368  * components are in a flattened 2D sort, we don't need to
369  * look far ahead in the array to find all overlapping boxes;
370  * 10 boxes is plenty. */
371  boxad = boxaHandleOverlaps(boxa3, L_COMBINE, 10, 0.5, 0.3, NULL);
372  boxaDestroy(&boxa3);
373 
374  /* Extract and save the image pieces from the input image. */
375  *ppixa = pixClipRectangles(pixs, boxad);
376  *pboxa = boxad;
377  return 0;
378 }
379 
380 
381 /*------------------------------------------------------------------------*
382  * Greedy character splitting *
383  *------------------------------------------------------------------------*/
404 l_ok
406  PIX *pixs,
407  BOXA **pboxa,
408  NUMA **pnascore,
409  NUMA **pnaindex,
410  SARRAY **psachar,
411  l_int32 debug)
412 {
413 char *charstr;
414 l_int32 index, remove, w, h, bx, bw, bxc, bwc, w1, w2, w3;
415 l_float32 score;
416 BOX *box, *boxc, *boxtrans, *boxl, *boxr, *boxlt, *boxrt;
417 BOXA *boxat;
418 NUMA *nascoret, *naindext, *nasort;
419 PIX *pixb, *pixc, *pixl, *pixr, *pixdb, *pixd;
420 PIXA *pixar, *pixadb;
421 SARRAY *sachart;
422 
423 l_int32 iter;
424 
425  PROCNAME("recogCorrelationBestRow");
426 
427  if (pnascore) *pnascore = NULL;
428  if (pnaindex) *pnaindex = NULL;
429  if (psachar) *psachar = NULL;
430  if (!pboxa)
431  return ERROR_INT("&boxa not defined", procName, 1);
432  *pboxa = NULL;
433  if (!recog)
434  return ERROR_INT("recog not defined", procName, 1);
435  if (!pixs || pixGetDepth(pixs) != 1)
436  return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
437  if (pixGetWidth(pixs) < recog->minwidth_u - 4)
438  return ERROR_INT("pixs too narrow", procName, 1);
439  if (!recog->train_done)
440  return ERROR_INT("training not finished", procName, 1);
441 
442  /* Binarize and crop to foreground if necessary */
443  pixb = recogProcessToIdentify(recog, pixs, 0);
444 
445  /* Initialize the arrays */
446  boxat = boxaCreate(4);
447  nascoret = numaCreate(4);
448  naindext = numaCreate(4);
449  sachart = sarrayCreate(4);
450  pixadb = (debug) ? pixaCreate(4) : NULL;
451 
452  /* Initialize the images remaining to be processed with the input.
453  * These are stored in pixar, which is used here as a queue,
454  * on which we only put image fragments that are large enough to
455  * contain at least one character. */
456  pixar = pixaCreate(1);
457  pixGetDimensions(pixb, &w, &h, NULL);
458  box = boxCreate(0, 0, w, h);
459  pixaAddPix(pixar, pixb, L_INSERT);
460  pixaAddBox(pixar, box, L_INSERT);
461 
462  /* Successively split on the best match until nothing is left.
463  * To be safe, we limit the search to 10 characters. */
464  for (iter = 0; iter < 11; iter++) {
465  if (pixaGetCount(pixar) == 0)
466  break;
467  if (iter == 10) {
468  L_WARNING("more than 10 chars; ending search\n", procName);
469  break;
470  }
471 
472  /* Pop one from the queue */
473  pixaRemovePixAndSave(pixar, 0, &pixc, &boxc);
474  boxGetGeometry(boxc, &bxc, NULL, &bwc, NULL);
475 
476  /* This is a single component; if noise, remove it */
477  recogSplittingFilter(recog, pixc, 0, MinFillFactor, &remove, debug);
478  if (debug)
479  lept_stderr("iter = %d, removed = %d\n", iter, remove);
480  if (remove) {
481  pixDestroy(&pixc);
482  boxDestroy(&boxc);
483  continue;
484  }
485 
486  /* Find the best character match */
487  if (debug) {
488  recogCorrelationBestChar(recog, pixc, &box, &score,
489  &index, &charstr, &pixdb);
490  pixaAddPix(pixadb, pixdb, L_INSERT);
491  } else {
492  recogCorrelationBestChar(recog, pixc, &box, &score,
493  &index, &charstr, NULL);
494  }
495 
496  /* Find the box in original coordinates, and append
497  * the results to the arrays. */
498  boxtrans = boxTransform(box, bxc, 0, 1.0, 1.0);
499  boxaAddBox(boxat, boxtrans, L_INSERT);
500  numaAddNumber(nascoret, score);
501  numaAddNumber(naindext, index);
502  sarrayAddString(sachart, charstr, L_INSERT);
503 
504  /* Split the current pixc into three regions and save
505  * each region if it is large enough. */
506  boxGetGeometry(box, &bx, NULL, &bw, NULL);
507  w1 = bx;
508  w2 = bw;
509  w3 = bwc - bx - bw;
510  if (debug)
511  lept_stderr(" w1 = %d, w2 = %d, w3 = %d\n", w1, w2, w3);
512  if (w1 < recog->minwidth_u - 4) {
513  if (debug) L_INFO("discarding width %d on left\n", procName, w1);
514  } else { /* extract and save left region */
515  boxl = boxCreate(0, 0, bx + 1, h);
516  pixl = pixClipRectangle(pixc, boxl, NULL);
517  boxlt = boxTransform(boxl, bxc, 0, 1.0, 1.0);
518  pixaAddPix(pixar, pixl, L_INSERT);
519  pixaAddBox(pixar, boxlt, L_INSERT);
520  boxDestroy(&boxl);
521  }
522  if (w3 < recog->minwidth_u - 4) {
523  if (debug) L_INFO("discarding width %d on right\n", procName, w3);
524  } else { /* extract and save left region */
525  boxr = boxCreate(bx + bw - 1, 0, w3 + 1, h);
526  pixr = pixClipRectangle(pixc, boxr, NULL);
527  boxrt = boxTransform(boxr, bxc, 0, 1.0, 1.0);
528  pixaAddPix(pixar, pixr, L_INSERT);
529  pixaAddBox(pixar, boxrt, L_INSERT);
530  boxDestroy(&boxr);
531  }
532  pixDestroy(&pixc);
533  boxDestroy(&box);
534  boxDestroy(&boxc);
535  }
536  pixaDestroy(&pixar);
537 
538 
539  /* Sort the output results by left-to-right in the boxa */
540  *pboxa = boxaSort(boxat, L_SORT_BY_X, L_SORT_INCREASING, &nasort);
541  if (pnascore)
542  *pnascore = numaSortByIndex(nascoret, nasort);
543  if (pnaindex)
544  *pnaindex = numaSortByIndex(naindext, nasort);
545  if (psachar)
546  *psachar = sarraySortByIndex(sachart, nasort);
547  numaDestroy(&nasort);
548  boxaDestroy(&boxat);
549  numaDestroy(&nascoret);
550  numaDestroy(&naindext);
551  sarrayDestroy(&sachart);
552 
553  /* Final debug output */
554  if (debug) {
555  pixd = pixaDisplayTiledInRows(pixadb, 32, 2000, 1.0, 0, 15, 2);
556  pixDisplay(pixd, 400, 400);
557  pixaAddPix(recog->pixadb_split, pixd, L_INSERT);
558  pixaDestroy(&pixadb);
559  }
560  return 0;
561 }
562 
563 
586 l_ok
588  PIX *pixs,
589  BOX **pbox,
590  l_float32 *pscore,
591  l_int32 *pindex,
592  char **pcharstr,
593  PIX **ppixdb)
594 {
595 l_int32 i, n, w1, h1, w2, area2, ycent2, delx, dely;
596 l_int32 bestdelx, bestdely, bestindex;
597 l_float32 score, bestscore;
598 BOX *box;
599 BOXA *boxa;
600 NUMA *nasum, *namoment;
601 PIX *pix1, *pix2;
602 
603  PROCNAME("recogCorrelationBestChar");
604 
605  if (pindex) *pindex = 0;
606  if (pcharstr) *pcharstr = NULL;
607  if (ppixdb) *ppixdb = NULL;
608  if (pbox) *pbox = NULL;
609  if (pscore) *pscore = 0.0;
610  if (!pbox || !pscore)
611  return ERROR_INT("&box and &score not both defined", procName, 1);
612  if (!recog)
613  return ERROR_INT("recog not defined", procName, 1);
614  if (!pixs || pixGetDepth(pixs) != 1)
615  return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
616  if (!recog->train_done)
617  return ERROR_INT("training not finished", procName, 1);
618 
619  /* Binarize and crop to foreground if necessary. Add padding
620  * to both the left and right side; this is compensated for
621  * when reporting the bounding box of the best matched character. */
622  pix1 = recogProcessToIdentify(recog, pixs, LeftRightPadding);
623  pixGetDimensions(pix1, &w1, &h1, NULL);
624 
625  /* Compute vertical sum and moment arrays */
626  nasum = pixCountPixelsByColumn(pix1);
627  namoment = pixGetMomentByColumn(pix1, 1);
628 
629  /* Do shifted correlation against all averaged templates. */
630  n = recog->setsize;
631  boxa = boxaCreate(n); /* location of best fits for each character */
632  bestscore = 0.0;
633  bestindex = bestdelx = bestdely = 0;
634  for (i = 0; i < n; i++) {
635  pix2 = pixaGetPix(recog->pixa_u, i, L_CLONE);
636  w2 = pixGetWidth(pix2);
637  /* Note that the slightly expended w1 is typically larger
638  * than w2 (the template). */
639  if (w1 >= w2) {
640  numaGetIValue(recog->nasum_u, i, &area2);
641  ptaGetIPt(recog->pta_u, i, NULL, &ycent2);
642  pixCorrelationBestShift(pix1, pix2, nasum, namoment, area2, ycent2,
643  recog->maxyshift, recog->sumtab, &delx,
644  &dely, &score, 1);
645  if (ppixdb) {
646  lept_stderr(
647  "Best match template %d: (x,y) = (%d,%d), score = %5.3f\n",
648  i, delx, dely, score);
649  }
650  /* Compensate for padding */
651  box = boxCreate(delx - LeftRightPadding, 0, w2, h1);
652  if (score > bestscore) {
653  bestscore = score;
654  bestdelx = delx - LeftRightPadding;
655  bestdely = dely;
656  bestindex = i;
657  }
658  } else {
659  box = boxCreate(0, 0, 1, 1); /* placeholder */
660  if (ppixdb)
661  lept_stderr("Component too thin: w1 = %d, w2 = %d\n", w1, w2);
662  }
663  boxaAddBox(boxa, box, L_INSERT);
664  pixDestroy(&pix2);
665  }
666 
667  *pscore = bestscore;
668  *pbox = boxaGetBox(boxa, bestindex, L_COPY);
669  if (pindex) *pindex = bestindex;
670  if (pcharstr)
671  recogGetClassString(recog, bestindex, pcharstr);
672 
673  if (ppixdb) {
674  L_INFO("Best match: class %d; shifts (%d, %d)\n",
675  procName, bestindex, bestdelx, bestdely);
676  pix2 = pixaGetPix(recog->pixa_u, bestindex, L_CLONE);
677  *ppixdb = recogShowMatch(recog, pix1, pix2, NULL, -1, 0.0);
678  pixDestroy(&pix2);
679  }
680 
681  pixDestroy(&pix1);
682  boxaDestroy(&boxa);
683  numaDestroy(&nasum);
684  numaDestroy(&namoment);
685  return 0;
686 }
687 
688 
728 static l_int32
730  PIX *pix2,
731  NUMA *nasum1,
732  NUMA *namoment1,
733  l_int32 area2,
734  l_int32 ycent2,
735  l_int32 maxyshift,
736  l_int32 *tab8,
737  l_int32 *pdelx,
738  l_int32 *pdely,
739  l_float32 *pscore,
740  l_int32 debugflag)
741 {
742 l_int32 w1, w2, h1, h2, i, j, nx, shifty, delx, dely;
743 l_int32 sum, moment, count;
744 l_int32 *tab, *area1, *arraysum, *arraymoment;
745 l_float32 maxscore, score;
746 l_float32 *ycent1;
747 FPIX *fpix;
748 PIX *pixt, *pixt1, *pixt2;
749 
750  PROCNAME("pixCorrelationBestShift");
751 
752  if (pdelx) *pdelx = 0;
753  if (pdely) *pdely = 0;
754  if (pscore) *pscore = 0.0;
755  if (!pix1 || pixGetDepth(pix1) != 1)
756  return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1);
757  if (!pix2 || pixGetDepth(pix2) != 1)
758  return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1);
759  if (!nasum1 || !namoment1)
760  return ERROR_INT("nasum1 and namoment1 not both defined", procName, 1);
761  if (area2 <= 0 || ycent2 <= 0)
762  return ERROR_INT("area2 and ycent2 must be > 0", procName, 1);
763 
764  /* If pix1 (the unknown image) is narrower than pix2,
765  * don't bother to try the match. pix1 is already padded with
766  * 2 pixels on each side. */
767  pixGetDimensions(pix1, &w1, &h1, NULL);
768  pixGetDimensions(pix2, &w2, &h2, NULL);
769  if (w1 < w2) {
770  if (debugflag > 0) {
771  L_INFO("skipping match with w1 = %d and w2 = %d\n",
772  procName, w1, w2);
773  }
774  return 0;
775  }
776  nx = w1 - w2 + 1;
777 
778  if (debugflag > 0)
779  fpix = fpixCreate(nx, 2 * maxyshift + 1);
780  if (!tab8)
781  tab = makePixelSumTab8();
782  else
783  tab = tab8;
784 
785  /* Set up the arrays for area1 and ycent1. We have to do this
786  * for each template (pix2) because the window width is w2. */
787  area1 = (l_int32 *)LEPT_CALLOC(nx, sizeof(l_int32));
788  ycent1 = (l_float32 *)LEPT_CALLOC(nx, sizeof(l_int32));
789  arraysum = numaGetIArray(nasum1);
790  arraymoment = numaGetIArray(namoment1);
791  for (i = 0, sum = 0, moment = 0; i < w2; i++) {
792  sum += arraysum[i];
793  moment += arraymoment[i];
794  }
795  for (i = 0; i < nx - 1; i++) {
796  area1[i] = sum;
797  ycent1[i] = (sum == 0) ? ycent2 : (l_float32)moment / (l_float32)sum;
798  sum += arraysum[w2 + i] - arraysum[i];
799  moment += arraymoment[w2 + i] - arraymoment[i];
800  }
801  area1[nx - 1] = sum;
802  ycent1[nx - 1] = (sum == 0) ? ycent2 : (l_float32)moment / (l_float32)sum;
803 
804  /* Find the best match location for pix2. At each location,
805  * to insure that pixels are ON only within the intersection of
806  * pix and the shifted pix2:
807  * (1) Start with pixt cleared and equal in size to pix1.
808  * (2) Blit the shifted pix2 onto pixt. Then all ON pixels
809  * are within the intersection of pix1 and the shifted pix2.
810  * (3) AND pix1 with pixt. */
811  pixt = pixCreate(w2, h1, 1);
812  maxscore = 0;
813  delx = 0;
814  dely = 0; /* amount to shift pix2 relative to pix1 to get alignment */
815  for (i = 0; i < nx; i++) {
816  shifty = (l_int32)(ycent1[i] - ycent2 + 0.5);
817  for (j = -maxyshift; j <= maxyshift; j++) {
818  pixClearAll(pixt);
819  pixRasterop(pixt, 0, shifty + j, w2, h2, PIX_SRC, pix2, 0, 0);
820  pixRasterop(pixt, 0, 0, w2, h1, PIX_SRC & PIX_DST, pix1, i, 0);
821  pixCountPixels(pixt, &count, tab);
822  score = (l_float32)count * (l_float32)count /
823  ((l_float32)area1[i] * (l_float32)area2);
824  if (score > maxscore) {
825  maxscore = score;
826  delx = i;
827  dely = shifty + j;
828  }
829 
830  if (debugflag > 0)
831  fpixSetPixel(fpix, i, maxyshift + j, 1000.0 * score);
832  }
833  }
834 
835  if (debugflag > 0) {
836  char buf[128];
837  lept_mkdir("lept/recog");
838  pixt1 = fpixDisplayMaxDynamicRange(fpix);
839  pixt2 = pixExpandReplicate(pixt1, 5);
840  snprintf(buf, sizeof(buf), "/tmp/lept/recog/junkbs_%d.png", debugflag);
841  pixWrite(buf, pixt2, IFF_PNG);
842  pixDestroy(&pixt1);
843  pixDestroy(&pixt2);
844  fpixDestroy(&fpix);
845  }
846 
847  if (pdelx) *pdelx = delx;
848  if (pdely) *pdely = dely;
849  if (pscore) *pscore = maxscore;
850  if (!tab8) LEPT_FREE(tab);
851  LEPT_FREE(area1);
852  LEPT_FREE(ycent1);
853  LEPT_FREE(arraysum);
854  LEPT_FREE(arraymoment);
855  pixDestroy(&pixt);
856  return 0;
857 }
858 
859 
860 /*------------------------------------------------------------------------*
861  * Low-level identification *
862  *------------------------------------------------------------------------*/
881 l_ok
883  PIXA *pixa,
884  PIX **ppixdb)
885 {
886 char *text;
887 l_int32 i, n, fail, index, depth;
888 l_float32 score;
889 PIX *pix1, *pix2, *pix3;
890 PIXA *pixa1;
891 L_RCH *rch;
892 
893  PROCNAME("recogIdentifyPixa");
894 
895  if (ppixdb) *ppixdb = NULL;
896  if (!recog)
897  return ERROR_INT("recog not defined", procName, 1);
898  if (!pixa)
899  return ERROR_INT("pixa not defined", procName, 1);
900 
901  /* Run the recognizer on the set of images. This writes
902  * the text string into each pix in pixa. */
903  n = pixaGetCount(pixa);
904  rchaDestroy(&recog->rcha);
905  recog->rcha = rchaCreate();
906  pixa1 = (ppixdb) ? pixaCreate(n) : NULL;
907  depth = 1;
908  for (i = 0; i < n; i++) {
909  pix1 = pixaGetPix(pixa, i, L_CLONE);
910  pix2 = NULL;
911  fail = FALSE;
912  if (!ppixdb)
913  fail = recogIdentifyPix(recog, pix1, NULL);
914  else
915  fail = recogIdentifyPix(recog, pix1, &pix2);
916  if (fail)
917  recogSkipIdentify(recog);
918  if ((rch = recog->rch) == NULL) {
919  L_ERROR("rch not found for char %d\n", procName, i);
920  pixDestroy(&pix1);
921  pixDestroy(&pix2);
922  continue;
923  }
924  rchExtract(rch, NULL, NULL, &text, NULL, NULL, NULL, NULL);
925  pixSetText(pix1, text);
926  LEPT_FREE(text);
927  if (ppixdb) {
928  rchExtract(rch, &index, &score, NULL, NULL, NULL, NULL, NULL);
929  pix3 = recogShowMatch(recog, pix2, NULL, NULL, index, score);
930  if (i == 0) depth = pixGetDepth(pix3);
931  pixaAddPix(pixa1, pix3, L_INSERT);
932  pixDestroy(&pix2);
933  }
934  transferRchToRcha(rch, recog->rcha);
935  pixDestroy(&pix1);
936  }
937 
938  /* Package the images for debug */
939  if (ppixdb) {
940  *ppixdb = pixaDisplayTiledInRows(pixa1, depth, 2500, 1.0, 0, 20, 1);
941  pixaDestroy(&pixa1);
942  }
943 
944  return 0;
945 }
946 
947 
974 l_ok
976  PIX *pixs,
977  PIX **ppixdb)
978 {
979 char *text;
980 l_int32 i, j, n, bestindex, bestsample, area1, area2;
981 l_int32 shiftx, shifty, bestdelx, bestdely, bestwidth, maxyshift;
982 l_float32 x1, y1, x2, y2, delx, dely, score, maxscore;
983 NUMA *numa;
984 PIX *pix0, *pix1, *pix2;
985 PIXA *pixa;
986 PTA *pta;
987 
988  PROCNAME("recogIdentifyPix");
989 
990  if (ppixdb) *ppixdb = NULL;
991  if (!recog)
992  return ERROR_INT("recog not defined", procName, 1);
993  if (!pixs || pixGetDepth(pixs) != 1)
994  return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
995 
996  /* Do the averaging if required and not yet done. */
997  if (recog->templ_use == L_USE_AVERAGE_TEMPLATES && !recog->ave_done) {
998  recogAverageSamples(&recog, 0);
999  if (!recog)
1000  return ERROR_INT("averaging failed", procName, 1);
1001  }
1002 
1003  /* Binarize and crop to foreground if necessary */
1004  if ((pix0 = recogProcessToIdentify(recog, pixs, 0)) == NULL)
1005  return ERROR_INT("no fg pixels in pix0", procName, 1);
1006 
1007  /* Optionally scale and/or convert to fixed stroke width */
1008  pix1 = recogModifyTemplate(recog, pix0);
1009  pixDestroy(&pix0);
1010  if (!pix1)
1011  return ERROR_INT("no fg pixels in pix1", procName, 1);
1012 
1013  /* Do correlation at all positions within +-maxyshift of
1014  * the nominal centroid alignment. */
1015  pixCountPixels(pix1, &area1, recog->sumtab);
1016  pixCentroid(pix1, recog->centtab, recog->sumtab, &x1, &y1);
1017  bestindex = bestsample = bestdelx = bestdely = bestwidth = 0;
1018  maxscore = 0.0;
1019  maxyshift = recog->maxyshift;
1020  if (recog->templ_use == L_USE_AVERAGE_TEMPLATES) {
1021  for (i = 0; i < recog->setsize; i++) {
1022  numaGetIValue(recog->nasum, i, &area2);
1023  if (area2 == 0) continue; /* no template available */
1024  pix2 = pixaGetPix(recog->pixa, i, L_CLONE);
1025  ptaGetPt(recog->pta, i, &x2, &y2);
1026  delx = x1 - x2;
1027  dely = y1 - y2;
1028  for (shifty = -maxyshift; shifty <= maxyshift; shifty++) {
1029  for (shiftx = -maxyshift; shiftx <= maxyshift; shiftx++) {
1030  pixCorrelationScoreSimple(pix1, pix2, area1, area2,
1031  delx + shiftx, dely + shifty,
1032  5, 5, recog->sumtab, &score);
1033  if (score > maxscore) {
1034  bestindex = i;
1035  bestdelx = delx + shiftx;
1036  bestdely = dely + shifty;
1037  maxscore = score;
1038  }
1039  }
1040  }
1041  pixDestroy(&pix2);
1042  }
1043  } else { /* use all the samples */
1044  for (i = 0; i < recog->setsize; i++) {
1045  pixa = pixaaGetPixa(recog->pixaa, i, L_CLONE);
1046  n = pixaGetCount(pixa);
1047  if (n == 0) {
1048  pixaDestroy(&pixa);
1049  continue;
1050  }
1051  numa = numaaGetNuma(recog->naasum, i, L_CLONE);
1052  pta = ptaaGetPta(recog->ptaa, i, L_CLONE);
1053  for (j = 0; j < n; j++) {
1054  pix2 = pixaGetPix(pixa, j, L_CLONE);
1055  numaGetIValue(numa, j, &area2);
1056  ptaGetPt(pta, j, &x2, &y2);
1057  delx = x1 - x2;
1058  dely = y1 - y2;
1059  for (shifty = -maxyshift; shifty <= maxyshift; shifty++) {
1060  for (shiftx = -maxyshift; shiftx <= maxyshift; shiftx++) {
1061  pixCorrelationScoreSimple(pix1, pix2, area1, area2,
1062  delx + shiftx, dely + shifty,
1063  5, 5, recog->sumtab, &score);
1064  if (score > maxscore) {
1065  bestindex = i;
1066  bestsample = j;
1067  bestdelx = delx + shiftx;
1068  bestdely = dely + shifty;
1069  maxscore = score;
1070  bestwidth = pixGetWidth(pix2);
1071  }
1072  }
1073  }
1074  pixDestroy(&pix2);
1075  }
1076  pixaDestroy(&pixa);
1077  numaDestroy(&numa);
1078  ptaDestroy(&pta);
1079  }
1080  }
1081 
1082  /* Package up the results */
1083  recogGetClassString(recog, bestindex, &text);
1084  rchDestroy(&recog->rch);
1085  recog->rch = rchCreate(bestindex, maxscore, text, bestsample,
1086  bestdelx, bestdely, bestwidth);
1087 
1088  if (ppixdb) {
1089  if (recog->templ_use == L_USE_AVERAGE_TEMPLATES) {
1090  L_INFO("Best match: str %s; class %d; sh (%d, %d); score %5.3f\n",
1091  procName, text, bestindex, bestdelx, bestdely, maxscore);
1092  pix2 = pixaGetPix(recog->pixa, bestindex, L_CLONE);
1093  } else { /* L_USE_ALL_TEMPLATES */
1094  L_INFO("Best match: str %s; sample %d in class %d; score %5.3f\n",
1095  procName, text, bestsample, bestindex, maxscore);
1096  if (maxyshift > 0 && (L_ABS(bestdelx) > 0 || L_ABS(bestdely) > 0)) {
1097  L_INFO(" Best shift: (%d, %d)\n",
1098  procName, bestdelx, bestdely);
1099  }
1100  pix2 = pixaaGetPix(recog->pixaa, bestindex, bestsample, L_CLONE);
1101  }
1102  *ppixdb = recogShowMatch(recog, pix1, pix2, NULL, -1, 0.0);
1103  pixDestroy(&pix2);
1104  }
1105 
1106  pixDestroy(&pix1);
1107  return 0;
1108 }
1109 
1110 
1123 l_ok
1125 {
1126  PROCNAME("recogSkipIdentify");
1127 
1128  if (!recog)
1129  return ERROR_INT("recog not defined", procName, 1);
1130 
1131  /* Package up placeholder results */
1132  rchDestroy(&recog->rch);
1133  recog->rch = rchCreate(0, 0.0, stringNew(""), 0, 0, 0, 0);
1134  return 0;
1135 }
1136 
1137 
1138 /*------------------------------------------------------------------------*
1139  * Operations for handling identification results *
1140  *------------------------------------------------------------------------*/
1149 static L_RCHA *
1151 {
1152 L_RCHA *rcha;
1153 
1154  rcha = (L_RCHA *)LEPT_CALLOC(1, sizeof(L_RCHA));
1155  rcha->naindex = numaCreate(0);
1156  rcha->nascore = numaCreate(0);
1157  rcha->satext = sarrayCreate(0);
1158  rcha->nasample = numaCreate(0);
1159  rcha->naxloc = numaCreate(0);
1160  rcha->nayloc = numaCreate(0);
1161  rcha->nawidth = numaCreate(0);
1162  return rcha;
1163 }
1164 
1165 
1171 void
1173 {
1174 L_RCHA *rcha;
1175 
1176  PROCNAME("rchaDestroy");
1177 
1178  if (prcha == NULL) {
1179  L_WARNING("&rcha is null!\n", procName);
1180  return;
1181  }
1182  if ((rcha = *prcha) == NULL)
1183  return;
1184 
1185  numaDestroy(&rcha->naindex);
1186  numaDestroy(&rcha->nascore);
1187  sarrayDestroy(&rcha->satext);
1188  numaDestroy(&rcha->nasample);
1189  numaDestroy(&rcha->naxloc);
1190  numaDestroy(&rcha->nayloc);
1191  numaDestroy(&rcha->nawidth);
1192  LEPT_FREE(rcha);
1193  *prcha = NULL;
1194 }
1195 
1196 
1216 static L_RCH *
1217 rchCreate(l_int32 index,
1218  l_float32 score,
1219  char *text,
1220  l_int32 sample,
1221  l_int32 xloc,
1222  l_int32 yloc,
1223  l_int32 width)
1224 {
1225 L_RCH *rch;
1226 
1227  rch = (L_RCH *)LEPT_CALLOC(1, sizeof(L_RCH));
1228  rch->index = index;
1229  rch->score = score;
1230  rch->text = text;
1231  rch->sample = sample;
1232  rch->xloc = xloc;
1233  rch->yloc = yloc;
1234  rch->width = width;
1235  return rch;
1236 }
1237 
1238 
1244 void
1246 {
1247 L_RCH *rch;
1248 
1249  PROCNAME("rchDestroy");
1250 
1251  if (prch == NULL) {
1252  L_WARNING("&rch is null!\n", procName);
1253  return;
1254  }
1255  if ((rch = *prch) == NULL)
1256  return;
1257  LEPT_FREE(rch->text);
1258  LEPT_FREE(rch);
1259  *prch = NULL;
1260 }
1261 
1262 
1282 l_ok
1284  NUMA **pnaindex,
1285  NUMA **pnascore,
1286  SARRAY **psatext,
1287  NUMA **pnasample,
1288  NUMA **pnaxloc,
1289  NUMA **pnayloc,
1290  NUMA **pnawidth)
1291 {
1292  PROCNAME("rchaExtract");
1293 
1294  if (pnaindex) *pnaindex = NULL;
1295  if (pnascore) *pnascore = NULL;
1296  if (psatext) *psatext = NULL;
1297  if (pnasample) *pnasample = NULL;
1298  if (pnaxloc) *pnaxloc = NULL;
1299  if (pnayloc) *pnayloc = NULL;
1300  if (pnawidth) *pnawidth = NULL;
1301  if (!rcha)
1302  return ERROR_INT("rcha not defined", procName, 1);
1303 
1304  if (pnaindex) *pnaindex = numaClone(rcha->naindex);
1305  if (pnascore) *pnascore = numaClone(rcha->nascore);
1306  if (psatext) *psatext = sarrayClone(rcha->satext);
1307  if (pnasample) *pnasample = numaClone(rcha->nasample);
1308  if (pnaxloc) *pnaxloc = numaClone(rcha->naxloc);
1309  if (pnayloc) *pnayloc = numaClone(rcha->nayloc);
1310  if (pnawidth) *pnawidth = numaClone(rcha->nawidth);
1311  return 0;
1312 }
1313 
1314 
1328 l_ok
1330  l_int32 *pindex,
1331  l_float32 *pscore,
1332  char **ptext,
1333  l_int32 *psample,
1334  l_int32 *pxloc,
1335  l_int32 *pyloc,
1336  l_int32 *pwidth)
1337 {
1338  PROCNAME("rchExtract");
1339 
1340  if (pindex) *pindex = 0;
1341  if (pscore) *pscore = 0.0;
1342  if (ptext) *ptext = NULL;
1343  if (psample) *psample = 0;
1344  if (pxloc) *pxloc = 0;
1345  if (pyloc) *pyloc = 0;
1346  if (pwidth) *pwidth = 0;
1347  if (!rch)
1348  return ERROR_INT("rch not defined", procName, 1);
1349 
1350  if (pindex) *pindex = rch->index;
1351  if (pscore) *pscore = rch->score;
1352  if (ptext) *ptext = stringNew(rch->text); /* new string: owned by caller */
1353  if (psample) *psample = rch->sample;
1354  if (pxloc) *pxloc = rch->xloc;
1355  if (pyloc) *pyloc = rch->yloc;
1356  if (pwidth) *pwidth = rch->width;
1357  return 0;
1358 }
1359 
1360 
1374 static l_int32
1376  L_RCHA *rcha)
1377 {
1378 
1379  PROCNAME("transferRchToRcha");
1380 
1381  if (!rch)
1382  return ERROR_INT("rch not defined", procName, 1);
1383  if (!rcha)
1384  return ERROR_INT("rcha not defined", procName, 1);
1385 
1386  numaAddNumber(rcha->naindex, rch->index);
1387  numaAddNumber(rcha->nascore, rch->score);
1388  sarrayAddString(rcha->satext, rch->text, L_COPY);
1389  numaAddNumber(rcha->nasample, rch->sample);
1390  numaAddNumber(rcha->naxloc, rch->xloc);
1391  numaAddNumber(rcha->nayloc, rch->yloc);
1392  numaAddNumber(rcha->nawidth, rch->width);
1393  return 0;
1394 }
1395 
1396 
1397 /*------------------------------------------------------------------------*
1398  * Preprocessing and filtering *
1399  *------------------------------------------------------------------------*/
1416 PIX *
1418  PIX *pixs,
1419  l_int32 pad)
1420 {
1421 l_int32 canclip;
1422 PIX *pix1, *pix2, *pixd;
1423 
1424  PROCNAME("recogProcessToIdentify");
1425 
1426  if (!recog)
1427  return (PIX *)ERROR_PTR("recog not defined", procName, NULL);
1428  if (!pixs)
1429  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1430 
1431  if (pixGetDepth(pixs) != 1)
1432  pix1 = pixThresholdToBinary(pixs, recog->threshold);
1433  else
1434  pix1 = pixClone(pixs);
1435  pixTestClipToForeground(pix1, &canclip);
1436  if (canclip)
1437  pixClipToForeground(pix1, &pix2, NULL);
1438  else
1439  pix2 = pixClone(pix1);
1440  pixDestroy(&pix1);
1441  if (!pix2)
1442  return (PIX *)ERROR_PTR("no foreground pixels", procName, NULL);
1443 
1444  pixd = pixAddBorderGeneral(pix2, pad, pad, 0, 0, 0);
1445  pixDestroy(&pix2);
1446  return pixd;
1447 }
1448 
1449 
1460 static PIX *
1462  PIX *pixs,
1463  l_int32 minh,
1464  l_float32 minaf,
1465  l_int32 debug)
1466 {
1467 l_int32 scaling, minsplitw, maxsplith, maxasp;
1468 BOXA *boxas;
1469 NUMA *naw, *nah, *na1, *na1c, *na2, *na3, *na4, *na5, *na6, *na7;
1470 PIX *pixd;
1471 PIXA *pixas;
1472 
1473  PROCNAME("recogPreSplittingFilter");
1474 
1475  if (!recog)
1476  return (PIX *)ERROR_PTR("recog not defined", procName, NULL);
1477  if (!pixs)
1478  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1479 
1480  /* If there is scaling, do not remove components based on the
1481  * values of min_splitw and max_splith. */
1482  scaling = (recog->scalew > 0 || recog->scaleh > 0) ? TRUE : FALSE;
1483  minsplitw = (scaling) ? 1 : recog->min_splitw - 3;
1484  maxsplith = (scaling) ? 150 : recog->max_splith;
1485  maxasp = recog->max_wh_ratio;
1486 
1487  /* Generate an indicator array of connected components to remove:
1488  * short stuff
1489  * tall stuff
1490  * components with large width/height ratio
1491  * components with small area fill fraction */
1492  boxas = pixConnComp(pixs, &pixas, 8);
1493  pixaFindDimensions(pixas, &naw, &nah);
1494  na1 = numaMakeThresholdIndicator(naw, minsplitw, L_SELECT_IF_LT);
1495  na1c = numaCopy(na1);
1496  na2 = numaMakeThresholdIndicator(nah, minh, L_SELECT_IF_LT);
1497  na3 = numaMakeThresholdIndicator(nah, maxsplith, L_SELECT_IF_GT);
1498  na4 = pixaFindWidthHeightRatio(pixas);
1499  na5 = numaMakeThresholdIndicator(na4, maxasp, L_SELECT_IF_GT);
1500  na6 = pixaFindAreaFraction(pixas);
1501  na7 = numaMakeThresholdIndicator(na6, minaf, L_SELECT_IF_LT);
1502  numaLogicalOp(na1, na1, na2, L_UNION);
1503  numaLogicalOp(na1, na1, na3, L_UNION);
1504  numaLogicalOp(na1, na1, na5, L_UNION);
1505  numaLogicalOp(na1, na1, na7, L_UNION);
1506  pixd = pixCopy(NULL, pixs);
1507  pixRemoveWithIndicator(pixd, pixas, na1);
1508  if (debug)
1509  l_showIndicatorSplitValues(na1c, na2, na3, na5, na7, na1);
1510  numaDestroy(&naw);
1511  numaDestroy(&nah);
1512  numaDestroy(&na1);
1513  numaDestroy(&na1c);
1514  numaDestroy(&na2);
1515  numaDestroy(&na3);
1516  numaDestroy(&na4);
1517  numaDestroy(&na5);
1518  numaDestroy(&na6);
1519  numaDestroy(&na7);
1520  boxaDestroy(&boxas);
1521  pixaDestroy(&pixas);
1522  return pixd;
1523 }
1524 
1525 
1537 static l_int32
1539  PIX *pixs,
1540  l_int32 minh,
1541  l_float32 minaf,
1542  l_int32 *premove,
1543  l_int32 debug)
1544 {
1545 l_int32 w, h;
1546 l_float32 aspratio, fract;
1547 
1548  PROCNAME("recogSplittingFilter");
1549 
1550  if (!premove)
1551  return ERROR_INT("&remove not defined", procName, 1);
1552  *premove = 0;
1553  if (!recog)
1554  return ERROR_INT("recog not defined", procName, 1);
1555  if (!pixs)
1556  return ERROR_INT("pixs not defined", procName, 1);
1557  if (minh <= 0) minh = DefaultMinHeight;
1558 
1559  /* Remove from further consideration:
1560  * small stuff
1561  * components with large width/height ratio
1562  * components with small area fill fraction */
1563  pixGetDimensions(pixs, &w, &h, NULL);
1564  if (w < recog->min_splitw) {
1565  if (debug) L_INFO("w = %d < %d\n", procName, w, recog->min_splitw);
1566  *premove = 1;
1567  return 0;
1568  }
1569  if (h < minh) {
1570  if (debug) L_INFO("h = %d < %d\n", procName, h, minh);
1571  *premove = 1;
1572  return 0;
1573  }
1574  aspratio = (l_float32)w / (l_float32)h;
1575  if (aspratio > recog->max_wh_ratio) {
1576  if (debug) L_INFO("w/h = %5.3f too large\n", procName, aspratio);
1577  *premove = 1;
1578  return 0;
1579  }
1580  pixFindAreaFraction(pixs, recog->sumtab, &fract);
1581  if (fract < minaf) {
1582  if (debug) L_INFO("area fill fract %5.3f < %5.3f\n",
1583  procName, fract, minaf);
1584  *premove = 1;
1585  return 0;
1586  }
1587 
1588  return 0;
1589 }
1590 
1591 
1592 /*------------------------------------------------------------------------*
1593  * Postprocessing *
1594  *------------------------------------------------------------------------*/
1629 SARRAY *
1631  BOXA *boxas,
1632  l_float32 scorethresh,
1633  l_int32 spacethresh,
1634  BOXAA **pbaa,
1635  NUMAA **pnaa)
1636 {
1637 char *str, *text;
1638 l_int32 i, n, x1, x2, h_ovl, v_ovl, h_sep, v_sep;
1639 l_float32 score;
1640 BOX *box, *prebox;
1641 BOXA *ba;
1642 BOXAA *baa;
1643 NUMA *nascore, *na;
1644 NUMAA *naa;
1645 SARRAY *satext, *sa, *saout;
1646 
1647  PROCNAME("recogExtractNumbers");
1648 
1649  if (pbaa) *pbaa = NULL;
1650  if (pnaa) *pnaa = NULL;
1651  if (!recog || !recog->rcha)
1652  return (SARRAY *)ERROR_PTR("recog and rcha not both defined",
1653  procName, NULL);
1654  if (!boxas)
1655  return (SARRAY *)ERROR_PTR("boxas not defined", procName, NULL);
1656 
1657  if (spacethresh < 0)
1658  spacethresh = L_MAX(recog->maxheight_u, 20);
1659  rchaExtract(recog->rcha, NULL, &nascore, &satext, NULL, NULL, NULL, NULL);
1660  if (!nascore || !satext) {
1661  numaDestroy(&nascore);
1662  sarrayDestroy(&satext);
1663  return (SARRAY *)ERROR_PTR("nascore and satext not both returned",
1664  procName, NULL);
1665  }
1666 
1667  saout = sarrayCreate(0);
1668  naa = numaaCreate(0);
1669  baa = boxaaCreate(0);
1670  prebox = NULL;
1671  n = numaGetCount(nascore);
1672  for (i = 0; i < n; i++) {
1673  numaGetFValue(nascore, i, &score);
1674  text = sarrayGetString(satext, i, L_NOCOPY);
1675  if (prebox == NULL) { /* no current run */
1676  if (score < scorethresh) {
1677  continue;
1678  } else { /* start a number run */
1679  sa = sarrayCreate(0);
1680  ba = boxaCreate(0);
1681  na = numaCreate(0);
1682  sarrayAddString(sa, text, L_COPY);
1683  prebox = boxaGetBox(boxas, i, L_CLONE);
1684  boxaAddBox(ba, prebox, L_COPY);
1685  numaAddNumber(na, score);
1686  }
1687  } else { /* in a current number run */
1688  box = boxaGetBox(boxas, i, L_CLONE);
1689  boxGetGeometry(prebox, &x1, NULL, NULL, NULL);
1690  boxGetGeometry(box, &x2, NULL, NULL, NULL);
1691  boxOverlapDistance(box, prebox, &h_ovl, &v_ovl);
1692  h_sep = -h_ovl;
1693  v_sep = -v_ovl;
1694  boxDestroy(&prebox);
1695  if (x1 < x2 && h_sep <= spacethresh &&
1696  v_sep < 0 && score >= scorethresh) { /* add to number */
1697  sarrayAddString(sa, text, L_COPY);
1698  boxaAddBox(ba, box, L_COPY);
1699  numaAddNumber(na, score);
1700  prebox = box;
1701  } else { /* save the completed number */
1702  str = sarrayToString(sa, 0);
1703  sarrayAddString(saout, str, L_INSERT);
1704  sarrayDestroy(&sa);
1705  boxaaAddBoxa(baa, ba, L_INSERT);
1706  numaaAddNuma(naa, na, L_INSERT);
1707  boxDestroy(&box);
1708  if (score >= scorethresh) { /* start a new number */
1709  i--;
1710  continue;
1711  }
1712  }
1713  }
1714  }
1715 
1716  if (prebox) { /* save the last number */
1717  str = sarrayToString(sa, 0);
1718  sarrayAddString(saout, str, L_INSERT);
1719  boxaaAddBoxa(baa, ba, L_INSERT);
1720  numaaAddNuma(naa, na, L_INSERT);
1721  sarrayDestroy(&sa);
1722  boxDestroy(&prebox);
1723  }
1724 
1725  numaDestroy(&nascore);
1726  sarrayDestroy(&satext);
1727  if (sarrayGetCount(saout) == 0) {
1728  sarrayDestroy(&saout);
1729  boxaaDestroy(&baa);
1730  numaaDestroy(&naa);
1731  L_INFO("saout has no identified text\n", procName);
1732  return NULL;
1733  }
1734 
1735  if (pbaa)
1736  *pbaa = baa;
1737  else
1738  boxaaDestroy(&baa);
1739  if (pnaa)
1740  *pnaa = naa;
1741  else
1742  numaaDestroy(&naa);
1743  return saout;
1744 }
1745 
1764 PIXA *
1766  SARRAY *sa,
1767  BOXAA *baa,
1768  NUMAA *naa,
1769  PIX **ppixdb)
1770 {
1771 char buf[128];
1772 char *textstr, *scorestr;
1773 l_int32 i, j, n, nchar, len;
1774 l_float32 score;
1775 L_BMF *bmf;
1776 BOX *box1, *box2;
1777 BOXA *ba;
1778 NUMA *na;
1779 PIX *pix1, *pix2, *pix3, *pix4;
1780 PIXA *pixa;
1781 
1782  PROCNAME("showExtractNumbers");
1783 
1784  if (ppixdb) *ppixdb = NULL;
1785  if (!pixs)
1786  return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL);
1787  if (!sa)
1788  return (PIXA *)ERROR_PTR("sa not defined", procName, NULL);
1789  if (!baa)
1790  return (PIXA *)ERROR_PTR("baa not defined", procName, NULL);
1791  if (!naa)
1792  return (PIXA *)ERROR_PTR("naa not defined", procName, NULL);
1793 
1794  n = sarrayGetCount(sa);
1795  pixa = pixaCreate(n);
1796  bmf = bmfCreate(NULL, 6);
1797  if (ppixdb) *ppixdb = pixConvertTo8(pixs, 1);
1798  for (i = 0; i < n; i++) {
1799  textstr = sarrayGetString(sa, i, L_NOCOPY);
1800  ba = boxaaGetBoxa(baa, i, L_CLONE);
1801  na = numaaGetNuma(naa, i, L_CLONE);
1802  boxaGetExtent(ba, NULL, NULL, &box1);
1803  box2 = boxAdjustSides(NULL, box1, -5, 5, -5, 5);
1804  if (ppixdb) pixRenderBoxArb(*ppixdb, box2, 3, 255, 0, 0);
1805  pix1 = pixClipRectangle(pixs, box1, NULL);
1806  len = strlen(textstr) + 1;
1807  pix2 = pixAddBlackOrWhiteBorder(pix1, 14 * len, 14 * len,
1808  5, 3, L_SET_WHITE);
1809  pix3 = pixConvertTo8(pix2, 1);
1810  nchar = numaGetCount(na);
1811  scorestr = NULL;
1812  for (j = 0; j < nchar; j++) {
1813  numaGetFValue(na, j, &score);
1814  snprintf(buf, sizeof(buf), "%d", (l_int32)(100 * score));
1815  stringJoinIP(&scorestr, buf);
1816  if (j < nchar - 1) stringJoinIP(&scorestr, ",");
1817  }
1818  snprintf(buf, sizeof(buf), "%s: %s\n", textstr, scorestr);
1819  pix4 = pixAddTextlines(pix3, bmf, buf, 0xff000000, L_ADD_BELOW);
1820  pixaAddPix(pixa, pix4, L_INSERT);
1821  boxDestroy(&box1);
1822  boxDestroy(&box2);
1823  pixDestroy(&pix1);
1824  pixDestroy(&pix2);
1825  pixDestroy(&pix3);
1826  boxaDestroy(&ba);
1827  numaDestroy(&na);
1828  LEPT_FREE(scorestr);
1829  }
1830 
1831  bmfDestroy(&bmf);
1832  return pixa;
1833 }
1834 
1835 
1836 /*------------------------------------------------------------------------*
1837  * Static debug helper *
1838  *------------------------------------------------------------------------*/
1851 static void
1853  NUMA *na2,
1854  NUMA *na3,
1855  NUMA *na4,
1856  NUMA *na5,
1857  NUMA *na6)
1858 {
1859 l_int32 i, n;
1860 
1861  n = numaGetCount(na1);
1862  lept_stderr("================================================\n");
1863  lept_stderr("lt minw: ");
1864  for (i = 0; i < n; i++)
1865  lept_stderr("%4d ", (l_int32)na1->array[i]);
1866  lept_stderr("\nlt minh: ");
1867  for (i = 0; i < n; i++)
1868  lept_stderr("%4d ", (l_int32)na2->array[i]);
1869  lept_stderr("\ngt maxh: ");
1870  for (i = 0; i < n; i++)
1871  lept_stderr("%4d ", (l_int32)na3->array[i]);
1872  lept_stderr("\ngt maxasp: ");
1873  for (i = 0; i < n; i++)
1874  lept_stderr("%4d ", (l_int32)na4->array[i]);
1875  lept_stderr("\nlt minaf: ");
1876  for (i = 0; i < n; i++)
1877  lept_stderr("%4d ", (l_int32)na5->array[i]);
1878  lept_stderr("\n------------------------------------------------");
1879  lept_stderr("\nresult: ");
1880  for (i = 0; i < n; i++)
1881  lept_stderr("%4d ", (l_int32)na6->array[i]);
1882  lept_stderr("\n================================================\n");
1883 }
void bmfDestroy(L_BMF **pbmf)
bmfDestroy()
Definition: bmf.c:169
L_BMF * bmfCreate(const char *dir, l_int32 fontsize)
bmfCreate()
Definition: bmf.c:117
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:282
BOXAA * boxaaCreate(l_int32 n)
boxaaCreate()
Definition: boxbasic.c:1244
l_int32 boxaGetCount(BOXA *boxa)
boxaGetCount()
Definition: boxbasic.c:734
l_ok boxaaAddBoxa(BOXAA *baa, BOXA *ba, l_int32 copyflag)
boxaaAddBoxa()
Definition: boxbasic.c:1346
l_ok boxGetGeometry(BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:313
l_ok boxaAddBox(BOXA *boxa, BOX *box, l_int32 copyflag)
boxaAddBox()
Definition: boxbasic.c:620
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:583
BOXA * boxaaGetBoxa(BOXAA *baa, l_int32 index, l_int32 accessflag)
boxaaGetBoxa()
Definition: boxbasic.c:1501
BOX * boxaGetBox(BOXA *boxa, l_int32 index, l_int32 accessflag)
boxaGetBox()
Definition: boxbasic.c:779
void boxaaDestroy(BOXAA **pbaa)
boxaaDestroy()
Definition: boxbasic.c:1310
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:172
BOXA * boxaCreate(l_int32 n)
boxaCreate()
Definition: boxbasic.c:502
l_ok boxaJoin(BOXA *boxad, BOXA *boxas, l_int32 istart, l_int32 iend)
boxaJoin()
Definition: boxfunc1.c:2537
l_ok boxOverlapDistance(BOX *box1, BOX *box2, l_int32 *ph_ovl, l_int32 *pv_ovl)
boxOverlapDistance()
Definition: boxfunc1.c:1044
BOXA * boxaHandleOverlaps(BOXA *boxas, l_int32 op, l_int32 range, l_float32 min_overlap, l_float32 max_ratio, NUMA **pnamap)
boxaHandleOverlaps()
Definition: boxfunc1.c:914
BOX * boxAdjustSides(BOX *boxd, BOX *boxs, l_int32 delleft, l_int32 delright, l_int32 deltop, l_int32 delbot)
boxAdjustSides()
Definition: boxfunc1.c:1991
BOX * boxTransform(BOX *box, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley)
boxTransform()
Definition: boxfunc2.c:152
BOXA * boxaTransform(BOXA *boxas, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley)
boxaTransform()
Definition: boxfunc2.c:102
BOXA * boxaaFlattenToBoxa(BOXAA *baa, NUMA **pnaindex, l_int32 copyflag)
boxaaFlattenToBoxa()
Definition: boxfunc2.c:1646
BOXA * boxaSort(BOXA *boxas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex)
boxaSort()
Definition: boxfunc2.c:637
BOXAA * boxaSort2d(BOXA *boxas, NUMAA **pnaad, l_int32 delta1, l_int32 delta2, l_int32 minh1)
boxaSort2d()
Definition: boxfunc2.c:915
l_ok boxaGetExtent(BOXA *boxa, l_int32 *pw, l_int32 *ph, BOX **pbox)
boxaGetExtent()
Definition: boxfunc4.c:953
BOXA * pixConnComp(PIX *pixs, PIXA **ppixa, l_int32 connectivity)
pixConnComp()
Definition: conncomp.c:151
l_ok fpixSetPixel(FPIX *fpix, l_int32 x, l_int32 y, l_float32 val)
fpixSetPixel()
Definition: fpix1.c:600
void fpixDestroy(FPIX **pfpix)
fpixDestroy()
Definition: fpix1.c:292
FPIX * fpixCreate(l_int32 width, l_int32 height)
fpixCreate()
Definition: fpix1.c:156
PIX * fpixDisplayMaxDynamicRange(FPIX *fpixs)
fpixDisplayMaxDynamicRange()
Definition: fpix2.c:428
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
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:447
l_ok pixCentroid(PIX *pix, l_int32 *centtab, l_int32 *sumtab, l_float32 *pxave, l_float32 *pyave)
pixCentroid()
Definition: morphapp.c:1533
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
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:194
NUMA * numaaGetNuma(NUMAA *naa, l_int32 index, l_int32 accessflag)
numaaGetNuma()
Definition: numabasic.c:1740
NUMA * numaClone(NUMA *na)
numaClone()
Definition: numabasic.c:428
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:366
NUMAA * numaaCreate(l_int32 n)
numaaCreate()
Definition: numabasic.c:1407
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
l_ok numaaAddNuma(NUMAA *naa, NUMA *na, l_int32 copyflag)
numaaAddNuma()
Definition: numabasic.c:1546
NUMA * numaCopy(NUMA *na)
numaCopy()
Definition: numabasic.c:399
l_int32 * numaGetIArray(NUMA *na)
numaGetIArray()
Definition: numabasic.c:847
void numaaDestroy(NUMAA **pnaa)
numaaDestroy()
Definition: numabasic.c:1510
NUMA * numaMakeThresholdIndicator(NUMA *nas, l_float32 thresh, l_int32 type)
numaMakeThresholdIndicator()
Definition: numafunc1.c:1231
NUMA * numaLogicalOp(NUMA *nad, NUMA *na1, NUMA *na2, l_int32 op)
numaLogicalOp()
Definition: numafunc1.c:253
NUMA * numaSortByIndex(NUMA *nas, NUMA *naindex)
numaSortByIndex()
Definition: numafunc1.c:2916
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
l_ok pixSetText(PIX *pix, const char *textstring)
pixSetText()
Definition: pix1.c:1536
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:705
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
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
PIX * pixAddBlackOrWhiteBorder(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_int32 op)
pixAddBlackOrWhiteBorder()
Definition: pix2.c:1863
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
NUMA * pixCountPixelsByColumn(PIX *pix)
pixCountPixelsByColumn()
Definition: pix3.c:2177
NUMA * pixGetMomentByColumn(PIX *pix, l_int32 order)
pixGetMomentByColumn()
Definition: pix3.c:2281
l_int32 * makePixelSumTab8(void)
makePixelSumTab8()
Definition: pix3.c:2411
NUMA * pixaFindAreaFraction(PIXA *pixa)
pixaFindAreaFraction()
Definition: pix5.c:441
l_ok pixFindAreaFraction(PIX *pixs, l_int32 *tab, l_float32 *pfract)
pixFindAreaFraction()
Definition: pix5.c:484
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition: pix5.c:1026
l_ok pixTestClipToForeground(PIX *pixs, l_int32 *pcanclip)
pixTestClipToForeground()
Definition: pix5.c:1884
NUMA * pixaFindWidthHeightRatio(PIXA *pixa)
pixaFindWidthHeightRatio()
Definition: pix5.c:670
l_ok pixClipToForeground(PIX *pixs, PIX **ppixd, BOX **pbox)
pixClipToForeground()
Definition: pix5.c:1784
l_ok pixaFindDimensions(PIXA *pixa, NUMA **pnaw, NUMA **pnah)
pixaFindDimensions()
Definition: pix5.c:140
PIXA * pixClipRectangles(PIX *pixs, BOXA *boxa)
pixClipRectangles()
Definition: pix5.c:960
#define PIX_DST
Definition: pix.h:331
@ L_SELECT_IF_LT
Definition: pix.h:782
@ L_SELECT_IF_GT
Definition: pix.h:783
@ L_SORT_BY_X
Definition: pix.h:735
@ L_ADD_BELOW
Definition: pix.h:1210
@ L_COPY
Definition: pix.h:712
@ L_CLONE
Definition: pix.h:713
@ L_NOCOPY
Definition: pix.h:710
@ L_INSERT
Definition: pix.h:711
@ L_SET_WHITE
Definition: pix.h:906
@ L_COMBINE
Definition: pix.h:1087
#define PIX_SRC
Definition: pix.h:330
@ L_SORT_INCREASING
Definition: pix.h:729
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:506
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:412
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
l_ok pixaRemovePixAndSave(PIXA *pixa, l_int32 index, PIX **ppix, BOX **pbox)
pixaRemovePixAndSave()
Definition: pixabasic.c:1477
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 * pixaaGetPix(PIXAA *paa, l_int32 index, l_int32 ipix, l_int32 accessflag)
pixaaGetPix()
Definition: pixabasic.c:2268
PIXA * pixaaGetPixa(PIXAA *paa, l_int32 index, l_int32 accesstype)
pixaaGetPixa()
Definition: pixabasic.c:2206
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:691
l_ok pixRemoveWithIndicator(PIX *pixs, PIXA *pixa, NUMA *na)
pixRemoveWithIndicator()
Definition: pixafunc1.c:1228
PIX * pixaDisplayTiledInRows(PIXA *pixa, l_int32 outdepth, l_int32 maxwidth, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border)
pixaDisplayTiledInRows()
Definition: pixafunc2.c:746
PIX * pixaDisplayTiledInColumns(PIXA *pixas, l_int32 nx, l_float32 scalefactor, l_int32 spacing, l_int32 border)
pixaDisplayTiledInColumns()
Definition: pixafunc2.c:930
PIX * pixConvertTo1(PIX *pixs, l_int32 threshold)
pixConvertTo1()
Definition: pixconv.c:3026
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3133
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3332
PTA * ptaaGetPta(PTAA *ptaa, l_int32 index, l_int32 accessflag)
ptaaGetPta()
Definition: ptabasic.c:1145
l_ok ptaGetIPt(PTA *pta, l_int32 index, l_int32 *px, l_int32 *py)
ptaGetIPt()
Definition: ptabasic.c:578
l_ok ptaGetPt(PTA *pta, l_int32 index, l_float32 *px, l_float32 *py)
ptaGetPt()
Definition: ptabasic.c:548
void ptaDestroy(PTA **ppta)
ptaDestroy()
Definition: ptabasic.c:195
@ L_USE_AVERAGE_TEMPLATES
Definition: recog.h:261
l_int32 recogGetClassString(L_RECOG *recog, l_int32 index, char **pcharstr)
recogGetClassString()
Definition: recogbasic.c:753
BOXA * recogDecode(L_RECOG *recog, PIX *pixs, l_int32 nlevels, PIX **ppixdb)
recogDecode()
Definition: recogdid.c:219
PIXA * showExtractNumbers(PIX *pixs, SARRAY *sa, BOXAA *baa, NUMAA *naa, PIX **ppixdb)
showExtractNumbers()
Definition: recogident.c:1765
SARRAY * recogExtractNumbers(L_RECOG *recog, BOXA *boxas, l_float32 scorethresh, l_int32 spacethresh, BOXAA **pbaa, NUMAA **pnaa)
recogExtractNumbers()
Definition: recogident.c:1630
l_ok recogCorrelationBestChar(L_RECOG *recog, PIX *pixs, BOX **pbox, l_float32 *pscore, l_int32 *pindex, char **pcharstr, PIX **ppixdb)
recogCorrelationBestChar()
Definition: recogident.c:587
void rchaDestroy(L_RCHA **prcha)
rchaDestroy()
Definition: recogident.c:1172
l_ok rchExtract(L_RCH *rch, l_int32 *pindex, l_float32 *pscore, char **ptext, l_int32 *psample, l_int32 *pxloc, l_int32 *pyloc, l_int32 *pwidth)
rchExtract()
Definition: recogident.c:1329
static l_int32 recogSplittingFilter(L_RECOG *recog, PIX *pixs, l_int32 min, l_float32 minaf, l_int32 *premove, l_int32 debug)
recogSplittingFilter()
Definition: recogident.c:1538
static void l_showIndicatorSplitValues(NUMA *na1, NUMA *na2, NUMA *na3, NUMA *na4, NUMA *na5, NUMA *na6)
l_showIndicatorSplitValues()
Definition: recogident.c:1852
static l_int32 transferRchToRcha(L_RCH *rch, L_RCHA *rcha)
transferRchToRcha()
Definition: recogident.c:1375
static l_int32 pixCorrelationBestShift(PIX *pix1, PIX *pix2, NUMA *nasum1, NUMA *namoment1, l_int32 area2, l_int32 ycent2, l_int32 maxyshift, l_int32 *tab8, l_int32 *pdelx, l_int32 *pdely, l_float32 *pscore, l_int32 debugflag)
pixCorrelationBestShift()
Definition: recogident.c:729
void rchDestroy(L_RCH **prch)
rchDestroy()
Definition: recogident.c:1245
l_ok recogIdentifyPixa(L_RECOG *recog, PIXA *pixa, PIX **ppixdb)
recogIdentifyPixa()
Definition: recogident.c:882
l_ok recogIdentifyPix(L_RECOG *recog, PIX *pixs, PIX **ppixdb)
recogIdentifyPix()
Definition: recogident.c:975
static L_RCHA * rchaCreate()
rchaCreate()
Definition: recogident.c:1150
l_ok recogCorrelationBestRow(L_RECOG *recog, PIX *pixs, BOXA **pboxa, NUMA **pnascore, NUMA **pnaindex, SARRAY **psachar, l_int32 debug)
recogCorrelationBestRow()
Definition: recogident.c:405
l_ok recogSkipIdentify(L_RECOG *recog)
recogSkipIdentify()
Definition: recogident.c:1124
l_ok recogIdentifyMultiple(L_RECOG *recog, PIX *pixs, l_int32 minh, l_int32 skipsplit, BOXA **pboxa, PIXA **ppixa, PIX **ppixdb, l_int32 debugsplit)
recogIdentifyMultiple()
Definition: recogident.c:162
static PIX * recogPreSplittingFilter(L_RECOG *recog, PIX *pixs, l_int32 minh, l_float32 minaf, l_int32 debug)
recogPreSplittingFilter()
Definition: recogident.c:1461
l_ok recogSplitIntoCharacters(L_RECOG *recog, PIX *pixs, l_int32 minh, l_int32 skipsplit, BOXA **pboxa, PIXA **ppixa, l_int32 debug)
recogSplitIntoCharacters()
Definition: recogident.c:250
l_ok rchaExtract(L_RCHA *rcha, NUMA **pnaindex, NUMA **pnascore, SARRAY **psatext, NUMA **pnasample, NUMA **pnaxloc, NUMA **pnayloc, NUMA **pnawidth)
rchaExtract()
Definition: recogident.c:1283
static L_RCH * rchCreate(l_int32 index, l_float32 score, char *text, l_int32 sample, l_int32 xloc, l_int32 yloc, l_int32 width)
rchCreate()
Definition: recogident.c:1217
PIX * recogProcessToIdentify(L_RECOG *recog, PIX *pixs, l_int32 pad)
recogProcessToIdentify()
Definition: recogident.c:1417
PIX * recogShowMatch(L_RECOG *recog, PIX *pix1, PIX *pix2, BOX *box, l_int32 index, l_float32 score)
recogShowMatch()
Definition: recogtrain.c:2429
PIX * recogModifyTemplate(L_RECOG *recog, PIX *pixs)
recogModifyTemplate()
Definition: recogtrain.c:421
l_int32 recogAverageSamples(L_RECOG **precog, l_int32 debug)
recogAverageSamples()
Definition: recogtrain.c:490
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
SARRAY * sarrayCreate(l_int32 n)
sarrayCreate()
Definition: sarray1.c:170
char * sarrayGetString(SARRAY *sa, l_int32 index, l_int32 copyflag)
sarrayGetString()
Definition: sarray1.c:703
SARRAY * sarrayClone(SARRAY *sa)
sarrayClone()
Definition: sarray1.c:425
l_int32 sarrayGetCount(SARRAY *sa)
sarrayGetCount()
Definition: sarray1.c:643
void sarrayDestroy(SARRAY **psa)
sarrayDestroy()
Definition: sarray1.c:362
l_ok sarrayAddString(SARRAY *sa, const char *string, l_int32 copyflag)
sarrayAddString()
Definition: sarray1.c:451
char * sarrayToString(SARRAY *sa, l_int32 addnlflag)
sarrayToString()
Definition: sarray1.c:785
SARRAY * sarraySortByIndex(SARRAY *sain, NUMA *naindex)
sarraySortByIndex()
Definition: sarray2.c:147
PIX * pixExpandReplicate(PIX *pixs, l_int32 factor)
pixExpandReplicate()
Definition: scale2.c:872
Definition: pix.h:481
Definition: pix.h:492
Definition: pix.h:502
Definition: pix.h:579
Definition: bmf.h:47
Definition: recog.h:182
l_int32 sample
Definition: recog.h:186
l_int32 yloc
Definition: recog.h:189
l_float32 score
Definition: recog.h:184
char * text
Definition: recog.h:185
l_int32 index
Definition: recog.h:183
l_int32 xloc
Definition: recog.h:188
l_int32 width
Definition: recog.h:190
Definition: recog.h:197
struct Numa * nasample
Definition: recog.h:201
struct Sarray * satext
Definition: recog.h:200
struct Numa * nascore
Definition: recog.h:199
struct Numa * nawidth
Definition: recog.h:204
struct Numa * naxloc
Definition: recog.h:202
struct Numa * naindex
Definition: recog.h:198
struct Numa * nayloc
Definition: recog.h:203
Definition: recog.h:116
struct Numa * nasum
Definition: recog.h:163
l_int32 ave_done
Definition: recog.h:141
l_int32 templ_use
Definition: recog.h:123
l_int32 * centtab
Definition: recog.h:150
struct L_Rch * rch
Definition: recog.h:174
struct Pixa * pixa_u
Definition: recog.h:158
l_int32 train_done
Definition: recog.h:142
l_int32 maxwidth_u
Definition: recog.h:136
struct Pta * pta_u
Definition: recog.h:159
l_int32 maxyshift
Definition: recog.h:129
l_int32 scalew
Definition: recog.h:117
struct Ptaa * ptaa
Definition: recog.h:156
l_int32 maxheight_u
Definition: recog.h:138
l_int32 min_splitw
Definition: recog.h:146
l_int32 threshold
Definition: recog.h:128
struct Numa * nasum_u
Definition: recog.h:160
struct L_Rcha * rcha
Definition: recog.h:175
l_int32 scaleh
Definition: recog.h:119
l_int32 max_splith
Definition: recog.h:147
l_int32 * sumtab
Definition: recog.h:151
struct Pixa * pixadb_split
Definition: recog.h:170
struct Pta * pta
Definition: recog.h:162
l_int32 minwidth_u
Definition: recog.h:135
struct Numaa * naasum
Definition: recog.h:157
l_float32 max_wh_ratio
Definition: recog.h:144
l_int32 setsize
Definition: recog.h:127
struct Pixaa * pixaa
Definition: recog.h:155
struct Pixa * pixa
Definition: recog.h:161
Definition: array.h:71
l_float32 * array
Definition: array.h:77
Definition: array.h:83
Definition: pix.h:139
Definition: pix.h:456
Definition: pix.h:517
Definition: array.h:127
PIX * pixAddTextlines(PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location)
pixAddTextlines()
Definition: textops.c:276
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
char * stringNew(const char *src)
stringNew()
Definition: utils2.c:223
l_ok stringJoinIP(char **psrc1, const char *src2)
stringJoinIP()
Definition: utils2.c:573
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:2218