Leptonica  1.77.0
Image processing and image analysis suite
strokes.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 
27 
56 #include "allheaders.h"
57 
58 /*-----------------------------------------------------------------*
59  * Stroke parameter measurement *
60  *-----------------------------------------------------------------*/
74 l_ok
76  l_int32 *tab8,
77  l_int32 *plength)
78 {
79 l_int32 n;
80 l_int32 *tab;
81 PIX *pix1;
82 
83  PROCNAME("pixFindStrokeLength");
84 
85  if (!plength)
86  return ERROR_INT("&length not defined", procName, 1);
87  *plength = 0;
88  if (!pixs)
89  return ERROR_INT("pixs not defined", procName, 1);
90 
91  pix1 = pixExtractBoundary(pixs, 1);
92  tab = (tab8) ? tab8 : makePixelSumTab8();
93  pixCountPixels(pix1, &n, tab);
94  *plength = n / 2;
95  if (!tab8) LEPT_FREE(tab);
96  pixDestroy(&pix1);
97  return 0;
98 }
99 
100 
122 l_ok
124  l_float32 thresh,
125  l_int32 *tab8,
126  l_float32 *pwidth,
127  NUMA **pnahisto)
128 {
129 l_int32 i, n, count, length, first, last;
130 l_int32 *tab;
131 l_float32 width1, width2, ratio, extra;
132 l_float32 *fa;
133 NUMA *na1, *na2;
134 PIX *pix1;
135 
136  PROCNAME("pixFindStrokeWidth");
137 
138  if (!pwidth)
139  return ERROR_INT("&width not defined", procName, 1);
140  *pwidth = 0;
141  if (!pixs)
142  return ERROR_INT("pixs not defined", procName, 1);
143 
144  tab = (tab8) ? tab8 : makePixelSumTab8();
145 
146  /* ------- Method 1: via boundary length ------- */
147  /* The computed stroke length is a bit larger than that actual
148  * length, because of the addition of the 'caps' at the
149  * stroke ends. Therefore the computed width is a bit
150  * smaller than the average width. */
151  pixFindStrokeLength(pixs, tab8, &length);
152  pixCountPixels(pixs, &count, tab8);
153  width1 = (l_float32)count / (l_float32)length;
154 
155  /* ------- Method 2: via distance transform ------- */
156  /* First get the histogram of distances */
157  pix1 = pixDistanceFunction(pixs, 8, 8, L_BOUNDARY_BG);
158  na1 = pixGetGrayHistogram(pix1, 1);
159  pixDestroy(&pix1);
160  numaGetNonzeroRange(na1, 0.1, &first, &last);
161  na2 = numaClipToInterval(na1, 0, last);
162  numaWriteStream(stderr, na2);
163 
164  /* Find the bucket with the largest distance whose contents
165  * exceed the threshold. */
166  fa = numaGetFArray(na2, L_NOCOPY);
167  n = numaGetCount(na2);
168  for (i = n - 1; i > 0; i--) {
169  ratio = fa[i] / fa[1];
170  if (ratio > thresh) break;
171  }
172  /* Let the last skipped bucket contribute to the stop bucket.
173  * This is the 'extra' term below. The result may be a slight
174  * over-correction, so the computed width may be a bit larger
175  * than the average width. */
176  extra = (i < n - 1) ? fa[i + 1] / fa[1] : 0;
177  width2 = 2.0 * (i - 1.0 + ratio + extra);
178  fprintf(stderr, "width1 = %5.2f, width2 = %5.2f\n", width1, width2);
179 
180  /* Average the two results */
181  *pwidth = (width1 + width2) / 2.0;
182 
183  if (!tab8) LEPT_FREE(tab);
184  numaDestroy(&na1);
185  if (pnahisto)
186  *pnahisto = na2;
187  else
188  numaDestroy(&na2);
189  return 0;
190 }
191 
192 
207 NUMA *
209  l_float32 thresh,
210  l_int32 *tab8,
211  l_int32 debug)
212 {
213 l_int32 i, n, same, maxd;
214 l_int32 *tab;
215 l_float32 width;
216 NUMA *na;
217 PIX *pix;
218 
219  PROCNAME("pixaFindStrokeWidth");
220 
221  if (!pixa)
222  return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL);
223  pixaVerifyDepth(pixa, &same, &maxd);
224  if (maxd > 1)
225  return (NUMA *)ERROR_PTR("pix not all 1 bpp", procName, NULL);
226 
227  tab = (tab8) ? tab8 : makePixelSumTab8();
228 
229  n = pixaGetCount(pixa);
230  na = numaCreate(n);
231  for (i = 0; i < n; i++) {
232  pix = pixaGetPix(pixa, i, L_CLONE);
233  pixFindStrokeWidth(pix, thresh, tab8, &width, NULL);
234  numaAddNumber(na, width);
235  pixDestroy(&pix);
236  }
237 
238  if (!tab8) LEPT_FREE(tab);
239  return na;
240 }
241 
242 
243 /*-----------------------------------------------------------------*
244  * Change stroke width *
245  *-----------------------------------------------------------------*/
253 PIXA *
255  l_float32 targetw)
256 {
257 l_int32 i, n, same, maxd;
258 l_float32 width;
259 NUMA *na;
260 PIX *pix1, *pix2;
261 PIXA *pixad;
262 
263  PROCNAME("pixaModifyStrokeWidth");
264 
265  if (!pixas)
266  return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
267  if (targetw < 1)
268  return (PIXA *)ERROR_PTR("target width < 1", procName, NULL);
269  pixaVerifyDepth(pixas, &same, &maxd);
270  if (maxd > 1)
271  return (PIXA *)ERROR_PTR("pix not all 1 bpp", procName, NULL);
272 
273  na = pixaFindStrokeWidth(pixas, 0.1, NULL, 0);
274  n = pixaGetCount(pixas);
275  pixad = pixaCreate(n);
276  for (i = 0; i < n; i++) {
277  pix1 = pixaGetPix(pixas, i, L_CLONE);
278  numaGetFValue(na, i, &width);
279  pix2 = pixModifyStrokeWidth(pix1, width, targetw);
280  pixaAddPix(pixad, pix2, L_INSERT);
281  pixDestroy(&pix1);
282  }
283 
284  numaDestroy(&na);
285  return pixad;
286 }
287 
288 
297 PIX *
299  l_float32 width,
300  l_float32 targetw)
301 {
302 char buf[32];
303 l_int32 diff, size;
304 
305  PROCNAME("pixModifyStrokeWidth");
306 
307  if (!pixs || (pixGetDepth(pixs) != 1))
308  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
309  if (targetw < 1)
310  return (PIX *)ERROR_PTR("target width < 1", procName, NULL);
311 
312  diff = lept_roundftoi(targetw - width);
313  if (diff == 0) return pixCopy(NULL, pixs);
314 
315  size = L_ABS(diff) + 1;
316  if (diff < 0) /* erode */
317  snprintf(buf, sizeof(buf), "e%d.%d", size, size);
318  else /* diff > 0; dilate */
319  snprintf(buf, sizeof(buf), "d%d.%d", size, size);
320  return pixMorphSequence(pixs, buf, 0);
321 }
322 
323 
344 PIXA *
346  l_int32 width,
347  l_int32 thinfirst,
348  l_int32 connectivity)
349 {
350 l_int32 i, n, maxd, same;
351 PIX *pix1, *pix2;
352 PIXA *pixad;
353 
354  PROCNAME("pixaSetStrokeWidth");
355 
356  if (!pixas)
357  return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
358  if (width < 1 || width > 100)
359  return (PIXA *)ERROR_PTR("width not in [1 ... 100]", procName, NULL);
360  if (connectivity != 4 && connectivity != 8)
361  return (PIXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
362  pixaVerifyDepth(pixas, &same, &maxd);
363  if (maxd > 1)
364  return (PIXA *)ERROR_PTR("pix are not all 1 bpp", procName, NULL);
365 
366  n = pixaGetCount(pixas);
367  pixad = pixaCreate(n);
368  for (i = 0; i < n; i++) {
369  pix1 = pixaGetPix(pixas, i, L_CLONE);
370  pix2 = pixSetStrokeWidth(pix1, width, thinfirst, connectivity);
371  pixaAddPix(pixad, pix2, L_INSERT);
372  pixDestroy(&pix1);
373  }
374 
375  return pixad;
376 }
377 
378 
396 PIX *
398  l_int32 width,
399  l_int32 thinfirst,
400  l_int32 connectivity)
401 {
402 char buf[16];
403 l_int32 border;
404 PIX *pix1, *pix2, *pixd;
405 
406  PROCNAME("pixSetStrokeWidth");
407 
408  if (!pixs || (pixGetDepth(pixs) != 1))
409  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
410  if (width < 1 || width > 100)
411  return (PIX *)ERROR_PTR("width not in [1 ... 100]", procName, NULL);
412  if (connectivity != 4 && connectivity != 8)
413  return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
414 
415  if (!thinfirst && width == 1) /* nothing to do */
416  return pixCopy(NULL, pixs);
417 
418  /* Add a white border */
419  border = width / 2;
420  pix1 = pixAddBorder(pixs, border, 0);
421 
422  /* Thin to a skeleton */
423  if (thinfirst)
424  pix2 = pixThinConnected(pix1, L_THIN_FG, connectivity, 0);
425  else
426  pix2 = pixClone(pix1);
427  pixDestroy(&pix1);
428 
429  /* Dilate */
430  snprintf(buf, sizeof(buf), "D%d.%d", width, width);
431  pixd = pixMorphSequence(pix2, buf, 0);
432  pixCopyText(pixd, pixs);
433  pixDestroy(&pix2);
434  return pixd;
435 }
436 
NUMA * pixGetGrayHistogram(PIX *pixs, l_int32 factor)
pixGetGrayHistogram()
Definition: pix4.c:109
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:692
Definition: pix.h:717
l_int32 lept_roundftoi(l_float32 fval)
lept_roundftoi()
Definition: utils1.c:547
l_ok pixaVerifyDepth(PIXA *pixa, l_int32 *psame, l_int32 *pmaxd)
pixaVerifyDepth()
Definition: pixabasic.c:941
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:473
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:163
l_ok numaGetNonzeroRange(NUMA *na, l_float32 eps, l_int32 *pfirst, l_int32 *plast)
numaGetNonzeroRange()
Definition: numafunc1.c:1006
Definition: pix.h:716
l_ok numaWriteStream(FILE *fp, NUMA *na)
numaWriteStream()
Definition: numabasic.c:1246
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:187
NUMA * numaClipToInterval(NUMA *nas, l_int32 first, l_int32 last)
numaClipToInterval()
Definition: numafunc1.c:1105
PIX * pixAddBorder(PIX *pixs, l_int32 npix, l_uint32 val)
pixAddBorder()
Definition: pix2.c:1748
PIX * pixThinConnected(PIX *pixs, l_int32 type, l_int32 connectivity, l_int32 maxiters)
pixThinConnected()
Definition: ccthin.c:158
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:503
Definition: array.h:59
PIXA * pixaModifyStrokeWidth(PIXA *pixas, l_float32 targetw)
pixaModifyStrokeWidth()
Definition: strokes.c:254
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:631
PIXA * pixaSetStrokeWidth(PIXA *pixas, l_int32 width, l_int32 thinfirst, l_int32 connectivity)
pixaSetStrokeWidth()
Definition: strokes.c:345
NUMA * pixaFindStrokeWidth(PIXA *pixa, l_float32 thresh, l_int32 *tab8, l_int32 debug)
pixaFindStrokeWidth()
Definition: strokes.c:208
l_ok pixFindStrokeLength(PIX *pixs, l_int32 *tab8, l_int32 *plength)
pixFindStrokeLength()
Definition: strokes.c:75
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:133
l_ok pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1823
l_int32 * makePixelSumTab8(void)
makePixelSumTab8()
Definition: pix3.c:2297
PIX * pixSetStrokeWidth(PIX *pixs, l_int32 width, l_int32 thinfirst, l_int32 connectivity)
pixSetStrokeWidth()
Definition: strokes.c:397
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:515
l_int32 n
Definition: stringcode.h:47
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:543
l_ok pixFindStrokeWidth(PIX *pixs, l_float32 thresh, l_int32 *tab8, l_float32 *pwidth, NUMA **pnahisto)
pixFindStrokeWidth()
Definition: strokes.c:123
Definition: pix.h:454
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:360
PIX * pixModifyStrokeWidth(PIX *pixs, l_float32 width, l_float32 targetw)
pixModifyStrokeWidth()
Definition: strokes.c:298
l_float32 * numaGetFArray(NUMA *na, l_int32 copyflag)
numaGetFArray()
Definition: numabasic.c:865
PIX * pixDistanceFunction(PIX *pixs, l_int32 connectivity, l_int32 outdepth, l_int32 boundcond)
pixDistanceFunction()
Definition: seedfill.c:2533
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:672
PIX * pixExtractBoundary(PIX *pixs, l_int32 type)
pixExtractBoundary()
Definition: morphapp.c:108
Definition: pix.h:134
Definition: pix.h:719
PIX * pixCopy(PIX *pixd, PIX *pixs)
pixCopy()
Definition: pix1.c:628
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:631