src/flipdetect.c
l_int32
pixMirrorDetect(PIX        *pixs,
                l_float32  *pconf,
                l_int32     mincount,
                l_int32     debug)
{
l_int32    count1, count2, nmax;
l_float32  nleft, nright;
PIX       *pixt0, *pixt1, *pixt2, *pixt3;
SEL       *sel1, *sel2;

    PROCNAME("pixMirrorDetect");

    if (!pconf)
        return ERROR_INT("&conf not defined", procName, 1);
    *pconf = 0.0;
    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (mincount == 0)
        mincount = DEFAULT_MIN_MIRROR_FLIP_COUNT;

    sel1 = selCreateFromString(textsel1, 5, 6, NULL);
    sel2 = selCreateFromString(textsel2, 5, 6, NULL);

        /* Fill x-height characters but not space between them, sort of. */
    pixt3 = pixMorphCompSequence(pixs, "d1.30", 0);
    pixXor(pixt3, pixt3, pixs);
    pixt0 = pixMorphCompSequence(pixs, "c15.1", 0);
    pixXor(pixt0, pixt0, pixs);
    pixAnd(pixt0, pixt0, pixt3);
    pixOr(pixt0, pixt0, pixs);
    pixDestroy(&pixt3);
/*    pixDisplayWrite(pixt0, 1); */

        /* Filter the right-facing characters. */
    pixt1 = pixHMT(NULL, pixt0, sel1);
    pixt3 = pixReduceRankBinaryCascade(pixt1, 1, 1, 0, 0);
    pixCountPixels(pixt3, &count1, NULL);
    pixDebugFlipDetect("junkpixright", pixs, pixt1, debug);
    pixDestroy(&pixt1);
    pixDestroy(&pixt3);

        /* Filter the left-facing characters. */
    pixt2 = pixHMT(NULL, pixt0, sel2);
    pixt3 = pixReduceRankBinaryCascade(pixt2, 1, 1, 0, 0);
    pixCountPixels(pixt3, &count2, NULL);
    pixDebugFlipDetect("junkpixleft", pixs, pixt2, debug);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);

    nright = (l_float32)count1;
    nleft = (l_float32)count2;
    nmax = L_MAX(count1, count2);
    pixDestroy(&pixt0);
    selDestroy(&sel1);
    selDestroy(&sel2);

    if (nmax > mincount)
        *pconf = 2. * ((nright - nleft) / sqrt(nright + nleft));

    if (debug) {
        fprintf(stderr, "nright = %f, nleft = %f\n", nright, nleft);
        if (*pconf > DEFAULT_MIN_MIRROR_FLIP_CONF)
            fprintf(stderr, "Text is not mirror reversed\n");
        if (*pconf < -DEFAULT_MIN_MIRROR_FLIP_CONF)
            fprintf(stderr, "Text is mirror reversed\n");
    }

    return 0;
}
/*!
 *  pixMirrorDetect()
 *
 *      Input:  pixs (1 bpp, deskewed, English text)
 *              &conf (<return> confidence that text is not LR mirror reversed)
 *              mincount (min number of left + right; use 0 for default)
 *              debug (1 for debug output; 0 otherwise)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) For this test, it is necessary that the text is horizontally
 *          oriented, with ascenders going up.
 *      (2) conf is the normalized difference between the number of
 *          right and left facing characters with ascenders.
 *          Left-facing are {d}; right-facing are {b, h, k}.
 *          At least that was the expectation.  In practice, we can
 *          really just say that it is the normalized difference in
 *          hits using two specific hit-miss filters, textsel1 and textsel2,
 *          after the image has been suitably pre-filtered so that
 *          these filters are effective.  See (4) for what's really happening.
 *      (3) A large positive conf value indicates normal text, whereas
 *          a large negative conf value means the page is mirror reversed.
 *      (4) The implementation is a bit tricky.  The general idea is
 *          to fill the x-height part of characters, but not the space
 *          between them, before doing the HMT.  This is done by
 *          finding pixels added using two different operations -- a
 *          horizontal close and a vertical dilation -- and adding
 *          the intersection of these sets to the original.  It turns
 *          out that the original intuition about the signal was largely
 *          in error: much of the signal for right-facing characters
 *          comes from the lower part of common x-height characters, like
 *          the e and c, that remain open after these operations.
 *          So it's important that the operations to close the x-height
 *          parts of the characters are purposely weakened sufficiently
 *          to allow these characters to remain open.  The wonders
 *          of morphology!
 */
pixMirrorDetect()
Input:  pixs (1 bpp, deskewed, English text)
&conf ( confidence that text is not LR mirror reversed)
mincount (min number of left + right; use 0 for default)
debug (1 for debug output; 0 otherwise)
Return: 0 if OK, 1 on error

(1) For this test, it is necessary that the text is horizontally
oriented, with ascenders going up.
(2) conf is the normalized difference between the number of
right and left facing characters with ascenders.
Left-facing are {d}; right-facing are {b, h, k}.
At least that was the expectation.  In practice, we can
really just say that it is the normalized difference in
hits using two specific hit-miss filters, textsel1 and textsel2,
after the image has been suitably pre-filtered so that
these filters are effective.  See (4) for what's really happening.
(3) A large positive conf value indicates normal text, whereas
a large negative conf value means the page is mirror reversed.
(4) The implementation is a bit tricky.  The general idea is
to fill the x-height part of characters, but not the space
between them, before doing the HMT.  This is done by
finding pixels added using two different operations -- a
horizontal close and a vertical dilation -- and adding
the intersection of these sets to the original.  It turns
out that the original intuition about the signal was largely
in error: much of the signal for right-facing characters
comes from the lower part of common x-height characters, like
the e and c, that remain open after these operations.
So it's important that the operations to close the x-height
parts of the characters are purposely weakened sufficiently
to allow these characters to remain open.  The wonders
of morphology!
pixMirrorDetect () Input : pixs ( 1 bpp deskewed English text ) &conf ( confidence that text is not LR mirror reversed ) mincount ( min number of left + right ; use 0 for default ) debug ( 1 for debug output ; 0 otherwise ) Return : 0 if OK 1 on error ( 1 ) For this test it is necessary that the text is horizontally oriented with ascenders going up . ( 2 ) conf is the normalized difference between the number of right and left facing characters with ascenders . Left - facing are { d } ; right - facing are { b h k } . At least that was the expectation . In practice we can really just say that it is the normalized difference in hits using two specific hit - miss filters textsel1 and textsel2 after the image has been suitably pre - filtered so that these filters are effective . See ( 4 ) for what ' s really happening . ( 3 ) A large positive conf value indicates normal text whereas a large negative conf value means the page is mirror reversed . ( 4 ) The implementation is a bit tricky . The general idea is to fill the x - height part of characters but not the space between them before doing the HMT . This is done by finding pixels added using two different operations -- a horizontal close and a vertical dilation -- and adding the intersection of these sets to the original . It turns out that the original intuition about the signal was largely in error : much of the signal for right - facing characters comes from the lower part of common x - height characters like the e and c that remain open after these operations . So it ' s important that the operations to close the x - height parts of the characters are purposely weakened sufficiently to allow these characters to remain open . The wonders of morphology!
l_int32
pixMirrorDetect(PIX        *pixs,
                l_float32  *pconf,
                l_int32     mincount,
                l_int32     debug)
{
l_int32    count1, count2, nmax;
l_float32  nleft, nright;
PIX       *pixt0, *pixt1, *pixt2, *pixt3;
SEL       *sel1, *sel2;
    PROCNAME("pixMirrorDetect");
    if (!pconf)
        return ERROR_INT("&conf not defined", procName, 1);
    *pconf = 0.0;
    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (mincount == 0)
        mincount = DEFAULT_MIN_MIRROR_FLIP_COUNT;
    sel1 = selCreateFromString(textsel1, 5, 6, NULL);
    sel2 = selCreateFromString(textsel2, 5, 6, NULL);
    pixt3 = pixMorphCompSequence(pixs, "d1.30", 0);
    pixXor(pixt3, pixt3, pixs);
    pixt0 = pixMorphCompSequence(pixs, "c15.1", 0);
    pixXor(pixt0, pixt0, pixs);
    pixAnd(pixt0, pixt0, pixt3);
    pixOr(pixt0, pixt0, pixs);
    pixDestroy(&pixt3);
    pixt1 = pixHMT(NULL, pixt0, sel1);
    pixt3 = pixReduceRankBinaryCascade(pixt1, 1, 1, 0, 0);
    pixCountPixels(pixt3, &count1, NULL);
    pixDebugFlipDetect("junkpixright", pixs, pixt1, debug);
    pixDestroy(&pixt1);
    pixDestroy(&pixt3);
    pixt2 = pixHMT(NULL, pixt0, sel2);
    pixt3 = pixReduceRankBinaryCascade(pixt2, 1, 1, 0, 0);
    pixCountPixels(pixt3, &count2, NULL);
    pixDebugFlipDetect("junkpixleft", pixs, pixt2, debug);
    pixDestroy(&pixt2);
    pixDestroy(&pixt3);
    nright = (l_float32)count1;
    nleft = (l_float32)count2;
    nmax = L_MAX(count1, count2);
    pixDestroy(&pixt0);
    selDestroy(&sel1);
    selDestroy(&sel2);
    if (nmax > mincount)
        *pconf = 2. * ((nright - nleft) / sqrt(nright + nleft));
    if (debug) {
        fprintf(stderr, "nright = %f, nleft = %f\n", nright, nleft);
        if (*pconf > DEFAULT_MIN_MIRROR_FLIP_CONF)
            fprintf(stderr, "Text is not mirror reversed\n");
        if (*pconf < -DEFAULT_MIN_MIRROR_FLIP_CONF)
            fprintf(stderr, "Text is mirror reversed\n");
    }
    return 0;
}
l_int32 pixMirrorDetect ( PIX * pixs , l_float32 * pconf , l_int32 mincount , l_int32 debug ) { l_int32 count1 , count2 , nmax ; l_float32 nleft , nright ; PIX * pixt0 , * pixt1 , * pixt2 , * pixt3 ; SEL * sel1 , * sel2 ; PROCNAME ( " " ) ; if ( ! pconf ) return ERROR_INT ( " " , procName , 1 ) ; * pconf = 0.0 ; if ( ! pixs ) return ERROR_INT ( " " , procName , 1 ) ; if ( mincount == 0 ) mincount = DEFAULT_MIN_MIRROR_FLIP_COUNT ; sel1 = selCreateFromString ( textsel1 , 5 , 6 , NULL ) ; sel2 = selCreateFromString ( textsel2 , 5 , 6 , NULL ) ; pixt3 = pixMorphCompSequence ( pixs , " " , 0 ) ; pixXor ( pixt3 , pixt3 , pixs ) ; pixt0 = pixMorphCompSequence ( pixs , " " , 0 ) ; pixXor ( pixt0 , pixt0 , pixs ) ; pixAnd ( pixt0 , pixt0 , pixt3 ) ; pixOr ( pixt0 , pixt0 , pixs ) ; pixDestroy ( & pixt3 ) ; pixt1 = pixHMT ( NULL , pixt0 , sel1 ) ; pixt3 = pixReduceRankBinaryCascade ( pixt1 , 1 , 1 , 0 , 0 ) ; pixCountPixels ( pixt3 , & count1 , NULL ) ; pixDebugFlipDetect ( " " , pixs , pixt1 , debug ) ; pixDestroy ( & pixt1 ) ; pixDestroy ( & pixt3 ) ; pixt2 = pixHMT ( NULL , pixt0 , sel2 ) ; pixt3 = pixReduceRankBinaryCascade ( pixt2 , 1 , 1 , 0 , 0 ) ; pixCountPixels ( pixt3 , & count2 , NULL ) ; pixDebugFlipDetect ( " " , pixs , pixt2 , debug ) ; pixDestroy ( & pixt2 ) ; pixDestroy ( & pixt3 ) ; nright = ( l_float32 ) count1 ; nleft = ( l_float32 ) count2 ; nmax = L_MAX ( count1 , count2 ) ; pixDestroy ( & pixt0 ) ; selDestroy ( & sel1 ) ; selDestroy ( & sel2 ) ; if ( nmax > mincount ) * pconf = 2. * ( ( nright - nleft ) / sqrt ( nright + nleft ) ) ; if ( debug ) { fprintf ( stderr , " \n " , nright , nleft ) ; if ( * pconf > DEFAULT_MIN_MIRROR_FLIP_CONF ) fprintf ( stderr , " \n " ) ; if ( * pconf < - DEFAULT_MIN_MIRROR_FLIP_CONF ) fprintf ( stderr , " \n " ) ; } return 0 ; }
pixMirrorDetect()
      Input:  pixs (1 bpp, deskewed, English text)
              &conf (<return> confidence that text is not LR mirror reversed)
              mincount (min number of left + right; use 0 for default)
              debug (1 for debug output; 0 otherwise)
      Return: 0 if OK, 1 on error
pixMirrorDetect () Input : pixs ( 1 bpp deskewed English text ) &conf ( <return > confidence that text is not LR mirror reversed ) mincount ( min number of left + right ; use 0 for default ) debug ( 1 for debug output ; 0 otherwise ) Return : 0 if OK 1 on error
/* Fill x-height characters but not space between them, sort of. */ /*    pixDisplayWrite(pixt0, 1); */ /* Filter the right-facing characters. */ /* Filter the left-facing characters. */