Leptonica  1.73
Image processing and image analysis suite
classapp.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 
52 #include <string.h>
53 #include "allheaders.h"
54 
55 static const l_int32 L_BUF_SIZE = 512;
56 static const l_int32 JB_WORDS_MIN_WIDTH = 5;
57 static const l_int32 JB_WORDS_MIN_HEIGHT = 3;
59  /* Static comparison functions */
60 static l_int32 testLineAlignmentX(NUMA *na1, NUMA *na2, l_int32 shiftx,
61  l_int32 delx, l_int32 nperline);
62 static l_int32 countAlignedMatches(NUMA *nai1, NUMA *nai2, NUMA *nasx,
63  NUMA *nasy, l_int32 n1, l_int32 n2,
64  l_int32 delx, l_int32 dely,
65  l_int32 nreq, l_int32 *psame,
66  l_int32 debugflag);
67 static void printRowIndices(l_int32 *index1, l_int32 n1,
68  l_int32 *index2, l_int32 n2);
69 
70 
71 /*------------------------------------------------------------------*
72  * Top-level jb2 correlation and rank-hausdorff *
73  *------------------------------------------------------------------*/
95 l_int32
96 jbCorrelation(const char *dirin,
97  l_float32 thresh,
98  l_float32 weight,
99  l_int32 components,
100  const char *rootname,
101  l_int32 firstpage,
102  l_int32 npages,
103  l_int32 renderflag)
104 {
105 char filename[L_BUF_SIZE];
106 l_int32 nfiles, i, numpages;
107 JBDATA *data;
108 JBCLASSER *classer;
109 PIX *pix;
110 PIXA *pixa;
111 SARRAY *safiles;
112 
113  PROCNAME("jbCorrelation");
114 
115  if (!dirin)
116  return ERROR_INT("dirin not defined", procName, 1);
117  if (!rootname)
118  return ERROR_INT("rootname not defined", procName, 1);
119  if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
120  components != JB_WORDS)
121  return ERROR_INT("components invalid", procName, 1);
122 
123  safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages);
124  nfiles = sarrayGetCount(safiles);
125 
126  /* Classify components */
127  classer = jbCorrelationInit(components, 0, 0, thresh, weight);
128  jbAddPages(classer, safiles);
129 
130  /* Save data */
131  data = jbDataSave(classer);
132  jbDataWrite(rootname, data);
133 
134  /* Optionally, render pages using class templates */
135  if (renderflag) {
136  pixa = jbDataRender(data, FALSE);
137  numpages = pixaGetCount(pixa);
138  if (numpages != nfiles)
139  fprintf(stderr, "numpages = %d, nfiles = %d, not equal!\n",
140  numpages, nfiles);
141  for (i = 0; i < numpages; i++) {
142  pix = pixaGetPix(pixa, i, L_CLONE);
143  snprintf(filename, L_BUF_SIZE, "%s.%04d", rootname, i);
144  fprintf(stderr, "filename: %s\n", filename);
145  pixWrite(filename, pix, IFF_PNG);
146  pixDestroy(&pix);
147  }
148  pixaDestroy(&pixa);
149  }
150 
151  sarrayDestroy(&safiles);
152  jbClasserDestroy(&classer);
153  jbDataDestroy(&data);
154  return 0;
155 }
156 
157 
177 l_int32
178 jbRankHaus(const char *dirin,
179  l_int32 size,
180  l_float32 rank,
181  l_int32 components,
182  const char *rootname,
183  l_int32 firstpage,
184  l_int32 npages,
185  l_int32 renderflag)
186 {
187 char filename[L_BUF_SIZE];
188 l_int32 nfiles, i, numpages;
189 JBDATA *data;
190 JBCLASSER *classer;
191 PIX *pix;
192 PIXA *pixa;
193 SARRAY *safiles;
194 
195  PROCNAME("jbRankHaus");
196 
197  if (!dirin)
198  return ERROR_INT("dirin not defined", procName, 1);
199  if (!rootname)
200  return ERROR_INT("rootname not defined", procName, 1);
201  if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
202  components != JB_WORDS)
203  return ERROR_INT("components invalid", procName, 1);
204 
205  safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages);
206  nfiles = sarrayGetCount(safiles);
207 
208  /* Classify components */
209  classer = jbRankHausInit(components, 0, 0, size, rank);
210  jbAddPages(classer, safiles);
211 
212  /* Save data */
213  data = jbDataSave(classer);
214  jbDataWrite(rootname, data);
215 
216  /* Optionally, render pages using class templates */
217  if (renderflag) {
218  pixa = jbDataRender(data, FALSE);
219  numpages = pixaGetCount(pixa);
220  if (numpages != nfiles)
221  fprintf(stderr, "numpages = %d, nfiles = %d, not equal!\n",
222  numpages, nfiles);
223  for (i = 0; i < numpages; i++) {
224  pix = pixaGetPix(pixa, i, L_CLONE);
225  snprintf(filename, L_BUF_SIZE, "%s.%04d", rootname, i);
226  fprintf(stderr, "filename: %s\n", filename);
227  pixWrite(filename, pix, IFF_PNG);
228  pixDestroy(&pix);
229  }
230  pixaDestroy(&pixa);
231  }
232 
233  sarrayDestroy(&safiles);
234  jbClasserDestroy(&classer);
235  jbDataDestroy(&data);
236  return 0;
237 }
238 
239 
240 
241 /*------------------------------------------------------------------*
242  * Extract and classify words in textline order *
243  *------------------------------------------------------------------*/
265 JBCLASSER *
266 jbWordsInTextlines(const char *dirin,
267  l_int32 reduction,
268  l_int32 maxwidth,
269  l_int32 maxheight,
270  l_float32 thresh,
271  l_float32 weight,
272  NUMA **pnatl,
273  l_int32 firstpage,
274  l_int32 npages)
275 {
276 char *fname;
277 l_int32 nfiles, i, w, h;
278 BOXA *boxa;
279 JBCLASSER *classer;
280 NUMA *nai, *natl;
281 PIX *pix1, *pix2;
282 PIXA *pixa;
283 SARRAY *safiles;
284 
285  PROCNAME("jbWordsInTextlines");
286 
287  if (!pnatl)
288  return (JBCLASSER *)ERROR_PTR("&natl not defined", procName, NULL);
289  *pnatl = NULL;
290  if (!dirin)
291  return (JBCLASSER *)ERROR_PTR("dirin not defined", procName, NULL);
292  if (reduction != 1 && reduction != 2)
293  return (JBCLASSER *)ERROR_PTR("reduction not in {1,2}", procName, NULL);
294 
295  safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages);
296  nfiles = sarrayGetCount(safiles);
297 
298  /* Classify components */
299  classer = jbCorrelationInit(JB_WORDS, maxwidth, maxheight, thresh, weight);
300  classer->safiles = sarrayCopy(safiles);
301  natl = numaCreate(0);
302  *pnatl = natl;
303  for (i = 0; i < nfiles; i++) {
304  fname = sarrayGetString(safiles, i, L_NOCOPY);
305  if ((pix1 = pixRead(fname)) == NULL) {
306  L_WARNING("image file %d not read\n", procName, i);
307  continue;
308  }
309  if (reduction == 1)
310  pix2 = pixClone(pix1);
311  else /* reduction == 2 */
312  pix2 = pixReduceRankBinaryCascade(pix1, 1, 0, 0, 0);
314  JB_WORDS_MIN_HEIGHT, maxwidth, maxheight,
315  &boxa, &pixa, &nai);
316  pixGetDimensions(pix2, &w, &h, NULL);
317  classer->w = w;
318  classer->h = h;
319  jbAddPageComponents(classer, pix2, boxa, pixa);
320  numaJoin(natl, nai, 0, -1);
321  pixDestroy(&pix1);
322  pixDestroy(&pix2);
323  numaDestroy(&nai);
324  boxaDestroy(&boxa);
325  pixaDestroy(&pixa);
326  }
327 
328  sarrayDestroy(&safiles);
329  return classer;
330 }
331 
332 
377 l_int32
379  l_int32 minwidth,
380  l_int32 minheight,
381  l_int32 maxwidth,
382  l_int32 maxheight,
383  BOXA **pboxad,
384  PIXA **ppixad,
385  NUMA **pnai)
386 {
387 BOXA *boxa1, *boxad;
388 BOXAA *baa;
389 NUMA *nai;
390 NUMAA *naa;
391 PIXA *pixa1, *pixad;
392 PIXAA *paa;
393 
394  PROCNAME("pixGetWordsInTextlines");
395 
396  if (!pboxad || !ppixad || !pnai)
397  return ERROR_INT("&boxad, &pixad, &nai not all defined", procName, 1);
398  *pboxad = NULL;
399  *ppixad = NULL;
400  *pnai = NULL;
401  if (!pixs)
402  return ERROR_INT("pixs not defined", procName, 1);
403 
404  /* Get the bounding boxes of the words from the word mask. */
405  pixWordBoxesByDilation(pixs, minwidth, minheight, maxwidth, maxheight,
406  &boxa1, NULL, NULL);
407 
408  /* Generate a pixa of the word images */
409  pixa1 = pixaCreateFromBoxa(pixs, boxa1, NULL); /* mask over each word */
410 
411  /* Sort the bounding boxes of these words by line. We use the
412  * index mapping to allow identical sorting of the pixa. */
413  baa = boxaSort2d(boxa1, &naa, -1, -1, 4);
414  paa = pixaSort2dByIndex(pixa1, naa, L_CLONE);
415 
416  /* Flatten the word paa */
417  pixad = pixaaFlattenToPixa(paa, &nai, L_CLONE);
418  boxad = pixaGetBoxa(pixad, L_COPY);
419 
420  *pnai = nai;
421  *pboxad = boxad;
422  *ppixad = pixad;
423 
424  pixaDestroy(&pixa1);
425  boxaDestroy(&boxa1);
426  boxaaDestroy(&baa);
427  pixaaDestroy(&paa);
428  numaaDestroy(&naa);
429  return 0;
430 }
431 
432 
452 l_int32
454  l_int32 minwidth,
455  l_int32 minheight,
456  l_int32 maxwidth,
457  l_int32 maxheight,
458  BOXA **pboxad,
459  NUMA **pnai)
460 {
461 BOXA *boxa1;
462 BOXAA *baa;
463 NUMA *nai;
464 
465  PROCNAME("pixGetWordBoxesInTextlines");
466 
467  if (pnai) *pnai = NULL;
468  if (!pboxad)
469  return ERROR_INT("&boxad and &nai not both defined", procName, 1);
470  *pboxad = NULL;
471  if (!pixs)
472  return ERROR_INT("pixs not defined", procName, 1);
473 
474  /* Get the bounding boxes of the words from the word mask. */
475  pixWordBoxesByDilation(pixs, minwidth, minheight, maxwidth, maxheight,
476  &boxa1, NULL, NULL);
477 
478  /* 2D sort the bounding boxes of these words. */
479  baa = boxaSort2d(boxa1, NULL, 3, -5, 5);
480 
481  /* Flatten the boxaa, saving the boxa index for each box */
482  *pboxad = boxaaFlattenToBoxa(baa, &nai, L_CLONE);
483 
484  if (pnai)
485  *pnai = nai;
486  else
487  numaDestroy(&nai);
488  boxaDestroy(&boxa1);
489  boxaaDestroy(&baa);
490  return 0;
491 }
492 
493 
494 /*------------------------------------------------------------------*
495  * Use word bounding boxes to compare page images *
496  *------------------------------------------------------------------*/
514 NUMAA *
516  NUMA *na)
517 {
518 l_int32 index, nbox, row, prevrow, x, y, w, h;
519 BOX *box;
520 NUMA *nad;
521 NUMAA *naa;
522 
523  PROCNAME("boxaExtractSortedPattern");
524 
525  if (!boxa)
526  return (NUMAA *)ERROR_PTR("boxa not defined", procName, NULL);
527  if (!na)
528  return (NUMAA *)ERROR_PTR("na not defined", procName, NULL);
529 
530  naa = numaaCreate(0);
531  nbox = boxaGetCount(boxa);
532  if (nbox == 0)
533  return naa;
534 
535  prevrow = -1;
536  for (index = 0; index < nbox; index++) {
537  box = boxaGetBox(boxa, index, L_CLONE);
538  numaGetIValue(na, index, &row);
539  if (row > prevrow) {
540  if (index > 0)
541  numaaAddNuma(naa, nad, L_INSERT);
542  nad = numaCreate(0);
543  prevrow = row;
544  boxGetGeometry(box, NULL, &y, NULL, &h);
545  numaAddNumber(nad, y + h / 2);
546  }
547  boxGetGeometry(box, &x, NULL, &w, NULL);
548  numaAddNumber(nad, x);
549  numaAddNumber(nad, x + w - 1);
550  boxDestroy(&box);
551  }
552  numaaAddNuma(naa, nad, L_INSERT);
553 
554  return naa;
555 }
556 
557 
602 l_int32
604  NUMAA *naa2,
605  l_int32 nperline,
606  l_int32 nreq,
607  l_int32 maxshiftx,
608  l_int32 maxshifty,
609  l_int32 delx,
610  l_int32 dely,
611  l_int32 *psame,
612  l_int32 debugflag)
613 {
614 l_int32 n1, n2, i, j, nbox, y1, y2, xl1, xl2;
615 l_int32 shiftx, shifty, match;
616 l_int32 *line1, *line2; /* indicator for sufficient boxes in a line */
617 l_int32 *yloc1, *yloc2; /* arrays of y value for first box in a line */
618 l_int32 *xleft1, *xleft2; /* arrays of x value for left side of first box */
619 NUMA *na1, *na2, *nai1, *nai2, *nasx, *nasy;
620 
621  PROCNAME("numaaCompareImagesByBoxes");
622 
623  if (!psame)
624  return ERROR_INT("&same not defined", procName, 1);
625  *psame = 0;
626  if (!naa1)
627  return ERROR_INT("naa1 not defined", procName, 1);
628  if (!naa2)
629  return ERROR_INT("naa2 not defined", procName, 1);
630  if (nperline < 1)
631  return ERROR_INT("nperline < 1", procName, 1);
632  if (nreq < 1)
633  return ERROR_INT("nreq < 1", procName, 1);
634 
635  n1 = numaaGetCount(naa1);
636  n2 = numaaGetCount(naa2);
637  if (n1 < nreq || n2 < nreq)
638  return 0;
639 
640  /* Find the lines in naa1 and naa2 with sufficient boxes.
641  * Also, find the y-values for each of the lines, and the
642  * LH x-values of the first box in each line. */
643  line1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32));
644  line2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32));
645  yloc1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32));
646  yloc2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32));
647  xleft1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32));
648  xleft2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32));
649  if (!line1 || !line2 || !yloc1 || !yloc2 || !xleft1 || !xleft2)
650  return ERROR_INT("callof failure for an array", procName, 1);
651  for (i = 0; i < n1; i++) {
652  na1 = numaaGetNuma(naa1, i, L_CLONE);
653  numaGetIValue(na1, 0, yloc1 + i);
654  numaGetIValue(na1, 1, xleft1 + i);
655  nbox = (numaGetCount(na1) - 1) / 2;
656  if (nbox >= nperline)
657  line1[i] = 1;
658  numaDestroy(&na1);
659  }
660  for (i = 0; i < n2; i++) {
661  na2 = numaaGetNuma(naa2, i, L_CLONE);
662  numaGetIValue(na2, 0, yloc2 + i);
663  numaGetIValue(na2, 1, xleft2 + i);
664  nbox = (numaGetCount(na2) - 1) / 2;
665  if (nbox >= nperline)
666  line2[i] = 1;
667  numaDestroy(&na2);
668  }
669 
670  /* Enumerate all possible line matches. A 'possible' line
671  * match is one where the x and y shifts for the first box
672  * in each line are within the maxshiftx and maxshifty
673  * constraints, and the left and right sides of the remaining
674  * (nperline - 1) successive boxes are within delx of each other.
675  * The result is a set of four numas giving parameters of
676  * each set of matching lines. */
677  nai1 = numaCreate(0); /* line index 1 of match */
678  nai2 = numaCreate(0); /* line index 2 of match */
679  nasx = numaCreate(0); /* shiftx for match */
680  nasy = numaCreate(0); /* shifty for match */
681  for (i = 0; i < n1; i++) {
682  if (line1[i] == 0) continue;
683  y1 = yloc1[i];
684  xl1 = xleft1[i];
685  na1 = numaaGetNuma(naa1, i, L_CLONE);
686  for (j = 0; j < n2; j++) {
687  if (line2[j] == 0) continue;
688  y2 = yloc2[j];
689  if (L_ABS(y1 - y2) > maxshifty) continue;
690  xl2 = xleft2[j];
691  if (L_ABS(xl1 - xl2) > maxshiftx) continue;
692  shiftx = xl1 - xl2; /* shift to add to x2 values */
693  shifty = y1 - y2; /* shift to add to y2 values */
694  na2 = numaaGetNuma(naa2, j, L_CLONE);
695 
696  /* Now check if 'nperline' boxes in the two lines match */
697  match = testLineAlignmentX(na1, na2, shiftx, delx, nperline);
698  if (match) {
699  numaAddNumber(nai1, i);
700  numaAddNumber(nai2, j);
701  numaAddNumber(nasx, shiftx);
702  numaAddNumber(nasy, shifty);
703  }
704  numaDestroy(&na2);
705  }
706  numaDestroy(&na1);
707  }
708 
709  /* Determine if there are a sufficient number of mutually
710  * aligned matches. Mutually aligned matches place an additional
711  * constraint on the 'possible' matches, where the relative
712  * shifts must not exceed the (delx, dely) distances. */
713  countAlignedMatches(nai1, nai2, nasx, nasy, n1, n2, delx, dely,
714  nreq, psame, debugflag);
715 
716  LEPT_FREE(line1);
717  LEPT_FREE(line2);
718  LEPT_FREE(yloc1);
719  LEPT_FREE(yloc2);
720  LEPT_FREE(xleft1);
721  LEPT_FREE(xleft2);
722  numaDestroy(&nai1);
723  numaDestroy(&nai2);
724  numaDestroy(&nasx);
725  numaDestroy(&nasy);
726  return 0;
727 }
728 
729 
730 static l_int32
731 testLineAlignmentX(NUMA *na1,
732  NUMA *na2,
733  l_int32 shiftx,
734  l_int32 delx,
735  l_int32 nperline)
736 {
737 l_int32 i, xl1, xr1, xl2, xr2, diffl, diffr;
738 
739  PROCNAME("testLineAlignmentX");
740 
741  if (!na1)
742  return ERROR_INT("na1 not defined", procName, 1);
743  if (!na2)
744  return ERROR_INT("na2 not defined", procName, 1);
745 
746  for (i = 0; i < nperline; i++) {
747  numaGetIValue(na1, i + 1, &xl1);
748  numaGetIValue(na1, i + 2, &xr1);
749  numaGetIValue(na2, i + 1, &xl2);
750  numaGetIValue(na2, i + 2, &xr2);
751  diffl = L_ABS(xl1 - xl2 - shiftx);
752  diffr = L_ABS(xr1 - xr2 - shiftx);
753  if (diffl > delx || diffr > delx)
754  return 0;
755  }
756 
757  return 1;
758 }
759 
760 
761 /*
762  * countAlignedMatches()
763  * Input: nai1, nai2 (numas of row pairs for matches)
764  * nasx, nasy (numas of x and y shifts for the matches)
765  * n1, n2 (number of rows in images 1 and 2)
766  * delx, dely (allowed difference in shifts of the match,
767  * compared to the reference match)
768  * nreq (number of required aligned matches)
769  * &same (<return> 1 if %nreq row matches are found; 0 otherwise)
770  * Return: 0 if OK, 1 on error
771  *
772  * Notes:
773  * (1) This takes 4 input arrays giving parameters of all the
774  * line matches. It looks for the maximum set of aligned
775  * matches (matches with approximately the same overall shifts)
776  * that do not use rows from either image more than once.
777  */
778 static l_int32
779 countAlignedMatches(NUMA *nai1,
780  NUMA *nai2,
781  NUMA *nasx,
782  NUMA *nasy,
783  l_int32 n1,
784  l_int32 n2,
785  l_int32 delx,
786  l_int32 dely,
787  l_int32 nreq,
788  l_int32 *psame,
789  l_int32 debugflag)
790 {
791 l_int32 i, j, nm, shiftx, shifty, nmatch, diffx, diffy;
792 l_int32 *ia1, *ia2, *iasx, *iasy, *index1, *index2;
793 
794  PROCNAME("countAlignedMatches");
795 
796  if (!nai1 || !nai2 || !nasx || !nasy)
797  return ERROR_INT("4 input numas not defined", procName, 1);
798  if (!psame)
799  return ERROR_INT("&same not defined", procName, 1);
800  *psame = 0;
801 
802  /* Check for sufficient aligned matches, doing a double iteration
803  * over the set of raw matches. The row index arrays
804  * are used to verify that the same rows in either image
805  * are not used in more than one match. Whenever there
806  * is a match that is properly aligned, those rows are
807  * marked in the index arrays. */
808  nm = numaGetCount(nai1); /* number of matches */
809  if (nm < nreq)
810  return 0;
811 
812  ia1 = numaGetIArray(nai1);
813  ia2 = numaGetIArray(nai2);
814  iasx = numaGetIArray(nasx);
815  iasy = numaGetIArray(nasy);
816  index1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32)); /* watch rows */
817  index2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32));
818  if (!index1 || !index2)
819  return ERROR_INT("calloc fail for array", procName, 1);
820  for (i = 0; i < nm; i++) {
821  if (*psame == 1)
822  break;
823 
824  /* Reset row index arrays */
825  memset(index1, 0, 4 * n1);
826  memset(index2, 0, 4 * n2);
827  nmatch = 1;
828  index1[ia1[i]] = nmatch; /* mark these rows as taken */
829  index2[ia2[i]] = nmatch;
830  shiftx = iasx[i]; /* reference shift between two rows */
831  shifty = iasy[i]; /* ditto */
832  if (nreq == 1) {
833  *psame = 1;
834  break;
835  }
836  for (j = 0; j < nm; j++) {
837  if (j == i) continue;
838  /* Rows must both be different from any previously seen */
839  if (index1[ia1[j]] > 0 || index2[ia2[j]] > 0) continue;
840  /* Check the shift for this match */
841  diffx = L_ABS(shiftx - iasx[j]);
842  diffy = L_ABS(shifty - iasy[j]);
843  if (diffx > delx || diffy > dely) continue;
844  /* We have a match */
845  nmatch++;
846  index1[ia1[j]] = nmatch; /* mark the rows */
847  index2[ia2[j]] = nmatch;
848  if (nmatch >= nreq) {
849  *psame = 1;
850  if (debugflag)
851  printRowIndices(index1, n1, index2, n2);
852  break;
853  }
854  }
855  }
856 
857  LEPT_FREE(ia1);
858  LEPT_FREE(ia2);
859  LEPT_FREE(iasx);
860  LEPT_FREE(iasy);
861  LEPT_FREE(index1);
862  LEPT_FREE(index2);
863  return 0;
864 }
865 
866 
867 static void
868 printRowIndices(l_int32 *index1,
869  l_int32 n1,
870  l_int32 *index2,
871  l_int32 n2)
872 {
873 l_int32 i;
874 
875  fprintf(stderr, "Index1: ");
876  for (i = 0; i < n1; i++) {
877  if (i && (i % 20 == 0))
878  fprintf(stderr, "\n ");
879  fprintf(stderr, "%3d", index1[i]);
880  }
881  fprintf(stderr, "\n");
882 
883  fprintf(stderr, "Index2: ");
884  for (i = 0; i < n2; i++) {
885  if (i && (i % 20 == 0))
886  fprintf(stderr, "\n ");
887  fprintf(stderr, "%3d", index2[i]);
888  }
889  fprintf(stderr, "\n");
890  return;
891 }
void pixaaDestroy(PIXAA **ppaa)
pixaaDestroy()
Definition: pixabasic.c:1876
NUMAA * boxaExtractSortedPattern(BOXA *boxa, NUMA *na)
boxaExtractSortedPattern()
Definition: classapp.c:515
l_int32 numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:472
PIXAA * pixaSort2dByIndex(PIXA *pixas, NUMAA *naa, l_int32 copyflag)
pixaSort2dByIndex()
Definition: pixafunc1.c:1595
SARRAY * sarrayCopy(SARRAY *sa)
sarrayCopy()
Definition: sarray1.c:387
l_int32 w
Definition: jbclass.h:66
Definition: pix.h:704
l_int32 pixGetWordsInTextlines(PIX *pixs, l_int32 minwidth, l_int32 minheight, l_int32 maxwidth, l_int32 maxheight, BOXA **pboxad, PIXA **ppixad, NUMA **pnai)
pixGetWordsInTextlines()
Definition: classapp.c:378
PIXA * pixaaFlattenToPixa(PIXAA *paa, NUMA **pnaindex, l_int32 copyflag)
pixaaFlattenToPixa()
Definition: pixafunc1.c:2093
l_int32 numaJoin(NUMA *nad, NUMA *nas, l_int32 istart, l_int32 iend)
numaJoin()
Definition: numafunc1.c:3319
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:186
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:577
static const l_int32 JB_WORDS_MIN_WIDTH
Definition: classapp.c:56
NUMAA * numaaCreate(l_int32 n)
numaaCreate()
Definition: numabasic.c:1307
Definition: pix.h:492
l_int32 h
Definition: jbclass.h:67
void numaaDestroy(NUMAA **pnaa)
numaaDestroy()
Definition: numabasic.c:1410
Definition: array.h:116
l_int32 numaaAddNuma(NUMAA *naa, NUMA *na, l_int32 copyflag)
numaaAddNuma()
Definition: numabasic.c:1448
static const l_int32 JB_WORDS_MIN_HEIGHT
Definition: classapp.c:57
l_int32 jbCorrelation(const char *dirin, l_float32 thresh, l_float32 weight, l_int32 components, const char *rootname, l_int32 firstpage, l_int32 npages, l_int32 renderflag)
jbCorrelation()
Definition: classapp.c:96
Definition: pix.h:502
l_int32 * numaGetIArray(NUMA *na)
numaGetIArray()
Definition: numabasic.c:819
NUMA * numaaGetNuma(NUMAA *naa, l_int32 index, l_int32 accessflag)
numaaGetNuma()
Definition: numabasic.c:1625
Definition: array.h:59
void boxaaDestroy(BOXAA **pbaa)
boxaaDestroy()
Definition: boxbasic.c:1303
static const l_int32 L_INSERT
Definition: pix.h:710
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:630
struct Pix * pix
Definition: ccbord.h:108
l_int32 jbRankHaus(const char *dirin, l_int32 size, l_float32 rank, l_int32 components, const char *rootname, l_int32 firstpage, l_int32 npages, l_int32 renderflag)
jbRankHaus()
Definition: classapp.c:178
l_int32 boxGetGeometry(BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:309
l_int32 h
Definition: ccbord.h:110
l_int32 w
Definition: ccbord.h:109
char * sarrayGetString(SARRAY *sa, l_int32 index, l_int32 copyflag)
sarrayGetString()
Definition: sarray1.c:675
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:517
Definition: array.h:71
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:545
BOX * boxaGetBox(BOXA *boxa, l_int32 index, l_int32 accessflag)
boxaGetBox()
Definition: boxbasic.c:760
SARRAY * getSortedPathnamesInDirectory(const char *dirname, const char *substr, l_int32 first, l_int32 nfiles)
getSortedPathnamesInDirectory()
Definition: sarray1.c:1710
Definition: pix.h:454
JBCLASSER * jbWordsInTextlines(const char *dirin, l_int32 reduction, l_int32 maxwidth, l_int32 maxheight, l_float32 thresh, l_float32 weight, NUMA **pnatl, l_int32 firstpage, l_int32 npages)
jbWordsInTextlines()
Definition: classapp.c:266
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:359
Definition: pix.h:465
l_int32 pixGetWordBoxesInTextlines(PIX *pixs, l_int32 minwidth, l_int32 minheight, l_int32 maxwidth, l_int32 maxheight, BOXA **pboxad, NUMA **pnai)
pixGetWordBoxesInTextlines()
Definition: classapp.c:453
PIXA * pixaCreateFromBoxa(PIX *pixs, BOXA *boxa, l_int32 *pcropwarn)
pixaCreateFromBoxa()
Definition: pixabasic.c:263
l_int32 sarrayGetCount(SARRAY *sa)
sarrayGetCount()
Definition: sarray1.c:615
PIX * pixRead(const char *filename)
pixRead()
Definition: readfile.c:189
l_int32 numaaGetCount(NUMAA *naa)
numaaGetCount()
Definition: numabasic.c:1516
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:660
Definition: pix.h:705
Definition: pix.h:134
Definition: pix.h:706
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:277
BOXAA * boxaSort2d(BOXA *boxas, NUMAA **pnaad, l_int32 delta1, l_int32 delta2, l_int32 minh1)
boxaSort2d()
Definition: boxfunc2.c:837
l_int32 numaaCompareImagesByBoxes(NUMAA *naa1, NUMAA *naa2, l_int32 nperline, l_int32 nreq, l_int32 maxshiftx, l_int32 maxshifty, l_int32 delx, l_int32 dely, l_int32 *psame, l_int32 debugflag)
numaaCompareImagesByBoxes()
Definition: classapp.c:603
l_int32 boxaGetCount(BOXA *boxa)
boxaGetCount()
Definition: boxbasic.c:715
l_int32 numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:726
struct Sarray * safiles
Definition: jbclass.h:49
l_int32 pixGetDimensions(PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1052
BOXA * boxaaFlattenToBoxa(BOXAA *baa, NUMA **pnaindex, l_int32 copyflag)
boxaaFlattenToBoxa()
Definition: boxfunc2.c:1478
Definition: pix.h:480
PIX * pixReduceRankBinaryCascade(PIX *pixs, l_int32 level1, l_int32 level2, l_int32 level3, l_int32 level4)
pixReduceRankBinaryCascade()
Definition: binreduce.c:148
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:398
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:620
static const l_int32 L_BUF_SIZE
Definition: classapp.c:55
void sarrayDestroy(SARRAY **psa)
sarrayDestroy()
Definition: sarray1.c:349
BOXA * pixaGetBoxa(PIXA *pixa, l_int32 accesstype)
pixaGetBoxa()
Definition: pixabasic.c:729