Logo Search packages:      
Sourcecode: leptonlib version File versions  Download package

morph.c

/*====================================================================*
 -  Copyright (C) 2001 Leptonica.  All rights reserved.
 -  This software is distributed in the hope that it will be
 -  useful, but with NO WARRANTY OF ANY KIND.
 -  No author or distributor accepts responsibility to anyone for the
 -  consequences of using this software, or for whether it serves any
 -  particular purpose or works at all, unless he or she says so in
 -  writing.  Everyone is granted permission to copy, modify and
 -  redistribute this source code, for commercial or non-commercial
 -  purposes, with the following restrictions: (1) the origin of this
 -  source code must not be misrepresented; (2) modified versions must
 -  be plainly marked as such; and (3) this notice may not be removed
 -  or altered from any source or modified source distribution.
 *====================================================================*/


/*
 *  morph.c
 *
 *      Top-level binary morphological operations
 *
 *            PIX     *pixDilate()
 *            PIX     *pixErode()
 *            PIX     *pixHMT()
 *            PIX     *pixOpen()
 *            PIX     *pixClose()
 *            PIX     *pixCloseSafe()
 *            PIX     *pixOpenGeneralized()
 *            PIX     *pixCloseGeneralized()
 *
 *            void     resetMorphBoundaryCondition()
 *      
 *      Notes:
 *          - The constant MORPH_BC in morph.c is set by default to
 *            ASYMMETRIC_MORPH_BC for a non-symmetric convention for
 *            boundary pixels in dilation and erosion; namely,
 *            all pixels outside the image are assumed to be OFF
 *            for both dilation and erosion.   If you want to use
 *            a symmetric definition, see comments in pixErode()
 *            and reset MORPH_BC to SYMMETRIC_MORPH_BC.
 *          - the hit-miss transform (HMT) is:
 *            (erosion of the src by the hits) AND
 *            (erosion of the bit-inverted src by the misses)
 *          - boundary artifacts are possible in closing when
 *            the non-symmetric boundary conditions are used,
 *            because foreground pixels very close to the edge can
 *            be removed.  This can be avoided by using either
 *            the symmetric boundary conditions or the function
 *            pixCloseSafe(), which adds a border before the
 *            operation and removes it afterwards.
 *          - the 'generalized opening' is an HMT followed
 *            followed by a dilation (hits only).
 *          - the 'generalized closing' is a dilation (hits only)
 *            followed by an HMT.
 */

#include <stdio.h>
#include "allheaders.h"

    /* Global constant; initialized here; must be declared extern
     * in other files to access it directly.  However, in most
     * cases that is not necessary, because it can be reset
     * using resetMorphBoundaryCondition().  */
l_int32  MORPH_BC = ASYMMETRIC_MORPH_BC;


/*-----------------------------------------------------------------*
 *             Top-level binary morphological operations           *
 *-----------------------------------------------------------------*/
/*!
 *  pixDilate()
 *
 *      Input:  pixd  (<optional>)
 *              pixs
 *              sel
 *      Return: pixd
 *
 *  Note: dilates src using hits in Sel.
 */
PIX *
pixDilate(PIX  *pixd,
          PIX  *pixs,
          SEL  *sel)
{
l_int32  i, j, w, h, sx, sy, cx, cy, seldata;
PIX     *pixt;

    PROCNAME("pixDilate");

    if (!pixs)
      return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
    if (!sel)
      return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
    if (pixGetDepth(pixs) != 1)
      return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);

    if (sel->sx == 0 || sel->sy == 0)
      return (PIX *)ERROR_PTR("sel of size 0", procName, pixd);

    if (!pixd) {
      if ((pixd = pixCreateTemplate(pixs)) == NULL)
          return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
      pixt = pixClone(pixs);
    }
    else {
      if (!pixSizesEqual(pixs, pixd))
          return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);

      if (pixd == pixs) {
          if ((pixt = pixCopy(NULL, pixs)) == NULL)
            return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
      }
      else
          pixt = pixClone(pixs);
    }

    w = pixGetWidth(pixs);
    h = pixGetHeight(pixs);
    sx = sel->sx;
    sy = sel->sy;
    cx = sel->cx;
    cy = sel->cy;

    pixClearAll(pixd);
    for (i = 0; i < sy; i++) {
      for (j = 0; j < sx; j++) {
          seldata = sel->data[i][j];
          if (seldata == 1) {   /* src | dst */
            pixRasterop(pixd, j - cx, i - cy, w, h, PIX_SRC | PIX_DST,
                        pixt, 0, 0);
          }
      }
    }

    pixDestroy(&pixt);
    return pixd;
}


/*!
 *  pixErode()
 *
 *      Input:  pixd  (<optional>)
 *              pixs
 *              sel
 *      Return: pixd
 *
 *  Note: erodes src using hits in Sel.
 */
PIX *
pixErode(PIX  *pixd,
         PIX  *pixs,
         SEL  *sel)
{
l_int32  i, j, w, h, sx, sy, cx, cy, seldata;
l_int32  xp, yp, xn, yn;
PIX     *pixt;

    PROCNAME("pixErode");

    if (!pixs)
      return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
    if (!sel)
      return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
    if (pixGetDepth(pixs) != 1)
      return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);

    if (sel->sx == 0 || sel->sy == 0)
      return (PIX *)ERROR_PTR("sel of size 0", procName, pixd);

    if (!pixd) {
      if ((pixd = pixCreateTemplate(pixs)) == NULL)
          return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
      pixt = pixClone(pixs);
    }
    else {
      if (!pixSizesEqual(pixs, pixd))
          return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);

      if (pixd == pixs) {
          if ((pixt = pixCopy(NULL, pixs)) == NULL)
            return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
      }
      else
          pixt = pixClone(pixs);
    }

    w = pixGetWidth(pixs);
    h = pixGetHeight(pixs);
    sx = sel->sx;
    sy = sel->sy;
    cx = sel->cx;
    cy = sel->cy;

    pixSetAll(pixd);
    for (i = 0; i < sy; i++) {
      for (j = 0; j < sx; j++) {
          seldata = sel->data[i][j];
          if (seldata == 1) {   /* src & dst */
                pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST,
                          pixt, 0, 0);
          }
      }
    }

      /* Clear near edges.  We do this for the asymmetric boundary
       * condition convention that implements erosion assuming all
       * pixels surrounding the image are OFF.  If you use a
       * use a symmetric b.c. convention, where the erosion is
       * implemented assuming pixels surrounding the image
       * are ON, these operations are omitted.  */
    if (MORPH_BC == ASYMMETRIC_MORPH_BC) {
      selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
      if (xp > 0)
          pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0);
      if (xn > 0)
          pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0);
      if (yp > 0)
          pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0);
      if (yn > 0)
          pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0);
    }

    pixDestroy(&pixt);
    return pixd;
}


/*!
 *  pixHMT()
 *
 *      Input:  pixd  (<optional>)
 *              pixs
 *              sel
 *      Return: pixd
 *
 *  Note: The hit-miss transform erodes the src, using both hits
 *        and misses in the Sel.  It ANDs the shifted src for hits
 *        and ANDs the inverted shifted src for misses.
 */
PIX *
pixHMT(PIX  *pixd,
       PIX  *pixs,
       SEL  *sel)
{
l_int32  i, j, w, h, sx, sy, cx, cy, firstrasterop, seldata;
l_int32  xp, yp, xn, yn;
PIX     *pixt;

    PROCNAME("pixHMT");

    if (!pixs)
      return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
    if (!sel)
      return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
    if (pixGetDepth(pixs) != 1)
      return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);

    if (!pixd) {
      if ((pixd = pixCreateTemplate(pixs)) == NULL)
          return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
      pixt = pixClone(pixs);
    }
    else {
      if (!pixSizesEqual(pixs, pixd))
          return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);

      if (pixd == pixs) {
          if ((pixt = pixCopy(NULL, pixs)) == NULL)
            return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
      }
      else
          pixt = pixClone(pixs);
    }

    w = pixGetWidth(pixs);
    h = pixGetHeight(pixs);
    sx = sel->sx;
    sy = sel->sy;
    cx = sel->cx;
    cy = sel->cy;
    firstrasterop = TRUE;

    for (i = 0; i < sy; i++) {
      for (j = 0; j < sx; j++) {
          seldata = sel->data[i][j];
          if (seldata == 1) {  /* hit */
            if (firstrasterop == TRUE) {  /* src only */
                pixClearAll(pixd);
                pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC,
                            pixt, 0, 0);
                firstrasterop = FALSE;
            }
            else {   /* src & dst */
                pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST,
                          pixt, 0, 0);
            }
          }
          else if (seldata == 2) {  /* miss */
            if (firstrasterop == TRUE) {  /* ~src only */
                pixSetAll(pixd);
                pixRasterop(pixd, cx - j, cy - i, w, h, PIX_NOT(PIX_SRC),
                       pixt, 0, 0);
                firstrasterop = FALSE;
            }
            else  {  /* ~src & dst */
                pixRasterop(pixd, cx - j, cy - i, w, h,
                            PIX_NOT(PIX_SRC) & PIX_DST,
                        pixt, 0, 0);
            }
          }
      }
    }

      /* clear near edges */
    selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
    if (xp > 0)
      pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0);
    if (xn > 0)
      pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0);
    if (yp > 0)
      pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0);
    if (yn > 0)
      pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0);

    pixDestroy(&pixt);
    return pixd;
}


/*!
 *  pixOpen()
 *
 *      Input:  pixd  (<optional>)
 *              pixs
 *              sel
 *      Return: pixd
 *
 *  Note: standard morphological opening, using hits in the Sel.
 */
PIX *
pixOpen(PIX  *pixd,
        PIX  *pixs,
        SEL  *sel)
{
PIX  *pixt;

    PROCNAME("pixOpen");

    if (!pixs)
      return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
    if (!sel)
      return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
    if (pixGetDepth(pixs) != 1)
      return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);

    if (pixd) {
      if (!pixSizesEqual(pixs, pixd))
          return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
    }
    else {
      if ((pixd = pixCreateTemplate(pixs)) == NULL)
          return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    }

    if ((pixt = pixErode(NULL, pixs, sel)) == NULL)
      return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
    pixDilate(pixd, pixt, sel);
    pixDestroy(&pixt);

    return pixd;
}
    
    
/*!
 *  pixClose()
 *
 *      Input:  pixd  (<optional>)
 *              pixs
 *              sel
 *      Return: pixd
 *
 *  Notes:
 *      - Standard morphological closing, using hits in the Sel. 
 *      - This implementation is a strict dual of the opening if
 *        symmetric boundary conditions are used (see notes at top
 *        of this file).
 */
PIX *
pixClose(PIX  *pixd,
         PIX  *pixs,
         SEL  *sel)
{
PIX  *pixt;

    PROCNAME("pixClose");

    if (!pixs)
      return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
    if (!sel)
      return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
    if (pixGetDepth(pixs) != 1)
      return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);

    if (pixd) {
      if (!pixSizesEqual(pixs, pixd))
          return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
    }
    else {
      if ((pixd = pixCreateTemplate(pixs)) == NULL)
          return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
    }

    if ((pixt = pixDilate(NULL, pixs, sel)) == NULL)
      return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
    pixErode(pixd, pixt, sel);
    pixDestroy(&pixt);

    return pixd;
}
    
    
/*!
 *  pixCloseSafe()
 *
 *      Input:  pixd  (<optional>)
 *              pixs
 *              sel
 *      Return: pixd
 *
 *  Notes:
 *      - Standard morphological closing, using hits in the SEL.
 *      - If non-symmetric boundary conditions are used, this
 *        function adds a border of sufficient size to avoid
 *        losing pixels from the dilation, and it removes the
 *        border after the operation is finished.  It thus enforces
 *        a correct extensive result for closing.
 *      - If symmetric b.c. are used, it is not necessary to add
 *        and remove this boundary.
 */
PIX *
pixCloseSafe(PIX  *pixd,
             PIX  *pixs,
             SEL  *sel)
{
l_int32  xp, yp, xn, yn, xmax, xbord;
PIX     *pixt1, *pixt2;

    PROCNAME("pixCloseSafe");

    if (!pixs)
      return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
    if (!sel)
      return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
    if (pixGetDepth(pixs) != 1)
      return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);

    if (pixd) {
      if (!pixSizesEqual(pixs, pixd))
          L_WARNING("pix src and dest sizes unequal", procName);
    }

        /* symmetric b.c. handles correctly without added pixels */
    if (MORPH_BC == SYMMETRIC_MORPH_BC)
        return pixClose(pixd, pixs, sel);

    selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
    xmax = L_MAX(xp, xn);
    xbord = 32 * ((xmax + 31) / 32);  /* full 32 bit words */

    if ((pixt1 = pixAddBorderGeneral(pixs, xbord, xbord, yp, yn, 0)) == NULL)
      return (PIX *)ERROR_PTR("pixt1 not made", procName, pixd);
    pixClose(pixt1, pixt1, sel);
    if ((pixt2 = pixRemoveBorderGeneral(pixt1, xbord, xbord, yp, yn)) == NULL)
      return (PIX *)ERROR_PTR("pixt2 not made", procName, pixd);
    pixDestroy(&pixt1);

    if (!pixd)
      return pixt2;

    pixCopy(pixd, pixt2);
    pixDestroy(&pixt2);
    return pixd;
}
    
    
/*!
 *  pixOpenGeneralized()
 *
 *      Input:  pixd  (<optional>)
 *              pixs
 *              sel
 *      Return: pixd
 *
 *  Notes:
 *      - Generalized morphological opening, using both hits and
 *        misses in the Sel.
 *      - This does a hit-miss transform, followed by a dilation
 *        using the hits.
 */
PIX *
pixOpenGeneralized(PIX  *pixd,
                   PIX  *pixs,
                   SEL  *sel)
{
PIX  *pixt;

    PROCNAME("pixOpenGeneralized");

    if (!pixs)
      return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
    if (!sel)
      return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
    if (pixGetDepth(pixs) != 1)
      return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);

    if (pixd) {
      if (!pixSizesEqual(pixs, pixd))
          return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
    }
    else {
      if ((pixd = pixCreateTemplate(pixs)) == NULL)
          return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
    }

    if ((pixt = pixHMT(NULL, pixs, sel)) == NULL)
      return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
    pixDilate(pixd, pixt, sel);
    pixDestroy(&pixt);

    return pixd;
}
    
    
/*!
 *  pixCloseGeneralized()
 *
 *      Input:  pixd  (<optional>)
 *              pixs
 *              sel
 *      Return: pixd
 *
 *  Notes:
 *      - Generalized morphological closing, using both hits and
 *        misses in the Sel.
 *      - This does a dilation using the hits, followed by a
 *        hit-miss transform.
 *      - This operation is a dual of the generalized opening.
 */
PIX *
pixCloseGeneralized(PIX  *pixd,
                    PIX  *pixs,
                    SEL  *sel)
{
PIX  *pixt;

    PROCNAME("pixCloseGeneralized");

    if (!pixs)
      return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
    if (!sel)
      return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
    if (pixGetDepth(pixs) != 1)
      return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);

    if (pixd) {
      if (!pixSizesEqual(pixs, pixd))
          return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
    }
    else {
      if ((pixd = pixCreateTemplate(pixs)) == NULL)
          return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
    }

    if ((pixt = pixDilate(NULL, pixs, sel)) == NULL)
      return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
    pixHMT(pixd, pixt, sel);
    pixDestroy(&pixt);

    return pixd;
}


/*!
 *  resetMorphBoundaryCondition()
 *
 *      Input:  bc (SYMMETRIC_MORPH_BC, ASYMMETRIC_MORPH_BC)
 *      Return: void
 */
void
resetMorphBoundaryCondition(l_int32  bc)
{
    PROCNAME("resetMorphBoundaryCondition");

    if (bc != SYMMETRIC_MORPH_BC && bc != ASYMMETRIC_MORPH_BC) {
        L_WARNING("invalid bc; using asymmetric", procName);
        bc = ASYMMETRIC_MORPH_BC;
    }
    MORPH_BC = bc;
    return;
}



Generated by  Doxygen 1.6.0   Back to index