| /*====================================================================* |
| - 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; |
| } |
| |
| |