Coverage Report

Created: 2024-02-28 06:46

/src/leptonica/src/sel1.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
/*!
29
 * \file sel1.c
30
 * <pre>
31
 *
32
 *      Basic ops on Sels and Selas
33
 *
34
 *         Create/destroy/copy:
35
 *            SELA      *selaCreate()
36
 *            void       selaDestroy()
37
 *            SEL       *selCreate()
38
 *            void       selDestroy()
39
 *            SEL       *selCopy()
40
 *            SEL       *selCreateBrick()
41
 *            SEL       *selCreateComb()
42
 *
43
 *         Helper proc:
44
 *            l_int32  **create2dIntArray()
45
 *
46
 *         Extension of sela:
47
 *            SELA      *selaAddSel()
48
 *            static l_int32  selaExtendArray()
49
 *
50
 *         Accessors:
51
 *            l_int32    selaGetCount()
52
 *            SEL       *selaGetSel()
53
 *            char      *selGetName()
54
 *            l_int32    selSetName()
55
 *            l_int32    selaFindSelByName()
56
 *            l_int32    selGetElement()
57
 *            l_int32    selSetElement()
58
 *            l_int32    selGetParameters()
59
 *            l_int32    selSetOrigin()
60
 *            l_int32    selGetTypeAtOrigin()
61
 *            char      *selaGetBrickName()
62
 *            char      *selaGetCombName()
63
 *     static char      *selaComputeCompositeParameters()
64
 *            l_int32    getCompositeParameters()
65
 *            SARRAY    *selaGetSelnames()
66
 *
67
 *         Max translations for erosion and hmt
68
 *            l_int32    selFindMaxTranslations()
69
 *
70
 *         Rotation by multiples of 90 degrees
71
 *            SEL       *selRotateOrth()
72
 *
73
 *         Sela and Sel serialized I/O
74
 *            SELA      *selaRead()
75
 *            SELA      *selaReadStream()
76
 *            SEL       *selRead()
77
 *            SEL       *selReadStream()
78
 *            l_int32    selaWrite()
79
 *            l_int32    selaWriteStream()
80
 *            l_int32    selWrite()
81
 *            l_int32    selWriteStream()
82
 *
83
 *         Building custom hit-miss sels from compiled strings
84
 *            SEL       *selCreateFromString()
85
 *            char      *selPrintToString()     [for debugging]
86
 *
87
 *         Building custom hit-miss sels from a simple file format
88
 *            SELA      *selaCreateFromFile()
89
 *            static SEL *selCreateFromSArray()
90
 *
91
 *         Making hit-only sels from Pta and Pix
92
 *            SEL       *selCreateFromPta()
93
 *            SEL       *selCreateFromPix()
94
 *
95
 *         Making hit-miss sels from Pix and image files
96
 *            SEL       *selReadFromColorImage()
97
 *            SEL       *selCreateFromColorPix()
98
              SELA      *selaCreateFromColorPixa()
99
 *
100
 *         Printable display of sel
101
 *            PIX       *selDisplayInPix()
102
 *            PIX       *selaDisplayInPix()
103
 *
104
 *     Usage notes:
105
 *        In this file we have seven functions that make sels:
106
 *          (1)  selCreate(), with input (h, w, [name])
107
 *               The generic function.  Roll your own, using selSetElement().
108
 *          (2)  selCreateBrick(), with input (h, w, cy, cx, val)
109
 *               The most popular function.  Makes a rectangular sel of
110
 *               all hits, misses or don't-cares.  We have many morphology
111
 *               operations that create a sel of all hits, use it, and
112
 *               destroy it.
113
 *          (3)  selCreateFromString() with input (text, h, w, [name])
114
 *               Adam Langley's clever function, allows you to make a hit-miss
115
 *               sel from a string in code that is geometrically laid out
116
 *               just like the actual sel.
117
 *          (4)  selaCreateFromFile() with input (filename)
118
 *               This parses a simple file format to create an array of
119
 *               hit-miss sels.  The sel data uses the same encoding
120
 *               as in (3), with geometrical layout enforced.
121
 *          (5)  selCreateFromPta() with input (pta, cy, cx, [name])
122
 *               Another way to make a sel with only hits.
123
 *          (6)  selCreateFromPix() with input (pix, cy, cx, [name])
124
 *               Yet another way to make a sel from hits.
125
 *          (7)  selCreateFromColorPix() with input (pix, name).
126
 *               Another way to make a general hit-miss sel, starting with
127
 *               an image editor.
128
 *        In addition, there are three functions in selgen.c that
129
 *        automatically generate a hit-miss sel from a pix and
130
 *        a number of parameters.  This is useful for problems like
131
 *        "find all patterns that look like this one."
132
 *
133
 *        Consistency, being the hobgoblin of small minds,
134
 *        is adhered to here in the dimensioning and accessing of sels.
135
 *        Everything is done in standard matrix (row, column) order.
136
 *        When we set specific elements in a sel, we likewise use
137
 *        (row, col) ordering:
138
 *             selSetElement(), with input (row, col, type)
139
 * </pre>
140
 */
141
142
#ifdef HAVE_CONFIG_H
143
#include <config_auto.h>
144
#endif  /* HAVE_CONFIG_H */
145
146
#include <string.h>
147
#include "allheaders.h"
148
149
    /* Bounds on sel ptr array size */
150
static const l_uint32  MaxPtrArraySize = 10000;
151
static const l_int32 InitialPtrArraySize = 50;      /*!< n'importe quoi */
152
153
    /* Bounds on kernel size */
154
static const l_uint32  MaxKernelSize = 10000;
155
156
    /* Bounds on pix template size */
157
static const l_uint32  MaxPixTemplateSize = 100;
158
static const l_uint32  MaxPixTemplateHits = 1000;
159
160
    /* Static functions */
161
static l_int32 selaExtendArray(SELA *sela);
162
static SEL *selCreateFromSArray(SARRAY *sa, l_int32 first, l_int32 last);
163
164
struct CompParameterMap
165
{
166
    l_int32  size;
167
    l_int32  size1;
168
    l_int32  size2;
169
    char     selnameh1[20];
170
    char     selnameh2[20];
171
    char     selnamev1[20];
172
    char     selnamev2[20];
173
};
174
175
static const struct CompParameterMap  comp_parameter_map[] =
176
    { { 2, 2, 1, "sel_2h", "", "sel_2v", "" },
177
      { 3, 3, 1, "sel_3h", "", "sel_3v", "" },
178
      { 4, 2, 2, "sel_2h", "sel_comb_4h", "sel_2v", "sel_comb_4v" },
179
      { 5, 5, 1, "sel_5h", "", "sel_5v", "" },
180
      { 6, 3, 2, "sel_3h", "sel_comb_6h", "sel_3v", "sel_comb_6v" },
181
      { 7, 7, 1, "sel_7h", "", "sel_7v", "" },
182
      { 8, 4, 2, "sel_4h", "sel_comb_8h", "sel_4v", "sel_comb_8v" },
183
      { 9, 3, 3, "sel_3h", "sel_comb_9h", "sel_3v", "sel_comb_9v" },
184
      { 10, 5, 2, "sel_5h", "sel_comb_10h", "sel_5v", "sel_comb_10v" },
185
      { 11, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" },
186
      { 12, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" },
187
      { 13, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" },
188
      { 14, 7, 2, "sel_7h", "sel_comb_14h", "sel_7v", "sel_comb_14v" },
189
      { 15, 5, 3, "sel_5h", "sel_comb_15h", "sel_5v", "sel_comb_15v" },
190
      { 16, 4, 4, "sel_4h", "sel_comb_16h", "sel_4v", "sel_comb_16v" },
191
      { 17, 4, 4, "sel_4h", "sel_comb_16h", "sel_4v", "sel_comb_16v" },
192
      { 18, 6, 3, "sel_6h", "sel_comb_18h", "sel_6v", "sel_comb_18v" },
193
      { 19, 5, 4, "sel_5h", "sel_comb_20h", "sel_5v", "sel_comb_20v" },
194
      { 20, 5, 4, "sel_5h", "sel_comb_20h", "sel_5v", "sel_comb_20v" },
195
      { 21, 7, 3, "sel_7h", "sel_comb_21h", "sel_7v", "sel_comb_21v" },
196
      { 22, 11, 2, "sel_11h", "sel_comb_22h", "sel_11v", "sel_comb_22v" },
197
      { 23, 6, 4, "sel_6h", "sel_comb_24h", "sel_6v", "sel_comb_24v" },
198
      { 24, 6, 4, "sel_6h", "sel_comb_24h", "sel_6v", "sel_comb_24v" },
199
      { 25, 5, 5, "sel_5h", "sel_comb_25h", "sel_5v", "sel_comb_25v" },
200
      { 26, 5, 5, "sel_5h", "sel_comb_25h", "sel_5v", "sel_comb_25v" },
201
      { 27, 9, 3, "sel_9h", "sel_comb_27h", "sel_9v", "sel_comb_27v" },
202
      { 28, 7, 4, "sel_7h", "sel_comb_28h", "sel_7v", "sel_comb_28v" },
203
      { 29, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" },
204
      { 30, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" },
205
      { 31, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" },
206
      { 32, 8, 4, "sel_8h", "sel_comb_32h", "sel_8v", "sel_comb_32v" },
207
      { 33, 11, 3, "sel_11h", "sel_comb_33h", "sel_11v", "sel_comb_33v" },
208
      { 34, 7, 5, "sel_7h", "sel_comb_35h", "sel_7v", "sel_comb_35v" },
209
      { 35, 7, 5, "sel_7h", "sel_comb_35h", "sel_7v", "sel_comb_35v" },
210
      { 36, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" },
211
      { 37, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" },
212
      { 38, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" },
213
      { 39, 13, 3, "sel_13h", "sel_comb_39h", "sel_13v", "sel_comb_39v" },
214
      { 40, 8, 5, "sel_8h", "sel_comb_40h", "sel_8v", "sel_comb_40v" },
215
      { 41, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" },
216
      { 42, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" },
217
      { 43, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" },
218
      { 44, 11, 4, "sel_11h", "sel_comb_44h", "sel_11v", "sel_comb_44v" },
219
      { 45, 9, 5, "sel_9h", "sel_comb_45h", "sel_9v", "sel_comb_45v" },
220
      { 46, 9, 5, "sel_9h", "sel_comb_45h", "sel_9v", "sel_comb_45v" },
221
      { 47, 8, 6, "sel_8h", "sel_comb_48h", "sel_8v", "sel_comb_48v" },
222
      { 48, 8, 6, "sel_8h", "sel_comb_48h", "sel_8v", "sel_comb_48v" },
223
      { 49, 7, 7, "sel_7h", "sel_comb_49h", "sel_7v", "sel_comb_49v" },
224
      { 50, 10, 5, "sel_10h", "sel_comb_50h", "sel_10v", "sel_comb_50v" },
225
      { 51, 10, 5, "sel_10h", "sel_comb_50h", "sel_10v", "sel_comb_50v" },
226
      { 52, 13, 4, "sel_13h", "sel_comb_52h", "sel_13v", "sel_comb_52v" },
227
      { 53, 9, 6, "sel_9h", "sel_comb_54h", "sel_9v", "sel_comb_54v" },
228
      { 54, 9, 6, "sel_9h", "sel_comb_54h", "sel_9v", "sel_comb_54v" },
229
      { 55, 11, 5, "sel_11h", "sel_comb_55h", "sel_11v", "sel_comb_55v" },
230
      { 56, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" },
231
      { 57, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" },
232
      { 58, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" },
233
      { 59, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" },
234
      { 60, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" },
235
      { 61, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" },
236
      { 62, 9, 7, "sel_9h", "sel_comb_63h", "sel_9v", "sel_comb_63v" },
237
      { 63, 9, 7, "sel_9h", "sel_comb_63h", "sel_9v", "sel_comb_63v" } };
238
239
240
241
/*------------------------------------------------------------------------*
242
 *                      Create / Destroy / Copy                           *
243
 *------------------------------------------------------------------------*/
244
/*!
245
 * \brief   selaCreate()
246
 *
247
 * \param[in]    n    initial number of sel ptrs; use 0 for default
248
 * \return  sela, or NULL on error
249
 */
250
SELA *
251
selaCreate(l_int32  n)
252
0
{
253
0
SELA  *sela;
254
255
0
    if (n <= 0 || n > (l_int32)MaxPtrArraySize)
256
0
        n = InitialPtrArraySize;
257
258
        /* Make array of sel ptrs */
259
0
    sela = (SELA *)LEPT_CALLOC(1, sizeof(SELA));
260
0
    sela->nalloc = n;
261
0
    sela->n = 0;
262
0
    sela->sel = (SEL **)LEPT_CALLOC(n, sizeof(SEL *));
263
0
    return sela;
264
0
}
265
266
267
/*!
268
 * \brief   selaDestroy()
269
 *
270
 * \param[in,out]   psela    will be set to null before returning
271
 * \return  void
272
 */
273
void
274
selaDestroy(SELA  **psela)
275
0
{
276
0
SELA    *sela;
277
0
l_int32  i;
278
279
0
    if (!psela) return;
280
0
    if ((sela = *psela) == NULL)
281
0
        return;
282
283
0
    for (i = 0; i < sela->n; i++)
284
0
        selDestroy(&sela->sel[i]);
285
0
    LEPT_FREE(sela->sel);
286
0
    LEPT_FREE(sela);
287
0
    *psela = NULL;
288
0
}
289
290
291
/*!
292
 * \brief   selCreate()
293
 *
294
 * \param[in]    height
295
 * \param[in]    width
296
 * \param[in]    name      [optional] sel name; can be null
297
 * \return  sel, or NULL on error
298
 *
299
 * <pre>
300
 * Notes:
301
 *      (1) selCreate() initializes all values to 0.
302
 *      (2) After this call, (cy,cx) and nonzero data values must be
303
 *          assigned.  If a text name is not assigned here, it will
304
 *          be needed later when the sel is put into a sela.
305
 * </pre>
306
 */
307
SEL *
308
selCreate(l_int32      height,
309
          l_int32      width,
310
          const char  *name)
311
12
{
312
12
SEL  *sel;
313
314
12
    sel = (SEL *)LEPT_CALLOC(1, sizeof(SEL));
315
12
    if (name)
316
0
        sel->name = stringNew(name);
317
12
    sel->sy = height;
318
12
    sel->sx = width;
319
12
    if ((sel->data = create2dIntArray(height, width)) == NULL) {
320
0
        LEPT_FREE(sel->name);
321
0
        LEPT_FREE(sel);
322
0
        return (SEL *)ERROR_PTR("data not allocated", __func__, NULL);
323
0
    }
324
325
12
    return sel;
326
12
}
327
328
329
/*!
330
 * \brief   selDestroy()
331
 *
332
 * \param[in,out]   psel   will be set to null before returning
333
 * \return  void
334
 */
335
void
336
selDestroy(SEL  **psel)
337
12
{
338
12
l_int32  i;
339
12
SEL     *sel;
340
341
12
    if (psel == NULL)  {
342
0
        L_WARNING("ptr address is NULL!\n", __func__);
343
0
        return;
344
0
    }
345
12
    if ((sel = *psel) == NULL)
346
0
        return;
347
348
432
    for (i = 0; i < sel->sy; i++)
349
420
        LEPT_FREE(sel->data[i]);
350
12
    LEPT_FREE(sel->data);
351
12
    if (sel->name)
352
0
        LEPT_FREE(sel->name);
353
12
    LEPT_FREE(sel);
354
12
    *psel = NULL;
355
12
}
356
357
358
/*!
359
 * \brief   selCopy()
360
 *
361
 * \param[in]    sel
362
 * \return  a copy of the sel, or NULL on error
363
 */
364
SEL *
365
selCopy(SEL  *sel)
366
0
{
367
0
l_int32  sx, sy, cx, cy, i, j;
368
0
SEL     *csel;
369
370
0
    if (!sel)
371
0
        return (SEL *)ERROR_PTR("sel not defined", __func__, NULL);
372
373
0
    csel = (SEL *)LEPT_CALLOC(1, sizeof(SEL));
374
0
    selGetParameters(sel, &sy, &sx, &cy, &cx);
375
0
    csel->sy = sy;
376
0
    csel->sx = sx;
377
0
    csel->cy = cy;
378
0
    csel->cx = cx;
379
380
0
    if ((csel->data = create2dIntArray(sy, sx)) == NULL) {
381
0
        LEPT_FREE(csel);
382
0
        return (SEL *)ERROR_PTR("sel data not made", __func__, NULL);
383
0
    }
384
385
0
    for (i = 0; i < sy; i++)
386
0
        for (j = 0; j < sx; j++)
387
0
            csel->data[i][j] = sel->data[i][j];
388
389
0
    if (sel->name)
390
0
        csel->name = stringNew(sel->name);
391
392
0
    return csel;
393
0
}
394
395
396
/*!
397
 * \brief   selCreateBrick()
398
 *
399
 * \param[in]    h, w      height, width
400
 * \param[in]    cy, cx    origin, relative to UL corner at 0,0
401
 * \param[in]    type      SEL_HIT, SEL_MISS, or SEL_DONT_CARE
402
 * \return  sel, or NULL on error
403
 *
404
 * <pre>
405
 * Notes:
406
 *      (1) This is a rectangular sel of all hits, misses or don't cares.
407
 * </pre>
408
 */
409
SEL *
410
selCreateBrick(l_int32  h,
411
               l_int32  w,
412
               l_int32  cy,
413
               l_int32  cx,
414
               l_int32  type)
415
12
{
416
12
l_int32  i, j;
417
12
SEL     *sel;
418
419
12
    if (h <= 0 || w <= 0)
420
0
        return (SEL *)ERROR_PTR("h and w must both be > 0", __func__, NULL);
421
12
    if (type != SEL_HIT && type != SEL_MISS && type != SEL_DONT_CARE)
422
0
        return (SEL *)ERROR_PTR("invalid sel element type", __func__, NULL);
423
424
12
    if ((sel = selCreate(h, w, NULL)) == NULL)
425
0
        return (SEL *)ERROR_PTR("sel not made", __func__, NULL);
426
12
    selSetOrigin(sel, cy, cx);
427
432
    for (i = 0; i < h; i++)
428
840
        for (j = 0; j < w; j++)
429
420
            sel->data[i][j] = type;
430
431
12
    return sel;
432
12
}
433
434
435
/*!
436
 * \brief   selCreateComb()
437
 *
438
 * \param[in]    factor1     contiguous space between comb tines
439
 * \param[in]    factor2     number of comb tines
440
 * \param[in]    direction   L_HORIZ, L_VERT
441
 * \return  sel, or NULL on error
442
 *
443
 * <pre>
444
 * Notes:
445
 *      (1) This generates a comb Sel of hits with the origin as
446
 *          near the center as possible.
447
 *      (2) In use, this is complemented by a brick sel of size %factor1,
448
 *          Both brick and comb sels are made by selectComposableSels().
449
 * </pre>
450
 */
451
SEL *
452
selCreateComb(l_int32  factor1,
453
              l_int32  factor2,
454
              l_int32  direction)
455
0
{
456
0
l_int32  i, size, z;
457
0
SEL     *sel;
458
459
0
    if (factor1 < 1 || factor2 < 1)
460
0
        return (SEL *)ERROR_PTR("factors must be >= 1", __func__, NULL);
461
0
    if (direction != L_HORIZ && direction != L_VERT)
462
0
        return (SEL *)ERROR_PTR("invalid direction", __func__, NULL);
463
464
0
    size = factor1 * factor2;
465
0
    if (direction == L_HORIZ) {
466
0
        if ((sel = selCreate(1, size, NULL)) == NULL)
467
0
            return (SEL *)ERROR_PTR("horiz sel not made", __func__, NULL);
468
0
        selSetOrigin(sel, 0, size / 2);
469
0
    } else {
470
0
        if ((sel = selCreate(size, 1, NULL)) == NULL)
471
0
            return (SEL *)ERROR_PTR("vert sel not made", __func__, NULL);
472
0
        selSetOrigin(sel, size / 2, 0);
473
0
    }
474
475
        /* Lay down the elements of the comb */
476
0
    for (i = 0; i < factor2; i++) {
477
0
        z = factor1 / 2 + i * factor1;
478
/*        lept_stderr("i = %d, factor1 = %d, factor2 = %d, z = %d\n",
479
                      i, factor1, factor2, z); */
480
0
        if (direction == L_HORIZ)
481
0
            selSetElement(sel, 0, z, SEL_HIT);
482
0
        else
483
0
            selSetElement(sel, z, 0, SEL_HIT);
484
0
    }
485
486
0
    return sel;
487
0
}
488
489
490
/*!
491
 * \brief   create2dIntArray()
492
 *
493
 * \param[in]    sy     rows == height
494
 * \param[in]    sx     columns == width
495
 * \return  doubly indexed array i.e., an array of sy row pointers,
496
 *              each of which points to an array of sx ints
497
 *
498
 * <pre>
499
 * Notes:
500
 *      (1) The array[sy][sx] is indexed in standard "matrix notation",
501
 *          with the row index first.
502
 * </pre>
503
 */
504
l_int32 **
505
create2dIntArray(l_int32  sy,
506
                 l_int32  sx)
507
12
{
508
12
l_int32    i;
509
12
l_int32  **array;
510
511
12
    if (sx <= 0 || sx > (l_int32)MaxKernelSize)
512
0
        return (l_int32 **)ERROR_PTR("sx out of bounds", __func__, NULL);
513
12
    if (sy <= 0 || sy > (l_int32)MaxKernelSize)
514
0
        return (l_int32 **)ERROR_PTR("sy out of bounds", __func__, NULL);
515
516
12
    array = (l_int32 **)LEPT_CALLOC(sy, sizeof(l_int32 *));
517
432
    for (i = 0; i < sy; i++)
518
420
        array[i] = (l_int32 *)LEPT_CALLOC(sx, sizeof(l_int32));
519
12
    return array;
520
12
}
521
522
523
/*------------------------------------------------------------------------*
524
 *                           Extension of sela                            *
525
 *------------------------------------------------------------------------*/
526
/*!
527
 * \brief   selaAddSel()
528
 *
529
 * \param[in]    sela
530
 * \param[in]    sel        to be added
531
 * \param[in]    selname    ignored if already defined in sel;
532
 *                          req'd in sel when added to a sela
533
 * \param[in]    copyflag   L_INSERT or L_COPY
534
 * \return  0 if OK; 1 on error
535
 *
536
 * <pre>
537
 * Notes:
538
 *      (1) This adds a sel, either inserting or making a copy.
539
 *      (2) Because every sel in a sela must have a name, it copies
540
 *          the input name if necessary.  You can input NULL for
541
 *          selname if the sel already has a name.
542
 * </pre>
543
 */
544
l_ok
545
selaAddSel(SELA        *sela,
546
           SEL         *sel,
547
           const char  *selname,
548
           l_int32      copyflag)
549
0
{
550
0
l_int32  n;
551
0
SEL     *csel;
552
553
0
    if (!sela)
554
0
        return ERROR_INT("sela not defined", __func__, 1);
555
0
    if (!sel)
556
0
        return ERROR_INT("sel not defined", __func__, 1);
557
0
    if (!sel->name && !selname)
558
0
        return ERROR_INT("added sel must have name", __func__, 1);
559
0
    if (copyflag != L_INSERT && copyflag != L_COPY)
560
0
        return ERROR_INT("invalid copyflag", __func__, 1);
561
562
0
    if (copyflag == L_COPY) {
563
0
        if ((csel = selCopy(sel)) == NULL)
564
0
            return ERROR_INT("csel not made", __func__, 1);
565
0
    } else {  /* copyflag == L_INSERT */
566
0
        csel = sel;
567
0
    }
568
0
    if (!csel->name)
569
0
        csel->name = stringNew(selname);
570
571
0
    n = selaGetCount(sela);
572
0
    if (n >= sela->nalloc) {
573
0
        if (selaExtendArray(sela)) {
574
0
            if (copyflag != L_INSERT)
575
0
                selDestroy(&csel);
576
0
            return ERROR_INT("extension failed", __func__, 1);
577
0
        }
578
0
    }
579
580
0
    sela->sel[n] = csel;
581
0
    sela->n++;
582
0
    return 0;
583
0
}
584
585
586
/*!
587
 * \brief   selaExtendArray()
588
 *
589
 * \param[in]    sela
590
 * \return  0 if OK; 1 on error
591
 */
592
static l_int32
593
selaExtendArray(SELA  *sela)
594
0
{
595
0
    if (!sela)
596
0
        return ERROR_INT("sela not defined", __func__, 1);
597
598
0
    if ((sela->sel = (SEL **)reallocNew((void **)&sela->sel,
599
0
                              sizeof(SEL *) * sela->nalloc,
600
0
                              2 * sizeof(SEL *) * sela->nalloc)) == NULL)
601
0
            return ERROR_INT("new ptr array not returned", __func__, 1);
602
603
0
    sela->nalloc = 2 * sela->nalloc;
604
0
    return 0;
605
0
}
606
607
608
609
/*----------------------------------------------------------------------*
610
 *                               Accessors                              *
611
 *----------------------------------------------------------------------*/
612
/*!
613
 * \brief   selaGetCount()
614
 *
615
 * \param[in]    sela
616
 * \return  count, or 0 on error
617
 */
618
l_int32
619
selaGetCount(SELA  *sela)
620
0
{
621
0
    if (!sela)
622
0
        return ERROR_INT("sela not defined", __func__, 0);
623
624
0
    return sela->n;
625
0
}
626
627
628
/*!
629
 * \brief   selaGetSel()
630
 *
631
 * \param[in]    sela
632
 * \param[in]    i        index of sel to be retrieved not copied
633
 * \return  sel, or NULL on error
634
 *
635
 * <pre>
636
 * Notes:
637
 *      (1) This returns a ptr to the sel, not a copy, so the caller
638
 *          must not destroy it!
639
 * </pre>
640
 */
641
SEL *
642
selaGetSel(SELA    *sela,
643
           l_int32  i)
644
0
{
645
0
    if (!sela)
646
0
        return (SEL *)ERROR_PTR("sela not defined", __func__, NULL);
647
648
0
    if (i < 0 || i >= sela->n)
649
0
        return (SEL *)ERROR_PTR("invalid index", __func__, NULL);
650
0
    return sela->sel[i];
651
0
}
652
653
654
/*!
655
 * \brief   selGetName()
656
 *
657
 * \param[in]    sel
658
 * \return  sel name not copied, or NULL if no name or on error
659
 */
660
char *
661
selGetName(SEL  *sel)
662
0
{
663
0
    if (!sel)
664
0
        return (char *)ERROR_PTR("sel not defined", __func__, NULL);
665
666
0
    return sel->name;
667
0
}
668
669
670
/*!
671
 * \brief   selSetName()
672
 *
673
 * \param[in]    sel
674
 * \param[in]    name    [optional]; can be null
675
 * \return  0 if OK, 1 on error
676
 *
677
 * <pre>
678
 * Notes:
679
 *      (1) Always frees the existing sel name, if defined.
680
 *      (2) If name is not defined, just clears any existing sel name.
681
 * </pre>
682
 */
683
l_ok
684
selSetName(SEL         *sel,
685
           const char  *name)
686
0
{
687
0
    if (!sel)
688
0
        return ERROR_INT("sel not defined", __func__, 1);
689
690
0
    return stringReplace(&sel->name, name);
691
0
}
692
693
694
/*!
695
 * \brief   selaFindSelByName()
696
 *
697
 * \param[in]    sela
698
 * \param[in]    name      sel name
699
 * \param[out]   pindex    [optional]
700
 * \param[in]    psel      [optional] sel (not a copy)
701
 * \return  0 if OK; 1 on error
702
 */
703
l_ok
704
selaFindSelByName(SELA        *sela,
705
                  const char  *name,
706
                  l_int32     *pindex,
707
                  SEL        **psel)
708
0
{
709
0
l_int32  i, n;
710
0
char    *sname;
711
0
SEL     *sel;
712
713
0
    if (pindex) *pindex = -1;
714
0
    if (psel) *psel = NULL;
715
716
0
    if (!sela)
717
0
        return ERROR_INT("sela not defined", __func__, 1);
718
719
0
    n = selaGetCount(sela);
720
0
    for (i = 0; i < n; i++)
721
0
    {
722
0
        if ((sel = selaGetSel(sela, i)) == NULL) {
723
0
            L_WARNING("missing sel\n", __func__);
724
0
            continue;
725
0
        }
726
727
0
        sname = selGetName(sel);
728
0
        if (sname && (!strcmp(name, sname))) {
729
0
            if (pindex)
730
0
                *pindex = i;
731
0
            if (psel)
732
0
                *psel = sel;
733
0
            return 0;
734
0
        }
735
0
    }
736
737
0
    return 1;
738
0
}
739
740
741
/*!
742
 * \brief   selGetElement()
743
 *
744
 * \param[in]    sel
745
 * \param[in]    row
746
 * \param[in]    col
747
 * \param[out]   ptype    SEL_HIT, SEL_MISS, SEL_DONT_CARE
748
 * \return  0 if OK; 1 on error
749
 */
750
l_ok
751
selGetElement(SEL      *sel,
752
              l_int32   row,
753
              l_int32   col,
754
              l_int32  *ptype)
755
0
{
756
0
    if (!ptype)
757
0
        return ERROR_INT("&type not defined", __func__, 1);
758
0
    *ptype = SEL_DONT_CARE;
759
0
    if (!sel)
760
0
        return ERROR_INT("sel not defined", __func__, 1);
761
0
    if (row < 0 || row >= sel->sy)
762
0
        return ERROR_INT("sel row out of bounds", __func__, 1);
763
0
    if (col < 0 || col >= sel->sx)
764
0
        return ERROR_INT("sel col out of bounds", __func__, 1);
765
766
0
    *ptype = sel->data[row][col];
767
0
    return 0;
768
0
}
769
770
771
/*!
772
 * \brief   selSetElement()
773
 *
774
 * \param[in]    sel
775
 * \param[in]    row
776
 * \param[in]    col
777
 * \param[in]    type    SEL_HIT, SEL_MISS, SEL_DONT_CARE
778
 * \return  0 if OK; 1 on error
779
 *
780
 * <pre>
781
 * Notes:
782
 *      (1) Because we use row and column to index into an array,
783
 *          they are always non-negative.  The location of the origin
784
 *          (and the type of operation) determine the actual
785
 *          direction of the rasterop.
786
 * </pre>
787
 */
788
l_ok
789
selSetElement(SEL     *sel,
790
              l_int32  row,
791
              l_int32  col,
792
              l_int32  type)
793
0
{
794
0
    if (!sel)
795
0
        return ERROR_INT("sel not defined", __func__, 1);
796
0
    if (type != SEL_HIT && type != SEL_MISS && type != SEL_DONT_CARE)
797
0
        return ERROR_INT("invalid sel element type", __func__, 1);
798
0
    if (row < 0 || row >= sel->sy)
799
0
        return ERROR_INT("sel row out of bounds", __func__, 1);
800
0
    if (col < 0 || col >= sel->sx)
801
0
        return ERROR_INT("sel col out of bounds", __func__, 1);
802
803
0
    sel->data[row][col] = type;
804
0
    return 0;
805
0
}
806
807
808
/*!
809
 * \brief   selGetParameters()
810
 *
811
 * \param[in]    sel
812
 * \param[out]   psy, psx, pcy, pcx    [optional] each can be null
813
 * \return  0 if OK, 1 on error
814
 */
815
l_ok
816
selGetParameters(SEL      *sel,
817
                 l_int32  *psy,
818
                 l_int32  *psx,
819
                 l_int32  *pcy,
820
                 l_int32  *pcx)
821
72
{
822
72
    if (psy) *psy = 0;
823
72
    if (psx) *psx = 0;
824
72
    if (pcy) *pcy = 0;
825
72
    if (pcx) *pcx = 0;
826
72
    if (!sel)
827
0
        return ERROR_INT("sel not defined", __func__, 1);
828
72
    if (psy) *psy = sel->sy;
829
72
    if (psx) *psx = sel->sx;
830
72
    if (pcy) *pcy = sel->cy;
831
72
    if (pcx) *pcx = sel->cx;
832
72
    return 0;
833
72
}
834
835
836
/*!
837
 * \brief   selSetOrigin()
838
 *
839
 * \param[in]    sel
840
 * \param[in]    cy, cx
841
 * \return  0 if OK; 1 on error
842
 */
843
l_ok
844
selSetOrigin(SEL     *sel,
845
             l_int32  cy,
846
             l_int32  cx)
847
12
{
848
12
    if (!sel)
849
0
        return ERROR_INT("sel not defined", __func__, 1);
850
12
    sel->cy = cy;
851
12
    sel->cx = cx;
852
12
    return 0;
853
12
}
854
855
856
/*!
857
 * \brief   selGetTypeAtOrigin()
858
 *
859
 * \param[in]    sel
860
 * \param[out]   ptype    SEL_HIT, SEL_MISS, SEL_DONT_CARE
861
 * \return  0 if OK; 1 on error or if origin is not found
862
 */
863
l_ok
864
selGetTypeAtOrigin(SEL      *sel,
865
                   l_int32  *ptype)
866
0
{
867
0
l_int32  sx, sy, cx, cy, i, j;
868
869
0
    if (!ptype)
870
0
        return ERROR_INT("&type not defined", __func__, 1);
871
0
    *ptype = SEL_DONT_CARE;  /* init */
872
0
    if (!sel)
873
0
        return ERROR_INT("sel not defined", __func__, 1);
874
875
0
    selGetParameters(sel, &sy, &sx, &cy, &cx);
876
0
    for (i = 0; i < sy; i++) {
877
0
        for (j = 0; j < sx; j++) {
878
0
            if (i == cy && j == cx) {
879
0
                selGetElement(sel, i, j, ptype);
880
0
                return 0;
881
0
            }
882
0
        }
883
0
    }
884
885
0
    return ERROR_INT("sel origin not found", __func__, 1);
886
0
}
887
888
889
/*!
890
 * \brief   selaGetBrickName()
891
 *
892
 * \param[in]    sela
893
 * \param[in]    hsize, vsize    of brick sel
894
 * \return  sel name new string, or NULL if no name or on error
895
 */
896
char *
897
selaGetBrickName(SELA    *sela,
898
                 l_int32  hsize,
899
                 l_int32  vsize)
900
0
{
901
0
l_int32  i, nsels, sx, sy;
902
0
SEL     *sel;
903
904
0
    if (!sela)
905
0
        return (char *)ERROR_PTR("sela not defined", __func__, NULL);
906
907
0
    nsels = selaGetCount(sela);
908
0
    for (i = 0; i < nsels; i++) {
909
0
        sel = selaGetSel(sela, i);
910
0
        selGetParameters(sel, &sy, &sx, NULL, NULL);
911
0
        if (hsize == sx && vsize == sy)
912
0
            return stringNew(selGetName(sel));
913
0
    }
914
915
0
    return (char *)ERROR_PTR("sel not found", __func__, NULL);
916
0
}
917
918
919
/*!
920
 * \brief   selaGetCombName()
921
 *
922
 * \param[in]    sela
923
 * \param[in]    size        the product of sizes of the brick and comb parts
924
 * \param[in]    direction   L_HORIZ, L_VERT
925
 * \return  sel name new string, or NULL if name not found or on error
926
 *
927
 * <pre>
928
 * Notes:
929
 *      (1) Combs are by definition 1-dimensional, either horiz or vert.
930
 *      (2) Use this with comb Sels; e.g., from selaAddDwaCombs().
931
 * </pre>
932
 */
933
char *
934
selaGetCombName(SELA    *sela,
935
                l_int32  size,
936
                l_int32  direction)
937
0
{
938
0
char    *selname = NULL;
939
0
char     combname[256];
940
0
l_int32  i, nsels, sx, sy, found;
941
0
SEL     *sel;
942
943
0
    if (!sela)
944
0
        return (char *)ERROR_PTR("sela not defined", __func__, NULL);
945
0
    if (direction != L_HORIZ && direction != L_VERT)
946
0
        return (char *)ERROR_PTR("invalid direction", __func__, NULL);
947
948
        /* Derive the comb name we're looking for */
949
0
    if (direction == L_HORIZ)
950
0
        snprintf(combname, sizeof(combname), "sel_comb_%dh", size);
951
0
    else  /* direction == L_VERT */
952
0
        snprintf(combname, sizeof(combname), "sel_comb_%dv", size);
953
954
0
    found = FALSE;
955
0
    nsels = selaGetCount(sela);
956
0
    for (i = 0; i < nsels; i++) {
957
0
        sel = selaGetSel(sela, i);
958
0
        selGetParameters(sel, &sy, &sx, NULL, NULL);
959
0
        if (sy != 1 && sx != 1)  /* 2-D; not a comb */
960
0
            continue;
961
0
        selname = selGetName(sel);
962
0
        if (!strcmp(selname, combname)) {
963
0
            found = TRUE;
964
0
            break;
965
0
        }
966
0
    }
967
968
0
    if (found)
969
0
        return stringNew(selname);
970
0
    else
971
0
        return (char *)ERROR_PTR("sel not found", __func__, NULL);
972
0
}
973
974
975
/* --------- Function used to generate code in this file  ---------- */
976
#if 0
977
static void selaComputeCompositeParameters(const char *fileout);
978
979
/*!
980
 * \brief   selaComputeCompParameters()
981
 *
982
 * \param[in]    fileout
983
 * \return  void
984
 *
985
 * <pre>
986
 * Notes:
987
 *      (1) This static function was used to construct the comp_parameter_map[]
988
 *          array at the top of this file.  It is static because it does
989
 *          not need to be called again.  It remains here to show how
990
 *          the composite parameter map was computed.
991
 *      (2) The output file was pasted directly into comp_parameter_map[].
992
 *          The composite parameter map is used to quickly determine
993
 *          the linear decomposition parameters and sel names.
994
 * </pre>
995
 */
996
static void
997
selaComputeCompositeParameters(const char  *fileout)
998
{
999
char    *str, *nameh1, *nameh2, *namev1, *namev2;
1000
char     buf[256];
1001
l_int32  size, size1, size2, len;
1002
SARRAY  *sa;
1003
SELA    *selabasic, *selacomb;
1004
1005
    selabasic = selaAddBasic(NULL);
1006
    selacomb = selaAddDwaCombs(NULL);
1007
    sa = sarrayCreate(64);
1008
    for (size = 2; size < 64; size++) {
1009
        selectComposableSizes(size, &size1, &size2);
1010
        nameh1 = selaGetBrickName(selabasic, size1, 1);
1011
        namev1 = selaGetBrickName(selabasic, 1, size1);
1012
        if (size2 > 1) {
1013
            nameh2 = selaGetCombName(selacomb, size1 * size2, L_HORIZ);
1014
            namev2 = selaGetCombName(selacomb, size1 * size2, L_VERT);
1015
        } else {
1016
            nameh2 = stringNew("");
1017
            namev2 = stringNew("");
1018
        }
1019
        snprintf(buf, sizeof(buf),
1020
                 "      { %d, %d, %d, \"%s\", \"%s\", \"%s\", \"%s\" },",
1021
                 size, size1, size2, nameh1, nameh2, namev1, namev2);
1022
        sarrayAddString(sa, buf, L_COPY);
1023
        LEPT_FREE(nameh1);
1024
        LEPT_FREE(nameh2);
1025
        LEPT_FREE(namev1);
1026
        LEPT_FREE(namev2);
1027
    }
1028
    str = sarrayToString(sa, 1);
1029
    len = strlen(str);
1030
    l_binaryWrite(fileout, "w", str, len + 1);
1031
    LEPT_FREE(str);
1032
    sarrayDestroy(&sa);
1033
    selaDestroy(&selabasic);
1034
    selaDestroy(&selacomb);
1035
}
1036
#endif
1037
/* -------------------------------------------------------------------- */
1038
1039
1040
/*!
1041
 * \brief   getCompositeParameters()
1042
 *
1043
 * \param[in]    size
1044
 * \param[out]   psize1    [optional] brick factor size
1045
 * \param[out]   psize2    [optional] comb factor size
1046
 * \param[out]   pnameh1   [optional] name of horiz brick
1047
 * \param[out]   pnameh2   [optional] name of horiz comb
1048
 * \param[out]   pnamev1   [optional] name of vert brick
1049
 * \param[out]   pnamev2   [optional] name of vert comb
1050
 * \return  0 if OK, 1 on error
1051
 *
1052
 * <pre>
1053
 * Notes:
1054
 *      (1) This uses the big lookup table at the top of this file.
1055
 *      (2) All returned strings are copies that must be freed.
1056
 * </pre>
1057
 */
1058
l_ok
1059
getCompositeParameters(l_int32   size,
1060
                       l_int32  *psize1,
1061
                       l_int32  *psize2,
1062
                       char    **pnameh1,
1063
                       char    **pnameh2,
1064
                       char    **pnamev1,
1065
                       char    **pnamev2)
1066
0
{
1067
0
l_int32  index;
1068
1069
0
    if (psize1) *psize1 = 0;
1070
0
    if (psize2) *psize2 = 0;
1071
0
    if (pnameh1) *pnameh1 = NULL;
1072
0
    if (pnameh2) *pnameh2 = NULL;
1073
0
    if (pnamev1) *pnamev1 = NULL;
1074
0
    if (pnamev2) *pnamev2 = NULL;
1075
0
    if (size < 2 || size > 63)
1076
0
        return ERROR_INT("valid size range is {2 ... 63}", __func__, 1);
1077
0
    index = size - 2;
1078
0
    if (psize1)
1079
0
        *psize1 = comp_parameter_map[index].size1;
1080
0
    if (psize2)
1081
0
        *psize2 = comp_parameter_map[index].size2;
1082
0
    if (pnameh1)
1083
0
        *pnameh1 = stringNew(comp_parameter_map[index].selnameh1);
1084
0
    if (pnameh2)
1085
0
        *pnameh2 = stringNew(comp_parameter_map[index].selnameh2);
1086
0
    if (pnamev1)
1087
0
        *pnamev1 = stringNew(comp_parameter_map[index].selnamev1);
1088
0
    if (pnamev2)
1089
0
        *pnamev2 = stringNew(comp_parameter_map[index].selnamev2);
1090
0
    return 0;
1091
0
}
1092
1093
1094
/*!
1095
 * \brief   selaGetSelnames()
1096
 *
1097
 * \param[in]    sela
1098
 * \return  sa of all sel names, or NULL on error
1099
 */
1100
SARRAY *
1101
selaGetSelnames(SELA  *sela)
1102
0
{
1103
0
char    *selname;
1104
0
l_int32  i, n;
1105
0
SEL     *sel;
1106
0
SARRAY  *sa;
1107
1108
0
    if (!sela)
1109
0
        return (SARRAY *)ERROR_PTR("sela not defined", __func__, NULL);
1110
0
    if ((n = selaGetCount(sela)) == 0)
1111
0
        return (SARRAY *)ERROR_PTR("no sels in sela", __func__, NULL);
1112
1113
0
    if ((sa = sarrayCreate(n)) == NULL)
1114
0
        return (SARRAY *)ERROR_PTR("sa not made", __func__, NULL);
1115
0
    for (i = 0; i < n; i++) {
1116
0
        sel = selaGetSel(sela, i);
1117
0
        selname = selGetName(sel);
1118
0
        sarrayAddString(sa, selname, L_COPY);
1119
0
    }
1120
1121
0
    return sa;
1122
0
}
1123
1124
1125
1126
/*----------------------------------------------------------------------*
1127
 *                Max translations for erosion and hmt                  *
1128
 *----------------------------------------------------------------------*/
1129
/*!
1130
 * \brief   selFindMaxTranslations()
1131
 *
1132
 * \param[in]    sel
1133
 * \param[out]   pxp, pyp, pxn, pyn     max shifts
1134
 * \return  0 if OK; 1 on error
1135
 *
1136
 * <pre>
1137
 * Notes:
1138
          These are the maximum shifts for the erosion operation.
1139
 *        For example, when j < cx, the shift of the image
1140
 *        is +x to the cx.  This is a positive xp shift.
1141
 * </pre>
1142
 */
1143
l_ok
1144
selFindMaxTranslations(SEL      *sel,
1145
                       l_int32  *pxp,
1146
                       l_int32  *pyp,
1147
                       l_int32  *pxn,
1148
                       l_int32  *pyn)
1149
12
{
1150
12
l_int32  sx, sy, cx, cy, i, j;
1151
12
l_int32  maxxp, maxyp, maxxn, maxyn;
1152
1153
12
    if (!pxp || !pyp || !pxn || !pyn)
1154
0
        return ERROR_INT("&xp (etc) defined", __func__, 1);
1155
12
    *pxp = *pyp = *pxn = *pyn = 0;
1156
12
    if (!sel)
1157
0
        return ERROR_INT("sel not defined", __func__, 1);
1158
12
    selGetParameters(sel, &sy, &sx, &cy, &cx);
1159
1160
12
    maxxp = maxyp = maxxn = maxyn = 0;
1161
432
    for (i = 0; i < sy; i++) {
1162
840
        for (j = 0; j < sx; j++) {
1163
420
            if (sel->data[i][j] == 1) {
1164
420
                maxxp = L_MAX(maxxp, cx - j);
1165
420
                maxyp = L_MAX(maxyp, cy - i);
1166
420
                maxxn = L_MAX(maxxn, j - cx);
1167
420
                maxyn = L_MAX(maxyn, i - cy);
1168
420
            }
1169
420
        }
1170
420
    }
1171
1172
12
    *pxp = maxxp;
1173
12
    *pyp = maxyp;
1174
12
    *pxn = maxxn;
1175
12
    *pyn = maxyn;
1176
1177
12
    return 0;
1178
12
}
1179
1180
1181
/*----------------------------------------------------------------------*
1182
 *                   Rotation by multiples of 90 degrees                *
1183
 *----------------------------------------------------------------------*/
1184
/*!
1185
 * \brief   selRotateOrth()
1186
 *
1187
 * \param[in]    sel
1188
 * \param[in]    quads    0 - 4; number of 90 degree cw rotations
1189
 * \return  seld, or NULL on error
1190
 */
1191
SEL  *
1192
selRotateOrth(SEL     *sel,
1193
              l_int32  quads)
1194
0
{
1195
0
l_int32  i, j, ni, nj, sx, sy, cx, cy, nsx, nsy, ncx, ncy, type;
1196
0
SEL     *seld;
1197
1198
0
    if (!sel)
1199
0
        return (SEL *)ERROR_PTR("sel not defined", __func__, NULL);
1200
0
    if (quads < 0 || quads > 4)
1201
0
        return (SEL *)ERROR_PTR("quads not in {0,1,2,3,4}", __func__, NULL);
1202
0
    if (quads == 0 || quads == 4)
1203
0
        return selCopy(sel);
1204
1205
0
    selGetParameters(sel, &sy, &sx, &cy, &cx);
1206
0
    if (quads == 1) {  /* 90 degrees cw */
1207
0
        nsx = sy;
1208
0
        nsy = sx;
1209
0
        ncx = sy - cy - 1;
1210
0
        ncy = cx;
1211
0
    } else if (quads == 2) {  /* 180 degrees cw */
1212
0
        nsx = sx;
1213
0
        nsy = sy;
1214
0
        ncx = sx - cx - 1;
1215
0
        ncy = sy - cy - 1;
1216
0
    } else {  /* 270 degrees cw */
1217
0
        nsx = sy;
1218
0
        nsy = sx;
1219
0
        ncx = cy;
1220
0
        ncy = sx - cx - 1;
1221
0
    }
1222
0
    seld = selCreateBrick(nsy, nsx, ncy, ncx, SEL_DONT_CARE);
1223
0
    if (sel->name)
1224
0
        seld->name = stringNew(sel->name);
1225
1226
0
    for (i = 0; i < sy; i++) {
1227
0
        for (j = 0; j < sx; j++) {
1228
0
            selGetElement(sel, i, j, &type);
1229
0
            if (quads == 1) {
1230
0
               ni = j;
1231
0
               nj = sy - i - 1;
1232
0
            } else if (quads == 2) {
1233
0
               ni = sy - i - 1;
1234
0
               nj = sx - j - 1;
1235
0
            } else {  /* quads == 3 */
1236
0
               ni = sx - j - 1;
1237
0
               nj = i;
1238
0
            }
1239
0
            selSetElement(seld, ni, nj, type);
1240
0
        }
1241
0
    }
1242
1243
0
    return seld;
1244
0
}
1245
1246
1247
/*----------------------------------------------------------------------*
1248
 *                       Sela and Sel serialized I/O                    *
1249
 *----------------------------------------------------------------------*/
1250
/*!
1251
 * \brief   selaRead()
1252
 *
1253
 * \param[in]    fname    filename
1254
 * \return  sela, or NULL on error
1255
 */
1256
SELA *
1257
selaRead(const char  *fname)
1258
0
{
1259
0
FILE  *fp;
1260
0
SELA  *sela;
1261
1262
0
    if (!fname)
1263
0
        return (SELA *)ERROR_PTR("fname not defined", __func__, NULL);
1264
1265
0
    if ((fp = fopenReadStream(fname)) == NULL)
1266
0
        return (SELA *)ERROR_PTR_1("stream not opened", fname, __func__, NULL);
1267
0
    if ((sela = selaReadStream(fp)) == NULL) {
1268
0
        fclose(fp);
1269
0
        return (SELA *)ERROR_PTR_1("sela not returned", fname, __func__, NULL);
1270
0
    }
1271
0
    fclose(fp);
1272
1273
0
    return sela;
1274
0
}
1275
1276
1277
/*!
1278
 * \brief   selaReadStream()
1279
 *
1280
 * \param[in]    fp    file stream
1281
 * \return  sela, or NULL on error
1282
 */
1283
SELA  *
1284
selaReadStream(FILE  *fp)
1285
0
{
1286
0
l_int32  i, n, version;
1287
0
SEL     *sel;
1288
0
SELA    *sela;
1289
1290
0
    if (!fp)
1291
0
        return (SELA *)ERROR_PTR("stream not defined", __func__, NULL);
1292
1293
0
    if (fscanf(fp, "\nSela Version %d\n", &version) != 1)
1294
0
        return (SELA *)ERROR_PTR("not a sela file", __func__, NULL);
1295
0
    if (version != SEL_VERSION_NUMBER)
1296
0
        return (SELA *)ERROR_PTR("invalid sel version", __func__, NULL);
1297
0
    if (fscanf(fp, "Number of Sels = %d\n\n", &n) != 1)
1298
0
        return (SELA *)ERROR_PTR("not a sela file", __func__, NULL);
1299
1300
0
    if ((sela = selaCreate(n)) == NULL)
1301
0
        return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
1302
0
    sela->nalloc = n;
1303
1304
0
    for (i = 0; i < n; i++) {
1305
0
        if ((sel = selReadStream(fp)) == NULL) {
1306
0
            selaDestroy(&sela);
1307
0
            return (SELA *)ERROR_PTR("sel not read", __func__, NULL);
1308
0
        }
1309
0
        selaAddSel(sela, sel, NULL, 0);
1310
0
    }
1311
1312
0
    return sela;
1313
0
}
1314
1315
1316
/*!
1317
 * \brief   selRead()
1318
 *
1319
 * \param[in]    fname    filename
1320
 * \return  sel, or NULL on error
1321
 */
1322
SEL  *
1323
selRead(const char  *fname)
1324
0
{
1325
0
FILE  *fp;
1326
0
SEL   *sel;
1327
1328
0
    if (!fname)
1329
0
        return (SEL *)ERROR_PTR("fname not defined", __func__, NULL);
1330
1331
0
    if ((fp = fopenReadStream(fname)) == NULL)
1332
0
        return (SEL *)ERROR_PTR_1("stream not opened", fname, __func__, NULL);
1333
0
    if ((sel = selReadStream(fp)) == NULL) {
1334
0
        fclose(fp);
1335
0
        return (SEL *)ERROR_PTR_1("sela not returned", fname, __func__, NULL);
1336
0
    }
1337
0
    fclose(fp);
1338
1339
0
    return sel;
1340
0
}
1341
1342
1343
/*!
1344
 * \brief   selReadStream()
1345
 *
1346
 * \param[in]    fp    file stream
1347
 * \return  sel, or NULL on error
1348
 */
1349
SEL  *
1350
selReadStream(FILE  *fp)
1351
0
{
1352
0
char     selname[256];
1353
0
char     linebuf[256];
1354
0
l_int32  sy, sx, cy, cx, i, j, version, ignore;
1355
0
SEL     *sel;
1356
1357
0
    if (!fp)
1358
0
        return (SEL *)ERROR_PTR("stream not defined", __func__, NULL);
1359
1360
0
    if (fscanf(fp, "  Sel Version %d\n", &version) != 1)
1361
0
        return (SEL *)ERROR_PTR("not a sel file", __func__, NULL);
1362
0
    if (version != SEL_VERSION_NUMBER)
1363
0
        return (SEL *)ERROR_PTR("invalid sel version", __func__, NULL);
1364
1365
0
    if (fgets(linebuf, sizeof(linebuf), fp) == NULL)
1366
0
        return (SEL *)ERROR_PTR("error reading into linebuf", __func__, NULL);
1367
0
    sscanf(linebuf, "  ------  %200s  ------", selname);
1368
1369
0
    if (fscanf(fp, "  sy = %d, sx = %d, cy = %d, cx = %d\n",
1370
0
               &sy, &sx, &cy, &cx) != 4)
1371
0
        return (SEL *)ERROR_PTR("dimensions not read", __func__, NULL);
1372
1373
0
    if ((sel = selCreate(sy, sx, selname)) == NULL)
1374
0
        return (SEL *)ERROR_PTR("sel not made", __func__, NULL);
1375
0
    selSetOrigin(sel, cy, cx);
1376
1377
0
    for (i = 0; i < sy; i++) {
1378
0
        ignore = fscanf(fp, "    ");
1379
0
        for (j = 0; j < sx; j++)
1380
0
            ignore = fscanf(fp, "%1d", &sel->data[i][j]);
1381
0
        ignore = fscanf(fp, "\n");
1382
0
    }
1383
0
    ignore = fscanf(fp, "\n");
1384
1385
0
    return sel;
1386
0
}
1387
1388
1389
/*!
1390
 * \brief   selaWrite()
1391
 *
1392
 * \param[in]    fname    filename
1393
 * \param[in]    sela
1394
 * \return  0 if OK, 1 on error
1395
 */
1396
l_ok
1397
selaWrite(const char  *fname,
1398
          SELA        *sela)
1399
0
{
1400
0
FILE  *fp;
1401
1402
0
    if (!fname)
1403
0
        return ERROR_INT("fname not defined", __func__, 1);
1404
0
    if (!sela)
1405
0
        return ERROR_INT("sela not defined", __func__, 1);
1406
1407
0
    if ((fp = fopenWriteStream(fname, "wb")) == NULL)
1408
0
        return ERROR_INT_1("stream not opened", fname, __func__, 1);
1409
0
    selaWriteStream(fp, sela);
1410
0
    fclose(fp);
1411
1412
0
    return 0;
1413
0
}
1414
1415
1416
/*!
1417
 * \brief   selaWriteStream()
1418
 *
1419
 * \param[in]    fp    file stream
1420
 * \param[in]    sela
1421
 * \return  0 if OK, 1 on error
1422
 */
1423
l_ok
1424
selaWriteStream(FILE  *fp,
1425
                SELA  *sela)
1426
0
{
1427
0
l_int32  i, n;
1428
0
SEL     *sel;
1429
1430
0
    if (!fp)
1431
0
        return ERROR_INT("stream not defined", __func__, 1);
1432
0
    if (!sela)
1433
0
        return ERROR_INT("sela not defined", __func__, 1);
1434
1435
0
    n = selaGetCount(sela);
1436
0
    fprintf(fp, "\nSela Version %d\n", SEL_VERSION_NUMBER);
1437
0
    fprintf(fp, "Number of Sels = %d\n\n", n);
1438
0
    for (i = 0; i < n; i++) {
1439
0
        if ((sel = selaGetSel(sela, i)) == NULL)
1440
0
            continue;
1441
0
        selWriteStream(fp, sel);
1442
0
    }
1443
0
    return 0;
1444
0
}
1445
1446
1447
/*!
1448
 * \brief   selWrite()
1449
 *
1450
 * \param[in]    fname    filename
1451
 * \param[in]    sel
1452
 * \return  0 if OK, 1 on error
1453
 */
1454
l_ok
1455
selWrite(const char  *fname,
1456
         SEL         *sel)
1457
0
{
1458
0
FILE  *fp;
1459
1460
0
    if (!fname)
1461
0
        return ERROR_INT("fname not defined", __func__, 1);
1462
0
    if (!sel)
1463
0
        return ERROR_INT("sel not defined", __func__, 1);
1464
1465
0
    if ((fp = fopenWriteStream(fname, "wb")) == NULL)
1466
0
        return ERROR_INT_1("stream not opened", fname, __func__, 1);
1467
0
    selWriteStream(fp, sel);
1468
0
    fclose(fp);
1469
1470
0
    return 0;
1471
0
}
1472
1473
1474
/*!
1475
 * \brief   selWriteStream()
1476
 *
1477
 * \param[in]    fp    file stream
1478
 * \param[in]    sel
1479
 * \return  0 if OK, 1 on error
1480
 */
1481
l_ok
1482
selWriteStream(FILE  *fp,
1483
               SEL   *sel)
1484
0
{
1485
0
l_int32  sx, sy, cx, cy, i, j;
1486
1487
0
    if (!fp)
1488
0
        return ERROR_INT("stream not defined", __func__, 1);
1489
0
    if (!sel)
1490
0
        return ERROR_INT("sel not defined", __func__, 1);
1491
0
    selGetParameters(sel, &sy, &sx, &cy, &cx);
1492
1493
0
    fprintf(fp, "  Sel Version %d\n", SEL_VERSION_NUMBER);
1494
0
    fprintf(fp, "  ------  %s  ------\n", selGetName(sel));
1495
0
    fprintf(fp, "  sy = %d, sx = %d, cy = %d, cx = %d\n", sy, sx, cy, cx);
1496
0
    for (i = 0; i < sy; i++) {
1497
0
        fprintf(fp, "    ");
1498
0
        for (j = 0; j < sx; j++)
1499
0
            fprintf(fp, "%d", sel->data[i][j]);
1500
0
        fprintf(fp, "\n");
1501
0
    }
1502
0
    fprintf(fp, "\n");
1503
1504
0
    return 0;
1505
0
}
1506
1507
1508
/*----------------------------------------------------------------------*
1509
 *           Building custom hit-miss sels from compiled strings        *
1510
 *----------------------------------------------------------------------*/
1511
/*!
1512
 * \brief   selCreateFromString()
1513
 *
1514
 * \param[in]    text
1515
 * \param[in]    h, w    height, width
1516
 * \param[in]    name    [optional] sel name; can be null
1517
 * \return  sel of the given size, or NULL on error
1518
 *
1519
 * <pre>
1520
 * Notes:
1521
 *      (1) The text is an array of chars (in row-major order) where
1522
 *          each char can be one of the following:
1523
 *             'x': hit
1524
 *             'o': miss
1525
 *             ' ': don't-care
1526
 *      (2) When the origin falls on a hit or miss, use an upper case
1527
 *          char (e.g., 'X' or 'O') to indicate it.  When the origin
1528
 *          falls on a don't-care, indicate this with a 'C'.
1529
 *          The string must have exactly one origin specified.
1530
 *      (3) The advantage of this method is that the text can be input
1531
 *          in a format that shows the 2D layout of the Sel; e.g.,
1532
 * \code
1533
 *              static const char *seltext = "x    "
1534
 *                                           "x Oo "
1535
 *                                           "x    "
1536
 *                                           "xxxxx";
1537
 * \endcode
1538
 * </pre>
1539
 */
1540
SEL *
1541
selCreateFromString(const char  *text,
1542
                    l_int32      h,
1543
                    l_int32      w,
1544
                    const char  *name)
1545
0
{
1546
0
SEL     *sel;
1547
0
l_int32  y, x, norig;
1548
0
char     ch;
1549
1550
0
    if (!text || text[0] == '\0')
1551
0
        return (SEL *)ERROR_PTR("text undefined or empty", __func__, NULL);
1552
0
    if (h < 1)
1553
0
        return (SEL *)ERROR_PTR("height must be > 0", __func__, NULL);
1554
0
    if (w < 1)
1555
0
        return (SEL *)ERROR_PTR("width must be > 0", __func__, NULL);
1556
0
    if (strlen(text) != (size_t)w * h)
1557
0
        return (SEL *)ERROR_PTR("text size != w * h", __func__, NULL);
1558
1559
0
    sel = selCreate(h, w, name);
1560
0
    norig = 0;
1561
0
    for (y = 0; y < h; ++y) {
1562
0
        for (x = 0; x < w; ++x) {
1563
0
            ch = *(text++);
1564
0
            switch (ch)
1565
0
            {
1566
0
                case 'X':
1567
0
                    norig++;
1568
0
                    selSetOrigin(sel, y, x);
1569
                    /* fall through */
1570
0
                case 'x':
1571
0
                    selSetElement(sel, y, x, SEL_HIT);
1572
0
                    break;
1573
1574
0
                case 'O':
1575
0
                    norig++;
1576
0
                    selSetOrigin(sel, y, x);
1577
                    /* fall through */
1578
0
                case 'o':
1579
0
                    selSetElement(sel, y, x, SEL_MISS);
1580
0
                    break;
1581
1582
0
                case 'C':
1583
0
                    norig++;
1584
0
                    selSetOrigin(sel, y, x);
1585
                    /* fall through */
1586
0
                case ' ':
1587
0
                    selSetElement(sel, y, x, SEL_DONT_CARE);
1588
0
                    break;
1589
1590
0
                case '\n':
1591
                    /* ignored */
1592
0
                    continue;
1593
1594
0
                default:
1595
0
                    selDestroy(&sel);
1596
0
                    return (SEL *)ERROR_PTR("unknown char", __func__, NULL);
1597
0
            }
1598
0
        }
1599
0
    }
1600
0
    if (norig != 1) {
1601
0
        L_ERROR("Exactly one origin must be specified; this string has %d\n",
1602
0
                __func__, norig);
1603
0
        selDestroy(&sel);
1604
0
    }
1605
1606
0
    return sel;
1607
0
}
1608
1609
1610
/*!
1611
 * \brief   selPrintToString()
1612
 *
1613
 * \param[in]    sel
1614
 * \return  str string; caller must free
1615
 *
1616
 * <pre>
1617
 * Notes:
1618
 *      (1) This is an inverse function of selCreateFromString.
1619
 *          It prints a textual representation of the SEL to a malloc'd
1620
 *          string.  The format is the same as selCreateFromString
1621
 *          except that newlines are inserted into the output
1622
 *          between rows.
1623
 *      (2) This is useful for debugging.  However, if you want to
1624
 *          save some Sels in a file, put them in a Sela and write
1625
 *          them out with selaWrite().  They can then be read in
1626
 *          with selaRead().
1627
 * </pre>
1628
 */
1629
char *
1630
selPrintToString(SEL  *sel)
1631
0
{
1632
0
char     is_center;
1633
0
char    *str, *strptr;
1634
0
l_int32  type;
1635
0
l_int32  sx, sy, cx, cy, x, y;
1636
1637
0
    if (!sel)
1638
0
        return (char *)ERROR_PTR("sel not defined", __func__, NULL);
1639
1640
0
    selGetParameters(sel, &sy, &sx, &cy, &cx);
1641
0
    if ((str = (char *)LEPT_CALLOC(1, sy * (sx + 1) + 1)) == NULL)
1642
0
        return (char *)ERROR_PTR("calloc fail for str", __func__, NULL);
1643
0
    strptr = str;
1644
1645
0
    for (y = 0; y < sy; ++y) {
1646
0
        for (x = 0; x < sx; ++x) {
1647
0
            selGetElement(sel, y, x, &type);
1648
0
            is_center = (x == cx && y == cy);
1649
0
            switch (type) {
1650
0
                case SEL_HIT:
1651
0
                    *(strptr++) = is_center ? 'X' : 'x';
1652
0
                    break;
1653
0
                case SEL_MISS:
1654
0
                    *(strptr++) = is_center ? 'O' : 'o';
1655
0
                    break;
1656
0
                case SEL_DONT_CARE:
1657
0
                    *(strptr++) = is_center ? 'C' : ' ';
1658
0
                    break;
1659
0
            }
1660
0
        }
1661
0
        *(strptr++) = '\n';
1662
0
    }
1663
1664
0
    return str;
1665
0
}
1666
1667
1668
/*----------------------------------------------------------------------*
1669
 *         Building custom hit-miss sels from a simple file format      *
1670
 *----------------------------------------------------------------------*/
1671
/*!
1672
 * \brief   selaCreateFromFile()
1673
 *
1674
 * \param[in]    filename
1675
 * \return  sela, or NULL on error
1676
 *
1677
 * <pre>
1678
 * Notes:
1679
 *      (1) The file contains a sequence of Sel descriptions.
1680
 *      (2) Each Sel is formatted as follows:
1681
 *           ~ Any number of comment lines starting with '#' are ignored
1682
 *           ~ The next line contains the selname
1683
 *           ~ The next lines contain the Sel data.  They must be
1684
 *             formatted similarly to the string format in
1685
 *             selCreateFromString(), with each line beginning and
1686
 *             ending with a double-quote, and showing the 2D layout.
1687
 *           ~ Each Sel ends when a blank line, a comment line, or
1688
 *             the end of file is reached.
1689
 *      (3) See selCreateFromString() for a description of the string
1690
 *          format for the Sel data.  As an example, here are the lines
1691
 *          of is a valid file for a single Sel.  In the file, all lines
1692
 *          are left-justified:
1693
 *                    # diagonal sel
1694
 *                    sel_5diag
1695
 *                    "x    "
1696
 *                    " x   "
1697
 *                    "  X  "
1698
 *                    "   x "
1699
 *                    "    x"
1700
 * </pre>
1701
 */
1702
SELA *
1703
selaCreateFromFile(const char  *filename)
1704
0
{
1705
0
char    *filestr, *line;
1706
0
l_int32  i, n, first, last, nsel, insel;
1707
0
size_t   nbytes;
1708
0
NUMA    *nafirst, *nalast;
1709
0
SARRAY  *sa;
1710
0
SEL     *sel;
1711
0
SELA    *sela;
1712
1713
0
    if (!filename)
1714
0
        return (SELA *)ERROR_PTR("filename not defined", __func__, NULL);
1715
1716
0
    filestr = (char *)l_binaryRead(filename, &nbytes);
1717
0
    sa = sarrayCreateLinesFromString(filestr, 1);
1718
0
    LEPT_FREE(filestr);
1719
0
    n = sarrayGetCount(sa);
1720
0
    sela = selaCreate(0);
1721
1722
        /* Find the start and end lines for each Sel.
1723
         * We allow the "blank" lines to be null strings or
1724
         * to have standard whitespace (' ','\t',\'n') or be '#'. */
1725
0
    nafirst = numaCreate(0);
1726
0
    nalast = numaCreate(0);
1727
0
    insel = FALSE;
1728
0
    for (i = 0; i < n; i++) {
1729
0
        line = sarrayGetString(sa, i, L_NOCOPY);
1730
0
        if (!insel &&
1731
0
            (line[0] != '\0' && line[0] != ' ' &&
1732
0
             line[0] != '\t' && line[0] != '\n' && line[0] != '#')) {
1733
0
            numaAddNumber(nafirst, i);
1734
0
            insel = TRUE;
1735
0
            continue;
1736
0
        }
1737
0
        if (insel &&
1738
0
            (line[0] == '\0' || line[0] == ' ' ||
1739
0
             line[0] == '\t' || line[0] == '\n' || line[0] == '#')) {
1740
0
            numaAddNumber(nalast, i - 1);
1741
0
            insel = FALSE;
1742
0
            continue;
1743
0
        }
1744
0
    }
1745
0
    if (insel)  /* fell off the end of the file */
1746
0
        numaAddNumber(nalast, n - 1);
1747
1748
        /* Extract sels */
1749
0
    nsel = numaGetCount(nafirst);
1750
0
    for (i = 0; i < nsel; i++) {
1751
0
        numaGetIValue(nafirst, i, &first);
1752
0
        numaGetIValue(nalast, i, &last);
1753
0
        if ((sel = selCreateFromSArray(sa, first, last)) == NULL) {
1754
0
            lept_stderr("Error reading sel from %d to %d\n", first, last);
1755
0
            selaDestroy(&sela);
1756
0
            sarrayDestroy(&sa);
1757
0
            numaDestroy(&nafirst);
1758
0
            numaDestroy(&nalast);
1759
0
            return (SELA *)ERROR_PTR("bad sela file", __func__, NULL);
1760
0
        }
1761
0
        selaAddSel(sela, sel, NULL, 0);
1762
0
    }
1763
1764
0
    numaDestroy(&nafirst);
1765
0
    numaDestroy(&nalast);
1766
0
    sarrayDestroy(&sa);
1767
0
    return sela;
1768
0
}
1769
1770
1771
/*!
1772
 * \brief   selCreateFromSArray()
1773
 *
1774
 * \param[in]    sa
1775
 * \param[in]    first    line of sarray where Sel begins
1776
 * \param[in]    last     line of sarray where Sel ends
1777
 * \return  sela, or NULL on error
1778
 *
1779
 * <pre>
1780
 * Notes:
1781
 *      (1) The Sel contains the following lines:
1782
 *          ~ The first line is the selname
1783
 *          ~ The remaining lines contain the Sel data.  They must
1784
 *            be formatted similarly to the string format in
1785
 *            selCreateFromString(), with each line beginning and
1786
 *            ending with a double-quote, and showing the 2D layout.
1787
 *          ~ 'last' gives the last line in the Sel data.
1788
 *      (2) See selCreateFromString() for a description of the string
1789
 *          format for the Sel data.  As an example, here are the lines
1790
 *          of is a valid file for a single Sel.  In the file, all lines
1791
 *          are left-justified:
1792
 *                    # diagonal sel
1793
 *                    sel_5diag
1794
 *                    "x    "
1795
 *                    " x   "
1796
 *                    "  X  "
1797
 *                    "   x "
1798
 *                    "    x"
1799
 * </pre>
1800
 */
1801
static SEL *
1802
selCreateFromSArray(SARRAY  *sa,
1803
                    l_int32  first,
1804
                    l_int32  last)
1805
0
{
1806
0
char     ch;
1807
0
char    *name, *line;
1808
0
l_int32  n, len, i, w, h, y, x;
1809
0
SEL     *sel;
1810
1811
0
    if (!sa)
1812
0
        return (SEL *)ERROR_PTR("sa not defined", __func__, NULL);
1813
0
    n = sarrayGetCount(sa);
1814
0
    if (first < 0 || first >= n || last <= first || last >= n)
1815
0
        return (SEL *)ERROR_PTR("invalid range", __func__, NULL);
1816
1817
0
    name = sarrayGetString(sa, first, L_NOCOPY);
1818
0
    h = last - first;
1819
0
    line = sarrayGetString(sa, first + 1, L_NOCOPY);
1820
0
    len = strlen(line);
1821
0
    if (line[0] != '"' || line[len - 1] != '"')
1822
0
        return (SEL *)ERROR_PTR("invalid format", __func__, NULL);
1823
0
    w = len - 2;
1824
0
    if ((sel = selCreate(h, w, name)) == NULL)
1825
0
        return (SEL *)ERROR_PTR("sel not made", __func__, NULL);
1826
0
    for (i = first + 1; i <= last; i++) {
1827
0
        line = sarrayGetString(sa, i, L_NOCOPY);
1828
0
        y = i - first - 1;
1829
0
        for (x = 0; x < w; ++x) {
1830
0
            ch = line[x + 1];  /* skip the leading double-quote */
1831
0
            switch (ch)
1832
0
            {
1833
0
                case 'X':
1834
0
                    selSetOrigin(sel, y, x);  /* set origin and hit */
1835
                    /* fall through */
1836
0
                case 'x':
1837
0
                    selSetElement(sel, y, x, SEL_HIT);
1838
0
                    break;
1839
1840
0
                case 'O':
1841
0
                    selSetOrigin(sel, y, x);  /* set origin and miss */
1842
                    /* fall through */
1843
0
                case 'o':
1844
0
                    selSetElement(sel, y, x, SEL_MISS);
1845
0
                    break;
1846
1847
0
                case 'C':
1848
0
                    selSetOrigin(sel, y, x);  /* set origin and don't-care */
1849
                    /* fall through */
1850
0
                case ' ':
1851
0
                    selSetElement(sel, y, x, SEL_DONT_CARE);
1852
0
                    break;
1853
1854
0
                default:
1855
0
                    selDestroy(&sel);
1856
0
                    return (SEL *)ERROR_PTR("unknown char", __func__, NULL);
1857
0
            }
1858
0
        }
1859
0
    }
1860
1861
0
    return sel;
1862
0
}
1863
1864
1865
/*----------------------------------------------------------------------*
1866
 *               Making hit-only SELs from Pta and Pix                  *
1867
 *----------------------------------------------------------------------*/
1868
/*!
1869
 * \brief   selCreateFromPta()
1870
 *
1871
 * \param[in]    pta
1872
 * \param[in]    cy, cx    origin of sel
1873
 * \param[in]    name      [optional] sel name; can be null
1874
 * \return  sel of minimum required size, or NULL on error
1875
 *
1876
 * <pre>
1877
 * Notes:
1878
 *      (1) The origin and all points in the pta must be positive.
1879
 * </pre>
1880
 */
1881
SEL *
1882
selCreateFromPta(PTA         *pta,
1883
                 l_int32      cy,
1884
                 l_int32      cx,
1885
                 const char  *name)
1886
0
{
1887
0
l_int32  i, n, x, y, w, h;
1888
0
BOX     *box;
1889
0
SEL     *sel;
1890
1891
0
    if (!pta)
1892
0
        return (SEL *)ERROR_PTR("pta not defined", __func__, NULL);
1893
0
    if (cy < 0 || cx < 0)
1894
0
        return (SEL *)ERROR_PTR("(cy, cx) not both >= 0", __func__, NULL);
1895
0
    n = ptaGetCount(pta);
1896
0
    if (n == 0)
1897
0
        return (SEL *)ERROR_PTR("no pts in pta", __func__, NULL);
1898
1899
0
    box = ptaGetBoundingRegion(pta);
1900
0
    boxGetGeometry(box, &x, &y, &w, &h);
1901
0
    boxDestroy(&box);
1902
0
    if (x < 0 || y < 0)
1903
0
        return (SEL *)ERROR_PTR("not all x and y >= 0", __func__, NULL);
1904
1905
0
    sel = selCreate(y + h, x + w, name);
1906
0
    selSetOrigin(sel, cy, cx);
1907
0
    for (i = 0; i < n; i++) {
1908
0
        ptaGetIPt(pta, i, &x, &y);
1909
0
        selSetElement(sel, y, x, SEL_HIT);
1910
0
    }
1911
1912
0
    return sel;
1913
0
}
1914
1915
1916
/*!
1917
 * \brief   selCreateFromPix()
1918
 *
1919
 * \param[in]    pix
1920
 * \param[in]    cy, cx    origin of sel
1921
 * \param[in]    name      [optional] sel name; can be null
1922
 * \return  sel, or NULL on error
1923
 *
1924
 * <pre>
1925
 * Notes:
1926
 *      (1) The origin must be positive.
1927
 *      (2) The pix must not exceed MaxPixTemplateSize in either dimension.
1928
 *          and the total number of hits must not exceed MaxPixTemplateHits.
1929
 * </pre>
1930
 */
1931
SEL *
1932
selCreateFromPix(PIX         *pix,
1933
                 l_int32      cy,
1934
                 l_int32      cx,
1935
                 const char  *name)
1936
0
{
1937
0
SEL      *sel;
1938
0
l_int32   i, j, w, h, d, nhits;
1939
0
l_uint32  val;
1940
1941
0
    if (!pix)
1942
0
        return (SEL *)ERROR_PTR("pix not defined", __func__, NULL);
1943
0
    if (cy < 0 || cx < 0)
1944
0
        return (SEL *)ERROR_PTR("(cy, cx) not both >= 0", __func__, NULL);
1945
0
    pixGetDimensions(pix, &w, &h, &d);
1946
0
    if (d != 1)
1947
0
        return (SEL *)ERROR_PTR("pix not 1 bpp", __func__, NULL);
1948
0
    if (w > MaxPixTemplateSize || h > MaxPixTemplateSize) {
1949
0
        L_ERROR("pix template too large (w = %d, h = %d)\n", __func__, w, h);
1950
0
        return NULL;
1951
0
    }
1952
0
    pixCountPixels(pix, &nhits, NULL);
1953
0
    if (nhits > MaxPixTemplateHits) {
1954
0
        L_ERROR("too many hits (%d) in pix template\n", __func__, nhits);
1955
0
        return NULL;
1956
0
    }
1957
1958
0
    sel = selCreate(h, w, name);
1959
0
    selSetOrigin(sel, cy, cx);
1960
0
    for (i = 0; i < h; i++) {
1961
0
        for (j = 0; j < w; j++) {
1962
0
            pixGetPixel(pix, j, i, &val);
1963
0
            if (val)
1964
0
                selSetElement(sel, i, j, SEL_HIT);
1965
0
        }
1966
0
    }
1967
1968
0
    return sel;
1969
0
}
1970
1971
1972
/*----------------------------------------------------------------------*
1973
 *            Making hit-miss sels from color Pix and image files             *
1974
 *----------------------------------------------------------------------*/
1975
/*!
1976
 *
1977
 *  selReadFromColorImage()
1978
 *
1979
 * \param[in]    pathname
1980
 * \return  sel if OK; NULL on error
1981
 *
1982
 * <pre>
1983
 * Notes:
1984
 *      (1) Loads an image from a file and creates a (hit-miss) sel.
1985
 *      (2) The sel name is taken from the pathname without the directory
1986
 *          and extension.
1987
 * </pre>
1988
 */
1989
SEL *
1990
selReadFromColorImage(const char  *pathname)
1991
0
{
1992
0
PIX   *pix;
1993
0
SEL   *sel;
1994
0
char  *basename, *selname;
1995
1996
0
    splitPathAtExtension (pathname, &basename, NULL);
1997
0
    splitPathAtDirectory (basename, NULL, &selname);
1998
0
    LEPT_FREE(basename);
1999
2000
0
    if ((pix = pixRead(pathname)) == NULL) {
2001
0
        LEPT_FREE(selname);
2002
0
        return (SEL *)ERROR_PTR("pix not returned", __func__, NULL);
2003
0
    }
2004
0
    if ((sel = selCreateFromColorPix(pix, selname)) == NULL)
2005
0
        L_ERROR("sel not made\n", __func__);
2006
2007
0
    LEPT_FREE(selname);
2008
0
    pixDestroy(&pix);
2009
0
    return sel;
2010
0
}
2011
2012
2013
/*!
2014
 *
2015
 *  selCreateFromColorPix()
2016
 *
2017
 * \param[in]    pixs      cmapped or rgb
2018
 * \param[in]    selname   [optional] sel name; can be null
2019
 * \return  sel if OK, NULL on error
2020
 *
2021
 * <pre>
2022
 * Notes:
2023
 *      (1) The sel size is given by the size of pixs.
2024
 *      (2) In pixs, hits are represented by green pixels, misses by red
2025
 *          pixels, and don't-cares by white pixels.
2026
 *      (3) In pixs, there may be no misses, but there must be at least 1 hit.
2027
 *      (4) At most there can be only one origin pixel, which is optionally
2028
 *          specified by using a lower-intensity pixel:
2029
 *            if a hit:  dark green
2030
 *            if a miss: dark red
2031
 *            if a don't care: gray
2032
 *          If there is no such pixel, the origin defaults to the approximate
2033
 *          center of the sel.
2034
 * </pre>
2035
 */
2036
SEL *
2037
selCreateFromColorPix(PIX         *pixs,
2038
                      const char  *selname)
2039
0
{
2040
0
PIXCMAP  *cmap;
2041
0
SEL      *sel;
2042
0
l_int32   hascolor, num_origins, nohits;
2043
0
l_int32   w, h, d, i, j, red, green, blue;
2044
0
l_uint32  pixval;
2045
2046
0
    if (!pixs)
2047
0
        return (SEL *)ERROR_PTR("pixs not defined", __func__, NULL);
2048
2049
0
    hascolor = FALSE;
2050
0
    cmap = pixGetColormap(pixs);
2051
0
    if (cmap)
2052
0
        pixcmapHasColor(cmap, &hascolor);
2053
0
    pixGetDimensions(pixs, &w, &h, &d);
2054
0
    if (hascolor == FALSE && d != 32)
2055
0
        return (SEL *)ERROR_PTR("pixs has no color", __func__, NULL);
2056
2057
0
    if ((sel = selCreate (h, w, NULL)) == NULL)
2058
0
        return (SEL *)ERROR_PTR ("sel not made", __func__, NULL);
2059
0
    selSetOrigin (sel, h / 2, w / 2);  /* default */
2060
0
    selSetName(sel, selname);
2061
2062
0
    num_origins = 0;
2063
0
    nohits = TRUE;
2064
0
    for (i = 0; i < h; i++) {
2065
0
        for (j = 0; j < w; j++) {
2066
0
            pixGetPixel (pixs, j, i, &pixval);
2067
2068
0
            if (cmap) {
2069
0
                pixcmapGetColor (cmap, pixval, &red, &green, &blue);
2070
0
            } else {
2071
0
                red = GET_DATA_BYTE (&pixval, COLOR_RED);
2072
0
                green = GET_DATA_BYTE (&pixval, COLOR_GREEN);
2073
0
                blue = GET_DATA_BYTE (&pixval, COLOR_BLUE);
2074
0
            }
2075
2076
0
            if (red < 255 && green < 255 && blue < 255) {
2077
0
                num_origins++;
2078
0
                if (num_origins == 1)  /* first one found */
2079
0
                    selSetOrigin (sel, i, j);
2080
0
                if (num_origins == 2)
2081
0
                    L_WARNING("multiple origins in sel image\n", __func__);
2082
0
            }
2083
0
            if (!red && green && !blue) {
2084
0
                nohits = FALSE;
2085
0
                selSetElement (sel, i, j, SEL_HIT);
2086
0
            } else if (red && !green && !blue) {
2087
0
                selSetElement (sel, i, j, SEL_MISS);
2088
0
            } else if (red && green && blue) {
2089
0
                selSetElement (sel, i, j, SEL_DONT_CARE);
2090
0
            } else {
2091
0
                selDestroy(&sel);
2092
0
                return (SEL *)ERROR_PTR("invalid color", __func__, NULL);
2093
0
            }
2094
0
        }
2095
0
    }
2096
2097
0
    if (nohits) {
2098
0
        selDestroy(&sel);
2099
0
        return (SEL *)ERROR_PTR("no hits in sel", __func__, NULL);
2100
0
    }
2101
0
    return sel;
2102
0
}
2103
2104
2105
/*!
2106
 *
2107
 *  selaCreateFromColorPixa()
2108
 *
2109
 * \param[in]    pixa      color pixa representing the sels
2110
 * \param[in]    sa        sarray of sel names
2111
 * \return  sel if OK, NULL on error
2112
 *
2113
 * <pre>
2114
 * Notes:
2115
 *      (1) See notes in selCreateFromColorPix()
2116
 *      (2) sa is required because all sels that are put in a sela
2117
 *          must have a name.
2118
 * </pre>
2119
 */
2120
SELA *
2121
selaCreateFromColorPixa(PIXA    *pixa,
2122
                        SARRAY  *sa)
2123
0
{
2124
0
char    *str;
2125
0
l_int32  i, n;
2126
0
PIX     *pix;
2127
0
SEL     *sel;
2128
0
SELA    *sela;
2129
2130
0
    if (!pixa)
2131
0
        return (SELA *)ERROR_PTR("pixa not defined", __func__, NULL);
2132
0
    if (!sa)
2133
0
        return (SELA *)ERROR_PTR("sa of sel names not defined", __func__, NULL);
2134
2135
0
    n = pixaGetCount(pixa);
2136
0
    if ((sela = selaCreate(n)) == NULL)
2137
0
        return (SELA *)ERROR_PTR("sela not allocated", __func__, NULL);
2138
0
    for (i = 0; i < n; i++) {
2139
0
        pix = pixaGetPix(pixa, i, L_CLONE);
2140
0
        str = sarrayGetString(sa, i, L_NOCOPY);
2141
0
        sel = selCreateFromColorPix(pix, str);
2142
0
        selaAddSel(sela, sel, NULL, L_INSERT);
2143
0
        pixDestroy(&pix);
2144
0
    }
2145
0
    return sela;
2146
0
}
2147
2148
2149
/*----------------------------------------------------------------------*
2150
 *                     Printable display of sel                         *
2151
 *----------------------------------------------------------------------*/
2152
/*!
2153
 * \brief   selDisplayInPix()
2154
 *
2155
 * \param[in]    sel
2156
 * \param[in]    size     of grid interiors; odd; minimum size of 13 is enforced
2157
 * \param[in]    gthick   grid thickness; minimum size of 2 is enforced
2158
 * \return  pix display of sel, or NULL on error
2159
 *
2160
 * <pre>
2161
 * Notes:
2162
 *      (1) This gives a visual representation of a general (hit-miss) sel.
2163
 *      (2) The empty sel is represented by a grid of intersecting lines.
2164
 *      (3) Three different patterns are generated for the sel elements:
2165
 *          ~ hit (solid black circle)
2166
 *          ~ miss (black ring; inner radius is radius2)
2167
 *          ~ origin (cross, XORed with whatever is there)
2168
 * </pre>
2169
 */
2170
PIX *
2171
selDisplayInPix(SEL     *sel,
2172
                l_int32  size,
2173
                l_int32  gthick)
2174
0
{
2175
0
l_int32  i, j, w, h, sx, sy, cx, cy, type, width;
2176
0
l_int32  radius1, radius2, shift1, shift2, x0, y0;
2177
0
PIX     *pixd, *pix2, *pixh, *pixm, *pixorig;
2178
0
PTA     *pta1, *pta2, *pta1t, *pta2t;
2179
2180
0
    if (!sel)
2181
0
        return (PIX *)ERROR_PTR("sel not defined", __func__, NULL);
2182
0
    if (size < 13) {
2183
0
        L_WARNING("size < 13; setting to 13\n", __func__);
2184
0
        size = 13;
2185
0
    }
2186
0
    if (size % 2 == 0)
2187
0
        size++;
2188
0
    if (gthick < 2) {
2189
0
        L_WARNING("grid thickness < 2; setting to 2\n", __func__);
2190
0
        gthick = 2;
2191
0
    }
2192
0
    selGetParameters(sel, &sy, &sx, &cy, &cx);
2193
0
    w = size * sx + gthick * (sx + 1);
2194
0
    h = size * sy + gthick * (sy + 1);
2195
0
    pixd = pixCreate(w, h, 1);
2196
2197
        /* Generate grid lines */
2198
0
    for (i = 0; i <= sy; i++)
2199
0
        pixRenderLine(pixd, 0, gthick / 2 + i * (size + gthick),
2200
0
                      w - 1, gthick / 2 + i * (size + gthick),
2201
0
                      gthick, L_SET_PIXELS);
2202
0
    for (j = 0; j <= sx; j++)
2203
0
        pixRenderLine(pixd, gthick / 2 + j * (size + gthick), 0,
2204
0
                      gthick / 2 + j * (size + gthick), h - 1,
2205
0
                      gthick, L_SET_PIXELS);
2206
2207
        /* Generate hit and miss patterns */
2208
0
    radius1 = (l_int32)(0.85 * ((size - 1) / 2.0) + 0.5);  /* of hit */
2209
0
    radius2 = (l_int32)(0.65 * ((size - 1) / 2.0) + 0.5);  /* of inner miss */
2210
0
    pta1 = generatePtaFilledCircle(radius1);
2211
0
    pta2 = generatePtaFilledCircle(radius2);
2212
0
    shift1 = (size - 1) / 2 - radius1;  /* center circle in square */
2213
0
    shift2 = (size - 1) / 2 - radius2;
2214
0
    pta1t = ptaTransform(pta1, shift1, shift1, 1.0, 1.0);
2215
0
    pta2t = ptaTransform(pta2, shift2, shift2, 1.0, 1.0);
2216
0
    pixh = pixGenerateFromPta(pta1t, size, size);  /* hits */
2217
0
    pix2 = pixGenerateFromPta(pta2t, size, size);
2218
0
    pixm = pixSubtract(NULL, pixh, pix2);
2219
2220
        /* Generate crossed lines for origin pattern */
2221
0
    pixorig = pixCreate(size, size, 1);
2222
0
    width = size / 8;
2223
0
    pixRenderLine(pixorig, size / 2, (l_int32)(0.12 * size),
2224
0
                           size / 2, (l_int32)(0.88 * size),
2225
0
                           width, L_SET_PIXELS);
2226
0
    pixRenderLine(pixorig, (l_int32)(0.15 * size), size / 2,
2227
0
                           (l_int32)(0.85 * size), size / 2,
2228
0
                           width, L_FLIP_PIXELS);
2229
0
    pixRasterop(pixorig, size / 2 - width, size / 2 - width,
2230
0
                2 * width, 2 * width, PIX_NOT(PIX_DST), NULL, 0, 0);
2231
2232
        /* Specialize origin pattern for this sel */
2233
0
    selGetTypeAtOrigin(sel, &type);
2234
0
    if (type == SEL_HIT)
2235
0
        pixXor(pixorig, pixorig, pixh);
2236
0
    else if (type == SEL_MISS)
2237
0
        pixXor(pixorig, pixorig, pixm);
2238
2239
        /* Paste the patterns in */
2240
0
    y0 = gthick;
2241
0
    for (i = 0; i < sy; i++) {
2242
0
        x0 = gthick;
2243
0
        for (j = 0; j < sx; j++) {
2244
0
            selGetElement(sel, i, j, &type);
2245
0
            if (i == cy && j == cx)  /* origin */
2246
0
                pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixorig, 0, 0);
2247
0
            else if (type == SEL_HIT)
2248
0
                pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixh, 0, 0);
2249
0
            else if (type == SEL_MISS)
2250
0
                pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixm, 0, 0);
2251
0
            x0 += size + gthick;
2252
0
        }
2253
0
        y0 += size + gthick;
2254
0
    }
2255
2256
0
    pixDestroy(&pix2);
2257
0
    pixDestroy(&pixh);
2258
0
    pixDestroy(&pixm);
2259
0
    pixDestroy(&pixorig);
2260
0
    ptaDestroy(&pta1);
2261
0
    ptaDestroy(&pta1t);
2262
0
    ptaDestroy(&pta2);
2263
0
    ptaDestroy(&pta2t);
2264
0
    return pixd;
2265
0
}
2266
2267
2268
/*!
2269
 * \brief   selaDisplayInPix()
2270
 *
2271
 * \param[in]    sela
2272
 * \param[in]    size     of grid interiors; odd; minimum size of 13 is enforced
2273
 * \param[in]    gthick   grid thickness; minimum size of 2 is enforced
2274
 * \param[in]    spacing  between sels, both horizontally and vertically
2275
 * \param[in]    ncols    number of sels per "line"
2276
 * \return  pix display of all sels in sela, or NULL on error
2277
 *
2278
 * <pre>
2279
 * Notes:
2280
 *      (1) This gives a visual representation of all the sels in a sela.
2281
 *      (2) See notes in selDisplayInPix() for display params of each sel.
2282
 *      (3) This gives the nicest results when all sels in the sela
2283
 *          are the same size.
2284
 * </pre>
2285
 */
2286
PIX *
2287
selaDisplayInPix(SELA    *sela,
2288
                 l_int32  size,
2289
                 l_int32  gthick,
2290
                 l_int32  spacing,
2291
                 l_int32  ncols)
2292
0
{
2293
0
l_int32  nsels, i, w, width;
2294
0
PIX     *pixt, *pixd;
2295
0
PIXA    *pixa;
2296
0
SEL     *sel;
2297
2298
0
    if (!sela)
2299
0
        return (PIX *)ERROR_PTR("sela not defined", __func__, NULL);
2300
0
    if (size < 13) {
2301
0
        L_WARNING("size < 13; setting to 13\n", __func__);
2302
0
        size = 13;
2303
0
    }
2304
0
    if (size % 2 == 0)
2305
0
        size++;
2306
0
    if (gthick < 2) {
2307
0
        L_WARNING("grid thickness < 2; setting to 2\n", __func__);
2308
0
        gthick = 2;
2309
0
    }
2310
0
    if (spacing < 5) {
2311
0
        L_WARNING("spacing < 5; setting to 5\n", __func__);
2312
0
        spacing = 5;
2313
0
    }
2314
2315
        /* Accumulate the pix of each sel */
2316
0
    nsels = selaGetCount(sela);
2317
0
    pixa = pixaCreate(nsels);
2318
0
    for (i = 0; i < nsels; i++) {
2319
0
        sel = selaGetSel(sela, i);
2320
0
        pixt = selDisplayInPix(sel, size, gthick);
2321
0
        pixaAddPix(pixa, pixt, L_INSERT);
2322
0
    }
2323
2324
        /* Find the tiled output width, using just the first
2325
         * ncols pix in the pixa.   If all pix have the same width,
2326
         * they will align properly in columns. */
2327
0
    width = 0;
2328
0
    ncols = L_MIN(nsels, ncols);
2329
0
    for (i = 0; i < ncols; i++) {
2330
0
        pixt = pixaGetPix(pixa, i, L_CLONE);
2331
0
        pixGetDimensions(pixt, &w, NULL, NULL);
2332
0
        width += w;
2333
0
        pixDestroy(&pixt);
2334
0
    }
2335
0
    width += (ncols + 1) * spacing;  /* add spacing all around as well */
2336
2337
0
    pixd = pixaDisplayTiledInRows(pixa, 1, width, 1.0, 0, spacing, 0);
2338
0
    pixaDestroy(&pixa);
2339
0
    return pixd;
2340
0
}