Leptonica 1.68
C Image Processing Library

ccthin.c

Go to the documentation of this file.
00001 /*====================================================================*
00002   -  Copyright (C) 2001 Leptonica.  All rights reserved.
00003   -  This software is distributed in the hope that it will be
00004   -  useful, but with NO WARRANTY OF ANY KIND.
00005   -  No author or distributor accepts responsibility to anyone for the
00006   -  consequences of using this software, or for whether it serves any
00007   -  particular purpose or works at all, unless he or she says so in
00008   -  writing.  Everyone is granted permission to copy, modify and
00009   -  redistribute this source code, for commercial or non-commercial
00010   -  purposes, with the following restrictions: (1) the origin of this
00011   -  source code must not be misrepresented; (2) modified versions must
00012   -  be plainly marked as such; and (3) this notice may not be removed
00013   -  or altered from any source or modified source distribution.
00014   *====================================================================*/
00015 
00016 /*
00017  *  ccthin.c
00018  *
00019  *     PIX    *pixThin()
00020  *     PIX    *pixThinGeneral()
00021  *     PIX    *pixThinExamples()
00022  */
00023 
00024 #include <stdio.h>
00025 #include <stdlib.h>
00026 #include "allheaders.h"
00027 
00028 
00029     /* ------------------------------------------------------------
00030      * These sels (and their rotated counterparts) are the useful
00031      * 3x3 Sels for thinning.   The notation is based on
00032      * "Connectivity-preserving morphological image transformations,"
00033      * a version of which can be found at
00034      *           http://www.leptonica.com/papers/conn.pdf
00035      * ------------------------------------------------------------ */
00036 
00037     /* Sels for 4-connected thinning */
00038 static const char *sel_4_1 = "  x"
00039                              "oCx"
00040                              "  x";
00041 
00042 static const char *sel_4_2 = "  x"
00043                              "oCx"
00044                              " o ";
00045 
00046 static const char *sel_4_3 = " o "
00047                              "oCx"
00048                              "  x";
00049 
00050 static const char *sel_4_4 = " o "
00051                              "oCx"
00052                              " o ";
00053 
00054 static const char *sel_4_5 = " ox"
00055                              "oCx"
00056                              " o ";
00057 
00058 static const char *sel_4_6 = " o "
00059                              "oCx"
00060                              " ox";
00061 
00062 static const char *sel_4_7 = " xx"
00063                              "oCx"
00064                              " o ";
00065 
00066 static const char *sel_4_8 = "  x"
00067                              "oCx"
00068                              "o x";
00069 
00070 static const char *sel_4_9 = "o x"
00071                              "oCx"
00072                              "  x";
00073 
00074     /* Sels for 8-connected thinning */
00075 static const char *sel_8_1 = " x "
00076                              "oCx"
00077                              " x ";
00078 
00079 static const char *sel_8_2 = " x "
00080                              "oCx"
00081                              "o  ";
00082 
00083 static const char *sel_8_3 = "o  "
00084                              "oCx"
00085                              " x ";
00086 
00087 static const char *sel_8_4 = "o  "
00088                              "oCx"
00089                              "o  ";
00090 
00091 static const char *sel_8_5 = "o x"
00092                              "oCx"
00093                              "o  ";
00094 
00095 static const char *sel_8_6 = "o  "
00096                              "oCx"
00097                              "o x";
00098 
00099 static const char *sel_8_7 = " x "
00100                              "oCx"
00101                              "oo ";
00102 
00103 static const char *sel_8_8 = " x "
00104                              "oCx"
00105                              "ox ";
00106 
00107 static const char *sel_8_9 = "ox "
00108                              "oCx"
00109                              " x ";
00110 
00111     /* Sels for both 4 and 8-connected thinning */
00112 static const char *sel_48_1 = " xx"
00113                               "oCx"
00114                               "oo ";
00115 
00116 static const char *sel_48_2 = "o x"
00117                               "oCx"
00118                               "o x";
00119 
00120 #ifndef NO_CONSOLE_IO
00121 #define  DEBUG_SELS     0
00122 #endif   /* ~NO_CONSOLE_IO */
00123 
00124 
00125 /*----------------------------------------------------------------*
00126  *                      CC-preserving thinning                    *
00127  *----------------------------------------------------------------*/
00128 /*!
00129  *  pixThin()
00130  *
00131  *      Input:  pixs (1 bpp)
00132  *              type (L_THIN_FG, L_THIN_BG)
00133  *              connectivity (4 or 8)
00134  *              maxiters (max number of iters allowed; use 0 to iterate
00135  *                        until completion)
00136  *      Return: pixd, or null on error
00137  *
00138  *  Notes:
00139  *      (1) See "Connectivity-preserving morphological image transformations,"
00140  *          Dan S. Bloomberg, in SPIE Visual Communications and Image
00141  *          Processing, Conference 1606, pp. 320-334, November 1991,
00142  *          Boston, MA.   A web version is available at
00143  *              http://www.leptonica.com/papers/conn.pdf
00144  *      (2) We implement here two of the best iterative
00145  *          morphological thinning algorithms, for 4 c.c and 8 c.c.
00146  *          Each iteration uses a mixture of parallel operations
00147  *          (using several different 3x3 Sels) and serial operations.
00148  *          Specifically, each thinning iteration consists of
00149  *          four sequential thinnings from each of four directions.
00150  *          Each of these thinnings is a parallel composite
00151  *          operation, where the union of a set of HMTs are set
00152  *          subtracted from the input.  For 4-cc thinning, we
00153  *          use 3 HMTs in parallel, and for 8-cc thinning we use 4 HMTs.
00154  *      (3) A "good" thinning algorithm is one that generates a skeleton
00155  *          that is near the medial axis and has neither pruned
00156  *          real branches nor left extra dendritic branches.
00157  *      (4) To thin the foreground, which is the usual situation,
00158  *          use type == L_THIN_FG.  Thickening the foreground is equivalent
00159  *          to thinning the background (type == L_THIN_BG), where the
00160  *          opposite connectivity gets preserved.  For example, to thicken
00161  *          the fg using 4-connectivity, we thin the bg using Sels that
00162  *          preserve 8-connectivity.
00163  */
00164 PIX *
00165 pixThin(PIX     *pixs,
00166         l_int32  type,
00167         l_int32  connectivity,
00168         l_int32  maxiters)
00169 {
00170 PIX   *pixd;
00171 SEL   *sel;
00172 SELA  *sela;
00173 
00174     PROCNAME("pixThin");
00175 
00176     if (!pixs)
00177         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00178     if (pixGetDepth(pixs) != 1)
00179         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00180     if (type != L_THIN_FG && type != L_THIN_BG)
00181         return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL);
00182     if (connectivity != 4 && connectivity != 8)
00183         return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
00184     if (maxiters == 0) maxiters = 10000;
00185 
00186     sela = selaCreate(4);
00187     if (connectivity == 4) {
00188         sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1");
00189         selaAddSel(sela, sel, NULL, 0);
00190         sel = selCreateFromString(sel_4_2, 3, 3, "sel_4_2");
00191         selaAddSel(sela, sel, NULL, 0);
00192         sel = selCreateFromString(sel_4_3, 3, 3, "sel_4_3");
00193         selaAddSel(sela, sel, NULL, 0);
00194     } else {  /* connectivity == 8 */
00195         sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
00196         selaAddSel(sela, sel, NULL, 0);
00197         sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
00198         selaAddSel(sela, sel, NULL, 0);
00199         sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
00200         selaAddSel(sela, sel, NULL, 0);
00201         sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
00202         selaAddSel(sela, sel, NULL, 0);
00203     }
00204 
00205     pixd = pixThinGeneral(pixs, type, sela, maxiters);
00206 
00207     selaDestroy(&sela);
00208     return pixd;
00209 }
00210 
00211 
00212 /*!
00213  *  pixThinGeneral()
00214  *
00215  *      Input:  pixs (1 bpp)
00216  *              type (L_THIN_FG, L_THIN_BG)
00217  *              sela (of Sels for parallel composite HMTs)
00218  *              maxiters (max number of iters allowed; use 0 to iterate
00219  *                        until completion)
00220  *      Return: pixd, or null on error
00221  *
00222  *  Notes:
00223  *      (1) See notes in pixThin().  That function chooses among
00224  *          the best of the Sels for thinning.
00225  *      (2) This is a general function that takes a Sela of HMTs
00226  *          that are used in parallel for thinning from each
00227  *          of four directions.  One iteration consists of four
00228  *          such parallel thins.
00229  */
00230 PIX *
00231 pixThinGeneral(PIX     *pixs,
00232                l_int32  type,
00233                SELA    *sela,
00234                l_int32  maxiters)
00235 {
00236 l_int32  i, j, r, nsels, same;
00237 PIXA    *pixahmt;
00238 PIX    **pixhmt;  /* array owned by pixahmt; do not destroy! */
00239 PIX     *pixd, *pixt;
00240 SEL     *sel, *selr;
00241 
00242     PROCNAME("pixThinGeneral");
00243 
00244     if (!pixs)
00245         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00246     if (pixGetDepth(pixs) != 1)
00247         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00248     if (type != L_THIN_FG && type != L_THIN_BG)
00249         return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL);
00250     if (!sela)
00251         return (PIX *)ERROR_PTR("sela not defined", procName, NULL);
00252     if (maxiters == 0) maxiters = 10000;
00253 
00254         /* Set up array of temp pix to hold hmts */
00255     nsels = selaGetCount(sela);
00256     pixahmt = pixaCreate(nsels);
00257     for (i = 0; i < nsels; i++) {
00258         pixt = pixCreateTemplate(pixs);
00259         pixaAddPix(pixahmt, pixt, L_INSERT);
00260     }
00261     pixhmt = pixaGetPixArray(pixahmt);
00262     if (!pixhmt)
00263         return (PIX *)ERROR_PTR("pixhmt array not made", procName, NULL);
00264     
00265 #if  DEBUG_SELS
00266     pixt = selaDisplayInPix(sela, 35, 3, 15, 4);
00267     pixDisplayWithTitle(pixt, 100, 100, "allsels", 1);
00268     pixDestroy(&pixt);
00269 #endif  /* DEBUG_SELS */
00270 
00271         /* Set up initial image for fg thinning */
00272     if (type == L_THIN_FG)
00273         pixd = pixCopy(NULL, pixs);
00274     else  /* bg thinning */
00275         pixd = pixInvert(NULL, pixs);
00276 
00277         /* Thin the fg, with up to maxiters iterations */
00278     for (i = 0; i < maxiters; i++) {
00279         pixt = pixCopy(NULL, pixd);  /* test for completion */
00280         for (r = 0; r < 4; r++) {  /* over 90 degree rotations of Sels */
00281             for (j = 0; j < nsels; j++) {  /* over individual sels in sela */
00282                 sel = selaGetSel(sela, j);  /* not a copy */
00283                 selr = selRotateOrth(sel, r);
00284                 pixHMT(pixhmt[j], pixd, selr);
00285                 selDestroy(&selr);
00286                 if (j > 0)
00287                     pixOr(pixhmt[0], pixhmt[0], pixhmt[j]);  /* accum result */
00288             }
00289             pixSubtract(pixd, pixd, pixhmt[0]);  /* remove result */
00290         }
00291         pixEqual(pixd, pixt, &same);
00292         pixDestroy(&pixt);
00293         if (same) {
00294             L_INFO_INT("%d iterations to completion", procName, i);
00295             break;
00296         }
00297     }
00298         
00299     if (type == L_THIN_BG)
00300         pixInvert(pixd, pixd);
00301 
00302     pixaDestroy(&pixahmt);
00303     return pixd;
00304 }
00305 
00306 
00307 /*!
00308  *  pixThinExamples()
00309  *
00310  *      Input:  pixs (1 bpp)
00311  *              type (L_THIN_FG, L_THIN_BG)
00312  *              index (into specific examples; valid 1-9; see notes)
00313  *              maxiters (max number of iters allowed; use 0 to iterate
00314  *                        until completion)
00315  *              selfile (<optional> filename for output sel display)
00316  *      Return: pixd, or null on error
00317  *
00318  *  Notes:
00319  *      (1) See notes in pixThin().  The examples are taken from
00320  *          the paper referenced there.
00321  *      (2) Here we allow specific sets of HMTs to be used in
00322  *          parallel for thinning from each of four directions.
00323  *          One iteration consists of four such parallel thins.
00324  *      (3) The examples are indexed as follows:
00325  *          Thinning  (e.g., run to completion):
00326  *              index = 1     sel_4_1, sel_4_5, sel_4_6
00327  *              index = 2     sel_4_1, sel_4_7, sel_4_7_rot
00328  *              index = 3     sel_48_1, sel_48_1_rot, sel_48_2
00329  *              index = 4     sel_8_2, sel_8_3, sel_48_2
00330  *              index = 5     sel_8_1, sel_8_5, sel_8_6
00331  *              index = 6     sel_8_2, sel_8_3, sel_8_8, sel_8_9
00332  *              index = 7     sel_8_5, sel_8_6, sel_8_7, sel_8_7_rot
00333  *          Thickening:
00334  *              index = 8     sel_4_2, sel_4_3 (e.g,, do just a few iterations)
00335  *              index = 9     sel_8_4 (e.g., do just a few iterations)
00336  */
00337 PIX *
00338 pixThinExamples(PIX         *pixs,
00339                 l_int32      type,
00340                 l_int32      index,
00341                 l_int32      maxiters,
00342                 const char  *selfile)
00343 {
00344 PIX   *pixd, *pixt;
00345 SEL   *sel;
00346 SELA  *sela;
00347 
00348     PROCNAME("pixThinExamples");
00349 
00350     if (!pixs)
00351         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00352     if (pixGetDepth(pixs) != 1)
00353         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00354     if (type != L_THIN_FG && type != L_THIN_BG)
00355         return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL);
00356     if (index < 1 || index > 9)
00357         return (PIX *)ERROR_PTR("invalid index", procName, NULL);
00358     if (maxiters == 0) maxiters = 10000;
00359 
00360     switch(index)
00361     {
00362     case 1:
00363         sela = selaCreate(3);
00364         sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1");
00365         selaAddSel(sela, sel, NULL, 0);
00366         sel = selCreateFromString(sel_4_5, 3, 3, "sel_4_5");
00367         selaAddSel(sela, sel, NULL, 0);
00368         sel = selCreateFromString(sel_4_6, 3, 3, "sel_4_6");
00369         selaAddSel(sela, sel, NULL, 0);
00370         break;
00371     case 2:
00372         sela = selaCreate(3);
00373         sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1");
00374         selaAddSel(sela, sel, NULL, 0);
00375         sel = selCreateFromString(sel_4_7, 3, 3, "sel_4_7");
00376         selaAddSel(sela, sel, NULL, 0);
00377         sel = selRotateOrth(sel, 1);
00378         selaAddSel(sela, sel, "sel_4_7_rot", 0);
00379         break;
00380     case 3:
00381         sela = selaCreate(3);
00382         sel = selCreateFromString(sel_48_1, 3, 3, "sel_48_1");
00383         selaAddSel(sela, sel, NULL, 0);
00384         sel = selRotateOrth(sel, 1);
00385         selaAddSel(sela, sel, "sel_48_1_rot", 0);
00386         sel = selCreateFromString(sel_48_2, 3, 3, "sel_48_2");
00387         selaAddSel(sela, sel, NULL, 0);
00388         break;
00389     case 4:
00390         sela = selaCreate(3);
00391         sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
00392         selaAddSel(sela, sel, NULL, 0);
00393         sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
00394         selaAddSel(sela, sel, NULL, 0);
00395         sel = selCreateFromString(sel_48_2, 3, 3, "sel_48_2");
00396         selaAddSel(sela, sel, NULL, 0);
00397         break;
00398     case 5:
00399         sela = selaCreate(3);
00400         sel = selCreateFromString(sel_8_1, 3, 3, "sel_8_1");
00401         selaAddSel(sela, sel, NULL, 0);
00402         sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
00403         selaAddSel(sela, sel, NULL, 0);
00404         sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
00405         selaAddSel(sela, sel, NULL, 0);
00406         break;
00407     case 6:
00408         sela = selaCreate(4);
00409         sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
00410         selaAddSel(sela, sel, NULL, 0);
00411         sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
00412         selaAddSel(sela, sel, NULL, 0);
00413         sel = selCreateFromString(sel_8_8, 3, 3, "sel_8_8");
00414         selaAddSel(sela, sel, NULL, 0);
00415         sel = selCreateFromString(sel_8_9, 3, 3, "sel_8_9");
00416         selaAddSel(sela, sel, NULL, 0);
00417         break;
00418     case 7:
00419         sela = selaCreate(4);
00420         sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
00421         selaAddSel(sela, sel, NULL, 0);
00422         sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
00423         selaAddSel(sela, sel, NULL, 0);
00424         sel = selCreateFromString(sel_8_7, 3, 3, "sel_8_7");
00425         selaAddSel(sela, sel, NULL, 0);
00426         sel = selRotateOrth(sel, 1);
00427         selaAddSel(sela, sel, "sel_8_7_rot", 0);
00428         break;
00429     case 8:  /* thicken for this one; just a few iterations */
00430         sela = selaCreate(2);
00431         sel = selCreateFromString(sel_4_2, 3, 3, "sel_4_2");
00432         selaAddSel(sela, sel, NULL, 0);
00433         sel = selCreateFromString(sel_4_3, 3, 3, "sel_4_3");
00434         selaAddSel(sela, sel, NULL, 0);
00435         pixt = pixThinGeneral(pixs, type, sela, maxiters);
00436         pixd = pixRemoveBorderConnComps(pixt, 4);
00437         pixDestroy(&pixt);
00438         break;
00439     case 9:  /* thicken for this one; just a few iterations */
00440         sela = selaCreate(1);
00441         sel = selCreateFromString(sel_8_4, 3, 3, "sel_8_4");
00442         selaAddSel(sela, sel, NULL, 0);
00443         pixt = pixThinGeneral(pixs, type, sela, maxiters);
00444         pixd = pixRemoveBorderConnComps(pixt, 4);
00445         pixDestroy(&pixt);
00446         break;
00447     default:
00448         return (PIX *)ERROR_PTR("invalid index", procName, NULL);
00449     }
00450 
00451     if (index <= 7)
00452         pixd = pixThinGeneral(pixs, type, sela, maxiters);
00453 
00454         /* Optionally display the sels */
00455     if (selfile) {
00456         pixt = selaDisplayInPix(sela, 35, 3, 15, 4);
00457         pixWrite(selfile, pixt, IFF_PNG);
00458         pixDestroy(&pixt);
00459     }
00460 
00461     selaDestroy(&sela);
00462     return pixd;
00463 }
00464 
00465 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines