Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/leptonica/src/sel2.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/leptonica/src/sel2.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,883 @@ +/*====================================================================* + - 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 sel2.c + * <pre> + * + * Contains definitions of simple structuring elements + * + * Basic brick structuring elements + * SELA *selaAddBasic() + * Linear horizontal and vertical + * Square + * Diagonals + * + * Simple hit-miss structuring elements + * SELA *selaAddHitMiss() + * Isolated foreground pixel + * Horizontal and vertical edges + * Slanted edge + * Corners + * + * Structuring elements for comparing with DWA operations + * SELA *selaAddDwaLinear() + * SELA *selaAddDwaCombs() + * + * Structuring elements for the intersection of lines + * SELA *selaAddCrossJunctions() + * SELA *selaAddTJunctions() + * + * Structuring elements for connectivity-preserving thinning operations + * SELA *sela4ccThin() + * SELA *sela8ccThin() + * SELA *sela4and8ccThin() + * + * Other structuring elements + * SEL *selMakePlusSign() + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include <math.h> +#include "allheaders.h" + +#define L_BUF_SIZE 512 + + /* Linear brick sel sizes, including all those that are required + * for decomposable sels up to size 63. */ +static const l_int32 num_linear = 25; +static const l_int32 basic_linear[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 20, 21, 25, 30, 31, 35, 40, 41, 45, 50, 51}; + + +/* ------------------------------------------------------------------- * + * Basic brick structuring elements * + * ------------------------------------------------------------------- */ +/*! + * \brief selaAddBasic() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + * + * <pre> + * Notes: + * (1) Adds the following sels: + * ~ all linear (horiz, vert) brick sels that are + * necessary for decomposable sels up to size 63 + * ~ square brick sels up to size 10 + * ~ 4 diagonal sels + * </pre> + */ +SELA * +selaAddBasic(SELA *sela) +{ +char name[L_BUF_SIZE]; +l_int32 i, size; +SEL *sel; + + if (!sela) { + if ((sela = selaCreate(0)) == NULL) + return (SELA *)ERROR_PTR("sela not made", __func__, NULL); + } + + /*--------------------------------------------------------------* + * Linear horizontal and vertical sels * + *--------------------------------------------------------------*/ + for (i = 0; i < num_linear; i++) { + size = basic_linear[i]; + sel = selCreateBrick(1, size, 0, size / 2, 1); + snprintf(name, L_BUF_SIZE, "sel_%dh", size); + selaAddSel(sela, sel, name, 0); + } + for (i = 0; i < num_linear; i++) { + size = basic_linear[i]; + sel = selCreateBrick(size, 1, size / 2, 0, 1); + snprintf(name, L_BUF_SIZE, "sel_%dv", size); + selaAddSel(sela, sel, name, 0); + } + + /*-----------------------------------------------------------* + * 2-d Bricks * + *-----------------------------------------------------------*/ + for (i = 2; i <= 5; i++) { + sel = selCreateBrick(i, i, i / 2, i / 2, 1); + snprintf(name, L_BUF_SIZE, "sel_%d", i); + selaAddSel(sela, sel, name, 0); + } + + /*-----------------------------------------------------------* + * Diagonals * + *-----------------------------------------------------------*/ + /* 0c 1 + 1 0 */ + sel = selCreateBrick(2, 2, 0, 0, 1); + selSetElement(sel, 0, 0, 0); + selSetElement(sel, 1, 1, 0); + selaAddSel(sela, sel, "sel_2dp", 0); + + /* 1c 0 + 0 1 */ + sel = selCreateBrick(2, 2, 0, 0, 1); + selSetElement(sel, 0, 1, 0); + selSetElement(sel, 1, 0, 0); + selaAddSel(sela, sel, "sel_2dm", 0); + + /* Diagonal, slope +, size 5 */ + sel = selCreate(5, 5, "sel_5dp"); + selSetOrigin(sel, 2, 2); + selSetElement(sel, 0, 4, 1); + selSetElement(sel, 1, 3, 1); + selSetElement(sel, 2, 2, 1); + selSetElement(sel, 3, 1, 1); + selSetElement(sel, 4, 0, 1); + selaAddSel(sela, sel, "sel_5dp", 0); + + /* Diagonal, slope -, size 5 */ + sel = selCreate(5, 5, "sel_5dm"); + selSetOrigin(sel, 2, 2); + selSetElement(sel, 0, 0, 1); + selSetElement(sel, 1, 1, 1); + selSetElement(sel, 2, 2, 1); + selSetElement(sel, 3, 3, 1); + selSetElement(sel, 4, 4, 1); + selaAddSel(sela, sel, "sel_5dm", 0); + + return sela; +} + + +/* ------------------------------------------------------------------- * + * Simple hit-miss structuring elements * + * ------------------------------------------------------------------- */ +/*! + * \brief selaAddHitMiss() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + */ +SELA * +selaAddHitMiss(SELA *sela) +{ +SEL *sel; + + if (!sela) { + if ((sela = selaCreate(0)) == NULL) + return (SELA *)ERROR_PTR("sela not made", __func__, NULL); + } + +#if 0 /* use just for testing */ + sel = selCreateBrick(3, 3, 1, 1, 2); + selaAddSel(sela, sel, "sel_bad", 0); +#endif + + + /*--------------------------------------------------------------* + * Isolated foreground pixel * + *--------------------------------------------------------------*/ + sel = selCreateBrick(3, 3, 1, 1, SEL_MISS); + selSetElement(sel, 1, 1, SEL_HIT); + selaAddSel(sela, sel, "sel_3hm", 0); + + /*--------------------------------------------------------------* + * Horizontal and vertical edges * + *--------------------------------------------------------------*/ + sel = selCreateBrick(2, 3, 0, 1, SEL_HIT); + selSetElement(sel, 1, 0, SEL_MISS); + selSetElement(sel, 1, 1, SEL_MISS); + selSetElement(sel, 1, 2, SEL_MISS); + selaAddSel(sela, sel, "sel_3de", 0); + + sel = selCreateBrick(2, 3, 1, 1, SEL_HIT); + selSetElement(sel, 0, 0, SEL_MISS); + selSetElement(sel, 0, 1, SEL_MISS); + selSetElement(sel, 0, 2, SEL_MISS); + selaAddSel(sela, sel, "sel_3ue", 0); + + sel = selCreateBrick(3, 2, 1, 0, SEL_HIT); + selSetElement(sel, 0, 1, SEL_MISS); + selSetElement(sel, 1, 1, SEL_MISS); + selSetElement(sel, 2, 1, SEL_MISS); + selaAddSel(sela, sel, "sel_3re", 0); + + sel = selCreateBrick(3, 2, 1, 1, SEL_HIT); + selSetElement(sel, 0, 0, SEL_MISS); + selSetElement(sel, 1, 0, SEL_MISS); + selSetElement(sel, 2, 0, SEL_MISS); + selaAddSel(sela, sel, "sel_3le", 0); + + /*--------------------------------------------------------------* + * Slanted edge * + *--------------------------------------------------------------*/ + sel = selCreateBrick(13, 6, 6, 2, SEL_DONT_CARE); + selSetElement(sel, 0, 3, SEL_MISS); + selSetElement(sel, 0, 5, SEL_HIT); + selSetElement(sel, 4, 2, SEL_MISS); + selSetElement(sel, 4, 4, SEL_HIT); + selSetElement(sel, 8, 1, SEL_MISS); + selSetElement(sel, 8, 3, SEL_HIT); + selSetElement(sel, 12, 0, SEL_MISS); + selSetElement(sel, 12, 2, SEL_HIT); + selaAddSel(sela, sel, "sel_sl1", 0); + + /*--------------------------------------------------------------* + * Corners * + * This allows for up to 3 missing edge pixels at the corner * + *--------------------------------------------------------------*/ + sel = selCreateBrick(4, 4, 1, 1, SEL_MISS); + selSetElement(sel, 1, 1, SEL_DONT_CARE); + selSetElement(sel, 1, 2, SEL_DONT_CARE); + selSetElement(sel, 2, 1, SEL_DONT_CARE); + selSetElement(sel, 1, 3, SEL_HIT); + selSetElement(sel, 2, 2, SEL_HIT); + selSetElement(sel, 2, 3, SEL_HIT); + selSetElement(sel, 3, 1, SEL_HIT); + selSetElement(sel, 3, 2, SEL_HIT); + selSetElement(sel, 3, 3, SEL_HIT); + selaAddSel(sela, sel, "sel_ulc", 0); + + sel = selCreateBrick(4, 4, 1, 2, SEL_MISS); + selSetElement(sel, 1, 1, SEL_DONT_CARE); + selSetElement(sel, 1, 2, SEL_DONT_CARE); + selSetElement(sel, 2, 2, SEL_DONT_CARE); + selSetElement(sel, 1, 0, SEL_HIT); + selSetElement(sel, 2, 0, SEL_HIT); + selSetElement(sel, 2, 1, SEL_HIT); + selSetElement(sel, 3, 0, SEL_HIT); + selSetElement(sel, 3, 1, SEL_HIT); + selSetElement(sel, 3, 2, SEL_HIT); + selaAddSel(sela, sel, "sel_urc", 0); + + sel = selCreateBrick(4, 4, 2, 1, SEL_MISS); + selSetElement(sel, 1, 1, SEL_DONT_CARE); + selSetElement(sel, 2, 1, SEL_DONT_CARE); + selSetElement(sel, 2, 2, SEL_DONT_CARE); + selSetElement(sel, 0, 1, SEL_HIT); + selSetElement(sel, 0, 2, SEL_HIT); + selSetElement(sel, 0, 3, SEL_HIT); + selSetElement(sel, 1, 2, SEL_HIT); + selSetElement(sel, 1, 3, SEL_HIT); + selSetElement(sel, 2, 3, SEL_HIT); + selaAddSel(sela, sel, "sel_llc", 0); + + sel = selCreateBrick(4, 4, 2, 2, SEL_MISS); + selSetElement(sel, 1, 2, SEL_DONT_CARE); + selSetElement(sel, 2, 1, SEL_DONT_CARE); + selSetElement(sel, 2, 2, SEL_DONT_CARE); + selSetElement(sel, 0, 0, SEL_HIT); + selSetElement(sel, 0, 1, SEL_HIT); + selSetElement(sel, 0, 2, SEL_HIT); + selSetElement(sel, 1, 0, SEL_HIT); + selSetElement(sel, 1, 1, SEL_HIT); + selSetElement(sel, 2, 0, SEL_HIT); + selaAddSel(sela, sel, "sel_lrc", 0); + + return sela; +} + + +/* ------------------------------------------------------------------- * + * Structuring elements for comparing with DWA operations * + * ------------------------------------------------------------------- */ +/*! + * \brief selaAddDwaLinear() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + * + * <pre> + * Notes: + * (1) Adds all linear (horizontal, vertical) sels from + * 2 to 63 pixels in length, which are the sizes over + * which dwa code can be generated. + * </pre> + */ +SELA * +selaAddDwaLinear(SELA *sela) +{ +char name[L_BUF_SIZE]; +l_int32 i; +SEL *sel; + + if (!sela) { + if ((sela = selaCreate(0)) == NULL) + return (SELA *)ERROR_PTR("sela not made", __func__, NULL); + } + + for (i = 2; i < 64; i++) { + sel = selCreateBrick(1, i, 0, i / 2, 1); + snprintf(name, L_BUF_SIZE, "sel_%dh", i); + selaAddSel(sela, sel, name, 0); + } + for (i = 2; i < 64; i++) { + sel = selCreateBrick(i, 1, i / 2, 0, 1); + snprintf(name, L_BUF_SIZE, "sel_%dv", i); + selaAddSel(sela, sel, name, 0); + } + return sela; +} + + +/*! + * \brief selaAddDwaCombs() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + * + * <pre> + * Notes: + * (1) Adds all comb (horizontal, vertical) Sels that are + * used in composite linear morphological operations + * up to 63 pixels in length, which are the sizes over + * which dwa code can be generated. + * </pre> + */ +SELA * +selaAddDwaCombs(SELA *sela) +{ +char name[L_BUF_SIZE]; +l_int32 i, f1, f2, prevsize, size; +SEL *selh, *selv; + + if (!sela) { + if ((sela = selaCreate(0)) == NULL) + return (SELA *)ERROR_PTR("sela not made", __func__, NULL); + } + + prevsize = 0; + for (i = 4; i < 64; i++) { + selectComposableSizes(i, &f1, &f2); + size = f1 * f2; + if (size == prevsize) + continue; + selectComposableSels(i, L_HORIZ, NULL, &selh); + if (selh) { + snprintf(name, L_BUF_SIZE, "sel_comb_%dh", size); + selaAddSel(sela, selh, name, 0); + } else { + L_ERROR("selh not made for i = %d\n", __func__, i); + } + selectComposableSels(i, L_VERT, NULL, &selv); + if (selv) { + snprintf(name, L_BUF_SIZE, "sel_comb_%dv", size); + selaAddSel(sela, selv, name, 0); + } else { + L_ERROR("selv not made for i = %d\n", __func__, i); + } + prevsize = size; + } + + return sela; +} + + +/* ------------------------------------------------------------------- * + * Structuring elements for the intersection of lines * + * ------------------------------------------------------------------- */ +/*! + * \brief selaAddCrossJunctions() + * + * \param[in] sela [optional] + * \param[in] hlsize length of each line of hits from origin + * \param[in] mdist distance of misses from the origin + * \param[in] norient number of orientations; max of 8 + * \param[in] debugflag 1 for debug output + * \return sela with additional sels, or NULL on error + * + * <pre> + * Notes: + * (1) Adds hitmiss Sels for the intersection of two lines. + * If the lines are very thin, they must be nearly orthogonal + * to register. + * (2) The number of Sels generated is equal to %norient. + * (3) If %norient == 2, this generates 2 Sels of crosses, each with + * two perpendicular lines of hits. One Sel has horizontal and + * vertical hits; the other has hits along lines at +-45 degrees. + * Likewise, if %norient == 3, this generates 3 Sels of crosses + * oriented at 30 degrees with each other. + * (4) It is suggested that %hlsize be chosen at least 1 greater + * than %mdist. Try values of (%hlsize, %mdist) such as + * (6,5), (7,6), (8,7), (9,7), etc. + * </pre> + */ +SELA * +selaAddCrossJunctions(SELA *sela, + l_float32 hlsize, + l_float32 mdist, + l_int32 norient, + l_int32 debugflag) +{ +char name[L_BUF_SIZE]; +l_int32 i, j, w, xc, yc; +l_float64 pi, halfpi, radincr, radang; +l_float64 angle; +PIX *pixc, *pixm, *pixt; +PIXA *pixa; +PTA *pta1, *pta2, *pta3, *pta4; +SEL *sel; + + if (hlsize <= 0) + return (SELA *)ERROR_PTR("hlsize not > 0", __func__, NULL); + if (norient < 1 || norient > 8) + return (SELA *)ERROR_PTR("norient not in [1, ... 8]", __func__, NULL); + + if (!sela) { + if ((sela = selaCreate(0)) == NULL) + return (SELA *)ERROR_PTR("sela not made", __func__, NULL); + } + + pi = 3.1415926535; + halfpi = 3.1415926535 / 2.0; + radincr = halfpi / (l_float64)norient; + w = (l_int32)(2.2 * (L_MAX(hlsize, mdist) + 0.5)); + if (w % 2 == 0) + w++; + xc = w / 2; + yc = w / 2; + + pixa = pixaCreate(norient); + for (i = 0; i < norient; i++) { + + /* Set the don't cares */ + pixc = pixCreate(w, w, 32); + pixSetAll(pixc); + + /* Add the green lines of hits */ + pixm = pixCreate(w, w, 1); + radang = (l_float32)i * radincr; + pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang); + pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + halfpi); + pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi); + pta4 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi + halfpi); + ptaJoin(pta1, pta2, 0, -1); + ptaJoin(pta1, pta3, 0, -1); + ptaJoin(pta1, pta4, 0, -1); + pixRenderPta(pixm, pta1, L_SET_PIXELS); + pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); + ptaDestroy(&pta1); + ptaDestroy(&pta2); + ptaDestroy(&pta3); + ptaDestroy(&pta4); + + /* Add red misses between the lines */ + for (j = 0; j < 4; j++) { + angle = radang + (j - 0.5) * halfpi; + pixSetPixel(pixc, xc + (l_int32)(mdist * cos(angle)), + yc + (l_int32)(mdist * sin(angle)), 0xff000000); + } + + /* Add dark green for origin */ + pixSetPixel(pixc, xc, yc, 0x00550000); + + /* Generate the sel */ + sel = selCreateFromColorPix(pixc, NULL); + snprintf(name, sizeof(name), "sel_cross_%d", i); + selaAddSel(sela, sel, name, 0); + + if (debugflag) { + pixt = pixScaleBySampling(pixc, 10.0, 10.0); + pixaAddPix(pixa, pixt, L_INSERT); + } + pixDestroy(&pixm); + pixDestroy(&pixc); + } + + if (debugflag) { + l_int32 w; + lept_mkdir("lept/sel"); + pixaGetPixDimensions(pixa, 0, &w, NULL, NULL); + pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 1, 0, 10, 2); + pixWriteDebug("/tmp/lept/sel/xsel1.png", pixt, IFF_PNG); + pixDisplay(pixt, 0, 100); + pixDestroy(&pixt); + pixt = selaDisplayInPix(sela, 15, 2, 20, 1); + pixWriteDebug("/tmp/lept/sel/xsel2.png", pixt, IFF_PNG); + pixDisplay(pixt, 500, 100); + pixDestroy(&pixt); + selaWriteStream(stderr, sela); + } + pixaDestroy(&pixa); + + return sela; +} + + +/*! + * \brief selaAddTJunctions() + * + * \param[in] sela [optional] + * \param[in] hlsize length of each line of hits from origin + * \param[in] mdist distance of misses from the origin + * \param[in] norient number of orientations; max of 8 + * \param[in] debugflag 1 for debug output + * \return sela with additional sels, or NULL on error + * + * <pre> + * Notes: + * (1) Adds hitmiss Sels for the T-junction of two lines. + * If the lines are very thin, they must be nearly orthogonal + * to register. + * (2) The number of Sels generated is 4 * %norient. + * (3) It is suggested that %hlsize be chosen at least 1 greater + * than %mdist. Try values of (%hlsize, %mdist) such as + * (6,5), (7,6), (8,7), (9,7), etc. + * </pre> + */ +SELA * +selaAddTJunctions(SELA *sela, + l_float32 hlsize, + l_float32 mdist, + l_int32 norient, + l_int32 debugflag) +{ +char name[L_BUF_SIZE]; +l_int32 i, j, k, w, xc, yc; +l_float64 pi, halfpi, radincr, jang, radang; +l_float64 angle[3], dist[3]; +PIX *pixc, *pixm, *pixt; +PIXA *pixa; +PTA *pta1, *pta2, *pta3; +SEL *sel; + + if (hlsize <= 2) + return (SELA *)ERROR_PTR("hlsizel not > 1", __func__, NULL); + if (norient < 1 || norient > 8) + return (SELA *)ERROR_PTR("norient not in [1, ... 8]", __func__, NULL); + + if (!sela) { + if ((sela = selaCreate(0)) == NULL) + return (SELA *)ERROR_PTR("sela not made", __func__, NULL); + } + + pi = 3.1415926535; + halfpi = 3.1415926535 / 2.0; + radincr = halfpi / (l_float32)norient; + w = (l_int32)(2.4 * (L_MAX(hlsize, mdist) + 0.5)); + if (w % 2 == 0) + w++; + xc = w / 2; + yc = w / 2; + + pixa = pixaCreate(4 * norient); + for (i = 0; i < norient; i++) { + for (j = 0; j < 4; j++) { /* 4 orthogonal orientations */ + jang = (l_float32)j * halfpi; + + /* Set the don't cares */ + pixc = pixCreate(w, w, 32); + pixSetAll(pixc); + + /* Add the green lines of hits */ + pixm = pixCreate(w, w, 1); + radang = (l_float32)i * radincr; + pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang); + pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, + jang + radang + halfpi); + pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, + jang + radang + pi); + ptaJoin(pta1, pta2, 0, -1); + ptaJoin(pta1, pta3, 0, -1); + pixRenderPta(pixm, pta1, L_SET_PIXELS); + pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); + ptaDestroy(&pta1); + ptaDestroy(&pta2); + ptaDestroy(&pta3); + + /* Add red misses between the lines */ + angle[0] = radang + jang - halfpi; + angle[1] = radang + jang + 0.5 * halfpi; + angle[2] = radang + jang + 1.5 * halfpi; + dist[0] = 0.8 * mdist; + dist[1] = dist[2] = mdist; + for (k = 0; k < 3; k++) { + pixSetPixel(pixc, xc + (l_int32)(dist[k] * cos(angle[k])), + yc + (l_int32)(dist[k] * sin(angle[k])), + 0xff000000); + } + + /* Add dark green for origin */ + pixSetPixel(pixc, xc, yc, 0x00550000); + + /* Generate the sel */ + sel = selCreateFromColorPix(pixc, NULL); + snprintf(name, sizeof(name), "sel_cross_%d", 4 * i + j); + selaAddSel(sela, sel, name, 0); + + if (debugflag) { + pixt = pixScaleBySampling(pixc, 10.0, 10.0); + pixaAddPix(pixa, pixt, L_INSERT); + } + pixDestroy(&pixm); + pixDestroy(&pixc); + } + } + + if (debugflag) { + l_int32 w; + lept_mkdir("lept/sel"); + pixaGetPixDimensions(pixa, 0, &w, NULL, NULL); + pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 4, 0, 10, 2); + pixWriteDebug("/tmp/lept/sel/tsel1.png", pixt, IFF_PNG); + pixDisplay(pixt, 0, 100); + pixDestroy(&pixt); + pixt = selaDisplayInPix(sela, 15, 2, 20, 4); + pixWriteDebug("/tmp/lept/sel/tsel2.png", pixt, IFF_PNG); + pixDisplay(pixt, 500, 100); + pixDestroy(&pixt); + selaWriteStream(stderr, sela); + } + pixaDestroy(&pixa); + + return sela; +} + + +/* -------------------------------------------------------------------------- * + * Structuring elements for connectivity-preserving thinning operations * + * -------------------------------------------------------------------------- */ + + /* ------------------------------------------------------------ + * These sels (and their rotated counterparts) are the useful + * 3x3 Sels for thinning. The notation is based on + * "Connectivity-preserving morphological image transformations," + * a version of which can be found at + * http://www.leptonica.com/papers/conn.pdf + * ------------------------------------------------------------ */ + + /* Sels for 4-connected thinning */ +static const char *sel_4_1 = " x" + "oCx" + " x"; +static const char *sel_4_2 = " x" + "oCx" + " o "; +static const char *sel_4_3 = " o " + "oCx" + " x"; +static const char *sel_4_4 = " o " + "oCx" + " o "; +static const char *sel_4_5 = " ox" + "oCx" + " o "; +static const char *sel_4_6 = " o " + "oCx" + " ox"; +static const char *sel_4_7 = " xx" + "oCx" + " o "; +static const char *sel_4_8 = " x" + "oCx" + "o x"; +static const char *sel_4_9 = "o x" + "oCx" + " x"; + + /* Sels for 8-connected thinning */ +static const char *sel_8_1 = " x " + "oCx" + " x "; +static const char *sel_8_2 = " x " + "oCx" + "o "; +static const char *sel_8_3 = "o " + "oCx" + " x "; +static const char *sel_8_4 = "o " + "oCx" + "o "; +static const char *sel_8_5 = "o x" + "oCx" + "o "; +static const char *sel_8_6 = "o " + "oCx" + "o x"; +static const char *sel_8_7 = " x " + "oCx" + "oo "; +static const char *sel_8_8 = " x " + "oCx" + "ox "; +static const char *sel_8_9 = "ox " + "oCx" + " x "; + + /* Sels for both 4 and 8-connected thinning */ +static const char *sel_48_1 = " xx" + "oCx" + "oo "; +static const char *sel_48_2 = "o x" + "oCx" + "o x"; + + +/*! + * \brief sela4ccThin() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + * + * <pre> + * Notes: + * (1) Adds the 9 basic sels for 4-cc thinning. + * </pre> + */ +SELA * +sela4ccThin(SELA *sela) +{ +SEL *sel; + + if (!sela) sela = selaCreate(9); + + sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_2, 3, 3, "sel_4_2"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_3, 3, 3, "sel_4_3"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_4, 3, 3, "sel_4_4"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_5, 3, 3, "sel_4_5"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_6, 3, 3, "sel_4_6"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_7, 3, 3, "sel_4_7"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_8, 3, 3, "sel_4_8"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_9, 3, 3, "sel_4_9"); + selaAddSel(sela, sel, NULL, 0); + + return sela; +} + + +/*! + * \brief sela8ccThin() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + * + * <pre> + * Notes: + * (1) Adds the 9 basic sels for 8-cc thinning. + * </pre> + */ +SELA * +sela8ccThin(SELA *sela) +{ +SEL *sel; + + if (!sela) sela = selaCreate(9); + + sel = selCreateFromString(sel_8_1, 3, 3, "sel_8_1"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_4, 3, 3, "sel_8_4"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_7, 3, 3, "sel_8_7"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_8, 3, 3, "sel_8_8"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_9, 3, 3, "sel_8_9"); + selaAddSel(sela, sel, NULL, 0); + + return sela; +} + + +/*! + * \brief sela4and8ccThin() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + * + * <pre> + * Notes: + * (1) Adds the 2 basic sels for either 4-cc or 8-cc thinning. + * </pre> + */ +SELA * +sela4and8ccThin(SELA *sela) +{ +SEL *sel; + + if (!sela) sela = selaCreate(2); + + sel = selCreateFromString(sel_48_1, 3, 3, "sel_48_1"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_48_2, 3, 3, "sel_48_2"); + selaAddSel(sela, sel, NULL, 0); + + return sela; +} + + +/* -------------------------------------------------------------------------- * + * Other structuring elements * + * -------------------------------------------------------------------------- */ +/*! + * \brief selMakePlusSign() + * + * \param[in] size side of containing square + * \param[in] linewidth of lines + * \return sel, or NULL on error + * + * <pre> + * Notes: + * (1) Useful for debugging to show location of selected pixels. + * (2) See displaySelectedPixels() for an example of use. + * </pre> + */ +SEL * +selMakePlusSign(l_int32 size, + l_int32 linewidth) +{ +PIX *pix; +SEL *sel; + + if (size < 3 || linewidth > size) + return (SEL *)ERROR_PTR("invalid input", __func__, NULL); + + pix = pixCreate(size, size, 1); + pixRenderLine(pix, size / 2, 0, size / 2, size - 1, + linewidth, L_SET_PIXELS); + pixRenderLine(pix, 0, size / 2, size, size / 2, + linewidth, L_SET_PIXELS); + sel = selCreateFromPix(pix, size / 2, size / 2, "plus_sign"); + pixDestroy(&pix); + return sel; +}
