blob: 9dd3994e883ae1d083d932b44222f16f093eb86b [file] [log] [blame]
/*====================================================================*
- 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.
*====================================================================*/
/*
* graymorph.c
*
* Top-level binary morphological operations
*
* PIX *pixErodeGray()
* PIX *pixDilateGray()
* PIX *pixOpenGray()
* PIX *pixCloseGray()
*
*
* Method: Algorithm by van Herk and Gil and Werman, 1992
*
* Measured speed is about 1 output pixel per 120 PIII clock cycles,
* for a horizontal or vertical erosion or dilation. The
* computation time doubles for opening or closing, or for a
* square SE, as expected, and is independent of the size of the SE.
*/
#include <stdio.h>
#include <stdlib.h>
#include "allheaders.h"
/*-----------------------------------------------------------------*
* Top-level gray morphological operations *
*-----------------------------------------------------------------*/
/*!
* pixErodeGray()
*
* Input: pixs
* hsize (of Sel; must be odd; origin implicitly in center)
* vsize (ditto)
* Return: pixd
*
* Notes:
* (1) Sel is a brick with all elements being hits
* (2) If hsize = vsize = 1, just returns a copy.
*/
PIX *
pixErodeGray(PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
l_uint8 *buffer, *minarray;
l_int32 w, h, wplb, wplt;
l_int32 leftpix, rightpix, toppix, bottompix, maxsize;
l_uint32 *datab, *datat;
PIX *pixb, *pixt, *pixd;
PROCNAME("pixErodeGray");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 8)
return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL);
if ((hsize & 1) == 0 ) {
L_WARNING("horiz sel size must be odd; increasing by 1", procName);
hsize++;
}
if ((vsize & 1) == 0 ) {
L_WARNING("vert sel size must be odd; increasing by 1", procName);
vsize++;
}
if (hsize == 1 && vsize == 1)
return pixCopy(NULL, pixs);
if (vsize == 1) { /* horizontal sel */
leftpix = (hsize + 1) / 2;
rightpix = (3 * hsize + 1) / 2;
toppix = 0;
bottompix = 0;
}
else if (hsize == 1) { /* vertical sel */
leftpix = 0;
rightpix = 0;
toppix = (vsize + 1) / 2;
bottompix = (3 * vsize + 1) / 2;
}
else {
leftpix = (hsize + 1) / 2;
rightpix = (3 * hsize + 1) / 2;
toppix = (vsize + 1) / 2;
bottompix = (3 * vsize + 1) / 2;
}
if ((pixb = pixAddBorderGeneral(pixs,
leftpix, rightpix, toppix, bottompix, 255)) == NULL)
return (PIX *)ERROR_PTR("pixb not made", procName, NULL);
if ((pixt = pixCreateTemplate(pixb)) == NULL)
return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
w = pixGetWidth(pixt);
h = pixGetHeight(pixt);
datab = pixGetData(pixb);
datat = pixGetData(pixt);
wplb = pixGetWpl(pixb);
wplt = pixGetWpl(pixt);
if ((buffer = (l_uint8 *)CALLOC(L_MAX(w, h), sizeof(l_uint8))) == NULL)
return (PIX *)ERROR_PTR("buffer not made", procName, NULL);
maxsize = L_MAX(hsize, vsize);
if ((minarray = (l_uint8 *)CALLOC(2 * maxsize, sizeof(l_uint8))) == NULL)
return (PIX *)ERROR_PTR("minarray not made", procName, NULL);
if (vsize == 1)
erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ,
buffer, minarray);
else if (hsize == 1)
erodeGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT,
buffer, minarray);
else {
erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ,
buffer, minarray);
pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix,
PIX_SET);
erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT,
buffer, minarray);
pixDestroy(&pixt);
pixt = pixClone(pixb);
}
if ((pixd = pixRemoveBorderGeneral(pixt,
leftpix, rightpix, toppix, bottompix)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
FREE(buffer);
FREE(minarray);
pixDestroy(&pixb);
pixDestroy(&pixt);
return pixd;
}
/*!
* pixDilateGray()
*
* Input: pixs
* hsize (of Sel; must be odd; origin implicitly in center)
* vsize (ditto)
* Return: pixd
*
* Notes:
* (1) Sel is a brick with all elements being hits
* (2) If hsize = vsize = 1, just returns a copy.
*/
PIX *
pixDilateGray(PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
l_uint8 *buffer, *maxarray;
l_int32 w, h, wplb, wplt;
l_int32 leftpix, rightpix, toppix, bottompix, maxsize;
l_uint32 *datab, *datat;
PIX *pixb, *pixt, *pixd;
PROCNAME("pixDilateGray");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 8)
return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL);
if ((hsize & 1) == 0 ) {
L_WARNING("horiz sel size must be odd; increasing by 1", procName);
hsize++;
}
if ((vsize & 1) == 0 ) {
L_WARNING("vert sel size must be odd; increasing by 1", procName);
vsize++;
}
if (hsize == 1 && vsize == 1)
return pixCopy(NULL, pixs);
if (vsize == 1) { /* horizontal sel */
leftpix = (hsize + 1) / 2;
rightpix = (3 * hsize + 1) / 2;
toppix = 0;
bottompix = 0;
}
else if (hsize == 1) { /* vertical sel */
leftpix = 0;
rightpix = 0;
toppix = (vsize + 1) / 2;
bottompix = (3 * vsize + 1) / 2;
}
else {
leftpix = (hsize + 1) / 2;
rightpix = (3 * hsize + 1) / 2;
toppix = (vsize + 1) / 2;
bottompix = (3 * vsize + 1) / 2;
}
if ((pixb = pixAddBorderGeneral(pixs,
leftpix, rightpix, toppix, bottompix, 0)) == NULL)
return (PIX *)ERROR_PTR("pixb not made", procName, NULL);
if ((pixt = pixCreateTemplate(pixb)) == NULL)
return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
w = pixGetWidth(pixt);
h = pixGetHeight(pixt);
datab = pixGetData(pixb);
datat = pixGetData(pixt);
wplb = pixGetWpl(pixb);
wplt = pixGetWpl(pixt);
if ((buffer = (l_uint8 *)CALLOC(L_MAX(w, h), sizeof(l_uint8))) == NULL)
return (PIX *)ERROR_PTR("buffer not made", procName, NULL);
maxsize = L_MAX(hsize, vsize);
if ((maxarray = (l_uint8 *)CALLOC(2 * maxsize, sizeof(l_uint8))) == NULL)
return (PIX *)ERROR_PTR("buffer not made", procName, NULL);
if (vsize == 1)
dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ,
buffer, maxarray);
else if (hsize == 1)
dilateGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT,
buffer, maxarray);
else {
dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ,
buffer, maxarray);
pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix,
PIX_CLR);
dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT,
buffer, maxarray);
pixDestroy(&pixt);
pixt = pixClone(pixb);
}
if ((pixd = pixRemoveBorderGeneral(pixt,
leftpix, rightpix, toppix, bottompix)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
FREE(buffer);
FREE(maxarray);
pixDestroy(&pixb);
pixDestroy(&pixt);
return pixd;
}
/*!
* pixOpenGray()
*
* Input: pixs
* hsize (of Sel; must be odd; origin implicitly in center)
* vsize (ditto)
* Return: pixd
*
* Notes:
* (1) Sel is a brick with all elements being hits
* (2) If hsize = vsize = 1, just returns a copy.
*/
PIX *
pixOpenGray(PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
l_uint8 *buffer;
l_uint8 *array; /* used to find either min or max in interval */
l_int32 w, h, wplb, wplt;
l_int32 leftpix, rightpix, toppix, bottompix, maxsize;
l_uint32 *datab, *datat;
PIX *pixb, *pixt, *pixd;
PROCNAME("pixOpenGray");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 8)
return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL);
if ((hsize & 1) == 0 ) {
L_WARNING("horiz sel size must be odd; increasing by 1", procName);
hsize++;
}
if ((vsize & 1) == 0 ) {
L_WARNING("vert sel size must be odd; increasing by 1", procName);
vsize++;
}
if (hsize == 1 && vsize == 1)
return pixCopy(NULL, pixs);
if (vsize == 1) { /* horizontal sel */
leftpix = (hsize + 1) / 2;
rightpix = (3 * hsize + 1) / 2;
toppix = 0;
bottompix = 0;
}
else if (hsize == 1) { /* vertical sel */
leftpix = 0;
rightpix = 0;
toppix = (vsize + 1) / 2;
bottompix = (3 * vsize + 1) / 2;
}
else {
leftpix = (hsize + 1) / 2;
rightpix = (3 * hsize + 1) / 2;
toppix = (vsize + 1) / 2;
bottompix = (3 * vsize + 1) / 2;
}
if ((pixb = pixAddBorderGeneral(pixs,
leftpix, rightpix, toppix, bottompix, 255)) == NULL)
return (PIX *)ERROR_PTR("pixb not made", procName, NULL);
if ((pixt = pixCreateTemplate(pixb)) == NULL)
return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
w = pixGetWidth(pixt);
h = pixGetHeight(pixt);
datab = pixGetData(pixb);
datat = pixGetData(pixt);
wplb = pixGetWpl(pixb);
wplt = pixGetWpl(pixt);
if ((buffer = (l_uint8 *)CALLOC(L_MAX(w, h), sizeof(l_uint8))) == NULL)
return (PIX *)ERROR_PTR("buffer not made", procName, NULL);
maxsize = L_MAX(hsize, vsize);
if ((array = (l_uint8 *)CALLOC(2 * maxsize, sizeof(l_uint8))) == NULL)
return (PIX *)ERROR_PTR("array not made", procName, NULL);
if (vsize == 1) {
erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ,
buffer, array);
pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix,
PIX_CLR);
dilateGrayLow(datab, w, h, wplb, datat, wplt, hsize, L_HORIZ,
buffer, array);
}
else if (hsize == 1) {
erodeGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT,
buffer, array);
pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix,
PIX_CLR);
dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT,
buffer, array);
}
else {
erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ,
buffer, array);
pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix,
PIX_SET);
erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT,
buffer, array);
pixSetOrClearBorder(pixb, leftpix, rightpix, toppix, bottompix,
PIX_CLR);
dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ,
buffer, array);
pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix,
PIX_CLR);
dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT,
buffer, array);
}
if ((pixd = pixRemoveBorderGeneral(pixb,
leftpix, rightpix, toppix, bottompix)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
FREE(buffer);
FREE(array);
pixDestroy(&pixb);
pixDestroy(&pixt);
return pixd;
}
/*!
* pixCloseGray()
*
* Input: pixs
* hsize (of Sel; must be odd; origin implicitly in center)
* vsize (ditto)
* Return: pixd
*
* Notes:
* (1) Sel is a brick with all elements being hits
* (2) If hsize = vsize = 1, just returns a copy.
*/
PIX *
pixCloseGray(PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
l_uint8 *buffer;
l_uint8 *array; /* used to find either min or max in interval */
l_int32 w, h, wplb, wplt;
l_int32 leftpix, rightpix, toppix, bottompix, maxsize;
l_uint32 *datab, *datat;
PIX *pixb, *pixt, *pixd;
PROCNAME("pixCloseGray");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 8)
return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL);
if ((hsize & 1) == 0 ) {
L_WARNING("horiz sel size must be odd; increasing by 1", procName);
hsize++;
}
if ((vsize & 1) == 0 ) {
L_WARNING("vert sel size must be odd; increasing by 1", procName);
vsize++;
}
if (hsize == 1 && vsize == 1)
return pixCopy(NULL, pixs);
if (vsize == 1) { /* horizontal sel */
leftpix = (hsize + 1) / 2;
rightpix = (3 * hsize + 1) / 2;
toppix = 0;
bottompix = 0;
}
else if (hsize == 1) { /* vertical sel */
leftpix = 0;
rightpix = 0;
toppix = (vsize + 1) / 2;
bottompix = (3 * vsize + 1) / 2;
}
else {
leftpix = (hsize + 1) / 2;
rightpix = (3 * hsize + 1) / 2;
toppix = (vsize + 1) / 2;
bottompix = (3 * vsize + 1) / 2;
}
if ((pixb = pixAddBorderGeneral(pixs,
leftpix, rightpix, toppix, bottompix, 0)) == NULL)
return (PIX *)ERROR_PTR("pixb not made", procName, NULL);
if ((pixt = pixCreateTemplate(pixb)) == NULL)
return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
w = pixGetWidth(pixt);
h = pixGetHeight(pixt);
datab = pixGetData(pixb);
datat = pixGetData(pixt);
wplb = pixGetWpl(pixb);
wplt = pixGetWpl(pixt);
if ((buffer = (l_uint8 *)CALLOC(L_MAX(w, h), sizeof(l_uint8))) == NULL)
return (PIX *)ERROR_PTR("buffer not made", procName, NULL);
maxsize = L_MAX(hsize, vsize);
if ((array = (l_uint8 *)CALLOC(2 * maxsize, sizeof(l_uint8))) == NULL)
return (PIX *)ERROR_PTR("array not made", procName, NULL);
if (vsize == 1) {
dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ,
buffer, array);
pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix,
PIX_SET);
erodeGrayLow(datab, w, h, wplb, datat, wplt, hsize, L_HORIZ,
buffer, array);
}
else if (hsize == 1) {
dilateGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT,
buffer, array);
pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix,
PIX_SET);
erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT,
buffer, array);
}
else {
dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ,
buffer, array);
pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix,
PIX_CLR);
dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT,
buffer, array);
pixSetOrClearBorder(pixb, leftpix, rightpix, toppix, bottompix,
PIX_SET);
erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ,
buffer, array);
pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix,
PIX_SET);
erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT,
buffer, array);
}
if ((pixd = pixRemoveBorderGeneral(pixb,
leftpix, rightpix, toppix, bottompix)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
FREE(buffer);
FREE(array);
pixDestroy(&pixb);
pixDestroy(&pixt);
return pixd;
}