/*
 *   pixFindSkewOrthogonalRange()
 *
 *      Input:  pixs  (1 bpp)
 *              &angle  (<return> angle required to deskew; in degrees cw)
 *              &conf   (<return> confidence given by ratio of max/min score)
 *              redsweep  (sweep reduction factor = 1, 2, 4 or 8)
 *              redsearch  (binary search reduction factor = 1, 2, 4 or 8;
 *                          and must not exceed redsweep)
 *              sweeprange  (half the full range in each orthogonal
 *                           direction, taken about 0, in degrees)
 *              sweepdelta   (angle increment of sweep; in degrees)
 *              minbsdelta   (min binary search increment angle; in degrees)
 *              confprior  (amount by which confidence of 90 degree rotated
 *                          result is reduced when comparing with unrotated
 *                          confidence value)
 *      Return: 0 if OK, 1 on error or if angle measurment not valid
 *
 *  Notes:
 *      (1) This searches for the skew angle, first in the range
 *          [-sweeprange, sweeprange], and then in
 *          [90 - sweeprange, 90 + sweeprange], with angles measured
 *          clockwise.  For exploring the full range of possibilities,
 *          suggest using sweeprange = 47.0 degrees, giving some overlap
 *          at 45 and 135 degrees.  From these results, and discounting
 *          the the second confidence by %confprior, it selects the
 *          angle for maximal differential variance.  If the angle
 *          is larger than pi/4, the angle found after 90 degree rotation
 *          is selected.
 *      (2) The larger the confidence value, the greater the probability
 *          that the proper alignment is given by the angle that maximizes
 *          variance.  It should be compared to a threshold, which depends
 *          on the application.  Values between 3.0 and 6.0 are common.
 *      (3) Allowing for both portrait and landscape searches is more
 *          difficult, because if the signal from the text lines is weak,
 *          a signal from vertical rules can be larger!
 *          The most difficult documents to deskew have some or all of:
 *            (a) Multiple columns, not aligned
 *            (b) Black lines along the vertical edges
 *            (c) Text from two pages, and at different angles
 *          Rule of thumb for resolution:
 *            (a) If the margins are clean, you can work at 75 ppi,
 *                although 100 ppi is safer.
 *            (b) If there are vertical lines in the margins, do not
 *                work below 150 ppi.  The signal from the text lines must
 *                exceed that from the margin lines.
 *      (4) Choosing the %confprior parameter depends on knowing something
 *          about the source of image.  However, we're not using
 *          real probabilities here, so its use is qualitative.
 *          If landscape and portrait are equally likely, use
 *          %confprior = 0.0.  If the likelihood of portrait (non-rotated)
 *          is 100 times higher than that of landscape, we want to reduce
 *          the chance that we rotate to landscape in a situation where
 *          the landscape signal is accidentally larger than the
 *          portrait signal.  To do this use a positive value of
 *          %confprior; say 1.5.
 */
l_int32
pixFindSkewOrthogonalRange(PIX        *pixs,
                           l_float32  *pangle,
                           l_float32  *pconf,
                           l_int32     redsweep,
                           l_int32     redsearch,
                           l_float32   sweeprange,
                           l_float32   sweepdelta,
                           l_float32   minbsdelta,
                           l_float32   confprior)
{
l_float32  angle1, conf1, score1, angle2, conf2, score2;
PIX       *pixr;

    PROCNAME("pixFindSkewOrthogonalRange");

    if (pangle) *pangle = 0.0;
    if (pconf) *pconf = 0.0;
    if (!pangle || !pconf)
        return ERROR_INT("&angle and/or &conf not defined", procName, 1);
    if (!pixs || pixGetDepth(pixs) != 1)
        return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);

    pixFindSkewSweepAndSearchScorePivot(pixs, &angle1, &conf1, &score1,
                                        redsweep, redsearch, 0.0,
                                        sweeprange, sweepdelta, minbsdelta,
                                        L_SHEAR_ABOUT_CORNER);
    pixr = pixRotateOrth(pixs, 1);
    pixFindSkewSweepAndSearchScorePivot(pixr, &angle2, &conf2, &score2,
                                        redsweep, redsearch, 0.0,
                                        sweeprange, sweepdelta, minbsdelta,
                                        L_SHEAR_ABOUT_CORNER);
    pixDestroy(&pixr);

    if (conf1 > conf2 - confprior) {
        *pangle = angle1;
        *pconf = conf1;
    } else {
        *pangle = -90.0 + angle2;
        *pconf = conf2;
    }

#if  DEBUG_PRINT_ORTH
    fprintf(stderr, " About 0:  angle1 = %7.3f, conf1 = %7.3f, score1 = %f\n",
            angle1, conf1, score1);
    fprintf(stderr, " About 90: angle2 = %7.3f, conf2 = %7.3f, score2 = %f\n",
            angle2, conf2, score2);
    fprintf(stderr, " Final:    angle = %7.3f, conf = %7.3f\n", *pangle, *pconf);
#endif  /* DEBUG_PRINT_ORTH */

    return 0;
}
Example #2
0
/*!
 *  pixFindSkewSweepAndSearchScore()
 *
 *      Input:  pixs  (1 bpp)
 *              &angle   (<return> angle required to deskew; in degrees)
 *              &conf    (<return> confidence given by ratio of max/min score)
 *              &endscore (<optional return> max score; use NULL to ignore)
 *              redsweep  (sweep reduction factor = 1, 2, 4 or 8)
 *              redsearch  (binary search reduction factor = 1, 2, 4 or 8;
 *                          and must not exceed redsweep)
 *              sweepcenter  (angle about which sweep is performed; in degrees)
 *              sweeprange   (half the full range, taken about sweepcenter;
 *                            in degrees)
 *              sweepdelta   (angle increment of sweep; in degrees)
 *              minbsdelta   (min binary search increment angle; in degrees)
 *      Return: 0 if OK, 1 on error or if angle measurment not valid
 *
 *  Notes:
 *      (1) This finds the skew angle, doing first a sweep through a set
 *          of equal angles, and then doing a binary search until convergence.
 *      (2) There are two built-in constants that determine if the
 *          returned confidence is nonzero:
 *            - MIN_VALID_MAXSCORE (minimum allowed maxscore)
 *            - MINSCORE_THRESHOLD_CONSTANT (determines minimum allowed
 *                 minscore, by multiplying by (height * width^2)
 *          If either of these conditions is not satisfied, the returned
 *          confidence value will be zero.  The maxscore is optionally
 *          returned in this function to allow evaluation of the
 *          resulting angle by a method that is independent of the
 *          returned confidence value.
 *      (3) The larger the confidence value, the greater the probability
 *          that the proper alignment is given by the angle that maximizes
 *          variance.  It should be compared to a threshold, which depends
 *          on the application.  Values between 3.0 and 6.0 are common.
 *      (4) By default, the shear is about the UL corner.
 */
l_int32
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)
{
    return pixFindSkewSweepAndSearchScorePivot(pixs, pangle, pconf, pendscore,
                                               redsweep, redsearch, 0.0,
                                               sweeprange, sweepdelta,
                                               minbsdelta,
                                               L_SHEAR_ABOUT_CORNER);
}
Example #3
0
int main(int argc,
         char **argv) {
    l_int32 w, h, wd, hd;
    l_float32 deg2rad, angle, conf;
    PIX *pixs, *pixb1, *pixb2, *pixr, *pixf, *pixd, *pixc;
    PIXA *pixa;
    L_REGPARAMS *rp;

    if (regTestSetup(argc, argv, &rp))
        return 1;

    deg2rad = 3.1415926535 / 180.;

    pixa = pixaCreate(0);
    pixs = pixRead("feyn.tif");
    pixSetOrClearBorder(pixs, 100, 250, 100, 0, PIX_CLR);
    pixb1 = pixReduceRankBinaryCascade(pixs, 2, 2, 0, 0);
    regTestWritePixAndCheck(rp, pixb1, IFF_PNG);  /* 0 */
    pixDisplayWithTitle(pixb1, 0, 100, NULL, rp->display);

    /* Add a border and locate and deskew a 40 degree rotation */
    pixb2 = pixAddBorder(pixb1, BORDER, 0);
    pixGetDimensions(pixb2, &w, &h, NULL);
    pixSaveTiled(pixb2, pixa, 0.5, 1, 20, 8);
    pixr = pixRotateBySampling(pixb2, w / 2, h / 2,
                               deg2rad * 40., L_BRING_IN_WHITE);
    regTestWritePixAndCheck(rp, pixr, IFF_PNG);  /* 1 */
    pixSaveTiled(pixr, pixa, 0.5, 0, 20, 0);
    pixFindSkewSweepAndSearchScorePivot(pixr, &angle, &conf, NULL, 1, 1,
                                        0.0, 45.0, 2.0, 0.03,
                                        L_SHEAR_ABOUT_CENTER);
    fprintf(stderr, "Should be 40 degrees: angle = %7.3f, conf = %7.3f\n",
            angle, conf);
    pixf = pixRotateBySampling(pixr, w / 2, h / 2,
                               deg2rad * angle, L_BRING_IN_WHITE);
    pixd = pixRemoveBorder(pixf, BORDER);
    regTestWritePixAndCheck(rp, pixd, IFF_PNG);  /* 2 */
    pixSaveTiled(pixd, pixa, 0.5, 0, 20, 0);
    pixDestroy(&pixr);
    pixDestroy(&pixf);
    pixDestroy(&pixd);

    /* Do a rotation larger than 90 degrees using embedding;
     * Use 2 sets of measurements at 90 degrees to scan the
     * full range of possible rotation angles. */
    pixGetDimensions(pixb1, &w, &h, NULL);
    pixr = pixRotate(pixb1, deg2rad * 37., L_ROTATE_SAMPLING,
                     L_BRING_IN_WHITE, w, h);
    regTestWritePixAndCheck(rp, pixr, IFF_PNG);  /* 3 */
    pixSaveTiled(pixr, pixa, 0.5, 1, 20, 0);
    startTimer();
    pixFindSkewOrthogonalRange(pixr, &angle, &conf, 2, 1,
                               47.0, 1.0, 0.03, 0.0);
    fprintf(stderr, "Orth search time: %7.3f sec\n", stopTimer());
    fprintf(stderr, "Should be about -128 degrees: angle = %7.3f\n", angle);
    pixd = pixRotate(pixr, deg2rad * angle, L_ROTATE_SAMPLING,
                     L_BRING_IN_WHITE, w, h);
    regTestWritePixAndCheck(rp, pixd, IFF_PNG);  /* 4 */
    pixGetDimensions(pixd, &wd, &hd, NULL);
    pixc = pixCreate(w, h, 1);
    pixRasterop(pixc, 0, 0, w, h, PIX_SRC, pixd, (wd - w) / 2, (hd - h) / 2);
    regTestWritePixAndCheck(rp, pixc, IFF_PNG);  /* 5 */
    pixSaveTiled(pixc, pixa, 0.5, 0, 20, 0);
    pixDestroy(&pixr);
    pixDestroy(&pixf);
    pixDestroy(&pixd);
    pixDestroy(&pixc);

    pixd = pixaDisplay(pixa, 0, 0);
    regTestWritePixAndCheck(rp, pixd, IFF_PNG);  /* 6 */
    pixDisplayWithTitle(pixd, 100, 100, NULL, rp->display);
    pixDestroy(&pixd);

    pixDestroy(&pixs);
    pixDestroy(&pixb1);
    pixDestroy(&pixb2);
    pixaDestroy(&pixa);
    return regTestCleanup(rp);
}
Example #4
0
int main(int    argc,
         char **argv)
{
char        *filein, *fileout;
l_int32      ret;
l_float32    deg2rad;
l_float32    angle, conf, score;
PIX         *pix, *pixs, *pixd;
static char  mainName[] = "skewtest";

    if (argc != 3)
        return ERROR_INT(" Syntax:  skewtest filein fileout", mainName, 1);
    filein = argv[1];
    fileout = argv[2];

    setLeptDebugOK(1);
    pixd = NULL;
    deg2rad = 3.1415926535 / 180.;

    if ((pixs = pixRead(filein)) == NULL)
        return ERROR_INT("pixs not made", mainName, 1);

        /* Find the skew angle various ways */
    pix = pixConvertTo1(pixs, 130);
    pixWrite("/tmp/binarized.tif", pix, IFF_TIFF_G4);
    pixFindSkew(pix, &angle, &conf);
    fprintf(stderr, "pixFindSkew():\n"
                    "  conf = %5.3f, angle = %7.3f degrees\n", conf, angle);

    pixFindSkewSweepAndSearchScorePivot(pix, &angle, &conf, &score,
                                        SWEEP_REDUCTION2, SEARCH_REDUCTION,
                                        0.0, SWEEP_RANGE2, SWEEP_DELTA2,
                                        SEARCH_MIN_DELTA,
                                        L_SHEAR_ABOUT_CORNER);
    fprintf(stderr, "pixFind...Pivot(about corner):\n"
                    "  conf = %5.3f, angle = %7.3f degrees, score = %f\n",
            conf, angle, score);

    pixFindSkewSweepAndSearchScorePivot(pix, &angle, &conf, &score,
                                        SWEEP_REDUCTION2, SEARCH_REDUCTION,
                                        0.0, SWEEP_RANGE2, SWEEP_DELTA2,
                                        SEARCH_MIN_DELTA,
                                        L_SHEAR_ABOUT_CENTER);
    fprintf(stderr, "pixFind...Pivot(about center):\n"
                    "  conf = %5.3f, angle = %7.3f degrees, score = %f\n",
            conf, angle, score);

        /* Use top-level */
    pixd = pixDeskew(pixs, 0);
    pixWriteImpliedFormat(fileout, pixd, 0, 0);


#if 0
        /* Do it piecemeal; fails if outside the range */
    if (pixGetDepth(pixs) == 1) {
        pixd = pixDeskew(pix, DESKEW_REDUCTION);
        pixWrite(fileout, pixd, IFF_PNG);
    }
    else {
        ret = pixFindSkewSweepAndSearch(pix, &angle, &conf, SWEEP_REDUCTION2,
                                        SEARCH_REDUCTION, SWEEP_RANGE2,
                                        SWEEP_DELTA2, SEARCH_MIN_DELTA);
        if (ret)
            L_WARNING("skew angle not valid\n", mainName);
        else {
            fprintf(stderr, "conf = %5.3f, angle = %7.3f degrees\n",
                    conf, angle);
            if (conf > 2.5)
                pixd = pixRotate(pixs, angle * deg2rad, L_ROTATE_AREA_MAP,
                                 L_BRING_IN_WHITE, 0, 0);
            else
                pixd = pixClone(pixs);
            pixWrite(fileout, pixd, IFF_PNG);
            pixDestroy(&pixd);
        }
    }
#endif

    pixDestroy(&pixs);
    pixDestroy(&pix);
    pixDestroy(&pixd);
    return 0;
}