/*====================================================================* - 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 #include #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; }