Leptonica 1.82.0
Image processing and image analysis suite
ccthin.c
Go to the documentation of this file.
1/*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 -
4 - Redistribution and use in source and binary forms, with or without
5 - modification, are permitted provided that the following conditions
6 - are met:
7 - 1. Redistributions of source code must retain the above copyright
8 - notice, this list of conditions and the following disclaimer.
9 - 2. Redistributions in binary form must reproduce the above
10 - copyright notice, this list of conditions and the following
11 - disclaimer in the documentation and/or other materials
12 - provided with the distribution.
13 -
14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *====================================================================*/
26
38#ifdef HAVE_CONFIG_H
39#include <config_auto.h>
40#endif /* HAVE_CONFIG_H */
41
42#include "allheaders.h"
43
44 /* ------------------------------------------------------------
45 * The sels used here (and their rotated counterparts) are the
46 * useful 3x3 Sels for thinning. They are defined in sel2.c,
47 * and the sets are constructed in selaMakeThinSets().
48 * The notation is based on "Connectivity-preserving morphological
49 * image transformations", a version of which can be found at
50 * http://www.leptonica.com/papers/conn.pdf
51 * ------------------------------------------------------------ */
52
53/*----------------------------------------------------------------*
54 * CC-preserving thinning *
55 *----------------------------------------------------------------*/
71PIXA *
73 l_int32 type,
74 l_int32 connectivity,
75 l_int32 maxiters)
76{
77l_int32 i, n, d, same;
78PIX *pix1, *pix2;
79PIXA *pixad;
80SELA *sela;
81
82 PROCNAME("pixaThinConnected");
83
84 if (!pixas)
85 return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
86 if (type != L_THIN_FG && type != L_THIN_BG)
87 return (PIXA *)ERROR_PTR("invalid fg/bg type", procName, NULL);
88 if (connectivity != 4 && connectivity != 8)
89 return (PIXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
90 if (maxiters == 0) maxiters = 10000;
91
92 pixaVerifyDepth(pixas, &same, &d);
93 if (d != 1)
94 return (PIXA *)ERROR_PTR("pix are not all 1 bpp", procName, NULL);
95
96 if (connectivity == 4)
97 sela = selaMakeThinSets(1, 0);
98 else /* connectivity == 8 */
99 sela = selaMakeThinSets(5, 0);
100
101 n = pixaGetCount(pixas);
102 pixad = pixaCreate(n);
103 for (i = 0; i < n; i++) {
104 pix1 = pixaGetPix(pixas, i, L_CLONE);
105 pix2 = pixThinConnectedBySet(pix1, type, sela, maxiters);
106 pixaAddPix(pixad, pix2, L_INSERT);
107 pixDestroy(&pix1);
108 }
109
110 selaDestroy(&sela);
111 return pixad;
112}
113
114
161PIX *
163 l_int32 type,
164 l_int32 connectivity,
165 l_int32 maxiters)
166{
167PIX *pixd;
168SELA *sela;
169
170 PROCNAME("pixThinConnected");
171
172 if (!pixs)
173 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
174 if (pixGetDepth(pixs) != 1)
175 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
176 if (type != L_THIN_FG && type != L_THIN_BG)
177 return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL);
178 if (connectivity != 4 && connectivity != 8)
179 return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
180 if (maxiters == 0) maxiters = 10000;
181
182 if (connectivity == 4)
183 sela = selaMakeThinSets(1, 0);
184 else /* connectivity == 8 */
185 sela = selaMakeThinSets(5, 0);
186
187 pixd = pixThinConnectedBySet(pixs, type, sela, maxiters);
188
189 selaDestroy(&sela);
190 return pixd;
191}
192
193
223PIX *
225 l_int32 type,
226 SELA *sela,
227 l_int32 maxiters)
228{
229l_int32 i, j, r, nsels, same;
230PIXA *pixahmt;
231PIX **pixhmt; /* array owned by pixahmt; do not destroy! */
232PIX *pix1, *pix2, *pixd;
233SEL *sel, *selr;
234
235 PROCNAME("pixThinConnectedBySet");
236
237 if (!pixs)
238 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
239 if (pixGetDepth(pixs) != 1)
240 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
241 if (type != L_THIN_FG && type != L_THIN_BG)
242 return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL);
243 if (!sela)
244 return (PIX *)ERROR_PTR("sela not defined", procName, NULL);
245 if (maxiters == 0) maxiters = 10000;
246
247 /* Set up array of temp pix to hold hmts */
248 nsels = selaGetCount(sela);
249 pixahmt = pixaCreate(nsels);
250 for (i = 0; i < nsels; i++) {
251 pix1 = pixCreateTemplate(pixs);
252 pixaAddPix(pixahmt, pix1, L_INSERT);
253 }
254 pixhmt = pixaGetPixArray(pixahmt);
255 if (!pixhmt) {
256 pixaDestroy(&pixahmt);
257 return (PIX *)ERROR_PTR("pixhmt array not made", procName, NULL);
258 }
259
260 /* Set up initial image for fg thinning */
261 if (type == L_THIN_FG)
262 pixd = pixCopy(NULL, pixs);
263 else /* bg thinning */
264 pixd = pixInvert(NULL, pixs);
265
266 /* Thin the fg, with up to maxiters iterations */
267 for (i = 0; i < maxiters; i++) {
268 pix1 = pixCopy(NULL, pixd); /* test for completion */
269 for (r = 0; r < 4; r++) { /* over 90 degree rotations of Sels */
270 for (j = 0; j < nsels; j++) { /* over individual sels in sela */
271 sel = selaGetSel(sela, j); /* not a copy */
272 selr = selRotateOrth(sel, r);
273 pixHMT(pixhmt[j], pixd, selr);
274 selDestroy(&selr);
275 if (j > 0)
276 pixOr(pixhmt[0], pixhmt[0], pixhmt[j]); /* accum result */
277 }
278 pixSubtract(pixd, pixd, pixhmt[0]); /* remove result */
279 }
280 pixEqual(pixd, pix1, &same);
281 pixDestroy(&pix1);
282 if (same) {
283/* L_INFO("%d iterations to completion\n", procName, i); */
284 break;
285 }
286 }
287
288 /* This is a bit tricky. If we're thickening the foreground, then
289 * we get a fg border of thickness equal to the number of
290 * iterations. This border is connected to all components that
291 * were initially touching the border, but as it grows, it does
292 * not touch other growing components -- it leaves a 1 pixel wide
293 * background between it and the growing components, and that
294 * thin background prevents the components from growing further.
295 * This border can be entirely removed as follows:
296 * (1) Subtract the original (unthickened) image pixs from the
297 * thickened image. This removes the pixels that were originally
298 * touching the border.
299 * (2) Get all remaining pixels that are connected to the border.
300 * (3) Remove those pixels from the thickened image. */
301 if (type == L_THIN_BG) {
302 pixInvert(pixd, pixd); /* finish with duality */
303 pix1 = pixSubtract(NULL, pixd, pixs);
304 pix2 = pixExtractBorderConnComps(pix1, 4);
305 pixSubtract(pixd, pixd, pix2);
306 pixDestroy(&pix1);
307 pixDestroy(&pix2);
308 }
309
310 pixaDestroy(&pixahmt);
311 return pixd;
312}
313
314
344SELA *
345selaMakeThinSets(l_int32 index,
346 l_int32 debug)
347{
348SEL *sel;
349SELA *sela1, *sela2, *sela3;
350
351 PROCNAME("selaMakeThinSets");
352
353 if (index < 1 || index > 11)
354 return (SELA *)ERROR_PTR("invalid index", procName, NULL);
355
356 sela2 = selaCreate(4);
357 switch(index)
358 {
359 case 1:
360 sela1 = sela4ccThin(NULL);
361 selaFindSelByName(sela1, "sel_4_1", NULL, &sel);
362 selaAddSel(sela2, sel, NULL, L_COPY);
363 selaFindSelByName(sela1, "sel_4_2", NULL, &sel);
364 selaAddSel(sela2, sel, NULL, L_COPY);
365 selaFindSelByName(sela1, "sel_4_3", NULL, &sel);
366 selaAddSel(sela2, sel, NULL, L_COPY);
367 break;
368 case 2:
369 sela1 = sela4ccThin(NULL);
370 selaFindSelByName(sela1, "sel_4_1", NULL, &sel);
371 selaAddSel(sela2, sel, NULL, L_COPY);
372 selaFindSelByName(sela1, "sel_4_5", NULL, &sel);
373 selaAddSel(sela2, sel, NULL, L_COPY);
374 selaFindSelByName(sela1, "sel_4_6", NULL, &sel);
375 selaAddSel(sela2, sel, NULL, L_COPY);
376 break;
377 case 3:
378 sela1 = sela4ccThin(NULL);
379 selaFindSelByName(sela1, "sel_4_1", NULL, &sel);
380 selaAddSel(sela2, sel, NULL, L_COPY);
381 selaFindSelByName(sela1, "sel_4_7", NULL, &sel);
382 selaAddSel(sela2, sel, NULL, L_COPY);
383 sel = selRotateOrth(sel, 1);
384 selaAddSel(sela2, sel, "sel_4_7_rot", L_INSERT);
385 break;
386 case 4:
387 sela1 = sela4and8ccThin(NULL);
388 selaFindSelByName(sela1, "sel_48_1", NULL, &sel);
389 selaAddSel(sela2, sel, NULL, L_COPY);
390 sel = selRotateOrth(sel, 1);
391 selaAddSel(sela2, sel, "sel_48_1_rot", L_INSERT);
392 selaFindSelByName(sela1, "sel_48_2", NULL, &sel);
393 selaAddSel(sela2, sel, NULL, L_COPY);
394 break;
395 case 5:
396 sela1 = sela8ccThin(NULL);
397 selaFindSelByName(sela1, "sel_8_2", NULL, &sel);
398 selaAddSel(sela2, sel, NULL, L_COPY);
399 selaFindSelByName(sela1, "sel_8_3", NULL, &sel);
400 selaAddSel(sela2, sel, NULL, L_COPY);
401 selaFindSelByName(sela1, "sel_8_5", NULL, &sel);
402 selaAddSel(sela2, sel, NULL, L_COPY);
403 selaFindSelByName(sela1, "sel_8_6", NULL, &sel);
404 selaAddSel(sela2, sel, NULL, L_COPY);
405 break;
406 case 6:
407 sela1 = sela8ccThin(NULL);
408 sela3 = sela4and8ccThin(NULL);
409 selaFindSelByName(sela1, "sel_8_2", NULL, &sel);
410 selaAddSel(sela2, sel, NULL, L_COPY);
411 selaFindSelByName(sela1, "sel_8_3", NULL, &sel);
412 selaAddSel(sela2, sel, NULL, L_COPY);
413 selaFindSelByName(sela3, "sel_48_2", NULL, &sel);
414 selaAddSel(sela2, sel, NULL, L_COPY);
415 selaDestroy(&sela3);
416 break;
417 case 7:
418 sela1 = sela8ccThin(NULL);
419 selaFindSelByName(sela1, "sel_8_1", NULL, &sel);
420 selaAddSel(sela2, sel, NULL, L_COPY);
421 selaFindSelByName(sela1, "sel_8_5", NULL, &sel);
422 selaAddSel(sela2, sel, NULL, L_COPY);
423 selaFindSelByName(sela1, "sel_8_6", NULL, &sel);
424 selaAddSel(sela2, sel, NULL, L_COPY);
425 break;
426 case 8:
427 sela1 = sela8ccThin(NULL);
428 selaFindSelByName(sela1, "sel_8_2", NULL, &sel);
429 selaAddSel(sela2, sel, NULL, L_COPY);
430 selaFindSelByName(sela1, "sel_8_3", NULL, &sel);
431 selaAddSel(sela2, sel, NULL, L_COPY);
432 selaFindSelByName(sela1, "sel_8_8", NULL, &sel);
433 selaAddSel(sela2, sel, NULL, L_COPY);
434 selaFindSelByName(sela1, "sel_8_9", NULL, &sel);
435 selaAddSel(sela2, sel, NULL, L_COPY);
436 break;
437 case 9:
438 sela1 = sela8ccThin(NULL);
439 selaFindSelByName(sela1, "sel_8_5", NULL, &sel);
440 selaAddSel(sela2, sel, NULL, L_COPY);
441 selaFindSelByName(sela1, "sel_8_6", NULL, &sel);
442 selaAddSel(sela2, sel, NULL, L_COPY);
443 selaFindSelByName(sela1, "sel_8_7", NULL, &sel);
444 selaAddSel(sela2, sel, NULL, L_COPY);
445 sel = selRotateOrth(sel, 1);
446 selaAddSel(sela2, sel, "sel_8_7_rot", L_INSERT);
447 break;
448 case 10: /* thicken for this one; use just a few iterations */
449 sela1 = sela4ccThin(NULL);
450 selaFindSelByName(sela1, "sel_4_2", NULL, &sel);
451 selaAddSel(sela2, sel, NULL, L_COPY);
452 selaFindSelByName(sela1, "sel_4_3", NULL, &sel);
453 selaAddSel(sela2, sel, NULL, L_COPY);
454 break;
455 case 11: /* thicken for this one; use just a few iterations */
456 sela1 = sela8ccThin(NULL);
457 selaFindSelByName(sela1, "sel_8_4", NULL, &sel);
458 selaAddSel(sela2, sel, NULL, L_COPY);
459 break;
460 }
461
462 /* Optionally display the sel set */
463 if (debug) {
464 PIX *pix1;
465 char buf[32];
466 lept_mkdir("/lept/sels");
467 pix1 = selaDisplayInPix(sela2, 35, 3, 15, 4);
468 snprintf(buf, sizeof(buf), "/tmp/lept/sels/set%d.png", index);
469 pixWrite(buf, pix1, IFF_PNG);
470 pixDisplay(pix1, 100, 100);
471 pixDestroy(&pix1);
472 }
473
474 selaDestroy(&sela1);
475 return sela2;
476}
SELA * selaMakeThinSets(l_int32 index, l_int32 debug)
selaMakeThinSets()
Definition: ccthin.c:345
PIX * pixThinConnectedBySet(PIX *pixs, l_int32 type, SELA *sela, l_int32 maxiters)
pixThinConnectedBySet()
Definition: ccthin.c:224
PIX * pixThinConnected(PIX *pixs, l_int32 type, l_int32 connectivity, l_int32 maxiters)
pixThinConnected()
Definition: ccthin.c:162
PIXA * pixaThinConnected(PIXA *pixas, l_int32 type, l_int32 connectivity, l_int32 maxiters)
pixaThinConnected()
Definition: ccthin.c:72
l_ok pixEqual(PIX *pix1, PIX *pix2, l_int32 *psame)
pixEqual()
Definition: compare.c:156
PIX * pixHMT(PIX *pixd, PIX *pixs, SEL *sel)
pixHMT()
Definition: morph.c:342
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:383
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:705
PIX * pixOr(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixOr()
Definition: pix3.c:1560
PIX * pixSubtract(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixSubtract()
Definition: pix3.c:1753
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1509
@ L_COPY
Definition: pix.h:712
@ L_CLONE
Definition: pix.h:713
@ L_INSERT
Definition: pix.h:711
@ L_THIN_BG
Definition: pix.h:1153
@ L_THIN_FG
Definition: pix.h:1152
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:506
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:412
l_ok pixaVerifyDepth(PIXA *pixa, l_int32 *psame, l_int32 *pmaxd)
pixaVerifyDepth()
Definition: pixabasic.c:960
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:650
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:691
PIX ** pixaGetPixArray(PIXA *pixa)
pixaGetPixArray()
Definition: pixabasic.c:935
PIX * pixExtractBorderConnComps(PIX *pixs, l_int32 connectivity)
pixExtractBorderConnComps()
Definition: seedfill.c:698
void selaDestroy(SELA **psela)
selaDestroy()
Definition: sel1.c:276
l_ok selaFindSelByName(SELA *sela, const char *name, l_int32 *pindex, SEL **psel)
selaFindSelByName()
Definition: sel1.c:730
SEL * selRotateOrth(SEL *sel, l_int32 quads)
selRotateOrth()
Definition: sel1.c:1240
SEL * selaGetSel(SELA *sela, l_int32 i)
selaGetSel()
Definition: sel1.c:662
void selDestroy(SEL **psel)
selDestroy()
Definition: sel1.c:340
l_ok selaAddSel(SELA *sela, SEL *sel, const char *selname, l_int32 copyflag)
selaAddSel()
Definition: sel1.c:559
SELA * selaCreate(l_int32 n)
selaCreate()
Definition: sel1.c:251
l_int32 selaGetCount(SELA *sela)
selaGetCount()
Definition: sel1.c:637
PIX * selaDisplayInPix(SELA *sela, l_int32 size, l_int32 gthick, l_int32 spacing, l_int32 ncols)
selaDisplayInPix()
Definition: sel1.c:2373
SELA * sela8ccThin(SELA *sela)
sela8ccThin()
Definition: sel2.c:805
SELA * sela4and8ccThin(SELA *sela)
sela4and8ccThin()
Definition: sel2.c:846
SELA * sela4ccThin(SELA *sela)
sela4ccThin()
Definition: sel2.c:764
Definition: pix.h:139
Definition: pix.h:456
Definition: morph.h:74
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:2218