53#include <config_auto.h>
57#include "allheaders.h"
64static l_int32 testLineAlignmentX(
NUMA *na1,
NUMA *na2, l_int32 shiftx,
65 l_int32 delx, l_int32 nperline);
66static l_int32 countAlignedMatches(
NUMA *nai1,
NUMA *nai2,
NUMA *nasx,
67 NUMA *nasy, l_int32 n1, l_int32 n2,
68 l_int32 delx, l_int32 dely,
69 l_int32 nreq, l_int32 *psame,
71static void printRowIndices(l_int32 *index1, l_int32 n1,
72 l_int32 *index2, l_int32 n2);
103 const char *rootname,
109l_int32 nfiles, i, numpages;
117 return ERROR_INT(
"dirin not defined", __func__, 1);
119 return ERROR_INT(
"rootname not defined", __func__, 1);
120 if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
121 components != JB_WORDS)
122 return ERROR_INT(
"components invalid", __func__, 1);
124 safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages);
125 nfiles = sarrayGetCount(safiles);
128 classer = jbCorrelationInit(components, 0, 0, thresh, weight);
129 jbAddPages(classer, safiles);
132 data = jbDataSave(classer);
133 jbDataWrite(rootname, data);
137 pixa = jbDataRender(data, FALSE);
138 numpages = pixaGetCount(pixa);
139 if (numpages != nfiles)
140 lept_stderr(
"numpages = %d, nfiles = %d, not equal!\n",
142 for (i = 0; i < numpages; i++) {
143 pix = pixaGetPix(pixa, i,
L_CLONE);
144 snprintf(filename,
L_BUF_SIZE,
"%s.%04d", rootname, i);
145 lept_stderr(
"filename: %s\n", filename);
146 pixWrite(filename, pix, IFF_PNG);
152 sarrayDestroy(&safiles);
153 jbClasserDestroy(&classer);
154 jbDataDestroy(&data);
183 const char *rootname,
189l_int32 nfiles, i, numpages;
197 return ERROR_INT(
"dirin not defined", __func__, 1);
199 return ERROR_INT(
"rootname not defined", __func__, 1);
200 if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
201 components != JB_WORDS)
202 return ERROR_INT(
"components invalid", __func__, 1);
204 safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages);
205 nfiles = sarrayGetCount(safiles);
208 classer = jbRankHausInit(components, 0, 0, size, rank);
209 jbAddPages(classer, safiles);
212 data = jbDataSave(classer);
213 jbDataWrite(rootname, data);
217 pixa = jbDataRender(data, FALSE);
218 numpages = pixaGetCount(pixa);
219 if (numpages != nfiles)
220 lept_stderr(
"numpages = %d, nfiles = %d, not equal!\n",
222 for (i = 0; i < numpages; i++) {
223 pix = pixaGetPix(pixa, i,
L_CLONE);
224 snprintf(filename,
L_BUF_SIZE,
"%s.%04d", rootname, i);
225 lept_stderr(
"filename: %s\n", filename);
226 pixWrite(filename, pix, IFF_PNG);
232 sarrayDestroy(&safiles);
233 jbClasserDestroy(&classer);
234 jbDataDestroy(&data);
276l_int32 nfiles, i, w, h;
285 return (
JBCLASSER *)ERROR_PTR(
"&natl not defined", __func__, NULL);
288 return (
JBCLASSER *)ERROR_PTR(
"dirin not defined", __func__, NULL);
289 if (reduction != 1 && reduction != 2)
290 return (
JBCLASSER *)ERROR_PTR(
"reduction not in {1,2}", __func__, NULL);
292 safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages);
293 nfiles = sarrayGetCount(safiles);
296 classer = jbCorrelationInit(JB_WORDS, maxwidth, maxheight, thresh, weight);
297 classer->
safiles = sarrayCopy(safiles);
298 natl = numaCreate(0);
300 for (i = 0; i < nfiles; i++) {
301 fname = sarrayGetString(safiles, i,
L_NOCOPY);
302 if ((pix1 = pixRead(fname)) == NULL) {
303 L_WARNING(
"image file %d not read\n", __func__, i);
307 pix2 = pixClone(pix1);
309 pix2 = pixReduceRankBinaryCascade(pix1, 1, 0, 0, 0);
313 pixGetDimensions(pix2, &w, &h, NULL);
316 jbAddPageComponents(classer, pix2, boxa, pixa);
317 numaJoin(natl, nai, 0, -1);
325 sarrayDestroy(&safiles);
393 if (!pboxad || !ppixad || !pnai)
394 return ERROR_INT(
"&boxad, &pixad, &nai not all defined", __func__, 1);
399 return ERROR_INT(
"pixs not defined", __func__, 1);
402 pixWordBoxesByDilation(pixs, minwidth, minheight, maxwidth, maxheight,
406 pixa1 = pixaCreateFromBoxa(pixs, boxa1, 0, 0, NULL);
410 baa = boxaSort2d(boxa1, &naa, -1, -1, 4);
411 paa = pixaSort2dByIndex(pixa1, naa,
L_CLONE);
414 pixad = pixaaFlattenToPixa(paa, &nai,
L_CLONE);
415 boxad = pixaGetBoxa(pixad,
L_COPY);
464 if (pnai) *pnai = NULL;
466 return ERROR_INT(
"&boxad and &nai not both defined", __func__, 1);
469 return ERROR_INT(
"pixs not defined", __func__, 1);
472 pixWordBoxesByDilation(pixs, minwidth, minheight, maxwidth, maxheight,
476 baa = boxaSort2d(boxa1, NULL, 3, -5, 5);
479 *pboxad = boxaaFlattenToBoxa(baa, &nai,
L_CLONE);
521 const char *debugdir)
523char *debugfile, *subdir;
524l_int32 i, xs, ys, xb, yb, nb, loc;
527BOXA *boxa1, *boxa1a, *boxa2, *boxa3, *boxa4, *boxa5, *boxaw;
529PIX *pix1, *pix2, *pix3, *pix3a, *pix4, *pix5;
531 if (pboxaw) *pboxaw = NULL;
532 if (pboxaac) *pboxaac = NULL;
533 if (!pboxaw || !pboxaac)
534 return ERROR_INT(
"&boxaw and &boxaac not defined", __func__, 1);
535 if (!pixs || pixGetDepth(pixs) == 1)
536 return ERROR_INT(
"pixs not defined or 1 bpp", __func__, 1);
538 L_WARNING(
"threshold is %d; may be too high\n", __func__, thresh);
541 if ((pix1 = pixClipRectangle(pixs, boxs, NULL)) == NULL)
542 return ERROR_INT(
"pix1 not made", __func__, 1);
543 boxGetGeometry(boxs, &xs, &ys, NULL, NULL);
545 pix1 = pixClone(pixs);
550 pix2 = pixConvertTo8(pix1, FALSE);
554 pix3 = pixConvertTo1(pix2, thresh);
557 pix3a = pixScaleToResolution(pix3, 120.0, 300.0, &scalefact);
562 boxa1 = boxaTransform(boxa1a, 0, 0, 1.0 / scalefact, 1.0 / scalefact);
565 subdir = stringReplaceSubstr(debugdir,
"/tmp/",
"", &loc, NULL);
568 pix4 = pixConvertTo32(pix2);
569 pixRenderBoxaArb(pix4, boxa1, 2, 255, 0, 0);
570 debugfile = stringJoin(debugdir,
"/words.png");
571 pixWrite(debugfile, pix4, IFF_PNG);
573 LEPT_FREE(debugfile);
577 nb = boxaGetCount(boxa1);
578 boxaw = boxaCreate(nb);
579 boxaac = boxaaCreate(nb);
582 for (i = 0; i < nb; i++) {
583 box1 = boxaGetBox(boxa1, i,
L_COPY);
584 boxGetGeometry(box1, &xb, &yb, NULL, NULL);
585 pix4 = pixClipRectangle(pix3, box1, NULL);
587 pix5 = pixMorphSequence(pix4,
"c1.10", 0);
589 boxa2 = pixConnCompBB(pix5, 4);
596 boxa5 = boxaTransform(boxa4, xs + xb, ys + yb, 1.0, 1.0);
597 box2 = boxTransform(box1, xs, ys, 1.0, 1.0);
600 if (boxaGetCount(boxa5) > 0) {
602 boxaaAddBoxa(boxaac, boxa5,
L_INSERT);
619 boxaDestroy(&boxa1a);
621 pix4 = pixConvertTo32(pixs);
622 boxa2 = boxaaFlattenToBoxa(boxaac, NULL,
L_COPY);
623 pixRenderBoxaArb(pix4, boxa2, 2, 255, 0, 0);
624 boxa3 = boxaAdjustSides(boxaw, -2, 2, -2, 2);
625 pixRenderBoxaArb(pix4, boxa3, 2, 0, 255, 0);
626 debugfile = stringJoin(debugdir,
"/chars.png");
627 pixWrite(debugfile, pix4, IFF_PNG);
631 LEPT_FREE(debugfile);
661l_int32 index, nbox, row, prevrow, x, y, w, h;
667 return (
NUMAA *)ERROR_PTR(
"boxa not defined", __func__, NULL);
669 return (
NUMAA *)ERROR_PTR(
"na not defined", __func__, NULL);
671 naa = numaaCreate(0);
672 nbox = boxaGetCount(boxa);
677 for (index = 0; index < nbox; index++) {
678 box = boxaGetBox(boxa, index,
L_CLONE);
679 numaGetIValue(na, index, &row);
685 boxGetGeometry(box, NULL, &y, NULL, &h);
686 numaAddNumber(nad, y + h / 2);
688 boxGetGeometry(box, &x, NULL, &w, NULL);
689 numaAddNumber(nad, x);
690 numaAddNumber(nad, x + w - 1);
755l_int32 n1, n2, i, j, nbox, y1, y2, xl1, xl2;
756l_int32 shiftx, shifty, match;
757l_int32 *line1, *line2;
758l_int32 *yloc1, *yloc2;
759l_int32 *xleft1, *xleft2;
760NUMA *na1, *na2, *nai1, *nai2, *nasx, *nasy;
763 return ERROR_INT(
"&same not defined", __func__, 1);
766 return ERROR_INT(
"naa1 not defined", __func__, 1);
768 return ERROR_INT(
"naa2 not defined", __func__, 1);
770 return ERROR_INT(
"nperline < 1", __func__, 1);
772 return ERROR_INT(
"nreq < 1", __func__, 1);
774 n1 = numaaGetCount(naa1);
775 n2 = numaaGetCount(naa2);
776 if (n1 < nreq || n2 < nreq)
782 line1 = (l_int32 *)LEPT_CALLOC(n1,
sizeof(l_int32));
783 line2 = (l_int32 *)LEPT_CALLOC(n2,
sizeof(l_int32));
784 yloc1 = (l_int32 *)LEPT_CALLOC(n1,
sizeof(l_int32));
785 yloc2 = (l_int32 *)LEPT_CALLOC(n2,
sizeof(l_int32));
786 xleft1 = (l_int32 *)LEPT_CALLOC(n1,
sizeof(l_int32));
787 xleft2 = (l_int32 *)LEPT_CALLOC(n2,
sizeof(l_int32));
788 if (!line1 || !line2 || !yloc1 || !yloc2 || !xleft1 || !xleft2) {
795 return ERROR_INT(
"calloc failure for an array", __func__, 1);
797 for (i = 0; i < n1; i++) {
798 na1 = numaaGetNuma(naa1, i,
L_CLONE);
799 numaGetIValue(na1, 0, yloc1 + i);
800 numaGetIValue(na1, 1, xleft1 + i);
801 nbox = (numaGetCount(na1) - 1) / 2;
802 if (nbox >= nperline)
806 for (i = 0; i < n2; i++) {
807 na2 = numaaGetNuma(naa2, i,
L_CLONE);
808 numaGetIValue(na2, 0, yloc2 + i);
809 numaGetIValue(na2, 1, xleft2 + i);
810 nbox = (numaGetCount(na2) - 1) / 2;
811 if (nbox >= nperline)
823 nai1 = numaCreate(0);
824 nai2 = numaCreate(0);
825 nasx = numaCreate(0);
826 nasy = numaCreate(0);
827 for (i = 0; i < n1; i++) {
828 if (line1[i] == 0)
continue;
831 na1 = numaaGetNuma(naa1, i,
L_CLONE);
832 for (j = 0; j < n2; j++) {
833 if (line2[j] == 0)
continue;
835 if (L_ABS(y1 - y2) > maxshifty)
continue;
837 if (L_ABS(xl1 - xl2) > maxshiftx)
continue;
840 na2 = numaaGetNuma(naa2, j,
L_CLONE);
843 match = testLineAlignmentX(na1, na2, shiftx, delx, nperline);
845 numaAddNumber(nai1, i);
846 numaAddNumber(nai2, j);
847 numaAddNumber(nasx, shiftx);
848 numaAddNumber(nasy, shifty);
859 countAlignedMatches(nai1, nai2, nasx, nasy, n1, n2, delx, dely,
860 nreq, psame, debugflag);
877testLineAlignmentX(
NUMA *na1,
883l_int32 i, xl1, xr1, xl2, xr2, diffl, diffr;
886 return ERROR_INT(
"na1 not defined", __func__, 1);
888 return ERROR_INT(
"na2 not defined", __func__, 1);
890 for (i = 0; i < nperline; i++) {
891 numaGetIValue(na1, i + 1, &xl1);
892 numaGetIValue(na1, i + 2, &xr1);
893 numaGetIValue(na2, i + 1, &xl2);
894 numaGetIValue(na2, i + 2, &xr2);
895 diffl = L_ABS(xl1 - xl2 - shiftx);
896 diffr = L_ABS(xr1 - xr2 - shiftx);
897 if (diffl > delx || diffr > delx)
927countAlignedMatches(
NUMA *nai1,
939l_int32 i, j, nm, shiftx, shifty, nmatch, diffx, diffy;
940l_int32 *ia1, *ia2, *iasx, *iasy, *index1, *index2;
942 if (!nai1 || !nai2 || !nasx || !nasy)
943 return ERROR_INT(
"4 input numas not defined", __func__, 1);
945 return ERROR_INT(
"&same not defined", __func__, 1);
954 nm = numaGetCount(nai1);
958 ia1 = numaGetIArray(nai1);
959 ia2 = numaGetIArray(nai2);
960 iasx = numaGetIArray(nasx);
961 iasy = numaGetIArray(nasy);
962 index1 = (l_int32 *)LEPT_CALLOC(n1,
sizeof(l_int32));
963 index2 = (l_int32 *)LEPT_CALLOC(n2,
sizeof(l_int32));
964 if (!index1 || !index2)
965 return ERROR_INT(
"calloc fail for array", __func__, 1);
966 for (i = 0; i < nm; i++) {
971 memset(index1, 0, 4 * n1);
972 memset(index2, 0, 4 * n2);
974 index1[ia1[i]] = nmatch;
975 index2[ia2[i]] = nmatch;
982 for (j = 0; j < nm; j++) {
983 if (j == i)
continue;
985 if (index1[ia1[j]] > 0 || index2[ia2[j]] > 0)
continue;
987 diffx = L_ABS(shiftx - iasx[j]);
988 diffy = L_ABS(shifty - iasy[j]);
989 if (diffx > delx || diffy > dely)
continue;
992 index1[ia1[j]] = nmatch;
993 index2[ia2[j]] = nmatch;
994 if (nmatch >= nreq) {
997 printRowIndices(index1, n1, index2, n2);
1014printRowIndices(l_int32 *index1,
1021 lept_stderr(
"Index1: ");
1022 for (i = 0; i < n1; i++) {
1023 if (i && (i % 20 == 0))
1025 lept_stderr(
"%3d", index1[i]);
1029 lept_stderr(
"Index2: ");
1030 for (i = 0; i < n2; i++) {
1031 if (i && (i % 20 == 0))
1033 lept_stderr(
"%3d", index2[i]);
l_ok pixGetWordBoxesInTextlines(PIX *pixs, l_int32 minwidth, l_int32 minheight, l_int32 maxwidth, l_int32 maxheight, BOXA **pboxad, NUMA **pnai)
pixGetWordBoxesInTextlines()
NUMAA * boxaExtractSortedPattern(BOXA *boxa, NUMA *na)
boxaExtractSortedPattern()
l_ok 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()
static const l_int32 JB_WORDS_MIN_HEIGHT
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()
static const l_int32 JB_WORDS_MIN_WIDTH
l_ok 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()
l_ok pixGetWordsInTextlines(PIX *pixs, l_int32 minwidth, l_int32 minheight, l_int32 maxwidth, l_int32 maxheight, BOXA **pboxad, PIXA **ppixad, NUMA **pnai)
pixGetWordsInTextlines()
l_ok pixFindWordAndCharacterBoxes(PIX *pixs, BOX *boxs, l_int32 thresh, BOXA **pboxaw, BOXAA **pboxaac, const char *debugdir)
pixFindWordAndCharacterBoxes()
l_ok 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()