Skip to content

Commit

Permalink
Add new function for determining if box sizes are essentially the same.
Browse files Browse the repository at this point in the history
* These boxes can represent page image regions and have outliers.
* If the test comes back true, page size reconciliation can be attempted.
* Add test to boxa3_reg.
  • Loading branch information
DanBloomberg committed Dec 4, 2018
1 parent 47c1d43 commit f312b53
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 25 deletions.
41 changes: 27 additions & 14 deletions prog/boxa3_reg.c
Expand Up @@ -38,6 +38,9 @@
static const char *boxafiles[3] = {"boxap1.ba", "boxap2.ba", "boxap3.ba"};

void static TestBoxa(L_REGPARAMS *rp, l_int32 index);
static l_float32 varp[3] = {0.0165, 0.0432, 0.0716};
static l_float32 varm[3] = {0.0088, 0.0213, 0.0357};
static l_int32 same[3] = {1, -1, -1};
static l_float32 devwidth[3] = {0.0864, 0.0895, 0.1174};
static l_float32 devheight[3] = {0.0048, 0.0294, 0.0023};

Expand All @@ -62,9 +65,9 @@ TestBoxa(L_REGPARAMS *rp,
l_int32 index)
{
l_uint8 *data;
l_int32 w, h, medw, medh;
l_int32 w, h, medw, medh, isame;
size_t size;
l_float32 scalefact, devw, devh, ratiowh;
l_float32 scalefact, devw, devh, ratiowh, fvarp, fvarm;
BOXA *boxa1, *boxa2, *boxa3;
PIX *pix1;

Expand All @@ -75,10 +78,10 @@ PIX *pix1;
scalefact = 100.0 / (l_float32)w;
boxa2 = boxaTransform(boxa1, 0, 0, scalefact, scalefact);
boxaWriteMem(&data, &size, boxa2);
regTestWriteDataAndCheck(rp, data, size, "ba"); /* 0, 10, 20 */
regTestWriteDataAndCheck(rp, data, size, "ba"); /* 0, 13, 26 */
lept_free(data);
pix1 = boxaDisplayTiled(boxa2, NULL, 0, -1, 2200, 2, 1.0, 0, 3, 2);
regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 1, 11, 21 */
regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 1, 14, 27 */
pixDisplayWithTitle(pix1, 0, 0, NULL, rp->display);
pixDestroy(&pix1);

Expand All @@ -88,21 +91,31 @@ PIX *pix1;
if (rp->display)
fprintf(stderr, "median width = %d, median height = %d\n", medw, medh);

/* Check for deviations from median by pairs */
boxaEvalSizeConsistency(boxa2, &devw, &devh, 0);
regTestCompareValues(rp, devwidth[index], devw, 0.001); /* 2, 12, 22 */
regTestCompareValues(rp, devheight[index], devh, 0.001); /* 3, 13, 23 */
/* Check for deviations from median by pairs: method 1 */
boxaSizeConsistency1(boxa2, L_CHECK_HEIGHT, 0.0, 0.0,
&fvarp, &fvarm, &isame);
regTestCompareValues(rp, varp[index], fvarp, 0.003); /* 2, 15, 28 */
regTestCompareValues(rp, varm[index], fvarm, 0.003); /* 3, 16, 29 */
regTestCompareValues(rp, same[index], isame, 0); /* 4, 17, 30 */
if (rp->display)
fprintf(stderr, "fvarp = %7.4f, fvarm = %7.4f, same = %d\n",
fvarp, fvarm, isame);

/* Check for deviations from median by pairs: method 2 */
boxaSizeConsistency2(boxa2, &devw, &devh, 0);
regTestCompareValues(rp, devwidth[index], devw, 0.001); /* 5, 18, 31 */
regTestCompareValues(rp, devheight[index], devh, 0.001); /* 6, 19, 32 */
if (rp->display)
fprintf(stderr, "dev width = %7.4f, dev height = %7.4f\n", devw, devh);

/* Reconcile widths */
boxa3 = boxaReconcileSizeByMedian(boxa2, L_CHECK_WIDTH, 0.05, 1.03,
NULL, NULL, &ratiowh);
boxaWriteMem(&data, &size, boxa3);
regTestWriteDataAndCheck(rp, data, size, "ba"); /* 4, 14, 24 */
regTestWriteDataAndCheck(rp, data, size, "ba"); /* 7, 20, 33 */
lept_free(data);
pix1 = boxaDisplayTiled(boxa3, NULL, 0, -1, 2200, 2, 1.0, 0, 3, 2);
regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 5, 15, 25 */
regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 8, 21, 34 */
pixDisplayWithTitle(pix1, 500, 0, NULL, rp->display);
if (rp->display)
fprintf(stderr, "ratio median width/height = %6.3f\n", ratiowh);
Expand All @@ -113,10 +126,10 @@ PIX *pix1;
boxa3 = boxaReconcileSizeByMedian(boxa2, L_CHECK_HEIGHT, 0.05, 1.03,
NULL, NULL, NULL);
boxaWriteMem(&data, &size, boxa3);
regTestWriteDataAndCheck(rp, data, size, "ba"); /* 6, 16, 26 */
regTestWriteDataAndCheck(rp, data, size, "ba"); /* 9, 22, 35 */
lept_free(data);
pix1 = boxaDisplayTiled(boxa3, NULL, 0, -1, 2200, 2, 1.0, 0, 3, 2);
regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 7, 17, 27 */
regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 10, 23, 36 */
pixDisplayWithTitle(pix1, 1000, 0, NULL, rp->display);
boxaDestroy(&boxa3);
pixDestroy(&pix1);
Expand All @@ -125,10 +138,10 @@ PIX *pix1;
boxa3 = boxaReconcileSizeByMedian(boxa2, L_CHECK_BOTH, 0.05, 1.03,
NULL, NULL, NULL);
boxaWriteMem(&data, &size, boxa3);
regTestWriteDataAndCheck(rp, data, size, "ba"); /* 8, 18, 28 */
regTestWriteDataAndCheck(rp, data, size, "ba"); /* 11, 24, 37 */
lept_free(data);
pix1 = boxaDisplayTiled(boxa3, NULL, 0, -1, 2200, 2, 1.0, 0, 3, 2);
regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 9, 19, 29 */
regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 12, 25, 38 */
pixDisplayWithTitle(pix1, 1500, 0, NULL, rp->display);
boxaDestroy(&boxa3);
pixDestroy(&pix1);
Expand Down
3 changes: 2 additions & 1 deletion src/allheaders.h
Expand Up @@ -369,7 +369,8 @@ LEPT_DLL extern BOXA * boxaModifyWithBoxa ( BOXA *boxas, BOXA *boxam, l_int32 su
LEPT_DLL extern BOXA * boxaConstrainSize ( BOXA *boxas, l_int32 width, l_int32 widthflag, l_int32 height, l_int32 heightflag );
LEPT_DLL extern BOXA * boxaReconcileEvenOddHeight ( BOXA *boxas, l_int32 sides, l_int32 delh, l_int32 op, l_float32 factor, l_int32 start );
LEPT_DLL extern BOXA * boxaReconcilePairWidth ( BOXA *boxas, l_int32 delw, l_int32 op, l_float32 factor, NUMA *na );
LEPT_DLL extern l_ok boxaEvalSizeConsistency ( BOXA *boxas, l_float32 *pfdevw, l_float32 *pfdevh, l_int32 debug );
LEPT_DLL extern l_ok boxaSizeConsistency1 ( BOXA *boxas, l_int32 type, l_float32 threshp, l_float32 threshm, l_float32 *pfvarp, l_float32 *pfvarm, l_int32 *psame );
LEPT_DLL extern l_ok boxaSizeConsistency2 ( BOXA *boxas, l_float32 *pfdevw, l_float32 *pfdevh, l_int32 debug );
LEPT_DLL extern BOXA * boxaReconcileSizeByMedian ( BOXA *boxas, l_int32 type, l_float32 fract, l_float32 factor, NUMA **pnadelw, NUMA **pnadelh, l_float32 *pratiowh );
LEPT_DLL extern l_ok boxaPlotSides ( BOXA *boxa, const char *plotname, NUMA **pnal, NUMA **pnat, NUMA **pnar, NUMA **pnab, PIX **ppixd );
LEPT_DLL extern l_ok boxaPlotSizes ( BOXA *boxa, const char *plotname, NUMA **pnaw, NUMA **pnah, PIX **ppixd );
Expand Down
145 changes: 135 additions & 10 deletions src/boxfunc5.c
Expand Up @@ -38,7 +38,8 @@
* BOXA *boxaReconcileEvenOddHeight()
* static l_int32 boxaTestEvenOddHeight()
* BOXA *boxaReconcilePairWidth()
* l_int32 boxaEvalSizeConsistency()
* l_int32 boxaSizeConsistency1()
* l_int32 boxaSizeConsistency2()
* BOXA *boxaReconcileSizeByMedian()
* l_int32 boxaPlotSides() [for debugging]
* l_int32 boxaPlotSizes() [for debugging]
Expand Down Expand Up @@ -1084,7 +1085,128 @@ BOXA *boxae, *boxao, *boxad;


/*!
* \brief boxaEvalSizeConsistency()
* \brief boxaSizeConsistency1()
*
* \param[in] boxas of size >= 10
* \param[in] type L_CHECK_WIDTH, L_CHECK_HEIGHT
* \param[in] threshp threshold for pairwise fractional variation
* \param[in] threshm threshold for fractional variation from median
* \param[out] pfvarp [optional] average fractional pairwise variation
* \param[out] pfvarm [optional] average fractional median variation
* \param[out] psame decision for uniformity of page size (1, 0, -1)
*
* <pre>
* Notes:
* (1) This evaluates a boxa for particular types of dimensional
* variation. Select either width or height variation. Then
* it returns two numbers: one is based on pairwise (even/odd)
* variation; the other is based on the average variation
* from the boxa median.
* (2) For the pairwise variation, get the fraction of the absolute
* difference in dimension of each pair of boxes, and take
* the average value. The median variation is simply the
* the average of the fractional deviation from the median
* of all the boxes.
* (3) Use 0 for default values of %threshp and %threshm. They are
* threshp: 0.02
* threshm: 0.015
* (4) The intended application is that the boxes are a sequence of
* page regions in a book scan, and we calculate two numbers
* that can give an indication if the pages are approximately
* the same size. The pairwise variation should be small if
* the boxes are correctly calculated. If there are a
* significant number of random or systematic outliers, the
* pairwise variation will be large, and no decision will be made
* (i.e., return same == -1). Here are the possible outcomes:
* Pairwise Var Median Var Decision
* ------------ ---------- --------
* small small same size (1)
* small large different size (0)
* large small/large unknown (-1)
* </pre>
*/
l_ok
boxaSizeConsistency1(BOXA *boxas,
l_int32 type,
l_float32 threshp,
l_float32 threshm,
l_float32 *pfvarp,
l_float32 *pfvarm,
l_int32 *psame)
{
l_int32 i, n, bw1, bh1, bw2, bh2, npairs;
l_float32 ave, fdiff, sumdiff, med, fvarp, fvarm;
NUMA *na1;

PROCNAME("boxaSizeConsistency1");

if (pfvarp) *pfvarp = 0.0;
if (pfvarm) *pfvarm = 0.0;
if (!psame)
return ERROR_INT("&same not defined", procName, 1);
*psame = -1;
if (!boxas)
return ERROR_INT("boxas not defined", procName, 1);
if (boxaGetValidCount(boxas) < 6)
return ERROR_INT("need a least 6 valid boxes", procName, 1);
if (type != L_CHECK_WIDTH && type != L_CHECK_HEIGHT)
return ERROR_INT("invalid type", procName, 1);
if (threshp < 0.0 || threshp >= 0.5)
return ERROR_INT("invalid threshp", procName, 1);
if (threshm < 0.0 || threshm >= 0.5)
return ERROR_INT("invalid threshm", procName, 1);
if (threshp == 0.0) threshp = 0.02;
if (threshm == 0.0) threshm = 0.015;

/* Evaluate pairwise variation */
n = boxaGetCount(boxas);
na1 = numaCreate(0);
for (i = 0, npairs = 0, sumdiff = 0; i < n - 1; i += 2) {
boxaGetBoxGeometry(boxas, i, NULL, NULL, &bw1, &bh1);
boxaGetBoxGeometry(boxas, i + 1, NULL, NULL, &bw2, &bh2);
if (bw1 == 0 || bh1 == 0 || bw2 == 0 || bh2 == 0)
continue;
npairs++;
if (type == L_CHECK_WIDTH) {
ave = (bw1 + bw2) / 2.0;
fdiff = L_ABS(bw1 - bw2) / ave;
numaAddNumber(na1, bw1);
numaAddNumber(na1, bw2);
} else { /* type == L_CHECK_HEIGHT) */
ave = (bh1 + bh2) / 2.0;
fdiff = L_ABS(bh1 - bh2) / ave;
numaAddNumber(na1, bh1);
numaAddNumber(na1, bh2);
}
sumdiff += fdiff;
}
fvarp = sumdiff / npairs;
if (pfvarp) *pfvarp = fvarp;

/* Evaluate the average abs fractional deviation from the median */
numaGetMedian(na1, &med);
if (med == 0.0) {
L_WARNING("median value is 0\n", procName);
} else {
numaGetMeanDevFromMedian(na1, med, &fvarm);
fvarm /= med;
if (pfvarm) *pfvarm = fvarm;
}
numaDestroy(&na1);

/* Make decision */
if (fvarp < threshp && fvarm < threshm)
*psame = 1;
else if (fvarp < threshp && fvarm > threshm)
*psame = 0;
else
*psame = -1; /* unknown */
return 0;
}


/*!
* \brief boxaSizeConsistency2()
*
* \param[in] boxas of size >= 10
* \param[out] pfdevw average fractional deviation from median width
Expand All @@ -1100,23 +1222,26 @@ BOXA *boxae, *boxao, *boxad;
* about whether the pages should be approximately the same size.
* The determination should be robust to outliers, both random
* and (for many cases) systematic.
* (2) Adjacent even and odd boxes are expected to be the same size.
* (2) This differs from boxaSizeConsistency1() in that it attempts
* to correct for box dimensional errors before doing the
* evaluation. For this reason, it may be less robust.
* (3) Adjacent even and odd boxes are expected to be the same size.
* Take them pairwise, and assume the minimum height, hmin,
* is correct. Then for (the usual case) wmin/hmin > 0.5, assume
* the minimum width is correct. If wmin/hmin <= 0.5, assume
* the maximum width is correct.
* (3) After correcting each pair so that they are the same size,
* (4) After correcting each pair so that they are the same size,
* compute the average fractional deviation, from median width and
* height. A deviation of width or height by more than about
* 0.02 is evidence that the boxes may be from a non-homogeneous
* source, such as a book with significantly different page sizes.
* </pre>
*/
l_ok
boxaEvalSizeConsistency(BOXA *boxas,
l_float32 *pfdevw,
l_float32 *pfdevh,
l_int32 debug)
boxaSizeConsistency2(BOXA *boxas,
l_float32 *pfdevw,
l_float32 *pfdevh,
l_int32 debug)
{
l_int32 i, n, bw1, bh1, bw2, bh2, npairs;
l_float32 medw, medh, devw, devh, minw, maxw, minh, w;
Expand All @@ -1126,7 +1251,7 @@ NUMA *naw, *nah;
PIX *pix1, *pix2, *pix3;
PIXA *pixa;

PROCNAME("boxaEvalSizeConsistency");
PROCNAME("boxaSizeConsistency2");

if (pfdevw) *pfdevw = 0.0;
if (pfdevh) *pfdevh = 0.0;
Expand Down Expand Up @@ -1208,7 +1333,7 @@ PIXA *pixa;
* \param[in] boxas containing at least 6 valid boxes
* \param[in] type L_CHECK_WIDTH, L_CHECK_HEIGHT, L_CHECK_BOTH
* \param[in] fract threshold fraction of size variation from median;
* in range (0 ... 1); typ. about 0.1.
* in range (0 ... 1); typ. about 0.05.
* \param[in] factor expansion for fixed box beyond median width;
* should be near 1.0.
* \param[out] pnadelw [optional] diff from median width for boxes
Expand Down

0 comments on commit f312b53

Please sign in to comment.