Coverage Report

Created: 2025-06-13 07:15

/src/leptonica/src/boxfunc4.c
Line
Count
Source (jump to first uncovered line)
1
/*====================================================================*
2
 -  Copyright (C) 2001 Leptonica.  All rights reserved.
3
 -
4
 -  Redistribution and use in source and binary forms, with or without
5
 -  modification, are permitted provided that the following conditions
6
 -  are met:
7
 -  1. Redistributions of source code must retain the above copyright
8
 -     notice, this list of conditions and the following disclaimer.
9
 -  2. Redistributions in binary form must reproduce the above
10
 -     copyright notice, this list of conditions and the following
11
 -     disclaimer in the documentation and/or other materials
12
 -     provided with the distribution.
13
 -
14
 -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15
 -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16
 -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17
 -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
18
 -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19
 -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
 -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21
 -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22
 -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23
 -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
 -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 *====================================================================*/
26
27
/*!
28
 * \file  boxfunc4.c
29
 * <pre>
30
 *
31
 *      Boxa and Boxaa range selection
32
 *           BOXA     *boxaSelectRange()
33
 *           BOXAA    *boxaaSelectRange()
34
 *
35
 *      Boxa size selection
36
 *           BOXA     *boxaSelectBySize()
37
 *           NUMA     *boxaMakeSizeIndicator()
38
 *           BOXA     *boxaSelectByArea()
39
 *           NUMA     *boxaMakeAreaIndicator()
40
 *           BOXA     *boxaSelectByWHRatio()
41
 *           NUMA     *boxaMakeWHRatioIndicator()
42
 *           BOXA     *boxaSelectWithIndicator()
43
 *
44
 *      Boxa permutation
45
 *           BOXA     *boxaPermutePseudorandom()
46
 *           BOXA     *boxaPermuteRandom()
47
 *           l_int32   boxaSwapBoxes()
48
 *
49
 *      Boxa and box conversions
50
 *           PTA      *boxaConvertToPta()
51
 *           BOXA     *ptaConvertToBoxa()
52
 *           PTA      *boxConvertToPta()
53
 *           BOX      *ptaConvertToBox()
54
 *
55
 *      Miscellaneous boxa functions
56
 *           l_int32   boxaGetExtent()
57
 *           l_int32   boxaGetCoverage()
58
 *           l_int32   boxaaSizeRange()
59
 *           l_int32   boxaSizeRange()
60
 *           l_int32   boxaLocationRange()
61
 *           NUMA     *boxaGetSizes()
62
 *           l_int32   boxaGetArea()
63
 *           PIX      *boxaDisplayTiled()
64
 * </pre>
65
 */
66
67
#ifdef HAVE_CONFIG_H
68
#include <config_auto.h>
69
#endif  /* HAVE_CONFIG_H */
70
71
#include <math.h>
72
#include "allheaders.h"
73
#include "pix_internal.h"
74
75
/*---------------------------------------------------------------------*
76
 *                     Boxa and boxaa range selection                  *
77
 *---------------------------------------------------------------------*/
78
/*!
79
 * \brief   boxaSelectRange()
80
 *
81
 * \param[in]    boxas
82
 * \param[in]    first      use 0 to select from the beginning
83
 * \param[in]    last       use -1 to select to the end
84
 * \param[in]    copyflag   L_COPY, L_CLONE
85
 * \return  boxad, or NULL on error
86
 *
87
 * <pre>
88
 * Notes:
89
 *      (1) The copyflag specifies what we do with each box from boxas.
90
 *          Specifically, L_CLONE inserts a clone into boxad of each
91
 *          selected box from boxas.
92
 * </pre>
93
 */
94
BOXA *
95
boxaSelectRange(BOXA    *boxas,
96
                l_int32  first,
97
                l_int32  last,
98
                l_int32  copyflag)
99
0
{
100
0
l_int32  n, nbox, i;
101
0
BOX     *box;
102
0
BOXA    *boxad;
103
104
0
    if (!boxas)
105
0
        return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
106
0
    if (copyflag != L_COPY && copyflag != L_CLONE)
107
0
        return (BOXA *)ERROR_PTR("invalid copyflag", __func__, NULL);
108
0
    if ((n = boxaGetCount(boxas)) == 0) {
109
0
        L_WARNING("boxas is empty\n", __func__);
110
0
        return boxaCopy(boxas, copyflag);
111
0
    }
112
0
    first = L_MAX(0, first);
113
0
    if (last < 0) last = n - 1;
114
0
    if (first >= n)
115
0
        return (BOXA *)ERROR_PTR("invalid first", __func__, NULL);
116
0
    if (last >= n) {
117
0
        L_WARNING("last = %d is beyond max index = %d; adjusting\n",
118
0
                  __func__, last, n - 1);
119
0
        last = n - 1;
120
0
    }
121
0
    if (first > last)
122
0
        return (BOXA *)ERROR_PTR("first > last", __func__, NULL);
123
124
0
    nbox = last - first + 1;
125
0
    boxad = boxaCreate(nbox);
126
0
    for (i = first; i <= last; i++) {
127
0
        box = boxaGetBox(boxas, i, copyflag);
128
0
        boxaAddBox(boxad, box, L_INSERT);
129
0
    }
130
0
    return boxad;
131
0
}
132
133
134
/*!
135
 * \brief   boxaaSelectRange()
136
 *
137
 * \param[in]    baas
138
 * \param[in]    first      use 0 to select from the beginning
139
 * \param[in]    last       use -1 to select to the end
140
 * \param[in]    copyflag   L_COPY, L_CLONE
141
 * \return  baad, or NULL on error
142
 *
143
 * <pre>
144
 * Notes:
145
 *      (1) The copyflag specifies what we do with each boxa from baas.
146
 *          Specifically, L_CLONE inserts a clone into baad of each
147
 *          selected boxa from baas.
148
 * </pre>
149
 */
150
BOXAA *
151
boxaaSelectRange(BOXAA   *baas,
152
                 l_int32  first,
153
                 l_int32  last,
154
                 l_int32  copyflag)
155
0
{
156
0
l_int32  n, nboxa, i;
157
0
BOXA    *boxa;
158
0
BOXAA   *baad;
159
160
0
    if (!baas)
161
0
        return (BOXAA *)ERROR_PTR("baas not defined", __func__, NULL);
162
0
    if (copyflag != L_COPY && copyflag != L_CLONE)
163
0
        return (BOXAA *)ERROR_PTR("invalid copyflag", __func__, NULL);
164
0
    if ((n = boxaaGetCount(baas)) == 0)
165
0
        return (BOXAA *)ERROR_PTR("empty baas", __func__, NULL);
166
0
    first = L_MAX(0, first);
167
0
    if (last < 0) last = n - 1;
168
0
    if (first >= n)
169
0
        return (BOXAA *)ERROR_PTR("invalid first", __func__, NULL);
170
0
    if (last >= n) {
171
0
        L_WARNING("last = %d is beyond max index = %d; adjusting\n",
172
0
                  __func__, last, n - 1);
173
0
        last = n - 1;
174
0
    }
175
0
    if (first > last)
176
0
        return (BOXAA *)ERROR_PTR("first > last", __func__, NULL);
177
178
0
    nboxa = last - first + 1;
179
0
    baad = boxaaCreate(nboxa);
180
0
    for (i = first; i <= last; i++) {
181
0
        boxa = boxaaGetBoxa(baas, i, copyflag);
182
0
        boxaaAddBoxa(baad, boxa, L_INSERT);
183
0
    }
184
0
    return baad;
185
0
}
186
187
188
/*---------------------------------------------------------------------*
189
 *                          Boxa size selection                        *
190
 *---------------------------------------------------------------------*/
191
/*!
192
 * \brief   boxaSelectBySize()
193
 *
194
 * \param[in]    boxas
195
 * \param[in]    width, height    threshold dimensions
196
 * \param[in]    type             L_SELECT_WIDTH, L_SELECT_HEIGHT,
197
 *                                L_SELECT_IF_EITHER, L_SELECT_IF_BOTH
198
 * \param[in]    relation         L_SELECT_IF_LT, L_SELECT_IF_GT,
199
 *                                L_SELECT_IF_LTE, L_SELECT_IF_GTE
200
 * \param[out]   pchanged         [optional] 1 if changed; 0 if clone returned
201
 * \return  boxad filtered set, or NULL on error
202
 *
203
 * <pre>
204
 * Notes:
205
 *      (1) The args specify constraints on the size of the
206
 *          components that are kept.
207
 *      (2) Uses box copies in the new boxa.
208
 *      (3) If the selection type is L_SELECT_WIDTH, the input
209
 *          height is ignored, and v.v.
210
 *      (4) To keep small components, use relation = L_SELECT_IF_LT or
211
 *          L_SELECT_IF_LTE.
212
 *          To keep large components, use relation = L_SELECT_IF_GT or
213
 *          L_SELECT_IF_GTE.
214
 * </pre>
215
 */
216
BOXA *
217
boxaSelectBySize(BOXA     *boxas,
218
                 l_int32   width,
219
                 l_int32   height,
220
                 l_int32   type,
221
                 l_int32   relation,
222
                 l_int32  *pchanged)
223
0
{
224
0
BOXA  *boxad;
225
0
NUMA  *na;
226
227
0
    if (pchanged) *pchanged = FALSE;
228
0
    if (!boxas)
229
0
        return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
230
0
    if (boxaGetCount(boxas) == 0) {
231
0
        L_WARNING("boxas is empty\n", __func__);
232
0
        return boxaCopy(boxas, L_COPY);
233
0
    }
234
0
    if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
235
0
        type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
236
0
        return (BOXA *)ERROR_PTR("invalid type", __func__, NULL);
237
0
    if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
238
0
        relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
239
0
        return (BOXA *)ERROR_PTR("invalid relation", __func__, NULL);
240
241
        /* Compute the indicator array for saving components */
242
0
    if ((na =
243
0
         boxaMakeSizeIndicator(boxas, width, height, type, relation)) == NULL)
244
0
        return (BOXA *)ERROR_PTR("na not made", __func__, NULL);
245
246
        /* Filter to get output */
247
0
    boxad = boxaSelectWithIndicator(boxas, na, pchanged);
248
249
0
    numaDestroy(&na);
250
0
    return boxad;
251
0
}
252
253
254
/*!
255
 * \brief   boxaMakeSizeIndicator()
256
 *
257
 * \param[in]    boxa
258
 * \param[in]    width, height   threshold dimensions
259
 * \param[in]    type            L_SELECT_WIDTH, L_SELECT_HEIGHT,
260
 *                               L_SELECT_IF_EITHER, L_SELECT_IF_BOTH
261
 * \param[in]    relation        L_SELECT_IF_LT, L_SELECT_IF_GT,
262
 *                               L_SELECT_IF_LTE, L_SELECT_IF_GTE
263
 * \return  na indicator array, or NULL on error
264
 *
265
 * <pre>
266
 * Notes:
267
 *      (1) The args specify constraints on the size of the
268
 *          components that are kept.
269
 *      (2) If the selection type is L_SELECT_WIDTH, the input
270
 *          height is ignored, and v.v.
271
 *      (3) To keep small components, use relation = L_SELECT_IF_LT or
272
 *          L_SELECT_IF_LTE.
273
 *          To keep large components, use relation = L_SELECT_IF_GT or
274
 *          L_SELECT_IF_GTE.
275
 * </pre>
276
 */
277
NUMA *
278
boxaMakeSizeIndicator(BOXA     *boxa,
279
                      l_int32   width,
280
                      l_int32   height,
281
                      l_int32   type,
282
                      l_int32   relation)
283
0
{
284
0
l_int32  i, n, w, h, ival;
285
0
NUMA    *na;
286
287
0
    if (!boxa)
288
0
        return (NUMA *)ERROR_PTR("boxa not defined", __func__, NULL);
289
0
    if ((n = boxaGetCount(boxa)) == 0)
290
0
        return (NUMA *)ERROR_PTR("boxa is empty", __func__, NULL);
291
0
    if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
292
0
        type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
293
0
        return (NUMA *)ERROR_PTR("invalid type", __func__, NULL);
294
0
    if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
295
0
        relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
296
0
        return (NUMA *)ERROR_PTR("invalid relation", __func__, NULL);
297
298
0
    na = numaCreate(n);
299
0
    for (i = 0; i < n; i++) {
300
0
        ival = 0;
301
0
        boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
302
0
        switch (type)
303
0
        {
304
0
        case L_SELECT_WIDTH:
305
0
            if ((relation == L_SELECT_IF_LT && w < width) ||
306
0
                (relation == L_SELECT_IF_GT && w > width) ||
307
0
                (relation == L_SELECT_IF_LTE && w <= width) ||
308
0
                (relation == L_SELECT_IF_GTE && w >= width))
309
0
                ival = 1;
310
0
            break;
311
0
        case L_SELECT_HEIGHT:
312
0
            if ((relation == L_SELECT_IF_LT && h < height) ||
313
0
                (relation == L_SELECT_IF_GT && h > height) ||
314
0
                (relation == L_SELECT_IF_LTE && h <= height) ||
315
0
                (relation == L_SELECT_IF_GTE && h >= height))
316
0
                ival = 1;
317
0
            break;
318
0
        case L_SELECT_IF_EITHER:
319
0
            if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) ||
320
0
                ((relation == L_SELECT_IF_GT) && (w > width || h > height)) ||
321
0
               ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) ||
322
0
                ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height)))
323
0
                    ival = 1;
324
0
            break;
325
0
        case L_SELECT_IF_BOTH:
326
0
            if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) ||
327
0
                ((relation == L_SELECT_IF_GT) && (w > width && h > height)) ||
328
0
               ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) ||
329
0
                ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height)))
330
0
                    ival = 1;
331
0
            break;
332
0
        default:
333
0
            L_WARNING("can't get here!\n", __func__);
334
0
            break;
335
0
        }
336
0
        numaAddNumber(na, ival);
337
0
    }
338
339
0
    return na;
340
0
}
341
342
343
/*!
344
 * \brief   boxaSelectByArea()
345
 *
346
 * \param[in]    boxas
347
 * \param[in]    area       threshold value of width * height
348
 * \param[in]    relation   L_SELECT_IF_LT, L_SELECT_IF_GT,
349
 *                          L_SELECT_IF_LTE, L_SELECT_IF_GTE
350
 * \param[out]   pchanged   [optional] 1 if changed; 0 if clone returned
351
 * \return  boxad filtered set, or NULL on error
352
 *
353
 * <pre>
354
 * Notes:
355
 *      (1) Uses box copies in the new boxa.
356
 *      (2) To keep small components, use relation = L_SELECT_IF_LT or
357
 *          L_SELECT_IF_LTE.
358
 *          To keep large components, use relation = L_SELECT_IF_GT or
359
 *          L_SELECT_IF_GTE.
360
 * </pre>
361
 */
362
BOXA *
363
boxaSelectByArea(BOXA     *boxas,
364
                 l_int32   area,
365
                 l_int32   relation,
366
                 l_int32  *pchanged)
367
0
{
368
0
BOXA  *boxad;
369
0
NUMA  *na;
370
371
0
    if (pchanged) *pchanged = FALSE;
372
0
    if (!boxas)
373
0
        return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
374
0
    if (boxaGetCount(boxas) == 0) {
375
0
        L_WARNING("boxas is empty\n", __func__);
376
0
        return boxaCopy(boxas, L_COPY);
377
0
    }
378
0
    if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
379
0
        relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
380
0
        return (BOXA *)ERROR_PTR("invalid relation", __func__, NULL);
381
382
        /* Compute the indicator array for saving components */
383
0
    na = boxaMakeAreaIndicator(boxas, area, relation);
384
385
        /* Filter to get output */
386
0
    boxad = boxaSelectWithIndicator(boxas, na, pchanged);
387
388
0
    numaDestroy(&na);
389
0
    return boxad;
390
0
}
391
392
393
/*!
394
 * \brief   boxaMakeAreaIndicator()
395
 *
396
 * \param[in]    boxa
397
 * \param[in]    area       threshold value of width * height
398
 * \param[in]    relation   L_SELECT_IF_LT, L_SELECT_IF_GT,
399
 *                          L_SELECT_IF_LTE, L_SELECT_IF_GTE
400
 * \return  na indicator array, or NULL on error
401
 *
402
 * <pre>
403
 * Notes:
404
 *      (1) To keep small components, use relation = L_SELECT_IF_LT or
405
 *          L_SELECT_IF_LTE.
406
 *          To keep large components, use relation = L_SELECT_IF_GT or
407
 *          L_SELECT_IF_GTE.
408
 * </pre>
409
 */
410
NUMA *
411
boxaMakeAreaIndicator(BOXA     *boxa,
412
                      l_int32   area,
413
                      l_int32   relation)
414
0
{
415
0
l_int32  i, n, w, h, ival;
416
0
NUMA    *na;
417
418
0
    if (!boxa)
419
0
        return (NUMA *)ERROR_PTR("boxa not defined", __func__, NULL);
420
0
    if ((n = boxaGetCount(boxa)) == 0)
421
0
        return (NUMA *)ERROR_PTR("boxa is empty", __func__, NULL);
422
0
    if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
423
0
        relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
424
0
        return (NUMA *)ERROR_PTR("invalid relation", __func__, NULL);
425
426
0
    na = numaCreate(n);
427
0
    for (i = 0; i < n; i++) {
428
0
        ival = 0;
429
0
        boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
430
431
0
        if ((relation == L_SELECT_IF_LT && w * h < area) ||
432
0
            (relation == L_SELECT_IF_GT && w * h > area) ||
433
0
            (relation == L_SELECT_IF_LTE && w * h <= area) ||
434
0
            (relation == L_SELECT_IF_GTE && w * h >= area))
435
0
            ival = 1;
436
0
        numaAddNumber(na, ival);
437
0
    }
438
439
0
    return na;
440
0
}
441
442
443
/*!
444
 * \brief   boxaSelectByWHRatio()
445
 *
446
 * \param[in]    boxas
447
 * \param[in]    ratio     width/height threshold value
448
 * \param[in]    relation  L_SELECT_IF_LT, L_SELECT_IF_GT,
449
 *                         L_SELECT_IF_LTE, L_SELECT_IF_GTE
450
 * \param[out]   pchanged  [optional] 1 if changed; 0 if clone returned
451
 * \return  boxad filtered set, or NULL on error
452
 *
453
 * <pre>
454
 * Notes:
455
 *      (1) Uses box copies in the new boxa.
456
 *      (2) To keep narrow components, use relation = L_SELECT_IF_LT or
457
 *          L_SELECT_IF_LTE.
458
 *          To keep wide components, use relation = L_SELECT_IF_GT or
459
 *          L_SELECT_IF_GTE.
460
 * </pre>
461
 */
462
BOXA *
463
boxaSelectByWHRatio(BOXA      *boxas,
464
                    l_float32  ratio,
465
                    l_int32    relation,
466
                    l_int32   *pchanged)
467
0
{
468
0
BOXA  *boxad;
469
0
NUMA  *na;
470
471
0
    if (pchanged) *pchanged = FALSE;
472
0
    if (!boxas)
473
0
        return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
474
0
    if (boxaGetCount(boxas) == 0) {
475
0
        L_WARNING("boxas is empty\n", __func__);
476
0
        return boxaCopy(boxas, L_COPY);
477
0
    }
478
0
    if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
479
0
        relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
480
0
        return (BOXA *)ERROR_PTR("invalid relation", __func__, NULL);
481
482
        /* Compute the indicator array for saving components */
483
0
    na = boxaMakeWHRatioIndicator(boxas, ratio, relation);
484
485
        /* Filter to get output */
486
0
    boxad = boxaSelectWithIndicator(boxas, na, pchanged);
487
488
0
    numaDestroy(&na);
489
0
    return boxad;
490
0
}
491
492
493
/*!
494
 * \brief   boxaMakeWHRatioIndicator()
495
 *
496
 * \param[in]    boxa
497
 * \param[in]    ratio     width/height threshold value
498
 * \param[in]    relation  L_SELECT_IF_LT, L_SELECT_IF_GT,
499
 *                         L_SELECT_IF_LTE, L_SELECT_IF_GTE
500
 * \return  na indicator array, or NULL on error
501
 *
502
 * <pre>
503
 * Notes:
504
 *      (1) To keep narrow components, use relation = L_SELECT_IF_LT or
505
 *          L_SELECT_IF_LTE.
506
 *          To keep wide components, use relation = L_SELECT_IF_GT or
507
 *          L_SELECT_IF_GTE.
508
 * </pre>
509
 */
510
NUMA *
511
boxaMakeWHRatioIndicator(BOXA      *boxa,
512
                         l_float32  ratio,
513
                         l_int32    relation)
514
0
{
515
0
l_int32    i, n, w, h, ival;
516
0
l_float32  whratio;
517
0
NUMA      *na;
518
519
0
    if (!boxa)
520
0
        return (NUMA *)ERROR_PTR("boxa not defined", __func__, NULL);
521
0
    if ((n = boxaGetCount(boxa)) == 0)
522
0
        return (NUMA *)ERROR_PTR("boxa is empty", __func__, NULL);
523
0
    if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
524
0
        relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
525
0
        return (NUMA *)ERROR_PTR("invalid relation", __func__, NULL);
526
527
0
    na = numaCreate(n);
528
0
    for (i = 0; i < n; i++) {
529
0
        ival = 0;
530
0
        boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
531
0
        whratio = (l_float32)w / (l_float32)h;
532
533
0
        if ((relation == L_SELECT_IF_LT && whratio < ratio) ||
534
0
            (relation == L_SELECT_IF_GT && whratio > ratio) ||
535
0
            (relation == L_SELECT_IF_LTE && whratio <= ratio) ||
536
0
            (relation == L_SELECT_IF_GTE && whratio >= ratio))
537
0
            ival = 1;
538
0
        numaAddNumber(na, ival);
539
0
    }
540
541
0
    return na;
542
0
}
543
544
545
/*!
546
 * \brief   boxaSelectWithIndicator()
547
 *
548
 * \param[in]    boxas
549
 * \param[in]    na         indicator numa
550
 * \param[out]   pchanged   [optional] 1 if changed; 0 if clone returned
551
 * \return  boxad, or NULL on error
552
 *
553
 * <pre>
554
 * Notes:
555
 *      (1) Returns a copy of the boxa if no components are removed.
556
 *      (2) Uses box copies in the new boxa.
557
 *      (3) The indicator numa has values 0 (ignore) and 1 (accept).
558
 *      (4) If all indicator values are 0, the returned boxa is empty.
559
 * </pre>
560
 */
561
BOXA *
562
boxaSelectWithIndicator(BOXA     *boxas,
563
                        NUMA     *na,
564
                        l_int32  *pchanged)
565
0
{
566
0
l_int32  i, n, ival, nsave;
567
0
BOX     *box;
568
0
BOXA    *boxad;
569
570
0
    if (pchanged) *pchanged = FALSE;
571
0
    if (!boxas)
572
0
        return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
573
0
    if (!na)
574
0
        return (BOXA *)ERROR_PTR("na not defined", __func__, NULL);
575
576
0
    nsave = 0;
577
0
    n = numaGetCount(na);
578
0
    for (i = 0; i < n; i++) {
579
0
        numaGetIValue(na, i, &ival);
580
0
        if (ival == 1) nsave++;
581
0
    }
582
583
0
    if (nsave == n) {
584
0
        if (pchanged) *pchanged = FALSE;
585
0
        return boxaCopy(boxas, L_COPY);
586
0
    }
587
0
    if (pchanged) *pchanged = TRUE;
588
0
    boxad = boxaCreate(nsave);
589
0
    for (i = 0; i < n; i++) {
590
0
        numaGetIValue(na, i, &ival);
591
0
        if (ival == 0) continue;
592
0
        box = boxaGetBox(boxas, i, L_COPY);
593
0
        boxaAddBox(boxad, box, L_INSERT);
594
0
    }
595
596
0
    return boxad;
597
0
}
598
599
600
/*---------------------------------------------------------------------*
601
 *                           Boxa Permutation                          *
602
 *---------------------------------------------------------------------*/
603
/*!
604
 * \brief   boxaPermutePseudorandom()
605
 *
606
 * \param[in]    boxas   input boxa
607
 * \return  boxad with boxes permuted, or NULL on error
608
 *
609
 * <pre>
610
 * Notes:
611
 *      (1) This does a pseudorandom in-place permutation of the boxes.
612
 *      (2) The result is guaranteed not to have any boxes in their
613
 *          original position, but it is not very random.  If you
614
 *          need randomness, use boxaPermuteRandom().
615
 * </pre>
616
 */
617
BOXA *
618
boxaPermutePseudorandom(BOXA  *boxas)
619
0
{
620
0
l_int32  n;
621
0
NUMA    *na;
622
0
BOXA    *boxad;
623
624
0
    if (!boxas)
625
0
        return (BOXA *)ERROR_PTR("boxa not defined", __func__, NULL);
626
627
0
    n = boxaGetCount(boxas);
628
0
    na = numaPseudorandomSequence(n, 0);
629
0
    boxad = boxaSortByIndex(boxas, na);
630
0
    numaDestroy(&na);
631
0
    return boxad;
632
0
}
633
634
635
/*!
636
 * \brief   boxaPermuteRandom()
637
 *
638
 * \param[in]    boxad    [optional]   can be null or equal to boxas
639
 * \param[in]    boxas    input boxa
640
 * \return  boxad with boxes permuted, or NULL on error
641
 *
642
 * <pre>
643
 * Notes:
644
 *      (1) If boxad is null, make a copy of boxas and permute the copy.
645
 *          Otherwise, boxad must be equal to boxas, and the operation
646
 *          is done in-place.
647
 *      (2) If boxas is empty, return an empty boxad.
648
 *      (3) This does a random in-place permutation of the boxes,
649
 *          by swapping each box in turn with a random box.  The
650
 *          result is almost guaranteed not to have any boxes in their
651
 *          original position.
652
 *      (4) MSVC rand() has MAX_RAND = 2^15 - 1, so it will not do
653
 *          a proper permutation is the number of boxes exceeds this.
654
 * </pre>
655
 */
656
BOXA *
657
boxaPermuteRandom(BOXA  *boxad,
658
                  BOXA  *boxas)
659
0
{
660
0
l_int32  i, n, index;
661
662
0
    if (!boxas)
663
0
        return (BOXA *)ERROR_PTR("boxa not defined", __func__, NULL);
664
0
    if (boxad && (boxad != boxas))
665
0
        return (BOXA *)ERROR_PTR("boxad defined but in-place", __func__, NULL);
666
667
0
    if (!boxad)
668
0
        boxad = boxaCopy(boxas, L_COPY);
669
0
    if ((n = boxaGetCount(boxad)) == 0)
670
0
        return boxad;
671
0
    index = (l_uint32)rand() % n;
672
0
    index = L_MAX(1, index);
673
0
    boxaSwapBoxes(boxad, 0, index);
674
0
    for (i = 1; i < n; i++) {
675
0
        index = (l_uint32)rand() % n;
676
0
        if (index == i) index--;
677
0
        boxaSwapBoxes(boxad, i, index);
678
0
    }
679
680
0
    return boxad;
681
0
}
682
683
684
/*!
685
 * \brief   boxaSwapBoxes()
686
 *
687
 * \param[in]    boxa
688
 * \param[in]    i, j      two indices of boxes, that are to be swapped
689
 * \return  0 if OK, 1 on error
690
 */
691
l_ok
692
boxaSwapBoxes(BOXA    *boxa,
693
              l_int32  i,
694
              l_int32  j)
695
0
{
696
0
l_int32  n;
697
0
BOX     *box;
698
699
0
    if (!boxa)
700
0
        return ERROR_INT("boxa not defined", __func__, 1);
701
0
    n = boxaGetCount(boxa);
702
0
    if (i < 0 || i >= n)
703
0
        return ERROR_INT("i invalid", __func__, 1);
704
0
    if (j < 0 || j >= n)
705
0
        return ERROR_INT("j invalid", __func__, 1);
706
0
    if (i == j)
707
0
        return ERROR_INT("i == j", __func__, 1);
708
709
0
    box = boxa->box[i];
710
0
    boxa->box[i] = boxa->box[j];
711
0
    boxa->box[j] = box;
712
0
    return 0;
713
0
}
714
715
716
/*---------------------------------------------------------------------*
717
 *                     Boxa and Box Conversions                        *
718
 *---------------------------------------------------------------------*/
719
/*!
720
 * \brief   boxaConvertToPta()
721
 *
722
 * \param[in]    boxa
723
 * \param[in]    ncorners     2 or 4 for the representation of each box
724
 * \return  pta with %ncorners points for each box in the boxa,
725
 *                   or NULL on error
726
 *
727
 * <pre>
728
 * Notes:
729
 *      (1) If ncorners == 2, we select the UL and LR corners.
730
 *          Otherwise we save all 4 corners in this order: UL, UR, LL, LR.
731
 *      (2) Other boxa --> pta functions are:
732
 *          * boxaExtractAsPta(): allows extraction of any dimension
733
 *            and/or side location, with each in a separate pta.
734
 *          * boxaExtractCorners(): extracts any of the four corners as a pta.
735
 * </pre>
736
 */
737
PTA *
738
boxaConvertToPta(BOXA    *boxa,
739
                 l_int32  ncorners)
740
0
{
741
0
l_int32  i, n;
742
0
BOX     *box;
743
0
PTA     *pta, *pta1;
744
745
0
    if (!boxa)
746
0
        return (PTA *)ERROR_PTR("boxa not defined", __func__, NULL);
747
0
    if (ncorners != 2 && ncorners != 4)
748
0
        return (PTA *)ERROR_PTR("ncorners not 2 or 4", __func__, NULL);
749
750
0
    n = boxaGetCount(boxa);
751
0
    if ((pta = ptaCreate(n)) == NULL)
752
0
        return (PTA *)ERROR_PTR("pta not made", __func__, NULL);
753
0
    for (i = 0; i < n; i++) {
754
0
        box = boxaGetBox(boxa, i, L_COPY);
755
0
        pta1 = boxConvertToPta(box, ncorners);
756
0
        ptaJoin(pta, pta1, 0, -1);
757
0
        boxDestroy(&box);
758
0
        ptaDestroy(&pta1);
759
0
    }
760
761
0
    return pta;
762
0
}
763
764
765
/*!
766
 * \brief   ptaConvertToBoxa()
767
 *
768
 * \param[in]    pta
769
 * \param[in]    ncorners   2 or 4 for the representation of each box
770
 * \return  boxa with one box for each 2 or 4 points in the pta,
771
 *                    or NULL on error
772
 *
773
 * <pre>
774
 * Notes:
775
 *      (1) For 2 corners, the order of the 2 points is UL, LR.
776
 *          For 4 corners, the order of points is UL, UR, LL, LR.
777
 *      (2) Each derived box is the minimum size containing all corners.
778
 * </pre>
779
 */
780
BOXA *
781
ptaConvertToBoxa(PTA     *pta,
782
                 l_int32  ncorners)
783
0
{
784
0
l_int32  i, n, nbox, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax;
785
0
BOX     *box;
786
0
BOXA    *boxa;
787
788
0
    if (!pta)
789
0
        return (BOXA *)ERROR_PTR("pta not defined", __func__, NULL);
790
0
    if (ncorners != 2 && ncorners != 4)
791
0
        return (BOXA *)ERROR_PTR("ncorners not 2 or 4", __func__, NULL);
792
0
    n = ptaGetCount(pta);
793
0
    if (n % ncorners != 0)
794
0
        return (BOXA *)ERROR_PTR("size % ncorners != 0", __func__, NULL);
795
0
    nbox = n / ncorners;
796
0
    if ((boxa = boxaCreate(nbox)) == NULL)
797
0
        return (BOXA *)ERROR_PTR("boxa not made", __func__, NULL);
798
0
    for (i = 0; i < n; i += ncorners) {
799
0
        ptaGetIPt(pta, i, &x1, &y1);
800
0
        ptaGetIPt(pta, i + 1, &x2, &y2);
801
0
        if (ncorners == 2) {
802
0
            box = boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
803
0
            boxaAddBox(boxa, box, L_INSERT);
804
0
            continue;
805
0
        }
806
0
        ptaGetIPt(pta, i + 2, &x3, &y3);
807
0
        ptaGetIPt(pta, i + 3, &x4, &y4);
808
0
        x = L_MIN(x1, x3);
809
0
        y = L_MIN(y1, y2);
810
0
        xmax = L_MAX(x2, x4);
811
0
        ymax = L_MAX(y3, y4);
812
0
        box = boxCreate(x, y, xmax - x + 1, ymax - y + 1);
813
0
        boxaAddBox(boxa, box, L_INSERT);
814
0
    }
815
816
0
    return boxa;
817
0
}
818
819
820
/*!
821
 * \brief   boxConvertToPta()
822
 *
823
 * \param[in]    box
824
 * \param[in]    ncorners   2 or 4 for the representation of the box
825
 * \return  pta with %ncorners points, or NULL on error
826
 *
827
 * <pre>
828
 * Notes:
829
 *      (1) If ncorners == 2, we select the UL and LR corners.
830
 *          Otherwise we save all 4 corners in this order: UL, UR, LL, LR.
831
 * </pre>
832
 */
833
PTA *
834
boxConvertToPta(BOX     *box,
835
                l_int32  ncorners)
836
0
{
837
0
l_int32  x, y, w, h;
838
0
PTA     *pta;
839
840
0
    if (!box)
841
0
        return (PTA *)ERROR_PTR("box not defined", __func__, NULL);
842
0
    if (ncorners != 2 && ncorners != 4)
843
0
        return (PTA *)ERROR_PTR("ncorners not 2 or 4", __func__, NULL);
844
845
0
    if ((pta = ptaCreate(ncorners)) == NULL)
846
0
        return (PTA *)ERROR_PTR("pta not made", __func__, NULL);
847
0
    boxGetGeometry(box, &x, &y, &w, &h);
848
0
    ptaAddPt(pta, x, y);
849
0
    if (ncorners == 2) {
850
0
        ptaAddPt(pta, x + w - 1, y + h - 1);
851
0
    } else {
852
0
        ptaAddPt(pta, x + w - 1, y);
853
0
        ptaAddPt(pta, x, y + h - 1);
854
0
        ptaAddPt(pta, x + w - 1, y + h - 1);
855
0
    }
856
857
0
    return pta;
858
0
}
859
860
861
/*!
862
 * \brief   ptaConvertToBox()
863
 *
864
 * \param[in]    pta
865
 * \return  box minimum containing all points in the pta, or NULL on error
866
 *
867
 * <pre>
868
 * Notes:
869
 *      (1) For 2 corners, the order of the 2 points is UL, LR.
870
 *          For 4 corners, the order of points is UL, UR, LL, LR.
871
 * </pre>
872
 */
873
BOX *
874
ptaConvertToBox(PTA  *pta)
875
0
{
876
0
l_int32  n, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax;
877
878
0
    if (!pta)
879
0
        return (BOX *)ERROR_PTR("pta not defined", __func__, NULL);
880
0
    n = ptaGetCount(pta);
881
0
    if (n != 2 && n != 4)
882
0
        return (BOX *)ERROR_PTR("n must be 2 or 4", __func__, NULL);
883
0
    ptaGetIPt(pta, 0, &x1, &y1);
884
0
    ptaGetIPt(pta, 1, &x2, &y2);
885
0
    if (n == 2)
886
0
        return boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
887
888
        /* 4 corners */
889
0
    ptaGetIPt(pta, 2, &x3, &y3);
890
0
    ptaGetIPt(pta, 3, &x4, &y4);
891
0
    x = L_MIN(x1, x3);
892
0
    y = L_MIN(y1, y2);
893
0
    xmax = L_MAX(x2, x4);
894
0
    ymax = L_MAX(y3, y4);
895
0
    return boxCreate(x, y, xmax - x + 1, ymax - y + 1);
896
0
}
897
898
899
/*---------------------------------------------------------------------*
900
 *                    Miscellaneous Boxa functions                     *
901
 *---------------------------------------------------------------------*/
902
/*!
903
 * \brief   boxaGetExtent()
904
 *
905
 * \param[in]    boxa
906
 * \param[out]   pw      [optional] width
907
 * \param[out]   ph      [optional] height
908
 * \param[out]   pbox    [optional]  minimum box containing all boxes in boxa
909
 * \return  0 if OK, 1 on error
910
 *
911
 * <pre>
912
 * Notes:
913
 *      (1) This computes the minimum rectangular bounding region
914
 *          that contains all valid boxes in a boxa.
915
 *      (2) The returned w and h are the minimum size image
916
 *          that would contain all boxes untranslated.
917
 *      (3) If there are no valid boxes, returned w and h are 0 and
918
 *          all parameters in the returned box are 0.  This
919
 *          is not an error, because an empty boxa is valid and
920
 *          boxaGetExtent() is required for serialization.
921
 * </pre>
922
 */
923
l_ok
924
boxaGetExtent(BOXA     *boxa,
925
              l_int32  *pw,
926
              l_int32  *ph,
927
              BOX     **pbox)
928
0
{
929
0
l_int32  i, n, x, y, w, h, xmax, ymax, xmin, ymin, found;
930
931
0
    if (!pw && !ph && !pbox)
932
0
        return ERROR_INT("no ptrs defined", __func__, 1);
933
0
    if (pw) *pw = 0;
934
0
    if (ph) *ph = 0;
935
0
    if (pbox) *pbox = NULL;
936
0
    if (!boxa)
937
0
        return ERROR_INT("boxa not defined", __func__, 1);
938
939
0
    n = boxaGetCount(boxa);
940
0
    xmax = ymax = 0;
941
0
    xmin = ymin = 100000000;
942
0
    found = FALSE;
943
0
    for (i = 0; i < n; i++) {
944
0
        boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
945
0
        if (w <= 0 || h <= 0)
946
0
            continue;
947
0
        found = TRUE;
948
0
        xmin = L_MIN(xmin, x);
949
0
        ymin = L_MIN(ymin, y);
950
0
        xmax = L_MAX(xmax, x + w);
951
0
        ymax = L_MAX(ymax, y + h);
952
0
    }
953
0
    if (found == FALSE)  /* no valid boxes in boxa */
954
0
        xmin = ymin = 0;
955
0
    if (pw) *pw = xmax;
956
0
    if (ph) *ph = ymax;
957
0
    if (pbox)
958
0
      *pbox = boxCreate(xmin, ymin, xmax - xmin, ymax - ymin);
959
960
0
    return 0;
961
0
}
962
963
964
/*!
965
 * \brief   boxaGetCoverage()
966
 *
967
 * \param[in]    boxa
968
 * \param[in]    wc, hc     dimensions of overall clipping rectangle with UL
969
 *                          corner at (0, 0 that is covered by the boxes.
970
 * \param[in]    exactflag  1 for guaranteeing an exact result; 0 for getting
971
 *                          an exact result only if the boxes do not overlap
972
 * \param[out]   pfract     sum of box area as fraction of w * h
973
 * \return  0 if OK, 1 on error
974
 *
975
 * <pre>
976
 * Notes:
977
 *      (1) The boxes in boxa are clipped to the input rectangle.
978
 *      (2) * When %exactflag == 1, we generate a 1 bpp pix of size
979
 *            wc x hc, paint all the boxes black, and count the fg pixels.
980
 *            This can take 1 msec on a large page with many boxes.
981
 *          * When %exactflag == 0, we clip each box to the wc x hc region
982
 *            and sum the resulting areas.  This is faster.
983
 *          * The results are the same when none of the boxes overlap
984
 *            within the wc x hc region.
985
 * </pre>
986
 */
987
l_ok
988
boxaGetCoverage(BOXA       *boxa,
989
                l_int32     wc,
990
                l_int32     hc,
991
                l_int32     exactflag,
992
                l_float32  *pfract)
993
0
{
994
0
l_int32  i, n, x, y, w, h, sum;
995
0
BOX     *box, *boxc;
996
0
PIX     *pixt;
997
998
0
    if (!pfract)
999
0
        return ERROR_INT("&fract not defined", __func__, 1);
1000
0
    *pfract = 0.0;
1001
0
    if (!boxa)
1002
0
        return ERROR_INT("boxa not defined", __func__, 1);
1003
1004
0
    n = boxaGetCount(boxa);
1005
0
    if (n == 0)
1006
0
        return ERROR_INT("no boxes in boxa", __func__, 1);
1007
1008
0
    if (exactflag == 0) {  /* quick and dirty */
1009
0
        sum = 0;
1010
0
        for (i = 0; i < n; i++) {
1011
0
            box = boxaGetBox(boxa, i, L_CLONE);
1012
0
            if ((boxc = boxClipToRectangle(box, wc, hc)) != NULL) {
1013
0
                boxGetGeometry(boxc, NULL, NULL, &w, &h);
1014
0
                sum += w * h;
1015
0
                boxDestroy(&boxc);
1016
0
            }
1017
0
            boxDestroy(&box);
1018
0
        }
1019
0
    } else {  /* slower and exact */
1020
0
        pixt = pixCreate(wc, hc, 1);
1021
0
        for (i = 0; i < n; i++) {
1022
0
            box = boxaGetBox(boxa, i, L_CLONE);
1023
0
            boxGetGeometry(box, &x, &y, &w, &h);
1024
0
            pixRasterop(pixt, x, y, w, h, PIX_SET, NULL, 0, 0);
1025
0
            boxDestroy(&box);
1026
0
        }
1027
0
        pixCountPixels(pixt, &sum, NULL);
1028
0
        pixDestroy(&pixt);
1029
0
    }
1030
1031
0
    *pfract = (l_float32)sum / (l_float32)(wc * hc);
1032
0
    return 0;
1033
0
}
1034
1035
1036
/*!
1037
 * \brief   boxaaSizeRange()
1038
 *
1039
 * \param[in]    baa
1040
 * \param[out]   pminw    [optional] min width of all boxes
1041
 * \param[out]   pmaxw    [optional] max width of all boxes
1042
 * \param[out]   pminh    [optional] min height of all boxes
1043
 * \param[out]   pmaxh    [optional] max height of all boxes
1044
 * \return  0 if OK, 1 on error
1045
 */
1046
l_ok
1047
boxaaSizeRange(BOXAA    *baa,
1048
               l_int32  *pminw,
1049
               l_int32  *pminh,
1050
               l_int32  *pmaxw,
1051
               l_int32  *pmaxh)
1052
0
{
1053
0
l_int32  minw, minh, maxw, maxh, minbw, minbh, maxbw, maxbh, i, n;
1054
0
BOXA    *boxa;
1055
1056
0
    if (!pminw && !pmaxw && !pminh && !pmaxh)
1057
0
        return ERROR_INT("no data can be returned", __func__, 1);
1058
0
    if (pminw) *pminw = 0;
1059
0
    if (pminh) *pminh = 0;
1060
0
    if (pmaxw) *pmaxw = 0;
1061
0
    if (pmaxh) *pmaxh = 0;
1062
0
    if (!baa)
1063
0
        return ERROR_INT("baa not defined", __func__, 1);
1064
1065
0
    minw = minh = 100000000;
1066
0
    maxw = maxh = 0;
1067
0
    n = boxaaGetCount(baa);
1068
0
    for (i = 0; i < n; i++) {
1069
0
        boxa = boxaaGetBoxa(baa, i, L_CLONE);
1070
0
        boxaSizeRange(boxa, &minbw, &minbh, &maxbw, &maxbh);
1071
0
        if (minbw < minw)
1072
0
            minw = minbw;
1073
0
        if (minbh < minh)
1074
0
            minh = minbh;
1075
0
        if (maxbw > maxw)
1076
0
            maxw = maxbw;
1077
0
        if (maxbh > maxh)
1078
0
            maxh = maxbh;
1079
0
        boxaDestroy(&boxa);
1080
0
    }
1081
1082
0
    if (pminw) *pminw = minw;
1083
0
    if (pminh) *pminh = minh;
1084
0
    if (pmaxw) *pmaxw = maxw;
1085
0
    if (pmaxh) *pmaxh = maxh;
1086
0
    return 0;
1087
0
}
1088
1089
1090
/*!
1091
 * \brief   boxaSizeRange()
1092
 *
1093
 * \param[in]    boxa
1094
 * \param[out]   pminw    [optional] min width of all boxes
1095
 * \param[out]   pmaxw    [optional] max width of all boxes
1096
 * \param[out]   pminh    [optional] min height of all boxes
1097
 * \param[out]   pmaxh    [optional] max height of all boxes
1098
 * \return  0 if OK, 1 on error
1099
 */
1100
l_ok
1101
boxaSizeRange(BOXA     *boxa,
1102
              l_int32  *pminw,
1103
              l_int32  *pminh,
1104
              l_int32  *pmaxw,
1105
              l_int32  *pmaxh)
1106
0
{
1107
0
l_int32  minw, minh, maxw, maxh, i, n, w, h;
1108
1109
0
    if (!pminw && !pmaxw && !pminh && !pmaxh)
1110
0
        return ERROR_INT("no data can be returned", __func__, 1);
1111
0
    if (pminw) *pminw = 0;
1112
0
    if (pminh) *pminh = 0;
1113
0
    if (pmaxw) *pmaxw = 0;
1114
0
    if (pmaxh) *pmaxh = 0;
1115
0
    if (!boxa)
1116
0
        return ERROR_INT("boxa not defined", __func__, 1);
1117
1118
0
    minw = minh = 100000000;
1119
0
    maxw = maxh = 0;
1120
0
    n = boxaGetCount(boxa);
1121
0
    for (i = 0; i < n; i++) {
1122
0
        boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
1123
0
        if (w < minw)
1124
0
            minw = w;
1125
0
        if (h < minh)
1126
0
            minh = h;
1127
0
        if (w > maxw)
1128
0
            maxw = w;
1129
0
        if (h > maxh)
1130
0
            maxh = h;
1131
0
    }
1132
1133
0
    if (pminw) *pminw = minw;
1134
0
    if (pminh) *pminh = minh;
1135
0
    if (pmaxw) *pmaxw = maxw;
1136
0
    if (pmaxh) *pmaxh = maxh;
1137
0
    return 0;
1138
0
}
1139
1140
1141
/*!
1142
 * \brief   boxaLocationRange()
1143
 *
1144
 * \param[in]    boxa
1145
 * \param[out]   pminx    [optional] min (UL corner) x value of all boxes
1146
 * \param[out]   pminy    [optional] min (UL corner) y value of all boxes
1147
 * \param[out]   pmaxx    [optional] max (UL corner) x value of all boxes
1148
 * \param[out]   pmaxy    [optional] max (UL corner) y value of all boxes
1149
 * \return  0 if OK, 1 on error
1150
 */
1151
l_ok
1152
boxaLocationRange(BOXA     *boxa,
1153
                  l_int32  *pminx,
1154
                  l_int32  *pminy,
1155
                  l_int32  *pmaxx,
1156
                  l_int32  *pmaxy)
1157
0
{
1158
0
l_int32  minx, miny, maxx, maxy, i, n, x, y;
1159
1160
0
    if (!pminx && !pminy && !pmaxx && !pmaxy)
1161
0
        return ERROR_INT("no data can be returned", __func__, 1);
1162
0
    if (pminx) *pminx = 0;
1163
0
    if (pminy) *pminy = 0;
1164
0
    if (pmaxx) *pmaxx = 0;
1165
0
    if (pmaxy) *pmaxy = 0;
1166
0
    if (!boxa)
1167
0
        return ERROR_INT("boxa not defined", __func__, 1);
1168
1169
0
    minx = miny = 100000000;
1170
0
    maxx = maxy = 0;
1171
0
    n = boxaGetCount(boxa);
1172
0
    for (i = 0; i < n; i++) {
1173
0
        boxaGetBoxGeometry(boxa, i, &x, &y, NULL, NULL);
1174
0
        if (x < minx)
1175
0
            minx = x;
1176
0
        if (y < miny)
1177
0
            miny = y;
1178
0
        if (x > maxx)
1179
0
            maxx = x;
1180
0
        if (y > maxy)
1181
0
            maxy = y;
1182
0
    }
1183
1184
0
    if (pminx) *pminx = minx;
1185
0
    if (pminy) *pminy = miny;
1186
0
    if (pmaxx) *pmaxx = maxx;
1187
0
    if (pmaxy) *pmaxy = maxy;
1188
1189
0
    return 0;
1190
0
}
1191
1192
1193
/*!
1194
 * \brief   boxaGetSizes()
1195
 *
1196
 * \param[in]    boxa
1197
 * \param[out]   pnaw    [optional] widths of valid boxes
1198
 * \param[out]   pnah    [optional] heights of valid boxes
1199
 * \return  0 if OK, 1 on error
1200
 */
1201
l_ok
1202
boxaGetSizes(BOXA   *boxa,
1203
             NUMA  **pnaw,
1204
             NUMA  **pnah)
1205
0
{
1206
0
l_int32  i, n, w, h;
1207
0
BOX     *box;
1208
1209
0
    if (pnaw) *pnaw = NULL;
1210
0
    if (pnah) *pnah = NULL;
1211
0
    if (!pnaw && !pnah)
1212
0
        return ERROR_INT("no output requested", __func__, 1);
1213
0
    if (!boxa)
1214
0
        return ERROR_INT("boxa not defined", __func__, 1);
1215
1216
0
    n = boxaGetValidCount(boxa);
1217
0
    if (pnaw) *pnaw = numaCreate(n);
1218
0
    if (pnah) *pnah = numaCreate(n);
1219
0
    for (i = 0; i < n; i++) {
1220
0
        box = boxaGetValidBox(boxa, i, L_COPY);
1221
0
        if (box) {
1222
0
            boxGetGeometry(box, NULL, NULL, &w, &h);
1223
0
            if (pnaw) numaAddNumber(*pnaw, w);
1224
0
            if (pnah) numaAddNumber(*pnah, h);
1225
0
            boxDestroy(&box);
1226
0
        }
1227
0
    }
1228
1229
0
    return 0;
1230
0
}
1231
1232
1233
/*!
1234
 * \brief   boxaGetArea()
1235
 *
1236
 * \param[in]    boxa
1237
 * \param[out]   parea    total area of all boxes
1238
 * \return  0 if OK, 1 on error
1239
 *
1240
 * <pre>
1241
 * Notes:
1242
 *      (1) Measures the total area of the boxes, without regard to overlaps.
1243
 * </pre>
1244
 */
1245
l_ok
1246
boxaGetArea(BOXA     *boxa,
1247
            l_int32  *parea)
1248
0
{
1249
0
l_int32  i, n, w, h;
1250
1251
0
    if (!parea)
1252
0
        return ERROR_INT("&area not defined", __func__, 1);
1253
0
    *parea = 0;
1254
0
    if (!boxa)
1255
0
        return ERROR_INT("boxa not defined", __func__, 1);
1256
1257
0
    n = boxaGetCount(boxa);
1258
0
    for (i = 0; i < n; i++) {
1259
0
        boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
1260
0
        *parea += w * h;
1261
0
    }
1262
0
    return 0;
1263
0
}
1264
1265
1266
/*!
1267
 * \brief   boxaDisplayTiled()
1268
 *
1269
 * \param[in]    boxas
1270
 * \param[in]    pixa          [optional] background for each box
1271
 * \param[in]    first         index of first box
1272
 * \param[in]    last          index of last box; use -1 to go to end
1273
 * \param[in]    maxwidth      of output image
1274
 * \param[in]    linewidth     width of box outlines, before scaling
1275
 * \param[in]    scalefactor   applied to every box; use 1.0 for no scaling
1276
 * \param[in]    background    0 for white, 1 for black; this is the color
1277
 *                             of the spacing between the images
1278
 * \param[in]    spacing       between images, and on outside
1279
 * \param[in]    border        width of black border added to each image;
1280
 *                             use 0 for no border
1281
 * \return  pixd of tiled images of boxes, or NULL on error
1282
 *
1283
 * <pre>
1284
 * Notes:
1285
 *      (1) Displays each box separately in a tiled 32 bpp image.
1286
 *      (2) If pixa is defined, it must have the same count as the boxa,
1287
 *          and it will be a background over with each box is rendered.
1288
 *          If pixa is not defined, the boxes will be rendered over
1289
 *          blank images of identical size.
1290
 *      (3) See pixaDisplayTiledInRows() for other parameters.
1291
 * </pre>
1292
 */
1293
PIX *
1294
boxaDisplayTiled(BOXA        *boxas,
1295
                 PIXA        *pixa,
1296
                 l_int32      first,
1297
                 l_int32      last,
1298
                 l_int32      maxwidth,
1299
                 l_int32      linewidth,
1300
                 l_float32    scalefactor,
1301
                 l_int32      background,
1302
                 l_int32      spacing,
1303
                 l_int32      border)
1304
0
{
1305
0
char     buf[32];
1306
0
l_int32  i, n, npix, w, h, fontsize;
1307
0
L_BMF   *bmf;
1308
0
BOX     *box;
1309
0
BOXA    *boxa;
1310
0
PIX     *pix1, *pix2, *pixd;
1311
0
PIXA    *pixat;
1312
1313
0
    if (!boxas)
1314
0
        return (PIX *)ERROR_PTR("boxas not defined", __func__, NULL);
1315
1316
0
    boxa = boxaSaveValid(boxas, L_COPY);
1317
0
    n = boxaGetCount(boxa);
1318
0
    if (pixa) {
1319
0
        npix = pixaGetCount(pixa);
1320
0
        if (n != npix) {
1321
0
            boxaDestroy(&boxa);
1322
0
            return (PIX *)ERROR_PTR("boxa and pixa counts differ",
1323
0
                                    __func__, NULL);
1324
0
        }
1325
0
    }
1326
0
    first = L_MAX(0, first);
1327
0
    if (last < 0) last = n - 1;
1328
0
    if (first >= n) {
1329
0
        boxaDestroy(&boxa);
1330
0
        return (PIX *)ERROR_PTR("invalid first", __func__, NULL);
1331
0
    }
1332
0
    if (last >= n) {
1333
0
        L_WARNING("last = %d is beyond max index = %d; adjusting\n",
1334
0
                  __func__, last, n - 1);
1335
0
        last = n - 1;
1336
0
    }
1337
0
    if (first > last) {
1338
0
        boxaDestroy(&boxa);
1339
0
        return (PIX *)ERROR_PTR("first > last", __func__, NULL);
1340
0
    }
1341
1342
        /* Because the bitmap font will be reduced when tiled, choose the
1343
         * font size inversely with the scale factor. */
1344
0
    if (scalefactor > 0.8)
1345
0
        fontsize = 6;
1346
0
    else if (scalefactor > 0.6)
1347
0
        fontsize = 10;
1348
0
    else if (scalefactor > 0.4)
1349
0
        fontsize = 14;
1350
0
    else if (scalefactor > 0.3)
1351
0
        fontsize = 18;
1352
0
    else fontsize = 20;
1353
0
    bmf = bmfCreate(NULL, fontsize);
1354
1355
0
    pixat = pixaCreate(n);
1356
0
    boxaGetExtent(boxa, &w, &h, NULL);
1357
0
    for (i = first; i <= last; i++) {
1358
0
        box = boxaGetBox(boxa, i, L_CLONE);
1359
0
        if (!pixa) {
1360
0
            pix1 = pixCreate(w, h, 32);
1361
0
            pixSetAll(pix1);
1362
0
        } else {
1363
0
            pix1 = pixaGetPix(pixa, i, L_COPY);
1364
0
        }
1365
0
        pixSetBorderVal(pix1, 0, 0, 0, 2, 0x0000ff00);
1366
0
        snprintf(buf, sizeof(buf), "%d", i);
1367
0
        pix2 = pixAddSingleTextblock(pix1, bmf, buf, 0x00ff0000,
1368
0
                                     L_ADD_BELOW, NULL);
1369
0
        pixDestroy(&pix1);
1370
0
        pixRenderBoxArb(pix2, box, linewidth, 255, 0, 0);
1371
0
        pixaAddPix(pixat, pix2, L_INSERT);
1372
0
        boxDestroy(&box);
1373
0
    }
1374
0
    bmfDestroy(&bmf);
1375
0
    boxaDestroy(&boxa);
1376
1377
0
    pixd = pixaDisplayTiledInRows(pixat, 32, maxwidth, scalefactor, background,
1378
0
                                  spacing, border);
1379
0
    pixaDestroy(&pixat);
1380
0
    return pixd;
1381
0
}