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