Coverage Report

Created: 2026-01-13 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/leptonica/src/morphdwa.c
Line
Count
Source
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 morphdwa.c
29
 * <pre>
30
 *
31
 *    Binary morphological (dwa) ops with brick Sels
32
 *         PIX     *pixDilateBrickDwa()
33
 *         PIX     *pixErodeBrickDwa()
34
 *         PIX     *pixOpenBrickDwa()
35
 *         PIX     *pixCloseBrickDwa()
36
 *
37
 *    Binary composite morphological (dwa) ops with brick Sels
38
 *         PIX     *pixDilateCompBrickDwa()
39
 *         PIX     *pixErodeCompBrickDwa()
40
 *         PIX     *pixOpenCompBrickDwa()
41
 *         PIX     *pixCloseCompBrickDwa()
42
 *
43
 *    Binary extended composite morphological (dwa) ops with brick Sels
44
 *         PIX     *pixDilateCompBrickExtendDwa()
45
 *         PIX     *pixErodeCompBrickExtendDwa()
46
 *         PIX     *pixOpenCompBrickExtendDwa()
47
 *         PIX     *pixCloseCompBrickExtendDwa()
48
 *         l_int32  getExtendedCompositeParameters()
49
 *
50
 *    These are higher-level interfaces for dwa morphology with brick Sels.
51
 *    Because many morphological operations are performed using
52
 *    separable brick Sels, it is useful to have a simple interface
53
 *    for this.
54
 *
55
 *    We have included all 58 of the brick Sels that are generated
56
 *    by selaAddBasic().  These are sufficient for all the decomposable
57
 *    bricks up to size 63, which is the limit for dwa Sels with
58
 *    origins at the center of the Sel.
59
 *
60
 *    All three sets can be used as the basic interface for general
61
 *    brick operations.  Here are the internal calling sequences:
62
 *
63
 *      (1) If you try to apply a non-decomposable operation, such as
64
 *          pixErodeBrickDwa(), with a Sel size that doesn't exist,
65
 *          this calls a decomposable operation, pixErodeCompBrickDwa(),
66
 *          instead.  This can differ in linear Sel size by up to
67
 *          2 pixels from the request.
68
 *
69
 *      (2) If either Sel brick dimension is greater than 63, the extended
70
 *          composite function is called.
71
 *
72
 *      (3) The extended composite function calls the composite function
73
 *          a number of times with size 63, and once with size < 63.
74
 *          Because each operation with a size of 63 is done compositely
75
 *          with 7 x 9 (exactly 63), the net result is correct in
76
 *          length to within 2 pixels.
77
 *
78
 *    For composite operations, both using a comb and extended (beyond 63),
79
 *    horizontal and vertical operations are composed separately
80
 *    and sequentially.
81
 *
82
 *    We have also included use of all the 76 comb Sels that are generated
83
 *    by selaAddDwaCombs().  The generated code is in dwacomb.2.c
84
 *    and dwacomblow.2.c.  These are used for the composite dwa
85
 *    brick operations.
86
 *
87
 *    The non-composite brick operations, such as pixDilateBrickDwa(),
88
 *    will call the associated composite operation in situations where
89
 *    the requisite brick Sel has not been compiled into fmorphgen*.1.c.
90
 *
91
 *    If you want to use brick Sels that are not represented in the
92
 *    basic set of 58, you must generate the dwa code to implement them.
93
 *    You have three choices for how to use these:
94
 *
95
 *    (1) Add both the new Sels and the dwa code to the library:
96
 *        ~ For simplicity, add your new brick Sels to those defined
97
 *          in selaAddBasic().
98
 *        ~ Recompile the library.
99
 *        ~ Make prog/fmorphautogen.
100
 *        ~ Run prog/fmorphautogen, to generate new versions of the
101
 *          dwa code in fmorphgen.1.c and fmorphgenlow.1.c.
102
 *        ~ Copy these two files to src.
103
 *        ~ Recompile the library again.
104
 *        ~ Use the new brick Sels in your program and compile it.
105
 *
106
 *    (2) Make both the new Sels and dwa code outside the library,
107
 *        and link it directly to an executable:
108
 *        ~ Write a function to generate the new Sels in a Sela, and call
109
 *          fmorphautogen(sela, <N>, filename) to generate the code.
110
 *        ~ Compile your program that uses the newly generated function
111
 *          pixMorphDwa_<N>(), and link to the two new C files.
112
 *
113
 *    (3) Make the new Sels in the library and use the dwa code outside it:
114
 *        ~ Add code in the library to generate your new brick Sels.
115
 *          (It is suggested that you NOT add these Sels to the
116
 *          selaAddBasic() function; write a new function that generates
117
 *          a new Sela.)
118
 *        ~ Recompile the library.
119
 *        ~ Write a small program that generates the Sela and calls
120
 *          fmorphautogen(sela, <N>, filename) to generate the code.
121
 *        ~ Compile your program that uses the newly generated function
122
 *          pixMorphDwa_<N>(), and link to the two new C files.
123
 *       As an example of this approach, see prog/dwamorph*_reg.c:
124
 *        ~ added selaAddDwaLinear() to sel2.c
125
 *        ~ wrote dwamorph1_reg.c, to generate the dwa code.
126
 *        ~ compiled and linked the generated code with the application,
127
 *          dwamorph2_reg.c.  (Note: because this was a regression test,
128
 *          dwamorph1_reg also builds and runs the application program.)
129
 * </pre>
130
 */
131
132
#ifdef HAVE_CONFIG_H
133
#include <config_auto.h>
134
#endif  /* HAVE_CONFIG_H */
135
136
#include "allheaders.h"
137
138
#ifndef  NO_CONSOLE_IO
139
#define  DEBUG_SEL_LOOKUP   0
140
#endif  /* ~NO_CONSOLE_IO */
141
142
/*-----------------------------------------------------------------*
143
 *           Binary morphological (dwa) ops with brick Sels        *
144
 *-----------------------------------------------------------------*/
145
/*!
146
 * \brief   pixDilateBrickDwa()
147
 *
148
 * \param[in]    pixd    [optional]; this can be null, equal to pixs,
149
 *                       or different from pixs
150
 * \param[in]    pixs    1 bpp
151
 * \param[in]    hsize   width of brick Sel
152
 * \param[in]    vsize   height of brick Sel
153
 * \return  pixd
154
 *
155
 * <pre>
156
 * Notes:
157
 *      (1) These implement 2D brick Sels, using linear Sels generated
158
 *          with selaAddBasic().
159
 *      (2) A brick Sel has hits for all elements.
160
 *      (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
161
 *      (4) Do separably if both hsize and vsize are > 1.
162
 *      (5) It is necessary that both horizontal and vertical Sels
163
 *          of the input size are defined in the basic sela.
164
 *      (6) There are three cases:
165
 *          (a) pixd == null   (result into new pixd)
166
 *          (b) pixd == pixs   (in-place; writes result back to pixs)
167
 *          (c) pixd != pixs   (puts result into existing pixd)
168
 *      (7) For clarity, if the case is known, use these patterns:
169
 *          (a) pixd = pixDilateBrickDwa(NULL, pixs, ...);
170
 *          (b) pixDilateBrickDwa(pixs, pixs, ...);
171
 *          (c) pixDilateBrickDwa(pixd, pixs, ...);
172
 *      (8) The size of pixd is determined by pixs.
173
 *      (9) If either linear Sel is not found, this calls
174
 *          the appropriate decomposible function.
175
 * </pre>
176
 */
177
PIX *
178
pixDilateBrickDwa(PIX     *pixd,
179
                  PIX     *pixs,
180
                  l_int32  hsize,
181
                  l_int32  vsize)
182
0
{
183
0
l_int32  found;
184
0
char    *selnameh, *selnamev;
185
0
SELA    *sela;
186
0
PIX     *pixt1, *pixt2, *pixt3;
187
188
0
    if (!pixs)
189
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
190
0
    if (pixGetDepth(pixs) != 1)
191
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
192
0
    if (hsize < 1 || vsize < 1)
193
0
        return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
194
195
0
    if (hsize == 1 && vsize == 1)
196
0
        return pixCopy(pixd, pixs);
197
198
0
    sela = selaAddBasic(NULL);
199
0
    found = TRUE;
200
0
    selnameh = selnamev = NULL;
201
0
    if (hsize > 1) {
202
0
        selnameh = selaGetBrickName(sela, hsize, 1);
203
0
        if (!selnameh) found = FALSE;
204
0
    }
205
0
    if (vsize > 1) {
206
0
        selnamev = selaGetBrickName(sela, 1, vsize);
207
0
        if (!selnamev) found = FALSE;
208
0
    }
209
0
    selaDestroy(&sela);
210
0
    if (!found) {
211
0
        L_INFO("Calling the decomposable dwa function\n", __func__);
212
0
        if (selnameh) LEPT_FREE(selnameh);
213
0
        if (selnamev) LEPT_FREE(selnamev);
214
0
        return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize);
215
0
    }
216
217
0
    if (vsize == 1) {
218
0
        pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnameh);
219
0
        LEPT_FREE(selnameh);
220
0
    } else if (hsize == 1) {
221
0
        pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnamev);
222
0
        LEPT_FREE(selnamev);
223
0
    } else {
224
0
        pixt1 = pixAddBorder(pixs, 32, 0);
225
0
        pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh);
226
0
        pixFMorphopGen_1(pixt1, pixt3, L_MORPH_DILATE, selnamev);
227
0
        pixt2 = pixRemoveBorder(pixt1, 32);
228
0
        pixDestroy(&pixt1);
229
0
        pixDestroy(&pixt3);
230
0
        LEPT_FREE(selnameh);
231
0
        LEPT_FREE(selnamev);
232
0
    }
233
234
0
    if (!pixd)
235
0
        return pixt2;
236
237
0
    pixTransferAllData(pixd, &pixt2, 0, 0);
238
0
    return pixd;
239
0
}
240
241
242
/*!
243
 * \brief   pixErodeBrickDwa()
244
 *
245
 * \param[in]    pixd     [optional]; this can be null, equal to pixs,
246
 *                        or different from pixs
247
 * \param[in]    pixs     1 bpp
248
 * \param[in]    hsize    width of brick Sel
249
 * \param[in]    vsize    height of brick Sel
250
 * \return  pixd
251
 *
252
 * <pre>
253
 * Notes:
254
 *      (1) These implement 2D brick Sels, using linear Sels generated
255
 *          with selaAddBasic().
256
 *      (2) A brick Sel has hits for all elements.
257
 *      (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
258
 *      (4) Do separably if both hsize and vsize are > 1.
259
 *      (5) It is necessary that both horizontal and vertical Sels
260
 *          of the input size are defined in the basic sela.
261
 *      (6) Note that we must always set or clear the border pixels
262
 *          before each operation, depending on the the b.c.
263
 *          (symmetric or asymmetric).
264
 *      (7) There are three cases:
265
 *          (a) pixd == null   (result into new pixd)
266
 *          (b) pixd == pixs   (in-place; writes result back to pixs)
267
 *          (c) pixd != pixs   (puts result into existing pixd)
268
 *      (8) For clarity, if the case is known, use these patterns:
269
 *          (a) pixd = pixErodeBrickDwa(NULL, pixs, ...);
270
 *          (b) pixErodeBrickDwa(pixs, pixs, ...);
271
 *          (c) pixErodeBrickDwa(pixd, pixs, ...);
272
 *      (9) The size of the result is determined by pixs.
273
 *      (10) If either linear Sel is not found, this calls
274
 *           the appropriate decomposible function.
275
 * </pre>
276
 */
277
PIX *
278
pixErodeBrickDwa(PIX     *pixd,
279
                 PIX     *pixs,
280
                 l_int32  hsize,
281
                 l_int32  vsize)
282
0
{
283
0
l_int32  found;
284
0
char    *selnameh, *selnamev;
285
0
SELA    *sela;
286
0
PIX     *pixt1, *pixt2, *pixt3;
287
288
0
    if (!pixs)
289
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
290
0
    if (pixGetDepth(pixs) != 1)
291
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
292
0
    if (hsize < 1 || vsize < 1)
293
0
        return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
294
295
0
    if (hsize == 1 && vsize == 1)
296
0
        return pixCopy(pixd, pixs);
297
298
0
    sela = selaAddBasic(NULL);
299
0
    found = TRUE;
300
0
    selnameh = selnamev = NULL;
301
0
    if (hsize > 1) {
302
0
        selnameh = selaGetBrickName(sela, hsize, 1);
303
0
        if (!selnameh) found = FALSE;
304
0
    }
305
0
    if (vsize > 1) {
306
0
        selnamev = selaGetBrickName(sela, 1, vsize);
307
0
        if (!selnamev) found = FALSE;
308
0
    }
309
0
    selaDestroy(&sela);
310
0
    if (!found) {
311
0
        L_INFO("Calling the decomposable dwa function\n", __func__);
312
0
        if (selnameh) LEPT_FREE(selnameh);
313
0
        if (selnamev) LEPT_FREE(selnamev);
314
0
        return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize);
315
0
    }
316
317
0
    if (vsize == 1) {
318
0
        pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnameh);
319
0
        LEPT_FREE(selnameh);
320
0
    } else if (hsize == 1) {
321
0
        pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnamev);
322
0
        LEPT_FREE(selnamev);
323
0
    } else {
324
0
        pixt1 = pixAddBorder(pixs, 32, 0);
325
0
        pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh);
326
0
        pixFMorphopGen_1(pixt1, pixt3, L_MORPH_ERODE, selnamev);
327
0
        pixt2 = pixRemoveBorder(pixt1, 32);
328
0
        pixDestroy(&pixt1);
329
0
        pixDestroy(&pixt3);
330
0
        LEPT_FREE(selnameh);
331
0
        LEPT_FREE(selnamev);
332
0
    }
333
334
0
    if (!pixd)
335
0
        return pixt2;
336
337
0
    pixTransferAllData(pixd, &pixt2, 0, 0);
338
0
    return pixd;
339
0
}
340
341
342
/*!
343
 * \brief   pixOpenBrickDwa()
344
 *
345
 * \param[in]    pixd     [optional]; this can be null, equal to pixs,
346
 *                        or different from pixs
347
 * \param[in]    pixs     1 bpp
348
 * \param[in]    hsize    width of brick Sel
349
 * \param[in]    vsize    height of brick Sel
350
 * \return  pixd
351
 *
352
 * <pre>
353
 * Notes:
354
 *      (1) These implement 2D brick Sels, using linear Sels generated
355
 *          with selaAddBasic().
356
 *      (2) A brick Sel has hits for all elements.
357
 *      (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
358
 *      (4) Do separably if both hsize and vsize are > 1.
359
 *      (5) It is necessary that both horizontal and vertical Sels
360
 *          of the input size are defined in the basic sela.
361
 *      (6) Note that we must always set or clear the border pixels
362
 *          before each operation, depending on the the b.c.
363
 *          (symmetric or asymmetric).
364
 *      (7) There are three cases:
365
 *          (a) pixd == null   (result into new pixd)
366
 *          (b) pixd == pixs   (in-place; writes result back to pixs)
367
 *          (c) pixd != pixs   (puts result into existing pixd)
368
 *      (8) For clarity, if the case is known, use these patterns:
369
 *          (a) pixd = pixOpenBrickDwa(NULL, pixs, ...);
370
 *          (b) pixOpenBrickDwa(pixs, pixs, ...);
371
 *          (c) pixOpenBrickDwa(pixd, pixs, ...);
372
 *      (9) The size of the result is determined by pixs.
373
 *      (10) If either linear Sel is not found, this calls
374
 *           the appropriate decomposible function.
375
 * </pre>
376
 */
377
PIX *
378
pixOpenBrickDwa(PIX     *pixd,
379
                PIX     *pixs,
380
                l_int32  hsize,
381
                l_int32  vsize)
382
0
{
383
0
l_int32  found;
384
0
char    *selnameh, *selnamev;
385
0
SELA    *sela;
386
0
PIX     *pixt1, *pixt2, *pixt3;
387
388
0
    if (!pixs)
389
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
390
0
    if (pixGetDepth(pixs) != 1)
391
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
392
0
    if (hsize < 1 || vsize < 1)
393
0
        return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
394
395
0
    if (hsize == 1 && vsize == 1)
396
0
        return pixCopy(pixd, pixs);
397
398
0
    sela = selaAddBasic(NULL);
399
0
    found = TRUE;
400
0
    selnameh = selnamev = NULL;
401
0
    if (hsize > 1) {
402
0
        selnameh = selaGetBrickName(sela, hsize, 1);
403
0
        if (!selnameh) found = FALSE;
404
0
    }
405
0
    if (vsize > 1) {
406
0
        selnamev = selaGetBrickName(sela, 1, vsize);
407
0
        if (!selnamev) found = FALSE;
408
0
    }
409
0
    selaDestroy(&sela);
410
0
    if (!found) {
411
0
        L_INFO("Calling the decomposable dwa function\n", __func__);
412
0
        if (selnameh) LEPT_FREE(selnameh);
413
0
        if (selnamev) LEPT_FREE(selnamev);
414
0
        return pixOpenCompBrickDwa(pixd, pixs, hsize, vsize);
415
0
    }
416
417
0
    pixt1 = pixAddBorder(pixs, 32, 0);
418
0
    if (vsize == 1) {   /* horizontal only */
419
0
        pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnameh);
420
0
        LEPT_FREE(selnameh);
421
0
    } else if (hsize == 1) {   /* vertical only */
422
0
        pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnamev);
423
0
        LEPT_FREE(selnamev);
424
0
    } else {  /* do separable */
425
0
        pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh);
426
0
        pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev);
427
0
        pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh);
428
0
        pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev);
429
0
        LEPT_FREE(selnameh);
430
0
        LEPT_FREE(selnamev);
431
0
        pixDestroy(&pixt3);
432
0
    }
433
0
    pixt3 = pixRemoveBorder(pixt2, 32);
434
0
    pixDestroy(&pixt1);
435
0
    pixDestroy(&pixt2);
436
437
0
    if (!pixd)
438
0
        return pixt3;
439
440
0
    pixTransferAllData(pixd, &pixt3, 0, 0);
441
0
    return pixd;
442
0
}
443
444
445
/*!
446
 * \brief   pixCloseBrickDwa()
447
 *
448
 * \param[in]    pixd     [optional]; this can be null, equal to pixs,
449
 *                        or different from pixs
450
 * \param[in]    pixs     1 bpp
451
 * \param[in]    hsize    width of brick Sel
452
 * \param[in]    vsize    height of brick Sel
453
 * \return  pixd
454
 *
455
 * <pre>
456
 * Notes:
457
 *      (1) This is a 'safe' closing; we add an extra border of 32 OFF
458
 *          pixels for the standard asymmetric b.c.
459
 *      (2) These implement 2D brick Sels, using linear Sels generated
460
 *          with selaAddBasic().
461
 *      (3) A brick Sel has hits for all elements.
462
 *      (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
463
 *      (5) Do separably if both hsize and vsize are > 1.
464
 *      (6) It is necessary that both horizontal and vertical Sels
465
 *          of the input size are defined in the basic sela.
466
 *      (7) Note that we must always set or clear the border pixels
467
 *          before each operation, depending on the the b.c.
468
 *          (symmetric or asymmetric).
469
 *      (8) There are three cases:
470
 *          (a) pixd == null   (result into new pixd)
471
 *          (b) pixd == pixs   (in-place; writes result back to pixs)
472
 *          (c) pixd != pixs   (puts result into existing pixd)
473
 *      (9) For clarity, if the case is known, use these patterns:
474
 *          (a) pixd = pixCloseBrickDwa(NULL, pixs, ...);
475
 *          (b) pixCloseBrickDwa(pixs, pixs, ...);
476
 *          (c) pixCloseBrickDwa(pixd, pixs, ...);
477
 *      (10) The size of the result is determined by pixs.
478
 *      (11) If either linear Sel is not found, this calls
479
 *           the appropriate decomposible function.
480
 * </pre>
481
 */
482
PIX *
483
pixCloseBrickDwa(PIX     *pixd,
484
                 PIX     *pixs,
485
                 l_int32  hsize,
486
                 l_int32  vsize)
487
0
{
488
0
l_int32  bordercolor, bordersize, found;
489
0
char    *selnameh, *selnamev;
490
0
SELA    *sela;
491
0
PIX     *pixt1, *pixt2, *pixt3;
492
493
0
    if (!pixs)
494
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
495
0
    if (pixGetDepth(pixs) != 1)
496
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
497
0
    if (hsize < 1 || vsize < 1)
498
0
        return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
499
500
0
    if (hsize == 1 && vsize == 1)
501
0
        return pixCopy(pixd, pixs);
502
503
0
    sela = selaAddBasic(NULL);
504
0
    found = TRUE;
505
0
    selnameh = selnamev = NULL;
506
0
    if (hsize > 1) {
507
0
        selnameh = selaGetBrickName(sela, hsize, 1);
508
0
        if (!selnameh) found = FALSE;
509
0
    }
510
0
    if (vsize > 1) {
511
0
        selnamev = selaGetBrickName(sela, 1, vsize);
512
0
        if (!selnamev) found = FALSE;
513
0
    }
514
0
    selaDestroy(&sela);
515
0
    if (!found) {
516
0
        L_INFO("Calling the decomposable dwa function\n", __func__);
517
0
        if (selnameh) LEPT_FREE(selnameh);
518
0
        if (selnamev) LEPT_FREE(selnamev);
519
0
        return pixCloseCompBrickDwa(pixd, pixs, hsize, vsize);
520
0
    }
521
522
        /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need
523
         * an extra 32 OFF pixels around the image (in addition to
524
         * the 32 added pixels for all dwa operations), whereas with
525
         * SYMMETRIC_MORPH_BC this is not necessary. */
526
0
    bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
527
0
    if (bordercolor == 0)   /* asymmetric b.c. */
528
0
        bordersize = 64;
529
0
    else   /* symmetric b.c. */
530
0
        bordersize = 32;
531
0
    pixt1 = pixAddBorder(pixs, bordersize, 0);
532
533
0
    if (vsize == 1) {   /* horizontal only */
534
0
        pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh);
535
0
        LEPT_FREE(selnameh);
536
0
    } else if (hsize == 1) {   /* vertical only */
537
0
        pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev);
538
0
        LEPT_FREE(selnamev);
539
0
    } else {  /* do separable */
540
0
        pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh);
541
0
        pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev);
542
0
        pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh);
543
0
        pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev);
544
0
        LEPT_FREE(selnameh);
545
0
        LEPT_FREE(selnamev);
546
0
        pixDestroy(&pixt3);
547
0
    }
548
0
    pixt3 = pixRemoveBorder(pixt2, bordersize);
549
0
    pixDestroy(&pixt1);
550
0
    pixDestroy(&pixt2);
551
552
0
    if (!pixd)
553
0
        return pixt3;
554
555
0
    pixTransferAllData(pixd, &pixt3, 0, 0);
556
0
    return pixd;
557
0
}
558
559
560
/*-----------------------------------------------------------------*
561
 *    Binary composite morphological (dwa) ops with brick Sels     *
562
 *-----------------------------------------------------------------*/
563
/*!
564
 * \brief   pixDilateCompBrickDwa()
565
 *
566
 * \param[in]    pixd     [optional]; this can be null, equal to pixs,
567
 *                        or different from pixs
568
 * \param[in]    pixs     1 bpp
569
 * \param[in]    hsize    width of brick Sel
570
 * \param[in]    vsize    height of brick Sel
571
 * \return  pixd
572
 *
573
 * <pre>
574
 * Notes:
575
 *      (1) These implement a separable composite dilation with 2D brick Sels.
576
 *      (2) For efficiency, it may decompose each linear morphological
577
 *          operation into two (brick + comb).
578
 *      (3) A brick Sel has hits for all elements.
579
 *      (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
580
 *      (5) Do separably if both hsize and vsize are > 1.
581
 *      (6) It is necessary that both horizontal and vertical Sels
582
 *          of the input size are defined in the basic sela.
583
 *      (7) There are three cases:
584
 *          (a) pixd == null   (result into new pixd)
585
 *          (b) pixd == pixs   (in-place; writes result back to pixs)
586
 *          (c) pixd != pixs   (puts result into existing pixd)
587
 *      (8) For clarity, if the case is known, use these patterns:
588
 *          (a) pixd = pixDilateCompBrickDwa(NULL, pixs, ...);
589
 *          (b) pixDilateCompBrickDwa(pixs, pixs, ...);
590
 *          (c) pixDilateCompBrickDwa(pixd, pixs, ...);
591
 *      (9) The size of pixd is determined by pixs.
592
 *      (10) CAUTION: both hsize and vsize are being decomposed.
593
 *          The decomposer chooses a product of sizes (call them
594
 *          'terms') for each that is close to the input size,
595
 *           but not necessarily equal to it.  It attempts to optimize:
596
 *              (a) for consistency with the input values: the product
597
 *                  of terms is close to the input size
598
 *              (b) for efficiency of the operation: the sum of the
599
 *                  terms is small; ideally about twice the square
600
 *                   root of the input size.
601
 *           So, for example, if the input hsize = 37, which is
602
 *           a prime number, the decomposer will break this into two
603
 *           terms, 6 and 6, so that the net result is a dilation
604
 *           with hsize = 36.
605
 * </pre>
606
 */
607
PIX *
608
pixDilateCompBrickDwa(PIX     *pixd,
609
                      PIX     *pixs,
610
                      l_int32  hsize,
611
                      l_int32  vsize)
612
0
{
613
0
char    *selnameh1, *selnameh2, *selnamev1, *selnamev2;
614
0
l_int32  hsize1, hsize2, vsize1, vsize2;
615
0
PIX     *pixt1, *pixt2, *pixt3;
616
617
0
    if (!pixs)
618
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
619
0
    if (pixGetDepth(pixs) != 1)
620
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
621
0
    if (hsize < 1 || vsize < 1)
622
0
        return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
623
0
    if (hsize > 63 || vsize > 63)
624
0
        return pixDilateCompBrickExtendDwa(pixd, pixs, hsize, vsize);
625
626
0
    if (hsize == 1 && vsize == 1)
627
0
        return pixCopy(pixd, pixs);
628
629
0
    hsize1 = hsize2 = vsize1 = vsize2 = 1;
630
0
    selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL;
631
0
    if (hsize > 1)
632
0
        getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1,
633
0
                               &selnameh2, NULL, NULL);
634
0
    if (vsize > 1)
635
0
        getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL,
636
0
                               &selnamev1, &selnamev2);
637
638
#if DEBUG_SEL_LOOKUP
639
    lept_stderr("nameh1=%s, nameh2=%s, namev1=%s, namev2=%s\n",
640
                selnameh1, selnameh2, selnamev1, selnamev2);
641
    lept_stderr("hsize1=%d, hsize2=%d, vsize1=%d, vsize2=%d\n",
642
                hsize1, hsize2, vsize1, vsize2);
643
#endif  /* DEBUG_SEL_LOOKUP */
644
645
0
    pixt1 = pixAddBorder(pixs, 64, 0);
646
0
    if (vsize == 1) {
647
0
        if (hsize2 == 1) {
648
0
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
649
0
        } else {
650
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
651
0
            pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2);
652
0
            pixDestroy(&pixt3);
653
0
        }
654
0
    } else if (hsize == 1) {
655
0
        if (vsize2 == 1) {
656
0
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1);
657
0
        } else {
658
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1);
659
0
            pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2);
660
0
            pixDestroy(&pixt3);
661
0
        }
662
0
    } else {  /* vsize and hsize both > 1 */
663
0
        if (hsize2 == 1) {
664
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
665
0
        } else {
666
0
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
667
0
            pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_DILATE, selnameh2);
668
0
            pixDestroy(&pixt2);
669
0
        }
670
0
        if (vsize2 == 1) {
671
0
            pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
672
0
        } else {
673
0
            pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
674
0
            pixFMorphopGen_2(pixt2, pixt2, L_MORPH_DILATE, selnamev2);
675
0
        }
676
0
        pixDestroy(&pixt3);
677
0
    }
678
0
    pixDestroy(&pixt1);
679
0
    pixt1 = pixRemoveBorder(pixt2, 64);
680
0
    pixDestroy(&pixt2);
681
0
    if (selnameh1) LEPT_FREE(selnameh1);
682
0
    if (selnameh2) LEPT_FREE(selnameh2);
683
0
    if (selnamev1) LEPT_FREE(selnamev1);
684
0
    if (selnamev2) LEPT_FREE(selnamev2);
685
686
0
    if (!pixd)
687
0
        return pixt1;
688
689
0
    pixTransferAllData(pixd, &pixt1, 0, 0);
690
0
    return pixd;
691
0
}
692
693
694
/*!
695
 * \brief   pixErodeCompBrickDwa()
696
 *
697
 * \param[in]    pixd     [optional]; this can be null, equal to pixs,
698
 *                        or different from pixs
699
 * \param[in]    pixs     1 bpp
700
 * \param[in]    hsize    width of brick Sel
701
 * \param[in]    vsize    height of brick Sel
702
 * \return  pixd
703
 *
704
 * <pre>
705
 * Notes:
706
 *      (1) These implement a separable composite erosion with 2D brick Sels.
707
 *      (2) For efficiency, it may decompose each linear morphological
708
 *          operation into two (brick + comb).
709
 *      (3) A brick Sel has hits for all elements.
710
 *      (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
711
 *      (5) Do separably if both hsize and vsize are > 1.
712
 *      (6) It is necessary that both horizontal and vertical Sels
713
 *          of the input size are defined in the basic sela.
714
 *      (7) There are three cases:
715
 *          (a) pixd == null   (result into new pixd)
716
 *          (b) pixd == pixs   (in-place; writes result back to pixs)
717
 *          (c) pixd != pixs   (puts result into existing pixd)
718
 *      (8) For clarity, if the case is known, use these patterns:
719
 *          (a) pixd = pixErodeCompBrickDwa(NULL, pixs, ...);
720
 *          (b) pixErodeCompBrickDwa(pixs, pixs, ...);
721
 *          (c) pixErodeCompBrickDwa(pixd, pixs, ...);
722
 *      (9) The size of pixd is determined by pixs.
723
 *      (10) CAUTION: both hsize and vsize are being decomposed.
724
 *          The decomposer chooses a product of sizes (call them
725
 *          'terms') for each that is close to the input size,
726
 *           but not necessarily equal to it.  It attempts to optimize:
727
 *              (a) for consistency with the input values: the product
728
 *                  of terms is close to the input size
729
 *              (b) for efficiency of the operation: the sum of the
730
 *                  terms is small; ideally about twice the square
731
 *                   root of the input size.
732
 *           So, for example, if the input hsize = 37, which is
733
 *           a prime number, the decomposer will break this into two
734
 *           terms, 6 and 6, so that the net result is a dilation
735
 *           with hsize = 36.
736
 * </pre>
737
 */
738
PIX *
739
pixErodeCompBrickDwa(PIX     *pixd,
740
                     PIX     *pixs,
741
                     l_int32  hsize,
742
                     l_int32  vsize)
743
0
{
744
0
char    *selnameh1, *selnameh2, *selnamev1, *selnamev2;
745
0
l_int32  hsize1, hsize2, vsize1, vsize2, bordercolor;
746
0
PIX     *pixt1, *pixt2, *pixt3;
747
748
0
    if (!pixs)
749
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
750
0
    if (pixGetDepth(pixs) != 1)
751
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
752
0
    if (hsize < 1 || vsize < 1)
753
0
        return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
754
0
    if (hsize > 63 || vsize > 63)
755
0
        return pixErodeCompBrickExtendDwa(pixd, pixs, hsize, vsize);
756
757
0
    if (hsize == 1 && vsize == 1)
758
0
        return pixCopy(pixd, pixs);
759
760
0
    hsize1 = hsize2 = vsize1 = vsize2 = 1;
761
0
    selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL;
762
0
    if (hsize > 1)
763
0
        getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1,
764
0
                               &selnameh2, NULL, NULL);
765
0
    if (vsize > 1)
766
0
        getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL,
767
0
                               &selnamev1, &selnamev2);
768
769
        /* For symmetric b.c., bordercolor == 1 for erosion */
770
0
    bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
771
0
    pixt1 = pixAddBorder(pixs, 64, bordercolor);
772
773
0
    if (vsize == 1) {
774
0
        if (hsize2 == 1) {
775
0
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
776
0
        } else {
777
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
778
0
            pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2);
779
0
            pixDestroy(&pixt3);
780
0
        }
781
0
    } else if (hsize == 1) {
782
0
        if (vsize2 == 1) {
783
0
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1);
784
0
        } else {
785
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1);
786
0
            pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2);
787
0
            pixDestroy(&pixt3);
788
0
        }
789
0
    } else {  /* vsize and hsize both > 1 */
790
0
        if (hsize2 == 1) {
791
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
792
0
        } else {
793
0
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
794
0
            pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_ERODE, selnameh2);
795
0
            pixDestroy(&pixt2);
796
0
        }
797
0
        if (vsize2 == 1) {
798
0
            pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1);
799
0
        } else {
800
0
            pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1);
801
0
            pixFMorphopGen_2(pixt2, pixt2, L_MORPH_ERODE, selnamev2);
802
0
        }
803
0
        pixDestroy(&pixt3);
804
0
    }
805
0
    pixDestroy(&pixt1);
806
0
    pixt1 = pixRemoveBorder(pixt2, 64);
807
0
    pixDestroy(&pixt2);
808
0
    if (selnameh1) LEPT_FREE(selnameh1);
809
0
    if (selnameh2) LEPT_FREE(selnameh2);
810
0
    if (selnamev1) LEPT_FREE(selnamev1);
811
0
    if (selnamev2) LEPT_FREE(selnamev2);
812
813
0
    if (!pixd)
814
0
        return pixt1;
815
816
0
    pixTransferAllData(pixd, &pixt1, 0, 0);
817
0
    return pixd;
818
0
}
819
820
821
/*!
822
 * \brief   pixOpenCompBrickDwa()
823
 *
824
 * \param[in]    pixd     [optional]; this can be null, equal to pixs,
825
 *                        or different from pixs
826
 * \param[in]    pixs     1 bpp
827
 * \param[in]    hsize    width of brick Sel
828
 * \param[in]    vsize    height of brick Sel
829
 * \return  pixd
830
 *
831
 * <pre>
832
 * Notes:
833
 *      (1) These implement a separable composite opening with 2D brick Sels.
834
 *      (2) For efficiency, it may decompose each linear morphological
835
 *          operation into two (brick + comb).
836
 *      (3) A brick Sel has hits for all elements.
837
 *      (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
838
 *      (5) Do separably if both hsize and vsize are > 1.
839
 *      (6) It is necessary that both horizontal and vertical Sels
840
 *          of the input size are defined in the basic sela.
841
 *      (7) There are three cases:
842
 *          (a) pixd == null   (result into new pixd)
843
 *          (b) pixd == pixs   (in-place; writes result back to pixs)
844
 *          (c) pixd != pixs   (puts result into existing pixd)
845
 *      (8) For clarity, if the case is known, use these patterns:
846
 *          (a) pixd = pixOpenCompBrickDwa(NULL, pixs, ...);
847
 *          (b) pixOpenCompBrickDwa(pixs, pixs, ...);
848
 *          (c) pixOpenCompBrickDwa(pixd, pixs, ...);
849
 *      (9) The size of pixd is determined by pixs.
850
 *      (10) CAUTION: both hsize and vsize are being decomposed.
851
 *          The decomposer chooses a product of sizes (call them
852
 *          'terms') for each that is close to the input size,
853
 *           but not necessarily equal to it.  It attempts to optimize:
854
 *              (a) for consistency with the input values: the product
855
 *                  of terms is close to the input size
856
 *              (b) for efficiency of the operation: the sum of the
857
 *                  terms is small; ideally about twice the square
858
 *                   root of the input size.
859
 *           So, for example, if the input hsize = 37, which is
860
 *           a prime number, the decomposer will break this into two
861
 *           terms, 6 and 6, so that the net result is a dilation
862
 *           with hsize = 36.
863
 * </pre>
864
 */
865
PIX *
866
pixOpenCompBrickDwa(PIX     *pixd,
867
                    PIX     *pixs,
868
                    l_int32  hsize,
869
                    l_int32  vsize)
870
0
{
871
0
char    *selnameh1, *selnameh2, *selnamev1, *selnamev2;
872
0
l_int32  hsize1, hsize2, vsize1, vsize2, bordercolor;
873
0
PIX     *pixt1, *pixt2, *pixt3;
874
875
0
    if (!pixs)
876
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
877
0
    if (pixGetDepth(pixs) != 1)
878
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
879
0
    if (hsize < 1 || vsize < 1)
880
0
        return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
881
0
    if (hsize > 63 || vsize > 63)
882
0
        return pixOpenCompBrickExtendDwa(pixd, pixs, hsize, vsize);
883
884
0
    if (hsize == 1 && vsize == 1)
885
0
        return pixCopy(pixd, pixs);
886
887
0
    hsize1 = hsize2 = vsize1 = vsize2 = 1;
888
0
    selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL;
889
0
    if (hsize > 1)
890
0
        getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1,
891
0
                               &selnameh2, NULL, NULL);
892
0
    if (vsize > 1)
893
0
        getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL,
894
0
                               &selnamev1, &selnamev2);
895
896
        /* For symmetric b.c., initialize erosion with bordercolor == 1 */
897
0
    bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
898
0
    pixt1 = pixAddBorder(pixs, 64, bordercolor);
899
900
0
    if (vsize == 1) {
901
0
        if (hsize2 == 1) {
902
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
903
0
            if (bordercolor == 1)
904
0
                pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR);
905
0
            pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnameh1);
906
0
        } else {
907
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
908
0
            pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2);
909
0
            if (bordercolor == 1)
910
0
                pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR);
911
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1);
912
0
            pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2);
913
0
        }
914
0
    } else if (hsize == 1) {
915
0
        if (vsize2 == 1)  {
916
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1);
917
0
            if (bordercolor == 1)
918
0
                pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR);
919
0
            pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
920
0
        } else {
921
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1);
922
0
            pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2);
923
0
            if (bordercolor == 1)
924
0
                pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR);
925
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
926
0
            pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2);
927
0
        }
928
0
    } else {  /* vsize and hsize both > 1 */
929
0
        if (hsize2 == 1 && vsize2 == 1) {
930
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
931
0
            pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1);
932
0
            if (bordercolor == 1)
933
0
                pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR);
934
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1);
935
0
            pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1);
936
0
        } else if (vsize2 == 1) {
937
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
938
0
            pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2);
939
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
940
0
            if (bordercolor == 1)
941
0
                pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR);
942
0
            pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1);
943
0
            pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnameh2);
944
0
            pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1);
945
0
        } else if (hsize2 == 1) {
946
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
947
0
            pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1);
948
0
            pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnamev2);
949
0
            if (bordercolor == 1)
950
0
                pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR);
951
0
            pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1);
952
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
953
0
            pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2);
954
0
        } else {   /* both directions are combed */
955
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
956
0
            pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2);
957
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
958
0
            pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2);
959
0
            if (bordercolor == 1)
960
0
                pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR);
961
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1);
962
0
            pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2);
963
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
964
0
            pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2);
965
0
        }
966
0
    }
967
0
    pixDestroy(&pixt3);
968
969
0
    pixDestroy(&pixt1);
970
0
    pixt1 = pixRemoveBorder(pixt2, 64);
971
0
    pixDestroy(&pixt2);
972
0
    if (selnameh1) LEPT_FREE(selnameh1);
973
0
    if (selnameh2) LEPT_FREE(selnameh2);
974
0
    if (selnamev1) LEPT_FREE(selnamev1);
975
0
    if (selnamev2) LEPT_FREE(selnamev2);
976
977
0
    if (!pixd)
978
0
        return pixt1;
979
980
0
    pixTransferAllData(pixd, &pixt1, 0, 0);
981
0
    return pixd;
982
0
}
983
984
985
/*!
986
 * \brief   pixCloseCompBrickDwa()
987
 *
988
 * \param[in]    pixd     [optional]; this can be null, equal to pixs,
989
 *                        or different from pixs
990
 * \param[in]    pixs     1 bpp
991
 * \param[in]    hsize    width of brick Sel
992
 * \param[in]    vsize    height of brick Sel
993
 * \return  pixd
994
 *
995
 * <pre>
996
 * Notes:
997
 *      (1) This implements a separable composite safe closing with 2D
998
 *          brick Sels.
999
 *      (2) For efficiency, it may decompose each linear morphological
1000
 *          operation into two (brick + comb).
1001
 *      (3) A brick Sel has hits for all elements.
1002
 *      (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
1003
 *      (5) Do separably if both hsize and vsize are > 1.
1004
 *      (6) It is necessary that both horizontal and vertical Sels
1005
 *          of the input size are defined in the basic sela.
1006
 *      (7) There are three cases:
1007
 *          (a) pixd == null   (result into new pixd)
1008
 *          (b) pixd == pixs   (in-place; writes result back to pixs)
1009
 *          (c) pixd != pixs   (puts result into existing pixd)
1010
 *      (8) For clarity, if the case is known, use these patterns:
1011
 *          (a) pixd = pixCloseCompBrickDwa(NULL, pixs, ...);
1012
 *          (b) pixCloseCompBrickDwa(pixs, pixs, ...);
1013
 *          (c) pixCloseCompBrickDwa(pixd, pixs, ...);
1014
 *      (9) The size of pixd is determined by pixs.
1015
 *      (10) CAUTION: both hsize and vsize are being decomposed.
1016
 *          The decomposer chooses a product of sizes (call them
1017
 *          'terms') for each that is close to the input size,
1018
 *           but not necessarily equal to it.  It attempts to optimize:
1019
 *              (a) for consistency with the input values: the product
1020
 *                  of terms is close to the input size
1021
 *              (b) for efficiency of the operation: the sum of the
1022
 *                  terms is small; ideally about twice the square
1023
 *                   root of the input size.
1024
 *           So, for example, if the input hsize = 37, which is
1025
 *           a prime number, the decomposer will break this into two
1026
 *           terms, 6 and 6, so that the net result is a dilation
1027
 *           with hsize = 36.
1028
 * </pre>
1029
 */
1030
PIX *
1031
pixCloseCompBrickDwa(PIX     *pixd,
1032
                     PIX     *pixs,
1033
                     l_int32  hsize,
1034
                     l_int32  vsize)
1035
0
{
1036
0
char    *selnameh1, *selnameh2, *selnamev1, *selnamev2;
1037
0
l_int32  hsize1, hsize2, vsize1, vsize2, setborder;
1038
0
PIX     *pixt1, *pixt2, *pixt3;
1039
1040
0
    if (!pixs)
1041
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1042
0
    if (pixGetDepth(pixs) != 1)
1043
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1044
0
    if (hsize < 1 || vsize < 1)
1045
0
        return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1046
0
    if (hsize > 63 || vsize > 63)
1047
0
        return pixCloseCompBrickExtendDwa(pixd, pixs, hsize, vsize);
1048
1049
0
    if (hsize == 1 && vsize == 1)
1050
0
        return pixCopy(pixd, pixs);
1051
1052
0
    hsize1 = hsize2 = vsize1 = vsize2 = 1;
1053
0
    selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL;
1054
0
    if (hsize > 1)
1055
0
        getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1,
1056
0
                               &selnameh2, NULL, NULL);
1057
0
    if (vsize > 1)
1058
0
        getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL,
1059
0
                               &selnamev1, &selnamev2);
1060
1061
0
    pixt3 = NULL;
1062
        /* For symmetric b.c., PIX_SET border for erosions */
1063
0
    setborder = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
1064
0
    pixt1 = pixAddBorder(pixs, 64, 0);
1065
1066
0
    if (vsize == 1) {
1067
0
        if (hsize2 == 1) {
1068
0
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh1);
1069
0
        } else {
1070
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
1071
0
            pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2);
1072
0
            if (setborder == 1)
1073
0
                pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET);
1074
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1);
1075
0
            pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2);
1076
0
        }
1077
0
    } else if (hsize == 1) {
1078
0
        if (vsize2 == 1) {
1079
0
            pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev1);
1080
0
        } else {
1081
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1);
1082
0
            pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2);
1083
0
            if (setborder == 1)
1084
0
                pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET);
1085
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
1086
0
            pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2);
1087
0
        }
1088
0
    } else {  /* vsize and hsize both > 1 */
1089
0
        if (hsize2 == 1 && vsize2 == 1) {
1090
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
1091
0
            pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
1092
0
            if (setborder == 1)
1093
0
                pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET);
1094
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1);
1095
0
            pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1);
1096
0
        } else if (vsize2 == 1) {
1097
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
1098
0
            pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2);
1099
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
1100
0
            if (setborder == 1)
1101
0
                pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET);
1102
0
            pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1);
1103
0
            pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnameh2);
1104
0
            pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1);
1105
0
        } else if (hsize2 == 1) {
1106
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
1107
0
            pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
1108
0
            pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnamev2);
1109
0
            if (setborder == 1)
1110
0
                pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET);
1111
0
            pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1);
1112
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
1113
0
            pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2);
1114
0
        } else {   /* both directions are combed */
1115
0
            pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
1116
0
            pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2);
1117
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
1118
0
            pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2);
1119
0
            if (setborder == 1)
1120
0
                pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET);
1121
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1);
1122
0
            pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2);
1123
0
            pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
1124
0
            pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2);
1125
0
        }
1126
0
    }
1127
0
    pixDestroy(&pixt3);
1128
1129
0
    pixDestroy(&pixt1);
1130
0
    pixt1 = pixRemoveBorder(pixt2, 64);
1131
0
    pixDestroy(&pixt2);
1132
0
    if (selnameh1) LEPT_FREE(selnameh1);
1133
0
    if (selnameh2) LEPT_FREE(selnameh2);
1134
0
    if (selnamev1) LEPT_FREE(selnamev1);
1135
0
    if (selnamev2) LEPT_FREE(selnamev2);
1136
1137
0
    if (!pixd)
1138
0
        return pixt1;
1139
1140
0
    pixTransferAllData(pixd, &pixt1, 0, 0);
1141
0
    return pixd;
1142
0
}
1143
1144
1145
/*--------------------------------------------------------------------------*
1146
 *    Binary expanded composite morphological (dwa) ops with brick Sels     *
1147
 *--------------------------------------------------------------------------*/
1148
/*!
1149
 * \brief   pixDilateCompBrickExtendDwa()
1150
 *
1151
 * \param[in]    pixd     [optional]; this can be null, equal to pixs,
1152
 *                        or different from pixs
1153
 * \param[in]    pixs     1 bpp
1154
 * \param[in]    hsize    width of brick Sel
1155
 * \param[in]    vsize    height of brick Sel
1156
 * \return  pixd
1157
 *
1158
 * <pre>
1159
 * Notes:
1160
 *      (1) Ankur Jain suggested and implemented extending the composite
1161
 *          DWA operations beyond the 63 pixel limit.  This is a
1162
 *          simplified and approximate implementation of the extension.
1163
 *          This allows arbitrary Dwa morph operations using brick Sels,
1164
 *          by decomposing the horizontal and vertical dilations into
1165
 *          a sequence of 63-element dilations plus a dilation of size
1166
 *          between 3 and 62.
1167
 *      (2) The 63-element dilations are exact, whereas the extra dilation
1168
 *          is approximate, because the underlying decomposition is
1169
 *          in pixDilateCompBrickDwa().  See there for further details.
1170
 *      (3) There are three cases:
1171
 *          (a) pixd == null   (result into new pixd)
1172
 *          (b) pixd == pixs   (in-place; writes result back to pixs)
1173
 *          (c) pixd != pixs   (puts result into existing pixd)
1174
 *      (4) There is no need to call this directly:  pixDilateCompBrickDwa()
1175
 *          calls this function if either brick dimension exceeds 63.
1176
 * </pre>
1177
 */
1178
PIX *
1179
pixDilateCompBrickExtendDwa(PIX     *pixd,
1180
                            PIX     *pixs,
1181
                            l_int32  hsize,
1182
                            l_int32  vsize)
1183
0
{
1184
0
l_int32  i, nops, nh, extrah, nv, extrav;
1185
0
PIX     *pixt1, *pixt2, *pixt3;
1186
1187
0
    if (!pixs)
1188
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1189
0
    if (pixGetDepth(pixs) != 1)
1190
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1191
0
    if (hsize < 1 || vsize < 1)
1192
0
        return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1193
1194
0
    if (hsize < 64 && vsize < 64)
1195
0
        return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize);
1196
1197
0
    if (hsize > 63)
1198
0
        getExtendedCompositeParameters(hsize, &nh, &extrah, NULL);
1199
0
    if (vsize > 63)
1200
0
        getExtendedCompositeParameters(vsize, &nv, &extrav, NULL);
1201
1202
        /* Horizontal dilation first: pixs --> pixt2.  Do not alter pixs. */
1203
0
    pixt1 = pixCreateTemplate(pixs);  /* temp image */
1204
0
    if (hsize == 1) {
1205
0
        pixt2 = pixClone(pixs);
1206
0
    } else if (hsize < 64) {
1207
0
        pixt2 = pixDilateCompBrickDwa(NULL, pixs, hsize, 1);
1208
0
    } else if (hsize == 64) {  /* approximate */
1209
0
        pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1);
1210
0
    } else {
1211
0
        nops = (extrah < 3) ? nh : nh + 1;
1212
0
        if (nops & 1) {  /* odd */
1213
0
            if (extrah > 2)
1214
0
                pixt2 = pixDilateCompBrickDwa(NULL, pixs, extrah, 1);
1215
0
            else
1216
0
                pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1);
1217
0
            for (i = 0; i < nops / 2; i++) {
1218
0
                pixDilateCompBrickDwa(pixt1, pixt2, 63, 1);
1219
0
                pixDilateCompBrickDwa(pixt2, pixt1, 63, 1);
1220
0
            }
1221
0
        } else {  /* nops even */
1222
0
            if (extrah > 2) {
1223
0
                pixDilateCompBrickDwa(pixt1, pixs, extrah, 1);
1224
0
                pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1);
1225
0
            } else {  /* they're all 63s */
1226
0
                pixDilateCompBrickDwa(pixt1, pixs, 63, 1);
1227
0
                pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1);
1228
0
            }
1229
0
            for (i = 0; i < nops / 2 - 1; i++) {
1230
0
                pixDilateCompBrickDwa(pixt1, pixt2, 63, 1);
1231
0
                pixDilateCompBrickDwa(pixt2, pixt1, 63, 1);
1232
0
            }
1233
0
        }
1234
0
    }
1235
1236
        /* Vertical dilation: pixt2 --> pixt3.  */
1237
0
    if (vsize == 1) {
1238
0
        pixt3 = pixClone(pixt2);
1239
0
    } else if (vsize < 64) {
1240
0
        pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, vsize);
1241
0
    } else if (vsize == 64) {  /* approximate */
1242
0
        pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63);
1243
0
    } else {
1244
0
        nops = (extrav < 3) ? nv : nv + 1;
1245
0
        if (nops & 1) {  /* odd */
1246
0
            if (extrav > 2)
1247
0
                pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, extrav);
1248
0
            else
1249
0
                pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63);
1250
0
            for (i = 0; i < nops / 2; i++) {
1251
0
                pixDilateCompBrickDwa(pixt1, pixt3, 1, 63);
1252
0
                pixDilateCompBrickDwa(pixt3, pixt1, 1, 63);
1253
0
            }
1254
0
        } else {  /* nops even */
1255
0
            if (extrav > 2) {
1256
0
                pixDilateCompBrickDwa(pixt1, pixt2, 1, extrav);
1257
0
                pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63);
1258
0
            } else {  /* they're all 63s */
1259
0
                pixDilateCompBrickDwa(pixt1, pixt2, 1, 63);
1260
0
                pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63);
1261
0
            }
1262
0
            for (i = 0; i < nops / 2 - 1; i++) {
1263
0
                pixDilateCompBrickDwa(pixt1, pixt3, 1, 63);
1264
0
                pixDilateCompBrickDwa(pixt3, pixt1, 1, 63);
1265
0
            }
1266
0
        }
1267
0
    }
1268
0
    pixDestroy(&pixt1);
1269
0
    pixDestroy(&pixt2);
1270
1271
0
    if (!pixd)
1272
0
        return pixt3;
1273
1274
0
    pixTransferAllData(pixd, &pixt3, 0, 0);
1275
0
    return pixd;
1276
0
}
1277
1278
1279
/*!
1280
 * \brief   pixErodeCompBrickExtendDwa()
1281
 *
1282
 * \param[in]    pixd     [optional]; this can be null, equal to pixs,
1283
 *                        or different from pixs
1284
 * \param[in]    pixs     1 bpp
1285
 * \param[in]    hsize    width of brick Sel
1286
 * \param[in]    vsize    height of brick Sel
1287
 * \return  pixd
1288
 *
1289
 * <pre>
1290
 * Notes:
1291
 *      (1) See pixDilateCompBrickExtendDwa() for usage.
1292
 *      (2) There is no need to call this directly:  pixErodeCompBrickDwa()
1293
 *          calls this function if either brick dimension exceeds 63.
1294
 * </pre>
1295
 */
1296
PIX *
1297
pixErodeCompBrickExtendDwa(PIX     *pixd,
1298
                           PIX     *pixs,
1299
                           l_int32  hsize,
1300
                           l_int32  vsize)
1301
0
{
1302
0
l_int32  i, nops, nh, extrah, nv, extrav;
1303
0
PIX     *pixt1, *pixt2, *pixt3;
1304
1305
0
    if (!pixs)
1306
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1307
0
    if (pixGetDepth(pixs) != 1)
1308
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1309
0
    if (hsize < 1 || vsize < 1)
1310
0
        return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1311
1312
0
    if (hsize < 64 && vsize < 64)
1313
0
        return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize);
1314
1315
0
    if (hsize > 63)
1316
0
        getExtendedCompositeParameters(hsize, &nh, &extrah, NULL);
1317
0
    if (vsize > 63)
1318
0
        getExtendedCompositeParameters(vsize, &nv, &extrav, NULL);
1319
1320
        /* Horizontal erosion first: pixs --> pixt2.  Do not alter pixs. */
1321
0
    pixt1 = pixCreateTemplate(pixs);  /* temp image */
1322
0
    if (hsize == 1) {
1323
0
        pixt2 = pixClone(pixs);
1324
0
    } else if (hsize < 64) {
1325
0
        pixt2 = pixErodeCompBrickDwa(NULL, pixs, hsize, 1);
1326
0
    } else if (hsize == 64) {  /* approximate */
1327
0
        pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1);
1328
0
    } else {
1329
0
        nops = (extrah < 3) ? nh : nh + 1;
1330
0
        if (nops & 1) {  /* odd */
1331
0
            if (extrah > 2)
1332
0
                pixt2 = pixErodeCompBrickDwa(NULL, pixs, extrah, 1);
1333
0
            else
1334
0
                pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1);
1335
0
            for (i = 0; i < nops / 2; i++) {
1336
0
                pixErodeCompBrickDwa(pixt1, pixt2, 63, 1);
1337
0
                pixErodeCompBrickDwa(pixt2, pixt1, 63, 1);
1338
0
            }
1339
0
        } else {  /* nops even */
1340
0
            if (extrah > 2) {
1341
0
                pixErodeCompBrickDwa(pixt1, pixs, extrah, 1);
1342
0
                pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1);
1343
0
            } else {  /* they're all 63s */
1344
0
                pixErodeCompBrickDwa(pixt1, pixs, 63, 1);
1345
0
                pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1);
1346
0
            }
1347
0
            for (i = 0; i < nops / 2 - 1; i++) {
1348
0
                pixErodeCompBrickDwa(pixt1, pixt2, 63, 1);
1349
0
                pixErodeCompBrickDwa(pixt2, pixt1, 63, 1);
1350
0
            }
1351
0
        }
1352
0
    }
1353
1354
        /* Vertical erosion: pixt2 --> pixt3.  */
1355
0
    if (vsize == 1) {
1356
0
        pixt3 = pixClone(pixt2);
1357
0
    } else if (vsize < 64) {
1358
0
        pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, vsize);
1359
0
    } else if (vsize == 64) {  /* approximate */
1360
0
        pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63);
1361
0
    } else {
1362
0
        nops = (extrav < 3) ? nv : nv + 1;
1363
0
        if (nops & 1) {  /* odd */
1364
0
            if (extrav > 2)
1365
0
                pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, extrav);
1366
0
            else
1367
0
                pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63);
1368
0
            for (i = 0; i < nops / 2; i++) {
1369
0
                pixErodeCompBrickDwa(pixt1, pixt3, 1, 63);
1370
0
                pixErodeCompBrickDwa(pixt3, pixt1, 1, 63);
1371
0
            }
1372
0
        } else {  /* nops even */
1373
0
            if (extrav > 2) {
1374
0
                pixErodeCompBrickDwa(pixt1, pixt2, 1, extrav);
1375
0
                pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63);
1376
0
            } else {  /* they're all 63s */
1377
0
                pixErodeCompBrickDwa(pixt1, pixt2, 1, 63);
1378
0
                pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63);
1379
0
            }
1380
0
            for (i = 0; i < nops / 2 - 1; i++) {
1381
0
                pixErodeCompBrickDwa(pixt1, pixt3, 1, 63);
1382
0
                pixErodeCompBrickDwa(pixt3, pixt1, 1, 63);
1383
0
            }
1384
0
        }
1385
0
    }
1386
0
    pixDestroy(&pixt1);
1387
0
    pixDestroy(&pixt2);
1388
1389
0
    if (!pixd)
1390
0
        return pixt3;
1391
1392
0
    pixTransferAllData(pixd, &pixt3, 0, 0);
1393
0
    return pixd;
1394
0
}
1395
1396
1397
/*!
1398
 * \brief   pixOpenCompBrickExtendDwa()
1399
 *
1400
 * \param[in]    pixd     [optional]; this can be null, equal to pixs,
1401
 *                        or different from pixs
1402
 * \param[in]    pixs     1 bpp
1403
 * \param[in]    hsize    width of brick Sel
1404
 * \param[in]    vsize    height of brick Sel
1405
 * \return  pixd
1406
 *
1407
 * <pre>
1408
 * Notes:
1409
 *      1) There are three cases:
1410
 *          a) pixd == null   (result into new pixd
1411
 *          b) pixd == pixs   (in-place; writes result back to pixs
1412
 *          c) pixd != pixs   (puts result into existing pixd
1413
 *      2) There is no need to call this directly:  pixOpenCompBrickDwa(
1414
 *          calls this function if either brick dimension exceeds 63.
1415
 * </pre>
1416
 */
1417
PIX *
1418
pixOpenCompBrickExtendDwa(PIX     *pixd,
1419
                          PIX     *pixs,
1420
                          l_int32  hsize,
1421
                          l_int32  vsize)
1422
0
{
1423
0
PIX     *pixt;
1424
1425
0
    if (!pixs)
1426
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1427
0
    if (pixGetDepth(pixs) != 1)
1428
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1429
0
    if (hsize < 1 || vsize < 1)
1430
0
        return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1431
1432
0
    pixt = pixErodeCompBrickExtendDwa(NULL, pixs, hsize, vsize);
1433
0
    pixd = pixDilateCompBrickExtendDwa(pixd, pixt, hsize, vsize);
1434
0
    pixDestroy(&pixt);
1435
0
    return pixd;
1436
0
}
1437
1438
1439
/*!
1440
 * \brief   pixCloseCompBrickExtendDwa()
1441
 *
1442
 * \param[in]    pixd     [optional]; this can be null, equal to pixs,
1443
 *                        or different from pixs
1444
 * \param[in]    pixs     1 bpp
1445
 * \param[in]    hsize    width of brick Sel
1446
 * \param[in]    vsize    height of brick Sel
1447
 * \return  pixd
1448
 *
1449
 * <pre>
1450
 * Notes:
1451
 *      1) There are three cases:
1452
 *          a) pixd == null   (result into new pixd
1453
 *          b) pixd == pixs   (in-place; writes result back to pixs
1454
 *          c) pixd != pixs   (puts result into existing pixd
1455
 *      2) There is no need to call this directly:  pixCloseCompBrickDwa(
1456
 *          calls this function if either brick dimension exceeds 63.
1457
 * </pre>
1458
 */
1459
PIX *
1460
pixCloseCompBrickExtendDwa(PIX     *pixd,
1461
                           PIX     *pixs,
1462
                           l_int32  hsize,
1463
                           l_int32  vsize)
1464
0
{
1465
0
l_int32  bordercolor, borderx, bordery;
1466
0
PIX     *pixt1, *pixt2, *pixt3;
1467
1468
0
    if (!pixs)
1469
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1470
0
    if (pixGetDepth(pixs) != 1)
1471
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1472
0
    if (hsize < 1 || vsize < 1)
1473
0
        return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1474
1475
        /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need
1476
         * an extra 32 OFF pixels around the image (in addition to
1477
         * the 32 added pixels for all dwa operations), whereas with
1478
         * SYMMETRIC_MORPH_BC this is not necessary. */
1479
0
    bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
1480
0
    if (bordercolor == 0) {  /* asymmetric b.c. */
1481
0
        borderx = 32 + (hsize / 64) * 32;
1482
0
        bordery = 32 + (vsize / 64) * 32;
1483
0
    } else {  /* symmetric b.c. */
1484
0
        borderx = bordery = 32;
1485
0
    }
1486
0
    pixt1 = pixAddBorderGeneral(pixs, borderx, borderx, bordery, bordery, 0);
1487
1488
0
    pixt2 = pixDilateCompBrickExtendDwa(NULL, pixt1, hsize, vsize);
1489
0
    pixErodeCompBrickExtendDwa(pixt1, pixt2, hsize, vsize);
1490
1491
0
    pixt3 = pixRemoveBorderGeneral(pixt1, borderx, borderx, bordery, bordery);
1492
0
    pixDestroy(&pixt1);
1493
0
    pixDestroy(&pixt2);
1494
1495
0
    if (!pixd)
1496
0
        return pixt3;
1497
1498
0
    pixTransferAllData(pixd, &pixt3, 0, 0);
1499
0
    return pixd;
1500
0
}
1501
1502
1503
/*!
1504
 * \brief   getExtendedCompositeParameters()
1505
 *
1506
 * \param[in]    size          of linear Sel
1507
 * \param[out]   pn            number of 63 wide convolutions
1508
 * \param[out]   pextra        size of extra Sel
1509
 * \param[out]   pactualsize   [optional] actual size used in operation
1510
 * \return  0 if OK, 1 on error
1511
 *
1512
 * <pre>
1513
 * Notes:
1514
 *      (1) The DWA implementation allows Sels to be used with hits
1515
 *          up to 31 pixels from the origin, either horizontally or
1516
 *          vertically.  Larger Sels can be used if decomposed into
1517
 *          a set of operations with Sels not exceeding 63 pixels
1518
 *          in either width or height (and with the origin as close
1519
 *          to the center of the Sel as possible).
1520
 *      (2) This returns the decomposition of a linear Sel of length
1521
 *          %size into a set of %n Sels of length 63 plus an extra
1522
 *          Sel of length %extra.
1523
 *      (3) For notation, let w == %size, n == %n, and e == %extra.
1524
 *          We have 1 < e < 63.
1525
 *
1526
 *          Then if w < 64, we have n = 0 and e = w.
1527
 *          The general formula for w > 63 is:
1528
 *             w = 63 + (n - 1) * 62 + (e - 1)
1529
 *
1530
 *          Where did this come from?  Each successive convolution with
1531
 *          a Sel of length L adds a total length (L - 1) to w.
1532
 *          This accounts for using 62 for each additional Sel of size 63,
1533
 *          and using (e - 1) for the additional Sel of size e.
1534
 *
1535
 *          Solving for n and e for w > 63:
1536
 *             n = 1 + Int((w - 63) / 62)
1537
 *             e = w - 63 - (n - 1) * 62 + 1
1538
 *
1539
 *          The extra part is decomposed into two factors f1 and f2,
1540
 *          and the actual size of the extra part is
1541
 *             e' = f1 * f2
1542
 *          Then the actual width is:
1543
 *             w' = 63 + (n - 1) * 62 + f1 * f2 - 1
1544
 * </pre>
1545
 */
1546
l_ok
1547
getExtendedCompositeParameters(l_int32   size,
1548
                               l_int32  *pn,
1549
                               l_int32  *pextra,
1550
                               l_int32  *pactualsize)
1551
0
{
1552
0
l_int32  n, extra, fact1, fact2;
1553
1554
0
    if (!pn || !pextra)
1555
0
        return ERROR_INT("&n and &extra not both defined", __func__, 1);
1556
1557
0
    if (size <= 63) {
1558
0
        n = 0;
1559
0
        extra = L_MIN(1, size);
1560
0
    } else {  /* size > 63 */
1561
0
        n = 1 + (l_int32)((size - 63) / 62);
1562
0
        extra = size - 63 - (n - 1) * 62 + 1;
1563
0
    }
1564
1565
0
    if (pactualsize) {
1566
0
        selectComposableSizes(extra, &fact1, &fact2);
1567
0
        *pactualsize = 63 + (n - 1) * 62 + fact1 * fact2 - 1;
1568
0
    }
1569
1570
0
    *pn = n;
1571
0
    *pextra = extra;
1572
0
    return 0;
1573
0
}