Coverage Report

Created: 2025-07-23 07:12

/src/leptonica/src/sel2.c
Line
Count
Source (jump to first uncovered line)
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
/*!
28
 * \file sel2.c
29
 * <pre>
30
 *
31
 *      Contains definitions of simple structuring elements
32
 *
33
 *      Basic brick structuring elements
34
 *          SELA    *selaAddBasic()
35
 *               Linear horizontal and vertical
36
 *               Square
37
 *               Diagonals
38
 *
39
 *      Simple hit-miss structuring elements
40
 *          SELA    *selaAddHitMiss()
41
 *               Isolated foreground pixel
42
 *               Horizontal and vertical edges
43
 *               Slanted edge
44
 *               Corners
45
 *
46
 *      Structuring elements for comparing with DWA operations
47
 *          SELA    *selaAddDwaLinear()
48
 *          SELA    *selaAddDwaCombs()
49
 *
50
 *      Structuring elements for the intersection of lines
51
 *          SELA    *selaAddCrossJunctions()
52
 *          SELA    *selaAddTJunctions()
53
 *
54
 *      Structuring elements for connectivity-preserving thinning operations
55
 *          SELA    *sela4ccThin()
56
 *          SELA    *sela8ccThin()
57
 *          SELA    *sela4and8ccThin()
58
 *
59
 *      Other structuring elements
60
 *          SEL    *selMakePlusSign()
61
 * </pre>
62
 */
63
64
#ifdef HAVE_CONFIG_H
65
#include <config_auto.h>
66
#endif  /* HAVE_CONFIG_H */
67
68
#include <math.h>
69
#include "allheaders.h"
70
71
0
#define L_BUF_SIZE 512
72
73
    /* Linear brick sel sizes, including all those that are required
74
     * for decomposable sels up to size 63. */
75
static const l_int32  num_linear = 25;
76
static const l_int32  basic_linear[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
77
       12, 13, 14, 15, 20, 21, 25, 30, 31, 35, 40, 41, 45, 50, 51};
78
79
80
/* ------------------------------------------------------------------- *
81
 *                    Basic brick structuring elements                 *
82
 * ------------------------------------------------------------------- */
83
/*!
84
 * \brief   selaAddBasic()
85
 *
86
 * \param[in]    sela [optional]
87
 * \return  sela with additional sels, or NULL on error
88
 *
89
 * <pre>
90
 * Notes:
91
 *      (1) Adds the following sels:
92
 *            ~ all linear (horiz, vert) brick sels that are
93
 *              necessary for decomposable sels up to size 63
94
 *            ~ square brick sels up to size 10
95
 *            ~ 4 diagonal sels
96
 * </pre>
97
 */
98
SELA *
99
selaAddBasic(SELA  *sela)
100
0
{
101
0
char     name[L_BUF_SIZE];
102
0
l_int32  i, size;
103
0
SEL     *sel;
104
105
0
    if (!sela) {
106
0
        if ((sela = selaCreate(0)) == NULL)
107
0
            return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
108
0
    }
109
110
    /*--------------------------------------------------------------*
111
     *             Linear horizontal and vertical sels              *
112
     *--------------------------------------------------------------*/
113
0
    for (i = 0; i < num_linear; i++) {
114
0
        size = basic_linear[i];
115
0
        sel = selCreateBrick(1, size, 0, size / 2, 1);
116
0
        snprintf(name, L_BUF_SIZE, "sel_%dh", size);
117
0
        selaAddSel(sela, sel, name, 0);
118
0
    }
119
0
    for (i = 0; i < num_linear; i++) {
120
0
        size = basic_linear[i];
121
0
        sel = selCreateBrick(size, 1, size / 2, 0, 1);
122
0
        snprintf(name, L_BUF_SIZE, "sel_%dv", size);
123
0
        selaAddSel(sela, sel, name, 0);
124
0
    }
125
126
    /*-----------------------------------------------------------*
127
     *                      2-d Bricks                           *
128
     *-----------------------------------------------------------*/
129
0
    for (i = 2; i <= 5; i++) {
130
0
        sel = selCreateBrick(i, i, i / 2, i / 2, 1);
131
0
        snprintf(name, L_BUF_SIZE, "sel_%d", i);
132
0
        selaAddSel(sela, sel, name, 0);
133
0
    }
134
135
    /*-----------------------------------------------------------*
136
     *                        Diagonals                          *
137
     *-----------------------------------------------------------*/
138
        /*  0c  1
139
            1   0  */
140
0
    sel = selCreateBrick(2, 2, 0, 0, 1);
141
0
    selSetElement(sel, 0, 0, 0);
142
0
    selSetElement(sel, 1, 1, 0);
143
0
    selaAddSel(sela, sel, "sel_2dp", 0);
144
145
        /*  1c  0
146
            0   1   */
147
0
    sel = selCreateBrick(2, 2, 0, 0, 1);
148
0
    selSetElement(sel, 0, 1, 0);
149
0
    selSetElement(sel, 1, 0, 0);
150
0
    selaAddSel(sela, sel, "sel_2dm", 0);
151
152
        /*  Diagonal, slope +, size 5 */
153
0
    sel = selCreate(5, 5, "sel_5dp");
154
0
    selSetOrigin(sel, 2, 2);
155
0
    selSetElement(sel, 0, 4, 1);
156
0
    selSetElement(sel, 1, 3, 1);
157
0
    selSetElement(sel, 2, 2, 1);
158
0
    selSetElement(sel, 3, 1, 1);
159
0
    selSetElement(sel, 4, 0, 1);
160
0
    selaAddSel(sela, sel, "sel_5dp", 0);
161
162
        /*  Diagonal, slope -, size 5 */
163
0
    sel = selCreate(5, 5, "sel_5dm");
164
0
    selSetOrigin(sel, 2, 2);
165
0
    selSetElement(sel, 0, 0, 1);
166
0
    selSetElement(sel, 1, 1, 1);
167
0
    selSetElement(sel, 2, 2, 1);
168
0
    selSetElement(sel, 3, 3, 1);
169
0
    selSetElement(sel, 4, 4, 1);
170
0
    selaAddSel(sela, sel, "sel_5dm", 0);
171
172
0
    return sela;
173
0
}
174
175
176
/* ------------------------------------------------------------------- *
177
 *                 Simple hit-miss structuring elements                *
178
 * ------------------------------------------------------------------- */
179
/*!
180
 * \brief   selaAddHitMiss()
181
 *
182
 * \param[in]    sela  [optional]
183
 * \return  sela with additional sels, or NULL on error
184
 */
185
SELA *
186
selaAddHitMiss(SELA  *sela)
187
0
{
188
0
SEL  *sel;
189
190
0
    if (!sela) {
191
0
        if ((sela = selaCreate(0)) == NULL)
192
0
            return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
193
0
    }
194
195
#if 0   /*  use just for testing */
196
    sel = selCreateBrick(3, 3, 1, 1, 2);
197
    selaAddSel(sela, sel, "sel_bad", 0);
198
#endif
199
200
201
    /*--------------------------------------------------------------*
202
     *                   Isolated foreground pixel                  *
203
     *--------------------------------------------------------------*/
204
0
    sel = selCreateBrick(3, 3, 1, 1, SEL_MISS);
205
0
    selSetElement(sel, 1, 1, SEL_HIT);
206
0
    selaAddSel(sela, sel, "sel_3hm", 0);
207
208
    /*--------------------------------------------------------------*
209
     *                Horizontal and vertical edges                 *
210
     *--------------------------------------------------------------*/
211
0
    sel = selCreateBrick(2, 3, 0, 1, SEL_HIT);
212
0
    selSetElement(sel, 1, 0, SEL_MISS);
213
0
    selSetElement(sel, 1, 1, SEL_MISS);
214
0
    selSetElement(sel, 1, 2, SEL_MISS);
215
0
    selaAddSel(sela, sel, "sel_3de", 0);
216
217
0
    sel = selCreateBrick(2, 3, 1, 1, SEL_HIT);
218
0
    selSetElement(sel, 0, 0, SEL_MISS);
219
0
    selSetElement(sel, 0, 1, SEL_MISS);
220
0
    selSetElement(sel, 0, 2, SEL_MISS);
221
0
    selaAddSel(sela, sel, "sel_3ue", 0);
222
223
0
    sel = selCreateBrick(3, 2, 1, 0, SEL_HIT);
224
0
    selSetElement(sel, 0, 1, SEL_MISS);
225
0
    selSetElement(sel, 1, 1, SEL_MISS);
226
0
    selSetElement(sel, 2, 1, SEL_MISS);
227
0
    selaAddSel(sela, sel, "sel_3re", 0);
228
229
0
    sel = selCreateBrick(3, 2, 1, 1, SEL_HIT);
230
0
    selSetElement(sel, 0, 0, SEL_MISS);
231
0
    selSetElement(sel, 1, 0, SEL_MISS);
232
0
    selSetElement(sel, 2, 0, SEL_MISS);
233
0
    selaAddSel(sela, sel, "sel_3le", 0);
234
235
    /*--------------------------------------------------------------*
236
     *                        Slanted edge                          *
237
     *--------------------------------------------------------------*/
238
0
    sel = selCreateBrick(13, 6, 6, 2, SEL_DONT_CARE);
239
0
    selSetElement(sel, 0, 3, SEL_MISS);
240
0
    selSetElement(sel, 0, 5, SEL_HIT);
241
0
    selSetElement(sel, 4, 2, SEL_MISS);
242
0
    selSetElement(sel, 4, 4, SEL_HIT);
243
0
    selSetElement(sel, 8, 1, SEL_MISS);
244
0
    selSetElement(sel, 8, 3, SEL_HIT);
245
0
    selSetElement(sel, 12, 0, SEL_MISS);
246
0
    selSetElement(sel, 12, 2, SEL_HIT);
247
0
    selaAddSel(sela, sel, "sel_sl1", 0);
248
249
    /*--------------------------------------------------------------*
250
     *                           Corners                            *
251
     *  This allows for up to 3 missing edge pixels at the corner   *
252
     *--------------------------------------------------------------*/
253
0
    sel = selCreateBrick(4, 4, 1, 1, SEL_MISS);
254
0
    selSetElement(sel, 1, 1, SEL_DONT_CARE);
255
0
    selSetElement(sel, 1, 2, SEL_DONT_CARE);
256
0
    selSetElement(sel, 2, 1, SEL_DONT_CARE);
257
0
    selSetElement(sel, 1, 3, SEL_HIT);
258
0
    selSetElement(sel, 2, 2, SEL_HIT);
259
0
    selSetElement(sel, 2, 3, SEL_HIT);
260
0
    selSetElement(sel, 3, 1, SEL_HIT);
261
0
    selSetElement(sel, 3, 2, SEL_HIT);
262
0
    selSetElement(sel, 3, 3, SEL_HIT);
263
0
    selaAddSel(sela, sel, "sel_ulc", 0);
264
265
0
    sel = selCreateBrick(4, 4, 1, 2, SEL_MISS);
266
0
    selSetElement(sel, 1, 1, SEL_DONT_CARE);
267
0
    selSetElement(sel, 1, 2, SEL_DONT_CARE);
268
0
    selSetElement(sel, 2, 2, SEL_DONT_CARE);
269
0
    selSetElement(sel, 1, 0, SEL_HIT);
270
0
    selSetElement(sel, 2, 0, SEL_HIT);
271
0
    selSetElement(sel, 2, 1, SEL_HIT);
272
0
    selSetElement(sel, 3, 0, SEL_HIT);
273
0
    selSetElement(sel, 3, 1, SEL_HIT);
274
0
    selSetElement(sel, 3, 2, SEL_HIT);
275
0
    selaAddSel(sela, sel, "sel_urc", 0);
276
277
0
    sel = selCreateBrick(4, 4, 2, 1, SEL_MISS);
278
0
    selSetElement(sel, 1, 1, SEL_DONT_CARE);
279
0
    selSetElement(sel, 2, 1, SEL_DONT_CARE);
280
0
    selSetElement(sel, 2, 2, SEL_DONT_CARE);
281
0
    selSetElement(sel, 0, 1, SEL_HIT);
282
0
    selSetElement(sel, 0, 2, SEL_HIT);
283
0
    selSetElement(sel, 0, 3, SEL_HIT);
284
0
    selSetElement(sel, 1, 2, SEL_HIT);
285
0
    selSetElement(sel, 1, 3, SEL_HIT);
286
0
    selSetElement(sel, 2, 3, SEL_HIT);
287
0
    selaAddSel(sela, sel, "sel_llc", 0);
288
289
0
    sel = selCreateBrick(4, 4, 2, 2, SEL_MISS);
290
0
    selSetElement(sel, 1, 2, SEL_DONT_CARE);
291
0
    selSetElement(sel, 2, 1, SEL_DONT_CARE);
292
0
    selSetElement(sel, 2, 2, SEL_DONT_CARE);
293
0
    selSetElement(sel, 0, 0, SEL_HIT);
294
0
    selSetElement(sel, 0, 1, SEL_HIT);
295
0
    selSetElement(sel, 0, 2, SEL_HIT);
296
0
    selSetElement(sel, 1, 0, SEL_HIT);
297
0
    selSetElement(sel, 1, 1, SEL_HIT);
298
0
    selSetElement(sel, 2, 0, SEL_HIT);
299
0
    selaAddSel(sela, sel, "sel_lrc", 0);
300
301
0
    return sela;
302
0
}
303
304
305
/* ------------------------------------------------------------------- *
306
 *        Structuring elements for comparing with DWA operations       *
307
 * ------------------------------------------------------------------- */
308
/*!
309
 * \brief   selaAddDwaLinear()
310
 *
311
 * \param[in]    sela [optional]
312
 * \return  sela with additional sels, or NULL on error
313
 *
314
 * <pre>
315
 * Notes:
316
 *      (1) Adds all linear (horizontal, vertical) sels from
317
 *          2 to 63 pixels in length, which are the sizes over
318
 *          which dwa code can be generated.
319
 * </pre>
320
 */
321
SELA *
322
selaAddDwaLinear(SELA  *sela)
323
0
{
324
0
char     name[L_BUF_SIZE];
325
0
l_int32  i;
326
0
SEL     *sel;
327
328
0
    if (!sela) {
329
0
        if ((sela = selaCreate(0)) == NULL)
330
0
            return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
331
0
    }
332
333
0
    for (i = 2; i < 64; i++) {
334
0
        sel = selCreateBrick(1, i, 0, i / 2, 1);
335
0
        snprintf(name, L_BUF_SIZE, "sel_%dh", i);
336
0
        selaAddSel(sela, sel, name, 0);
337
0
    }
338
0
    for (i = 2; i < 64; i++) {
339
0
        sel = selCreateBrick(i, 1, i / 2, 0, 1);
340
0
        snprintf(name, L_BUF_SIZE, "sel_%dv", i);
341
0
        selaAddSel(sela, sel, name, 0);
342
0
    }
343
0
    return sela;
344
0
}
345
346
347
/*!
348
 * \brief   selaAddDwaCombs()
349
 *
350
 * \param[in]    sela [optional]
351
 * \return  sela with additional sels, or NULL on error
352
 *
353
 * <pre>
354
 * Notes:
355
 *      (1) Adds all comb (horizontal, vertical) Sels that are
356
 *          used in composite linear morphological operations
357
 *          up to 63 pixels in length, which are the sizes over
358
 *          which dwa code can be generated.
359
 * </pre>
360
 */
361
SELA *
362
selaAddDwaCombs(SELA  *sela)
363
0
{
364
0
char     name[L_BUF_SIZE];
365
0
l_int32  i, f1, f2, prevsize, size;
366
0
SEL     *selh, *selv;
367
368
0
    if (!sela) {
369
0
        if ((sela = selaCreate(0)) == NULL)
370
0
            return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
371
0
    }
372
373
0
    prevsize = 0;
374
0
    for (i = 4; i < 64; i++) {
375
0
        selectComposableSizes(i, &f1, &f2);
376
0
        size = f1 * f2;
377
0
        if (size == prevsize)
378
0
            continue;
379
0
        selectComposableSels(i, L_HORIZ, NULL, &selh);
380
0
        if (selh) {
381
0
            snprintf(name, L_BUF_SIZE, "sel_comb_%dh", size);
382
0
            selaAddSel(sela, selh, name, 0);
383
0
        } else {
384
0
            L_ERROR("selh not made for i = %d\n", __func__, i);
385
0
        }
386
0
        selectComposableSels(i, L_VERT, NULL, &selv);
387
0
        if (selv) {
388
0
            snprintf(name, L_BUF_SIZE, "sel_comb_%dv", size);
389
0
            selaAddSel(sela, selv, name, 0);
390
0
        } else {
391
0
            L_ERROR("selv not made for i = %d\n", __func__, i);
392
0
        }
393
0
        prevsize = size;
394
0
    }
395
396
0
    return sela;
397
0
}
398
399
400
/* ------------------------------------------------------------------- *
401
 *          Structuring elements for the intersection of lines         *
402
 * ------------------------------------------------------------------- */
403
/*!
404
 * \brief   selaAddCrossJunctions()
405
 *
406
 * \param[in]    sela [optional]
407
 * \param[in]    hlsize length of each line of hits from origin
408
 * \param[in]    mdist distance of misses from the origin
409
 * \param[in]    norient number of orientations; max of 8
410
 * \param[in]    debugflag 1 for debug output
411
 * \return  sela with additional sels, or NULL on error
412
 *
413
 * <pre>
414
 * Notes:
415
 *      (1) Adds hitmiss Sels for the intersection of two lines.
416
 *          If the lines are very thin, they must be nearly orthogonal
417
 *          to register.
418
 *      (2) The number of Sels generated is equal to %norient.
419
 *      (3) If %norient == 2, this generates 2 Sels of crosses, each with
420
 *          two perpendicular lines of hits.  One Sel has horizontal and
421
 *          vertical hits; the other has hits along lines at +-45 degrees.
422
 *          Likewise, if %norient == 3, this generates 3 Sels of crosses
423
 *          oriented at 30 degrees with each other.
424
 *      (4) It is suggested that %hlsize be chosen at least 1 greater
425
 *          than %mdist.  Try values of (%hlsize, %mdist) such as
426
 *          (6,5), (7,6), (8,7), (9,7), etc.
427
 * </pre>
428
 */
429
SELA *
430
selaAddCrossJunctions(SELA      *sela,
431
                      l_float32  hlsize,
432
                      l_float32  mdist,
433
                      l_int32    norient,
434
                      l_int32    debugflag)
435
0
{
436
0
char       name[L_BUF_SIZE];
437
0
l_int32    i, j, w, xc, yc;
438
0
l_float64  pi, halfpi, radincr, radang;
439
0
l_float64  angle;
440
0
PIX       *pixc, *pixm, *pixt;
441
0
PIXA      *pixa;
442
0
PTA       *pta1, *pta2, *pta3, *pta4;
443
0
SEL       *sel;
444
445
0
    if (hlsize <= 0)
446
0
        return (SELA *)ERROR_PTR("hlsize not > 0", __func__, NULL);
447
0
    if (norient < 1 || norient > 8)
448
0
        return (SELA *)ERROR_PTR("norient not in [1, ... 8]", __func__, NULL);
449
450
0
    if (!sela) {
451
0
        if ((sela = selaCreate(0)) == NULL)
452
0
            return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
453
0
    }
454
455
0
    pi = 3.1415926535;
456
0
    halfpi = 3.1415926535 / 2.0;
457
0
    radincr = halfpi / (l_float64)norient;
458
0
    w = (l_int32)(2.2 * (L_MAX(hlsize, mdist) + 0.5));
459
0
    if (w % 2 == 0)
460
0
        w++;
461
0
    xc = w / 2;
462
0
    yc = w / 2;
463
464
0
    pixa = pixaCreate(norient);
465
0
    for (i = 0; i < norient; i++) {
466
467
            /* Set the don't cares */
468
0
        pixc = pixCreate(w, w, 32);
469
0
        pixSetAll(pixc);
470
471
            /* Add the green lines of hits */
472
0
        pixm = pixCreate(w, w, 1);
473
0
        radang = (l_float32)i * radincr;
474
0
        pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang);
475
0
        pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + halfpi);
476
0
        pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi);
477
0
        pta4 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi + halfpi);
478
0
        ptaJoin(pta1, pta2, 0, -1);
479
0
        ptaJoin(pta1, pta3, 0, -1);
480
0
        ptaJoin(pta1, pta4, 0, -1);
481
0
        pixRenderPta(pixm, pta1, L_SET_PIXELS);
482
0
        pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000);
483
0
        ptaDestroy(&pta1);
484
0
        ptaDestroy(&pta2);
485
0
        ptaDestroy(&pta3);
486
0
        ptaDestroy(&pta4);
487
488
            /* Add red misses between the lines */
489
0
        for (j = 0; j < 4; j++) {
490
0
            angle = radang + (j - 0.5) * halfpi;
491
0
            pixSetPixel(pixc, xc + (l_int32)(mdist * cos(angle)),
492
0
                        yc + (l_int32)(mdist * sin(angle)), 0xff000000);
493
0
        }
494
495
            /* Add dark green for origin */
496
0
        pixSetPixel(pixc, xc, yc, 0x00550000);
497
498
            /* Generate the sel */
499
0
        sel = selCreateFromColorPix(pixc, NULL);
500
0
        snprintf(name, sizeof(name), "sel_cross_%d", i);
501
0
        selaAddSel(sela, sel, name, 0);
502
503
0
        if (debugflag) {
504
0
            pixt = pixScaleBySampling(pixc, 10.0, 10.0);
505
0
            pixaAddPix(pixa, pixt, L_INSERT);
506
0
        }
507
0
        pixDestroy(&pixm);
508
0
        pixDestroy(&pixc);
509
0
    }
510
511
0
    if (debugflag) {
512
0
        l_int32  w;
513
0
        lept_mkdir("lept/sel");
514
0
        pixaGetPixDimensions(pixa, 0, &w, NULL, NULL);
515
0
        pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 1, 0, 10, 2);
516
0
        pixWriteDebug("/tmp/lept/sel/xsel1.png", pixt, IFF_PNG);
517
0
        pixDisplay(pixt, 0, 100);
518
0
        pixDestroy(&pixt);
519
0
        pixt = selaDisplayInPix(sela, 15, 2, 20, 1);
520
0
        pixWriteDebug("/tmp/lept/sel/xsel2.png", pixt, IFF_PNG);
521
0
        pixDisplay(pixt, 500, 100);
522
0
        pixDestroy(&pixt);
523
0
        selaWriteStream(stderr, sela);
524
0
    }
525
0
    pixaDestroy(&pixa);
526
527
0
    return sela;
528
0
}
529
530
531
/*!
532
 * \brief   selaAddTJunctions()
533
 *
534
 * \param[in]    sela [optional]
535
 * \param[in]    hlsize length of each line of hits from origin
536
 * \param[in]    mdist distance of misses from the origin
537
 * \param[in]    norient number of orientations; max of 8
538
 * \param[in]    debugflag 1 for debug output
539
 * \return  sela with additional sels, or NULL on error
540
 *
541
 * <pre>
542
 * Notes:
543
 *      (1) Adds hitmiss Sels for the T-junction of two lines.
544
 *          If the lines are very thin, they must be nearly orthogonal
545
 *          to register.
546
 *      (2) The number of Sels generated is 4 * %norient.
547
 *      (3) It is suggested that %hlsize be chosen at least 1 greater
548
 *          than %mdist.  Try values of (%hlsize, %mdist) such as
549
 *          (6,5), (7,6), (8,7), (9,7), etc.
550
 * </pre>
551
 */
552
SELA *
553
selaAddTJunctions(SELA      *sela,
554
                  l_float32  hlsize,
555
                  l_float32  mdist,
556
                  l_int32    norient,
557
                  l_int32    debugflag)
558
0
{
559
0
char       name[L_BUF_SIZE];
560
0
l_int32    i, j, k, w, xc, yc;
561
0
l_float64  pi, halfpi, radincr, jang, radang;
562
0
l_float64  angle[3], dist[3];
563
0
PIX       *pixc, *pixm, *pixt;
564
0
PIXA      *pixa;
565
0
PTA       *pta1, *pta2, *pta3;
566
0
SEL       *sel;
567
568
0
    if (hlsize <= 2)
569
0
        return (SELA *)ERROR_PTR("hlsizel not > 1", __func__, NULL);
570
0
    if (norient < 1 || norient > 8)
571
0
        return (SELA *)ERROR_PTR("norient not in [1, ... 8]", __func__, NULL);
572
573
0
    if (!sela) {
574
0
        if ((sela = selaCreate(0)) == NULL)
575
0
            return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
576
0
    }
577
578
0
    pi = 3.1415926535;
579
0
    halfpi = 3.1415926535 / 2.0;
580
0
    radincr = halfpi / (l_float32)norient;
581
0
    w = (l_int32)(2.4 * (L_MAX(hlsize, mdist) + 0.5));
582
0
    if (w % 2 == 0)
583
0
        w++;
584
0
    xc = w / 2;
585
0
    yc = w / 2;
586
587
0
    pixa = pixaCreate(4 * norient);
588
0
    for (i = 0; i < norient; i++) {
589
0
        for (j = 0; j < 4; j++) {  /* 4 orthogonal orientations */
590
0
            jang = (l_float32)j * halfpi;
591
592
                /* Set the don't cares */
593
0
            pixc = pixCreate(w, w, 32);
594
0
            pixSetAll(pixc);
595
596
                /* Add the green lines of hits */
597
0
            pixm = pixCreate(w, w, 1);
598
0
            radang = (l_float32)i * radincr;
599
0
            pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang);
600
0
            pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1,
601
0
                                         jang + radang + halfpi);
602
0
            pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1,
603
0
                                         jang + radang + pi);
604
0
            ptaJoin(pta1, pta2, 0, -1);
605
0
            ptaJoin(pta1, pta3, 0, -1);
606
0
            pixRenderPta(pixm, pta1, L_SET_PIXELS);
607
0
            pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000);
608
0
            ptaDestroy(&pta1);
609
0
            ptaDestroy(&pta2);
610
0
            ptaDestroy(&pta3);
611
612
                /* Add red misses between the lines */
613
0
            angle[0] = radang + jang - halfpi;
614
0
            angle[1] = radang + jang + 0.5 * halfpi;
615
0
            angle[2] = radang + jang + 1.5 * halfpi;
616
0
            dist[0] = 0.8 * mdist;
617
0
            dist[1] = dist[2] = mdist;
618
0
            for (k = 0; k < 3; k++) {
619
0
                pixSetPixel(pixc, xc + (l_int32)(dist[k] * cos(angle[k])),
620
0
                            yc + (l_int32)(dist[k] * sin(angle[k])),
621
0
                            0xff000000);
622
0
            }
623
624
                /* Add dark green for origin */
625
0
            pixSetPixel(pixc, xc, yc, 0x00550000);
626
627
                /* Generate the sel */
628
0
            sel = selCreateFromColorPix(pixc, NULL);
629
0
            snprintf(name, sizeof(name), "sel_cross_%d", 4 * i + j);
630
0
            selaAddSel(sela, sel, name, 0);
631
632
0
            if (debugflag) {
633
0
                pixt = pixScaleBySampling(pixc, 10.0, 10.0);
634
0
                pixaAddPix(pixa, pixt, L_INSERT);
635
0
            }
636
0
            pixDestroy(&pixm);
637
0
            pixDestroy(&pixc);
638
0
        }
639
0
    }
640
641
0
    if (debugflag) {
642
0
        l_int32  w;
643
0
        lept_mkdir("lept/sel");
644
0
        pixaGetPixDimensions(pixa, 0, &w, NULL, NULL);
645
0
        pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 4, 0, 10, 2);
646
0
        pixWriteDebug("/tmp/lept/sel/tsel1.png", pixt, IFF_PNG);
647
0
        pixDisplay(pixt, 0, 100);
648
0
        pixDestroy(&pixt);
649
0
        pixt = selaDisplayInPix(sela, 15, 2, 20, 4);
650
0
        pixWriteDebug("/tmp/lept/sel/tsel2.png", pixt, IFF_PNG);
651
0
        pixDisplay(pixt, 500, 100);
652
0
        pixDestroy(&pixt);
653
0
        selaWriteStream(stderr, sela);
654
0
    }
655
0
    pixaDestroy(&pixa);
656
657
0
    return sela;
658
0
}
659
660
661
/* -------------------------------------------------------------------------- *
662
 *    Structuring elements for connectivity-preserving thinning operations    *
663
 * -------------------------------------------------------------------------- */
664
665
    /* ------------------------------------------------------------
666
     * These sels (and their rotated counterparts) are the useful
667
     * 3x3 Sels for thinning.   The notation is based on
668
     * "Connectivity-preserving morphological image transformations,"
669
     * a version of which can be found at
670
     *           http://www.leptonica.com/papers/conn.pdf
671
     * ------------------------------------------------------------ */
672
673
    /* Sels for 4-connected thinning */
674
static const char *sel_4_1 = "  x"
675
                             "oCx"
676
                             "  x";
677
static const char *sel_4_2 = "  x"
678
                             "oCx"
679
                             " o ";
680
static const char *sel_4_3 = " o "
681
                             "oCx"
682
                             "  x";
683
static const char *sel_4_4 = " o "
684
                             "oCx"
685
                             " o ";
686
static const char *sel_4_5 = " ox"
687
                             "oCx"
688
                             " o ";
689
static const char *sel_4_6 = " o "
690
                             "oCx"
691
                             " ox";
692
static const char *sel_4_7 = " xx"
693
                             "oCx"
694
                             " o ";
695
static const char *sel_4_8 = "  x"
696
                             "oCx"
697
                             "o x";
698
static const char *sel_4_9 = "o x"
699
                             "oCx"
700
                             "  x";
701
702
    /* Sels for 8-connected thinning */
703
static const char *sel_8_1 = " x "
704
                             "oCx"
705
                             " x ";
706
static const char *sel_8_2 = " x "
707
                             "oCx"
708
                             "o  ";
709
static const char *sel_8_3 = "o  "
710
                             "oCx"
711
                             " x ";
712
static const char *sel_8_4 = "o  "
713
                             "oCx"
714
                             "o  ";
715
static const char *sel_8_5 = "o x"
716
                             "oCx"
717
                             "o  ";
718
static const char *sel_8_6 = "o  "
719
                             "oCx"
720
                             "o x";
721
static const char *sel_8_7 = " x "
722
                             "oCx"
723
                             "oo ";
724
static const char *sel_8_8 = " x "
725
                             "oCx"
726
                             "ox ";
727
static const char *sel_8_9 = "ox "
728
                             "oCx"
729
                             " x ";
730
731
    /* Sels for both 4 and 8-connected thinning */
732
static const char *sel_48_1 = " xx"
733
                              "oCx"
734
                              "oo ";
735
static const char *sel_48_2 = "o x"
736
                              "oCx"
737
                              "o x";
738
739
740
/*!
741
 * \brief   sela4ccThin()
742
 *
743
 * \param[in]    sela [optional]
744
 * \return  sela with additional sels, or NULL on error
745
 *
746
 * <pre>
747
 * Notes:
748
 *      (1) Adds the 9 basic sels for 4-cc thinning.
749
 * </pre>
750
 */
751
SELA *
752
sela4ccThin(SELA  *sela)
753
0
{
754
0
SEL  *sel;
755
756
0
    if (!sela) sela = selaCreate(9);
757
758
0
    sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1");
759
0
    selaAddSel(sela, sel, NULL, 0);
760
0
    sel = selCreateFromString(sel_4_2, 3, 3, "sel_4_2");
761
0
    selaAddSel(sela, sel, NULL, 0);
762
0
    sel = selCreateFromString(sel_4_3, 3, 3, "sel_4_3");
763
0
    selaAddSel(sela, sel, NULL, 0);
764
0
    sel = selCreateFromString(sel_4_4, 3, 3, "sel_4_4");
765
0
    selaAddSel(sela, sel, NULL, 0);
766
0
    sel = selCreateFromString(sel_4_5, 3, 3, "sel_4_5");
767
0
    selaAddSel(sela, sel, NULL, 0);
768
0
    sel = selCreateFromString(sel_4_6, 3, 3, "sel_4_6");
769
0
    selaAddSel(sela, sel, NULL, 0);
770
0
    sel = selCreateFromString(sel_4_7, 3, 3, "sel_4_7");
771
0
    selaAddSel(sela, sel, NULL, 0);
772
0
    sel = selCreateFromString(sel_4_8, 3, 3, "sel_4_8");
773
0
    selaAddSel(sela, sel, NULL, 0);
774
0
    sel = selCreateFromString(sel_4_9, 3, 3, "sel_4_9");
775
0
    selaAddSel(sela, sel, NULL, 0);
776
777
0
    return sela;
778
0
}
779
780
781
/*!
782
 * \brief   sela8ccThin()
783
 *
784
 * \param[in]    sela [optional]
785
 * \return  sela with additional sels, or NULL on error
786
 *
787
 * <pre>
788
 * Notes:
789
 *      (1) Adds the 9 basic sels for 8-cc thinning.
790
 * </pre>
791
 */
792
SELA *
793
sela8ccThin(SELA  *sela)
794
0
{
795
0
SEL  *sel;
796
797
0
    if (!sela) sela = selaCreate(9);
798
799
0
    sel = selCreateFromString(sel_8_1, 3, 3, "sel_8_1");
800
0
    selaAddSel(sela, sel, NULL, 0);
801
0
    sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2");
802
0
    selaAddSel(sela, sel, NULL, 0);
803
0
    sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3");
804
0
    selaAddSel(sela, sel, NULL, 0);
805
0
    sel = selCreateFromString(sel_8_4, 3, 3, "sel_8_4");
806
0
    selaAddSel(sela, sel, NULL, 0);
807
0
    sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5");
808
0
    selaAddSel(sela, sel, NULL, 0);
809
0
    sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6");
810
0
    selaAddSel(sela, sel, NULL, 0);
811
0
    sel = selCreateFromString(sel_8_7, 3, 3, "sel_8_7");
812
0
    selaAddSel(sela, sel, NULL, 0);
813
0
    sel = selCreateFromString(sel_8_8, 3, 3, "sel_8_8");
814
0
    selaAddSel(sela, sel, NULL, 0);
815
0
    sel = selCreateFromString(sel_8_9, 3, 3, "sel_8_9");
816
0
    selaAddSel(sela, sel, NULL, 0);
817
818
0
    return sela;
819
0
}
820
821
822
/*!
823
 * \brief   sela4and8ccThin()
824
 *
825
 * \param[in]    sela [optional]
826
 * \return  sela with additional sels, or NULL on error
827
 *
828
 * <pre>
829
 * Notes:
830
 *      (1) Adds the 2 basic sels for either 4-cc or 8-cc thinning.
831
 * </pre>
832
 */
833
SELA *
834
sela4and8ccThin(SELA  *sela)
835
0
{
836
0
SEL  *sel;
837
838
0
    if (!sela) sela = selaCreate(2);
839
840
0
    sel = selCreateFromString(sel_48_1, 3, 3, "sel_48_1");
841
0
    selaAddSel(sela, sel, NULL, 0);
842
0
    sel = selCreateFromString(sel_48_2, 3, 3, "sel_48_2");
843
0
    selaAddSel(sela, sel, NULL, 0);
844
845
0
    return sela;
846
0
}
847
848
849
/* -------------------------------------------------------------------------- *
850
 *                        Other structuring elements                          *
851
 * -------------------------------------------------------------------------- */
852
/*!
853
 * \brief   selMakePlusSign()
854
 *
855
 * \param[in]    size        side of containing square
856
 * \param[in]    linewidth   of lines
857
 * \return  sel, or NULL on error
858
 *
859
 * <pre>
860
 * Notes:
861
 *      (1) Useful for debugging to show location of selected pixels.
862
 *      (2) See displaySelectedPixels() for an example of use.
863
 * </pre>
864
 */
865
SEL *
866
selMakePlusSign(l_int32  size,
867
                l_int32  linewidth)
868
0
{
869
0
PIX  *pix;
870
0
SEL  *sel;
871
872
0
    if (size < 3 || linewidth > size)
873
0
        return (SEL *)ERROR_PTR("invalid input", __func__, NULL);
874
875
0
    pix = pixCreate(size, size, 1);
876
0
    pixRenderLine(pix, size / 2, 0, size / 2, size - 1,
877
0
                  linewidth, L_SET_PIXELS);
878
0
    pixRenderLine(pix, 0, size / 2, size, size / 2,
879
0
                  linewidth, L_SET_PIXELS);
880
0
    sel = selCreateFromPix(pix, size / 2, size / 2, "plus_sign");
881
0
    pixDestroy(&pix);
882
0
    return sel;
883
0
}