83#include <config_auto.h>
87#include "allheaders.h"
91static const l_int32 MinWidth = 100;
92static const l_int32 MinHeight = 100;
120l_int32 w, h, htfound, tlfound;
121PIX *pixr, *pix1, *pix2;
132 if (ppixhm) *ppixhm = NULL;
133 if (ppixtm) *ppixtm = NULL;
134 if (ppixtb) *ppixtb = NULL;
135 if (!pixs || pixGetDepth(pixs) != 1)
136 return ERROR_INT(
"pixs undefined or not 1 bpp", __func__, 1);
137 pixGetDimensions(pixs, &w, &h, NULL);
138 if (w < MinWidth || h < MinHeight) {
139 L_ERROR(
"pix too small: w = %d, h = %d\n", __func__, w, h);
144 pixr = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0);
145 if (pixadb) pixaAddPix(pixadb, pixr,
L_COPY);
156 pixDestroy(&pixtext);
166 if (pixadb) pixaAddPix(pixadb, pixtbf2,
L_COPY);
171 pixhm = pixExpandReplicate(pixhm2, 2);
172 pix1 = pixSeedfillBinary(NULL, pixhm, pixs, 8);
173 pixOr(pixhm, pixhm, pix1);
176 if (pixadb) pixaAddPix(pixadb, pixhm,
L_COPY);
178 pix1 = pixExpandReplicate(pixtm2, 2);
179 pixtm = pixDilateBrick(NULL, pix1, 3, 3);
182 if (pixadb) pixaAddPix(pixadb, pixtm,
L_COPY);
185 pix1 = pixExpandReplicate(pixtbf2, 2);
186 pixtb = pixDilateBrick(NULL, pix1, 3, 3);
187 pixDestroy(&pixtbf2);
189 if (pixadb) pixaAddPix(pixadb, pixtb,
L_COPY);
191 pixtb = pixCreateTemplate(pixs);
196 pix1 = pixSubtract(NULL, pixs, pixtm);
197 pix2 = pixSubtract(NULL, pix1, pixhm);
207 boxa = pixConnComp(pixtm, &pixa, 8);
208 pixGetDimensions(pixtm, &w, &h, NULL);
209 pix1 = pixaDisplayRandomCmap(pixa, w, h);
210 pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255);
220 ptaa = pixGetOuterBordersPtaa(pixtb);
221 lept_mkdir(
"lept/pageseg");
222 ptaaWriteDebug(
"/tmp/lept/pageseg/tb_outlines.ptaa", ptaa, 1);
223 pix1 = pixRenderRandomCmapPtaa(pixtb, ptaa, 1, 16, 1);
224 cmap = pixGetColormap(pix1);
225 pixcmapResetColor(cmap, 0, 130, 130, 130);
232 BOXA *bahm, *batm, *batb;
233 bahm = pixConnComp(pixhm, NULL, 4);
234 batm = pixConnComp(pixtm, NULL, 4);
235 batb = pixConnComp(pixtb, NULL, 4);
236 boxaWriteDebug(
"/tmp/lept/pageseg/htmask.boxa", bahm);
237 boxaWriteDebug(
"/tmp/lept/pageseg/textmask.boxa", batm);
238 boxaWriteDebug(
"/tmp/lept/pageseg/textblock.boxa", batb);
244 pixaConvertToPdf(pixadb, 0, 1.0, 0, 0,
"Debug page segmentation",
245 "/tmp/lept/pageseg/debug.pdf");
246 L_INFO(
"Writing debug pdf to /tmp/lept/pageseg/debug.pdf\n", __func__);
311PIX *pix1, *pix2, *pixhs, *pixhm, *pixd;
313 if (ppixtext) *ppixtext = NULL;
314 if (phtfound) *phtfound = 0;
315 if (!pixs || pixGetDepth(pixs) != 1)
316 return (
PIX *)ERROR_PTR(
"pixs undefined or not 1 bpp", __func__, NULL);
317 pixGetDimensions(pixs, &w, &h, NULL);
318 if (w < MinWidth || h < MinHeight) {
319 L_ERROR(
"pix too small: w = %d, h = %d\n", __func__, w, h);
324 pix1 = pixReduceRankBinaryCascade(pixs, 4, 4, 0, 0);
325 pix2 = pixOpenBrick(NULL, pix1, 5, 5);
326 pixhs = pixExpandReplicate(pix2, 4);
329 if (pixadb) pixaAddPix(pixadb, pixhs,
L_COPY);
332 pixhm = pixCloseSafeBrick(NULL, pixs, 4, 4);
333 if (pixadb) pixaAddPix(pixadb, pixhm,
L_COPY);
336 pixd = pixSeedfillBinary(NULL, pixhs, pixhm, 4);
337 if (pixadb) pixaAddPix(pixadb, pixd,
L_COPY);
340 pixOpenBrick(pixd, pixd, 9, 9);
344 pixZero(pixd, &empty);
345 if (phtfound && !empty)
351 *ppixtext = pixCopy(NULL, pixs);
353 *ppixtext = pixSubtract(NULL, pixs, pixd);
354 if (pixadb) pixaAddPix(pixadb, *ppixtext,
L_COPY);
392PIX *pix1, *pix2, *pixvws, *pixd;
394 if (ptlfound) *ptlfound = 0;
396 return (
PIX *)ERROR_PTR(
"&pixvws not defined", __func__, NULL);
398 if (!pixs || pixGetDepth(pixs) != 1)
399 return (
PIX *)ERROR_PTR(
"pixs undefined or not 1 bpp", __func__, NULL);
400 pixGetDimensions(pixs, &w, &h, NULL);
401 if (w < MinWidth || h < MinHeight) {
402 L_ERROR(
"pix too small: w = %d, h = %d\n", __func__, w, h);
407 pix1 = pixInvert(NULL, pixs);
416 pix2 = pixMorphCompSequence(pix1,
"o80.60", 0);
417 pixSubtract(pix1, pix1, pix2);
418 if (pixadb) pixaAddPix(pixadb, pix1,
L_COPY);
424 pixvws = pixMorphCompSequence(pix1,
"o5.1 + o1.200", 0);
426 if (pixadb) pixaAddPix(pixadb, pixvws,
L_COPY);
433 pix1 = pixMorphSequence(pixs,
"c30.1", 0);
434 if (pixadb) pixaAddPix(pixadb, pix1,
L_COPY);
435 pixd = pixSubtract(NULL, pix1, pixvws);
436 pixOpenBrick(pixd, pixd, 3, 3);
437 if (pixadb) pixaAddPix(pixadb, pixd,
L_COPY);
442 pixZero(pixd, &empty);
481PIX *pix1, *pix2, *pix3, *pixd;
483 if (!pixs || pixGetDepth(pixs) != 1)
484 return (
PIX *)ERROR_PTR(
"pixs undefined or not 1 bpp", __func__, NULL);
485 pixGetDimensions(pixs, &w, &h, NULL);
486 if (w < MinWidth || h < MinHeight) {
487 L_ERROR(
"pix too small: w = %d, h = %d\n", __func__, w, h);
491 return (
PIX *)ERROR_PTR(
"pixvws not defined", __func__, NULL);
494 pix1 = pixMorphSequence(pixs,
"c1.10 + o4.1", 0);
495 pixZero(pix1, &empty);
498 L_INFO(
"no fg pixels in textblock mask\n", __func__);
501 if (pixadb) pixaAddPix(pixadb, pix1,
L_COPY);
509 pix2 = pixMorphSequenceByComponent(pix1,
"c30.30 + d3.3", 8, 0, 0, NULL);
510 pixCloseSafeBrick(pix2, pix2, 10, 1);
511 if (pixadb) pixaAddPix(pixadb, pix2,
L_COPY);
512 pix3 = pixSubtract(NULL, pix2, pixvws);
513 if (pixadb) pixaAddPix(pixadb, pix3,
L_COPY);
516 if (pixadb) pixaAddPix(pixadb, pixd,
L_COPY);
571l_int32 flag, nbox, intersects;
572l_int32 w, h, bx, by, bw, bh, left, right, top, bottom;
573PIX *pixb, *pixb2, *pixseed, *pixsf, *pixm, *pix1, *pixg2;
574BOX *box, *boxfg, *boxin, *boxd;
578 return (
BOX *)ERROR_PTR(
"pixs not defined", __func__, NULL);
579 pixGetDimensions(pixs, &w, &h, NULL);
580 if (w < MinWidth || h < MinHeight) {
581 L_ERROR(
"pix too small: w = %d, h = %d\n", __func__, w, h);
590 flag = (showmorph) ? 100 : 0;
591 pixb = pixConvertTo1(pixs, threshold);
592 pixb2 = pixScale(pixb, 0.5, 0.5);
593 pixseed = pixMorphSequence(pixb2,
"o1.2 + c9.9 + o3.3", flag);
594 pix1 = pixMorphSequence(pixb2,
"o50.1", 0);
595 pixOr(pixseed, pixseed, pix1);
597 pix1 = pixMorphSequence(pixb2,
"o1.50", 0);
598 pixOr(pixseed, pixseed, pix1);
600 pixsf = pixSeedfillBinary(NULL, pixseed, pixb2, 8);
601 pixSetOrClearBorder(pixsf, 10, 10, 10, 10,
PIX_SET);
602 pixm = pixRemoveBorderConnComps(pixsf, 8);
610 pix1 = pixMorphSequence(pixm,
"c50.50", flag);
611 ba1 = pixConnComp(pix1, NULL, 8);
613 pixGetDimensions(pix1, &w, &h, NULL);
614 nbox = boxaGetCount(ba2);
616 box = boxaGetBox(ba2, 0,
L_CLONE);
617 boxGetGeometry(box, &bx, &by, &bw, &bh);
618 left = (bx > mindist) ? erasedist : 0;
619 right = (w - bx - bw > mindist) ? erasedist : 0;
620 top = (by > mindist) ? erasedist : 0;
621 bottom = (h - by - bh > mindist) ? erasedist : 0;
622 pixSetOrClearBorder(pixm, left, right, top, bottom,
PIX_CLR);
630 pixClipToForeground(pixm, NULL, &boxfg);
636 boxin = boxCreate(0.1 * w, 0, 0.8 * w, h);
637 boxIntersects(boxfg, boxin, &intersects);
639 if (!intersects) boxDestroy(&boxfg);
644 boxAdjustSides(boxfg, boxfg, -2, 2, -2, 2);
645 boxd = boxTransform(boxfg, 0, 0, 2.0, 2.0);
649 pixg2 = pixConvert1To4Cmap(pixb);
650 pixRenderBoxArb(pixg2, boxd, 3, 255, 0, 0);
651 pixacompAddPix(pixac, pixg2, IFF_DEFAULT);
658 pixDestroy(&pixseed);
699l_int32 ncomp, i, xoff, yoff;
700BOXA *boxa1, *boxa2, *boxat1, *boxat2, *boxad;
702PIX *pix, *pix1, *pix2, *pixdb;
705 if (pboxa) *pboxa = NULL;
706 if (ppixa) *ppixa = NULL;
707 if (ppixdebug) *ppixdebug = NULL;
708 if (!pixs || pixGetDepth(pixs) != 1)
709 return ERROR_INT(
"pixs not defined or not 1 bpp", __func__, 1);
716 pix2 = pixMorphSequence(pix1,
"c1.10", 0);
720 boxa1 = pixConnComp(pix2, &pixa1, 8);
725 ncomp = pixaGetCount(pixa1);
726 boxa2 = boxaCreate(ncomp);
727 pixadb = (ppixdebug) ? pixaCreate(ncomp) : NULL;
728 for (i = 0; i < ncomp; i++) {
729 pix = pixaGetPix(pixa1, i,
L_CLONE);
733 pixaAddPix(pixadb, pixdb,
L_INSERT);
737 pixaGetBoxGeometry(pixa1, i, &xoff, &yoff, NULL, NULL);
738 boxat2 = boxaTransform(boxat1, xoff, yoff, 1.0, 1.0);
739 boxaJoin(boxa2, boxat2, 0, -1);
741 boxaDestroy(&boxat1);
742 boxaDestroy(&boxat2);
748 if (pixaGetCount(pixadb) > 0) {
749 *ppixdebug = pixaDisplayTiledInRows(pixadb, 32, 1500,
752 pixaDestroy(&pixadb);
756 baa = boxaSort2d(boxa2, NULL, 0, 0, 5);
757 boxad = boxaaFlattenToBoxa(baa, NULL,
L_CLONE);
763 *ppixa = pixClipRectangles(pixs, boxad);
796l_int32 w, h, n2, i, firstmin, xmin, xshift;
797l_int32 nmin, nleft, nright, nsplit, isplit, ncomp;
798l_int32 *array1, *array2;
801NUMA *na1, *na2, *nasplit;
804 if (ppixdebug) *ppixdebug = NULL;
805 if (!pixs || pixGetDepth(pixs) != 1)
806 return (
BOXA *)ERROR_PTR(
"pixa undefined or not 1 bpp", __func__, NULL);
807 pixGetDimensions(pixs, &w, &h, NULL);
810 pix1 = pixCloseSafeBrick(NULL, pixs, 1, 100);
813 boxad = boxaCreate(2);
814 na1 = pixCountPixelsByColumn(pix1);
816 na2 = numaFindExtrema(na1, delta, NULL);
817 n2 = numaGetCount(na2);
819 box = boxCreate(0, 0, w, h);
829 array1 = numaGetIArray(na1);
830 array2 = numaGetIArray(na2);
831 if (ppixdebug) numaWriteStderr(na2);
832 firstmin = (array1[array2[0]] > array1[array2[1]]) ? 1 : 2;
833 nasplit = numaCreate(n2);
834 for (i = firstmin; i < n2 - 1; i+= 2) {
837 if (xmin + 2 >= w)
break;
838 nleft = array1[xmin - 2];
839 nright = array1[xmin + 2];
842 "Splitting: xmin = %d, w = %d; nl = %d, nmin = %d, nr = %d\n",
843 xmin, w, nleft, nmin, nright);
845 if (nleft - nmin >= mindel && nright - nmin >= mindel)
846 numaAddNumber(nasplit, xmin);
848 nsplit = numaGetCount(nasplit);
851 if (ppixdebug && nsplit > 0) {
852 lept_mkdir(
"lept/split");
853 gplotSimple1(na1, GPLOT_PNG,
"/tmp/lept/split/split", NULL);
863 numaDestroy(&nasplit);
864 box = boxCreate(0, 0, w, h);
870 for (i = 0, xshift = 0; i < nsplit; i++) {
871 numaGetIValue(nasplit, i, &isplit);
872 box = boxCreate(xshift, 0, isplit - xshift, h);
876 box = boxCreate(xshift, 0, w - xshift, h);
878 numaDestroy(&nasplit);
881 pixdb = pixConvertTo32(pixs);
882 ncomp = boxaGetCount(boxad);
883 for (i = 0; i < ncomp; i++) {
884 box = boxaGetBox(boxad, i,
L_CLONE);
885 pixRenderBoxBlend(pixdb, box, 1, 255, 0, 0, 0.5);
955l_int32 res, csize, empty;
956BOXA *boxa1, *boxa2, *boxa3;
957PIX *pix1, *pix2, *pix3;
958PIXA *pixa1, *pixa2, *pixa3;
961 return (
PIXA *)ERROR_PTR(
"pixs not defined", __func__, NULL);
964 if (pixGetDepth(pixs) > 1) {
965 pix2 = pixConvertTo8(pixs, FALSE);
967 pix1 = pixThresholdToBinary(pix3, 150);
971 pix1 = pixClone(pixs);
973 pixZero(pix1, &empty);
976 L_INFO(
"no fg pixels in input image\n", __func__);
979 if (pixadb) pixaAddPix(pixadb, pix1,
L_COPY);
984 if (pixadb) pixaAddPix(pixadb, pix2,
L_COPY);
990 if ((res = pixGetXRes(pixs)) == 0) {
991 L_INFO(
"Resolution is not set: setting to 300 ppi\n", __func__);
994 csize = L_MIN(120., 60.0 * res / 300.0);
995 snprintf(buf,
sizeof(buf),
"c%d.1 + o%d.1", csize, csize / 3);
996 pix3 = pixMorphCompSequence(pix2, buf, 0);
997 if (pixadb) pixaAddPix(pixadb, pix3,
L_COPY);
1000 boxa1 = pixConnComp(pix3, &pixa1, 4);
1002 pix1 = pixaDisplayRandomCmap(pixa1, 0, 0);
1003 pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255);
1004 pixaAddPix(pixadb, pix1,
L_INSERT);
1008 minw = (minw != 0) ? minw : (l_int32)(0.12 * res);
1009 minh = (minh != 0) ? minh : (l_int32)(0.07 * res);
1015 pix1 = pixaDisplayRandomCmap(pixa2, 0, 0);
1016 pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255);
1017 pixaAddPix(pixadb, pix1,
L_INSERT);
1018 pix1 = pixConvertTo32(pix2);
1019 pixRenderBoxaArb(pix1, pixa2->
boxa, 2, 255, 0, 0);
1020 pixaAddPix(pixadb, pix1,
L_INSERT);
1024 boxa2 = pixaGetBoxa(pixa2,
L_CLONE);
1025 boxa3 = boxaAdjustSides(boxa2, -adjw, adjw, -adjh, adjh);
1026 pixa3 = pixClipRectangles(pix2, boxa3);
1028 pix1 = pixaDisplayRandomCmap(pixa3, 0, 0);
1029 pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255);
1030 pixaAddPix(pixadb, pix1,
L_INSERT);
1035 pixaDestroy(&pixa1);
1036 pixaDestroy(&pixa2);
1037 boxaDestroy(&boxa1);
1038 boxaDestroy(&boxa2);
1039 boxaDestroy(&boxa3);
1091l_int32 res, csize, empty;
1092BOXA *boxa1, *boxa2, *boxa3;
1094PIX *pix1, *pix2, *pix3;
1098 return (
PIXA *)ERROR_PTR(
"pixs not defined", __func__, NULL);
1101 if ((res = pixGetXRes(pixs)) == 0) {
1102 L_INFO(
"Resolution is not set: setting to 300 ppi\n", __func__);
1105 maxw = (maxw != 0) ? maxw : (l_int32)(0.5 * res);
1106 maxh = (maxh != 0) ? maxh : (l_int32)(0.5 * res);
1109 if (pixGetDepth(pixs) > 1) {
1110 pix2 = pixConvertTo8(pixs, FALSE);
1112 pix1 = pixThresholdToBinary(pix3, 150);
1116 pix1 = pixClone(pixs);
1118 pixZero(pix1, &empty);
1121 L_INFO(
"no fg pixels in input image\n", __func__);
1124 if (pixadb) pixaAddPix(pixadb, pix1,
L_COPY);
1129 if (pixadb) pixaAddPix(pixadb, pix2,
L_COPY);
1134 csize = L_MIN(120., 60.0 * res / 300.0);
1135 snprintf(buf,
sizeof(buf),
"c%d.1", csize);
1136 pix3 = pixMorphCompSequence(pix2, buf, 0);
1137 if (pixadb) pixaAddPix(pixadb, pix3,
L_COPY);
1140 boxa1 = pixConnComp(pix3, &pixa1, 4);
1142 pix1 = pixaDisplayRandomCmap(pixa1, 0, 0);
1143 pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255);
1144 pixaAddPix(pixadb, pix1,
L_INSERT);
1150 baa1 = boxaSort2d(boxa1, NULL, -1, -1, 5);
1151 boxaaGetExtent(baa1, NULL, NULL, NULL, &boxa2);
1153 pix1 = pixConvertTo32(pix2);
1154 pixRenderBoxaArb(pix1, boxa2, 2, 255, 0, 0);
1155 pixaAddPix(pixadb, pix1,
L_INSERT);
1160 boxa3 = boxaAdjustSides(boxa2, -adjw, adjw, -adjh, adjh);
1161 pixa2 = pixClipRectangles(pix2, boxa3);
1163 pix1 = pixaDisplayRandomCmap(pixa2, 0, 0);
1164 pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255);
1165 pixaAddPix(pixadb, pix1,
L_INSERT);
1170 pixaDestroy(&pixa1);
1171 boxaDestroy(&boxa1);
1172 boxaDestroy(&boxa2);
1173 boxaDestroy(&boxa3);
1174 boxaaDestroy(&baa1);
1210 l_float32 deltafract,
1211 l_float32 peakfract,
1212 l_float32 clipfract,
1216l_int32 w, h, res, i, n, npeak;
1217l_float32 scalefact, redfact, minval, maxval, val4, val5, fract;
1219NUMA *na1, *na2, *na3, *na4, *na5;
1220PIX *pix1, *pix2, *pix3, *pix4, *pix5;
1223 return ERROR_INT(
"&ncols not defined", __func__, 1);
1225 if (!pixs || pixGetDepth(pixs) != 1)
1226 return ERROR_INT(
"pixs not defined or not 1 bpp", __func__, 1);
1227 if (deltafract < 0.15 || deltafract > 0.75)
1228 L_WARNING(
"deltafract not in [0.15 ... 0.75]\n", __func__);
1229 if (peakfract < 0.25 || peakfract > 0.9)
1230 L_WARNING(
"peakfract not in [0.25 ... 0.9]\n", __func__);
1231 if (clipfract < 0.0 || clipfract >= 0.5)
1232 return ERROR_INT(
"clipfract not in [0.0 ... 0.5)\n", __func__, 1);
1233 if (pixadb) pixaAddPix(pixadb, pixs,
L_COPY);
1236 if ((res = pixGetXRes(pixs)) == 0) {
1237 L_WARNING(
"resolution undefined; set to 300\n", __func__);
1238 pixSetResolution(pixs, 300, 300);
1242 L_WARNING(
"resolution %d very low\n", __func__, res);
1243 scalefact = 37.5 / res;
1244 pix1 = pixScale(pixs, scalefact, scalefact);
1246 redfact = (l_float32)res / 37.5;
1248 pix1 = pixClone(pixs);
1249 else if (redfact < 4.0)
1250 pix1 = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0);
1251 else if (redfact < 8.0)
1252 pix1 = pixReduceRankBinaryCascade(pixs, 1, 2, 0, 0);
1253 else if (redfact < 16.0)
1254 pix1 = pixReduceRankBinaryCascade(pixs, 1, 2, 2, 0);
1256 pix1 = pixReduceRankBinaryCascade(pixs, 1, 2, 2, 2);
1258 if (pixadb) pixaAddPix(pixadb, pix1,
L_COPY);
1261 pixGetDimensions(pix1, &w, &h, NULL);
1262 box = boxCreate(clipfract * w, clipfract * h,
1263 (1.0 - 2 * clipfract) * w, (1.0 - 2 * clipfract) * h);
1264 pix2 = pixClipRectangle(pix1, box, NULL);
1265 pixGetDimensions(pix2, &w, &h, NULL);
1267 if (pixadb) pixaAddPix(pixadb, pix2,
L_COPY);
1270 pix3 = pixDeskew(pix2, 0);
1271 if (pixadb) pixaAddPix(pixadb, pix3,
L_COPY);
1274 pix4 = pixCloseSafeBrick(NULL, pix3, 5, 21);
1275 if (pixadb) pixaAddPix(pixadb, pix4,
L_COPY);
1276 pixInvert(pix4, pix4);
1277 na1 = pixCountByColumn(pix4, NULL);
1280 gplotSimple1(na1, GPLOT_PNG,
"/tmp/lept/plot", NULL);
1281 pix5 = pixRead(
"/tmp/lept/plot.png");
1282 pixaAddPix(pixadb, pix5,
L_INSERT);
1290 numaGetMax(na1, &maxval, NULL);
1291 numaGetMin(na1, &minval, NULL);
1292 fract = (l_float32)(maxval - minval) / h;
1294 L_INFO(
"very little content on page; 0 text columns\n", __func__);
1297 na2 = numaFindExtrema(na1, deltafract * (maxval - minval), &na3);
1298 na4 = numaTransform(na2, 0, 1.0 / w);
1299 na5 = numaTransform(na3, -minval, 1.0 / (maxval - minval));
1300 n = numaGetCount(na4);
1301 for (i = 0, npeak = 0; i < n; i++) {
1302 numaGetFValue(na4, i, &val4);
1303 numaGetFValue(na5, i, &val5);
1304 if (val4 > 0.3 && val4 < 0.7 && val5 >= peakfract) {
1306 L_INFO(
"Peak(loc,val) = (%5.3f,%5.3f)\n", __func__, val4, val5);
1309 *pncols = npeak + 1;
1360l_int32 i, empty, maxw, w, h, n1, n2, n3, minlines, big_comp;
1361l_float32 ratio1, ratio2;
1363BOXA *boxa1, *boxa2, *boxa3, *boxa4, *boxa5;
1364PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7;
1369 return ERROR_INT(
"&istext not defined", __func__, 1);
1372 return ERROR_INT(
"pixs not defined", __func__, 1);
1376 return ERROR_INT(
"pix1 not made", __func__, 1);
1378 pixZero(pix1, &empty);
1381 L_INFO(
"pix is empty\n", __func__);
1384 w = pixGetWidth(pix1);
1393 pix2 = pixCreate(11, 81, 1);
1394 for (i = 0; i < 81; i++)
1395 pixSetPixel(pix2, 5, i, 1);
1396 sel1 = selCreateFromPix(pix2, 40, 5, NULL);
1397 selSetElement(sel1, 20, 0, SEL_MISS);
1398 selSetElement(sel1, 20, 10, SEL_MISS);
1399 selSetElement(sel1, 40, 0, SEL_MISS);
1400 selSetElement(sel1, 40, 10, SEL_MISS);
1401 selSetElement(sel1, 60, 0, SEL_MISS);
1402 selSetElement(sel1, 60, 10, SEL_MISS);
1403 pix3 = pixHMT(NULL, pix1, sel1);
1404 pix4 = pixSeedfillBinaryRestricted(NULL, pix3, pix1, 8, 5, 1000);
1405 pix5 = pixXor(NULL, pix1, pix4);
1410 pix6 = pixMorphCompSequence(pix5,
"c30.1 + o15.1 + c60.1 + o2.2", 0);
1414 pixGetDimensions(pix6, NULL, &h, NULL);
1420 bmf = bmfCreate(NULL, 6);
1421 pixaAddPixWithText(pixadb, pix1, 1, bmf,
"threshold/crop to binary",
1423 pixaAddPixWithText(pixadb, pix3, 2, bmf,
"hit-miss for vertical line",
1425 pixaAddPixWithText(pixadb, pix4, 2, bmf,
"restricted seed-fill",
1427 pixaAddPixWithText(pixadb, pix5, 2, bmf,
"remove using xor",
1429 pixaAddPixWithText(pixadb, pix6, 2, bmf,
"make long horiz components",
1435 boxa1 = pixConnComp(pix6, &pixa1, 8);
1436 pix7 = pixaDisplayRandomCmap(pixa1, 0, 0);
1437 pixcmapResetColor(pixGetColormap(pix7), 0, 255, 255, 255);
1438 pixaAddPixWithText(pixadb, pix7, 2, bmf,
"show connected components",
1441 pixaDestroy(&pixa1);
1444 boxa1 = pixConnComp(pix6, NULL, 8);
1463 boxaGetBoxGeometry(boxa2, 1, NULL, NULL, &maxw, NULL);
1470 big_comp = (boxaGetCount(boxa5) == 0) ? 0 : 1;
1471 n1 = boxaGetCount(boxa1);
1472 n2 = boxaGetCount(boxa3);
1473 n3 = boxaGetCount(boxa4);
1474 ratio1 = (l_float32)maxw / (l_float32)w;
1475 ratio2 = (l_float32)n3 / (l_float32)n2;
1476 minlines = L_MAX(2, h / 125);
1477 if (big_comp || ratio1 < 0.6 || ratio2 < 0.8 || n3 < minlines)
1482 if (*pistext == 1) {
1483 L_INFO(
"This is text: \n n1 = %d, n2 = %d, n3 = %d, "
1484 "minlines = %d\n maxw = %d, ratio1 = %4.2f, h = %d, "
1485 "big_comp = %d\n", __func__, n1, n2, n3, minlines,
1486 maxw, ratio1, h, big_comp);
1488 L_INFO(
"This is not text: \n n1 = %d, n2 = %d, n3 = %d, "
1489 "minlines = %d\n maxw = %d, ratio1 = %4.2f, h = %d, "
1490 "big_comp = %d\n", __func__, n1, n2, n3, minlines,
1491 maxw, ratio1, h, big_comp);
1495 boxaDestroy(&boxa1);
1496 boxaDestroy(&boxa2);
1497 boxaDestroy(&boxa3);
1498 boxaDestroy(&boxa4);
1499 boxaDestroy(&boxa5);
1528 if (ptop) *ptop = 0;
1529 if (pbot) *pbot = 0;
1531 return ERROR_INT(
"nothing to determine", __func__, 1);
1532 if (!pixs || pixGetDepth(pixs) != 1)
1533 return ERROR_INT(
"pixs not defined or not 1 bpp", __func__, 1);
1535 na = pixCountPixelsByRow(pixs, NULL);
1536 n = numaGetCount(na);
1537 array = numaGetIArray(na);
1539 for (i = 0; i < n; i++) {
1540 if (array[i] >= thresh) {
1547 for (i = n - 1; i >= 0; i--) {
1548 if (array[i] >= thresh) {
1613l_int32 empty, nhb, nvb, nvw, score, htfound;
1614PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8, *pix9;
1617 return ERROR_INT(
"&score not defined", __func__, 1);
1620 return ERROR_INT(
"pixs not defined", __func__, 1);
1626 if (htfound && pixadb) pixaAddPix(pixadb, pix2,
L_COPY);
1631 L_INFO(
"pix has an image region\n", __func__);
1637 return ERROR_INT(
"pix1 not made", __func__, 1);
1639 pixZero(pix1, &empty);
1643 L_INFO(
"pix is empty\n", __func__);
1654 pix2 = pixDilateBrick(NULL, pix1, 2, 2);
1658 pix3 = pixDeskewBoth(pix2, 1);
1660 pixaAddPix(pixadb, pix2,
L_COPY);
1661 pixaAddPix(pixadb, pix3,
L_COPY);
1664 pix4 = pixRotate90(pix3, 1);
1666 pix4 = pixClone(pix3);
1670 pix1 = pixClone(pix4);
1674 pix2 = pixMorphSequence(pix1,
"o100.1 + c1.4", 0);
1675 pix3 = pixSeedfillBinary(NULL, pix2, pix1, 8);
1676 pix4 = pixMorphSequence(pix1,
"o1.100 + c4.1", 0);
1677 pix5 = pixSeedfillBinary(NULL, pix4, pix1, 8);
1678 pix6 = pixOr(NULL, pix3, pix5);
1680 pixaAddPix(pixadb, pix2,
L_COPY);
1681 pixaAddPix(pixadb, pix4,
L_COPY);
1682 pixaAddPix(pixadb, pix3,
L_COPY);
1683 pixaAddPix(pixadb, pix5,
L_COPY);
1684 pixaAddPix(pixadb, pix6,
L_COPY);
1686 pixCountConnComp(pix2, 8, &nhb);
1687 pixCountConnComp(pix4, 8, &nvb);
1690 pixSubtract(pix1, pix1, pix6);
1691 if (pixadb) pixaAddPix(pixadb, pix1,
L_COPY);
1694 pix7 = pixMorphSequence(pix1,
"c4.1 + o8.1", 0);
1695 if (pixadb) pixaAddPix(pixadb, pix7,
L_COPY);
1703 pixInvert(pix7, pix7);
1704 pix8 = pixMorphSequence(pix7,
"r1 + o1.100", 0);
1707 pixCountConnComp(pix9, 8, &nvw);
1709 pixaAddPix(pixadb, pixScale(pix8, 2.0, 2.0),
L_INSERT);
1710 pixaAddPix(pixadb, pixScale(pix9, 2.0, 2.0),
L_INSERT);
1717 if (nhb > 1) score++;
1718 if (nvb > 2) score++;
1719 if (nvw > 3) score++;
1720 if (nvw > 6) score++;
1757 l_float32 cropfract,
1763PIX *pix1, *pix2, *pix3, *pix4, *pix5;
1766 return (
PIX *)ERROR_PTR(
"pixs not defined", __func__, NULL);
1772 pix1 = pixClipRectangle(pixs, box, NULL);
1774 pixGetDimensions(pixs, &w, &h, NULL);
1775 box1 = boxCreate((l_int32)(cropfract * w), (l_int32)(cropfract * h),
1776 (l_int32)((1.0 - 2 * cropfract) * w),
1777 (l_int32)((1.0 - 2 * cropfract) * h));
1778 pix1 = pixClipRectangle(pixs, box1, NULL);
1783 if (pixGetDepth(pixs) > 1) {
1784 pix2 = pixConvertTo8(pix1, 0);
1789 L_INFO(
"pix cleaning failed\n", __func__);
1792 pix4 = pixThresholdToBinary(pix3, 200);
1795 pix4 = pixClone(pix1);
1803 if ((res = pixGetXRes(pixs)) == 0) {
1804 L_WARNING(
"Resolution is not set: using 300 ppi\n", __func__);
1807 if (res != outres) {
1808 factor = (l_float32)outres / (l_float32)res;
1809 pix5 = pixScale(pix4, factor, factor);
1811 pix5 = pixClone(pix4);
1843l_int32 w, h, sampling;
1846PIX *pix1, *pix2, *pixm;
1849 return ERROR_INT(
"&bg not defined", __func__, 1);
1851 if (!pixs || pixGetDepth(pixs) != 8)
1852 return ERROR_INT(
"pixs not defined or not 8 bpp", __func__, 1);
1853 if (darkthresh > 128)
1854 L_WARNING(
"darkthresh unusually large\n", __func__);
1855 if (edgecrop < 0.0 || edgecrop >= 1.0)
1856 return ERROR_INT(
"edgecrop not in [0.0 ... 1.0)", __func__, 1);
1859 pixGetDimensions(pix1, &w, &h, NULL);
1862 if (edgecrop > 0.0) {
1863 box = boxCreate(0.5 * edgecrop * w, 0.5 * edgecrop * h,
1864 (1.0 - edgecrop) * w, (1.0 - edgecrop) * h);
1865 pix2 = pixClipRectangle(pix1, box, NULL);
1868 pix2 = pixClone(pix1);
1872 sampling = L_MAX(1, (l_int32)sqrt((l_float64)(w * h) / 50000. + 0.5));
1876 if (darkthresh > 0) {
1877 pixm = pixThresholdToBinary(pix2, darkthresh);
1878 pixInvert(pixm, pixm);
1881 pixGetRankValueMasked(pix2, pixm, 0, 0, sampling, 0.5, &fbg, NULL);
1882 *pbg = (l_int32)(fbg + 0.5);
1926l_int32 i, op, bx, by, bw, bh;
1931 if (ppixdb) *ppixdb = NULL;
1933 return ERROR_INT(
"&boxa not defined", __func__, 1);
1935 if (!pixs || pixGetDepth(pixs) != 1)
1936 return ERROR_INT(
"pixs not defined or not 1 bpp", __func__, 1);
1937 if (polarity != 0 && polarity != 1)
1938 return ERROR_INT(
"invalid polarity", __func__, 1);
1940 L_WARNING(
"large num rectangles = %d requested; using 1000\n",
1945 pix = pixCopy(NULL, pixs);
1946 boxa = boxaCreate(nrect);
1950 for (i = 0; i < nrect; i++) {
1953 L_ERROR(
"failure in pixFindLargestRectangle\n", __func__);
1958 boxGetGeometry(box, &bx, &by, &bw, &bh);
1959 pixRasterop(pix, bx, by, bw, bh, op, NULL, 0, 0);
1963 *ppixdb = pixDrawBoxaRandom(pixs, boxa, 3);
2026l_int32 i, j, w, h, d, wpls, val;
2027l_int32 wp, hp, w1, w2, h1, h2, wmin, hmin, area1, area2;
2029l_int32 maxarea, wmax, hmax, vertdist, horizdist, prevfg;
2031l_uint32 *datas, *lines;
2032l_uint32 **linew, **lineh;
2037 if (ppixdb) *ppixdb = NULL;
2039 return ERROR_INT(
"&box not defined", __func__, 1);
2042 return ERROR_INT(
"pixs not defined", __func__, 1);
2043 pixGetDimensions(pixs, &w, &h, &d);
2045 return ERROR_INT(
"pixs not 1 bpp", __func__, 1);
2046 if (polarity != 0 && polarity != 1)
2047 return ERROR_INT(
"invalid polarity", __func__, 1);
2050 lowestfg = (l_int32 *)LEPT_CALLOC(w,
sizeof(l_int32));
2051 for (i = 0; i < w; i++)
2057 pixw = pixCreate(w, h, 32);
2058 pixh = pixCreate(w, h, 32);
2059 linew = (l_uint32 **)pixGetLinePtrs(pixw, NULL);
2060 lineh = (l_uint32 **)pixGetLinePtrs(pixh, NULL);
2061 datas = pixGetData(pixs);
2062 wpls = pixGetWpl(pixs);
2063 maxarea = xmax = ymax = wmax = hmax = 0;
2064 for (i = 0; i < h; i++) {
2065 lines = datas + i * wpls;
2067 for (j = 0; j < w; j++) {
2069 if ((val ^ polarity) == 0) {
2070 if (i == 0 && j == 0) {
2072 }
else if (i == 0) {
2073 wp = linew[i][j - 1] + 1;
2075 }
else if (j == 0) {
2077 hp = lineh[i - 1][j] + 1;
2080 w1 = linew[i - 1][j];
2081 h1 = lineh[i - 1][j];
2082 horizdist = j - prevfg;
2083 wmin = L_MIN(w1, horizdist);
2084 area1 = wmin * (h1 + 1);
2087 w2 = linew[i][j - 1];
2088 h2 = lineh[i][j - 1];
2089 vertdist = i - lowestfg[j];
2090 hmin = L_MIN(h2, vertdist);
2091 area2 = hmin * (w2 + 1);
2093 if (area1 > area2) {
2108 if (wp * hp > maxarea) {
2119 box = boxCreate(xmax - wmax + 1, ymax - hmax + 1, wmax, hmax);
2123 *ppixdb = pixConvertTo8(pixs, TRUE);
2129 LEPT_FREE(lowestfg);
2171pixFindRectangleInCC(
PIX *pixs,
2178l_int32 x, y, i, w, h, w1, h1, w2, h2, found, res;
2179l_int32 xfirst, xlast, xstart, yfirst, ylast, length;
2180BOX *box1, *box2, *box3, *box4, *box5;
2181PIX *pix1, *pix2, *pixdb1, *pixdb2;
2184 if (!pixs || pixGetDepth(pixs) != 1)
2185 return (
BOX *)ERROR_PTR(
"pixs undefined or not 1 bpp", __func__, NULL);
2186 if (fract <= 0.0 || fract > 1.0)
2187 return (
BOX *)ERROR_PTR(
"invalid fraction", __func__, NULL);
2189 return (
BOX *)ERROR_PTR(
"invalid scan direction", __func__, NULL);
2192 return (
BOX *)ERROR_PTR(
"invalid select", __func__, NULL);
2197 pix1 = pixClipRectangle(pixs, boxs, NULL);
2198 boxGetGeometry(boxs, &x, &y, NULL, NULL);
2200 pix1 = pixClone(pixs);
2205 pix2 = pixRotate90(pix1, 1);
2207 pix2 = pixClone(pix1);
2208 pixGetDimensions(pix2, &w, &h, NULL);
2210 pixadb = (debug) ? pixaCreate(0) : NULL;
2213 lept_mkdir(
"lept/rect");
2214 pixaAddPix(pixadb, pix1,
L_CLONE);
2215 pixdb1 = pixConvertTo32(pix2);
2222 for (i = 0; i < h; i++) {
2223 pixFindMaxHorizontalRunOnLine(pix2, i, &xstart, &length);
2224 if (length >= (l_int32)(fract * w + 0.5)) {
2227 xlast = xfirst + length - 1;
2233 L_WARNING(
"no run of sufficient size was found\n", __func__);
2235 pixDestroy(&pixdb1);
2236 pixaDestroy(&pixadb);
2241 w1 = xlast - xfirst + 1;
2244 for (i = yfirst + 1; i < h; i++) {
2245 pixFindMaxHorizontalRunOnLine(pix2, i, &xstart, &length);
2246 if (xstart > xfirst || (xstart + length - 1 < xlast) ||
2249 h1 = ylast - yfirst + 1;
2253 box1 = boxCreate(xfirst, yfirst, w1, h1);
2257 for (i = h - 1; i >= 0; i--) {
2258 pixFindMaxHorizontalRunOnLine(pix2, i, &xstart, &length);
2259 if (length >= (l_int32)(fract * w + 0.5)) {
2262 xlast = xfirst + length - 1;
2268 w2 = xlast - xfirst + 1;
2270 for (i = ylast - 1; i >= 0; i--) {
2271 pixFindMaxHorizontalRunOnLine(pix2, i, &xstart, &length);
2272 if (xstart > xfirst || (xstart + length - 1 < xlast) ||
2275 h2 = ylast - yfirst + 1;
2279 box2 = boxCreate(xfirst, yfirst, w2, h2);
2283 pixRenderBoxArb(pixdb1, box1, 2, 255, 0, 0);
2284 pixRenderBoxArb(pixdb1, box2, 2, 0, 255, 0);
2285 pixaAddPix(pixadb, pixdb1,
L_INSERT);
2290 box3 = boxBoundingRegion(box1, box2);
2292 box3 = boxOverlapRegion(box1, box2);
2294 box3 = (w1 * h1 >= w2 * h2) ? boxCopy(box1) : boxCopy(box2);
2296 box3 = (w1 * h1 <= w2 * h2) ? boxCopy(box1) : boxCopy(box2);
2304 box4 = boxRotateOrth(box3, w, h, 3);
2306 box4 = boxCopy(box3);
2310 box5 = (box4) ? boxTransform(box4, x, y, 1.0, 1.0) : NULL;
2316 pixdb1 = pixConvertTo8(pixs, 0);
2317 pixAddConstantGray(pixdb1, 190);
2318 pixdb2 = pixConvertTo32(pixdb1);
2319 if (box5) pixRenderBoxArb(pixdb2, box5, 4, 0, 0, 255);
2320 pixaAddPix(pixadb, pixdb2,
L_INSERT);
2321 res = pixGetXRes(pixs);
2322 L_INFO(
"Writing debug files to /tmp/lept/rect/\n", __func__);
2324 "/tmp/lept/rect/fitrect.pdf");
2325 pix1 = pixaDisplayTiledAndScaled(pixadb, 32, 800, 1, 0, 40, 2);
2326 pixWrite(
"/tmp/lept/rect/fitrect.png", pix1, IFF_PNG);
2328 pixDestroy(&pixdb1);
2329 pixaDestroy(&pixadb);
2363l_int32 i, n, empty, x, y, w, h;
2367PIX *pix1, *pix2, *pix3, *pix4, *pix5;
2369 if (ppixm) *ppixm = NULL;
2371 return (
PIX *)ERROR_PTR(
"pixs not defined", __func__, NULL);
2372 if (thresh == 0) thresh = 128;
2374 if ((pix1 = pixConvertTo1(pixs, thresh)) == NULL)
2375 return (
PIX *)ERROR_PTR(
"pix1 not made", __func__, NULL);
2376 if (pixadb) pixaAddPix(pixadb, pix1,
L_COPY);
2385 pix3 = pixMorphSequence(pix2,
"o15.15 + c25.25", 0);
2386 pix4 = pixFillHolesToBoundingRect(pix3, 1, 0.5, 1.0);
2388 pixaAddPix(pixadb, pix2,
L_CLONE);
2389 pixaAddPix(pixadb, pix3,
L_CLONE);
2390 pixaAddPix(pixadb, pix4,
L_COPY);
2394 pixZero(pix4, &empty);
2402 boxa1 = pixConnCompBB(pix4, 8);
2403 n = boxaGetCount(boxa1);
2404 for (i = 0; i < n; i++) {
2405 box1 = boxaGetBox(boxa1, i,
L_COPY);
2406 pix5 = pixClipRectangle(pix1, box1, NULL);
2407 pixForegroundFraction(pix5, &fgfract);
2408 if (pixadb) lept_stderr(
"fg fraction: %5.3f\n", fgfract);
2409 boxGetGeometry(box1, &x, &y, &w, &h);
2411 pixRasterop(pix4, x, y, w, h,
PIX_CLR, NULL, 0, 0);
2415 boxaDestroy(&boxa1);
2416 pixZero(pix4, &empty);
2423 pix5 = pixInvert(NULL, pix1);
2424 pixCombineMasked(pix1, pix5, pix4);
2427 pixaAddPix(pixadb, pix5,
L_CLONE);
2428 pixaAddPix(pixadb, pix1,
L_COPY);
PIX * pixCleanBackgroundToWhite(PIX *pixs, PIX *pixim, PIX *pixg, l_float32 gamma, l_int32 blackval, l_int32 whiteval)
pixCleanBackgroundToWhite()
#define GET_DATA_BIT(pdata, n)
l_ok pixFindThreshFgExtent(PIX *pixs, l_int32 thresh, l_int32 *ptop, l_int32 *pbot)
pixFindThreshFgExtent()
PIX * pixGenTextblockMask(PIX *pixs, PIX *pixvws, PIXA *pixadb)
pixGenTextblockMask()
l_ok pixGetRegionsBinary(PIX *pixs, PIX **ppixhm, PIX **ppixtm, PIX **ppixtb, PIXA *pixadb)
pixGetRegionsBinary()
PIX * pixGenTextlineMask(PIX *pixs, PIX **ppixvws, l_int32 *ptlfound, PIXA *pixadb)
pixGenTextlineMask()
l_ok pixEstimateBackground(PIX *pixs, l_int32 darkthresh, l_float32 edgecrop, l_int32 *pbg)
pixEstimateBackground()
BOX * pixFindPageForeground(PIX *pixs, l_int32 threshold, l_int32 mindist, l_int32 erasedist, l_int32 showmorph, PIXAC *pixac)
pixFindPageForeground()
PIXA * pixExtractRawTextlines(PIX *pixs, l_int32 maxw, l_int32 maxh, l_int32 adjw, l_int32 adjh, PIXA *pixadb)
pixExtractRawTextlines()
PIX * pixAutoPhotoinvert(PIX *pixs, l_int32 thresh, PIX **ppixm, PIXA *pixadb)
pixFindRectangleInCC()
PIX * pixPrepare1bpp(PIX *pixs, BOX *box, l_float32 cropfract, l_int32 outres)
pixPrepare1bpp()
PIXA * pixExtractTextlines(PIX *pixs, l_int32 maxw, l_int32 maxh, l_int32 minw, l_int32 minh, l_int32 adjw, l_int32 adjh, PIXA *pixadb)
pixExtractTextlines()
l_ok pixFindLargeRectangles(PIX *pixs, l_int32 polarity, l_int32 nrect, BOXA **pboxa, PIX **ppixdb)
pixFindLargeRectangles()
l_ok pixCountTextColumns(PIX *pixs, l_float32 deltafract, l_float32 peakfract, l_float32 clipfract, l_int32 *pncols, PIXA *pixadb)
pixCountTextColumns()
l_ok pixSplitIntoCharacters(PIX *pixs, l_int32 minw, l_int32 minh, BOXA **pboxa, PIXA **ppixa, PIX **ppixdebug)
pixSplitIntoCharacters()
PIX * pixGenHalftoneMask(PIX *pixs, PIX **ppixtext, l_int32 *phtfound, l_int32 debug)
pixGenHalftoneMask()
l_ok pixFindLargestRectangle(PIX *pixs, l_int32 polarity, BOX **pbox, PIX **ppixdb)
pixFindLargestRectangle()
PIX * pixGenerateHalftoneMask(PIX *pixs, PIX **ppixtext, l_int32 *phtfound, PIXA *pixadb)
pixGenerateHalftoneMask()
l_ok pixDecideIfTable(PIX *pixs, BOX *box, l_int32 orient, l_int32 *pscore, PIXA *pixadb)
pixDecideIfTable()
BOXA * pixSplitComponentWithProfile(PIX *pixs, l_int32 delta, l_int32 mindel, PIX **ppixdebug)
pixSplitComponentWithProfile()
l_ok pixDecideIfText(PIX *pixs, BOX *box, l_int32 *pistext, PIXA *pixadb)
pixDecideIfText()
@ REMOVE_CMAP_TO_GRAYSCALE
struct PixColormap PIXCMAP
@ L_GEOMETRIC_INTERSECTION