Leptonica  1.83.1
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  *----------------------------------------------------------------*/
71 PIXA *
73  l_int32 type,
74  l_int32 connectivity,
75  l_int32 maxiters)
76 {
77 l_int32 i, n, d, same;
78 PIX *pix1, *pix2;
79 PIXA *pixad;
80 SELA *sela;
81 
82  if (!pixas)
83  return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
84  if (type != L_THIN_FG && type != L_THIN_BG)
85  return (PIXA *)ERROR_PTR("invalid fg/bg type", __func__, NULL);
86  if (connectivity != 4 && connectivity != 8)
87  return (PIXA *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
88  if (maxiters == 0) maxiters = 10000;
89 
90  pixaVerifyDepth(pixas, &same, &d);
91  if (d != 1)
92  return (PIXA *)ERROR_PTR("pix are not all 1 bpp", __func__, NULL);
93 
94  if (connectivity == 4)
95  sela = selaMakeThinSets(1, 0);
96  else /* connectivity == 8 */
97  sela = selaMakeThinSets(5, 0);
98 
99  n = pixaGetCount(pixas);
100  pixad = pixaCreate(n);
101  for (i = 0; i < n; i++) {
102  pix1 = pixaGetPix(pixas, i, L_CLONE);
103  pix2 = pixThinConnectedBySet(pix1, type, sela, maxiters);
104  pixaAddPix(pixad, pix2, L_INSERT);
105  pixDestroy(&pix1);
106  }
107 
108  selaDestroy(&sela);
109  return pixad;
110 }
111 
112 
159 PIX *
161  l_int32 type,
162  l_int32 connectivity,
163  l_int32 maxiters)
164 {
165 PIX *pixd;
166 SELA *sela;
167 
168  if (!pixs)
169  return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
170  if (pixGetDepth(pixs) != 1)
171  return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL);
172  if (type != L_THIN_FG && type != L_THIN_BG)
173  return (PIX *)ERROR_PTR("invalid fg/bg type", __func__, NULL);
174  if (connectivity != 4 && connectivity != 8)
175  return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
176  if (maxiters == 0) maxiters = 10000;
177 
178  if (connectivity == 4)
179  sela = selaMakeThinSets(1, 0);
180  else /* connectivity == 8 */
181  sela = selaMakeThinSets(5, 0);
182 
183  pixd = pixThinConnectedBySet(pixs, type, sela, maxiters);
184 
185  selaDestroy(&sela);
186  return pixd;
187 }
188 
189 
219 PIX *
221  l_int32 type,
222  SELA *sela,
223  l_int32 maxiters)
224 {
225 l_int32 i, j, r, nsels, same;
226 PIXA *pixahmt;
227 PIX **pixhmt; /* array owned by pixahmt; do not destroy! */
228 PIX *pix1, *pix2, *pixd;
229 SEL *sel, *selr;
230 
231  if (!pixs)
232  return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
233  if (pixGetDepth(pixs) != 1)
234  return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL);
235  if (type != L_THIN_FG && type != L_THIN_BG)
236  return (PIX *)ERROR_PTR("invalid fg/bg type", __func__, NULL);
237  if (!sela)
238  return (PIX *)ERROR_PTR("sela not defined", __func__, NULL);
239  if (maxiters == 0) maxiters = 10000;
240 
241  /* Set up array of temp pix to hold hmts */
242  nsels = selaGetCount(sela);
243  pixahmt = pixaCreate(nsels);
244  for (i = 0; i < nsels; i++) {
245  pix1 = pixCreateTemplate(pixs);
246  pixaAddPix(pixahmt, pix1, L_INSERT);
247  }
248  pixhmt = pixaGetPixArray(pixahmt);
249  if (!pixhmt) {
250  pixaDestroy(&pixahmt);
251  return (PIX *)ERROR_PTR("pixhmt array not made", __func__, NULL);
252  }
253 
254  /* Set up initial image for fg thinning */
255  if (type == L_THIN_FG)
256  pixd = pixCopy(NULL, pixs);
257  else /* bg thinning */
258  pixd = pixInvert(NULL, pixs);
259 
260  /* Thin the fg, with up to maxiters iterations */
261  for (i = 0; i < maxiters; i++) {
262  pix1 = pixCopy(NULL, pixd); /* test for completion */
263  for (r = 0; r < 4; r++) { /* over 90 degree rotations of Sels */
264  for (j = 0; j < nsels; j++) { /* over individual sels in sela */
265  sel = selaGetSel(sela, j); /* not a copy */
266  selr = selRotateOrth(sel, r);
267  pixHMT(pixhmt[j], pixd, selr);
268  selDestroy(&selr);
269  if (j > 0)
270  pixOr(pixhmt[0], pixhmt[0], pixhmt[j]); /* accum result */
271  }
272  pixSubtract(pixd, pixd, pixhmt[0]); /* remove result */
273  }
274  pixEqual(pixd, pix1, &same);
275  pixDestroy(&pix1);
276  if (same) {
277 /* L_INFO("%d iterations to completion\n", __func__, i); */
278  break;
279  }
280  }
281 
282  /* This is a bit tricky. If we're thickening the foreground, then
283  * we get a fg border of thickness equal to the number of
284  * iterations. This border is connected to all components that
285  * were initially touching the border, but as it grows, it does
286  * not touch other growing components -- it leaves a 1 pixel wide
287  * background between it and the growing components, and that
288  * thin background prevents the components from growing further.
289  * This border can be entirely removed as follows:
290  * (1) Subtract the original (unthickened) image pixs from the
291  * thickened image. This removes the pixels that were originally
292  * touching the border.
293  * (2) Get all remaining pixels that are connected to the border.
294  * (3) Remove those pixels from the thickened image. */
295  if (type == L_THIN_BG) {
296  pixInvert(pixd, pixd); /* finish with duality */
297  pix1 = pixSubtract(NULL, pixd, pixs);
298  pix2 = pixExtractBorderConnComps(pix1, 4);
299  pixSubtract(pixd, pixd, pix2);
300  pixDestroy(&pix1);
301  pixDestroy(&pix2);
302  }
303 
304  pixaDestroy(&pixahmt);
305  return pixd;
306 }
307 
308 
338 SELA *
339 selaMakeThinSets(l_int32 index,
340  l_int32 debug)
341 {
342 SEL *sel;
343 SELA *sela1, *sela2, *sela3;
344 
345  if (index < 1 || index > 11)
346  return (SELA *)ERROR_PTR("invalid index", __func__, NULL);
347 
348  sela2 = selaCreate(4);
349  switch(index)
350  {
351  case 1:
352  sela1 = sela4ccThin(NULL);
353  selaFindSelByName(sela1, "sel_4_1", NULL, &sel);
354  selaAddSel(sela2, sel, NULL, L_COPY);
355  selaFindSelByName(sela1, "sel_4_2", NULL, &sel);
356  selaAddSel(sela2, sel, NULL, L_COPY);
357  selaFindSelByName(sela1, "sel_4_3", NULL, &sel);
358  selaAddSel(sela2, sel, NULL, L_COPY);
359  break;
360  case 2:
361  sela1 = sela4ccThin(NULL);
362  selaFindSelByName(sela1, "sel_4_1", NULL, &sel);
363  selaAddSel(sela2, sel, NULL, L_COPY);
364  selaFindSelByName(sela1, "sel_4_5", NULL, &sel);
365  selaAddSel(sela2, sel, NULL, L_COPY);
366  selaFindSelByName(sela1, "sel_4_6", NULL, &sel);
367  selaAddSel(sela2, sel, NULL, L_COPY);
368  break;
369  case 3:
370  sela1 = sela4ccThin(NULL);
371  selaFindSelByName(sela1, "sel_4_1", NULL, &sel);
372  selaAddSel(sela2, sel, NULL, L_COPY);
373  selaFindSelByName(sela1, "sel_4_7", NULL, &sel);
374  selaAddSel(sela2, sel, NULL, L_COPY);
375  sel = selRotateOrth(sel, 1);
376  selaAddSel(sela2, sel, "sel_4_7_rot", L_INSERT);
377  break;
378  case 4:
379  sela1 = sela4and8ccThin(NULL);
380  selaFindSelByName(sela1, "sel_48_1", NULL, &sel);
381  selaAddSel(sela2, sel, NULL, L_COPY);
382  sel = selRotateOrth(sel, 1);
383  selaAddSel(sela2, sel, "sel_48_1_rot", L_INSERT);
384  selaFindSelByName(sela1, "sel_48_2", NULL, &sel);
385  selaAddSel(sela2, sel, NULL, L_COPY);
386  break;
387  case 5:
388  sela1 = sela8ccThin(NULL);
389  selaFindSelByName(sela1, "sel_8_2", NULL, &sel);
390  selaAddSel(sela2, sel, NULL, L_COPY);
391  selaFindSelByName(sela1, "sel_8_3", NULL, &sel);
392  selaAddSel(sela2, sel, NULL, L_COPY);
393  selaFindSelByName(sela1, "sel_8_5", NULL, &sel);
394  selaAddSel(sela2, sel, NULL, L_COPY);
395  selaFindSelByName(sela1, "sel_8_6", NULL, &sel);
396  selaAddSel(sela2, sel, NULL, L_COPY);
397  break;
398  case 6:
399  sela1 = sela8ccThin(NULL);
400  sela3 = sela4and8ccThin(NULL);
401  selaFindSelByName(sela1, "sel_8_2", NULL, &sel);
402  selaAddSel(sela2, sel, NULL, L_COPY);
403  selaFindSelByName(sela1, "sel_8_3", NULL, &sel);
404  selaAddSel(sela2, sel, NULL, L_COPY);
405  selaFindSelByName(sela3, "sel_48_2", NULL, &sel);
406  selaAddSel(sela2, sel, NULL, L_COPY);
407  selaDestroy(&sela3);
408  break;
409  case 7:
410  sela1 = sela8ccThin(NULL);
411  selaFindSelByName(sela1, "sel_8_1", NULL, &sel);
412  selaAddSel(sela2, sel, NULL, L_COPY);
413  selaFindSelByName(sela1, "sel_8_5", NULL, &sel);
414  selaAddSel(sela2, sel, NULL, L_COPY);
415  selaFindSelByName(sela1, "sel_8_6", NULL, &sel);
416  selaAddSel(sela2, sel, NULL, L_COPY);
417  break;
418  case 8:
419  sela1 = sela8ccThin(NULL);
420  selaFindSelByName(sela1, "sel_8_2", NULL, &sel);
421  selaAddSel(sela2, sel, NULL, L_COPY);
422  selaFindSelByName(sela1, "sel_8_3", NULL, &sel);
423  selaAddSel(sela2, sel, NULL, L_COPY);
424  selaFindSelByName(sela1, "sel_8_8", NULL, &sel);
425  selaAddSel(sela2, sel, NULL, L_COPY);
426  selaFindSelByName(sela1, "sel_8_9", NULL, &sel);
427  selaAddSel(sela2, sel, NULL, L_COPY);
428  break;
429  case 9:
430  sela1 = sela8ccThin(NULL);
431  selaFindSelByName(sela1, "sel_8_5", NULL, &sel);
432  selaAddSel(sela2, sel, NULL, L_COPY);
433  selaFindSelByName(sela1, "sel_8_6", NULL, &sel);
434  selaAddSel(sela2, sel, NULL, L_COPY);
435  selaFindSelByName(sela1, "sel_8_7", NULL, &sel);
436  selaAddSel(sela2, sel, NULL, L_COPY);
437  sel = selRotateOrth(sel, 1);
438  selaAddSel(sela2, sel, "sel_8_7_rot", L_INSERT);
439  break;
440  case 10: /* thicken for this one; use just a few iterations */
441  sela1 = sela4ccThin(NULL);
442  selaFindSelByName(sela1, "sel_4_2", NULL, &sel);
443  selaAddSel(sela2, sel, NULL, L_COPY);
444  selaFindSelByName(sela1, "sel_4_3", NULL, &sel);
445  selaAddSel(sela2, sel, NULL, L_COPY);
446  break;
447  case 11: /* thicken for this one; use just a few iterations */
448  sela1 = sela8ccThin(NULL);
449  selaFindSelByName(sela1, "sel_8_4", NULL, &sel);
450  selaAddSel(sela2, sel, NULL, L_COPY);
451  break;
452  }
453 
454  /* Optionally display the sel set */
455  if (debug) {
456  PIX *pix1;
457  char buf[32];
458  lept_mkdir("/lept/sels");
459  pix1 = selaDisplayInPix(sela2, 35, 3, 15, 4);
460  snprintf(buf, sizeof(buf), "/tmp/lept/sels/set%d.png", index);
461  pixWrite(buf, pix1, IFF_PNG);
462  pixDisplay(pix1, 100, 100);
463  pixDestroy(&pix1);
464  }
465 
466  selaDestroy(&sela1);
467  return sela2;
468 }
PIX * pixThinConnectedBySet(PIX *pixs, l_int32 type, SELA *sela, l_int32 maxiters)
pixThinConnectedBySet()
Definition: ccthin.c:220
PIX * pixThinConnected(PIX *pixs, l_int32 type, l_int32 connectivity, l_int32 maxiters)
pixThinConnected()
Definition: ccthin.c:160
PIXA * pixaThinConnected(PIXA *pixas, l_int32 type, l_int32 connectivity, l_int32 maxiters)
pixaThinConnected()
Definition: ccthin.c:72
SELA * selaMakeThinSets(l_int32 index, l_int32 debug)
selaMakeThinSets()
Definition: ccthin.c:339
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:338
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:608
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:689
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:380
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1481
PIX * pixOr(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixOr()
Definition: pix3.c:1530
PIX * pixSubtract(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixSubtract()
Definition: pix3.c:1717
@ L_COPY
Definition: pix.h:505
@ L_CLONE
Definition: pix.h:506
@ L_INSERT
Definition: pix.h:504
@ L_THIN_BG
Definition: pix.h:946
@ L_THIN_FG
Definition: pix.h:945
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:493
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:404
l_ok pixaVerifyDepth(PIXA *pixa, l_int32 *psame, l_int32 *pmaxd)
pixaVerifyDepth()
Definition: pixabasic.c:900
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
PIX ** pixaGetPixArray(PIXA *pixa)
pixaGetPixArray()
Definition: pixabasic.c:877
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:629
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:647
PIX * pixExtractBorderConnComps(PIX *pixs, l_int32 connectivity)
pixExtractBorderConnComps()
Definition: seedfill.c:688
void selaDestroy(SELA **psela)
selaDestroy()
Definition: sel1.c:274
l_ok selaFindSelByName(SELA *sela, const char *name, l_int32 *pindex, SEL **psel)
selaFindSelByName()
Definition: sel1.c:704
SELA * selaCreate(l_int32 n)
selaCreate()
Definition: sel1.c:251
SEL * selRotateOrth(SEL *sel, l_int32 quads)
selRotateOrth()
Definition: sel1.c:1192
void selDestroy(SEL **psel)
selDestroy()
Definition: sel1.c:336
l_ok selaAddSel(SELA *sela, SEL *sel, const char *selname, l_int32 copyflag)
selaAddSel()
Definition: sel1.c:545
SEL * selaGetSel(SELA *sela, l_int32 i)
selaGetSel()
Definition: sel1.c:642
PIX * selaDisplayInPix(SELA *sela, l_int32 size, l_int32 gthick, l_int32 spacing, l_int32 ncols)
selaDisplayInPix()
Definition: sel1.c:2287
l_int32 selaGetCount(SELA *sela)
selaGetCount()
Definition: sel1.c:619
SELA * sela8ccThin(SELA *sela)
sela8ccThin()
Definition: sel2.c:793
SELA * sela4and8ccThin(SELA *sela)
sela4and8ccThin()
Definition: sel2.c:834
SELA * sela4ccThin(SELA *sela)
sela4ccThin()
Definition: sel2.c:752
Definition: morph.h:74
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:2138