206#include <config_auto.h>
211#include "allheaders.h"
214#define L_BUF_SIZE 512
220static const l_int32 JB_ADDED_PIXELS = 6;
224static const l_int32 MAX_DIFF_WIDTH = 2;
225static const l_int32 MAX_DIFF_HEIGHT = 2;
234static const l_int32 MAX_CONN_COMP_WIDTH = 350;
235static const l_int32 MAX_CHAR_COMP_WIDTH = 350;
236static const l_int32 MAX_WORD_COMP_WIDTH = 1000;
237static const l_int32 MAX_COMP_HEIGHT = 120;
253static JBCLASSER * jbCorrelationInitInternal(l_int32 components,
254 l_int32 maxwidth, l_int32 maxheight, l_float32 thresh,
255 l_float32 weightfactor, l_int32 keep_components);
259static l_int32 findSimilarSizedTemplatesNext(
JBFINDCTX *context);
260static void findSimilarSizedTemplatesDestroy(
JBFINDCTX **pcontext);
261static l_int32 finalPositioningForAlignment(
PIX *pixs, l_int32 x, l_int32 y,
262 l_int32 idelx, l_int32 idely,
PIX *pixt,
263 l_int32 *sumtab, l_int32 *pdx, l_int32 *pdy);
266#define DEBUG_CORRELATION_SCORE 0
287jbRankHausInit(l_int32 components,
295 if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
296 components != JB_WORDS)
297 return (
JBCLASSER *)ERROR_PTR(
"invalid components", __func__, NULL);
298 if (size < 1 || size > 10)
299 return (
JBCLASSER *)ERROR_PTR(
"size not reasonable", __func__, NULL);
300 if (rank < 0.5 || rank > 1.0)
301 return (
JBCLASSER *)ERROR_PTR(
"rank not in [0.5-1.0]", __func__, NULL);
303 if (components == JB_CONN_COMPS)
304 maxwidth = MAX_CONN_COMP_WIDTH;
305 else if (components == JB_CHARACTERS)
306 maxwidth = MAX_CHAR_COMP_WIDTH;
308 maxwidth = MAX_WORD_COMP_WIDTH;
311 maxheight = MAX_COMP_HEIGHT;
313 if ((classer = jbClasserCreate(JB_RANKHAUS, components)) == NULL)
314 return (
JBCLASSER *)ERROR_PTR(
"classer not made", __func__, NULL);
319 classer->
dahash = l_dnaHashCreate(5507, 4);
346jbCorrelationInit(l_int32 components,
350 l_float32 weightfactor)
352 return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh,
373jbCorrelationInitWithoutComponents(l_int32 components,
377 l_float32 weightfactor)
379 return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh,
385jbCorrelationInitInternal(l_int32 components,
389 l_float32 weightfactor,
390 l_int32 keep_components)
394 if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
395 components != JB_WORDS)
396 return (
JBCLASSER *)ERROR_PTR(
"invalid components", __func__, NULL);
397 if (thresh < 0.4 || thresh > 0.98)
398 return (
JBCLASSER *)ERROR_PTR(
"thresh not in range [0.4 - 0.98]",
400 if (weightfactor < 0.0 || weightfactor > 1.0)
401 return (
JBCLASSER *)ERROR_PTR(
"weightfactor not in range [0.0 - 1.0]",
404 if (components == JB_CONN_COMPS)
405 maxwidth = MAX_CONN_COMP_WIDTH;
406 else if (components == JB_CHARACTERS)
407 maxwidth = MAX_CHAR_COMP_WIDTH;
409 maxwidth = MAX_WORD_COMP_WIDTH;
412 maxheight = MAX_COMP_HEIGHT;
415 if ((classer = jbClasserCreate(JB_CORRELATION, components)) == NULL)
416 return (
JBCLASSER *)ERROR_PTR(
"classer not made", __func__, NULL);
421 classer->
dahash = l_dnaHashCreate(5507, 4);
452 return ERROR_INT(
"classer not defined", __func__, 1);
454 return ERROR_INT(
"safiles not defined", __func__, 1);
456 classer->
safiles = sarrayCopy(safiles);
457 nfiles = sarrayGetCount(safiles);
458 for (i = 0; i < nfiles; i++) {
459 fname = sarrayGetString(safiles, i,
L_NOCOPY);
460 if ((pix = pixRead(fname)) == NULL) {
461 L_WARNING(
"image file %d not read\n", __func__, i);
464 if (pixGetDepth(pix) != 1) {
465 L_WARNING(
"image file %d not 1 bpp\n", __func__, i);
468 jbAddPage(classer, pix);
491 return ERROR_INT(
"classer not defined", __func__, 1);
492 if (!pixs || pixGetDepth(pixs) != 1)
493 return ERROR_INT(
"pixs not defined or not 1 bpp", __func__, 1);
495 classer->
w = pixGetWidth(pixs);
496 classer->
h = pixGetHeight(pixs);
501 return ERROR_INT(
"components not made", __func__, 1);
504 jbAddPageComponents(classer, pixs, boxas, pixas);
535 return ERROR_INT(
"classer not defined", __func__, 1);
537 return ERROR_INT(
"pix not defined", __func__, 1);
541 if (!boxas || !pixas || (boxaGetCount(boxas) == 0)) {
549 if (classer->
method == JB_RANKHAUS) {
550 if (jbClassifyRankHaus(classer, boxas, pixas))
551 return ERROR_INT(
"rankhaus classification failed", __func__, 1);
553 if (jbClassifyCorrelation(classer, boxas, pixas))
554 return ERROR_INT(
"correlation classification failed", __func__, 1);
561 if (jbGetULCorners(classer, pixs, boxas))
562 return ERROR_INT(
"UL corners not found", __func__, 1);
565 n = boxaGetCount(boxas);
567 numaAddNumber(classer->
nacomps, n);
589l_int32 n, nt, i, wt, ht, iclass, size, found, testval;
590l_int32 npages, area1, area3;
592l_float32 rank, x1, y1, x2, y2;
594NUMA *naclass, *napage;
599PIX *pix, *pix1, *pix2, *pix3, *pix4;
600PIXA *pixa, *pixa1, *pixa2, *pixat, *pixatd;
602PTA *pta, *ptac, *ptact;
606 return ERROR_INT(
"classer not defined", __func__, 1);
608 return ERROR_INT(
"boxa not defined", __func__, 1);
610 return ERROR_INT(
"pixas not defined", __func__, 1);
611 if ((n = pixaGetCount(pixas)) == 0)
612 return ERROR_INT(
"pixas is empty", __func__, 1);
613 if ((nafg = pixaCountPixels(pixas)) == NULL)
614 return ERROR_INT(
"fg counting failed", __func__, 1);
618 sel = selCreateBrick(size, size, size / 2, size / 2, SEL_HIT);
622 pixa1 = pixaCreate(n);
623 pixa2 = pixaCreate(n);
624 for (i = 0; i < n; i++) {
625 pix = pixaGetPix(pixas, i,
L_CLONE);
626 pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS,
627 JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0);
628 pix2 = pixDilate(NULL, pix1, sel);
636 pta = pixaCentroids(pixa1);
637 ptac = classer->
ptac;
638 ptaJoin(ptac, pta, 0, -1);
639 ptact = classer->
ptact;
656 pixaa = classer->
pixaa;
659 pixat = classer->
pixat;
685 for (i = 0; i < n; i++) {
686 pix1 = pixaGetPix(pixa1, i,
L_CLONE);
687 pix2 = pixaGetPix(pixa2, i,
L_CLONE);
688 ptaGetPt(pta, i, &x1, &y1);
689 nt = pixaGetCount(pixat);
691 findcontext = findSimilarSizedTemplatesInit(classer, pix1);
692 while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) {
694 pix3 = pixaGetPix(pixat, iclass,
L_CLONE);
695 pix4 = pixaGetPix(pixatd, iclass,
L_CLONE);
696 ptaGetPt(ptact, iclass, &x2, &y2);
697 testval = pixHaustest(pix1, pix2, pix3, pix4, x1 - x2, y1 - y2,
698 MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT);
703 numaAddNumber(naclass, iclass);
704 numaAddNumber(napage, npages);
706 pixa = pixaaGetPixa(pixaa, iclass,
L_CLONE);
707 pix = pixaGetPix(pixas, i,
L_CLONE);
709 box = boxaGetBox(boxa, i,
L_CLONE);
716 findSimilarSizedTemplatesDestroy(&findcontext);
717 if (found == FALSE) {
718 numaAddNumber(naclass, nt);
719 numaAddNumber(napage, npages);
720 pixa = pixaCreate(0);
721 pix = pixaGetPix(pixas, i,
L_CLONE);
723 wt = pixGetWidth(pix);
724 ht = pixGetHeight(pix);
725 l_dnaHashAdd(dahash, (l_uint64)ht * wt, nt);
726 box = boxaGetBox(boxa, i,
L_CLONE);
728 pixaaAddPixa(pixaa, pixa,
L_INSERT);
729 ptaAddPt(ptact, x1, y1);
738 nafgt = classer->
nafgt;
739 tab8 = makePixelSumTab8();
740 for (i = 0; i < n; i++) {
741 pix1 = pixaGetPix(pixa1, i,
L_CLONE);
742 numaGetIValue(nafg, i, &area1);
743 pix2 = pixaGetPix(pixa2, i,
L_CLONE);
744 ptaGetPt(pta, i, &x1, &y1);
745 nt = pixaGetCount(pixat);
747 findcontext = findSimilarSizedTemplatesInit(classer, pix1);
748 while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) {
750 pix3 = pixaGetPix(pixat, iclass,
L_CLONE);
751 numaGetIValue(nafgt, iclass, &area3);
752 pix4 = pixaGetPix(pixatd, iclass,
L_CLONE);
753 ptaGetPt(ptact, iclass, &x2, &y2);
754 testval = pixRankHaustest(pix1, pix2, pix3, pix4,
756 MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT,
757 area1, area3, rank, tab8);
762 numaAddNumber(naclass, iclass);
763 numaAddNumber(napage, npages);
765 pixa = pixaaGetPixa(pixaa, iclass,
L_CLONE);
766 pix = pixaGetPix(pixas, i,
L_CLONE);
768 box = boxaGetBox(boxa, i,
L_CLONE);
775 findSimilarSizedTemplatesDestroy(&findcontext);
776 if (found == FALSE) {
777 numaAddNumber(naclass, nt);
778 numaAddNumber(napage, npages);
779 pixa = pixaCreate(0);
780 pix = pixaGetPix(pixas, i,
L_CLONE);
782 wt = pixGetWidth(pix);
783 ht = pixGetHeight(pix);
784 l_dnaHashAdd(dahash, (l_uint64)ht * wt, nt);
785 box = boxaGetBox(boxa, i,
L_CLONE);
787 pixaaAddPixa(pixaa, pixa,
L_INSERT);
788 ptaAddPt(ptact, x1, y1);
791 numaAddNumber(nafgt, area1);
799 classer->
nclass = pixaGetCount(pixat);
838pixHaustest(
PIX *pix1,
847l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch;
851 wi = pixGetWidth(pix1);
852 hi = pixGetHeight(pix1);
853 wt = pixGetWidth(pix3);
854 ht = pixGetHeight(pix3);
855 delw = L_ABS(wi - wt);
858 delh = L_ABS(hi - ht);
865 idelx = (l_int32)(delx + 0.5);
867 idelx = (l_int32)(delx - 0.5);
869 idely = (l_int32)(dely + 0.5);
871 idely = (l_int32)(dely - 0.5);
878 pixt = pixCreateTemplate(pix1);
879 pixRasterop(pixt, 0, 0, wi, hi,
PIX_SRC, pix1, 0, 0);
882 pixZero(pixt, &boolmatch);
883 if (boolmatch == 0) {
893 pixRasterop(pixt, idelx, idely, wt, ht,
PIX_SRC, pix3, 0, 0);
895 pixZero(pixt, &boolmatch);
936pixRankHaustest(
PIX *pix1,
949l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch;
950l_int32 thresh1, thresh3;
954 wi = pixGetWidth(pix1);
955 hi = pixGetHeight(pix1);
956 wt = pixGetWidth(pix3);
957 ht = pixGetHeight(pix3);
958 delw = L_ABS(wi - wt);
961 delh = L_ABS(hi - ht);
966 thresh1 = (l_int32)(area1 * (1. - rank) + 0.5);
967 thresh3 = (l_int32)(area3 * (1. - rank) + 0.5);
972 idelx = (l_int32)(delx + 0.5);
974 idelx = (l_int32)(delx - 0.5);
976 idely = (l_int32)(dely + 0.5);
978 idely = (l_int32)(dely - 0.5);
985 pixt = pixCreateTemplate(pix1);
986 pixRasterop(pixt, 0, 0, wi, hi,
PIX_SRC, pix1, 0, 0);
989 pixThresholdPixelSum(pixt, thresh1, &boolmatch, tab8);
990 if (boolmatch == 1) {
1000 pixRasterop(pixt, idelx, idely, wt, ht,
PIX_SRC, pix3, 0, 0);
1002 pixThresholdPixelSum(pixt, thresh3, &boolmatch, tab8);
1023jbClassifyCorrelation(
JBCLASSER *classer,
1027l_int32 n, nt, i, iclass, wt, ht, found, area, area1, area2, npages,
1029l_int32 *sumtab, *centtab;
1031l_float32 x1, y1, x2, y2, xsum, ysum;
1032l_float32 thresh, weight, threshold;
1034NUMA *naclass, *napage;
1039PIX *pix, *pix1, *pix2;
1040PIXA *pixa, *pixa1, *pixat;
1042PTA *pta, *ptac, *ptact;
1045l_int32 x, y, rowcount, downcount, wpl;
1049 return ERROR_INT(
"classer not found", __func__, 1);
1051 return ERROR_INT(
"boxa not found", __func__, 1);
1053 return ERROR_INT(
"pixas not found", __func__, 1);
1055 npages = classer->
npages;
1059 if ((n = pixaGetCount(pixas)) == 0) {
1060 L_WARNING(
"pixas is empty\n", __func__);
1063 pixa1 = pixaCreate(n);
1064 for (i = 0; i < n; i++) {
1065 pix = pixaGetPix(pixas, i,
L_CLONE);
1066 pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS,
1067 JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0);
1074 napage = classer->
napage;
1077 nafgt = classer->
nafgt;
1078 sumtab = makePixelSumTab8();
1080 pixcts = (l_int32 *)LEPT_CALLOC(n,
sizeof(*pixcts));
1081 pixrowcts = (l_int32 **)LEPT_CALLOC(n,
sizeof(*pixrowcts));
1082 centtab = makePixelCentroidTab8();
1092 for (i = 0; i < n; i++) {
1093 pix = pixaGetPix(pixa1, i,
L_CLONE);
1094 pixrowcts[i] = (l_int32 *)LEPT_CALLOC(pixGetHeight(pix),
1095 sizeof(**pixrowcts));
1098 wpl = pixGetWpl(pix);
1099 row = pixGetData(pix) + (pixGetHeight(pix) - 1) * wpl;
1101 for (y = pixGetHeight(pix) - 1; y >= 0; y--, row -= wpl) {
1102 pixrowcts[i][y] = downcount;
1104 for (x = 0; x < wpl; x++) {
1107 rowcount += sumtab[byte];
1108 xsum += centtab[byte] + (x * 32 + 24) * sumtab[
byte];
1109 byte = (word >> 8) & 0xff;
1110 rowcount += sumtab[byte];
1111 xsum += centtab[byte] + (x * 32 + 16) * sumtab[
byte];
1112 byte = (word >> 16) & 0xff;
1113 rowcount += sumtab[byte];
1114 xsum += centtab[byte] + (x * 32 + 8) * sumtab[
byte];
1115 byte = (word >> 24) & 0xff;
1116 rowcount += sumtab[byte];
1117 xsum += centtab[byte] + x * 32 * sumtab[byte];
1119 downcount += rowcount;
1120 ysum += rowcount * y;
1122 pixcts[i] = downcount;
1123 if (downcount > 0) {
1125 xsum / (l_float32)downcount, ysum / (l_float32)downcount);
1127 L_ERROR(
"downcount == 0 !\n", __func__);
1128 ptaAddPt(pta, pixGetWidth(pix) / 2, pixGetHeight(pix) / 2);
1133 ptac = classer->
ptac;
1134 ptaJoin(ptac, pta, 0, -1);
1135 ptact = classer->
ptact;
1148 pixaa = classer->
pixaa;
1151 pixat = classer->
pixat;
1171 thresh = classer->
thresh;
1173 naarea = classer->
naarea;
1174 dahash = classer->
dahash;
1175 for (i = 0; i < n; i++) {
1176 pix1 = pixaGetPix(pixa1, i,
L_CLONE);
1178 ptaGetPt(pta, i, &x1, &y1);
1179 nt = pixaGetCount(pixat);
1181 findcontext = findSimilarSizedTemplatesInit(classer, pix1);
1182 while ( (iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) {
1184 pix2 = pixaGetPix(pixat, iclass,
L_CLONE);
1185 numaGetIValue(nafgt, iclass, &area2);
1186 ptaGetPt(ptact, iclass, &x2, &y2);
1190 numaGetIValue(naarea, iclass, &area);
1191 threshold = thresh + (1. - thresh) * weight * area2 / area;
1197 overthreshold = pixCorrelationScoreThresholded(pix1, pix2,
1198 area1, area2, x1 - x2, y1 - y2,
1199 MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT,
1200 sumtab, pixrowcts[i], threshold);
1201#if DEBUG_CORRELATION_SCORE
1203 l_float32 score, testscore;
1204 l_int32 count, testcount;
1205 pixCorrelationScore(pix1, pix2, area1, area2, x1 - x2, y1 - y2,
1206 MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT,
1209 pixCorrelationScoreSimple(pix1, pix2, area1, area2,
1210 x1 - x2, y1 - y2, MAX_DIFF_WIDTH,
1211 MAX_DIFF_HEIGHT, sumtab, &testscore);
1212 count = (l_int32)rint(sqrt(score * area1 * area2));
1213 testcount = (l_int32)rint(sqrt(testscore * area1 * area2));
1214 if ((score >= threshold) != (testscore >= threshold)) {
1215 lept_stderr(
"Correlation score mismatch: "
1216 "%d(%g,%d) vs %d(%g,%d) (%g)\n",
1217 count, score, score >= threshold,
1218 testcount, testscore, testscore >= threshold,
1222 if ((score >= threshold) != overthreshold) {
1223 lept_stderr(
"Mismatch between correlation/threshold "
1224 "comparison: %g(%g,%d) >= %g(%g) vs %s\n",
1225 score, score*area1*area2, count, threshold,
1226 threshold*area1*area2,
1227 (overthreshold ?
"true" :
"false"));
1233 if (overthreshold) {
1235 numaAddNumber(naclass, iclass);
1236 numaAddNumber(napage, npages);
1239 pixa = pixaaGetPixa(pixaa, iclass,
L_CLONE);
1240 pix = pixaGetPix(pixas, i,
L_CLONE);
1242 box = boxaGetBox(boxa, i,
L_CLONE);
1249 findSimilarSizedTemplatesDestroy(&findcontext);
1250 if (found == FALSE) {
1251 numaAddNumber(naclass, nt);
1252 numaAddNumber(napage, npages);
1253 pixa = pixaCreate(0);
1254 pix = pixaGetPix(pixas, i,
L_CLONE);
1256 wt = pixGetWidth(pix);
1257 ht = pixGetHeight(pix);
1258 l_dnaHashAdd(dahash, (l_uint64)ht * wt, nt);
1259 box = boxaGetBox(boxa, i,
L_CLONE);
1261 pixaaAddPixa(pixaa, pixa,
L_INSERT);
1262 ptaAddPt(ptact, x1, y1);
1263 numaAddNumber(nafgt, area1);
1265 area = (pixGetWidth(pix1) - 2 * JB_ADDED_PIXELS) *
1266 (pixGetHeight(pix1) - 2 * JB_ADDED_PIXELS);
1267 numaAddNumber(naarea, area);
1272 classer->
nclass = pixaGetCount(pixat);
1276 for (i = 0; i < n; i++) {
1277 LEPT_FREE(pixrowcts[i]);
1279 LEPT_FREE(pixrowcts);
1283 pixaDestroy(&pixa1);
1303jbGetComponents(
PIX *pixs,
1310l_int32 empty, res, redfactor;
1312PIX *pix1, *pix2, *pix3;
1316 return ERROR_INT(
"&boxad not defined", __func__, 1);
1319 return ERROR_INT(
"&pixad not defined", __func__, 1);
1322 return ERROR_INT(
"pixs not defined", __func__, 1);
1323 if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
1324 components != JB_WORDS)
1325 return ERROR_INT(
"invalid components", __func__, 1);
1327 pixZero(pixs, &empty);
1329 *pboxad = boxaCreate(0);
1330 *ppixad = pixaCreate(0);
1347 if (components == JB_CONN_COMPS) {
1348 boxa = pixConnComp(pixs, &pixa, 8);
1349 }
else if (components == JB_CHARACTERS) {
1350 pix1 = pixMorphSequence(pixs,
"c1.6", 0);
1351 boxa = pixConnComp(pix1, &pixat, 8);
1352 pixa = pixaClipToPix(pixat, pixs);
1354 pixaDestroy(&pixat);
1362 res = pixGetXRes(pixs);
1365 pix1 = pixClone(pixs);
1366 }
else if (res <= 400) {
1368 pix1 = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0);
1371 pix1 = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0);
1376 pixWordMaskByDilation(pix1, &pix2, NULL, NULL);
1379 pix3 = pixExpandReplicate(pix2, redfactor);
1391 boxa = pixConnComp(pix3, &pixat, 4);
1392 pixa = pixaClipToPix(pixat, pixs);
1393 pixaDestroy(&pixat);
1443pixWordMaskByDilation(
PIX *pixs,
1448l_int32 i, n, ndil, maxdiff, diff, ibest;
1449l_int32 check, count, total, xres;
1456 if (ppixm) *ppixm = NULL;
1457 if (psize) *psize = 0;
1458 if (!pixs || pixGetDepth(pixs) != 1)
1459 return ERROR_INT(
"pixs undefined or not 1 bpp", __func__, 1);
1460 if (!ppixm && !psize)
1461 return ERROR_INT(
"no output requested", __func__, 1);
1465 pix1 = pixCopy(NULL, pixs);
1467 nacc = numaCreate(ndil + 1);
1468 nadiff = numaCreate(ndil + 1);
1469 for (i = 0; i <= ndil; i++) {
1471 pix2 = pixCopy(NULL, pix1);
1473 pix2 = pixMorphSequence(pix1,
"d2.1", 0);
1474 boxa = pixConnCompBB(pix2, 4);
1475 ncc[i] = boxaGetCount(boxa);
1476 numaAddNumber(nacc, ncc[i]);
1477 if (i == 0) total = ncc[0];
1479 diff = ncc[i - 1] - ncc[i];
1480 numaAddNumber(nadiff, diff);
1491 diffa = numaGetIArray(nadiff);
1492 n = numaGetCount(nadiff);
1496 for (i = 1; i < n; i++) {
1497 numaGetIValue(nacc, i, &count);
1498 if (check && count < 0.3 * total) {
1509 xres = pixGetXRes(pixs);
1510 if (xres == 0) xres = 150;
1511 if (xres > 110) ibest++;
1513 L_INFO(
"setting ibest to minimum allowed value of 2\n", __func__);
1518 lept_mkdir(
"lept/jb");
1521 L_INFO(
"Best dilation: %d\n", __func__, L_MAX(3, ibest + 1));
1522 naseq = numaMakeSequence(1, 1, numaGetCount(nacc));
1523 pix3 = gplotGeneralPix2(naseq, nacc, GPLOT_LINES,
1524 "/tmp/lept/jb/numcc",
1525 "Number of cc vs. horizontal dilation",
1526 "Sel horiz",
"Number of cc");
1527 pixaAddPix(pixadb, pix3,
L_INSERT);
1528 numaDestroy(&naseq);
1529 naseq = numaMakeSequence(1, 1, numaGetCount(nadiff));
1530 pix3 = gplotGeneralPix2(naseq, nadiff, GPLOT_LINES,
1531 "/tmp/lept/jb/diffcc",
1532 "Diff count of cc vs. horizontal dilation",
1533 "Sel horiz",
"Diff in cc");
1534 pixaAddPix(pixadb, pix3,
L_INSERT);
1535 numaDestroy(&naseq);
1536 pix3 = pixCloseBrick(NULL, pixs, ibest + 1, 1);
1537 pix4 = pixScaleToSize(pix3, 600, 0);
1538 pixaAddPix(pixadb, pix4,
L_INSERT);
1543 if (psize) *psize = ibest + 1;
1545 *ppixm = pixCloseBrick(NULL, pixs, ibest + 1, 1);
1548 numaDestroy(&nadiff);
1573pixWordBoxesByDilation(
PIX *pixs,
1585 if (psize) *psize = 0;
1586 if (!pixs || pixGetDepth(pixs) != 1)
1587 return ERROR_INT(
"pixs undefined or not 1 bpp", __func__, 1);
1589 return ERROR_INT(
"&boxa not defined", __func__, 1);
1593 if (pixWordMaskByDilation(pixs, &pix1, psize, pixadb))
1594 return ERROR_INT(
"pixWordMaskByDilation() failed", __func__, 1);
1600 boxa1 = pixConnComp(pix1, NULL, 8);
1606 pix2 = pixUnpackBinary(pixs, 32, 1);
1607 pixRenderBoxaArb(pix2, boxa1, 2, 255, 0, 0);
1608 pixaAddPix(pixadb, pix2,
L_INSERT);
1609 pix2 = pixUnpackBinary(pixs, 32, 1);
1610 pixRenderBoxaArb(pix2, boxa2, 2, 0, 255, 0);
1611 pixaAddPix(pixadb, pix2,
L_INSERT);
1613 boxaDestroy(&boxa1);
1614 boxaDestroy(&boxa2);
1633jbAccumulateComposites(
PIXAA *pixaa,
1637l_int32 n, nt, i, j, d, minw, maxw, minh, maxh, xdiff, ydiff;
1638l_float32 x, y, xave, yave;
1640PIX *pix, *pixt1, *pixt2, *pixsum;
1645 return (
PIXA *)ERROR_PTR(
"&ptat not defined", __func__, NULL);
1648 return (
PIXA *)ERROR_PTR(
"&na not defined", __func__, NULL);
1651 return (
PIXA *)ERROR_PTR(
"pixaa not defined", __func__, NULL);
1653 n = pixaaGetCount(pixaa, NULL);
1654 if ((ptat = ptaCreate(n)) == NULL)
1655 return (
PIXA *)ERROR_PTR(
"ptat not made", __func__, NULL);
1657 pixad = pixaCreate(n);
1661 for (i = 0; i < n; i++) {
1662 pixa = pixaaGetPixa(pixaa, i,
L_CLONE);
1663 nt = pixaGetCount(pixa);
1664 numaAddNumber(na, nt);
1666 L_WARNING(
"empty pixa found!\n", __func__);
1670 pixaSizeRange(pixa, &minw, &minh, &maxw, &maxh);
1671 pix = pixaGetPix(pixa, 0,
L_CLONE);
1672 d = pixGetDepth(pix);
1674 pixt1 = pixCreate(maxw, maxh, d);
1675 pixsum = pixInitAccumulate(maxw, maxh, 0);
1676 pta = pixaCentroids(pixa);
1680 for (j = 0; j < nt; j++) {
1681 ptaGetPt(pta, j, &x, &y);
1685 xave = xave / (l_float32)nt;
1686 yave = yave / (l_float32)nt;
1689 for (j = 0; j < nt; j++) {
1690 pixt2 = pixaGetPix(pixa, j,
L_CLONE);
1691 ptaGetPt(pta, j, &x, &y);
1692 xdiff = (l_int32)(x - xave);
1693 ydiff = (l_int32)(y - yave);
1695 pixRasterop(pixt1, xdiff, ydiff, maxw, maxh,
PIX_SRC,
1697 pixAccumulate(pixsum, pixt1, L_ARITH_ADD);
1700 pixaAddPix(pixad, pixsum,
L_INSERT);
1701 ptaAddPt(ptat, xave, yave);
1721jbTemplatesFromComposites(
PIXA *pixac,
1732 return (
PIXA *)ERROR_PTR(
"pixac not defined", __func__, NULL);
1734 return (
PIXA *)ERROR_PTR(
"na not defined", __func__, NULL);
1736 n = pixaGetCount(pixac);
1737 pixad = pixaCreate(n);
1738 for (i = 0; i < n; i++) {
1739 pixsum = pixaGetPix(pixac, i,
L_COPY);
1740 numaGetFValue(na, i, &nt);
1742 pixMultConstAccumulate(pixsum, factor, 0);
1743 pixd = pixFinalAccumulate(pixsum, 0, 8);
1745 pixDestroy(&pixsum);
1764jbClasserCreate(l_int32 method,
1769 if (method != JB_RANKHAUS && method != JB_CORRELATION)
1770 return (
JBCLASSER *)ERROR_PTR(
"invalid method", __func__, NULL);
1771 if (components != JB_CONN_COMPS && components != JB_CHARACTERS &&
1772 components != JB_WORDS)
1773 return (
JBCLASSER *)ERROR_PTR(
"invalid component", __func__, NULL);
1776 classer->
method = method;
1778 classer->
nacomps = numaCreate(0);
1779 classer->
pixaa = pixaaCreate(0);
1780 classer->
pixat = pixaCreate(0);
1781 classer->
pixatd = pixaCreate(0);
1782 classer->
nafgt = numaCreate(0);
1783 classer->
naarea = numaCreate(0);
1784 classer->
ptac = ptaCreate(0);
1785 classer->
ptact = ptaCreate(0);
1786 classer->
naclass = numaCreate(0);
1787 classer->
napage = numaCreate(0);
1788 classer->
ptaul = ptaCreate(0);
1806 if ((classer = *pclasser) == NULL)
1809 sarrayDestroy(&classer->
safiles);
1810 numaDestroy(&classer->
nacomps);
1811 pixaaDestroy(&classer->
pixaa);
1812 pixaDestroy(&classer->
pixat);
1813 pixaDestroy(&classer->
pixatd);
1814 l_dnaHashDestroy(&classer->
dahash);
1815 numaDestroy(&classer->
nafgt);
1816 numaDestroy(&classer->
naarea);
1817 ptaDestroy(&classer->
ptac);
1818 ptaDestroy(&classer->
ptact);
1819 numaDestroy(&classer->
naclass);
1820 numaDestroy(&classer->
napage);
1821 ptaDestroy(&classer->
ptaul);
1822 ptaDestroy(&classer->
ptall);
1856 return (
JBDATA *)ERROR_PTR(
"classer not defined", __func__, NULL);
1859 pixaSizeRange(classer->
pixat, NULL, NULL, &maxw, &maxh);
1860 pix = pixaDisplayOnLattice(classer->
pixat, maxw + 1, maxh + 1,
1863 return (
JBDATA *)ERROR_PTR(
"data not made", __func__, NULL);
1868 data->
w = classer->
w;
1869 data->
h = classer->
h;
1887jbDataDestroy(
JBDATA **pdata)
1893 if ((data = *pdata) == NULL)
1896 pixDestroy(&data->
pix);
1898 numaDestroy(&data->
napage);
1899 ptaDestroy(&data->
ptaul);
1918jbDataWrite(
const char *rootout,
1922l_int32 w, h, nclass, npages, cellw, cellh, ncomp, i, x, y, iclass, ipage;
1923NUMA *naclass, *napage;
1929 return ERROR_INT(
"no rootout", __func__, 1);
1931 return ERROR_INT(
"no jbdata", __func__, 1);
1942 ptaul = jbdata->
ptaul;
1945 pixWrite(buf, pixt, IFF_PNG);
1947 snprintf(buf, L_BUF_SIZE,
"%s%s", rootout, JB_DATA_EXT);
1948 if ((fp = fopenWriteStream(buf,
"wb")) == NULL)
1949 return ERROR_INT_1(
"stream not opened", buf, __func__, 1);
1950 ncomp = ptaGetCount(ptaul);
1951 fprintf(fp,
"jb data file\n");
1952 fprintf(fp,
"num pages = %d\n", npages);
1953 fprintf(fp,
"page size: w = %d, h = %d\n", w, h);
1954 fprintf(fp,
"num components = %d\n", ncomp);
1955 fprintf(fp,
"num classes = %d\n", nclass);
1956 fprintf(fp,
"template lattice size: w = %d, h = %d\n", cellw, cellh);
1957 for (i = 0; i < ncomp; i++) {
1958 numaGetIValue(napage, i, &ipage);
1959 numaGetIValue(naclass, i, &iclass);
1960 ptaGetIPt(ptaul, i, &x, &y);
1961 fprintf(fp,
"%d %d %d %d\n", ipage, iclass, x, y);
1976jbDataRead(
const char *rootname)
1981l_int32 nsa, i, w, h, cellw, cellh, x, y, iclass, ipage;
1982l_int32 npages, nclass, ncomp, ninit;
1985NUMA *naclass, *napage;
1991 return (
JBDATA *)ERROR_PTR(
"rootname not defined", __func__, NULL);
1994 if ((pixs = pixRead(fname)) == NULL)
1995 return (
JBDATA *)ERROR_PTR(
"pix not read", __func__, NULL);
1997 snprintf(fname, L_BUF_SIZE,
"%s%s", rootname, JB_DATA_EXT);
1998 if ((data = l_binaryRead(fname, &size)) == NULL) {
2000 return (
JBDATA *)ERROR_PTR(
"data not read", __func__, NULL);
2003 if ((sa = sarrayCreateLinesFromString((
char *)data, 0)) == NULL) {
2006 return (
JBDATA *)ERROR_PTR(
"sa not made", __func__, NULL);
2008 nsa = sarrayGetCount(sa);
2009 linestr = sarrayGetString(sa, 0,
L_NOCOPY);
2010 if (strcmp(linestr,
"jb data file") != 0) {
2014 return (
JBDATA *)ERROR_PTR(
"invalid jb data file", __func__, NULL);
2016 linestr = sarrayGetString(sa, 1,
L_NOCOPY);
2017 sscanf(linestr,
"num pages = %d", &npages);
2018 linestr = sarrayGetString(sa, 2,
L_NOCOPY);
2019 sscanf(linestr,
"page size: w = %d, h = %d", &w, &h);
2020 linestr = sarrayGetString(sa, 3,
L_NOCOPY);
2021 sscanf(linestr,
"num components = %d", &ncomp);
2022 linestr = sarrayGetString(sa, 4,
L_NOCOPY);
2023 sscanf(linestr,
"num classes = %d\n", &nclass);
2024 linestr = sarrayGetString(sa, 5,
L_NOCOPY);
2025 sscanf(linestr,
"template lattice size: w = %d, h = %d\n", &cellw, &cellh);
2028 lept_stderr(
"num pages = %d\n", npages);
2029 lept_stderr(
"page size: w = %d, h = %d\n", w, h);
2030 lept_stderr(
"num components = %d\n", ncomp);
2031 lept_stderr(
"num classes = %d\n", nclass);
2032 lept_stderr(
"template lattice size: w = %d, h = %d\n", cellw, cellh);
2036 if (ncomp > 1000000) {
2037 L_WARNING(
"ncomp > 1M\n", __func__);
2040 naclass = numaCreate(ninit);
2041 napage = numaCreate(ninit);
2042 ptaul = ptaCreate(ninit);
2043 for (i = 6; i < nsa; i++) {
2044 linestr = sarrayGetString(sa, i,
L_NOCOPY);
2045 sscanf(linestr,
"%d %d %d %d\n", &ipage, &iclass, &x, &y);
2046 numaAddNumber(napage, ipage);
2047 numaAddNumber(naclass, iclass);
2048 ptaAddPt(ptaul, x, y);
2061 jbdata->
ptaul = ptaul;
2079jbDataRender(
JBDATA *data,
2082l_int32 i, w, h, cellw, cellh, x, y, iclass, ipage;
2083l_int32 npages, nclass, ncomp, wp, hp;
2087NUMA *naclass, *napage;
2088PIX *pixt, *pix1, *pix2, *pixd;
2095 return (
PIXA *)ERROR_PTR(
"data not defined", __func__, NULL);
2106 ptaul = data->
ptaul;
2107 ncomp = numaGetCount(naclass);
2114 if ((pixad = pixaCreate(npages)) == NULL)
2115 return (
PIXA *)ERROR_PTR(
"pixad not made", __func__, NULL);
2116 for (i = 0; i < npages; i++) {
2117 if (debugflag == FALSE) {
2118 pix1 = pixCreate(w, h, 1);
2120 pix1 = pixCreate(w, h, 2);
2121 cmap = pixcmapCreate(2);
2122 pixcmapAddColor(cmap, 255, 255, 255);
2123 pixcmapAddColor(cmap, 0, 0, 0);
2124 pixSetColormap(pix1, cmap);
2130 if ((pixat = pixaCreateFromPix(pixt, nclass, cellw, cellh)) == NULL) {
2131 pixaDestroy(&pixad);
2132 return (
PIXA *)ERROR_PTR(
"pixat not made", __func__, NULL);
2146 if (debugflag == TRUE) {
2147 baa = boxaaCreate(npages);
2148 boxa = boxaCreate(0);
2149 boxaaInitFull(baa, boxa);
2152 for (i = 0; i < ncomp; i++) {
2153 numaGetIValue(napage, i, &ipage);
2154 numaGetIValue(naclass, i, &iclass);
2155 pix1 = pixaGetPix(pixat, iclass,
L_CLONE);
2156 wp = pixGetWidth(pix1);
2157 hp = pixGetHeight(pix1);
2158 ptaGetIPt(ptaul, i, &x, &y);
2159 pixd = pixaGetPix(pixad, ipage,
L_CLONE);
2160 if (debugflag == FALSE) {
2163 pix2 = pixConvert1To2Cmap(pix1);
2165 boxa = boxaaGetBoxa(baa, ipage,
L_CLONE);
2166 box = boxCreate(x, y, wp, hp);
2177 if (debugflag == TRUE) {
2178 for (i = 0; i < npages; i++) {
2179 pixd = pixaGetPix(pixad, i,
L_CLONE);
2180 boxa = boxaaGetBoxa(baa, i,
L_CLONE);
2181 pixRenderBoxaArb(pixd, boxa, 1, 255, 0, 0);
2188 pixaDestroy(&pixat);
2223l_int32 i, baseindex, index, n, iclass, idelx, idely, x, y, dx, dy;
2225l_float32 x1, x2, y1, y2, delx, dely;
2229PTA *ptac, *ptact, *ptaul;
2232 return ERROR_INT(
"classer not defined", __func__, 1);
2234 return ERROR_INT(
"pixs not defined", __func__, 1);
2236 return ERROR_INT(
"boxa not defined", __func__, 1);
2238 n = boxaGetCount(boxa);
2239 ptaul = classer->
ptaul;
2241 ptac = classer->
ptac;
2242 ptact = classer->
ptact;
2244 sumtab = makePixelSumTab8();
2245 for (i = 0; i < n; i++) {
2246 index = baseindex + i;
2247 ptaGetPt(ptac, index, &x1, &y1);
2248 numaGetIValue(naclass, index, &iclass);
2249 ptaGetPt(ptact, iclass, &x2, &y2);
2253 idelx = (l_int32)(delx + 0.5);
2255 idelx = (l_int32)(delx - 0.5);
2257 idely = (l_int32)(dely + 0.5);
2259 idely = (l_int32)(dely - 0.5);
2260 if ((box = boxaGetBox(boxa, i,
L_CLONE)) == NULL) {
2262 return ERROR_INT(
"box not found", __func__, 1);
2264 boxGetGeometry(box, &x, &y, NULL, NULL);
2268 finalPositioningForAlignment(pixs, x, y, idelx, idely,
2269 pixt, sumtab, &dx, &dy);
2272 ptaAddPt(ptaul, x - idelx + dx, y - idely + dy);
2311l_int32 i, iclass, n, x1, y1, h;
2318 return ERROR_INT(
"classer not defined", __func__, 1);
2320 ptaul = classer->
ptaul;
2322 pixat = classer->
pixat;
2324 ptaDestroy(&classer->
ptall);
2325 n = ptaGetCount(ptaul);
2326 ptall = ptaCreate(n);
2327 classer->
ptall = ptall;
2334 for (i = 0; i < n; i++) {
2335 ptaGetIPt(ptaul, i, &x1, &y1);
2336 numaGetIValue(naclass, i, &iclass);
2337 pix = pixaGetPix(pixat, iclass,
L_CLONE);
2338 h = pixGetHeight(pix);
2339 ptaAddPt(ptall, x1, y1 + h - 1 - 2 * JB_ADDED_PIXELS);
2354static int two_by_two_walk[50] = {
2390findSimilarSizedTemplatesInit(
JBCLASSER *classer,
2396 state->w = pixGetWidth(pixs) - 2 * JB_ADDED_PIXELS;
2397 state->h = pixGetHeight(pixs) - 2 * JB_ADDED_PIXELS;
2398 state->classer = classer;
2404findSimilarSizedTemplatesDestroy(
JBFINDCTX **pstate)
2408 if (pstate == NULL) {
2409 L_WARNING(
"ptr address is null\n", __func__);
2412 if ((state = *pstate) == NULL)
2415 l_dnaDestroy(&state->dna);
2440findSimilarSizedTemplatesNext(
JBFINDCTX *state)
2442l_int32 desiredh, desiredw, size, templ;
2446 if (state->i >= 25) {
2450 desiredw = state->w + two_by_two_walk[2 * state->i];
2451 desiredh = state->h + two_by_two_walk[2 * state->i + 1];
2452 if (desiredh < 1 || desiredw < 1) {
2459 state->dna = l_dnaHashGetDna(state->classer->
dahash,
2460 (l_uint64)desiredh * desiredw,
L_CLONE);
2470 size = l_dnaGetCount(state->dna);
2471 for ( ; state->n < size; ) {
2472 templ = (l_int32)(state->dna->
array[state->n++] + 0.5);
2473 pixt = pixaGetPix(state->classer->
pixat, templ,
L_CLONE);
2474 if (pixGetWidth(pixt) - 2 * JB_ADDED_PIXELS == desiredw &&
2475 pixGetHeight(pixt) - 2 * JB_ADDED_PIXELS == desiredh) {
2485 l_dnaDestroy(&state->dna);
2507finalPositioningForAlignment(
PIX *pixs,
2517l_int32 w, h, i, j, minx, miny, count, mincount;
2523 return ERROR_INT(
"pixs not defined", __func__, 1);
2525 return ERROR_INT(
"pixt not defined", __func__, 1);
2527 return ERROR_INT(
"&dx and &dy not both defined", __func__, 1);
2529 return ERROR_INT(
"sumtab not defined", __func__, 1);
2533 pixGetDimensions(pixt, &w, &h, NULL);
2534 box = boxCreate(x - idelx - JB_ADDED_PIXELS,
2535 y - idely - JB_ADDED_PIXELS, w, h);
2536 pixi = pixClipRectangle(pixs, box, NULL);
2539 return ERROR_INT(
"pixi not made", __func__, 1);
2541 pixr = pixCreate(pixGetWidth(pixi), pixGetHeight(pixi), 1);
2542 mincount = 0x7fffffff;
2543 for (i = -1; i <= 1; i++) {
2544 for (j = -1; j <= 1; j++) {
2545 pixCopy(pixr, pixi);
2547 pixCountPixels(pixr, &count, sumtab);
2548 if (count < mincount) {
struct L_DnaHash * dahash