Leptonica 1.68
C Image Processing Library
|
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