mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 14:16:10 -06:00
517 lines
17 KiB
C
517 lines
17 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.
|
|
*====================================================================*/
|
|
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
|