99#include <config_auto.h>
103#include "allheaders.h"
106static const l_float32 DefaultSweepRange = 7.0;
107static const l_float32 DefaultSweepDelta = 1.0;
113static const l_float32 DefaultMinbsDelta = 0.01;
116static const l_int32 DefaultSweepReduction = 4;
117static const l_int32 DefaultBsReduction = 2;
120static const l_float32 MinDeskewAngle = 0.1;
123static const l_float32 MinAllowedConfidence = 3.0;
126static const l_int32 MinValidMaxscore = 10000;
131static const l_float32 MinscoreThreshFactor = 0.000002;
134static const l_int32 DefaultBinaryThreshold = 130;
137#define DEBUG_PRINT_SCORES 0
138#define DEBUG_PRINT_SWEEP 0
139#define DEBUG_PRINT_BINARY 0
140#define DEBUG_PRINT_ORTH 0
141#define DEBUG_THRESHOLD 0
142#define DEBUG_PLOT_SCORES 0
169PIX *pix1, *pix2, *pix3, *pix4;
172 return (
PIX *)ERROR_PTR(
"pixs not defined", __func__, NULL);
174 redsearch = DefaultBsReduction;
175 else if (redsearch != 1 && redsearch != 2 && redsearch != 4)
176 return (
PIX *)ERROR_PTR(
"redsearch not in {1,2,4}", __func__, NULL);
179 pix2 = pixRotate90(pix1, 1);
181 pix4 = pixRotate90(pix3, -1);
211 return (
PIX *)ERROR_PTR(
"pixs not defined", __func__, NULL);
213 redsearch = DefaultBsReduction;
214 else if (redsearch != 1 && redsearch != 2 && redsearch != 4)
215 return (
PIX *)ERROR_PTR(
"redsearch not in {1,2,4}", __func__, NULL);
247 return (
PIX *)ERROR_PTR(
"pixs not defined", __func__, NULL);
249 redsearch = DefaultBsReduction;
250 else if (redsearch != 1 && redsearch != 2 && redsearch != 4)
251 return (
PIX *)ERROR_PTR(
"redsearch not in {1,2,4}", __func__, NULL);
285 l_float32 sweeprange,
286 l_float32 sweepdelta,
293l_float32 angle, conf, deg2rad;
296 if (pangle) *pangle = 0.0;
297 if (pconf) *pconf = 0.0;
299 return (
PIX *)ERROR_PTR(
"pixs not defined", __func__, NULL);
301 redsweep = DefaultSweepReduction;
302 else if (redsweep != 1 && redsweep != 2 && redsweep != 4)
303 return (
PIX *)ERROR_PTR(
"redsweep not in {1,2,4}", __func__, NULL);
304 if (sweeprange == 0.0)
305 sweeprange = DefaultSweepRange;
306 if (sweepdelta == 0.0)
307 sweepdelta = DefaultSweepDelta;
309 redsearch = DefaultBsReduction;
310 else if (redsearch != 1 && redsearch != 2 && redsearch != 4)
311 return (
PIX *)ERROR_PTR(
"redsearch not in {1,2,4}", __func__, NULL);
313 thresh = DefaultBinaryThreshold;
315 deg2rad = 3.1415926535 / 180.;
318 depth = pixGetDepth(pixs);
320 pixb = pixClone(pixs);
322 pixb = pixConvertTo1(pixs, thresh);
326 sweeprange, sweepdelta,
329 if (pangle) *pangle = angle;
330 if (pconf) *pconf = conf;
332 return pixClone(pixs);
334 if (L_ABS(angle) < MinDeskewAngle || conf < MinAllowedConfidence)
335 return pixClone(pixs);
339 return pixClone(pixs);
370 if (pangle) *pangle = 0.0;
371 if (pconf) *pconf = 0.0;
372 if (!pangle || !pconf)
373 return ERROR_INT(
"&angle and/or &conf not defined", __func__, 1);
375 return ERROR_INT(
"pixs not defined", __func__, 1);
376 if (pixGetDepth(pixs) != 1)
377 return ERROR_INT(
"pixs not 1 bpp", __func__, 1);
380 DefaultSweepReduction,
411 l_float32 sweeprange,
412 l_float32 sweepdelta)
414l_int32 ret, bzero, i, nangles;
415l_float32 deg2rad, theta;
416l_float32 sum, maxscore, maxangle;
417NUMA *natheta, *nascore;
421 return ERROR_INT(
"&angle not defined", __func__, 1);
424 return ERROR_INT(
"pixs not defined", __func__, 1);
425 if (pixGetDepth(pixs) != 1)
426 return ERROR_INT(
"pixs not 1 bpp", __func__, 1);
427 if (reduction != 1 && reduction != 2 && reduction != 4 && reduction != 8)
428 return ERROR_INT(
"reduction must be in {1,2,4,8}", __func__, 1);
430 deg2rad = 3.1415926535 / 180.;
435 pix = pixClone(pixs);
436 else if (reduction == 2)
437 pix = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0);
438 else if (reduction == 4)
439 pix = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0);
441 pix = pixReduceRankBinaryCascade(pixs, 1, 1, 2, 0);
443 pixZero(pix, &bzero);
449 nangles = (l_int32)((2. * sweeprange) / sweepdelta + 1);
450 natheta = numaCreate(nangles);
451 nascore = numaCreate(nangles);
452 pixt = pixCreateTemplate(pix);
455 ret = ERROR_INT(
"pix and pixt not both made", __func__, 1);
458 if (!natheta || !nascore) {
459 ret = ERROR_INT(
"natheta and nascore not both made", __func__, 1);
463 for (i = 0; i < nangles; i++) {
464 theta = -sweeprange + i * sweepdelta;
472#if DEBUG_PRINT_SCORES
473 L_INFO(
"sum(%7.2f) = %7.0f\n", __func__, theta, sum);
477 numaAddNumber(nascore, sum);
478 numaAddNumber(natheta, theta);
484 numaFitMax(nascore, &maxscore, natheta, &maxangle);
488 L_INFO(
" From sweep: angle = %7.3f, score = %7.3f\n", __func__,
502 gplot = gplotCreate(
"sweep_output", GPLOT_PNG,
503 "Sweep. Variance of difference of ON pixels vs. angle",
504 "angle (deg)",
"score");
505 gplotAddPlot(gplot, natheta, nascore, GPLOT_LINES,
"plot1");
506 gplotAddPlot(gplot, natheta, nascore, GPLOT_POINTS,
"plot2");
507 gplotMakeOutput(gplot);
508 gplotDestroy(&gplot);
515 numaDestroy(&nascore);
516 numaDestroy(&natheta);
555 l_float32 sweeprange,
556 l_float32 sweepdelta,
557 l_float32 minbsdelta)
560 redsweep, redsearch, 0.0, sweeprange,
561 sweepdelta, minbsdelta);
607 l_float32 *pendscore,
610 l_float32 sweepcenter,
611 l_float32 sweeprange,
612 l_float32 sweepdelta,
613 l_float32 minbsdelta)
616 redsweep, redsearch, 0.0,
617 sweeprange, sweepdelta,
656 l_float32 *pendscore,
659 l_float32 sweepcenter,
660 l_float32 sweeprange,
661 l_float32 sweepdelta,
662 l_float32 minbsdelta,
665l_int32 ret, bzero, i, nangles, n, ratio, maxindex, minloc;
666l_int32 width, height;
667l_float32 deg2rad, theta, delta;
668l_float32 sum, maxscore, maxangle;
669l_float32 centerangle, leftcenterangle, rightcenterangle;
670l_float32 lefttemp, righttemp;
671l_float32 bsearchscore[5];
672l_float32 minscore, minthresh;
674NUMA *natheta, *nascore;
675PIX *pixsw, *pixsch, *pixt1, *pixt2;
677 if (pendscore) *pendscore = 0.0;
678 if (pangle) *pangle = 0.0;
679 if (pconf) *pconf = 0.0;
680 if (!pangle || !pconf)
681 return ERROR_INT(
"&angle and/or &conf not defined", __func__, 1);
682 if (!pixs || pixGetDepth(pixs) != 1)
683 return ERROR_INT(
"pixs not defined or not 1 bpp", __func__, 1);
684 if (redsweep != 1 && redsweep != 2 && redsweep != 4 && redsweep != 8)
685 return ERROR_INT(
"redsweep must be in {1,2,4,8}", __func__, 1);
686 if (redsearch != 1 && redsearch != 2 && redsearch != 4 && redsearch != 8)
687 return ERROR_INT(
"redsearch must be in {1,2,4,8}", __func__, 1);
688 if (redsearch > redsweep)
689 return ERROR_INT(
"redsearch must not exceed redsweep", __func__, 1);
691 return ERROR_INT(
"invalid pivot", __func__, 1);
693 deg2rad = 3.1415926535 / 180.;
698 pixsch = pixClone(pixs);
699 else if (redsearch == 2)
700 pixsch = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0);
701 else if (redsearch == 4)
702 pixsch = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0);
704 pixsch = pixReduceRankBinaryCascade(pixs, 1, 1, 2, 0);
706 pixZero(pixsch, &bzero);
713 ratio = redsweep / redsearch;
715 pixsw = pixClone(pixsch);
718 pixsw = pixReduceRankBinaryCascade(pixsch, 1, 0, 0, 0);
720 pixsw = pixReduceRankBinaryCascade(pixsch, 1, 2, 0, 0);
722 pixsw = pixReduceRankBinaryCascade(pixsch, 1, 2, 2, 0);
725 pixt1 = pixCreateTemplate(pixsw);
727 pixt2 = pixClone(pixt1);
729 pixt2 = pixCreateTemplate(pixsch);
731 nangles = (l_int32)((2. * sweeprange) / sweepdelta + 1);
732 natheta = numaCreate(nangles);
733 nascore = numaCreate(nangles);
735 if (!pixsch || !pixsw) {
736 ret = ERROR_INT(
"pixsch and pixsw not both made", __func__, 1);
739 if (!pixt1 || !pixt2) {
740 ret = ERROR_INT(
"pixt1 and pixt2 not both made", __func__, 1);
743 if (!natheta || !nascore) {
744 ret = ERROR_INT(
"natheta and nascore not both made", __func__, 1);
749 rangeleft = sweepcenter - sweeprange;
750 for (i = 0; i < nangles; i++) {
751 theta = rangeleft + i * sweepdelta;
762#if DEBUG_PRINT_SCORES
763 L_INFO(
"sum(%7.2f) = %7.0f\n", __func__, theta, sum);
767 numaAddNumber(nascore, sum);
768 numaAddNumber(natheta, theta);
772 numaGetMax(nascore, &maxscore, &maxindex);
773 numaGetFValue(natheta, maxindex, &maxangle);
776 L_INFO(
" From sweep: angle = %7.3f, score = %7.3f\n", __func__,
784 gplot = gplotCreate(
"sweep_output", GPLOT_PNG,
785 "Sweep. Variance of difference of ON pixels vs. angle",
786 "angle (deg)",
"score");
787 gplotAddPlot(gplot, natheta, nascore, GPLOT_LINES,
"plot1");
788 gplotAddPlot(gplot, natheta, nascore, GPLOT_POINTS,
"plot2");
789 gplotMakeOutput(gplot);
790 gplotDestroy(&gplot);
795 n = numaGetCount(natheta);
796 if (maxindex == 0 || maxindex == n - 1) {
797 L_WARNING(
"max found at sweep edge\n", __func__);
807 centerangle = maxangle;
811 pixVShearCorner(pixt2, pixsch, deg2rad * (centerangle - sweepdelta),
814 pixVShearCorner(pixt2, pixsch, deg2rad * (centerangle + sweepdelta),
820 pixVShearCenter(pixt2, pixsch, deg2rad * (centerangle - sweepdelta),
823 pixVShearCenter(pixt2, pixsch, deg2rad * (centerangle + sweepdelta),
828 numaAddNumber(nascore, bsearchscore[2]);
829 numaAddNumber(natheta, centerangle);
830 numaAddNumber(nascore, bsearchscore[0]);
831 numaAddNumber(natheta, centerangle - sweepdelta);
832 numaAddNumber(nascore, bsearchscore[4]);
833 numaAddNumber(natheta, centerangle + sweepdelta);
836 delta = 0.5 * sweepdelta;
837 while (delta >= minbsdelta)
840 leftcenterangle = centerangle - delta;
842 pixVShearCorner(pixt2, pixsch, deg2rad * leftcenterangle,
845 pixVShearCenter(pixt2, pixsch, deg2rad * leftcenterangle,
848 numaAddNumber(nascore, bsearchscore[1]);
849 numaAddNumber(natheta, leftcenterangle);
852 rightcenterangle = centerangle + delta;
854 pixVShearCorner(pixt2, pixsch, deg2rad * rightcenterangle,
857 pixVShearCenter(pixt2, pixsch, deg2rad * rightcenterangle,
860 numaAddNumber(nascore, bsearchscore[3]);
861 numaAddNumber(natheta, rightcenterangle);
866 maxscore = bsearchscore[1];
868 for (i = 2; i < 4; i++) {
869 if (bsearchscore[i] > maxscore) {
870 maxscore = bsearchscore[i];
876 lefttemp = bsearchscore[maxindex - 1];
877 righttemp = bsearchscore[maxindex + 1];
878 bsearchscore[2] = maxscore;
879 bsearchscore[0] = lefttemp;
880 bsearchscore[4] = righttemp;
883 centerangle = centerangle + delta * (maxindex - 2);
886 *pangle = centerangle;
888#if DEBUG_PRINT_SCORES
889 L_INFO(
" Binary search score = %7.3f\n", __func__, bsearchscore[2]);
893 *pendscore = bsearchscore[2];
906 numaGetMin(nascore, &minscore, &minloc);
907 width = pixGetWidth(pixsch);
908 height = pixGetHeight(pixsch);
909 minthresh = MinscoreThreshFactor * width * width * height;
912 L_INFO(
" minthresh = %10.2f, minscore = %10.2f\n", __func__,
913 minthresh, minscore);
914 L_INFO(
" maxscore = %10.2f\n", __func__, maxscore);
917 if (minscore > minthresh)
918 *pconf = maxscore / minscore;
924 if ((centerangle > rangeleft + 2 * sweeprange - sweepdelta) ||
925 (centerangle < rangeleft + sweepdelta) ||
926 (maxscore < MinValidMaxscore))
929#if DEBUG_PRINT_BINARY
930 lept_stderr(
"Binary search: angle = %7.3f, score ratio = %6.2f\n",
932 lept_stderr(
" max score = %8.0f\n", maxscore);
941 gplot = gplotCreate(
"search_output", GPLOT_PNG,
942 "Binary search. Variance of difference of ON pixels vs. angle",
943 "angle (deg)",
"score");
944 gplotAddPlot(gplot, natheta, nascore, GPLOT_POINTS,
"plot1");
945 gplotMakeOutput(gplot);
946 gplotDestroy(&gplot);
955 numaDestroy(&nascore);
956 numaDestroy(&natheta);
1024pixFindSkewOrthogonalRange(
PIX *pixs,
1029 l_float32 sweeprange,
1030 l_float32 sweepdelta,
1031 l_float32 minbsdelta,
1032 l_float32 confprior)
1034l_float32 angle1, conf1, score1, angle2, conf2, score2;
1037 if (pangle) *pangle = 0.0;
1038 if (pconf) *pconf = 0.0;
1039 if (!pangle || !pconf)
1040 return ERROR_INT(
"&angle and/or &conf not defined", __func__, 1);
1041 if (!pixs || pixGetDepth(pixs) != 1)
1042 return ERROR_INT(
"pixs not defined or not 1 bpp", __func__, 1);
1045 redsweep, redsearch, 0.0,
1046 sweeprange, sweepdelta, minbsdelta,
1048 pixr = pixRotateOrth(pixs, 1);
1050 redsweep, redsearch, 0.0,
1051 sweeprange, sweepdelta, minbsdelta,
1055 if (conf1 > conf2 - confprior) {
1059 *pangle = -90.0 + angle2;
1064 lept_stderr(
" About 0: angle1 = %7.3f, conf1 = %7.3f, score1 = %f\n",
1065 angle1, conf1, score1);
1066 lept_stderr(
" About 90: angle2 = %7.3f, conf2 = %7.3f, score2 = %f\n",
1067 angle2, conf2, score2);
1068 lept_stderr(
" Final: angle = %7.3f, conf = %7.3f\n", *pangle, *pconf);
1099l_int32 w, h, skiph, skip, nskip;
1100l_float32 val1, val2, diff, sum;
1104 return ERROR_INT(
"&sum not defined", __func__, 1);
1107 return ERROR_INT(
"pixs not defined", __func__, 1);
1111 if ((na = pixCountPixelsByRow(pixs, NULL)) == NULL)
1112 return ERROR_INT(
"na not made", __func__, 1);
1117 w = pixGetWidth(pixs);
1118 h = pixGetHeight(pixs);
1119 skiph = (l_int32)(0.05 * w);
1120 skip = L_MIN(h / 10, skiph);
1121 nskip = L_MAX(skip / 2, 1);
1125 n = numaGetCount(na);
1127 for (i = nskip; i < n - nskip; i++) {
1128 numaGetFValue(na, i - 1, &val1);
1129 numaGetFValue(na, i, &val2);
1171l_int32 i, w, h, empty;
1172l_float32 sum, sumsq, uniform, val;
1176 if (phratio) *phratio = 0.0;
1177 if (pvratio) *pvratio = 0.0;
1178 if (pfract) *pfract = 0.0;
1179 if (!phratio && !pvratio)
1180 return ERROR_INT(
"nothing to do", __func__, 1);
1181 if (!pixs || pixGetDepth(pixs) != 1)
1182 return ERROR_INT(
"pixs not defined or not 1 bpp", __func__, 1);
1183 pixGetDimensions(pixs, &w, &h, NULL);
1187 na = pixCountPixelsByRow(pixs, NULL);
1188 numaGetSum(na, &sum);
1189 if (pfract) *pfract = sum / (l_float32)(w * h);
1191 uniform = sum * sum / h;
1193 for (i = 0; i < h; i++) {
1194 numaGetFValue(na, i, &val);
1197 *phratio = sumsq / uniform;
1205 if (empty == 1)
return 1;
1206 pixt = pixRotateOrth(pixs, 1);
1207 na = pixCountPixelsByRow(pixt, NULL);
1208 numaGetSum(na, &sum);
1209 if (pfract) *pfract = sum / (l_float32)(w * h);
1211 uniform = sum * sum / w;
1213 for (i = 0; i < w; i++) {
1214 numaGetFValue(na, i, &val);
1217 *pvratio = sumsq / uniform;
PIX * pixDeskewGeneral(PIX *pixs, l_int32 redsweep, l_float32 sweeprange, l_float32 sweepdelta, l_int32 redsearch, l_int32 thresh, l_float32 *pangle, l_float32 *pconf)
pixDeskewGeneral()
l_ok pixFindSkewSweepAndSearchScore(PIX *pixs, l_float32 *pangle, l_float32 *pconf, l_float32 *pendscore, l_int32 redsweep, l_int32 redsearch, l_float32 sweepcenter, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta)
pixFindSkewSweepAndSearchScore()
l_ok pixFindSkewSweep(PIX *pixs, l_float32 *pangle, l_int32 reduction, l_float32 sweeprange, l_float32 sweepdelta)
pixFindSkewSweep()
PIX * pixDeskewBoth(PIX *pixs, l_int32 redsearch)
pixDeskewBoth()
l_ok pixFindNormalizedSquareSum(PIX *pixs, l_float32 *phratio, l_float32 *pvratio, l_float32 *pfract)
pixFindNormalizedSquareSum()
PIX * pixFindSkewAndDeskew(PIX *pixs, l_int32 redsearch, l_float32 *pangle, l_float32 *pconf)
pixFindSkewAndDeskew()
l_ok pixFindSkewSweepAndSearchScorePivot(PIX *pixs, l_float32 *pangle, l_float32 *pconf, l_float32 *pendscore, l_int32 redsweep, l_int32 redsearch, l_float32 sweepcenter, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta, l_int32 pivot)
pixFindSkewSweepAndSearchScorePivot()
l_ok pixFindSkew(PIX *pixs, l_float32 *pangle, l_float32 *pconf)
pixFindSkew()
l_ok pixFindDifferentialSquareSum(PIX *pixs, l_float32 *psum)
pixFindDifferentialSquareSum()
l_ok pixFindSkewSweepAndSearch(PIX *pixs, l_float32 *pangle, l_float32 *pconf, l_int32 redsweep, l_int32 redsearch, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta)
pixFindSkewSweepAndSearch()
PIX * pixDeskew(PIX *pixs, l_int32 redsearch)
pixDeskew()