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 *-----------------------------------------------------------------*/
78l_ok
80 l_int32 *tab8,
81 l_int32 *plength)
82{
83l_int32 n;
84l_int32 *tab;
85PIX *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
126l_ok
128 l_float32 thresh,
129 l_int32 *tab8,
130 l_float32 *pwidth,
131 NUMA **pnahisto)
132{
133l_int32 i, n, count, length, first, last;
134l_int32 *tab;
135l_float32 width1, width2, ratio, extra;
136l_float32 *fa;
137NUMA *na1, *na2;
138PIX *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
211NUMA *
213 l_float32 thresh,
214 l_int32 *tab8,
215 l_int32 debug)
216{
217l_int32 i, n, same, maxd;
218l_int32 *tab;
219l_float32 width;
220NUMA *na;
221PIX *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 *-----------------------------------------------------------------*/
257PIXA *
259 l_float32 targetw)
260{
261l_int32 i, n, same, maxd;
262l_float32 width;
263NUMA *na;
264PIX *pix1, *pix2;
265PIXA *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
301PIX *
303 l_float32 width,
304 l_float32 targetw)
305{
306char buf[32];
307l_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
348PIXA *
350 l_int32 width,
351 l_int32 thinfirst,
352 l_int32 connectivity)
353{
354l_int32 i, n, maxd, same;
355PIX *pix1, *pix2;
356PIXA *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
400PIX *
402 l_int32 width,
403 l_int32 thinfirst,
404 l_int32 connectivity)
405{
406char buf[16];
407l_int32 border;
408PIX *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}
PIX * pixThinConnected(PIX *pixs, l_int32 type, l_int32 connectivity, l_int32 maxiters)
pixThinConnected()
Definition: ccthin.c:162
PIX * pixExtractBoundary(PIX *pixs, l_int32 type)
pixExtractBoundary()
Definition: morphapp.c:111
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:137
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:478
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:719
l_ok numaWriteStderr(NUMA *na)
numaWriteStderr()
Definition: numabasic.c:1313
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:366
l_float32 * numaGetFArray(NUMA *na, l_int32 copyflag)
numaGetFArray()
Definition: numabasic.c:892
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:658
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
l_ok numaGetNonzeroRange(NUMA *na, l_float32 eps, l_int32 *pfirst, l_int32 *plast)
numaGetNonzeroRange()
Definition: numafunc1.c:1078
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:705
PIX * pixAddBorder(PIX *pixs, l_int32 npix, l_uint32 val)
pixAddBorder()
Definition: pix2.c:1823
l_int32 * makePixelSumTab8(void)
makePixelSumTab8()
Definition: pix3.c:2411
l_ok pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1937
NUMA * pixGetGrayHistogram(PIX *pixs, l_int32 factor)
pixGetGrayHistogram()
Definition: pix4.c:115
@ L_CLONE
Definition: pix.h:713
@ L_NOCOPY
Definition: pix.h:710
@ L_INSERT
Definition: pix.h:711
@ L_THIN_FG
Definition: pix.h:1152
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:506
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 * pixDistanceFunction(PIX *pixs, l_int32 connectivity, l_int32 outdepth, l_int32 boundcond)
pixDistanceFunction()
Definition: seedfill.c:2535
NUMA * pixaFindStrokeWidth(PIXA *pixa, l_float32 thresh, l_int32 *tab8, l_int32 debug)
pixaFindStrokeWidth()
Definition: strokes.c:212
PIXA * pixaSetStrokeWidth(PIXA *pixas, l_int32 width, l_int32 thinfirst, l_int32 connectivity)
pixaSetStrokeWidth()
Definition: strokes.c:349
PIX * pixSetStrokeWidth(PIX *pixs, l_int32 width, l_int32 thinfirst, l_int32 connectivity)
pixSetStrokeWidth()
Definition: strokes.c:401
PIX * pixModifyStrokeWidth(PIX *pixs, l_float32 width, l_float32 targetw)
pixModifyStrokeWidth()
Definition: strokes.c:302
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:258
l_ok pixFindStrokeWidth(PIX *pixs, l_float32 thresh, l_int32 *tab8, l_float32 *pwidth, NUMA **pnahisto)
pixFindStrokeWidth()
Definition: strokes.c:127
Definition: array.h:71
Definition: pix.h:139
Definition: pix.h:456
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
l_int32 lept_roundftoi(l_float32 fval)
lept_roundftoi()
Definition: utils1.c:700