blob: 7a5953802acc2b5471a11d8082ac83f95d99fada [file] [log] [blame]
/*====================================================================*
- 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.
*====================================================================*/
/*
* morphdwa.c
*
* Binary morphological (dwa) ops with brick Sels
* PIX *pixDilateBrickDwa()
* PIX *pixErodeBrickDwa()
* PIX *pixOpenBrickDwa()
* PIX *pixCloseBrickDwa()
*
* Binary composite morphological (dwa) ops with brick Sels
* PIX *pixDilateCompBrickDwa()
* PIX *pixErodeCompBrickDwa()
* PIX *pixOpenCompBrickDwa()
* PIX *pixCloseCompBrickDwa()
*
* Binary extended composite morphological (dwa) ops with brick Sels
* PIX *pixDilateCompBrickExtendDwa()
* PIX *pixErodeCompBrickExtendDwa()
* PIX *pixOpenCompBrickExtendDwa()
* PIX *pixCloseCompBrickExtendDwa()
* l_int32 getExtendedCompositeParameters()
*
* These are higher-level interfaces for dwa morphology with brick Sels.
* Because many morphological operations are performed using
* separable brick Sels, it is useful to have a simple interface
* for this.
*
* We have included all 58 of the brick Sels that are generated
* by selaAddBasic(). These are sufficient for all the decomposable
* bricks up to size 63, which is the limit for dwa Sels with
* origins at the center of the Sel.
*
* All three sets can be used as the basic interface for general
* brick operations. Here are the internal calling sequences:
*
* (1) If you try to apply a non-decomposable operation, such as
* pixErodeBrickDwa(), with a Sel size that doesn't exist,
* this calls a decomposable operation, pixErodeCompBrickDwa(),
* instead. This can differ in linear Sel size by up to
* 2 pixels from the request.
*
* (2) If either Sel brick dimension is greater than 63, the extended
* composite function is called.
*
* (3) The extended composite function calls the composite function
* a number of times with size 63, and once with size < 63.
* Because each operation with a size of 63 is done compositely
* with 7 x 9 (exactly 63), the net result is correct in
* length to within 2 pixels.
*
* For composite operations, both using a comb and extended (beyond 63),
* horizontal and vertical operations are composed separately
* and sequentially.
*
* We have also included use of all the 76 comb Sels that are generated
* by selaAddDwaCombs(). The generated code is in dwacomb.2.c
* and dwacomblow.2.c. These are used for the composite dwa
* brick operations.
*
* The non-composite brick operations, such as pixDilateBrickDwa(),
* will call the associated composite operation in situations where
* the requisite brick Sel has not been compiled into fmorphgen*.1.c.
*
* If you want to use brick Sels that are not represented in the
* basic set of 58, you must generate the dwa code to implement them.
* You have three choices for how to use these:
*
* (1) Add both the new Sels and the dwa code to the library:
* - For simplicity, add your new brick Sels to those defined
* in selaAddBasic().
* - Recompile the library.
* - Make prog/fmorphautogen.
* - Run prog/fmorphautogen, to generate new versions of the
* dwa code in fmorphgen.1.c and fmorphgenlow.1.c.
* - Copy these two files to src.
* - Recompile the library again.
* - Use the new brick Sels in your program and compile it.
*
* (2) Make both the new Sels and dwa code outside the library,
* and link it directly to an executable:
* - Write a function to generate the new Sels in a Sela, and call
* fmorphautogen(sela, <N>, filename) to generate the code.
* - Compile your program that uses the newly generated function
* pixMorphDwa_<N>(), and link to the two new C files.
*
* (3) Make the new Sels in the library and use the dwa code outside it:
* - Add code in the library to generate your new brick Sels.
* (It is suggested that you NOT add these Sels to the
* selaAddBasic() function; write a new function that generates
* a new Sela.)
* - Recompile the library.
* - Write a small program that generates the Sela and calls
* fmorphautogen(sela, <N>, filename) to generate the code.
* - Compile your program that uses the newly generated function
* pixMorphDwa_<N>(), and link to the two new C files.
* As an example of this approach, see prog/dwamorph*_reg.c:
* - added selaAddDwaLinear() to sel2.c
* - wrote dwamorph1_reg.c, to generate the dwa code.
* - compiled and linked the generated code with the application,
* dwamorph2_reg.c. (Note: because this was a regression test,
* dwamorph1_reg also builds and runs the application program.)
*/
#include <stdio.h>
#include <stdlib.h>
#include "allheaders.h"
#ifndef NO_CONSOLE_IO
#define DEBUG_SEL_LOOKUP 0
#endif /* ~NO_CONSOLE_IO */
/*-----------------------------------------------------------------*
* Binary morphological (dwa) ops with brick Sels *
*-----------------------------------------------------------------*/
/*!
* pixDilateBrickDwa()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs (1 bpp)
* hsize (width of brick Sel)
* vsize (height of brick Sel)
* Return: pixd
*
* Notes:
* (1) These implement 2D brick Sels, using linear Sels generated
* with selaAddBasic().
* (2) A brick Sel has hits for all elements.
* (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
* (4) Do separably if both hsize and vsize are > 1.
* (5) It is necessary that both horizontal and vertical Sels
* of the input size are defined in the basic sela.
* (6) There are three cases:
* (a) pixd == null (result into new pixd)
* (b) pixd == pixs (in-place; writes result back to pixs)
* (c) pixd != pixs (puts result into existing pixd)
* (7) For clarity, if the case is known, use these patterns:
* (a) pixd = pixDilateBrickDwa(NULL, pixs, ...);
* (b) pixDilateBrickDwa(pixs, pixs, ...);
* (c) pixDilateBrickDwa(pixd, pixs, ...);
* (8) The size of pixd is determined by pixs.
* (9) If either linear Sel is not found, this calls
* the appropriate decomposible function.
*/
PIX *
pixDilateBrickDwa(PIX *pixd,
PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
l_int32 found;
char *selnameh, *selnamev;
SELA *sela;
PIX *pixt1, *pixt2, *pixt3;
PROCNAME("pixDilateBrickDwa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
if (hsize == 1 && vsize == 1)
return pixCopy(pixd, pixs);
sela = selaAddBasic(NULL);
found = TRUE;
selnameh = selnamev = NULL;
if (hsize > 1) {
selnameh = selaGetBrickName(sela, hsize, 1);
if (!selnameh) found = FALSE;
}
if (vsize > 1) {
selnamev = selaGetBrickName(sela, 1, vsize);
if (!selnamev) found = FALSE;
}
selaDestroy(&sela);
if (!found) {
L_INFO("Calling the decomposable dwa function", procName);
if (selnameh) FREE(selnameh);
if (selnamev) FREE(selnamev);
return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize);
}
if (vsize == 1) {
pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnameh);
FREE(selnameh);
}
else if (hsize == 1) {
pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnamev);
FREE(selnamev);
}
else {
pixt1 = pixAddBorder(pixs, 32, 0);
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh);
pixFMorphopGen_1(pixt1, pixt3, L_MORPH_DILATE, selnamev);
pixt2 = pixRemoveBorder(pixt1, 32);
pixDestroy(&pixt1);
pixDestroy(&pixt3);
FREE(selnameh);
FREE(selnamev);
}
if (!pixd)
return pixt2;
pixTransferAllData(pixd, &pixt2, 0, 0);
return pixd;
}
/*!
* pixErodeBrickDwa()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs (1 bpp)
* hsize (width of brick Sel)
* vsize (height of brick Sel)
* Return: pixd
*
* Notes:
* (1) These implement 2D brick Sels, using linear Sels generated
* with selaAddBasic().
* (2) A brick Sel has hits for all elements.
* (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
* (4) Do separably if both hsize and vsize are > 1.
* (5) It is necessary that both horizontal and vertical Sels
* of the input size are defined in the basic sela.
* (6) Note that we must always set or clear the border pixels
* before each operation, depending on the the b.c.
* (symmetric or asymmetric).
* (7) There are three cases:
* (a) pixd == null (result into new pixd)
* (b) pixd == pixs (in-place; writes result back to pixs)
* (c) pixd != pixs (puts result into existing pixd)
* (8) For clarity, if the case is known, use these patterns:
* (a) pixd = pixErodeBrickDwa(NULL, pixs, ...);
* (b) pixErodeBrickDwa(pixs, pixs, ...);
* (c) pixErodeBrickDwa(pixd, pixs, ...);
* (9) The size of the result is determined by pixs.
* (10) If either linear Sel is not found, this calls
* the appropriate decomposible function.
*/
PIX *
pixErodeBrickDwa(PIX *pixd,
PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
l_int32 found;
char *selnameh, *selnamev;
SELA *sela;
PIX *pixt1, *pixt2, *pixt3;
PROCNAME("pixErodeBrickDwa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
if (hsize == 1 && vsize == 1)
return pixCopy(pixd, pixs);
sela = selaAddBasic(NULL);
found = TRUE;
selnameh = selnamev = NULL;
if (hsize > 1) {
selnameh = selaGetBrickName(sela, hsize, 1);
if (!selnameh) found = FALSE;
}
if (vsize > 1) {
selnamev = selaGetBrickName(sela, 1, vsize);
if (!selnamev) found = FALSE;
}
selaDestroy(&sela);
if (!found) {
L_INFO("Calling the decomposable dwa function", procName);
if (selnameh) FREE(selnameh);
if (selnamev) FREE(selnamev);
return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize);
}
if (vsize == 1) {
pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnameh);
FREE(selnameh);
}
else if (hsize == 1) {
pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnamev);
FREE(selnamev);
}
else {
pixt1 = pixAddBorder(pixs, 32, 0);
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh);
pixFMorphopGen_1(pixt1, pixt3, L_MORPH_ERODE, selnamev);
pixt2 = pixRemoveBorder(pixt1, 32);
pixDestroy(&pixt1);
pixDestroy(&pixt3);
FREE(selnameh);
FREE(selnamev);
}
if (!pixd)
return pixt2;
pixTransferAllData(pixd, &pixt2, 0, 0);
return pixd;
}
/*!
* pixOpenBrickDwa()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs (1 bpp)
* hsize (width of brick Sel)
* vsize (height of brick Sel)
* Return: pixd
*
* Notes:
* (1) These implement 2D brick Sels, using linear Sels generated
* with selaAddBasic().
* (2) A brick Sel has hits for all elements.
* (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
* (4) Do separably if both hsize and vsize are > 1.
* (5) It is necessary that both horizontal and vertical Sels
* of the input size are defined in the basic sela.
* (6) Note that we must always set or clear the border pixels
* before each operation, depending on the the b.c.
* (symmetric or asymmetric).
* (7) There are three cases:
* (a) pixd == null (result into new pixd)
* (b) pixd == pixs (in-place; writes result back to pixs)
* (c) pixd != pixs (puts result into existing pixd)
* (8) For clarity, if the case is known, use these patterns:
* (a) pixd = pixOpenBrickDwa(NULL, pixs, ...);
* (b) pixOpenBrickDwa(pixs, pixs, ...);
* (c) pixOpenBrickDwa(pixd, pixs, ...);
* (9) The size of the result is determined by pixs.
* (10) If either linear Sel is not found, this calls
* the appropriate decomposible function.
*/
PIX *
pixOpenBrickDwa(PIX *pixd,
PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
l_int32 found;
char *selnameh, *selnamev;
SELA *sela;
PIX *pixt1, *pixt2, *pixt3;
PROCNAME("pixOpenBrickDwa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
if (hsize == 1 && vsize == 1)
return pixCopy(pixd, pixs);
sela = selaAddBasic(NULL);
found = TRUE;
selnameh = selnamev = NULL;
if (hsize > 1) {
selnameh = selaGetBrickName(sela, hsize, 1);
if (!selnameh) found = FALSE;
}
if (vsize > 1) {
selnamev = selaGetBrickName(sela, 1, vsize);
if (!selnamev) found = FALSE;
}
selaDestroy(&sela);
if (!found) {
L_INFO("Calling the decomposable dwa function", procName);
if (selnameh) FREE(selnameh);
if (selnamev) FREE(selnamev);
return pixOpenCompBrickDwa(pixd, pixs, hsize, vsize);
}
pixt1 = pixAddBorder(pixs, 32, 0);
if (vsize == 1) { /* horizontal only */
pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnameh);
FREE(selnameh);
}
else if (hsize == 1) { /* vertical only */
pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnamev);
FREE(selnamev);
}
else { /* do separable */
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh);
pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh);
pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev);
FREE(selnameh);
FREE(selnamev);
pixDestroy(&pixt3);
}
pixt3 = pixRemoveBorder(pixt2, 32);
pixDestroy(&pixt1);
pixDestroy(&pixt2);
if (!pixd)
return pixt3;
pixTransferAllData(pixd, &pixt3, 0, 0);
return pixd;
}
/*!
* pixCloseBrickDwa()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs (1 bpp)
* hsize (width of brick Sel)
* vsize (height of brick Sel)
* Return: pixd
*
* Notes:
* (1) This is a 'safe' closing; we add an extra border of 32 OFF
* pixels for the standard asymmetric b.c.
* (2) These implement 2D brick Sels, using linear Sels generated
* with selaAddBasic().
* (3) A brick Sel has hits for all elements.
* (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
* (5) Do separably if both hsize and vsize are > 1.
* (6) It is necessary that both horizontal and vertical Sels
* of the input size are defined in the basic sela.
* (7) Note that we must always set or clear the border pixels
* before each operation, depending on the the b.c.
* (symmetric or asymmetric).
* (8) There are three cases:
* (a) pixd == null (result into new pixd)
* (b) pixd == pixs (in-place; writes result back to pixs)
* (c) pixd != pixs (puts result into existing pixd)
* (9) For clarity, if the case is known, use these patterns:
* (a) pixd = pixCloseBrickDwa(NULL, pixs, ...);
* (b) pixCloseBrickDwa(pixs, pixs, ...);
* (c) pixCloseBrickDwa(pixd, pixs, ...);
* (10) The size of the result is determined by pixs.
* (11) If either linear Sel is not found, this calls
* the appropriate decomposible function.
*/
PIX *
pixCloseBrickDwa(PIX *pixd,
PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
l_int32 bordercolor, bordersize, found;
char *selnameh, *selnamev;
SELA *sela;
PIX *pixt1, *pixt2, *pixt3;
PROCNAME("pixCloseBrickDwa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
if (hsize == 1 && vsize == 1)
return pixCopy(pixd, pixs);
sela = selaAddBasic(NULL);
found = TRUE;
selnameh = selnamev = NULL;
if (hsize > 1) {
selnameh = selaGetBrickName(sela, hsize, 1);
if (!selnameh) found = FALSE;
}
if (vsize > 1) {
selnamev = selaGetBrickName(sela, 1, vsize);
if (!selnamev) found = FALSE;
}
selaDestroy(&sela);
if (!found) {
L_INFO("Calling the decomposable dwa function", procName);
if (selnameh) FREE(selnameh);
if (selnamev) FREE(selnamev);
return pixCloseCompBrickDwa(pixd, pixs, hsize, vsize);
}
/* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need
* an extra 32 OFF pixels around the image (in addition to
* the 32 added pixels for all dwa operations), whereas with
* SYMMETRIC_MORPH_BC this is not necessary. */
bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
if (bordercolor == 0) /* asymmetric b.c. */
bordersize = 64;
else /* symmetric b.c. */
bordersize = 32;
pixt1 = pixAddBorder(pixs, bordersize, 0);
if (vsize == 1) { /* horizontal only */
pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh);
FREE(selnameh);
}
else if (hsize == 1) { /* vertical only */
pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev);
FREE(selnamev);
}
else { /* do separable */
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh);
pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh);
pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev);
FREE(selnameh);
FREE(selnamev);
pixDestroy(&pixt3);
}
pixt3 = pixRemoveBorder(pixt2, bordersize);
pixDestroy(&pixt1);
pixDestroy(&pixt2);
if (!pixd)
return pixt3;
pixTransferAllData(pixd, &pixt3, 0, 0);
return pixd;
}
/*-----------------------------------------------------------------*
* Binary composite morphological (dwa) ops with brick Sels *
*-----------------------------------------------------------------*/
/*!
* pixDilateCompBrickDwa()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs (1 bpp)
* hsize (width of brick Sel)
* vsize (height of brick Sel)
* Return: pixd
*
* Notes:
* (1) These implement a separable composite dilation with 2D brick Sels.
* (2) For efficiency, it may decompose each linear morphological
* operation into two (brick + comb).
* (3) A brick Sel has hits for all elements.
* (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
* (5) Do separably if both hsize and vsize are > 1.
* (6) It is necessary that both horizontal and vertical Sels
* of the input size are defined in the basic sela.
* (7) There are three cases:
* (a) pixd == null (result into new pixd)
* (b) pixd == pixs (in-place; writes result back to pixs)
* (c) pixd != pixs (puts result into existing pixd)
* (8) For clarity, if the case is known, use these patterns:
* (a) pixd = pixDilateCompBrickDwa(NULL, pixs, ...);
* (b) pixDilateCompBrickDwa(pixs, pixs, ...);
* (c) pixDilateCompBrickDwa(pixd, pixs, ...);
* (9) The size of pixd is determined by pixs.
* (10) CAUTION: both hsize and vsize are being decomposed.
* The decomposer chooses a product of sizes (call them
* 'terms') for each that is close to the input size,
* but not necessarily equal to it. It attempts to optimize:
* (a) for consistency with the input values: the product
* of terms is close to the input size
* (b) for efficiency of the operation: the sum of the
* terms is small; ideally about twice the square
* root of the input size.
* So, for example, if the input hsize = 37, which is
* a prime number, the decomposer will break this into two
* terms, 6 and 6, so that the net result is a dilation
* with hsize = 36.
*/
PIX *
pixDilateCompBrickDwa(PIX *pixd,
PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
char *selnameh1, *selnameh2, *selnamev1, *selnamev2;
l_int32 hsize1, hsize2, vsize1, vsize2;
PIX *pixt1, *pixt2, *pixt3;
PROCNAME("pixDilateCompBrickDwa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
if (hsize > 63 || vsize > 63)
return pixDilateCompBrickExtendDwa(pixd, pixs, hsize, vsize);
if (hsize == 1 && vsize == 1)
return pixCopy(pixd, pixs);
hsize1 = hsize2 = vsize1 = vsize2 = 1;
selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL;
if (hsize > 1)
getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1,
&selnameh2, NULL, NULL);
if (vsize > 1)
getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL,
&selnamev1, &selnamev2);
#if DEBUG_SEL_LOOKUP
fprintf(stderr, "nameh1=%s, nameh2=%s, namev1=%s, namev2=%s\n",
selnameh1, selnameh2, selnamev1, selnamev2);
fprintf(stderr, "hsize1=%d, hsize2=%d, vsize1=%d, vsize2=%d\n",
hsize1, hsize2, vsize1, vsize2);
#endif /* DEBUG_SEL_LOOKUP */
pixt1 = pixAddBorder(pixs, 64, 0);
if (vsize == 1) {
if (hsize2 == 1)
pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
else {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2);
pixDestroy(&pixt3);
}
}
else if (hsize == 1) {
if (vsize2 == 1)
pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1);
else {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1);
pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2);
pixDestroy(&pixt3);
}
}
else { /* vsize and hsize both > 1 */
if (hsize2 == 1)
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
else {
pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_DILATE, selnameh2);
pixDestroy(&pixt2);
}
if (vsize2 == 1)
pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
else {
pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
pixFMorphopGen_2(pixt2, pixt2, L_MORPH_DILATE, selnamev2);
}
pixDestroy(&pixt3);
}
pixDestroy(&pixt1);
pixt1 = pixRemoveBorder(pixt2, 64);
pixDestroy(&pixt2);
if (selnameh1) FREE(selnameh1);
if (selnameh2) FREE(selnameh2);
if (selnamev1) FREE(selnamev1);
if (selnamev2) FREE(selnamev2);
if (!pixd)
return pixt1;
pixTransferAllData(pixd, &pixt1, 0, 0);
return pixd;
}
/*!
* pixErodeCompBrickDwa()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs (1 bpp)
* hsize (width of brick Sel)
* vsize (height of brick Sel)
* Return: pixd
*
* Notes:
* (1) These implement a separable composite erosion with 2D brick Sels.
* (2) For efficiency, it may decompose each linear morphological
* operation into two (brick + comb).
* (3) A brick Sel has hits for all elements.
* (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
* (5) Do separably if both hsize and vsize are > 1.
* (6) It is necessary that both horizontal and vertical Sels
* of the input size are defined in the basic sela.
* (7) There are three cases:
* (a) pixd == null (result into new pixd)
* (b) pixd == pixs (in-place; writes result back to pixs)
* (c) pixd != pixs (puts result into existing pixd)
* (8) For clarity, if the case is known, use these patterns:
* (a) pixd = pixErodeCompBrickDwa(NULL, pixs, ...);
* (b) pixErodeCompBrickDwa(pixs, pixs, ...);
* (c) pixErodeCompBrickDwa(pixd, pixs, ...);
* (9) The size of pixd is determined by pixs.
* (10) CAUTION: both hsize and vsize are being decomposed.
* The decomposer chooses a product of sizes (call them
* 'terms') for each that is close to the input size,
* but not necessarily equal to it. It attempts to optimize:
* (a) for consistency with the input values: the product
* of terms is close to the input size
* (b) for efficiency of the operation: the sum of the
* terms is small; ideally about twice the square
* root of the input size.
* So, for example, if the input hsize = 37, which is
* a prime number, the decomposer will break this into two
* terms, 6 and 6, so that the net result is a dilation
* with hsize = 36.
*/
PIX *
pixErodeCompBrickDwa(PIX *pixd,
PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
char *selnameh1, *selnameh2, *selnamev1, *selnamev2;
l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor;
PIX *pixt1, *pixt2, *pixt3;
PROCNAME("pixErodeCompBrickDwa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
if (hsize > 63 || vsize > 63)
return pixErodeCompBrickExtendDwa(pixd, pixs, hsize, vsize);
if (hsize == 1 && vsize == 1)
return pixCopy(pixd, pixs);
hsize1 = hsize2 = vsize1 = vsize2 = 1;
selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL;
if (hsize > 1)
getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1,
&selnameh2, NULL, NULL);
if (vsize > 1)
getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL,
&selnamev1, &selnamev2);
/* For symmetric b.c., bordercolor == 1 for erosion */
bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
pixt1 = pixAddBorder(pixs, 64, bordercolor);
if (vsize == 1) {
if (hsize2 == 1)
pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
else {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2);
pixDestroy(&pixt3);
}
}
else if (hsize == 1) {
if (vsize2 == 1)
pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1);
else {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1);
pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2);
pixDestroy(&pixt3);
}
}
else { /* vsize and hsize both > 1 */
if (hsize2 == 1)
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
else {
pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_ERODE, selnameh2);
pixDestroy(&pixt2);
}
if (vsize2 == 1)
pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1);
else {
pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1);
pixFMorphopGen_2(pixt2, pixt2, L_MORPH_ERODE, selnamev2);
}
pixDestroy(&pixt3);
}
pixDestroy(&pixt1);
pixt1 = pixRemoveBorder(pixt2, 64);
pixDestroy(&pixt2);
if (selnameh1) FREE(selnameh1);
if (selnameh2) FREE(selnameh2);
if (selnamev1) FREE(selnamev1);
if (selnamev2) FREE(selnamev2);
if (!pixd)
return pixt1;
pixTransferAllData(pixd, &pixt1, 0, 0);
return pixd;
}
/*!
* pixOpenCompBrickDwa()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs (1 bpp)
* hsize (width of brick Sel)
* vsize (height of brick Sel)
* Return: pixd
*
* Notes:
* (1) These implement a separable composite opening with 2D brick Sels.
* (2) For efficiency, it may decompose each linear morphological
* operation into two (brick + comb).
* (3) A brick Sel has hits for all elements.
* (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
* (5) Do separably if both hsize and vsize are > 1.
* (6) It is necessary that both horizontal and vertical Sels
* of the input size are defined in the basic sela.
* (7) There are three cases:
* (a) pixd == null (result into new pixd)
* (b) pixd == pixs (in-place; writes result back to pixs)
* (c) pixd != pixs (puts result into existing pixd)
* (8) For clarity, if the case is known, use these patterns:
* (a) pixd = pixOpenCompBrickDwa(NULL, pixs, ...);
* (b) pixOpenCompBrickDwa(pixs, pixs, ...);
* (c) pixOpenCompBrickDwa(pixd, pixs, ...);
* (9) The size of pixd is determined by pixs.
* (10) CAUTION: both hsize and vsize are being decomposed.
* The decomposer chooses a product of sizes (call them
* 'terms') for each that is close to the input size,
* but not necessarily equal to it. It attempts to optimize:
* (a) for consistency with the input values: the product
* of terms is close to the input size
* (b) for efficiency of the operation: the sum of the
* terms is small; ideally about twice the square
* root of the input size.
* So, for example, if the input hsize = 37, which is
* a prime number, the decomposer will break this into two
* terms, 6 and 6, so that the net result is a dilation
* with hsize = 36.
*/
PIX *
pixOpenCompBrickDwa(PIX *pixd,
PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
char *selnameh1, *selnameh2, *selnamev1, *selnamev2;
l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor;
PIX *pixt1, *pixt2, *pixt3;
PROCNAME("pixOpenCompBrickDwa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
if (hsize > 63 || vsize > 63)
return pixOpenCompBrickExtendDwa(pixd, pixs, hsize, vsize);
if (hsize == 1 && vsize == 1)
return pixCopy(pixd, pixs);
hsize1 = hsize2 = vsize1 = vsize2 = 1;
selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL;
if (hsize > 1)
getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1,
&selnameh2, NULL, NULL);
if (vsize > 1)
getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL,
&selnamev1, &selnamev2);
/* For symmetric b.c., initialize erosion with bordercolor == 1 */
bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
pixt1 = pixAddBorder(pixs, 64, bordercolor);
if (vsize == 1) {
if (hsize2 == 1) {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
if (bordercolor == 1)
pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR);
pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnameh1);
}
else {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2);
if (bordercolor == 1)
pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1);
pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2);
}
}
else if (hsize == 1) {
if (vsize2 == 1) {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1);
if (bordercolor == 1)
pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR);
pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
}
else {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1);
pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2);
if (bordercolor == 1)
pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2);
}
}
else { /* vsize and hsize both > 1 */
if (hsize2 == 1 && vsize2 == 1) {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1);
if (bordercolor == 1)
pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1);
pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1);
}
else if (vsize2 == 1) {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
if (bordercolor == 1)
pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR);
pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1);
pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnameh2);
pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1);
}
else if (hsize2 == 1) {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1);
pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnamev2);
if (bordercolor == 1)
pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR);
pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2);
}
else { /* both directions are combed */
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2);
if (bordercolor == 1)
pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1);
pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2);
}
}
pixDestroy(&pixt3);
pixDestroy(&pixt1);
pixt1 = pixRemoveBorder(pixt2, 64);
pixDestroy(&pixt2);
if (selnameh1) FREE(selnameh1);
if (selnameh2) FREE(selnameh2);
if (selnamev1) FREE(selnamev1);
if (selnamev2) FREE(selnamev2);
if (!pixd)
return pixt1;
pixTransferAllData(pixd, &pixt1, 0, 0);
return pixd;
}
/*!
* pixCloseCompBrickDwa()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs (1 bpp)
* hsize (width of brick Sel)
* vsize (height of brick Sel)
* Return: pixd
*
* Notes:
* (1) This implements a separable composite safe closing with 2D
* brick Sels.
* (2) For efficiency, it may decompose each linear morphological
* operation into two (brick + comb).
* (3) A brick Sel has hits for all elements.
* (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
* (5) Do separably if both hsize and vsize are > 1.
* (6) It is necessary that both horizontal and vertical Sels
* of the input size are defined in the basic sela.
* (7) There are three cases:
* (a) pixd == null (result into new pixd)
* (b) pixd == pixs (in-place; writes result back to pixs)
* (c) pixd != pixs (puts result into existing pixd)
* (8) For clarity, if the case is known, use these patterns:
* (a) pixd = pixCloseCompBrickDwa(NULL, pixs, ...);
* (b) pixCloseCompBrickDwa(pixs, pixs, ...);
* (c) pixCloseCompBrickDwa(pixd, pixs, ...);
* (9) The size of pixd is determined by pixs.
* (10) CAUTION: both hsize and vsize are being decomposed.
* The decomposer chooses a product of sizes (call them
* 'terms') for each that is close to the input size,
* but not necessarily equal to it. It attempts to optimize:
* (a) for consistency with the input values: the product
* of terms is close to the input size
* (b) for efficiency of the operation: the sum of the
* terms is small; ideally about twice the square
* root of the input size.
* So, for example, if the input hsize = 37, which is
* a prime number, the decomposer will break this into two
* terms, 6 and 6, so that the net result is a dilation
* with hsize = 36.
*/
PIX *
pixCloseCompBrickDwa(PIX *pixd,
PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
char *selnameh1, *selnameh2, *selnamev1, *selnamev2;
l_int32 hsize1, hsize2, vsize1, vsize2, setborder;
PIX *pixt1, *pixt2, *pixt3;
PROCNAME("pixCloseCompBrickDwa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
if (hsize > 63 || vsize > 63)
return pixCloseCompBrickExtendDwa(pixd, pixs, hsize, vsize);
if (hsize == 1 && vsize == 1)
return pixCopy(pixd, pixs);
hsize1 = hsize2 = vsize1 = vsize2 = 1;
selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL;
if (hsize > 1)
getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1,
&selnameh2, NULL, NULL);
if (vsize > 1)
getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL,
&selnamev1, &selnamev2);
pixt3 = NULL;
/* For symmetric b.c., PIX_SET border for erosions */
setborder = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
pixt1 = pixAddBorder(pixs, 64, 0);
if (vsize == 1) {
if (hsize2 == 1)
pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh1);
else {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2);
if (setborder == 1)
pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1);
pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2);
}
}
else if (hsize == 1) {
if (vsize2 == 1)
pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev1);
else {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1);
pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2);
if (setborder == 1)
pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2);
}
}
else { /* vsize and hsize both > 1 */
if (hsize2 == 1 && vsize2 == 1) {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
if (setborder == 1)
pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1);
pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1);
}
else if (vsize2 == 1) {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
if (setborder == 1)
pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET);
pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1);
pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnameh2);
pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1);
}
else if (hsize2 == 1) {
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnamev2);
if (setborder == 1)
pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET);
pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2);
}
else { /* both directions are combed */
pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2);
if (setborder == 1)
pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1);
pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2);
pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2);
}
}
pixDestroy(&pixt3);
pixDestroy(&pixt1);
pixt1 = pixRemoveBorder(pixt2, 64);
pixDestroy(&pixt2);
if (selnameh1) FREE(selnameh1);
if (selnameh2) FREE(selnameh2);
if (selnamev1) FREE(selnamev1);
if (selnamev2) FREE(selnamev2);
if (!pixd)
return pixt1;
pixTransferAllData(pixd, &pixt1, 0, 0);
return pixd;
}
/*--------------------------------------------------------------------------*
* Binary expanded composite morphological (dwa) ops with brick Sels *
*--------------------------------------------------------------------------*/
/*!
* pixDilateCompBrickExtendDwa()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs (1 bpp)
* hsize (width of brick Sel)
* vsize (height of brick Sel)
* Return: pixd
*
* Notes:
* (1) Ankur Jain suggested and implemented extending the composite
* DWA operations beyond the 63 pixel limit. This is a
* simplified and approximate implementation of the extension.
* This allows arbitrary Dwa morph operations using brick Sels,
* by decomposing the horizontal and vertical dilations into
* a sequence of 63-element dilations plus a dilation of size
* between 3 and 62.
* (2) The 63-element dilations are exact, whereas the extra dilation
* is approximate, because the underlying decomposition is
* in pixDilateCompBrickDwa(). See there for further details.
* (3) There are three cases:
* (a) pixd == null (result into new pixd)
* (b) pixd == pixs (in-place; writes result back to pixs)
* (c) pixd != pixs (puts result into existing pixd)
* (4) There is no need to call this directly: pixDilateCompBrickDwa()
* calls this function if either brick dimension exceeds 63.
*/
PIX *
pixDilateCompBrickExtendDwa(PIX *pixd,
PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
l_int32 i, nops, nh, extrah, nv, extrav;
PIX *pixt1, *pixt2, *pixt3;
PROCNAME("pixDilateCompBrickExtendDwa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
if (hsize < 64 && vsize < 64)
return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize);
if (hsize > 63)
getExtendedCompositeParameters(hsize, &nh, &extrah, NULL);
if (vsize > 63)
getExtendedCompositeParameters(vsize, &nv, &extrav, NULL);
/* Horizontal dilation first: pixs --> pixt2. Do not alter pixs. */
pixt1 = pixCreateTemplateNoInit(pixs); /* temp image */
if (hsize == 1)
pixt2 = pixClone(pixs);
else if (hsize < 64)
pixt2 = pixDilateCompBrickDwa(NULL, pixs, hsize, 1);
else if (hsize == 64) /* approximate */
pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1);
else {
nops = (extrah < 3) ? nh : nh + 1;
if (nops & 1) { /* odd */
if (extrah > 2)
pixt2 = pixDilateCompBrickDwa(NULL, pixs, extrah, 1);
else
pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1);
for (i = 0; i < nops / 2; i++) {
pixDilateCompBrickDwa(pixt1, pixt2, 63, 1);
pixDilateCompBrickDwa(pixt2, pixt1, 63, 1);
}
}
else { /* nops even */
if (extrah > 2) {
pixDilateCompBrickDwa(pixt1, pixs, extrah, 1);
pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1);
}
else { /* they're all 63s */
pixDilateCompBrickDwa(pixt1, pixs, 63, 1);
pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1);
}
for (i = 0; i < nops / 2 - 1; i++) {
pixDilateCompBrickDwa(pixt1, pixt2, 63, 1);
pixDilateCompBrickDwa(pixt2, pixt1, 63, 1);
}
}
}
/* Vertical dilation: pixt2 --> pixt3. */
if (vsize == 1)
pixt3 = pixClone(pixt2);
else if (vsize < 64)
pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, vsize);
else if (vsize == 64) /* approximate */
pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63);
else {
nops = (extrav < 3) ? nv : nv + 1;
if (nops & 1) { /* odd */
if (extrav > 2)
pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, extrav);
else
pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63);
for (i = 0; i < nops / 2; i++) {
pixDilateCompBrickDwa(pixt1, pixt3, 1, 63);
pixDilateCompBrickDwa(pixt3, pixt1, 1, 63);
}
}
else { /* nops even */
if (extrav > 2) {
pixDilateCompBrickDwa(pixt1, pixt2, 1, extrav);
pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63);
}
else { /* they're all 63s */
pixDilateCompBrickDwa(pixt1, pixt2, 1, 63);
pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63);
}
for (i = 0; i < nops / 2 - 1; i++) {
pixDilateCompBrickDwa(pixt1, pixt3, 1, 63);
pixDilateCompBrickDwa(pixt3, pixt1, 1, 63);
}
}
}
pixDestroy(&pixt1);
pixDestroy(&pixt2);
if (!pixd)
return pixt3;
pixTransferAllData(pixd, &pixt3, 0, 0);
return pixd;
}
/*!
* pixErodeCompBrickExtendDwa()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs (1 bpp)
* hsize (width of brick Sel)
* vsize (height of brick Sel)
* Return: pixd
*
* Notes:
* (1) See pixDilateCompBrickExtendDwa() for usage.
* (2) There is no need to call this directly: pixErodeCompBrickDwa()
* calls this function if either brick dimension exceeds 63.
*/
PIX *
pixErodeCompBrickExtendDwa(PIX *pixd,
PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
l_int32 i, nops, nh, extrah, nv, extrav;
PIX *pixt1, *pixt2, *pixt3;
PROCNAME("pixErodeCompBrickExtendDwa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
if (hsize < 64 && vsize < 64)
return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize);
if (hsize > 63)
getExtendedCompositeParameters(hsize, &nh, &extrah, NULL);
if (vsize > 63)
getExtendedCompositeParameters(vsize, &nv, &extrav, NULL);
/* Horizontal erosion first: pixs --> pixt2. Do not alter pixs. */
pixt1 = pixCreateTemplateNoInit(pixs); /* temp image */
if (hsize == 1)
pixt2 = pixClone(pixs);
else if (hsize < 64)
pixt2 = pixErodeCompBrickDwa(NULL, pixs, hsize, 1);
else if (hsize == 64) /* approximate */
pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1);
else {
nops = (extrah < 3) ? nh : nh + 1;
if (nops & 1) { /* odd */
if (extrah > 2)
pixt2 = pixErodeCompBrickDwa(NULL, pixs, extrah, 1);
else
pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1);
for (i = 0; i < nops / 2; i++) {
pixErodeCompBrickDwa(pixt1, pixt2, 63, 1);
pixErodeCompBrickDwa(pixt2, pixt1, 63, 1);
}
}
else { /* nops even */
if (extrah > 2) {
pixErodeCompBrickDwa(pixt1, pixs, extrah, 1);
pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1);
}
else { /* they're all 63s */
pixErodeCompBrickDwa(pixt1, pixs, 63, 1);
pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1);
}
for (i = 0; i < nops / 2 - 1; i++) {
pixErodeCompBrickDwa(pixt1, pixt2, 63, 1);
pixErodeCompBrickDwa(pixt2, pixt1, 63, 1);
}
}
}
/* Vertical erosion: pixt2 --> pixt3. */
if (vsize == 1)
pixt3 = pixClone(pixt2);
else if (vsize < 64)
pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, vsize);
else if (vsize == 64) /* approximate */
pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63);
else {
nops = (extrav < 3) ? nv : nv + 1;
if (nops & 1) { /* odd */
if (extrav > 2)
pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, extrav);
else
pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63);
for (i = 0; i < nops / 2; i++) {
pixErodeCompBrickDwa(pixt1, pixt3, 1, 63);
pixErodeCompBrickDwa(pixt3, pixt1, 1, 63);
}
}
else { /* nops even */
if (extrav > 2) {
pixErodeCompBrickDwa(pixt1, pixt2, 1, extrav);
pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63);
}
else { /* they're all 63s */
pixErodeCompBrickDwa(pixt1, pixt2, 1, 63);
pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63);
}
for (i = 0; i < nops / 2 - 1; i++) {
pixErodeCompBrickDwa(pixt1, pixt3, 1, 63);
pixErodeCompBrickDwa(pixt3, pixt1, 1, 63);
}
}
}
pixDestroy(&pixt1);
pixDestroy(&pixt2);
if (!pixd)
return pixt3;
pixTransferAllData(pixd, &pixt3, 0, 0);
return pixd;
}
/*!
* pixOpenCompBrickExtendDwa()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs (1 bpp)
* hsize (width of brick Sel)
* vsize (height of brick Sel)
* Return: pixd
*
* (1) There are three cases:
* (a) pixd == null (result into new pixd)
* (b) pixd == pixs (in-place; writes result back to pixs)
* (c) pixd != pixs (puts result into existing pixd)
* (2) There is no need to call this directly: pixOpenCompBrickDwa()
* calls this function if either brick dimension exceeds 63.
*/
PIX *
pixOpenCompBrickExtendDwa(PIX *pixd,
PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
PIX *pixt;
PROCNAME("pixOpenCompBrickExtendDwa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
pixt = pixErodeCompBrickExtendDwa(NULL, pixs, hsize, vsize);
pixd = pixDilateCompBrickExtendDwa(pixd, pixt, hsize, vsize);
pixDestroy(&pixt);
return pixd;
}
/*!
* pixCloseCompBrickExtendDwa()
*
* Input: pixd (<optional>; this can be null, equal to pixs,
* or different from pixs)
* pixs (1 bpp)
* hsize (width of brick Sel)
* vsize (height of brick Sel)
* Return: pixd
*
* (1) There are three cases:
* (a) pixd == null (result into new pixd)
* (b) pixd == pixs (in-place; writes result back to pixs)
* (c) pixd != pixs (puts result into existing pixd)
* (2) There is no need to call this directly: pixCloseCompBrickDwa()
* calls this function if either brick dimension exceeds 63.
*/
PIX *
pixCloseCompBrickExtendDwa(PIX *pixd,
PIX *pixs,
l_int32 hsize,
l_int32 vsize)
{
l_int32 bordercolor, borderx, bordery;
PIX *pixt1, *pixt2, *pixt3;
PROCNAME("pixCloseCompBrickExtendDwa");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
if (hsize < 1 || vsize < 1)
return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
/* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need
* an extra 32 OFF pixels around the image (in addition to
* the 32 added pixels for all dwa operations), whereas with
* SYMMETRIC_MORPH_BC this is not necessary. */
bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
if (bordercolor == 0) { /* asymmetric b.c. */
borderx = 32 + (hsize / 64) * 32;
bordery = 32 + (vsize / 64) * 32;
}
else /* symmetric b.c. */
borderx = bordery = 32;
pixt1 = pixAddBorderGeneral(pixs, borderx, borderx, bordery, bordery, 0);
pixt2 = pixDilateCompBrickExtendDwa(NULL, pixt1, hsize, vsize);
pixErodeCompBrickExtendDwa(pixt1, pixt2, hsize, vsize);
pixt3 = pixRemoveBorderGeneral(pixt1, borderx, borderx, bordery, bordery);
pixDestroy(&pixt1);
pixDestroy(&pixt2);
if (!pixd)
return pixt3;
pixTransferAllData(pixd, &pixt3, 0, 0);
return pixd;
}
/*!
* getExtendedCompositeParameters()
*
* Input: size (of linear Sel)
* &pn (<return> number of 63 wide convolutions)
* &pextra (<return> size of extra Sel)
* &actualsize (<optional return> actual size used in operation)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) The DWA implementation allows Sels to be used with hits
* up to 31 pixels from the origin, either horizontally or
* vertically. Larger Sels can be used if decomposed into
* a set of operations with Sels not exceeding 63 pixels
* in either width or height (and with the origin as close
* to the center of the Sel as possible).
* (2) This returns the decomposition of a linear Sel of length
* @size into a set of @n Sels of length 63 plus an extra
* Sel of length @extra.
* (3) For notation, let w == @size, n == @n, and e == @extra.
* We have 1 < e < 63.
*
* Then if w < 64, we have n = 0 and e = w.
* The general formula for w > 63 is:
* w = 63 + (n - 1) * 62 + (e - 1)
*
* Where did this come from? Each successive convolution with
* a Sel of length L adds a total length (L - 1) to w.
* This accounts for using 62 for each additional Sel of size 63,
* and using (e - 1) for the additional Sel of size e.
*
* Solving for n and e for w > 63:
* n = 1 + Int((w - 63) / 62)
* e = w - 63 - (n - 1) * 62 + 1
*
* The extra part is decomposed into two factors f1 and f2,
* and the actual size of the extra part is
* e' = f1 * f2
* Then the actual width is:
* w' = 63 + (n - 1) * 62 + f1 * f2 - 1
*/
l_int32
getExtendedCompositeParameters(l_int32 size,
l_int32 *pn,
l_int32 *pextra,
l_int32 *pactualsize)
{
l_int32 n, extra, fact1, fact2;
PROCNAME("getExtendedCompositeParameters");
if (!pn || !pextra)
return ERROR_INT("&n and &extra not both defined", procName, 1);
if (size <= 63) {
n = 0;
extra = L_MIN(1, size);
}
else { /* size > 63 */
n = 1 + (l_int32)((size - 63) / 62);
extra = size - 63 - (n - 1) * 62 + 1;
}
if (pactualsize) {
selectComposableSizes(extra, &fact1, &fact2);
*pactualsize = 63 + (n - 1) * 62 + fact1 * fact2 - 1;
}
*pn = n;
*pextra = extra;
return 0;
}