mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 22:03:07 -06:00
321 lines
13 KiB
C
321 lines
13 KiB
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.
|
|
*====================================================================*/
|
|
|
|
|
|
/*
|
|
* graymorphlow.c
|
|
*
|
|
* Low-level grayscale morphological operations
|
|
*
|
|
* void dilateGrayLow()
|
|
* void erodeGrayLow()
|
|
*
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "allheaders.h"
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------*
|
|
* Low-level gray morphological operations *
|
|
*-----------------------------------------------------------------*/
|
|
/*!
|
|
* dilateGrayLow()
|
|
*
|
|
* Input: datad, w, h, wpld (8 bpp image)
|
|
* datas, wpls (8 bpp image, of same dimensions)
|
|
* size (full length of SEL; restricted to odd numbers)
|
|
* direction (L_HORIZ or L_VERT)
|
|
* buffer (holds full line or column of src image pixels)
|
|
* maxarray (array of dimension 2*size+1)
|
|
* Return: void
|
|
*
|
|
* Note: 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.
|
|
*
|
|
* Method: Algorithm by van Herk and Gil and Werman
|
|
*/
|
|
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;
|
|
}
|
|
|
|
|
|
/*!
|
|
* erodeGrayLow()
|
|
*
|
|
* Input: datad, w, h, wpld (8 bpp image)
|
|
* datas, wpls (8 bpp image, of same dimensions)
|
|
* size (full length of SEL; restricted to odd numbers)
|
|
* direction (L_HORIZ or L_VERT)
|
|
* buffer (holds full line or column of src image pixels)
|
|
* minarray (array of dimension 2*size+1)
|
|
* Return: void
|
|
*
|
|
* Note: 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 255.
|
|
* This allows full processing over the actual image; at
|
|
* the end the border is removed.
|
|
*
|
|
* Method: Algorithm by van Herk and Gil and Werman
|
|
*
|
|
*/
|
|
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;
|
|
}
|
|
|
|
|