Files
core/ASCImageStudio3/ASCImageJBig2/LeptonLib/graymorph.cpp
Elen.Subbotina 70e623d70b восстановление
git-svn-id: svn://fileserver/activex/AVS/Sources/TeamlabOffice/trunk/ServerComponents@62634 954022d7-b5bf-4e40-9824-e11837661b57
2016-05-21 00:05:02 +03:00

1031 lines
34 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 (van Herk / Gil-Werman)
* PIX *pixErodeGray()
* PIX *pixDilateGray()
* PIX *pixOpenGray()
* PIX *pixCloseGray()
*
* Special operations for 1x3, 3x1 and 3x3 Sels (direct)
* PIX *pixErodeGray3()
* PIX *pixDilateGray3()
* PIX *pixOpenGray3()
* PIX *pixCloseGray3()
*
*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include "allheaders.h"
static PIX *pixErodeGray3h(PIX *pixs);
static PIX *pixErodeGray3v(PIX *pixs);
static PIX *pixDilateGray3h(PIX *pixs);
static PIX *pixDilateGray3v(PIX *pixs);
/*-----------------------------------------------------------------*
* 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);
pixGetDimensions(pixt, &w, &h, NULL);
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);
pixGetDimensions(pixt, &w, &h, NULL);
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);
pixGetDimensions(pixt, &w, &h, NULL);
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);
pixGetDimensions(pixt, &w, &h, NULL);
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;
}
/*-----------------------------------------------------------------*
* Special operations for 1x3, 3x1 and 3x3 Sels *
*-----------------------------------------------------------------*/
/*!
* pixErodeGray3()
*
* Input: pixs (8 bpp, not cmapped)
* hsize (1 or 3)
* vsize (1 or 3)
* Return: pixd, or null on error
*
* 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.
*/
PIX *
pixErodeGray3(PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
PIX *pixt, *pixb, *pixbd, *pixd;
PROCNAME("pixErodeGray3");
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 (pixGetColormap(pixs))
return (PIX *)ERROR_PTR("pix has colormap", procName, NULL);
if ((hsize != 1 && hsize != 3) ||
(vsize != 1 && vsize != 3))
return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, 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;
}
/*!
* pixErodeGray3h()
*
* Input: pixs (8 bpp, not cmapped)
* Return: pixd, or null on error
*
* Notes:
* (1) Special case for horizontal 3x1 brick Sel;
* also used as the first step for the 3x3 brick Sel.
*/
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;
PROCNAME("pixErodeGray3h");
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);
pixd = pixCreateTemplateNoInit(pixs);
pixSetBorderVal(pixd, 4, 8, 2, 8, 0); /* only to silence valgrind */
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;
}
/*!
* pixErodeGray3v()
*
* Input: pixs (8 bpp, not cmapped)
* Return: pixd, or null on error
*
* 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);
*/
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;
PROCNAME("pixErodeGray3v");
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);
pixd = pixCreateTemplateNoInit(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;
}
/*!
* pixDilateGray3()
*
* Input: pixs (8 bpp, not cmapped)
* hsize (1 or 3)
* vsize (1 or 3)
* Return: pixd, or null on error
*
* Notes:
* (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits)
* (2) If hsize = vsize = 1, just returns a copy.
*/
PIX *
pixDilateGray3(PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
PIX *pixt, *pixb, *pixbd, *pixd;
PROCNAME("pixDilateGray3");
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 (pixGetColormap(pixs))
return (PIX *)ERROR_PTR("pix has colormap", procName, NULL);
if ((hsize != 1 && hsize != 3) ||
(vsize != 1 && vsize != 3))
return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, 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;
}
/*!
* pixDilateGray3h()
*
* Input: pixs (8 bpp, not cmapped)
* Return: pixd, or null on error
*
* Notes:
* (1) Special case for horizontal 3x1 brick Sel;
* also used as the first step for the 3x3 brick Sel.
*/
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;
PROCNAME("pixDilateGray3h");
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);
pixd = pixCreateTemplateNoInit(pixs);
pixSetBorderVal(pixd, 4, 8, 2, 8, 0); /* only to silence valgrind */
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;
}
/*!
* pixDilateGray3v()
*
* Input: pixs (8 bpp, not cmapped)
* Return: pixd, or null on error
*
* Notes:
* (1) Special case for vertical 1x3 brick Sel;
* also used as the second step for the 3x3 brick Sel.
*/
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;
PROCNAME("pixDilateGray3v");
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);
pixd = pixCreateTemplateNoInit(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;
}
/*!
* pixOpenGray3()
*
* Input: pixs (8 bpp, not cmapped)
* hsize (1 or 3)
* vsize (1 or 3)
* Return: pixd, or null on error
*
* 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.
*/
PIX *
pixOpenGray3(PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
PIX *pixt, *pixb, *pixbd, *pixd;
PROCNAME("pixOpenGray3");
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 (pixGetColormap(pixs))
return (PIX *)ERROR_PTR("pix has colormap", procName, NULL);
if ((hsize != 1 && hsize != 3) ||
(vsize != 1 && vsize != 3))
return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, 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;
}
/*!
* pixCloseGray3()
*
* Input: pixs (8 bpp, not cmapped)
* hsize (1 or 3)
* vsize (1 or 3)
* Return: pixd, or null on error
*
* Notes:
* (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits)
* (2) If hsize = vsize = 1, just returns a copy.
*/
PIX *
pixCloseGray3(PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
PIX *pixt, *pixb, *pixbd, *pixd;
PROCNAME("pixCloseGray3");
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 (pixGetColormap(pixs))
return (PIX *)ERROR_PTR("pix has colormap", procName, NULL);
if ((hsize != 1 && hsize != 3) ||
(vsize != 1 && vsize != 3))
return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, 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;
}