Leptonica 1.85.0
Image processing and image analysis suite
Loading...
Searching...
No Matches
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 *-----------------------------------------------------------------*/
78l_ok
80 l_int32 *tab8,
81 l_int32 *plength)
82{
83l_int32 n;
84l_int32 *tab;
85PIX *pix1;
86
87 if (!plength)
88 return ERROR_INT("&length not defined", __func__, 1);
89 *plength = 0;
90 if (!pixs)
91 return ERROR_INT("pixs not defined", __func__, 1);
92
93 pix1 = pixExtractBoundary(pixs, 1);
94 tab = (tab8) ? tab8 : makePixelSumTab8();
95 pixCountPixels(pix1, &n, tab);
96 *plength = n / 2;
97 if (!tab8) LEPT_FREE(tab);
98 pixDestroy(&pix1);
99 return 0;
100}
101
102
124l_ok
126 l_float32 thresh,
127 l_int32 *tab8,
128 l_float32 *pwidth,
129 NUMA **pnahisto)
130{
131l_int32 i, n, count, length, first, last;
132l_int32 *tab;
133l_float32 width1, width2, ratio, extra;
134l_float32 *fa;
135NUMA *na1, *na2;
136PIX *pix1;
137
138 if (!pwidth)
139 return ERROR_INT("&width not defined", __func__, 1);
140 *pwidth = 0;
141 if (!pixs)
142 return ERROR_INT("pixs not defined", __func__, 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.1f, &first, &last);
161 na2 = numaClipToInterval(na1, 0, last);
162 numaWriteStderr(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.0f * (i - 1.0f + ratio + extra);
178 lept_stderr("width1 = %5.2f, width2 = %5.2f\n", width1, width2);
179
180 /* Average the two results */
181 *pwidth = (width1 + width2) / 2.0f;
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
207NUMA *
209 l_float32 thresh,
210 l_int32 *tab8,
211 l_int32 debug)
212{
213l_int32 i, n, same, maxd;
214l_int32 *tab;
215l_float32 width;
216NUMA *na;
217PIX *pix;
218
219 if (!pixa)
220 return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
221 pixaVerifyDepth(pixa, &same, &maxd);
222 if (maxd > 1)
223 return (NUMA *)ERROR_PTR("pix not all 1 bpp", __func__, NULL);
224
225 tab = (tab8) ? tab8 : makePixelSumTab8();
226
227 n = pixaGetCount(pixa);
228 na = numaCreate(n);
229 for (i = 0; i < n; i++) {
230 pix = pixaGetPix(pixa, i, L_CLONE);
231 pixFindStrokeWidth(pix, thresh, tab8, &width, NULL);
232 numaAddNumber(na, width);
233 pixDestroy(&pix);
234 }
235
236 if (!tab8) LEPT_FREE(tab);
237 return na;
238}
239
240
241/*-----------------------------------------------------------------*
242 * Change stroke width *
243 *-----------------------------------------------------------------*/
251PIXA *
253 l_float32 targetw)
254{
255l_int32 i, n, same, maxd;
256l_float32 width;
257NUMA *na;
258PIX *pix1, *pix2;
259PIXA *pixad;
260
261 if (!pixas)
262 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
263 if (targetw < 1)
264 return (PIXA *)ERROR_PTR("target width < 1", __func__, NULL);
265 pixaVerifyDepth(pixas, &same, &maxd);
266 if (maxd > 1)
267 return (PIXA *)ERROR_PTR("pix not all 1 bpp", __func__, NULL);
268
269 na = pixaFindStrokeWidth(pixas, 0.1f, NULL, 0);
270 n = pixaGetCount(pixas);
271 pixad = pixaCreate(n);
272 for (i = 0; i < n; i++) {
273 pix1 = pixaGetPix(pixas, i, L_CLONE);
274 numaGetFValue(na, i, &width);
275 pix2 = pixModifyStrokeWidth(pix1, width, targetw);
276 pixaAddPix(pixad, pix2, L_INSERT);
277 pixDestroy(&pix1);
278 }
279
280 numaDestroy(&na);
281 return pixad;
282}
283
284
293PIX *
295 l_float32 width,
296 l_float32 targetw)
297{
298char buf[32];
299l_int32 diff, size;
300
301 if (!pixs || (pixGetDepth(pixs) != 1))
302 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
303 if (targetw < 1)
304 return (PIX *)ERROR_PTR("target width < 1", __func__, NULL);
305
306 diff = lept_roundftoi(targetw - width);
307 if (diff == 0) return pixCopy(NULL, pixs);
308
309 size = L_ABS(diff) + 1;
310 if (diff < 0) /* erode */
311 snprintf(buf, sizeof(buf), "e%d.%d", size, size);
312 else /* diff > 0; dilate */
313 snprintf(buf, sizeof(buf), "d%d.%d", size, size);
314 return pixMorphSequence(pixs, buf, 0);
315}
316
317
338PIXA *
340 l_int32 width,
341 l_int32 thinfirst,
342 l_int32 connectivity)
343{
344l_int32 i, n, maxd, same;
345PIX *pix1, *pix2;
346PIXA *pixad;
347
348 if (!pixas)
349 return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
350 if (width < 1 || width > 100)
351 return (PIXA *)ERROR_PTR("width not in [1 ... 100]", __func__, NULL);
352 if (connectivity != 4 && connectivity != 8)
353 return (PIXA *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
354 pixaVerifyDepth(pixas, &same, &maxd);
355 if (maxd > 1)
356 return (PIXA *)ERROR_PTR("pix are not all 1 bpp", __func__, NULL);
357
358 n = pixaGetCount(pixas);
359 pixad = pixaCreate(n);
360 for (i = 0; i < n; i++) {
361 pix1 = pixaGetPix(pixas, i, L_CLONE);
362 pix2 = pixSetStrokeWidth(pix1, width, thinfirst, connectivity);
363 pixaAddPix(pixad, pix2, L_INSERT);
364 pixDestroy(&pix1);
365 }
366
367 return pixad;
368}
369
370
388PIX *
390 l_int32 width,
391 l_int32 thinfirst,
392 l_int32 connectivity)
393{
394char buf[16];
395l_int32 border;
396PIX *pix1, *pix2, *pixd;
397
398 if (!pixs || (pixGetDepth(pixs) != 1))
399 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
400 if (width < 1 || width > 100)
401 return (PIX *)ERROR_PTR("width not in [1 ... 100]", __func__, NULL);
402 if (connectivity != 4 && connectivity != 8)
403 return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
404
405 if (!thinfirst && width == 1) /* nothing to do */
406 return pixCopy(NULL, pixs);
407
408 /* Add a white border */
409 border = width / 2;
410 pix1 = pixAddBorder(pixs, border, 0);
411
412 /* Thin to a skeleton */
413 if (thinfirst)
414 pix2 = pixThinConnected(pix1, L_THIN_FG, connectivity, 0);
415 else
416 pix2 = pixClone(pix1);
417 pixDestroy(&pix1);
418
419 /* Dilate */
420 snprintf(buf, sizeof(buf), "D%d.%d", width, width);
421 pixd = pixMorphSequence(pix2, buf, 0);
422 pixCopyText(pixd, pixs);
423 pixDestroy(&pix2);
424 return pixd;
425}
@ L_CLONE
Definition pix.h:506
@ L_NOCOPY
Definition pix.h:503
@ L_INSERT
Definition pix.h:504
@ L_THIN_FG
Definition pix.h:945
NUMA * pixaFindStrokeWidth(PIXA *pixa, l_float32 thresh, l_int32 *tab8, l_int32 debug)
pixaFindStrokeWidth()
Definition strokes.c:208
PIXA * pixaSetStrokeWidth(PIXA *pixas, l_int32 width, l_int32 thinfirst, l_int32 connectivity)
pixaSetStrokeWidth()
Definition strokes.c:339
PIX * pixSetStrokeWidth(PIX *pixs, l_int32 width, l_int32 thinfirst, l_int32 connectivity)
pixSetStrokeWidth()
Definition strokes.c:389
PIX * pixModifyStrokeWidth(PIX *pixs, l_float32 width, l_float32 targetw)
pixModifyStrokeWidth()
Definition strokes.c:294
l_ok pixFindStrokeLength(PIX *pixs, l_int32 *tab8, l_int32 *plength)
pixFindStrokeLength()
Definition strokes.c:79
PIXA * pixaModifyStrokeWidth(PIXA *pixas, l_float32 targetw)
pixaModifyStrokeWidth()
Definition strokes.c:252
l_ok pixFindStrokeWidth(PIX *pixs, l_float32 thresh, l_int32 *tab8, l_float32 *pwidth, NUMA **pnahisto)
pixFindStrokeWidth()
Definition strokes.c:125