/*====================================================================* - Copyright (C) 2001 Leptonica. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *====================================================================*/ /*! * \file 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,*/ #include "allheaders.h" #ifndef NO_CONSOLE_IO #define DEBUG_SEL_LOOKUP 0 #endif /* ~NO_CONSOLE_IO */ /*-----------------------------------------------------------------* * Binary morphological (dwa) ops with brick Sels * *-----------------------------------------------------------------*/ /*! * \brief pixDilateBrickDwa() * * \param[in] pixd [optional]; this can be null, equal to pixs, * or different from pixs * \param[in] pixs 1 bpp * \param[in] hsize width of brick Sel * \param[in] vsize height of brick Sel * \return pixd * *, filename) to generate the code. * ~ Compile your program that uses the newly generated function * pixMorphDwa_ (), 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, , filename) to generate the code. * ~ Compile your program that uses the newly generated function * pixMorphDwa_ (), 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.) *
* 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\n", procName); if (selnameh) LEPT_FREE(selnameh); if (selnamev) LEPT_FREE(selnamev); return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize); } if (vsize == 1) { pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnameh); LEPT_FREE(selnameh); } else if (hsize == 1) { pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnamev); LEPT_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); LEPT_FREE(selnameh); LEPT_FREE(selnamev); } if (!pixd) return pixt2; pixTransferAllData(pixd, &pixt2, 0, 0); return pixd; } /*! * \brief pixErodeBrickDwa() * * \param[in] pixd [optional]; this can be null, equal to pixs, * or different from pixs * \param[in] pixs 1 bpp * \param[in] hsize width of brick Sel * \param[in] 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\n", procName); if (selnameh) LEPT_FREE(selnameh); if (selnamev) LEPT_FREE(selnamev); return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize); } if (vsize == 1) { pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnameh); LEPT_FREE(selnameh); } else if (hsize == 1) { pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnamev); LEPT_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); LEPT_FREE(selnameh); LEPT_FREE(selnamev); } if (!pixd) return pixt2; pixTransferAllData(pixd, &pixt2, 0, 0); return pixd; } /*! * \brief pixOpenBrickDwa() * * \param[in] pixd [optional]; this can be null, equal to pixs, * or different from pixs * \param[in] pixs 1 bpp * \param[in] hsize width of brick Sel * \param[in] 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\n", procName); if (selnameh) LEPT_FREE(selnameh); if (selnamev) LEPT_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); LEPT_FREE(selnameh); } else if (hsize == 1) { /* vertical only */ pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnamev); LEPT_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); LEPT_FREE(selnameh); LEPT_FREE(selnamev); pixDestroy(&pixt3); } pixt3 = pixRemoveBorder(pixt2, 32); pixDestroy(&pixt1); pixDestroy(&pixt2); if (!pixd) return pixt3; pixTransferAllData(pixd, &pixt3, 0, 0); return pixd; } /*! * \brief pixCloseBrickDwa() * * \param[in] pixd [optional]; this can be null, equal to pixs, * or different from pixs * \param[in] pixs 1 bpp * \param[in] hsize width of brick Sel * \param[in] 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\n", procName); if (selnameh) LEPT_FREE(selnameh); if (selnamev) LEPT_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); LEPT_FREE(selnameh); } else if (hsize == 1) { /* vertical only */ pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev); LEPT_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); LEPT_FREE(selnameh); LEPT_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 * *-----------------------------------------------------------------*/ /*! * \brief pixDilateCompBrickDwa() * * \param[in] pixd [optional]; this can be null, equal to pixs, * or different from pixs * \param[in] pixs 1 bpp * \param[in] hsize width of brick Sel * \param[in] 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) LEPT_FREE(selnameh1); if (selnameh2) LEPT_FREE(selnameh2); if (selnamev1) LEPT_FREE(selnamev1); if (selnamev2) LEPT_FREE(selnamev2); if (!pixd) return pixt1; pixTransferAllData(pixd, &pixt1, 0, 0); return pixd; } /*! * \brief pixErodeCompBrickDwa() * * \param[in] pixd [optional]; this can be null, equal to pixs, * or different from pixs * \param[in] pixs 1 bpp * \param[in] hsize width of brick Sel * \param[in] 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) LEPT_FREE(selnameh1); if (selnameh2) LEPT_FREE(selnameh2); if (selnamev1) LEPT_FREE(selnamev1); if (selnamev2) LEPT_FREE(selnamev2); if (!pixd) return pixt1; pixTransferAllData(pixd, &pixt1, 0, 0); return pixd; } /*! * \brief pixOpenCompBrickDwa() * * \param[in] pixd [optional]; this can be null, equal to pixs, * or different from pixs * \param[in] pixs 1 bpp * \param[in] hsize width of brick Sel * \param[in] 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) LEPT_FREE(selnameh1); if (selnameh2) LEPT_FREE(selnameh2); if (selnamev1) LEPT_FREE(selnamev1); if (selnamev2) LEPT_FREE(selnamev2); if (!pixd) return pixt1; pixTransferAllData(pixd, &pixt1, 0, 0); return pixd; } /*! * \brief pixCloseCompBrickDwa() * * \param[in] pixd [optional]; this can be null, equal to pixs, * or different from pixs * \param[in] pixs 1 bpp * \param[in] hsize width of brick Sel * \param[in] 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) LEPT_FREE(selnameh1); if (selnameh2) LEPT_FREE(selnameh2); if (selnamev1) LEPT_FREE(selnamev1); if (selnamev2) LEPT_FREE(selnamev2); if (!pixd) return pixt1; pixTransferAllData(pixd, &pixt1, 0, 0); return pixd; } /*--------------------------------------------------------------------------* * Binary expanded composite morphological (dwa) ops with brick Sels * *--------------------------------------------------------------------------*/ /*! * \brief pixDilateCompBrickExtendDwa() * * \param[in] pixd [optional]; this can be null, equal to pixs, * or different from pixs * \param[in] pixs 1 bpp * \param[in] hsize width of brick Sel * \param[in] 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 = pixCreateTemplate(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; } /*! * \brief pixErodeCompBrickExtendDwa() * * \param[in] pixd [optional]; this can be null, equal to pixs, * or different from pixs * \param[in] pixs 1 bpp * \param[in] hsize width of brick Sel * \param[in] 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 = pixCreateTemplate(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; } /*! * \brief pixOpenCompBrickExtendDwa() * * \param[in] pixd [optional]; this can be null, equal to pixs, * or different from pixs * \param[in] pixs 1 bpp * \param[in] hsize width of brick Sel * \param[in] vsize height of brick Sel * \return pixd * *
* Notes: * 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; } /*! * \brief pixCloseCompBrickExtendDwa() * * \param[in] pixd [optional]; this can be null, equal to pixs, * or different from pixs * \param[in] pixs 1 bpp * \param[in] hsize width of brick Sel * \param[in] vsize height of brick Sel * \return pixd * *
* Notes: * 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; } /*! * \brief getExtendedCompositeParameters() * * \param[in] size of linear Sel * \param[out] pn number of 63 wide convolutions * \param[out] pextra size of extra Sel * \param[out] pactualsize [optional] 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_ok 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; }