Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/leptonica/src/graymorph.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/leptonica/src/graymorph.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,1352 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - Redistribution and use in source and binary forms, with or without + - modification, are permitted provided that the following conditions + - are met: + - 1. Redistributions of source code must retain the above copyright + - notice, this list of conditions and the following disclaimer. + - 2. Redistributions in binary form must reproduce the above + - copyright notice, this list of conditions and the following + - disclaimer in the documentation and/or other materials + - provided with the distribution. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY + - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *====================================================================*/ + + +/*! + * \file graymorph.c + * <pre> + * + * Top-level grayscale morphological operations (van Herk / Gil-Werman) + * PIX *pixErodeGray() + * PIX *pixDilateGray() + * PIX *pixOpenGray() + * PIX *pixCloseGray() + * + * Special operations for 1x3, 3x1 and 3x3 Sels (direct) + * PIX *pixErodeGray3() + * static PIX *pixErodeGray3h() + * static PIX *pixErodeGray3v() + * PIX *pixDilateGray3() + * static PIX *pixDilateGray3h() + * static PIX *pixDilateGray3v() + * PIX *pixOpenGray3() + * PIX *pixCloseGray3() + * + * Low-level grayscale morphological operations + * static void dilateGrayLow() + * static void erodeGrayLow() + * + * + * Method: Algorithm by van Herk and Gil and Werman, 1992 + * + * Measured speed of the vH/G-W implementation 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. + * + * A faster implementation can be made directly for brick Sels + * of maximum size 3. We unroll the computation for sets of 8 bytes. + * It needs to be called explicitly; the general functions do not + * default for the size 3 brick Sels. + * + * We use the van Herk/Gil-Werman (vHGW) algorithm, [van Herk, + * Patt. Recog. Let. 13, pp. 517-521, 1992; Gil and Werman, + * IEEE Trans PAMI 15(5), pp. 504-507, 1993.] + * This was the first grayscale morphology + * algorithm to compute dilation and erosion with + * complexity independent of the size of the structuring + * element. It is simple and elegant, and surprising that + * it was discovered as recently as 1992. It works for + * SEs composed of horizontal and/or vertical lines. The + * general case requires finding the Min or Max over an + * arbitrary set of pixels, and this requires a number of + * pixel comparisons equal to the SE "size" at each pixel + * in the image. The vHGW algorithm requires not + * more than 3 comparisons at each point. The algorithm has been + * recently refined by Gil and Kimmel ("Efficient Dilation + * Erosion, Opening and Closing Algorithms", in "Mathematical + * Morphology and its Applications to Image and Signal Processing", + * the proceedings of the International Symposium on Mathematical + * Morphology, Palo Alto, CA, June 2000, Kluwer Academic + * Publishers, pp. 301-310). They bring this number down below + * 1.5 comparisons per output pixel but at a cost of significantly + * increased complexity, so I don't bother with that here. + * + * In brief, the method is as follows. We evaluate the dilation + * in groups of "size" pixels, equal to the size of the SE. + * For horizontal, we start at x = "size"/2 and go + * (w - 2 * ("size"/2))/"size" steps. This means that + * we don't evaluate the first 0.5 * "size" pixels and, worst + * case, the last 1.5 * "size" pixels. Thus we embed the + * image in a larger image with these augmented dimensions, where + * the new border pixels are appropriately initialized (0 for + * dilation; 255 for erosion), and remove the boundary at the end. + * (For vertical, use h instead of w.) Then for each group + * of "size" pixels, we form an array of length 2 * "size" + 1, + * consisting of backward and forward partial maxima (for + * dilation) or minima (for erosion). This represents a + * jumping window computed from the source image, over which + * the SE will slide. The center of the array gets the source + * pixel at the center of the SE. Call this the center pixel + * of the window. Array values to left of center get + * the maxima(minima) of the pixels from the center + * one and going to the left an equal distance. Array + * values to the right of center get the maxima(minima) to + * the pixels from the center one and going to the right + * an equal distance. These are computed sequentially starting + * from the center one. The SE (of length "size") can slide over this + * window (of length 2 * "size + 1) at "size" different places. + * At each place, the maxima(minima) of the values in the window + * that correspond to the end points of the SE give the extremal + * values over that interval, and these are stored at the dest + * pixel corresponding to the SE center. A picture is worth + * at least this many words, so if this isn't clear, see the + * leptonica documentation on grayscale morphology. + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + + /* Special static operations for 3x1, 1x3 and 3x3 structuring elements */ +static PIX *pixErodeGray3h(PIX *pixs); +static PIX *pixErodeGray3v(PIX *pixs); +static PIX *pixDilateGray3h(PIX *pixs); +static PIX *pixDilateGray3v(PIX *pixs); + + /* Low-level gray morphological operations */ +static void dilateGrayLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_int32 size, l_int32 direction, l_uint8 *buffer, + l_uint8 *maxarray); +static void erodeGrayLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_int32 size, l_int32 direction, l_uint8 *buffer, + l_uint8 *minarray); + +/*-----------------------------------------------------------------* + * Top-level grayscale morphological operations * + *-----------------------------------------------------------------*/ +/*! + * \brief pixErodeGray() + * + * \param[in] pixs + * \param[in] hsize of Sel; must be odd; origin implicitly in center + * \param[in] vsize ditto + * \return pixd + * + * <pre> + * Notes: + * (1) Sel is a brick with all elements being hits + * (2) If hsize = vsize = 1, just returns a copy. + * </pre> + */ +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; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize or vsize < 1", __func__, NULL); + if ((hsize & 1) == 0 ) { + L_WARNING("horiz sel size must be odd; increasing by 1\n", __func__); + hsize++; + } + if ((vsize & 1) == 0 ) { + L_WARNING("vert sel size must be odd; increasing by 1\n", __func__); + vsize++; + } + + pixb = pixt = pixd = NULL; + buffer = minarray = NULL; + + 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; + } + + pixb = pixAddBorderGeneral(pixs, leftpix, rightpix, toppix, bottompix, 255); + pixt = pixCreateTemplate(pixb); + if (!pixb || !pixt) { + L_ERROR("pixb and pixt not made\n", __func__); + goto cleanup; + } + + pixGetDimensions(pixt, &w, &h, NULL); + datab = pixGetData(pixb); + datat = pixGetData(pixt); + wplb = pixGetWpl(pixb); + wplt = pixGetWpl(pixt); + + buffer = (l_uint8 *)LEPT_CALLOC(L_MAX(w, h), sizeof(l_uint8)); + maxsize = L_MAX(hsize, vsize); + minarray = (l_uint8 *)LEPT_CALLOC(2 * maxsize, sizeof(l_uint8)); + if (!buffer || !minarray) { + L_ERROR("buffer and minarray not made\n", __func__); + goto cleanup; + } + + 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); + } + + pixd = pixRemoveBorderGeneral(pixt, leftpix, rightpix, toppix, bottompix); + if (!pixd) + L_ERROR("pixd not made\n", __func__); + +cleanup: + LEPT_FREE(buffer); + LEPT_FREE(minarray); + pixDestroy(&pixb); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixDilateGray() + * + * \param[in] pixs + * \param[in] hsize of Sel; must be odd; origin implicitly in center + * \param[in] vsize ditto + * \return pixd + * + * <pre> + * Notes: + * (1) Sel is a brick with all elements being hits + * (2) If hsize = vsize = 1, just returns a copy. + * </pre> + */ +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; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize or vsize < 1", __func__, NULL); + if ((hsize & 1) == 0 ) { + L_WARNING("horiz sel size must be odd; increasing by 1\n", __func__); + hsize++; + } + if ((vsize & 1) == 0 ) { + L_WARNING("vert sel size must be odd; increasing by 1\n", __func__); + vsize++; + } + + pixb = pixt = pixd = NULL; + buffer = maxarray = NULL; + + 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; + } + + pixb = pixAddBorderGeneral(pixs, leftpix, rightpix, toppix, bottompix, 0); + pixt = pixCreateTemplate(pixb); + if (!pixb || !pixt) { + L_ERROR("pixb and pixt not made\n", __func__); + goto cleanup; + } + + pixGetDimensions(pixt, &w, &h, NULL); + datab = pixGetData(pixb); + datat = pixGetData(pixt); + wplb = pixGetWpl(pixb); + wplt = pixGetWpl(pixt); + + buffer = (l_uint8 *)LEPT_CALLOC(L_MAX(w, h), sizeof(l_uint8)); + maxsize = L_MAX(hsize, vsize); + maxarray = (l_uint8 *)LEPT_CALLOC(2 * maxsize, sizeof(l_uint8)); + if (!buffer || !maxarray) { + L_ERROR("buffer and maxarray not made\n", __func__); + goto cleanup; + } + + 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); + } + + pixd = pixRemoveBorderGeneral(pixt, leftpix, rightpix, toppix, bottompix); + if (!pixd) + L_ERROR("pixd not made\n", __func__); + +cleanup: + LEPT_FREE(buffer); + LEPT_FREE(maxarray); + pixDestroy(&pixb); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixOpenGray() + * + * \param[in] pixs + * \param[in] hsize of Sel; must be odd; origin implicitly in center + * \param[in] vsize ditto + * \return pixd + * + * <pre> + * Notes: + * (1) Sel is a brick with all elements being hits + * (2) If hsize = vsize = 1, just returns a copy. + * </pre> + */ +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; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize or vsize < 1", __func__, NULL); + if ((hsize & 1) == 0 ) { + L_WARNING("horiz sel size must be odd; increasing by 1\n", __func__); + hsize++; + } + if ((vsize & 1) == 0 ) { + L_WARNING("vert sel size must be odd; increasing by 1\n", __func__); + vsize++; + } + + pixb = pixt = pixd = NULL; + buffer = array = NULL; + + 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; + } + + pixb = pixAddBorderGeneral(pixs, leftpix, rightpix, toppix, bottompix, 255); + pixt = pixCreateTemplate(pixb); + if (!pixb || !pixt) { + L_ERROR("pixb and pixt not made\n", __func__); + goto cleanup; + } + + pixGetDimensions(pixt, &w, &h, NULL); + datab = pixGetData(pixb); + datat = pixGetData(pixt); + wplb = pixGetWpl(pixb); + wplt = pixGetWpl(pixt); + + buffer = (l_uint8 *)LEPT_CALLOC(L_MAX(w, h), sizeof(l_uint8)); + maxsize = L_MAX(hsize, vsize); + array = (l_uint8 *)LEPT_CALLOC(2 * maxsize, sizeof(l_uint8)); + if (!buffer || !array) { + L_ERROR("buffer and array not made\n", __func__); + goto cleanup; + } + + 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); + } + + pixd = pixRemoveBorderGeneral(pixb, leftpix, rightpix, toppix, bottompix); + if (!pixd) + L_ERROR("pixd not made\n", __func__); + +cleanup: + LEPT_FREE(buffer); + LEPT_FREE(array); + pixDestroy(&pixb); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixCloseGray() + * + * \param[in] pixs + * \param[in] hsize of Sel; must be odd; origin implicitly in center + * \param[in] vsize ditto + * \return pixd + * + * <pre> + * Notes: + * (1) Sel is a brick with all elements being hits + * (2) If hsize = vsize = 1, just returns a copy. + * </pre> + */ +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; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize or vsize < 1", __func__, NULL); + if ((hsize & 1) == 0 ) { + L_WARNING("horiz sel size must be odd; increasing by 1\n", __func__); + hsize++; + } + if ((vsize & 1) == 0 ) { + L_WARNING("vert sel size must be odd; increasing by 1\n", __func__); + vsize++; + } + + pixb = pixt = pixd = NULL; + buffer = array = NULL; + + 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; + } + + pixb = pixAddBorderGeneral(pixs, leftpix, rightpix, toppix, bottompix, 0); + pixt = pixCreateTemplate(pixb); + if (!pixb || !pixt) { + L_ERROR("pixb and pixt not made\n", __func__); + goto cleanup; + } + + pixGetDimensions(pixt, &w, &h, NULL); + datab = pixGetData(pixb); + datat = pixGetData(pixt); + wplb = pixGetWpl(pixb); + wplt = pixGetWpl(pixt); + + buffer = (l_uint8 *)LEPT_CALLOC(L_MAX(w, h), sizeof(l_uint8)); + maxsize = L_MAX(hsize, vsize); + array = (l_uint8 *)LEPT_CALLOC(2 * maxsize, sizeof(l_uint8)); + if (!buffer || !array) { + L_ERROR("buffer and array not made\n", __func__); + goto cleanup; + } + + 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); + } + + pixd = pixRemoveBorderGeneral(pixb, leftpix, rightpix, toppix, bottompix); + if (!pixd) + L_ERROR("pixd not made\n", __func__); + +cleanup: + LEPT_FREE(buffer); + LEPT_FREE(array); + pixDestroy(&pixb); + pixDestroy(&pixt); + return pixd; +} + + +/*-----------------------------------------------------------------* + * Special operations for 1x3, 3x1 and 3x3 Sels * + *-----------------------------------------------------------------*/ +/*! + * \brief pixErodeGray3() + * + * \param[in] pixs 8 bpp, not cmapped + * \param[in] hsize 1 or 3 + * \param[in] vsize 1 or 3 + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits) + * (2) If hsize = vsize = 1, just returns a copy. + * (3) It would be nice not to add a border, but it is required + * if we want the same results as from the general case. + * We add 4 bytes on the left to speed up the copying, and + * 8 bytes at the right and bottom to allow unrolling of + * the computation of 8 pixels. + * </pre> + */ +PIX * +pixErodeGray3(PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt, *pixb, *pixbd, *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pix has colormap", __func__, NULL); + if ((hsize != 1 && hsize != 3) || + (vsize != 1 && vsize != 3)) + return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", __func__, NULL); + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 255); + + if (vsize == 1) + pixbd = pixErodeGray3h(pixb); + else if (hsize == 1) + pixbd = pixErodeGray3v(pixb); + else { /* vize == hsize == 3 */ + pixt = pixErodeGray3h(pixb); + pixbd = pixErodeGray3v(pixt); + pixDestroy(&pixt); + } + + pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); + pixDestroy(&pixb); + pixDestroy(&pixbd); + return pixd; +} + + +/*! + * \brief pixErodeGray3h() + * + * \param[in] pixs 8 bpp, not cmapped + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) Special case for horizontal 3x1 brick Sel; + * also used as the first step for the 3x3 brick Sel. + * </pre> + */ +static PIX * +pixErodeGray3h(PIX *pixs) +{ +l_uint32 *datas, *datad, *lines, *lined; +l_int32 w, h, wpl, i, j; +l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, minval; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); + + pixd = pixCreateTemplate(pixs); + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + lines = datas + i * wpl; + lined = datad + i * wpl; + for (j = 1; j < w - 8; j += 8) { + val0 = GET_DATA_BYTE(lines, j - 1); + val1 = GET_DATA_BYTE(lines, j); + val2 = GET_DATA_BYTE(lines, j + 1); + val3 = GET_DATA_BYTE(lines, j + 2); + val4 = GET_DATA_BYTE(lines, j + 3); + val5 = GET_DATA_BYTE(lines, j + 4); + val6 = GET_DATA_BYTE(lines, j + 5); + val7 = GET_DATA_BYTE(lines, j + 6); + val8 = GET_DATA_BYTE(lines, j + 7); + val9 = GET_DATA_BYTE(lines, j + 8); + minval = L_MIN(val1, val2); + SET_DATA_BYTE(lined, j, L_MIN(val0, minval)); + SET_DATA_BYTE(lined, j + 1, L_MIN(minval, val3)); + minval = L_MIN(val3, val4); + SET_DATA_BYTE(lined, j + 2, L_MIN(val2, minval)); + SET_DATA_BYTE(lined, j + 3, L_MIN(minval, val5)); + minval = L_MIN(val5, val6); + SET_DATA_BYTE(lined, j + 4, L_MIN(val4, minval)); + SET_DATA_BYTE(lined, j + 5, L_MIN(minval, val7)); + minval = L_MIN(val7, val8); + SET_DATA_BYTE(lined, j + 6, L_MIN(val6, minval)); + SET_DATA_BYTE(lined, j + 7, L_MIN(minval, val9)); + } + } + return pixd; +} + + +/*! + * \brief pixErodeGray3v() + * + * \param[in] pixs 8 bpp, not cmapped + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) Special case for vertical 1x3 brick Sel; + * also used as the second step for the 3x3 brick Sel. + * (2) Surprisingly, this is faster than setting up the + * lineptrs array and accessing into it; e.g., + * val4 = GET_DATA_BYTE(lines8[i + 3], j); + * </pre> + */ +static PIX * +pixErodeGray3v(PIX *pixs) +{ +l_uint32 *datas, *datad, *linesi, *linedi; +l_int32 w, h, wpl, i, j; +l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, minval; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); + + pixd = pixCreateTemplate(pixs); + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpl = pixGetWpl(pixs); + for (j = 0; j < w; j++) { + for (i = 1; i < h - 8; i += 8) { + linesi = datas + i * wpl; + linedi = datad + i * wpl; + val0 = GET_DATA_BYTE(linesi - wpl, j); + val1 = GET_DATA_BYTE(linesi, j); + val2 = GET_DATA_BYTE(linesi + wpl, j); + val3 = GET_DATA_BYTE(linesi + 2 * wpl, j); + val4 = GET_DATA_BYTE(linesi + 3 * wpl, j); + val5 = GET_DATA_BYTE(linesi + 4 * wpl, j); + val6 = GET_DATA_BYTE(linesi + 5 * wpl, j); + val7 = GET_DATA_BYTE(linesi + 6 * wpl, j); + val8 = GET_DATA_BYTE(linesi + 7 * wpl, j); + val9 = GET_DATA_BYTE(linesi + 8 * wpl, j); + minval = L_MIN(val1, val2); + SET_DATA_BYTE(linedi, j, L_MIN(val0, minval)); + SET_DATA_BYTE(linedi + wpl, j, L_MIN(minval, val3)); + minval = L_MIN(val3, val4); + SET_DATA_BYTE(linedi + 2 * wpl, j, L_MIN(val2, minval)); + SET_DATA_BYTE(linedi + 3 * wpl, j, L_MIN(minval, val5)); + minval = L_MIN(val5, val6); + SET_DATA_BYTE(linedi + 4 * wpl, j, L_MIN(val4, minval)); + SET_DATA_BYTE(linedi + 5 * wpl, j, L_MIN(minval, val7)); + minval = L_MIN(val7, val8); + SET_DATA_BYTE(linedi + 6 * wpl, j, L_MIN(val6, minval)); + SET_DATA_BYTE(linedi + 7 * wpl, j, L_MIN(minval, val9)); + } + } + return pixd; +} + + +/*! + * \brief pixDilateGray3() + * + * \param[in] pixs 8 bpp, not cmapped + * \param[in] hsize 1 or 3 + * \param[in] vsize 1 or 3 + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits) + * (2) If hsize = vsize = 1, just returns a copy. + * </pre> + */ +PIX * +pixDilateGray3(PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt, *pixb, *pixbd, *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pix has colormap", __func__, NULL); + if ((hsize != 1 && hsize != 3) || + (vsize != 1 && vsize != 3)) + return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", __func__, NULL); + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 0); + + if (vsize == 1) + pixbd = pixDilateGray3h(pixb); + else if (hsize == 1) + pixbd = pixDilateGray3v(pixb); + else { /* vize == hsize == 3 */ + pixt = pixDilateGray3h(pixb); + pixbd = pixDilateGray3v(pixt); + pixDestroy(&pixt); + } + + pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); + pixDestroy(&pixb); + pixDestroy(&pixbd); + return pixd; +} + + +/*! + * \brief pixDilateGray3h() + * + * \param[in] pixs 8 bpp, not cmapped + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) Special case for horizontal 3x1 brick Sel; + * also used as the first step for the 3x3 brick Sel. + * </pre> + */ +static PIX * +pixDilateGray3h(PIX *pixs) +{ +l_uint32 *datas, *datad, *lines, *lined; +l_int32 w, h, wpl, i, j; +l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, maxval; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); + + pixd = pixCreateTemplate(pixs); + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + lines = datas + i * wpl; + lined = datad + i * wpl; + for (j = 1; j < w - 8; j += 8) { + val0 = GET_DATA_BYTE(lines, j - 1); + val1 = GET_DATA_BYTE(lines, j); + val2 = GET_DATA_BYTE(lines, j + 1); + val3 = GET_DATA_BYTE(lines, j + 2); + val4 = GET_DATA_BYTE(lines, j + 3); + val5 = GET_DATA_BYTE(lines, j + 4); + val6 = GET_DATA_BYTE(lines, j + 5); + val7 = GET_DATA_BYTE(lines, j + 6); + val8 = GET_DATA_BYTE(lines, j + 7); + val9 = GET_DATA_BYTE(lines, j + 8); + maxval = L_MAX(val1, val2); + SET_DATA_BYTE(lined, j, L_MAX(val0, maxval)); + SET_DATA_BYTE(lined, j + 1, L_MAX(maxval, val3)); + maxval = L_MAX(val3, val4); + SET_DATA_BYTE(lined, j + 2, L_MAX(val2, maxval)); + SET_DATA_BYTE(lined, j + 3, L_MAX(maxval, val5)); + maxval = L_MAX(val5, val6); + SET_DATA_BYTE(lined, j + 4, L_MAX(val4, maxval)); + SET_DATA_BYTE(lined, j + 5, L_MAX(maxval, val7)); + maxval = L_MAX(val7, val8); + SET_DATA_BYTE(lined, j + 6, L_MAX(val6, maxval)); + SET_DATA_BYTE(lined, j + 7, L_MAX(maxval, val9)); + } + } + return pixd; +} + + +/*! + * \brief pixDilateGray3v() + * + * \param[in] pixs 8 bpp, not cmapped + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) Special case for vertical 1x3 brick Sel; + * also used as the second step for the 3x3 brick Sel. + * </pre> + */ +static PIX * +pixDilateGray3v(PIX *pixs) +{ +l_uint32 *datas, *datad, *linesi, *linedi; +l_int32 w, h, wpl, i, j; +l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, maxval; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); + + pixd = pixCreateTemplate(pixs); + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpl = pixGetWpl(pixs); + for (j = 0; j < w; j++) { + for (i = 1; i < h - 8; i += 8) { + linesi = datas + i * wpl; + linedi = datad + i * wpl; + val0 = GET_DATA_BYTE(linesi - wpl, j); + val1 = GET_DATA_BYTE(linesi, j); + val2 = GET_DATA_BYTE(linesi + wpl, j); + val3 = GET_DATA_BYTE(linesi + 2 * wpl, j); + val4 = GET_DATA_BYTE(linesi + 3 * wpl, j); + val5 = GET_DATA_BYTE(linesi + 4 * wpl, j); + val6 = GET_DATA_BYTE(linesi + 5 * wpl, j); + val7 = GET_DATA_BYTE(linesi + 6 * wpl, j); + val8 = GET_DATA_BYTE(linesi + 7 * wpl, j); + val9 = GET_DATA_BYTE(linesi + 8 * wpl, j); + maxval = L_MAX(val1, val2); + SET_DATA_BYTE(linedi, j, L_MAX(val0, maxval)); + SET_DATA_BYTE(linedi + wpl, j, L_MAX(maxval, val3)); + maxval = L_MAX(val3, val4); + SET_DATA_BYTE(linedi + 2 * wpl, j, L_MAX(val2, maxval)); + SET_DATA_BYTE(linedi + 3 * wpl, j, L_MAX(maxval, val5)); + maxval = L_MAX(val5, val6); + SET_DATA_BYTE(linedi + 4 * wpl, j, L_MAX(val4, maxval)); + SET_DATA_BYTE(linedi + 5 * wpl, j, L_MAX(maxval, val7)); + maxval = L_MAX(val7, val8); + SET_DATA_BYTE(linedi + 6 * wpl, j, L_MAX(val6, maxval)); + SET_DATA_BYTE(linedi + 7 * wpl, j, L_MAX(maxval, val9)); + } + } + return pixd; +} + + +/*! + * \brief pixOpenGray3() + * + * \param[in] pixs 8 bpp, not cmapped + * \param[in] hsize 1 or 3 + * \param[in] vsize 1 or 3 + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits) + * (2) If hsize = vsize = 1, just returns a copy. + * (3) It would be nice not to add a border, but it is required + * to get the same results as for the general case. + * </pre> + */ +PIX * +pixOpenGray3(PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt, *pixb, *pixbd, *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pix has colormap", __func__, NULL); + if ((hsize != 1 && hsize != 3) || + (vsize != 1 && vsize != 3)) + return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", __func__, NULL); + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 255); /* set to max */ + + if (vsize == 1) { + pixt = pixErodeGray3h(pixb); + pixSetBorderVal(pixt, 4, 8, 2, 8, 0); /* set to min */ + pixbd = pixDilateGray3h(pixt); + pixDestroy(&pixt); + } else if (hsize == 1) { + pixt = pixErodeGray3v(pixb); + pixSetBorderVal(pixt, 4, 8, 2, 8, 0); + pixbd = pixDilateGray3v(pixt); + pixDestroy(&pixt); + } else { /* vize == hsize == 3 */ + pixt = pixErodeGray3h(pixb); + pixbd = pixErodeGray3v(pixt); + pixDestroy(&pixt); + pixSetBorderVal(pixbd, 4, 8, 2, 8, 0); + pixt = pixDilateGray3h(pixbd); + pixDestroy(&pixbd); + pixbd = pixDilateGray3v(pixt); + pixDestroy(&pixt); + } + + pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); + pixDestroy(&pixb); + pixDestroy(&pixbd); + return pixd; +} + + +/*! + * \brief pixCloseGray3() + * + * \param[in] pixs 8 bpp, not cmapped + * \param[in] hsize 1 or 3 + * \param[in] vsize 1 or 3 + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits) + * (2) If hsize = vsize = 1, just returns a copy. + * </pre> + */ +PIX * +pixCloseGray3(PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt, *pixb, *pixbd, *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pix has colormap", __func__, NULL); + if ((hsize != 1 && hsize != 3) || + (vsize != 1 && vsize != 3)) + return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", __func__, NULL); + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 0); /* set to min */ + + if (vsize == 1) { + pixt = pixDilateGray3h(pixb); + pixSetBorderVal(pixt, 4, 8, 2, 8, 255); /* set to max */ + pixbd = pixErodeGray3h(pixt); + pixDestroy(&pixt); + } else if (hsize == 1) { + pixt = pixDilateGray3v(pixb); + pixSetBorderVal(pixt, 4, 8, 2, 8, 255); + pixbd = pixErodeGray3v(pixt); + pixDestroy(&pixt); + } else { /* vize == hsize == 3 */ + pixt = pixDilateGray3h(pixb); + pixbd = pixDilateGray3v(pixt); + pixDestroy(&pixt); + pixSetBorderVal(pixbd, 4, 8, 2, 8, 255); + pixt = pixErodeGray3h(pixbd); + pixDestroy(&pixbd); + pixbd = pixErodeGray3v(pixt); + pixDestroy(&pixt); + } + + pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); + pixDestroy(&pixb); + pixDestroy(&pixbd); + return pixd; +} + + +/*-----------------------------------------------------------------* + * Low-level gray morphological operations * + *-----------------------------------------------------------------*/ +/*! + * \brief dilateGrayLow() + * + * \param[in] datad 8 bpp dsst image + * \param[in] w, h dimensions of src and dest + * \param[in] wpld words/line of dest + * \param[in] datas 8 bpp src image + * \param[in] wpls words/line of src + * \param[in] size full length of SEL; restricted to odd numbers + * \param[in] direction L_HORIZ or L_VERT + * \param[in] buffer holds full line or column of src image pixels + * \param[in] maxarray array of dimension 2*size+1 + * \return void + * + * <pre> + * Notes: + * (1) To eliminate border effects on the actual image, these images + * are prepared with an additional border of dimensions: + * leftpix = 0.5 * size + * rightpix = 1.5 * size + * toppix = 0.5 * size + * bottompix = 1.5 * size + * and we initialize the src border pixels to 0. + * This allows full processing over the actual image; at + * the end the border is removed. + * (2) Uses algorithm of van Herk, Gil and Werman + * </pre> + */ +static void +dilateGrayLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 size, + l_int32 direction, + l_uint8 *buffer, + l_uint8 *maxarray) +{ +l_int32 i, j, k; +l_int32 hsize, nsteps, startmax, startx, starty; +l_uint8 maxval; +l_uint32 *lines, *lined; + + if (direction == L_HORIZ) { + hsize = size / 2; + nsteps = (w - 2 * hsize) / size; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + + /* fill buffer with pixels in byte order */ + for (j = 0; j < w; j++) + buffer[j] = GET_DATA_BYTE(lines, j); + + for (j = 0; j < nsteps; j++) { + /* refill the minarray */ + startmax = (j + 1) * size - 1; + maxarray[size - 1] = buffer[startmax]; + for (k = 1; k < size; k++) { + maxarray[size - 1 - k] = + L_MAX(maxarray[size - k], buffer[startmax - k]); + maxarray[size - 1 + k] = + L_MAX(maxarray[size + k - 2], buffer[startmax + k]); + } + + /* compute dilation values */ + startx = hsize + j * size; + SET_DATA_BYTE(lined, startx, maxarray[0]); + SET_DATA_BYTE(lined, startx + size - 1, maxarray[2 * size - 2]); + for (k = 1; k < size - 1; k++) { + maxval = L_MAX(maxarray[k], maxarray[k + size - 1]); + SET_DATA_BYTE(lined, startx + k, maxval); + } + } + } + } else { /* direction == L_VERT */ + hsize = size / 2; + nsteps = (h - 2 * hsize) / size; + for (j = 0; j < w; j++) { + /* fill buffer with pixels in byte order */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + buffer[i] = GET_DATA_BYTE(lines, j); + } + + for (i = 0; i < nsteps; i++) { + /* refill the minarray */ + startmax = (i + 1) * size - 1; + maxarray[size - 1] = buffer[startmax]; + for (k = 1; k < size; k++) { + maxarray[size - 1 - k] = + L_MAX(maxarray[size - k], buffer[startmax - k]); + maxarray[size - 1 + k] = + L_MAX(maxarray[size + k - 2], buffer[startmax + k]); + } + + /* compute dilation values */ + starty = hsize + i * size; + lined = datad + starty * wpld; + SET_DATA_BYTE(lined, j, maxarray[0]); + SET_DATA_BYTE(lined + (size - 1) * wpld, j, + maxarray[2 * size - 2]); + for (k = 1; k < size - 1; k++) { + maxval = L_MAX(maxarray[k], maxarray[k + size - 1]); + SET_DATA_BYTE(lined + wpld * k, j, maxval); + } + } + } + } + + return; +} + + +/*! + * \brief erodeGrayLow() + * + * \param[in] datad 8 bpp dsst image + * \param[in] w, h dimensions of src and dest + * \param[in] wpld words/line of dest + * \param[in] datas 8 bpp src image + * \param[in] wpls words/line of src + * \param[in] size full length of SEL; restricted to odd numbers + * \param[in] direction L_HORIZ or L_VERT + * \param[in] buffer holds full line or column of src image pixels + * \param[in] minarray array of dimension 2*size+1 + * \return void + * + * <pre> + * Notes: + * (1) See notes in dilateGrayLow() + * </pre> + */ +static void +erodeGrayLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 size, + l_int32 direction, + l_uint8 *buffer, + l_uint8 *minarray) +{ +l_int32 i, j, k; +l_int32 hsize, nsteps, startmin, startx, starty; +l_uint8 minval; +l_uint32 *lines, *lined; + + if (direction == L_HORIZ) { + hsize = size / 2; + nsteps = (w - 2 * hsize) / size; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + + /* fill buffer with pixels in byte order */ + for (j = 0; j < w; j++) + buffer[j] = GET_DATA_BYTE(lines, j); + + for (j = 0; j < nsteps; j++) { + /* refill the minarray */ + startmin = (j + 1) * size - 1; + minarray[size - 1] = buffer[startmin]; + for (k = 1; k < size; k++) { + minarray[size - 1 - k] = + L_MIN(minarray[size - k], buffer[startmin - k]); + minarray[size - 1 + k] = + L_MIN(minarray[size + k - 2], buffer[startmin + k]); + } + + /* compute erosion values */ + startx = hsize + j * size; + SET_DATA_BYTE(lined, startx, minarray[0]); + SET_DATA_BYTE(lined, startx + size - 1, minarray[2 * size - 2]); + for (k = 1; k < size - 1; k++) { + minval = L_MIN(minarray[k], minarray[k + size - 1]); + SET_DATA_BYTE(lined, startx + k, minval); + } + } + } + } else { /* direction == L_VERT */ + hsize = size / 2; + nsteps = (h - 2 * hsize) / size; + for (j = 0; j < w; j++) { + /* fill buffer with pixels in byte order */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + buffer[i] = GET_DATA_BYTE(lines, j); + } + + for (i = 0; i < nsteps; i++) { + /* refill the minarray */ + startmin = (i + 1) * size - 1; + minarray[size - 1] = buffer[startmin]; + for (k = 1; k < size; k++) { + minarray[size - 1 - k] = + L_MIN(minarray[size - k], buffer[startmin - k]); + minarray[size - 1 + k] = + L_MIN(minarray[size + k - 2], buffer[startmin + k]); + } + + /* compute erosion values */ + starty = hsize + i * size; + lined = datad + starty * wpld; + SET_DATA_BYTE(lined, j, minarray[0]); + SET_DATA_BYTE(lined + (size - 1) * wpld, j, + minarray[2 * size - 2]); + for (k = 1; k < size - 1; k++) { + minval = L_MIN(minarray[k], minarray[k + size - 1]); + SET_DATA_BYTE(lined + wpld * k, j, minval); + } + } + } + } + + return; +}
