Ejemplo n.º 1
0
Pix* binarizeLegacy(Pix* pix){
    Pix* phm = NULL;
    Pix* pixb = binarizeTiled(pix, phm);
    pixCopyResolution(pixb, pix);
    return pixb;
    
}
Ejemplo n.º 2
0
/*!
 *  pixExpandBinaryPower2()
 *
 *      Input:  pixs (1 bpp)
 *              factor (expansion factor: 1, 2, 4, 8, 16)
 *      Return: pixd (expanded 1 bpp by replication), or null on error
 */
PIX *
pixExpandBinaryPower2(PIX     *pixs,
                      l_int32  factor)
{
l_int32    w, h, d, wd, hd, wpls, wpld;
l_uint32  *datas, *datad;
PIX       *pixd;

    PROCNAME("pixExpandBinaryPower2");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    pixGetDimensions(pixs, &w, &h, &d);
    if (d != 1)
        return (PIX *)ERROR_PTR("pixs not binary", procName, NULL);
    if (factor == 1)
        return pixCopy(NULL, pixs);
    if (factor != 2 && factor != 4 && factor != 8 && factor != 16)
        return (PIX *)ERROR_PTR("factor must be in {2,4,8,16}", procName, NULL);

    wpls = pixGetWpl(pixs);
    datas = pixGetData(pixs);
    wd = factor * w;
    hd = factor * h;
    if ((pixd = pixCreate(wd, hd, 1)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs);
    pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor);
    wpld = pixGetWpl(pixd);
    datad = pixGetData(pixd);

    expandBinaryPower2Low(datad, wd, hd, wpld, datas, w, h, wpls, factor);

    return pixd;
}
Ejemplo n.º 3
0
/*!
 *  pixFinalAccumulateThreshold()
 *
 *      Input:  pixs (32 bpp)
 *              offset (same as used for initialization)
 *              threshold (values less than this are set in the destination)
 *      Return: pixd (1 bpp), or null on error
 *
 *  Notes:
 *      (1) The offset must be >= 0 and should not exceed 0x40000000.
 *      (2) The offset is subtracted from the src 32 bpp image
 */
PIX *
pixFinalAccumulateThreshold(PIX      *pixs,
                            l_uint32  offset,
                            l_uint32  threshold)
{
l_int32    w, h, wpls, wpld;
l_uint32  *datas, *datad;
PIX       *pixd;

    PROCNAME("pixFinalAccumulateThreshold");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 32)
        return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
    if (offset > 0x40000000)
        offset = 0x40000000;

    pixGetDimensions(pixs, &w, &h, NULL);
    if ((pixd = pixCreate(w, h, 1)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs);  /* but how did pixs get it initially? */
    datas = pixGetData(pixs);
    datad = pixGetData(pixd);
    wpls = pixGetWpl(pixs);
    wpld = pixGetWpl(pixd);

    finalAccumulateThreshLow(datad, w, h, wpld, datas, wpls, offset, threshold);
    return pixd;
}
/*!
 *  pixAddRGB()
 *
 *      Input:  pixs1, pixs2  (32 bpp RGB, or colormapped)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) Clips computation to the minimum size, aligning the UL corners.
 *      (2) Removes any colormap to RGB, and ignores the LSB of each
 *          pixel word (the alpha channel).
 *      (3) Adds each component value, pixelwise, clipping to 255.
 *      (4) This is useful to combine two images where most of the
 *          pixels are essentially black, such as in pixPerceptualDiff().
 */
PIX *
pixAddRGB(PIX  *pixs1,
          PIX  *pixs2)
{
l_int32    i, j, w, h, d, w2, h2, d2, wplc1, wplc2, wpld;
l_int32    rval1, gval1, bval1, rval2, gval2, bval2, rval, gval, bval;
l_uint32  *datac1, *datac2, *datad, *linec1, *linec2, *lined;
PIX       *pixc1, *pixc2, *pixd;

    PROCNAME("pixAddRGB");

    if (!pixs1)
        return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL);
    if (!pixs2)
        return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL);
    pixGetDimensions(pixs1, &w, &h, &d);
    pixGetDimensions(pixs2, &w2, &h2, &d2);
    if (!pixGetColormap(pixs1) && d != 32)
        return (PIX *)ERROR_PTR("pixs1 not cmapped or rgb", procName, NULL);
    if (!pixGetColormap(pixs2) && d2 != 32)
        return (PIX *)ERROR_PTR("pixs2 not cmapped or rgb", procName, NULL);
    if (pixGetColormap(pixs1))
        pixc1 = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR);
    else
        pixc1 = pixClone(pixs1);
    if (pixGetColormap(pixs2))
        pixc2 = pixRemoveColormap(pixs2, REMOVE_CMAP_TO_FULL_COLOR);
    else
        pixc2 = pixClone(pixs2);

    w = L_MIN(w, w2);
    h = L_MIN(h, h2);
    pixd = pixCreate(w, h, 32);
    pixCopyResolution(pixd, pixs1);
    datac1 = pixGetData(pixc1);
    datac2 = pixGetData(pixc2);
    datad = pixGetData(pixd);
    wplc1 = pixGetWpl(pixc1);
    wplc2 = pixGetWpl(pixc2);
    wpld = pixGetWpl(pixd);
    for (i = 0; i < h; i++) {
        linec1 = datac1 + i * wplc1;
        linec2 = datac2 + i * wplc2;
        lined = datad + i * wpld;
        for (j = 0; j < w; j++) {
            extractRGBValues(linec1[j], &rval1, &gval1, &bval1);
            extractRGBValues(linec2[j], &rval2, &gval2, &bval2);
            rval = L_MIN(255, rval1 + rval2);
            gval = L_MIN(255, gval1 + gval2);
            bval = L_MIN(255, bval1 + bval2);
            composeRGBPixel(rval, gval, bval, lined + j);
        }
    }

    pixDestroy(&pixc1);
    pixDestroy(&pixc2);
    return pixd;
}
Ejemplo n.º 5
0
/*!
 *  pixEmbedForRotation()
 *
 *      Input:  pixs (1, 2, 4, 8, 32 bpp rgb)
 *              angle (radians; clockwise is positive)
 *              incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
 *              width (original width; use 0 to avoid embedding)
 *              height (original height; use 0 to avoid embedding)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) For very small rotations, just return a clone.
 *      (2) Generate larger image to embed pixs if necessary, and
 *          place the center of the input image in the center.
 *      (3) Rotation brings either white or black pixels in
 *          from outside the image.  For colormapped images where
 *          there is no white or black, a new color is added if
 *          possible for these pixels; otherwise, either the
 *          lightest or darkest color is used.  In most cases,
 *          the colormap will be removed prior to rotation.
 *      (4) The dest is to be expanded so that no image pixels
 *          are lost after rotation.  Input of the original width
 *          and height allows the expansion to stop at the maximum
 *          required size, which is a square with side equal to
 *          sqrt(w*w + h*h).
 *      (5) For an arbitrary angle, the expansion can be found by
 *          considering the UL and UR corners.  As the image is
 *          rotated, these move in an arc centered at the center of
 *          the image.  Normalize to a unit circle by dividing by half
 *          the image diagonal.  After a rotation of T radians, the UL
 *          and UR corners are at points T radians along the unit
 *          circle.  Compute the x and y coordinates of both these
 *          points and take the max of absolute values; these represent
 *          the half width and half height of the containing rectangle.
 *          The arithmetic is done using formulas for sin(a+b) and cos(a+b),
 *          where b = T.  For the UR corner, sin(a) = h/d and cos(a) = w/d.
 *          For the UL corner, replace a by (pi - a), and you have
 *          sin(pi - a) = h/d, cos(pi - a) = -w/d.  The equations
 *          given below follow directly.
 */
PIX *
pixEmbedForRotation(PIX       *pixs,
                    l_float32  angle,
                    l_int32    incolor,
                    l_int32    width,
                    l_int32    height)
{
l_int32    w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff, setcolor;
l_float64  sina, cosa, fw, fh;
PIX       *pixd;

    PROCNAME("pixEmbedForRotation");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
        return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
    if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE)
        return pixClone(pixs);

        /* Test if big enough to hold any rotation of the original image */
    pixGetDimensions(pixs, &w, &h, &d);
    maxside = (l_int32)(sqrt((l_float64)(width * width) +
                             (l_float64)(height * height)) + 0.5);
    if (w >= maxside && h >= maxside)  /* big enough */
        return pixClone(pixs);

        /* Find the new sizes required to hold the image after rotation.
         * Note that the new dimensions must be at least as large as those
         * of pixs, because we're rasterop-ing into it before rotation. */
    cosa = cos(angle);
    sina = sin(angle);
    fw = (l_float64)w;
    fh = (l_float64)h;
    w1 = (l_int32)(L_ABS(fw * cosa - fh * sina) + 0.5);
    w2 = (l_int32)(L_ABS(-fw * cosa - fh * sina) + 0.5);
    h1 = (l_int32)(L_ABS(fw * sina + fh * cosa) + 0.5);
    h2 = (l_int32)(L_ABS(-fw * sina + fh * cosa) + 0.5);
    wnew = L_MAX(w, L_MAX(w1, w2));
    hnew = L_MAX(h, L_MAX(h1, h2));

    if ((pixd = pixCreate(wnew, hnew, d)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs);
    pixCopyColormap(pixd, pixs);
    pixCopySpp(pixd, pixs);
    pixCopyText(pixd, pixs);
    xoff = (wnew - w) / 2;
    yoff = (hnew - h) / 2;

        /* Set background to color to be rotated in */
    setcolor = (incolor == L_BRING_IN_BLACK) ? L_SET_BLACK : L_SET_WHITE;
    pixSetBlackOrWhite(pixd, setcolor);

        /* Rasterop automatically handles all 4 channels for rgba */
    pixRasterop(pixd, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0);
    return pixd;
}
Ejemplo n.º 6
0
/*!
 *  pixSelectByWidthHeightRatio()
 *
 *      Input:  pixs (1 bpp)
 *              thresh (threshold ratio of width/height)
 *              connectivity (4 or 8)
 *              type (L_SELECT_IF_LT, L_SELECT_IF_GT,
 *                    L_SELECT_IF_LTE, L_SELECT_IF_GTE)
 *              &changed (<optional return> 1 if changed; 0 if clone returned)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) The args specify constraints on the width-to-height ratio
 *          for components that are kept.
 *      (2) If unchanged, returns a copy of pixs.  Otherwise,
 *          returns a new pix with the filtered components.
 *      (3) This filters components based on the width-to-height ratios.
 *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
 *          with less than the threshold ratio, and
 *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
 */
PIX *
pixSelectByWidthHeightRatio(PIX       *pixs,
                            l_float32  thresh,
                            l_int32    connectivity,
                            l_int32    type,
                            l_int32   *pchanged)
{
l_int32  w, h, empty, changed, count;
BOXA    *boxa;
PIX     *pixd;
PIXA    *pixas, *pixad;

    PROCNAME("pixSelectByWidthHeightRatio");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (connectivity != 4 && connectivity != 8)
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
    if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
        type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
        return (PIX *)ERROR_PTR("invalid type", procName, NULL);
    if (pchanged) *pchanged = FALSE;
    
        /* Check if any components exist */
    pixZero(pixs, &empty);
    if (empty)
        return pixCopy(NULL, pixs);

        /* Filter components */
    boxa = pixConnComp(pixs, &pixas, connectivity); 
    pixad = pixaSelectByWidthHeightRatio(pixas, thresh, type, &changed);
    boxaDestroy(&boxa);
    pixaDestroy(&pixas);

        /* Render the result */
    if (!changed) {
        pixaDestroy(&pixad);
        return pixCopy(NULL, pixs);
    }
    else {
        if (pchanged) *pchanged = TRUE;
        pixGetDimensions(pixs, &w, &h, NULL);
        count = pixaGetCount(pixad);
        if (count == 0)  /* return empty pix */
            pixd = pixCreateTemplate(pixs);
        else {
            pixd = pixaDisplay(pixad, w, h);
            pixCopyResolution(pixd, pixs);
            pixCopyColormap(pixd, pixs);
            pixCopyText(pixd, pixs);
            pixCopyInputFormat(pixd, pixs);
        }
        pixaDestroy(&pixad);
        return pixd;
    }
}
Ejemplo n.º 7
0
Pix* edgeBinarize(Pix* pix){
    FUNCNAME("edgeBinarize");
#if HAS_ADAPTIVE_BINARIZER
    PixAdaptiveBinarizer binarizer(false);
    return binarizer.bradleyAdaptiveThresholding(pix, 0.15, 8);
#endif
    Pix* result;
    pixOtsuAdaptiveThreshold(pix, pixGetWidth(pix), pixGetHeight(pix), 0, 0, 0.1, NULL, &result);
    pixCopyResolution(result, pix);
    return result;
}
Ejemplo n.º 8
0
/*!
 * \brief   pixColorSegmentCluster()
 *
 * \param[in]    pixs  32 bpp; 24-bit color
 * \param[in]    maxdist max euclidean dist to existing cluster
 * \param[in]    maxcolors max number of colors allowed in first pass
 * \param[in]    debugflag  1 for debug output; 0 otherwise
 * \return  pixd 8 bit with colormap, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) This is phase 1.  See description in pixColorSegment().
 *      (2) Greedy unsupervised classification.  If the limit 'maxcolors'
 *          is exceeded, the computation is repeated with a larger
 *          allowed cluster size.
 *      (3) On each successive iteration, 'maxdist' is increased by a
 *          constant factor.  See comments in pixColorSegment() for
 *          a guideline on parameter selection.
 *          Note that the diagonal of the 8-bit rgb color cube is about
 *          440, so for 'maxdist' = 440, you are guaranteed to get 1 color!
 * </pre>
 */
PIX *
pixColorSegmentCluster(PIX     *pixs,
                       l_int32  maxdist,
                       l_int32  maxcolors,
                       l_int32  debugflag)
{
l_int32   w, h, newmaxdist, ret, niters, ncolors, success;
PIX      *pixd;
PIXCMAP  *cmap;

    PROCNAME("pixColorSegmentCluster");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 32)
        return (PIX *)ERROR_PTR("must be rgb color", procName, NULL);

    pixGetDimensions(pixs, &w, &h, NULL);
    if ((pixd = pixCreate(w, h, 8)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    cmap = pixcmapCreate(8);
    pixSetColormap(pixd, cmap);
    pixCopyResolution(pixd, pixs);

    newmaxdist = maxdist;
    niters = 0;
    success = TRUE;
    while (1) {
        ret = pixColorSegmentTryCluster(pixd, pixs, newmaxdist,
                                        maxcolors, debugflag);
        niters++;
        if (!ret) {
            ncolors = pixcmapGetCount(cmap);
            if (debugflag)
                L_INFO("Success with %d colors after %d iters\n", procName,
                       ncolors, niters);
            break;
        }
        if (niters == MAX_ALLOWED_ITERATIONS) {
            L_WARNING("too many iters; newmaxdist = %d\n",
                      procName, newmaxdist);
            success = FALSE;
            break;
        }
        newmaxdist = (l_int32)(DIST_EXPAND_FACT * (l_float32)newmaxdist);
    }

    if (!success) {
        pixDestroy(&pixd);
        return (PIX *)ERROR_PTR("failure in phase 1", procName, NULL);
    }

    return pixd;
}
/*!
 *  pixMaxDynamicRange()
 *
 *      Input:  pixs  (4, 8, 16 or 32 bpp source)
 *              type  (L_LINEAR_SCALE or L_LOG_SCALE)
 *      Return: pixd (8 bpp), or null on error
 *
 *  Notes:
 *      (1) Scales pixel values to fit maximally within the dest 8 bpp pixd
 *      (2) Uses a LUT for log scaling
 */
PIX *
pixMaxDynamicRange(PIX     *pixs,
                   l_int32  type)
{
l_uint8     dval;
l_int32     i, j, w, h, d, wpls, wpld, max, sval;
l_uint32   *datas, *datad;
l_uint32    word;
l_uint32   *lines, *lined;
l_float32   factor;
l_float32  *tab;
PIX        *pixd;

    PROCNAME("pixMaxDynamicRange");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    d = pixGetDepth(pixs);
    if (d != 4 && d != 8 && d != 16 && d != 32)
        return (PIX *)ERROR_PTR("pixs not in {4,8,16,32} bpp", procName, NULL);
    if (type != L_LINEAR_SCALE && type != L_LOG_SCALE)
        return (PIX *)ERROR_PTR("invalid type", procName, NULL);

    pixGetDimensions(pixs, &w, &h, NULL);
    if ((pixd = pixCreate(w, h, 8)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs);
    datas = pixGetData(pixs);
    datad = pixGetData(pixd);
    wpls = pixGetWpl(pixs);
    wpld = pixGetWpl(pixd);

        /* Get max */
    max = 0;
    for (i = 0; i < h; i++) {
        lines = datas + i * wpls;
        for (j = 0; j < wpls; j++) {
            word = *(lines + j);
            if (d == 4) {
                max = L_MAX(max, word >> 28);
                max = L_MAX(max, (word >> 24) & 0xf);
                max = L_MAX(max, (word >> 20) & 0xf);
                max = L_MAX(max, (word >> 16) & 0xf);
                max = L_MAX(max, (word >> 12) & 0xf);
                max = L_MAX(max, (word >> 8) & 0xf);
                max = L_MAX(max, (word >> 4) & 0xf);
                max = L_MAX(max, word & 0xf);
            } else if (d == 8) {
                max = L_MAX(max, word >> 24);
                max = L_MAX(max, (word >> 16) & 0xff);
                max = L_MAX(max, (word >> 8) & 0xff);
                max = L_MAX(max, word & 0xff);
            } else if (d == 16) {
Ejemplo n.º 10
0
/*!
 * \brief   pixExpandBinaryReplicate()
 *
 * \param[in]    pixs 1 bpp
 * \param[in]    xfact  integer scale factor for horiz. replicative expansion
 * \param[in]    yfact  integer scale factor for vertical replicative expansion
 * \return  pixd scaled up, or NULL on error
 */
PIX *
pixExpandBinaryReplicate(PIX     *pixs,
                         l_int32  xfact,
                         l_int32  yfact)
{
    l_int32    w, h, d, wd, hd, wpls, wpld, i, j, k, start;
    l_uint32  *datas, *datad, *lines, *lined;
    PIX       *pixd;

    PROCNAME("pixExpandBinaryReplicate");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    pixGetDimensions(pixs, &w, &h, &d);
    if (d != 1)
        return (PIX *)ERROR_PTR("pixs not binary", procName, NULL);
    if (xfact <= 0 || yfact <= 0)
        return (PIX *)ERROR_PTR("invalid scale factor: <= 0", procName, NULL);

    if (xfact == yfact) {
        if (xfact == 1)
            return pixCopy(NULL, pixs);
        if (xfact == 2 || xfact == 4 || xfact == 8 || xfact == 16)
            return pixExpandBinaryPower2(pixs, xfact);
    }

    wpls = pixGetWpl(pixs);
    datas = pixGetData(pixs);
    wd = xfact * w;
    hd = yfact * h;
    if ((pixd = pixCreate(wd, hd, 1)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs);
    pixScaleResolution(pixd, (l_float32)xfact, (l_float32)yfact);
    wpld = pixGetWpl(pixd);
    datad = pixGetData(pixd);

    for (i = 0; i < h; i++) {
        lines = datas + i * wpls;
        lined = datad + yfact * i * wpld;
        for (j = 0; j < w; j++) {  /* replicate pixels on a single line */
            if (GET_DATA_BIT(lines, j)) {
                start = xfact * j;
                for (k = 0; k < xfact; k++)
                    SET_DATA_BIT(lined, start + k);
            }
        }
        for (k = 1; k < yfact; k++)  /* replicate the line */
            memcpy(lined + k * wpld, lined, 4 * wpld);
    }

    return pixd;
}
Ejemplo n.º 11
0
Pix* dewarpOrDeskew(Pix* pix) {
    FUNCNAME("dewarpOrDeskew");
    Pix* pixText = NULL;
    l_int32 dewarpResult = pixDewarp(pix, &pixText);
    
    if(dewarpResult){
        L_INFO("dewarp failed. Attempting to correct skew instead.", procName);
        pixText = deskew(pix);
    }
    pixCopyResolution(pixText, pix);
    return pixText;
}
Ejemplo n.º 12
0
Pix* binarize(Pix* pix, ProgressCallback* callback) {
    FUNCNAME("binarize");
    Pix *pixb;
#ifdef HAS_ADAPTIVE_BINARIZER
    PixBinarizer binarizer(false);
    pixb = binarizer.binarize(pix, callback);
#else
    pixb = binarizeTiled(pix, NULL);
#endif
    pixCopyResolution(pixb, pix);
    return pixb;
}
Ejemplo n.º 13
0
/*!
 *  pixExpandBinaryReplicate()
 *
 *      Input:  pixs (1 bpp)
 *              factor (integer scale factor for replicative expansion)
 *      Return: pixd (scaled up), or null on error
 */
PIX *
pixExpandBinaryReplicate(PIX     *pixs,
                         l_int32  factor)
{
l_int32    w, h, d, wd, hd, wpls, wpld, i, j, k, start;
l_uint32  *datas, *datad, *lines, *lined;
PIX       *pixd;

    PROCNAME("pixExpandBinaryReplicate");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    pixGetDimensions(pixs, &w, &h, &d);
    if (d != 1)
        return (PIX *)ERROR_PTR("pixs not binary", procName, NULL);
    if (factor <= 0)
        return (PIX *)ERROR_PTR("factor <= 0; invalid", procName, NULL);

    if (factor == 1)
        return pixCopy(NULL, pixs);
    if (factor == 2 || factor == 4 || factor == 8 || factor == 16)
        return pixExpandBinaryPower2(pixs, factor);

    wpls = pixGetWpl(pixs);
    datas = pixGetData(pixs);
    wd = factor * w;
    hd = factor * h;
    if ((pixd = pixCreate(wd, hd, 1)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs);
    pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor);
    wpld = pixGetWpl(pixd);
    datad = pixGetData(pixd);

    for (i = 0; i < h; i++) {
        lines = datas + i * wpls;
        lined = datad + factor * i * wpld;
        for (j = 0; j < w; j++) {
            if (GET_DATA_BIT(lines, j)) {
                start = factor * j;
                for (k = 0; k < factor; k++)
                    SET_DATA_BIT(lined, start + k);
            }
        }
        for (k = 1; k < factor; k++)
            memcpy(lined + k * wpld, lined, 4 * wpld);
    }

    return pixd;
}
Ejemplo n.º 14
0
/*!
 *  pixTransferAllData()
 *
 *      Input:  pixd (must be different from pixs)
 *              &pixs (will be nulled if refcount goes to 0)
 *              copytext (1 to copy the text field; 0 to skip)
 *              copyformat (1 to copy the informat field; 0 to skip)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This does a complete data transfer from pixs to pixd,
 *          followed by the destruction of pixs (refcount permitting).
 *      (2) If the refcount of pixs is 1, pixs is destroyed.  Otherwise,
 *          the data in pixs is copied (rather than transferred) to pixd.
 *      (3) This operation, like all others with a pre-existing pixd,
 *          will side-effect any existing clones of pixd.  The pixd
 *          refcount does not change.
 *      (4) When might you use this?  Suppose you have an in-place Pix
 *          function (returning void) with the typical signature:
 *              void function-inplace(PIX *pix, ...)
 *          where "..." are non-pointer input parameters, and suppose
 *          further that you sometimes want to return an arbitrary Pix
 *          in place of the input Pix.  There are two ways you can do this:
 *          (a) The straightforward way is to change the function
 *              signature to take the address of the Pix ptr:
 *                  void function-inplace(PIX **ppix, ...) {
 *                      PIX *pixt = function-makenew(*ppix);
 *                      pixDestroy(ppix);
 *                      *ppix = pixt;
 *                      return;
 *                  }
 *              Here, the input and returned pix are different, as viewed
 *              by the calling function, and the inplace function is
 *              expected to destroy the input pix to avoid a memory leak.
 *          (b) Keep the signature the same and use pixTransferAllData()
 *              to return the new Pix in the input Pix struct:
 *                  void function-inplace(PIX *pix, ...) {
 *                      PIX *pixt = function-makenew(pix);
 *                      pixTransferAllData(pix, &pixt);  // pixt is destroyed
 *                      return;
 *                  }
 *              Here, the input and returned pix are the same, as viewed
 *              by the calling function, and the inplace function must
 *              never destroy the input pix, because the calling function
 *              maintains an unchanged handle to it.
 */
l_int32
pixTransferAllData(PIX     *pixd,
                   PIX    **ppixs,
                   l_int32  copytext,
                   l_int32  copyformat)
{
l_int32  nbytes;
PIX     *pixs;

    PROCNAME("pixTransferAllData");

    if (!ppixs)
        return ERROR_INT("&pixs not defined", procName, 1);
    if ((pixs = *ppixs) == NULL)
        return ERROR_INT("pixs not defined", procName, 1);
    if (!pixd)
        return ERROR_INT("pixd not defined", procName, 1);
    if (pixs == pixd)  /* no-op */
        return ERROR_INT("pixd == pixs", procName, 1);

    if (pixGetRefcount(pixs) == 1) {  /* transfer the data, cmap, text */
        pixFreeData(pixd);  /* dealloc any existing data */
        pixSetData(pixd, pixGetData(pixs));  /* transfer new data from pixs */
        pixs->data = NULL;  /* pixs no longer owns data */
        pixSetColormap(pixd, pixGetColormap(pixs));  /* frees old; sets new */
        pixs->colormap = NULL;  /* pixs no longer owns colormap */
        if (copytext) {
            pixSetText(pixd, pixGetText(pixs));
            pixSetText(pixs, NULL);
        }
    } else {  /* preserve pixs by making a copy of the data, cmap, text */
        pixResizeImageData(pixd, pixs);
        nbytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs);
        memcpy((char *)pixGetData(pixd), (char *)pixGetData(pixs), nbytes);
        pixCopyColormap(pixd, pixs);
        if (copytext)
            pixCopyText(pixd, pixs);
    }
  
    pixCopyResolution(pixd, pixs);
    pixCopyDimensions(pixd, pixs);
    if (copyformat)
        pixCopyInputFormat(pixd, pixs);

        /* This will destroy pixs if data was transferred;
         * otherwise, it just decrements its refcount. */
    pixDestroy(ppixs);
    return 0;
}
Ejemplo n.º 15
0
Pix* savGol(Pix* pix) {
    FUNCNAME("savGol");
    l_int32 xres, yres;
    pixGetResolution(pix, &xres, &yres);
    l_uint8 degree = 4;
    l_float32 textLineHeight = yres/6.25;
    l_uint8 window = L_MIN(15, L_MAX(5, textLineHeight/4));
    if(window % 2 == 0){
        window--;
    }

    L_INFO("text line height = %.2f, window = %i, degree = %i\n", procName, textLineHeight, window, degree);
    Pix* result =  pixSavGolFilter(pix, window, degree, degree);
    pixCopyResolution(result, pix);
    return result;
}
Ejemplo n.º 16
0
/*!
 *  pixCopy()
 *
 *      Input:  pixd (<optional>; can be null, or equal to pixs,
 *                    or different from pixs)
 *              pixs
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) There are three cases:
 *            (a) pixd == null  (makes a new pix; refcount = 1)
 *            (b) pixd == pixs  (no-op)
 *            (c) pixd != pixs  (data copy; no change in refcount)
 *          If the refcount of pixd > 1, case (c) will side-effect
 *          these handles.
 *      (2) The general pattern of use is:
 *             pixd = pixCopy(pixd, pixs);
 *          This will work for all three cases.
 *          For clarity when the case is known, you can use:
 *            (a) pixd = pixCopy(NULL, pixs);
 *            (c) pixCopy(pixd, pixs);
 *      (3) For case (c), we check if pixs and pixd are the same
 *          size (w,h,d).  If so, the data is copied directly.
 *          Otherwise, the data is reallocated to the correct size
 *          and the copy proceeds.  The refcount of pixd is unchanged.
 *      (4) This operation, like all others that may involve a pre-existing
 *          pixd, will side-effect any existing clones of pixd.
 */
PIX *
pixCopy(PIX  *pixd,   /* can be null */
        PIX  *pixs)
{
l_int32    bytes;
l_uint32  *datas, *datad;

    PROCNAME("pixCopy");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixs == pixd)
        return pixd;

        /* Total bytes in image data */
    bytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs);

        /* If we're making a new pix ... */
    if (!pixd) {
        if ((pixd = pixCreateTemplate(pixs)) == NULL)
            return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
        datas = pixGetData(pixs);
        datad = pixGetData(pixd);
        memcpy((char *)datad, (char *)datas, bytes);
        return pixd;
    }

        /* Reallocate image data if sizes are different */
    if (pixResizeImageData(pixd, pixs) == 1)
        return (PIX *)ERROR_PTR("reallocation of data failed", procName, NULL);

        /* Copy non-image data fields */
    pixCopyColormap(pixd, pixs);
    pixCopyResolution(pixd, pixs);
    pixCopyInputFormat(pixd, pixs);
    pixCopyText(pixd, pixs);

        /* Copy image data */
    datas = pixGetData(pixs);
    datad = pixGetData(pixd);
    memcpy((char*)datad, (char*)datas, bytes);
    return pixd;
}
Ejemplo n.º 17
0
/*!
 *  pixCreateTemplateNoInit()
 *
 *      Input:  pixs
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) Makes a Pix of the same size as the input Pix, with
 *          the data array allocated but not initialized to 0.
 *      (2) Copies the other fields, including colormap if it exists.
 */
PIX *
pixCreateTemplateNoInit(PIX  *pixs)
{
l_int32  w, h, d;
PIX     *pixd;

    PROCNAME("pixCreateTemplateNoInit");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);

    pixGetDimensions(pixs, &w, &h, &d);
    if ((pixd = pixCreateNoInit(w, h, d)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs);
    pixCopyColormap(pixd, pixs);
    pixCopyText(pixd, pixs);
    pixCopyInputFormat(pixd, pixs);

    return pixd;
}
/*!
 *  pixFinalAccumulateThreshold()
 *
 *      Input:  pixs (32 bpp)
 *              offset (same as used for initialization)
 *              threshold (values less than this are set in the destination)
 *      Return: pixd (1 bpp), or null on error
 *
 *  Notes:
 *      (1) The offset must be >= 0 and should not exceed 0x40000000.
 *      (2) The offset is subtracted from the src 32 bpp image
 */
PIX *
pixFinalAccumulateThreshold(PIX      *pixs,
                            l_uint32  offset,
                            l_uint32  threshold)
{
l_int32    i, j, w, h, wpls, wpld, val;
l_uint32  *datas, *datad, *lines, *lined;
PIX       *pixd;

    PROCNAME("pixFinalAccumulateThreshold");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 32)
        return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
    if (offset > 0x40000000)
        offset = 0x40000000;

    pixGetDimensions(pixs, &w, &h, NULL);
    if ((pixd = pixCreate(w, h, 1)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs);  /* but how did pixs get it initially? */
    datas = pixGetData(pixs);
    datad = pixGetData(pixd);
    wpls = pixGetWpl(pixs);
    wpld = pixGetWpl(pixd);
    for (i = 0; i < h; i++) {
        lines = datas + i * wpls;
        lined = datad + i * wpld;
        for (j = 0; j < w; j++) {
            val = lines[j] - offset;
            if (val >= threshold) {
                SET_DATA_BIT(lined, j);
            }
        }
    }

    return pixd;
}
Ejemplo n.º 19
0
/*!
 *  pixAbsDifference()
 *
 *      Input:  pixs1, pixs2  (both either 8 or 16 bpp gray, or 32 bpp RGB)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) The depth of pixs1 and pixs2 must be equal.
 *      (2) Clips computation to the min size, aligning the UL corners
 *      (3) For 8 and 16 bpp, assumes one gray component.
 *      (4) For 32 bpp, assumes 3 color components, and ignores the
 *          LSB of each word (the alpha channel)
 *      (5) Computes the absolute value of the difference between
 *          each component value.
 */
PIX *
pixAbsDifference(PIX  *pixs1,
                 PIX  *pixs2)
{
l_int32    w, h, w2, h2, d, wpls, wpld;
l_uint32  *datas1, *datas2, *datad;
PIX       *pixd;

    PROCNAME("pixAbsDifference");

    if (!pixs1)
        return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL);
    if (!pixs2)
        return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL);
    d = pixGetDepth(pixs1);
    if (d != pixGetDepth(pixs2))
        return (PIX *)ERROR_PTR("src1 and src2 depths unequal", procName, NULL);
    if (d != 8 && d != 16 && d != 32)
        return (PIX *)ERROR_PTR("depths not in {8, 16, 32}", procName, NULL);

    pixGetDimensions(pixs1, &w, &h, NULL);
    pixGetDimensions(pixs2, &w2, &h2, NULL);
    w = L_MIN(w, w2);
    h = L_MIN(h, h2);
    if ((pixd = pixCreate(w, h, d)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs1);
    datas1 = pixGetData(pixs1);
    datas2 = pixGetData(pixs2);
    datad = pixGetData(pixd);
    wpls = pixGetWpl(pixs1);
    wpld = pixGetWpl(pixd);

    absDifferenceLow(datad, w, h, wpld, datas1, datas2, d, wpls);

    return pixd;
}
Ejemplo n.º 20
0
/*!
 *  pixRotate90()
 *
 *      Input:  pixs (all depths)
 *              direction (1 = clockwise,  -1 = counter-clockwise)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) This does a 90 degree rotation of the image about the center,
 *          either cw or ccw, returning a new pix.
 *      (2) The direction must be either 1 (cw) or -1 (ccw).
 */
PIX *
pixRotate90(PIX     *pixs,
            l_int32  direction)
{
l_int32    wd, hd, d, wpls, wpld;
l_uint32  *datas, *datad;
PIX       *pixd;

    PROCNAME("pixRotate90");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    d = pixGetDepth(pixs);
    if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
        return (PIX *)ERROR_PTR("pixs not in {1,2,4,8,16,32} bpp",
                                procName, NULL);
    if (direction != 1 && direction != -1)
        return (PIX *)ERROR_PTR("invalid direction", procName, NULL);

    hd = pixGetWidth(pixs);
    wd = pixGetHeight(pixs);
    if ((pixd = pixCreate(wd, hd, d)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyColormap(pixd, pixs);
    pixCopyResolution(pixd, pixs);
    pixCopyInputFormat(pixd, pixs);

    datas = pixGetData(pixs);
    wpls = pixGetWpl(pixs);
    datad = pixGetData(pixd);
    wpld = pixGetWpl(pixd);

    rotate90Low(datad, wd, hd, d, wpld, datas, wpls, direction);

    return pixd;
}
Ejemplo n.º 21
0
/*!
 * \brief   pixOtsuAdaptiveThreshold()
 *
 * \param[in]    pixs 8 bpp
 * \param[in]    sx, sy desired tile dimensions; actual size may vary
 * \param[in]    smoothx, smoothy half-width of convolution kernel applied to
 *                                threshold array: use 0 for no smoothing
 * \param[in]    scorefract fraction of the max Otsu score; typ. 0.1;
 *                          use 0.0 for standard Otsu
 * \param[out]   ppixth [optional] array of threshold values
 *                      found for each tile
 * \param[out]   ppixd [optional] thresholded input pixs, based on
 *                     the threshold array
 * \return  0 if OK, 1 on error
 *
 * <pre>
 * Notes:
 *      (1) The Otsu method finds a single global threshold for an image.
 *          This function allows a locally adapted threshold to be
 *          found for each tile into which the image is broken up.
 *      (2) The array of threshold values, one for each tile, constitutes
 *          a highly downscaled image.  This array is optionally
 *          smoothed using a convolution.  The full width and height of the
 *          convolution kernel are (2 * %smoothx + 1) and (2 * %smoothy + 1).
 *      (3) The minimum tile dimension allowed is 16.  If such small
 *          tiles are used, it is recommended to use smoothing, because
 *          without smoothing, each small tile determines the splitting
 *          threshold independently.  A tile that is entirely in the
 *          image bg will then hallucinate fg, resulting in a very noisy
 *          binarization.  The smoothing should be large enough that no
 *          tile is only influenced by one type (fg or bg) of pixels,
 *          because it will force a split of its pixels.
 *      (4) To get a single global threshold for the entire image, use
 *          input values of %sx and %sy that are larger than the image.
 *          For this situation, the smoothing parameters are ignored.
 *      (5) The threshold values partition the image pixels into two classes:
 *          one whose values are less than the threshold and another
 *          whose values are greater than or equal to the threshold.
 *          This is the same use of 'threshold' as in pixThresholdToBinary().
 *      (6) The scorefract is the fraction of the maximum Otsu score, which
 *          is used to determine the range over which the histogram minimum
 *          is searched.  See numaSplitDistribution() for details on the
 *          underlying method of choosing a threshold.
 *      (7) This uses enables a modified version of the Otsu criterion for
 *          splitting the distribution of pixels in each tile into a
 *          fg and bg part.  The modification consists of searching for
 *          a minimum in the histogram over a range of pixel values where
 *          the Otsu score is within a defined fraction, %scorefract,
 *          of the max score.  To get the original Otsu algorithm, set
 *          %scorefract == 0.
 *      (8) N.B. This method is NOT recommended for images with weak text
 *          and significant background noise, such as bleedthrough, because
 *          of the problem noted in (3) above for tiling.  Use Sauvola.
 * </pre>
 */
l_int32
pixOtsuAdaptiveThreshold(PIX       *pixs,
                         l_int32    sx,
                         l_int32    sy,
                         l_int32    smoothx,
                         l_int32    smoothy,
                         l_float32  scorefract,
                         PIX      **ppixth,
                         PIX      **ppixd)
{
l_int32     w, h, nx, ny, i, j, thresh;
l_uint32    val;
PIX        *pixt, *pixb, *pixthresh, *pixth, *pixd;
PIXTILING  *pt;

    PROCNAME("pixOtsuAdaptiveThreshold");

    if (!ppixth && !ppixd)
        return ERROR_INT("neither &pixth nor &pixd defined", procName, 1);
    if (ppixth) *ppixth = NULL;
    if (ppixd) *ppixd = NULL;
    if (!pixs || pixGetDepth(pixs) != 8)
        return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
    if (sx < 16 || sy < 16)
        return ERROR_INT("sx and sy must be >= 16", procName, 1);

        /* Compute the threshold array for the tiles */
    pixGetDimensions(pixs, &w, &h, NULL);
    nx = L_MAX(1, w / sx);
    ny = L_MAX(1, h / sy);
    smoothx = L_MIN(smoothx, (nx - 1) / 2);
    smoothy = L_MIN(smoothy, (ny - 1) / 2);
    pt = pixTilingCreate(pixs, nx, ny, 0, 0, 0, 0);
    pixthresh = pixCreate(nx, ny, 8);
    for (i = 0; i < ny; i++) {
        for (j = 0; j < nx; j++) {
            pixt = pixTilingGetTile(pt, i, j);
            pixSplitDistributionFgBg(pixt, scorefract, 1, &thresh,
                                     NULL, NULL, NULL);
            pixSetPixel(pixthresh, j, i, thresh);  /* see note (4) */
            pixDestroy(&pixt);
        }
    }

        /* Optionally smooth the threshold array */
    if (smoothx > 0 || smoothy > 0)
        pixth = pixBlockconv(pixthresh, smoothx, smoothy);
    else
        pixth = pixClone(pixthresh);
    pixDestroy(&pixthresh);

        /* Optionally apply the threshold array to binarize pixs */
    if (ppixd) {
        pixd = pixCreate(w, h, 1);
        pixCopyResolution(pixd, pixs);
        for (i = 0; i < ny; i++) {
            for (j = 0; j < nx; j++) {
                pixt = pixTilingGetTile(pt, i, j);
                pixGetPixel(pixth, j, i, &val);
                pixb = pixThresholdToBinary(pixt, val);
                pixTilingPaintTile(pixd, i, j, pixb, pt);
                pixDestroy(&pixt);
                pixDestroy(&pixb);
            }
        }
        *ppixd = pixd;
    }

    if (ppixth)
        *ppixth = pixth;
    else
        pixDestroy(&pixth);

    pixTilingDestroy(&pt);
    return 0;
}
Ejemplo n.º 22
0
/*!
 * \brief   pixSauvolaBinarize()
 *
 * \param[in]    pixs 8 bpp grayscale; not colormapped
 * \param[in]    whsize window half-width for measuring local statistics
 * \param[in]    factor factor for reducing threshold due to variance; >= 0
 * \param[in]    addborder 1 to add border of width (%whsize + 1) on all sides
 * \param[out]   ppixm [optional] local mean values
 * \param[out]   ppixsd [optional] local standard deviation values
 * \param[out]   ppixth [optional] threshold values
 * \param[out]   ppixd [optional] thresholded image
 * \return  0 if OK, 1 on error
 *
 * <pre>
 * Notes:
 *      (1) The window width and height are 2 * %whsize + 1.  The minimum
 *          value for %whsize is 2; typically it is \>= 7..
 *      (2) The local statistics, measured over the window, are the
 *          average and standard deviation.
 *      (3) The measurements of the mean and standard deviation are
 *          performed inside a border of (%whsize + 1) pixels.  If pixs does
 *          not have these added border pixels, use %addborder = 1 to add
 *          it here; otherwise use %addborder = 0.
 *      (4) The Sauvola threshold is determined from the formula:
 *            t = m * (1 - k * (1 - s / 128))
 *          where:
 *            t = local threshold
 *            m = local mean
 *            k = %factor (\>= 0)   [ typ. 0.35 ]
 *            s = local standard deviation, which is maximized at
 *                127.5 when half the samples are 0 and half are 255.
 *      (5) The basic idea of Niblack and Sauvola binarization is that
 *          the local threshold should be less than the median value,
 *          and the larger the variance, the closer to the median
 *          it should be chosen.  Typical values for k are between
 *          0.2 and 0.5.
 * </pre>
 */
l_int32
pixSauvolaBinarize(PIX       *pixs,
                   l_int32    whsize,
                   l_float32  factor,
                   l_int32    addborder,
                   PIX      **ppixm,
                   PIX      **ppixsd,
                   PIX      **ppixth,
                   PIX      **ppixd)
{
l_int32  w, h;
PIX     *pixg, *pixsc, *pixm, *pixms, *pixth, *pixd;

    PROCNAME("pixSauvolaBinarize");

    if (ppixm) *ppixm = NULL;
    if (ppixsd) *ppixsd = NULL;
    if (ppixth) *ppixth = NULL;
    if (ppixd) *ppixd = NULL;
    if (!ppixm && !ppixsd && !ppixth && !ppixd)
        return ERROR_INT("no outputs", procName, 1);
    if (!pixs || pixGetDepth(pixs) != 8)
        return ERROR_INT("pixs undefined or not 8 bpp", procName, 1);
    if (pixGetColormap(pixs))
        return ERROR_INT("pixs is cmapped", procName, 1);
    pixGetDimensions(pixs, &w, &h, NULL);
    if (whsize < 2)
        return ERROR_INT("whsize must be >= 2", procName, 1);
    if (w < 2 * whsize + 3 || h < 2 * whsize + 3)
        return ERROR_INT("whsize too large for image", procName, 1);
    if (factor < 0.0)
        return ERROR_INT("factor must be >= 0", procName, 1);

    if (addborder) {
        pixg = pixAddMirroredBorder(pixs, whsize + 1, whsize + 1,
                                    whsize + 1, whsize + 1);
        pixsc = pixClone(pixs);
    } else {
        pixg = pixClone(pixs);
        pixsc = pixRemoveBorder(pixs, whsize + 1);
    }
    if (!pixg || !pixsc)
        return ERROR_INT("pixg and pixsc not made", procName, 1);

        /* All these functions strip off the border pixels. */
    if (ppixm || ppixth || ppixd)
        pixm = pixWindowedMean(pixg, whsize, whsize, 1, 1);
    if (ppixsd || ppixth || ppixd)
        pixms = pixWindowedMeanSquare(pixg, whsize, whsize, 1);
    if (ppixth || ppixd)
        pixth = pixSauvolaGetThreshold(pixm, pixms, factor, ppixsd);
    if (ppixd) {
        pixd = pixApplyLocalThreshold(pixsc, pixth, 1);
        pixCopyResolution(pixd, pixs);
    }

    if (ppixm)
        *ppixm = pixm;
    else
        pixDestroy(&pixm);
    pixDestroy(&pixms);
    if (ppixth)
        *ppixth = pixth;
    else
        pixDestroy(&pixth);
    if (ppixd)
        *ppixd = pixd;
    else
        pixDestroy(&pixd);
    pixDestroy(&pixg);
    pixDestroy(&pixsc);
    return 0;
}
Ejemplo n.º 23
0
/*!
 * \brief   pixThresholdByConnComp()
 *
 * \param[in]    pixs depth > 1, colormap OK
 * \param[in]    pixm [optional] 1 bpp mask giving region to ignore by setting
 *                    pixels to white; use NULL if no mask
 * \param[in]    start, end, incr binarization threshold levels to test
 * \param[in]    thresh48 threshold on normalized difference between the
 *                        numbers of 4 and 8 connected components
 * \param[in]    threshdiff threshold on normalized difference between the
 *                          number of 4 cc at successive iterations
 * \param[out]   pglobthresh [optional] best global threshold; 0
 *                           if no threshold is found
 * \param[out]   ppixd [optional] image thresholded to binary, or
 *                     null if no threshold is found
 * \param[in]    debugflag 1 for plotted results
 * \return  0 if OK, 1 on error or if no threshold is found
 *
 * <pre>
 * Notes:
 *      (1) This finds a global threshold based on connected components.
 *          Although slow, it is reasonable to use it in a situation where
 *          (a) the background in the image is relatively uniform, and
 *          (b) the result will be fed to an OCR program that accepts 1 bpp
 *              images and works best with easily segmented characters.
 *          The reason for (b) is that this selects a threshold with a
 *          minimum number of both broken characters and merged characters.
 *      (2) If the pix has color, it is converted to gray using the
 *          max component.
 *      (3) Input 0 to use default values for any of these inputs:
 *          %start, %end, %incr, %thresh48, %threshdiff.
 *      (4) This approach can be understood as follows.  When the
 *          binarization threshold is varied, the numbers of c.c. identify
 *          four regimes:
 *          (a) For low thresholds, text is broken into small pieces, and
 *              the number of c.c. is large, with the 4 c.c. significantly
 *              exceeding the 8 c.c.
 *          (b) As the threshold rises toward the optimum value, the text
 *              characters coalesce and there is very little difference
 *              between the numbers of 4 and 8 c.c, which both go
 *              through a minimum.
 *          (c) Above this, the image background gets noisy because some
 *              pixels are(thresholded to foreground, and the numbers
 *              of c.c. quickly increase, with the 4 c.c. significantly
 *              larger than the 8 c.c.
 *          (d) At even higher thresholds, the image background noise
 *              coalesces as it becomes mostly foreground, and the
 *              number of c.c. drops quickly.
 *      (5) If there is no global threshold that distinguishes foreground
 *          text from background (e.g., weak text over a background that
 *          has significant variation and/or bleedthrough), this returns 1,
 *          which the caller should check.
 * </pre>
 */
l_int32
pixThresholdByConnComp(PIX       *pixs,
                       PIX       *pixm,
                       l_int32    start,
                       l_int32    end,
                       l_int32    incr,
                       l_float32  thresh48,
                       l_float32  threshdiff,
                       l_int32   *pglobthresh,
                       PIX      **ppixd,
                       l_int32    debugflag)
{
l_int32    i, thresh, n, n4, n8, mincounts, found, globthresh;
l_float32  count4, count8, firstcount4, prevcount4, diff48, diff4;
GPLOT     *gplot;
NUMA      *na4, *na8;
PIX       *pix1, *pix2, *pix3;

    PROCNAME("pixThresholdByConnComp");

    if (pglobthresh) *pglobthresh = 0;
    if (ppixd) *ppixd = NULL;
    if (!pixs || pixGetDepth(pixs) == 1)
        return ERROR_INT("pixs undefined or 1 bpp", procName, 1);
    if (pixm && pixGetDepth(pixm) != 1)
        return ERROR_INT("pixm must be 1 bpp", procName, 1);

        /* Assign default values if requested */
    if (start <= 0) start = 80;
    if (end <= 0) end = 200;
    if (incr <= 0) incr = 10;
    if (thresh48 <= 0.0) thresh48 = 0.01;
    if (threshdiff <= 0.0) threshdiff = 0.01;
    if (start > end)
        return ERROR_INT("invalid start,end", procName, 1);

        /* Make 8 bpp, using the max component if color. */
    if (pixGetColormap(pixs))
        pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
    else
        pix1 = pixClone(pixs);
    if (pixGetDepth(pix1) == 32)
        pix2 = pixConvertRGBToGrayMinMax(pix1, L_CHOOSE_MAX);
    else
        pix2 = pixConvertTo8(pix1, 0);
    pixDestroy(&pix1);

        /* Mask out any non-text regions.  Do this in-place, because pix2
         * can never be the same pix as pixs. */
    if (pixm)
        pixSetMasked(pix2, pixm, 255);

        /* Make sure there are enough components to get a valid signal */
    pix3 = pixConvertTo1(pix2, start);
    pixCountConnComp(pix3, 4, &n4);
    pixDestroy(&pix3);
    mincounts = 500;
    if (n4 < mincounts) {
        L_INFO("Insufficient component count: %d\n", procName, n4);
        pixDestroy(&pix2);
        return 1;
    }

        /* Compute the c.c. data */
    na4 = numaCreate(0);
    na8 = numaCreate(0);
    numaSetParameters(na4, start, incr);
    numaSetParameters(na8, start, incr);
    for (thresh = start, i = 0; thresh <= end; thresh += incr, i++) {
        pix3 = pixConvertTo1(pix2, thresh);
        pixCountConnComp(pix3, 4, &n4);
        pixCountConnComp(pix3, 8, &n8);
        numaAddNumber(na4, n4);
        numaAddNumber(na8, n8);
        pixDestroy(&pix3);
    }
    if (debugflag) {
        gplot = gplotCreate("/tmp/threshroot", GPLOT_PNG,
                            "number of cc vs. threshold",
                            "threshold", "number of cc");
        gplotAddPlot(gplot, NULL, na4, GPLOT_LINES, "plot 4cc");
        gplotAddPlot(gplot, NULL, na8, GPLOT_LINES, "plot 8cc");
        gplotMakeOutput(gplot);
        gplotDestroy(&gplot);
    }

    n = numaGetCount(na4);
    found = FALSE;
    for (i = 0; i < n; i++) {
        if (i == 0) {
            numaGetFValue(na4, i, &firstcount4);
            prevcount4 = firstcount4;
        } else {
            numaGetFValue(na4, i, &count4);
            numaGetFValue(na8, i, &count8);
            diff48 = (count4 - count8) / firstcount4;
            diff4 = L_ABS(prevcount4 - count4) / firstcount4;
            if (debugflag) {
                fprintf(stderr, "diff48 = %7.3f, diff4 = %7.3f\n",
                        diff48, diff4);
            }
            if (diff48 < thresh48 && diff4 < threshdiff) {
                found = TRUE;
                break;
            }
            prevcount4 = count4;
        }
    }
    numaDestroy(&na4);
    numaDestroy(&na8);

    if (found) {
        globthresh = start + i * incr;
        if (pglobthresh) *pglobthresh = globthresh;
        if (ppixd) {
            *ppixd = pixConvertTo1(pix2, globthresh);
            pixCopyResolution(*ppixd, pixs);
        }
        if (debugflag) fprintf(stderr, "global threshold = %d\n", globthresh);
        pixDestroy(&pix2);
        return 0;
    }

    if (debugflag) fprintf(stderr, "no global threshold found\n");
    pixDestroy(&pix2);
    return 1;
}
Ejemplo n.º 24
0
Pix* binarizeAdaptive(Pix* pix) {
    PixAdaptiveBinarizer binarizer(false);
    Pix* pixb =  binarizer.bradleyAdaptiveThresholding(pix, 0.11, 30);
    pixCopyResolution(pixb, pix);
    return pixb;
}
Ejemplo n.º 25
0
/*!
 *  pixExpandBinaryPower2()
 *
 *      Input:  pixs (1 bpp)
 *              factor (expansion factor: 1, 2, 4, 8, 16)
 *      Return: pixd (expanded 1 bpp by replication), or null on error
 */
PIX *
pixExpandBinaryPower2(PIX     *pixs,
                      l_int32  factor)
{
l_uint8    sval;
l_uint16  *tab2;
l_int32    i, j, k, w, h, d, wd, hd, wpls, wpld, sdibits, sqbits, sbytes;
l_uint32  *datas, *datad, *lines, *lined, *tab4, *tab8;
PIX       *pixd;

    PROCNAME("pixExpandBinaryPower2");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    pixGetDimensions(pixs, &w, &h, &d);
    if (d != 1)
        return (PIX *)ERROR_PTR("pixs not binary", procName, NULL);
    if (factor == 1)
        return pixCopy(NULL, pixs);
    if (factor != 2 && factor != 4 && factor != 8 && factor != 16)
        return (PIX *)ERROR_PTR("factor must be in {2,4,8,16}", procName, NULL);

    wpls = pixGetWpl(pixs);
    datas = pixGetData(pixs);
    wd = factor * w;
    hd = factor * h;
    if ((pixd = pixCreate(wd, hd, 1)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs);
    pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor);
    wpld = pixGetWpl(pixd);
    datad = pixGetData(pixd);
    if (factor == 2) {
        if ((tab2 = makeExpandTab2x()) == NULL)
            return (PIX *)ERROR_PTR("tab2 not made", procName, NULL);
        sbytes = (w + 7) / 8;
        for (i = 0; i < h; i++) {
            lines = datas + i * wpls;
            lined = datad + 2 * i * wpld;
            for (j = 0; j < sbytes; j++) {
                sval = GET_DATA_BYTE(lines, j);
                SET_DATA_TWO_BYTES(lined, j, tab2[sval]);
            }
            memcpy((char *)(lined + wpld), (char *)lined, 4 * wpld);
        }
        FREE(tab2);
    } else if (factor == 4) {
        if ((tab4 = makeExpandTab4x()) == NULL)
            return (PIX *)ERROR_PTR("tab4 not made", procName, NULL);
        sbytes = (w + 7) / 8;
        for (i = 0; i < h; i++) {
            lines = datas + i * wpls;
            lined = datad + 4 * i * wpld;
            for (j = 0; j < sbytes; j++) {
                sval = GET_DATA_BYTE(lines, j);
                lined[j] = tab4[sval];
            }
            for (k = 1; k < 4; k++)
                memcpy((char *)(lined + k * wpld), (char *)lined, 4 * wpld);
        }
        FREE(tab4);
    } else if (factor == 8) {
        if ((tab8 = makeExpandTab8x()) == NULL)
            return (PIX *)ERROR_PTR("tab8 not made", procName, NULL);
        sqbits = (w + 3) / 4;
        for (i = 0; i < h; i++) {
            lines = datas + i * wpls;
            lined = datad + 8 * i * wpld;
            for (j = 0; j < sqbits; j++) {
                sval = GET_DATA_QBIT(lines, j);
                if (sval > 15)
                    L_WARNING("sval = %d; should be < 16\n", procName, sval);
                lined[j] = tab8[sval];
            }
            for (k = 1; k < 8; k++)
                memcpy((char *)(lined + k * wpld), (char *)lined, 4 * wpld);
        }
        FREE(tab8);
    } else {  /* factor == 16 */
        sdibits = (w + 1) / 2;
        for (i = 0; i < h; i++) {
            lines = datas + i * wpls;
            lined = datad + 16 * i * wpld;
            for (j = 0; j < sdibits; j++) {
                sval = GET_DATA_DIBIT(lines, j);
                lined[j] = expandtab16[sval];
            }
            for (k = 1; k < 16; k++)
                memcpy((char *)(lined + k * wpld), (char *)lined, 4 * wpld);
        }
    }

    return pixd;
}
Ejemplo n.º 26
0
Pix* savGol32(Pix* pix){
    FUNCNAME("savGol32");
    Pix* result =  pixSavGolFilter(pix, 3, 2, 2);
    pixCopyResolution(result, pix);
    return result;
}
/*!
 *  pixFinalAccumulate()
 *
 *      Input:  pixs (32 bpp)
 *              offset (same as used for initialization)
 *              depth  (8, 16 or 32 bpp, of destination)
 *      Return: pixd (8, 16 or 32 bpp), or null on error
 *
 *  Notes:
 *      (1) The offset must be >= 0 and should not exceed 0x40000000.
 *      (2) The offset is subtracted from the src 32 bpp image
 *      (3) For 8 bpp dest, the result is clipped to [0, 0xff]
 *      (4) For 16 bpp dest, the result is clipped to [0, 0xffff]
 */
PIX *
pixFinalAccumulate(PIX      *pixs,
                   l_uint32  offset,
                   l_int32   depth)
{
l_int32    i, j, w, h, wpls, wpld, val;
l_uint32  *datas, *datad, *lines, *lined;
PIX       *pixd;

    PROCNAME("pixFinalAccumulate");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 32)
        return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
    if (depth != 8 && depth != 16 && depth != 32)
        return (PIX *)ERROR_PTR("dest depth not 8, 16, 32 bpp", procName, NULL);
    if (offset > 0x40000000)
        offset = 0x40000000;

    pixGetDimensions(pixs, &w, &h, NULL);
    if ((pixd = pixCreate(w, h, depth)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs);  /* but how did pixs get it initially? */
    datas = pixGetData(pixs);
    datad = pixGetData(pixd);
    wpls = pixGetWpl(pixs);
    wpld = pixGetWpl(pixd);
    if (depth == 8) {
        for (i = 0; i < h; i++) {
            lines = datas + i * wpls;
            lined = datad + i * wpld;
            for (j = 0; j < w; j++) {
                val = lines[j] - offset;
                val = L_MAX(0, val);
                val = L_MIN(255, val);
                SET_DATA_BYTE(lined, j, (l_uint8)val);
            }
        }
    } else if (depth == 16) {
        for (i = 0; i < h; i++) {
            lines = datas + i * wpls;
            lined = datad + i * wpld;
            for (j = 0; j < w; j++) {
                val = lines[j] - offset;
                val = L_MAX(0, val);
                val = L_MIN(0xffff, val);
                SET_DATA_TWO_BYTES(lined, j, (l_uint16)val);
            }
        }
    } else {  /* depth == 32 */
        for (i = 0; i < h; i++) {
            lines = datas + i * wpls;
            lined = datad + i * wpld;
            for (j = 0; j < w; j++)
                lined[j] = lines[j] - offset;
        }
    }

    return pixd;
}
/*!
 *  pixAbsDifference()
 *
 *      Input:  pixs1, pixs2  (both either 8 or 16 bpp gray, or 32 bpp RGB)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) The depth of pixs1 and pixs2 must be equal.
 *      (2) Clips computation to the min size, aligning the UL corners
 *      (3) For 8 and 16 bpp, assumes one gray component.
 *      (4) For 32 bpp, assumes 3 color components, and ignores the
 *          LSB of each word (the alpha channel)
 *      (5) Computes the absolute value of the difference between
 *          each component value.
 */
PIX *
pixAbsDifference(PIX  *pixs1,
                 PIX  *pixs2)
{
l_int32    i, j, w, h, w2, h2, d, wpls1, wpls2, wpld, val1, val2, diff;
l_int32    rval1, gval1, bval1, rval2, gval2, bval2, rdiff, gdiff, bdiff;
l_uint32  *datas1, *datas2, *datad, *lines1, *lines2, *lined;
PIX       *pixd;

    PROCNAME("pixAbsDifference");

    if (!pixs1)
        return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL);
    if (!pixs2)
        return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL);
    d = pixGetDepth(pixs1);
    if (d != pixGetDepth(pixs2))
        return (PIX *)ERROR_PTR("src1 and src2 depths unequal", procName, NULL);
    if (d != 8 && d != 16 && d != 32)
        return (PIX *)ERROR_PTR("depths not in {8, 16, 32}", procName, NULL);

    pixGetDimensions(pixs1, &w, &h, NULL);
    pixGetDimensions(pixs2, &w2, &h2, NULL);
    w = L_MIN(w, w2);
    h = L_MIN(h, h2);
    if ((pixd = pixCreate(w, h, d)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs1);
    datas1 = pixGetData(pixs1);
    datas2 = pixGetData(pixs2);
    datad = pixGetData(pixd);
    wpls1 = pixGetWpl(pixs1);
    wpls2 = pixGetWpl(pixs2);
    wpld = pixGetWpl(pixd);
    if (d == 8) {
        for (i = 0; i < h; i++) {
            lines1 = datas1 + i * wpls1;
            lines2 = datas2 + i * wpls2;
            lined = datad + i * wpld;
            for (j = 0; j < w; j++) {
                val1 = GET_DATA_BYTE(lines1, j);
                val2 = GET_DATA_BYTE(lines2, j);
                diff = L_ABS(val1 - val2);
                SET_DATA_BYTE(lined, j, diff);
            }
        }
    } else if (d == 16) {
        for (i = 0; i < h; i++) {
            lines1 = datas1 + i * wpls1;
            lines2 = datas2 + i * wpls2;
            lined = datad + i * wpld;
            for (j = 0; j < w; j++) {
                val1 = GET_DATA_TWO_BYTES(lines1, j);
                val2 = GET_DATA_TWO_BYTES(lines2, j);
                diff = L_ABS(val1 - val2);
                SET_DATA_TWO_BYTES(lined, j, diff);
            }
        }
    } else {  /* d == 32 */
        for (i = 0; i < h; i++) {
            lines1 = datas1 + i * wpls1;
            lines2 = datas2 + i * wpls2;
            lined = datad + i * wpld;
            for (j = 0; j < w; j++) {
                extractRGBValues(lines1[j], &rval1, &gval1, &bval1);
                extractRGBValues(lines2[j], &rval2, &gval2, &bval2);
                rdiff = L_ABS(rval1 - rval2);
                gdiff = L_ABS(gval1 - gval2);
                bdiff = L_ABS(bval1 - bval2);
                composeRGBPixel(rdiff, gdiff, bdiff, lined + j);
            }
        }
    }

    return pixd;
}