Coverage Report

Created: 2025-06-13 07:02

/src/leptonica/src/pixafunc1.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  pixafunc1.c
29
 * <pre>
30
 *
31
 *      Filters
32
 *           PIX      *pixSelectBySize()
33
 *           PIXA     *pixaSelectBySize()
34
 *           NUMA     *pixaMakeSizeIndicator()
35
 *
36
 *           PIX      *pixSelectByPerimToAreaRatio()
37
 *           PIXA     *pixaSelectByPerimToAreaRatio()
38
 *           PIX      *pixSelectByPerimSizeRatio()
39
 *           PIXA     *pixaSelectByPerimSizeRatio()
40
 *           PIX      *pixSelectByAreaFraction()
41
 *           PIXA     *pixaSelectByAreaFraction()
42
 *           PIX      *pixSelectByArea()
43
 *           PIXA     *pixaSelectByArea()
44
 *           PIX      *pixSelectByWidthHeightRatio()
45
 *           PIXA     *pixaSelectByWidthHeightRatio()
46
 *           PIXA     *pixaSelectByNumConnComp()
47
 *
48
 *           PIXA     *pixaSelectWithIndicator()
49
 *           l_int32   pixRemoveWithIndicator()
50
 *           l_int32   pixAddWithIndicator()
51
 *           PIXA     *pixaSelectWithString()
52
 *           PIX      *pixaRenderComponent()
53
 *
54
 *      Sort functions
55
 *           PIXA     *pixaSort()
56
 *           PIXA     *pixaBinSort()
57
 *           PIXA     *pixaSortByIndex()
58
 *           PIXAA    *pixaSort2dByIndex()
59
 *
60
 *      Pixa and Pixaa range selection
61
 *           PIXA     *pixaSelectRange()
62
 *           PIXAA    *pixaaSelectRange()
63
 *
64
 *      Pixa and Pixaa scaling
65
 *           PIXAA    *pixaaScaleToSize()
66
 *           PIXAA    *pixaaScaleToSizeVar()
67
 *           PIXA     *pixaScaleToSize()
68
 *           PIXA     *pixaScaleToSizeRel()
69
 *           PIXA     *pixaScale()
70
 *           PIXA     *pixaScaleBySampling()
71
 *
72
 *      Pixa rotation and translation
73
 *           PIXA     *pixaRotate()
74
 *           PIXA     *pixaRotateOrth()
75
 *           PIXA     *pixaTranslate()
76
 *
77
 *      Miscellaneous
78
 *           PIXA     *pixaAddBorderGeneral()
79
 *           PIXA     *pixaaFlattenToPixa()
80
 *           l_int32   pixaaSizeRange()
81
 *           l_int32   pixaSizeRange()
82
 *           PIXA     *pixaClipToPix()
83
 *           PIXA     *pixaClipToForeground()
84
 *           l_int32   pixaGetRenderingDepth()
85
 *           l_int32   pixaHasColor()
86
 *           l_int32   pixaAnyColormaps()
87
 *           l_int32   pixaGetDepthInfo()
88
 *           PIXA     *pixaConvertToSameDepth()
89
 *           PIXA     *pixaConvertToGivenDepth()
90
 *           l_int32   pixaEqual()
91
 *           l_int32   pixaSetFullSizeBoxa()
92
 * </pre>
93
 */
94
95
#ifdef HAVE_CONFIG_H
96
#include <config_auto.h>
97
#endif  /* HAVE_CONFIG_H */
98
99
#include <string.h>
100
#include "allheaders.h"
101
#include "pix_internal.h"
102
103
    /* For more than this number of c.c. in a binarized image of
104
     * semi-perimeter (w + h) about 5000 or less, the O(n) binsort
105
     * is faster than the O(nlogn) shellsort.  */
106
static const l_int32   MinCompsForBinSort = 200;
107
108
    /* Don't rotate any angle smaller than this */
109
static const l_float32  MinAngleToRotate = 0.001f;  /* radians; ~0.06 deg */
110
111
/*---------------------------------------------------------------------*
112
 *                                Filters                              *
113
 *---------------------------------------------------------------------*/
114
/*
115
 * These filters work on the connected components of 1 bpp images.
116
 * They are typically used on pixa that have been generated from a Pix
117
 * using pixConnComp(), so that the corresponding Boxa is available.
118
 *
119
 * The filters remove or retain c.c. based on these properties:
120
 *    (a) size  [pixaFindDimensions()]
121
 *    (b) area-to-perimeter ratio   [pixaFindAreaPerimRatio()]
122
 *    (c) foreground area as a fraction of bounding box area (w * h)
123
 *        [pixaFindForegroundArea()]
124
 *    (d) number of foreground pixels   [pixaCountPixels()]
125
 *    (e) width/height aspect ratio  [pixFindWidthHeightRatio()]
126
 *
127
 * We provide two different high-level interfaces:
128
 *    (1) Functions that use one of the filters on either
129
 *        a pix or the pixa of components.
130
 *    (2) A general method that generates numas of indicator functions,
131
 *        logically combines them, and efficiently removes or adds
132
 *        the selected components.
133
 *
134
 * For interface (1), the filtering is performed with a single function call.
135
 * This is the easiest way to do simple filtering.  These functions
136
 * are named pixSelectBy*() and pixaSelectBy*(), where the '*' is one of:
137
 *        Size
138
 *        PerimToAreaRatio
139
 *        PerimSizeRatio
140
 *        Area
141
 *        AreaFraction
142
 *        WidthHeightRatio
143
 *
144
 * For more complicated filtering, use the general method (2).
145
 * The numa indicator functions for a pixa are generated by these functions:
146
 *        pixaFindDimensions()
147
 *        pixaFindPerimToAreaRatio()
148
 *        pixaFindPerimSizeRatio()
149
 *        pixaFindAreaFraction()
150
 *        pixaCountPixels()
151
 *        pixaFindWidthHeightRatio()
152
 *        pixaFindWidthHeightProduct()
153
 *
154
 * Here is an illustration using the general method.  Suppose you want
155
 * all 8-connected components that have a height greater than 40 pixels,
156
 * a width not more than 30 pixels, between 150 and 300 fg pixels,
157
 * and a perimeter-to-size ratio between 1.2 and 2.0.
158
 *
159
 *        // Generate the pixa of 8 cc pieces.
160
 *    boxa = pixConnComp(pixs, &pixa, 8);
161
 *
162
 *        // Extract the data we need about each component.
163
 *    pixaFindDimensions(pixa, &naw, &nah);
164
 *    nas = pixaCountPixels(pixa);
165
 *    nar = pixaFindPerimSizeRatio(pixa);
166
 *
167
 *        // Build the indicator arrays for the set of components,
168
 *        // based on thresholds and selection criteria.
169
 *    na1 = numaMakeThresholdIndicator(nah, 40, L_SELECT_IF_GT);
170
 *    na2 = numaMakeThresholdIndicator(naw, 30, L_SELECT_IF_LTE);
171
 *    na3 = numaMakeThresholdIndicator(nas, 150, L_SELECT_IF_GTE);
172
 *    na4 = numaMakeThresholdIndicator(nas, 300, L_SELECT_IF_LTE);
173
 *    na5 = numaMakeThresholdIndicator(nar, 1.2, L_SELECT_IF_GTE);
174
 *    na6 = numaMakeThresholdIndicator(nar, 2.0, L_SELECT_IF_LTE);
175
 *
176
 *        // Combine the indicator arrays logically to find
177
 *        // the components that will be retained.
178
 *    nad = numaLogicalOp(NULL, na1, na2, L_INTERSECTION);
179
 *    numaLogicalOp(nad, nad, na3, L_INTERSECTION);
180
 *    numaLogicalOp(nad, nad, na4, L_INTERSECTION);
181
 *    numaLogicalOp(nad, nad, na5, L_INTERSECTION);
182
 *    numaLogicalOp(nad, nad, na6, L_INTERSECTION);
183
 *
184
 *        // Invert to get the components that will be removed.
185
 *    numaInvert(nad, nad);
186
 *
187
 *        // Remove the components, in-place.
188
 *    pixRemoveWithIndicator(pixs, pixa, nad);
189
 */
190
191
192
/*!
193
 * \brief   pixSelectBySize()
194
 *
195
 * \param[in]    pixs           1 bpp
196
 * \param[in]    width, height  threshold dimensions
197
 * \param[in]    connectivity   4 or 8
198
 * \param[in]    type           L_SELECT_WIDTH, L_SELECT_HEIGHT,
199
 *                              L_SELECT_IF_EITHER, L_SELECT_IF_BOTH
200
 * \param[in]    relation       L_SELECT_IF_LT, L_SELECT_IF_GT,
201
 *                              L_SELECT_IF_LTE, L_SELECT_IF_GTE
202
 * \param[out]   pchanged       [optional] 1 if changed; 0 otherwise
203
 * \return  filtered pixd, or NULL on error
204
 *
205
 * <pre>
206
 * Notes:
207
 *      (1) The args specify constraints on the size of the
208
 *          components that are kept.
209
 *      (2) If unchanged, returns a copy of pixs.  Otherwise,
210
 *          returns a new pix with the filtered components.
211
 *      (3) If the selection type is L_SELECT_WIDTH, the input
212
 *          height is ignored, and v.v.
213
 *      (4) To keep small components, use relation = L_SELECT_IF_LT or
214
 *          L_SELECT_IF_LTE.
215
 *          To keep large components, use relation = L_SELECT_IF_GT or
216
 *          L_SELECT_IF_GTE.
217
 * </pre>
218
 */
219
PIX *
220
pixSelectBySize(PIX      *pixs,
221
                l_int32   width,
222
                l_int32   height,
223
                l_int32   connectivity,
224
                l_int32   type,
225
                l_int32   relation,
226
                l_int32  *pchanged)
227
0
{
228
0
l_int32  w, h, empty, changed, count;
229
0
BOXA    *boxa;
230
0
PIX     *pixd;
231
0
PIXA    *pixas, *pixad;
232
233
0
    if (!pixs)
234
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
235
0
    if (connectivity != 4 && connectivity != 8)
236
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
237
0
    if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
238
0
        type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
239
0
        return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
240
0
    if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
241
0
        relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
242
0
        return (PIX *)ERROR_PTR("invalid relation", __func__, NULL);
243
0
    if (pchanged) *pchanged = FALSE;
244
245
        /* Check if any components exist */
246
0
    pixZero(pixs, &empty);
247
0
    if (empty)
248
0
        return pixCopy(NULL, pixs);
249
250
        /* Identify and select the components */
251
0
    boxa = pixConnComp(pixs, &pixas, connectivity);
252
0
    pixad = pixaSelectBySize(pixas, width, height, type, relation, &changed);
253
0
    boxaDestroy(&boxa);
254
0
    pixaDestroy(&pixas);
255
256
0
    if (!changed) {
257
0
        pixaDestroy(&pixad);
258
0
        return pixCopy(NULL, pixs);
259
0
    }
260
261
        /* Render the result */
262
0
    if (pchanged) *pchanged = TRUE;
263
0
    pixGetDimensions(pixs, &w, &h, NULL);
264
0
    count = pixaGetCount(pixad);
265
0
    if (count == 0) {  /* return empty pix */
266
0
        pixd = pixCreateTemplate(pixs);
267
0
    } else {
268
0
        pixd = pixaDisplay(pixad, w, h);
269
0
        pixCopyResolution(pixd, pixs);
270
0
        pixCopyColormap(pixd, pixs);
271
0
        pixCopyText(pixd, pixs);
272
0
        pixCopyInputFormat(pixd, pixs);
273
0
    }
274
0
    pixaDestroy(&pixad);
275
0
    return pixd;
276
0
}
277
278
279
/*!
280
 * \brief   pixaSelectBySize()
281
 *
282
 * \param[in]    pixas
283
 * \param[in]    width, height  threshold dimensions
284
 * \param[in]    type           L_SELECT_WIDTH, L_SELECT_HEIGHT,
285
 *                              L_SELECT_IF_EITHER, L_SELECT_IF_BOTH
286
 * \param[in]    relation       L_SELECT_IF_LT, L_SELECT_IF_GT,
287
 *                              L_SELECT_IF_LTE, L_SELECT_IF_GTE
288
 * \param[out]   pchanged       [optional] 1 if changed; 0 otherwise
289
 * \return  pixad, or NULL on error
290
 *
291
 * <pre>
292
 * Notes:
293
 *      (1) The args specify constraints on the size of the
294
 *          components that are kept.
295
 *      (2) Uses pix and box clones in the new pixa.
296
 *      (3) If the selection type is L_SELECT_WIDTH, the input
297
 *          height is ignored, and v.v.
298
 *      (4) To keep small components, use relation = L_SELECT_IF_LT or
299
 *          L_SELECT_IF_LTE.
300
 *          To keep large components, use relation = L_SELECT_IF_GT or
301
 *          L_SELECT_IF_GTE.
302
 * </pre>
303
 */
304
PIXA *
305
pixaSelectBySize(PIXA     *pixas,
306
                 l_int32   width,
307
                 l_int32   height,
308
                 l_int32   type,
309
                 l_int32   relation,
310
                 l_int32  *pchanged)
311
0
{
312
0
NUMA  *na;
313
0
PIXA  *pixad;
314
315
0
    if (!pixas)
316
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
317
0
    if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
318
0
        type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
319
0
        return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
320
0
    if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
321
0
        relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
322
0
        return (PIXA *)ERROR_PTR("invalid relation", __func__, NULL);
323
324
        /* Compute the indicator array for saving components */
325
0
    na = pixaMakeSizeIndicator(pixas, width, height, type, relation);
326
327
        /* Filter to get output */
328
0
    pixad = pixaSelectWithIndicator(pixas, na, pchanged);
329
330
0
    numaDestroy(&na);
331
0
    return pixad;
332
0
}
333
334
335
/*!
336
 * \brief   pixaMakeSizeIndicator()
337
 *
338
 * \param[in]    pixa
339
 * \param[in]    width, height  threshold dimensions
340
 * \param[in]    type           L_SELECT_WIDTH, L_SELECT_HEIGHT,
341
 *                              L_SELECT_IF_EITHER, L_SELECT_IF_BOTH
342
 * \param[in]    relation       L_SELECT_IF_LT, L_SELECT_IF_GT,
343
 *                              L_SELECT_IF_LTE, L_SELECT_IF_GTE
344
 * \return  na indicator array, or NULL on error
345
 *
346
 * <pre>
347
 * Notes:
348
 *      (1) The args specify constraints on the size of the
349
 *          components that are kept.
350
 *      (2) If the selection type is L_SELECT_WIDTH, the input
351
 *          height is ignored, and v.v.
352
 *      (3) To keep small components, use relation = L_SELECT_IF_LT or
353
 *          L_SELECT_IF_LTE.
354
 *          To keep large components, use relation = L_SELECT_IF_GT or
355
 *          L_SELECT_IF_GTE.
356
 * </pre>
357
 */
358
NUMA *
359
pixaMakeSizeIndicator(PIXA     *pixa,
360
                      l_int32   width,
361
                      l_int32   height,
362
                      l_int32   type,
363
                      l_int32   relation)
364
0
{
365
0
l_int32  i, n, w, h, ival;
366
0
NUMA    *na;
367
368
0
    if (!pixa)
369
0
        return (NUMA *)ERROR_PTR("pixa not defined", __func__, NULL);
370
0
    if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
371
0
        type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
372
0
        return (NUMA *)ERROR_PTR("invalid type", __func__, NULL);
373
0
    if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
374
0
        relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
375
0
        return (NUMA *)ERROR_PTR("invalid relation", __func__, NULL);
376
377
0
    n = pixaGetCount(pixa);
378
0
    na = numaCreate(n);
379
0
    for (i = 0; i < n; i++) {
380
0
        ival = 0;
381
0
        pixaGetPixDimensions(pixa, i, &w, &h, NULL);
382
0
        switch (type)
383
0
        {
384
0
        case L_SELECT_WIDTH:
385
0
            if ((relation == L_SELECT_IF_LT && w < width) ||
386
0
                (relation == L_SELECT_IF_GT && w > width) ||
387
0
                (relation == L_SELECT_IF_LTE && w <= width) ||
388
0
                (relation == L_SELECT_IF_GTE && w >= width))
389
0
                ival = 1;
390
0
            break;
391
0
        case L_SELECT_HEIGHT:
392
0
            if ((relation == L_SELECT_IF_LT && h < height) ||
393
0
                (relation == L_SELECT_IF_GT && h > height) ||
394
0
                (relation == L_SELECT_IF_LTE && h <= height) ||
395
0
                (relation == L_SELECT_IF_GTE && h >= height))
396
0
                ival = 1;
397
0
            break;
398
0
        case L_SELECT_IF_EITHER:
399
0
            if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) ||
400
0
                ((relation == L_SELECT_IF_GT) && (w > width || h > height)) ||
401
0
               ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) ||
402
0
                ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height)))
403
0
                    ival = 1;
404
0
            break;
405
0
        case L_SELECT_IF_BOTH:
406
0
            if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) ||
407
0
                ((relation == L_SELECT_IF_GT) && (w > width && h > height)) ||
408
0
               ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) ||
409
0
                ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height)))
410
0
                    ival = 1;
411
0
            break;
412
0
        default:
413
0
            L_WARNING("can't get here!\n", __func__);
414
0
            break;
415
0
        }
416
0
        numaAddNumber(na, ival);
417
0
    }
418
419
0
    return na;
420
0
}
421
422
423
/*!
424
 * \brief   pixSelectByPerimToAreaRatio()
425
 *
426
 * \param[in]    pixs          1 bpp
427
 * \param[in]    thresh        threshold ratio of fg boundary to fg pixels
428
 * \param[in]    connectivity  4 or 8
429
 * \param[in]    type          L_SELECT_IF_LT, L_SELECT_IF_GT,
430
 *                             L_SELECT_IF_LTE, L_SELECT_IF_GTE
431
 * \param[out]   pchanged      [optional] 1 if changed; 0 if clone returned
432
 * \return  pixd, or NULL on error
433
 *
434
 * <pre>
435
 * Notes:
436
 *      (1) The args specify constraints on the size of the
437
 *          components that are kept.
438
 *      (2) If unchanged, returns a copy of pixs.  Otherwise,
439
 *          returns a new pix with the filtered components.
440
 *      (3) This filters "thick" components, where a thick component
441
 *          is defined to have a ratio of boundary to interior pixels
442
 *          that is smaller than a given threshold value.
443
 *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the thicker
444
 *          components, and L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
445
 * </pre>
446
 */
447
PIX *
448
pixSelectByPerimToAreaRatio(PIX       *pixs,
449
                            l_float32  thresh,
450
                            l_int32    connectivity,
451
                            l_int32    type,
452
                            l_int32   *pchanged)
453
0
{
454
0
l_int32  w, h, empty, changed, count;
455
0
BOXA    *boxa;
456
0
PIX     *pixd;
457
0
PIXA    *pixas, *pixad;
458
459
0
    if (!pixs)
460
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
461
0
    if (connectivity != 4 && connectivity != 8)
462
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
463
0
    if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
464
0
        type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
465
0
        return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
466
0
    if (pchanged) *pchanged = FALSE;
467
468
        /* Check if any components exist */
469
0
    pixZero(pixs, &empty);
470
0
    if (empty)
471
0
        return pixCopy(NULL, pixs);
472
473
        /* Filter thin components */
474
0
    boxa = pixConnComp(pixs, &pixas, connectivity);
475
0
    pixad = pixaSelectByPerimToAreaRatio(pixas, thresh, type, &changed);
476
0
    boxaDestroy(&boxa);
477
0
    pixaDestroy(&pixas);
478
479
0
    if (!changed) {
480
0
        pixaDestroy(&pixad);
481
0
        return pixCopy(NULL, pixs);
482
0
    }
483
484
        /* Render the result */
485
0
    if (pchanged) *pchanged = TRUE;
486
0
    pixGetDimensions(pixs, &w, &h, NULL);
487
0
    count = pixaGetCount(pixad);
488
0
    if (count == 0) {  /* return empty pix */
489
0
        pixd = pixCreateTemplate(pixs);
490
0
    } else {
491
0
        pixd = pixaDisplay(pixad, w, h);
492
0
        pixCopyResolution(pixd, pixs);
493
0
        pixCopyColormap(pixd, pixs);
494
0
        pixCopyText(pixd, pixs);
495
0
        pixCopyInputFormat(pixd, pixs);
496
0
    }
497
0
    pixaDestroy(&pixad);
498
0
    return pixd;
499
0
}
500
501
502
/*!
503
 * \brief   pixaSelectByPerimToAreaRatio()
504
 *
505
 * \param[in]    pixas
506
 * \param[in]    thresh     threshold ratio of fg boundary to fg pixels
507
 * \param[in]    type       L_SELECT_IF_LT, L_SELECT_IF_GT,
508
 *                          L_SELECT_IF_LTE, L_SELECT_IF_GTE
509
 * \param[out]   pchanged   [optional] 1 if changed; 0 if clone returned
510
 * \return  pixad, or NULL on error
511
 *
512
 * <pre>
513
 * Notes:
514
 *      (1) Returns a pixa clone if no components are removed.
515
 *      (2) Uses pix and box clones in the new pixa.
516
 *      (3) See pixSelectByPerimToAreaRatio().
517
 * </pre>
518
 */
519
PIXA *
520
pixaSelectByPerimToAreaRatio(PIXA      *pixas,
521
                             l_float32  thresh,
522
                             l_int32    type,
523
                             l_int32   *pchanged)
524
0
{
525
0
NUMA  *na, *nai;
526
0
PIXA  *pixad;
527
528
0
    if (!pixas)
529
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
530
0
    if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
531
0
        type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
532
0
        return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
533
534
        /* Compute component ratios. */
535
0
    na = pixaFindPerimToAreaRatio(pixas);
536
537
        /* Generate indicator array for elements to be saved. */
538
0
    nai = numaMakeThresholdIndicator(na, thresh, type);
539
0
    numaDestroy(&na);
540
541
        /* Filter to get output */
542
0
    pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
543
544
0
    numaDestroy(&nai);
545
0
    return pixad;
546
0
}
547
548
549
/*!
550
 * \brief   pixSelectByPerimSizeRatio()
551
 *
552
 * \param[in]    pixs          1 bpp
553
 * \param[in]    thresh        threshold ratio of fg boundary to fg pixels
554
 * \param[in]    connectivity  4 or 8
555
 * \param[in]    type          L_SELECT_IF_LT, L_SELECT_IF_GT,
556
 *                             L_SELECT_IF_LTE, L_SELECT_IF_GTE
557
 * \param[out]   pchanged      [optional] 1 if changed; 0 if clone returned
558
 * \return  pixd, or NULL on error
559
 *
560
 * <pre>
561
 * Notes:
562
 *      (1) The args specify constraints on the size of the
563
 *          components that are kept.
564
 *      (2) If unchanged, returns a copy of pixs.  Otherwise,
565
 *          returns a new pix with the filtered components.
566
 *      (3) This filters components with smooth vs. dendritic shape, using
567
 *          the ratio of the fg boundary pixels to the circumference of
568
 *          the bounding box, and comparing it to a threshold value.
569
 *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the smooth
570
 *          boundary components, and L_SELECT_IF_GT or L_SELECT_IF_GTE
571
 *          to remove them.
572
 * </pre>
573
 */
574
PIX *
575
pixSelectByPerimSizeRatio(PIX       *pixs,
576
                          l_float32  thresh,
577
                          l_int32    connectivity,
578
                          l_int32    type,
579
                          l_int32   *pchanged)
580
0
{
581
0
l_int32  w, h, empty, changed, count;
582
0
BOXA    *boxa;
583
0
PIX     *pixd;
584
0
PIXA    *pixas, *pixad;
585
586
0
    if (!pixs)
587
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
588
0
    if (connectivity != 4 && connectivity != 8)
589
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
590
0
    if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
591
0
        type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
592
0
        return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
593
0
    if (pchanged) *pchanged = FALSE;
594
595
        /* Check if any components exist */
596
0
    pixZero(pixs, &empty);
597
0
    if (empty)
598
0
        return pixCopy(NULL, pixs);
599
600
        /* Filter thin components */
601
0
    boxa = pixConnComp(pixs, &pixas, connectivity);
602
0
    pixad = pixaSelectByPerimSizeRatio(pixas, thresh, type, &changed);
603
0
    boxaDestroy(&boxa);
604
0
    pixaDestroy(&pixas);
605
606
0
    if (!changed) {
607
0
        pixaDestroy(&pixad);
608
0
        return pixCopy(NULL, pixs);
609
0
    }
610
611
        /* Render the result */
612
0
    if (pchanged) *pchanged = TRUE;
613
0
    pixGetDimensions(pixs, &w, &h, NULL);
614
0
    count = pixaGetCount(pixad);
615
0
    if (count == 0) {  /* return empty pix */
616
0
        pixd = pixCreateTemplate(pixs);
617
0
    } else {
618
0
        pixd = pixaDisplay(pixad, w, h);
619
0
        pixCopyResolution(pixd, pixs);
620
0
        pixCopyColormap(pixd, pixs);
621
0
        pixCopyText(pixd, pixs);
622
0
        pixCopyInputFormat(pixd, pixs);
623
0
    }
624
0
    pixaDestroy(&pixad);
625
0
    return pixd;
626
0
}
627
628
629
/*!
630
 * \brief   pixaSelectByPerimSizeRatio()
631
 *
632
 * \param[in]    pixas
633
 * \param[in]    thresh    threshold ratio of fg boundary to b.b. circumference
634
 * \param[in]    type      L_SELECT_IF_LT, L_SELECT_IF_GT,
635
 *                         L_SELECT_IF_LTE, L_SELECT_IF_GTE
636
 * \param[out]   pchanged  [optional] 1 if changed; 0 if clone returned
637
 * \return  pixad, or NULL on error
638
 *
639
 * <pre>
640
 * Notes:
641
 *      (1) Returns a pixa clone if no components are removed.
642
 *      (2) Uses pix and box clones in the new pixa.
643
 *      (3) See pixSelectByPerimSizeRatio().
644
 * </pre>
645
 */
646
PIXA *
647
pixaSelectByPerimSizeRatio(PIXA      *pixas,
648
                           l_float32  thresh,
649
                           l_int32    type,
650
                           l_int32   *pchanged)
651
0
{
652
0
NUMA  *na, *nai;
653
0
PIXA  *pixad;
654
655
0
    if (!pixas)
656
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
657
0
    if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
658
0
        type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
659
0
        return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
660
661
        /* Compute component ratios. */
662
0
    na = pixaFindPerimSizeRatio(pixas);
663
664
        /* Generate indicator array for elements to be saved. */
665
0
    nai = numaMakeThresholdIndicator(na, thresh, type);
666
0
    numaDestroy(&na);
667
668
        /* Filter to get output */
669
0
    pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
670
671
0
    numaDestroy(&nai);
672
0
    return pixad;
673
0
}
674
675
676
/*!
677
 * \brief   pixSelectByAreaFraction()
678
 *
679
 * \param[in]    pixs          1 bpp
680
 * \param[in]    thresh        threshold ratio of fg pixels to (w * h)
681
 * \param[in]    connectivity  4 or 8
682
 * \param[in]    type          L_SELECT_IF_LT, L_SELECT_IF_GT,
683
 *                             L_SELECT_IF_LTE, L_SELECT_IF_GTE
684
 * \param[out]   pchanged      [optional] 1 if changed; 0 if clone returned
685
 * \return  pixd, or NULL on error
686
 *
687
 * <pre>
688
 * Notes:
689
 *      (1) The args specify constraints on the amount of foreground
690
 *          coverage of the components that are kept.
691
 *      (2) If unchanged, returns a copy of pixs.  Otherwise,
692
 *          returns a new pix with the filtered components.
693
 *      (3) This filters components based on the fraction of fg pixels
694
 *          of the component in its bounding box.
695
 *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
696
 *          with less than the threshold fraction of foreground, and
697
 *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
698
 * </pre>
699
 */
700
PIX *
701
pixSelectByAreaFraction(PIX       *pixs,
702
                        l_float32  thresh,
703
                        l_int32    connectivity,
704
                        l_int32    type,
705
                        l_int32   *pchanged)
706
0
{
707
0
l_int32  w, h, empty, changed, count;
708
0
BOXA    *boxa;
709
0
PIX     *pixd;
710
0
PIXA    *pixas, *pixad;
711
712
0
    if (!pixs)
713
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
714
0
    if (connectivity != 4 && connectivity != 8)
715
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
716
0
    if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
717
0
        type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
718
0
        return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
719
0
    if (pchanged) *pchanged = FALSE;
720
721
        /* Check if any components exist */
722
0
    pixZero(pixs, &empty);
723
0
    if (empty)
724
0
        return pixCopy(NULL, pixs);
725
726
        /* Filter components */
727
0
    boxa = pixConnComp(pixs, &pixas, connectivity);
728
0
    pixad = pixaSelectByAreaFraction(pixas, thresh, type, &changed);
729
0
    boxaDestroy(&boxa);
730
0
    pixaDestroy(&pixas);
731
732
0
    if (!changed) {
733
0
        pixaDestroy(&pixad);
734
0
        return pixCopy(NULL, pixs);
735
0
    }
736
737
        /* Render the result */
738
0
    if (pchanged) *pchanged = TRUE;
739
0
    pixGetDimensions(pixs, &w, &h, NULL);
740
0
    count = pixaGetCount(pixad);
741
0
    if (count == 0) {  /* return empty pix */
742
0
        pixd = pixCreateTemplate(pixs);
743
0
    } else {
744
0
        pixd = pixaDisplay(pixad, w, h);
745
0
        pixCopyResolution(pixd, pixs);
746
0
        pixCopyColormap(pixd, pixs);
747
0
        pixCopyText(pixd, pixs);
748
0
        pixCopyInputFormat(pixd, pixs);
749
0
    }
750
0
    pixaDestroy(&pixad);
751
0
    return pixd;
752
0
}
753
754
755
/*!
756
 * \brief   pixaSelectByAreaFraction()
757
 *
758
 * \param[in]    pixas
759
 * \param[in]    thresh      threshold ratio of fg pixels to (w * h)
760
 * \param[in]    type        L_SELECT_IF_LT, L_SELECT_IF_GT,
761
 *                           L_SELECT_IF_LTE, L_SELECT_IF_GTE
762
 * \param[out]   pchanged    [optional] 1 if changed; 0 if clone returned
763
 * \return  pixad, or NULL on error
764
 *
765
 * <pre>
766
 * Notes:
767
 *      (1) Returns a pixa clone if no components are removed.
768
 *      (2) Uses pix and box clones in the new pixa.
769
 *      (3) This filters components based on the fraction of fg pixels
770
 *          of the component in its bounding box.
771
 *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
772
 *          with less than the threshold fraction of foreground, and
773
 *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
774
 * </pre>
775
 */
776
PIXA *
777
pixaSelectByAreaFraction(PIXA      *pixas,
778
                         l_float32  thresh,
779
                         l_int32    type,
780
                         l_int32   *pchanged)
781
0
{
782
0
NUMA  *na, *nai;
783
0
PIXA  *pixad;
784
785
0
    if (!pixas)
786
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
787
0
    if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
788
0
        type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
789
0
        return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
790
791
        /* Compute component ratios. */
792
0
    na = pixaFindAreaFraction(pixas);
793
794
        /* Generate indicator array for elements to be saved. */
795
0
    nai = numaMakeThresholdIndicator(na, thresh, type);
796
0
    numaDestroy(&na);
797
798
        /* Filter to get output */
799
0
    pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
800
801
0
    numaDestroy(&nai);
802
0
    return pixad;
803
0
}
804
805
806
/*!
807
 * \brief   pixSelectByArea()
808
 *
809
 * \param[in]    pixs          1 bpp
810
 * \param[in]    thresh        threshold number of FG pixels
811
 * \param[in]    connectivity  4 or 8
812
 * \param[in]    type          L_SELECT_IF_LT, L_SELECT_IF_GT,
813
 *                             L_SELECT_IF_LTE, L_SELECT_IF_GTE
814
 * \param[out]   pchanged      [optional] 1 if changed; 0 if clone returned
815
 * \return  pixd, or NULL on error
816
 *
817
 * <pre>
818
 * Notes:
819
 *      (1) The args specify constraints on the number of foreground
820
 *          pixels in the components that are kept.
821
 *      (2) If unchanged, returns a copy of pixs.  Otherwise,
822
 *          returns a new pix with the filtered components.
823
 *      (3) This filters components based on the number of fg pixels
824
 *          in each component.
825
 *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
826
 *          with less than the threshold number of fg pixels, and
827
 *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
828
 * </pre>
829
 */
830
PIX *
831
pixSelectByArea(PIX       *pixs,
832
                l_float32  thresh,
833
                l_int32    connectivity,
834
                l_int32    type,
835
                l_int32   *pchanged)
836
0
{
837
0
l_int32  w, h, empty, changed, count;
838
0
BOXA    *boxa;
839
0
PIX     *pixd;
840
0
PIXA    *pixas, *pixad;
841
842
0
    if (!pixs)
843
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
844
0
    if (connectivity != 4 && connectivity != 8)
845
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
846
0
    if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
847
0
        type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
848
0
        return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
849
0
    if (pchanged) *pchanged = FALSE;
850
851
        /* Check if any components exist */
852
0
    pixZero(pixs, &empty);
853
0
    if (empty)
854
0
        return pixCopy(NULL, pixs);
855
856
        /* Filter components */
857
0
    boxa = pixConnComp(pixs, &pixas, connectivity);
858
0
    pixad = pixaSelectByArea(pixas, thresh, type, &changed);
859
0
    boxaDestroy(&boxa);
860
0
    pixaDestroy(&pixas);
861
862
0
    if (!changed) {
863
0
        pixaDestroy(&pixad);
864
0
        return pixCopy(NULL, pixs);
865
0
    }
866
867
        /* Render the result */
868
0
    if (pchanged) *pchanged = TRUE;
869
0
    pixGetDimensions(pixs, &w, &h, NULL);
870
0
    count = pixaGetCount(pixad);
871
0
    if (count == 0) {  /* return empty pix */
872
0
        pixd = pixCreateTemplate(pixs);
873
0
    } else {
874
0
        pixd = pixaDisplay(pixad, w, h);
875
0
        pixCopyResolution(pixd, pixs);
876
0
        pixCopyColormap(pixd, pixs);
877
0
        pixCopyText(pixd, pixs);
878
0
        pixCopyInputFormat(pixd, pixs);
879
0
    }
880
0
    pixaDestroy(&pixad);
881
0
    return pixd;
882
0
}
883
884
885
/*!
886
 * \brief   pixaSelectByArea()
887
 *
888
 * \param[in]    pixas
889
 * \param[in]    thresh      threshold number of fg pixels
890
 * \param[in]    type        L_SELECT_IF_LT, L_SELECT_IF_GT,
891
 *                           L_SELECT_IF_LTE, L_SELECT_IF_GTE
892
 * \param[out]   pchanged    [optional] 1 if changed; 0 if clone returned
893
 * \return  pixad, or NULL on error
894
 *
895
 * <pre>
896
 * Notes:
897
 *      (1) Returns a pixa clone if no components are removed.
898
 *      (2) Uses pix and box clones in the new pixa.
899
 *      (3) This filters components based on the number of fg pixels
900
 *          in the component.
901
 *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
902
 *          with less than the threshold number of fg pixels, and
903
 *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
904
 * </pre>
905
 */
906
PIXA *
907
pixaSelectByArea(PIXA      *pixas,
908
                 l_float32  thresh,
909
                 l_int32    type,
910
                 l_int32   *pchanged)
911
0
{
912
0
NUMA  *na, *nai;
913
0
PIXA  *pixad;
914
915
0
    if (!pixas)
916
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
917
0
    if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
918
0
        type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
919
0
        return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
920
921
        /* Compute area of each component */
922
0
    na = pixaCountPixels(pixas);
923
924
        /* Generate indicator array for elements to be saved. */
925
0
    nai = numaMakeThresholdIndicator(na, thresh, type);
926
0
    numaDestroy(&na);
927
928
        /* Filter to get output */
929
0
    pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
930
931
0
    numaDestroy(&nai);
932
0
    return pixad;
933
0
}
934
935
936
/*!
937
 * \brief   pixSelectByWidthHeightRatio()
938
 *
939
 * \param[in]    pixs          1 bpp
940
 * \param[in]    thresh        threshold ratio of width/height
941
 * \param[in]    connectivity  4 or 8
942
 * \param[in]    type          L_SELECT_IF_LT, L_SELECT_IF_GT,
943
 *                             L_SELECT_IF_LTE, L_SELECT_IF_GTE
944
 * \param[out]   pchanged      [optional] 1 if changed; 0 if clone returned
945
 * \return  pixd, or NULL on error
946
 *
947
 * <pre>
948
 * Notes:
949
 *      (1) The args specify constraints on the width-to-height ratio
950
 *          for components that are kept.
951
 *      (2) If unchanged, returns a copy of pixs.  Otherwise,
952
 *          returns a new pix with the filtered components.
953
 *      (3) This filters components based on the width-to-height ratios.
954
 *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
955
 *          with less than the threshold ratio, and
956
 *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
957
 * </pre>
958
 */
959
PIX *
960
pixSelectByWidthHeightRatio(PIX       *pixs,
961
                            l_float32  thresh,
962
                            l_int32    connectivity,
963
                            l_int32    type,
964
                            l_int32   *pchanged)
965
0
{
966
0
l_int32  w, h, empty, changed, count;
967
0
BOXA    *boxa;
968
0
PIX     *pixd;
969
0
PIXA    *pixas, *pixad;
970
971
0
    if (!pixs)
972
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
973
0
    if (connectivity != 4 && connectivity != 8)
974
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
975
0
    if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
976
0
        type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
977
0
        return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
978
0
    if (pchanged) *pchanged = FALSE;
979
980
        /* Check if any components exist */
981
0
    pixZero(pixs, &empty);
982
0
    if (empty)
983
0
        return pixCopy(NULL, pixs);
984
985
        /* Filter components */
986
0
    boxa = pixConnComp(pixs, &pixas, connectivity);
987
0
    pixad = pixaSelectByWidthHeightRatio(pixas, thresh, type, &changed);
988
0
    boxaDestroy(&boxa);
989
0
    pixaDestroy(&pixas);
990
991
0
    if (!changed) {
992
0
        pixaDestroy(&pixad);
993
0
        return pixCopy(NULL, pixs);
994
0
    }
995
996
        /* Render the result */
997
0
    if (pchanged) *pchanged = TRUE;
998
0
    pixGetDimensions(pixs, &w, &h, NULL);
999
0
    count = pixaGetCount(pixad);
1000
0
    if (count == 0) {  /* return empty pix */
1001
0
        pixd = pixCreateTemplate(pixs);
1002
0
    } else {
1003
0
        pixd = pixaDisplay(pixad, w, h);
1004
0
        pixCopyResolution(pixd, pixs);
1005
0
        pixCopyColormap(pixd, pixs);
1006
0
        pixCopyText(pixd, pixs);
1007
0
        pixCopyInputFormat(pixd, pixs);
1008
0
    }
1009
0
    pixaDestroy(&pixad);
1010
0
    return pixd;
1011
0
}
1012
1013
1014
/*!
1015
 * \brief   pixaSelectByWidthHeightRatio()
1016
 *
1017
 * \param[in]    pixas
1018
 * \param[in]    thresh      threshold ratio of width/height
1019
 * \param[in]    type        L_SELECT_IF_LT, L_SELECT_IF_GT,
1020
 *                           L_SELECT_IF_LTE, L_SELECT_IF_GTE
1021
 * \param[out]   pchanged    [optional] 1 if changed; 0 if clone returned
1022
 * \return  pixad, or NULL on error
1023
 *
1024
 * <pre>
1025
 * Notes:
1026
 *      (1) Returns a pixa clone if no components are removed.
1027
 *      (2) Uses pix and box clones in the new pixa.
1028
 *      (3) This filters components based on the width-to-height ratio
1029
 *          of each pix.
1030
 *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
1031
 *          with less than the threshold ratio, and
1032
 *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
1033
 * </pre>
1034
 */
1035
PIXA *
1036
pixaSelectByWidthHeightRatio(PIXA      *pixas,
1037
                             l_float32  thresh,
1038
                             l_int32    type,
1039
                             l_int32   *pchanged)
1040
0
{
1041
0
NUMA  *na, *nai;
1042
0
PIXA  *pixad;
1043
1044
0
    if (!pixas)
1045
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1046
0
    if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
1047
0
        type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
1048
0
        return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
1049
1050
        /* Compute component ratios. */
1051
0
    na = pixaFindWidthHeightRatio(pixas);
1052
1053
        /* Generate indicator array for elements to be saved. */
1054
0
    nai = numaMakeThresholdIndicator(na, thresh, type);
1055
0
    numaDestroy(&na);
1056
1057
        /* Filter to get output */
1058
0
    pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
1059
1060
0
    numaDestroy(&nai);
1061
0
    return pixad;
1062
0
}
1063
1064
1065
/*!
1066
 * \brief   pixaSelectByNumConnComp()
1067
 *
1068
 * \param[in]    pixas
1069
 * \param[in]    nmin          minimum number of components
1070
 * \param[in]    nmax          maximum number of components
1071
 * \param[in]    connectivity  4 or 8
1072
 * \param[out]   pchanged      [optional] 1 if changed; 0 if clone returned
1073
 * \return  pixad, or NULL on error
1074
 *
1075
 * <pre>
1076
 * Notes:
1077
 *      (1) Returns a pixa clone if no components are removed.
1078
 *      (2) Uses pix and box clones in the new pixa.
1079
 *      (3) This filters by the number of connected components in
1080
 *          a given range.
1081
 * </pre>
1082
 */
1083
PIXA *
1084
pixaSelectByNumConnComp(PIXA      *pixas,
1085
                        l_int32    nmin,
1086
                        l_int32    nmax,
1087
                        l_int32    connectivity,
1088
                        l_int32   *pchanged)
1089
0
{
1090
0
l_int32  n, i, count;
1091
0
NUMA    *na;
1092
0
PIX     *pix;
1093
0
PIXA    *pixad;
1094
1095
0
    if (pchanged) *pchanged = 0;
1096
0
    if (!pixas)
1097
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1098
0
    if (nmin > nmax)
1099
0
        return (PIXA *)ERROR_PTR("nmin > nmax", __func__, NULL);
1100
0
    if (connectivity != 4 && connectivity != 8)
1101
0
        return (PIXA *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
1102
1103
        /* Get indicator array based on number of c.c. */
1104
0
    n = pixaGetCount(pixas);
1105
0
    na = numaCreate(n);
1106
0
    for (i = 0; i < n; i++) {
1107
0
        pix = pixaGetPix(pixas, i, L_CLONE);
1108
0
        pixCountConnComp(pix, connectivity, &count);
1109
0
        if (count >= nmin && count <= nmax)
1110
0
            numaAddNumber(na, 1);
1111
0
        else
1112
0
            numaAddNumber(na, 0);
1113
0
        pixDestroy(&pix);
1114
0
    }
1115
1116
        /* Filter to get output */
1117
0
    pixad = pixaSelectWithIndicator(pixas, na, pchanged);
1118
0
    numaDestroy(&na);
1119
0
    return pixad;
1120
0
}
1121
1122
1123
/*!
1124
 * \brief   pixaSelectWithIndicator()
1125
 *
1126
 * \param[in]    pixas
1127
 * \param[in]    na         indicator numa
1128
 * \param[out]   pchanged   [optional] 1 if changed; 0 if clone returned
1129
 * \return  pixad, or NULL on error
1130
 *
1131
 * <pre>
1132
 * Notes:
1133
 *      (1) Returns a pixa clone if no components are removed.
1134
 *      (2) Uses pix and box clones in the new pixa.
1135
 *      (3) The indicator numa has values 0 (ignore) and 1 (accept).
1136
 *      (4) If the source boxa is not fully populated, it is left
1137
 *          empty in the dest pixa.
1138
 * </pre>
1139
 */
1140
PIXA *
1141
pixaSelectWithIndicator(PIXA     *pixas,
1142
                        NUMA     *na,
1143
                        l_int32  *pchanged)
1144
0
{
1145
0
l_int32  i, n, nbox, ival, nsave;
1146
0
BOX     *box;
1147
0
PIX     *pix1;
1148
0
PIXA    *pixad;
1149
1150
0
    if (!pixas)
1151
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1152
0
    if (!na)
1153
0
        return (PIXA *)ERROR_PTR("na not defined", __func__, NULL);
1154
1155
0
    nsave = 0;
1156
0
    n = numaGetCount(na);
1157
0
    for (i = 0; i < n; i++) {
1158
0
        numaGetIValue(na, i, &ival);
1159
0
        if (ival == 1) nsave++;
1160
0
    }
1161
1162
0
    if (nsave == n) {
1163
0
        if (pchanged) *pchanged = FALSE;
1164
0
        return pixaCopy(pixas, L_CLONE);
1165
0
    }
1166
0
    if (pchanged) *pchanged = TRUE;
1167
0
    pixad = pixaCreate(nsave);
1168
0
    nbox = pixaGetBoxaCount(pixas);
1169
0
    for (i = 0; i < n; i++) {
1170
0
        numaGetIValue(na, i, &ival);
1171
0
        if (ival == 0) continue;
1172
0
        pix1 = pixaGetPix(pixas, i, L_CLONE);
1173
0
        pixaAddPix(pixad, pix1, L_INSERT);
1174
0
        if (nbox == n) {   /* fully populated boxa */
1175
0
            box = pixaGetBox(pixas, i, L_CLONE);
1176
0
            pixaAddBox(pixad, box, L_INSERT);
1177
0
        }
1178
0
    }
1179
1180
0
    return pixad;
1181
0
}
1182
1183
1184
/*!
1185
 * \brief   pixRemoveWithIndicator()
1186
 *
1187
 * \param[in]    pixs     1 bpp pix from which components are removed; in-place
1188
 * \param[in]    pixa     of connected components in pixs
1189
 * \param[in]    na       numa indicator: remove components corresponding to 1s
1190
 * \return  0 if OK, 1 on error
1191
 *
1192
 * <pre>
1193
 * Notes:
1194
 *      (1) This complements pixAddWithIndicator().   Here, the selected
1195
 *          components are set subtracted from pixs.
1196
 * </pre>
1197
 */
1198
l_ok
1199
pixRemoveWithIndicator(PIX   *pixs,
1200
                       PIXA  *pixa,
1201
                       NUMA  *na)
1202
0
{
1203
0
l_int32  i, n, ival, x, y, w, h;
1204
0
BOX     *box;
1205
0
PIX     *pix;
1206
1207
0
    if (!pixs)
1208
0
        return ERROR_INT("pixs not defined", __func__, 1);
1209
0
    if (!pixa)
1210
0
        return ERROR_INT("pixa not defined", __func__, 1);
1211
0
    if (!na)
1212
0
        return ERROR_INT("na not defined", __func__, 1);
1213
0
    n = pixaGetCount(pixa);
1214
0
    if (n != numaGetCount(na))
1215
0
        return ERROR_INT("pixa and na sizes not equal", __func__, 1);
1216
1217
0
    for (i = 0; i < n; i++) {
1218
0
        numaGetIValue(na, i, &ival);
1219
0
        if (ival == 1) {
1220
0
            pix = pixaGetPix(pixa, i, L_CLONE);
1221
0
            box = pixaGetBox(pixa, i, L_CLONE);
1222
0
            boxGetGeometry(box, &x, &y, &w, &h);
1223
0
            pixRasterop(pixs, x, y, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1224
0
                        pix, 0, 0);
1225
0
            boxDestroy(&box);
1226
0
            pixDestroy(&pix);
1227
0
        }
1228
0
    }
1229
1230
0
    return 0;
1231
0
}
1232
1233
1234
/*!
1235
 * \brief   pixAddWithIndicator()
1236
 *
1237
 * \param[in]    pixs     1 bpp pix from which components are added; in-place
1238
 * \param[in]    pixa     of connected components, some of which will be put
1239
 *                        into pixs
1240
 * \param[in]    na       numa indicator: add components corresponding to 1s
1241
 * \return  0 if OK, 1 on error
1242
 *
1243
 * <pre>
1244
 * Notes:
1245
 *      (1) This complements pixRemoveWithIndicator().   Here, the selected
1246
 *          components are added to pixs.
1247
 * </pre>
1248
 */
1249
l_ok
1250
pixAddWithIndicator(PIX   *pixs,
1251
                    PIXA  *pixa,
1252
                    NUMA  *na)
1253
0
{
1254
0
l_int32  i, n, ival, x, y, w, h;
1255
0
BOX     *box;
1256
0
PIX     *pix;
1257
1258
0
    if (!pixs)
1259
0
        return ERROR_INT("pixs not defined", __func__, 1);
1260
0
    if (!pixa)
1261
0
        return ERROR_INT("pixa not defined", __func__, 1);
1262
0
    if (!na)
1263
0
        return ERROR_INT("na not defined", __func__, 1);
1264
0
    n = pixaGetCount(pixa);
1265
0
    if (n != numaGetCount(na))
1266
0
        return ERROR_INT("pixa and na sizes not equal", __func__, 1);
1267
1268
0
    for (i = 0; i < n; i++) {
1269
0
        numaGetIValue(na, i, &ival);
1270
0
        if (ival == 1) {
1271
0
            pix = pixaGetPix(pixa, i, L_CLONE);
1272
0
            box = pixaGetBox(pixa, i, L_CLONE);
1273
0
            boxGetGeometry(box, &x, &y, &w, &h);
1274
0
            pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0);
1275
0
            boxDestroy(&box);
1276
0
            pixDestroy(&pix);
1277
0
        }
1278
0
    }
1279
1280
0
    return 0;
1281
0
}
1282
1283
1284
/*!
1285
 * \brief   pixaSelectWithString()
1286
 *
1287
 * \param[in]    pixas
1288
 * \param[in]    str      string of indices into pixa, giving the pix to
1289
 *                        be selected
1290
 * \param[out]   perror   [optional] 1 if any indices are invalid;
1291
 *                        0 if all indices are valid
1292
 * \return  pixad, or NULL on error
1293
 *
1294
 * <pre>
1295
 * Notes:
1296
 *      (1) Returns a pixa with copies of selected pix.
1297
 *      (2) Associated boxes are also copied, if fully populated.
1298
 * </pre>
1299
 */
1300
PIXA *
1301
pixaSelectWithString(PIXA        *pixas,
1302
                     const char  *str,
1303
                     l_int32     *perror)
1304
0
{
1305
0
l_int32    i, nval, npix, nbox, val, imaxval;
1306
0
l_float32  maxval;
1307
0
BOX       *box;
1308
0
NUMA      *na;
1309
0
PIX       *pix1;
1310
0
PIXA      *pixad;
1311
1312
0
    if (perror) *perror = 0;
1313
0
    if (!pixas)
1314
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1315
0
    if (!str)
1316
0
        return (PIXA *)ERROR_PTR("str not defined", __func__, NULL);
1317
1318
0
    if ((na = numaCreateFromString(str)) == NULL)
1319
0
        return (PIXA *)ERROR_PTR("na not made", __func__, NULL);
1320
0
    if ((nval = numaGetCount(na)) == 0) {
1321
0
        numaDestroy(&na);
1322
0
        return (PIXA *)ERROR_PTR("no indices found", __func__, NULL);
1323
0
    }
1324
0
    numaGetMax(na, &maxval, NULL);
1325
0
    imaxval = (l_int32)(maxval + 0.1);
1326
0
    nbox = pixaGetBoxaCount(pixas);
1327
0
    npix = pixaGetCount(pixas);
1328
0
    if (imaxval >= npix) {
1329
0
        if (perror) *perror = 1;
1330
0
        L_ERROR("max index = %d, size of pixa = %d\n", __func__, imaxval, npix);
1331
0
    }
1332
1333
0
    pixad = pixaCreate(nval);
1334
0
    for (i = 0; i < nval; i++) {
1335
0
        numaGetIValue(na, i, &val);
1336
0
        if (val < 0 || val >= npix) {
1337
0
            L_ERROR("index %d out of range of pix\n", __func__, val);
1338
0
            continue;
1339
0
        }
1340
0
        pix1 = pixaGetPix(pixas, val, L_COPY);
1341
0
        pixaAddPix(pixad, pix1, L_INSERT);
1342
0
        if (nbox == npix) {   /* fully populated boxa */
1343
0
            box = pixaGetBox(pixas, val, L_COPY);
1344
0
            pixaAddBox(pixad, box, L_INSERT);
1345
0
        }
1346
0
    }
1347
0
    numaDestroy(&na);
1348
0
    return pixad;
1349
0
}
1350
1351
1352
/*!
1353
 * \brief   pixaRenderComponent()
1354
 *
1355
 * \param[in]    pixs    [optional] 1 bpp pix
1356
 * \param[in]    pixa    of 1 bpp connected components, one of which will
1357
 *                       be rendered in pixs, with its origin determined
1358
 *                       by the associated box.
1359
 * \param[in]    index   of component to be rendered
1360
 * \return  pixd, or NULL on error
1361
 *
1362
 * <pre>
1363
 * Notes:
1364
 *      (1) If pixs is null, this generates an empty pix of a size determined
1365
 *          by union of the component bounding boxes, and including the origin.
1366
 *      (2) The selected component is blitted into pixs.
1367
 * </pre>
1368
 */
1369
PIX *
1370
pixaRenderComponent(PIX     *pixs,
1371
                    PIXA    *pixa,
1372
                    l_int32  index)
1373
0
{
1374
0
l_int32  n, x, y, w, h, same, maxd;
1375
0
BOX     *box;
1376
0
BOXA    *boxa;
1377
0
PIX     *pix;
1378
1379
0
    if (!pixa)
1380
0
        return (PIX *)ERROR_PTR("pixa not defined", __func__, pixs);
1381
0
    n = pixaGetCount(pixa);
1382
0
    if (index < 0 || index >= n)
1383
0
        return (PIX *)ERROR_PTR("invalid index", __func__, pixs);
1384
0
    if (pixs && (pixGetDepth(pixs) != 1))
1385
0
        return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixs);
1386
0
    pixaVerifyDepth(pixa, &same, &maxd);
1387
0
    if (maxd > 1)
1388
0
        return (PIX *)ERROR_PTR("not all pix with d == 1", __func__, pixs);
1389
1390
0
    boxa = pixaGetBoxa(pixa, L_CLONE);
1391
0
    if (!pixs) {
1392
0
        boxaGetExtent(boxa, &w, &h, NULL);
1393
0
        pixs = pixCreate(w, h, 1);
1394
0
    }
1395
1396
0
    pix = pixaGetPix(pixa, index, L_CLONE);
1397
0
    box = boxaGetBox(boxa, index, L_CLONE);
1398
0
    boxGetGeometry(box, &x, &y, &w, &h);
1399
0
    pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0);
1400
0
    boxDestroy(&box);
1401
0
    pixDestroy(&pix);
1402
0
    boxaDestroy(&boxa);
1403
1404
0
    return pixs;
1405
0
}
1406
1407
1408
/*---------------------------------------------------------------------*
1409
 *                              Sort functions                         *
1410
 *---------------------------------------------------------------------*/
1411
/*!
1412
 * \brief   pixaSort()
1413
 *
1414
 * \param[in]    pixas
1415
 * \param[in]    sorttype   L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH,
1416
 *                          L_SORT_BY_HEIGHT, L_SORT_BY_MIN_DIMENSION,
1417
 *                          L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER,
1418
 *                          L_SORT_BY_AREA, L_SORT_BY_ASPECT_RATIO
1419
 * \param[in]    sortorder  L_SORT_INCREASING, L_SORT_DECREASING
1420
 * \param[out]   pnaindex   [optional] index of sorted order into
1421
 *                          original array
1422
 * \param[in]    copyflag   L_COPY, L_CLONE
1423
 * \return  pixad sorted version of pixas, or NULL on error
1424
 *
1425
 * <pre>
1426
 * Notes:
1427
 *      (1) This sorts based on the data in the boxa.  If the boxa
1428
 *          count is not the same as the pixa count, this returns an error.
1429
 *      (2) If the boxa is empty, it makes one corresponding to the
1430
 *          dimensions of each pix, which allows meaningful sorting on
1431
 *          all types except x and y.
1432
 *      (3) The copyflag refers to the pix and box copies that are
1433
 *          inserted into the sorted pixa.  These are either L_COPY
1434
 *          or L_CLONE.
1435
 * </pre>
1436
 */
1437
PIXA *
1438
pixaSort(PIXA    *pixas,
1439
         l_int32  sorttype,
1440
         l_int32  sortorder,
1441
         NUMA   **pnaindex,
1442
         l_int32  copyflag)
1443
0
{
1444
0
l_int32  i, n, nb, x, y, w, h;
1445
0
BOXA    *boxa;
1446
0
NUMA    *na, *naindex;
1447
0
PIXA    *pixad;
1448
1449
0
    if (pnaindex) *pnaindex = NULL;
1450
0
    if (!pixas)
1451
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1452
0
    if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y &&
1453
0
        sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT &&
1454
0
        sorttype != L_SORT_BY_MIN_DIMENSION &&
1455
0
        sorttype != L_SORT_BY_MAX_DIMENSION &&
1456
0
        sorttype != L_SORT_BY_PERIMETER &&
1457
0
        sorttype != L_SORT_BY_AREA &&
1458
0
        sorttype != L_SORT_BY_ASPECT_RATIO)
1459
0
        return (PIXA *)ERROR_PTR("invalid sort type", __func__, NULL);
1460
0
    if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING)
1461
0
        return (PIXA *)ERROR_PTR("invalid sort order", __func__, NULL);
1462
0
    if (copyflag != L_COPY && copyflag != L_CLONE)
1463
0
        return (PIXA *)ERROR_PTR("invalid copy flag", __func__, NULL);
1464
1465
        /* Check the pixa and boxa counts. Make a boxa if required. */
1466
0
    if ((n = pixaGetCount(pixas)) == 0) {
1467
0
        L_INFO("no pix in pixa\n", __func__);
1468
0
        return pixaCopy(pixas, copyflag);
1469
0
    }
1470
0
    if ((boxa = pixas->boxa) == NULL)   /* not owned; do not destroy */
1471
0
        return (PIXA *)ERROR_PTR("boxa not found!", __func__, NULL);
1472
0
    nb = boxaGetCount(boxa);
1473
0
    if (nb == 0) {
1474
0
        pixaSetFullSizeBoxa(pixas);
1475
0
        nb = n;
1476
0
        boxa = pixas->boxa;  /* not owned */
1477
0
        if (sorttype == L_SORT_BY_X || sorttype == L_SORT_BY_Y)
1478
0
            L_WARNING("sort by x or y where all values are 0\n", __func__);
1479
0
    }
1480
0
    if (nb != n)
1481
0
        return (PIXA *)ERROR_PTR("boxa and pixa counts differ", __func__, NULL);
1482
1483
        /* Use O(n) binsort if possible */
1484
0
    if (n > MinCompsForBinSort &&
1485
0
        ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) ||
1486
0
         (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) ||
1487
0
         (sorttype == L_SORT_BY_PERIMETER)))
1488
0
        return pixaBinSort(pixas, sorttype, sortorder, pnaindex, copyflag);
1489
1490
        /* Build up numa of specific data */
1491
0
    if ((na = numaCreate(n)) == NULL)
1492
0
        return (PIXA *)ERROR_PTR("na not made", __func__, NULL);
1493
0
    for (i = 0; i < n; i++) {
1494
0
        boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
1495
0
        switch (sorttype)
1496
0
        {
1497
0
        case L_SORT_BY_X:
1498
0
            numaAddNumber(na, x);
1499
0
            break;
1500
0
        case L_SORT_BY_Y:
1501
0
            numaAddNumber(na, y);
1502
0
            break;
1503
0
        case L_SORT_BY_WIDTH:
1504
0
            numaAddNumber(na, w);
1505
0
            break;
1506
0
        case L_SORT_BY_HEIGHT:
1507
0
            numaAddNumber(na, h);
1508
0
            break;
1509
0
        case L_SORT_BY_MIN_DIMENSION:
1510
0
            numaAddNumber(na, L_MIN(w, h));
1511
0
            break;
1512
0
        case L_SORT_BY_MAX_DIMENSION:
1513
0
            numaAddNumber(na, L_MAX(w, h));
1514
0
            break;
1515
0
        case L_SORT_BY_PERIMETER:
1516
0
            numaAddNumber(na, w + h);
1517
0
            break;
1518
0
        case L_SORT_BY_AREA:
1519
0
            numaAddNumber(na, w * h);
1520
0
            break;
1521
0
        case L_SORT_BY_ASPECT_RATIO:
1522
0
            numaAddNumber(na, (l_float32)w / (l_float32)h);
1523
0
            break;
1524
0
        default:
1525
0
            L_WARNING("invalid sort type\n", __func__);
1526
0
        }
1527
0
    }
1528
1529
        /* Get the sort index for data array */
1530
0
    naindex = numaGetSortIndex(na, sortorder);
1531
0
    numaDestroy(&na);
1532
0
    if (!naindex)
1533
0
        return (PIXA *)ERROR_PTR("naindex not made", __func__, NULL);
1534
1535
        /* Build up sorted pixa using sort index */
1536
0
    if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) {
1537
0
        numaDestroy(&naindex);
1538
0
        return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL);
1539
0
    }
1540
1541
0
    if (pnaindex)
1542
0
        *pnaindex = naindex;
1543
0
    else
1544
0
        numaDestroy(&naindex);
1545
0
    return pixad;
1546
0
}
1547
1548
1549
/*!
1550
 * \brief   pixaBinSort()
1551
 *
1552
 * \param[in]    pixas
1553
 * \param[in]    sorttype    L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH,
1554
 *                           L_SORT_BY_HEIGHT, L_SORT_BY_PERIMETER
1555
 * \param[in]    sortorder   L_SORT_INCREASING, L_SORT_DECREASING
1556
 * \param[out]   pnaindex    [optional] index of sorted order into
1557
 *                           original array
1558
 * \param[in]    copyflag    L_COPY, L_CLONE
1559
 * \return  pixad sorted version of pixas, or NULL on error
1560
 *
1561
 * <pre>
1562
 * Notes:
1563
 *      (1) This sorts based on the data in the boxa.  If the boxa
1564
 *          count is not the same as the pixa count, this returns an error.
1565
 *      (2) The copyflag refers to the pix and box copies that are
1566
 *          inserted into the sorted pixa.  These are either L_COPY
1567
 *          or L_CLONE.
1568
 *      (3) For a large number of boxes (say, greater than 1000), this
1569
 *          O(n) binsort is much faster than the O(nlogn) shellsort.
1570
 *          For 5000 components, this is over 20x faster than boxaSort().
1571
 *      (4) Consequently, pixaSort() calls this function if it will
1572
 *          likely go much faster.
1573
 * </pre>
1574
 */
1575
PIXA *
1576
pixaBinSort(PIXA    *pixas,
1577
            l_int32  sorttype,
1578
            l_int32  sortorder,
1579
            NUMA   **pnaindex,
1580
            l_int32  copyflag)
1581
0
{
1582
0
l_int32  i, n, x, y, w, h;
1583
0
BOXA    *boxa;
1584
0
NUMA    *na, *naindex;
1585
0
PIXA    *pixad;
1586
1587
0
    if (pnaindex) *pnaindex = NULL;
1588
0
    if (!pixas)
1589
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1590
0
    if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y &&
1591
0
        sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT &&
1592
0
        sorttype != L_SORT_BY_PERIMETER)
1593
0
        return (PIXA *)ERROR_PTR("invalid sort type", __func__, NULL);
1594
0
    if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING)
1595
0
        return (PIXA *)ERROR_PTR("invalid sort order", __func__, NULL);
1596
0
    if (copyflag != L_COPY && copyflag != L_CLONE)
1597
0
        return (PIXA *)ERROR_PTR("invalid copy flag", __func__, NULL);
1598
1599
        /* Verify that the pixa and its boxa have the same count */
1600
0
    if ((boxa = pixas->boxa) == NULL)   /* not owned; do not destroy */
1601
0
        return (PIXA *)ERROR_PTR("boxa not found", __func__, NULL);
1602
0
    n = pixaGetCount(pixas);
1603
0
    if (boxaGetCount(boxa) != n)
1604
0
        return (PIXA *)ERROR_PTR("boxa and pixa counts differ", __func__, NULL);
1605
1606
        /* Generate Numa of appropriate box dimensions */
1607
0
    if ((na = numaCreate(n)) == NULL)
1608
0
        return (PIXA *)ERROR_PTR("na not made", __func__, NULL);
1609
0
    for (i = 0; i < n; i++) {
1610
0
        boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
1611
0
        switch (sorttype)
1612
0
        {
1613
0
        case L_SORT_BY_X:
1614
0
            numaAddNumber(na, x);
1615
0
            break;
1616
0
        case L_SORT_BY_Y:
1617
0
            numaAddNumber(na, y);
1618
0
            break;
1619
0
        case L_SORT_BY_WIDTH:
1620
0
            numaAddNumber(na, w);
1621
0
            break;
1622
0
        case L_SORT_BY_HEIGHT:
1623
0
            numaAddNumber(na, h);
1624
0
            break;
1625
0
        case L_SORT_BY_PERIMETER:
1626
0
            numaAddNumber(na, w + h);
1627
0
            break;
1628
0
        default:
1629
0
            L_WARNING("invalid sort type\n", __func__);
1630
0
        }
1631
0
    }
1632
1633
        /* Get the sort index for data array */
1634
0
    naindex = numaGetBinSortIndex(na, sortorder);
1635
0
    numaDestroy(&na);
1636
0
    if (!naindex)
1637
0
        return (PIXA *)ERROR_PTR("naindex not made", __func__, NULL);
1638
1639
        /* Build up sorted pixa using sort index */
1640
0
    if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) {
1641
0
        numaDestroy(&naindex);
1642
0
        return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL);
1643
0
    }
1644
1645
0
    if (pnaindex)
1646
0
        *pnaindex = naindex;
1647
0
    else
1648
0
        numaDestroy(&naindex);
1649
0
    return pixad;
1650
0
}
1651
1652
1653
/*!
1654
 * \brief   pixaSortByIndex()
1655
 *
1656
 * \param[in]    pixas
1657
 * \param[in]    naindex    na that maps from the new pixa to the input pixa
1658
 * \param[in]    copyflag   L_COPY, L_CLONE
1659
 * \return  pixad sorted, or NULL on error
1660
 */
1661
PIXA *
1662
pixaSortByIndex(PIXA    *pixas,
1663
                NUMA    *naindex,
1664
                l_int32  copyflag)
1665
0
{
1666
0
l_int32  i, n, index;
1667
0
BOX     *box;
1668
0
PIX     *pix;
1669
0
PIXA    *pixad;
1670
1671
0
    if (!pixas)
1672
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1673
0
    if (!naindex)
1674
0
        return (PIXA *)ERROR_PTR("naindex not defined", __func__, NULL);
1675
0
    if (copyflag != L_CLONE && copyflag != L_COPY)
1676
0
        return (PIXA *)ERROR_PTR("invalid copyflag", __func__, NULL);
1677
1678
0
    n = pixaGetCount(pixas);
1679
0
    pixad = pixaCreate(n);
1680
0
    for (i = 0; i < n; i++) {
1681
0
        numaGetIValue(naindex, i, &index);
1682
0
        pix = pixaGetPix(pixas, index, copyflag);
1683
0
        box = pixaGetBox(pixas, index, copyflag);
1684
0
        pixaAddPix(pixad, pix, L_INSERT);
1685
0
        pixaAddBox(pixad, box, L_INSERT);
1686
0
    }
1687
1688
0
    return pixad;
1689
0
}
1690
1691
1692
/*!
1693
 * \brief   pixaSort2dByIndex()
1694
 *
1695
 * \param[in]    pixas
1696
 * \param[in]    naa       numaa that maps from the new pixaa to the input pixas
1697
 * \param[in]    copyflag  L_CLONE or L_COPY
1698
 * \return  paa sorted, or NULL on error
1699
 */
1700
PIXAA *
1701
pixaSort2dByIndex(PIXA    *pixas,
1702
                  NUMAA   *naa,
1703
                  l_int32  copyflag)
1704
0
{
1705
0
l_int32  pixtot, ntot, i, j, n, nn, index;
1706
0
BOX     *box;
1707
0
NUMA    *na;
1708
0
PIX     *pix;
1709
0
PIXA    *pixa;
1710
0
PIXAA   *paa;
1711
1712
0
    if (!pixas)
1713
0
        return (PIXAA *)ERROR_PTR("pixas not defined", __func__, NULL);
1714
0
    if (!naa)
1715
0
        return (PIXAA *)ERROR_PTR("naindex not defined", __func__, NULL);
1716
1717
        /* Check counts */
1718
0
    ntot = numaaGetNumberCount(naa);
1719
0
    pixtot = pixaGetCount(pixas);
1720
0
    if (ntot != pixtot)
1721
0
        return (PIXAA *)ERROR_PTR("element count mismatch", __func__, NULL);
1722
1723
0
    n = numaaGetCount(naa);
1724
0
    paa = pixaaCreate(n);
1725
0
    for (i = 0; i < n; i++) {
1726
0
        na = numaaGetNuma(naa, i, L_CLONE);
1727
0
        nn = numaGetCount(na);
1728
0
        pixa = pixaCreate(nn);
1729
0
        for (j = 0; j < nn; j++) {
1730
0
            numaGetIValue(na, j, &index);
1731
0
            pix = pixaGetPix(pixas, index, copyflag);
1732
0
            box = pixaGetBox(pixas, index, copyflag);
1733
0
            pixaAddPix(pixa, pix, L_INSERT);
1734
0
            pixaAddBox(pixa, box, L_INSERT);
1735
0
        }
1736
0
        pixaaAddPixa(paa, pixa, L_INSERT);
1737
0
        numaDestroy(&na);
1738
0
    }
1739
1740
0
    return paa;
1741
0
}
1742
1743
1744
/*---------------------------------------------------------------------*
1745
 *                    Pixa and Pixaa range selection                   *
1746
 *---------------------------------------------------------------------*/
1747
/*!
1748
 * \brief   pixaSelectRange()
1749
 *
1750
 * \param[in]    pixas
1751
 * \param[in]    first     use 0 to select from the beginning
1752
 * \param[in]    last      use -1 to select to the end
1753
 * \param[in]    copyflag  L_COPY, L_CLONE
1754
 * \return  pixad, or NULL on error
1755
 *
1756
 * <pre>
1757
 * Notes:
1758
 *      (1) The copyflag specifies what we do with each pix from pixas.
1759
 *          Specifically, L_CLONE inserts a clone into pixad of each
1760
 *          selected pix from pixas.
1761
 * </pre>
1762
 */
1763
PIXA *
1764
pixaSelectRange(PIXA    *pixas,
1765
                l_int32  first,
1766
                l_int32  last,
1767
                l_int32  copyflag)
1768
0
{
1769
0
l_int32  n, npix, i;
1770
0
PIX     *pix;
1771
0
PIXA    *pixad;
1772
1773
0
    if (!pixas)
1774
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1775
0
    if (copyflag != L_COPY && copyflag != L_CLONE)
1776
0
        return (PIXA *)ERROR_PTR("invalid copyflag", __func__, NULL);
1777
0
    n = pixaGetCount(pixas);
1778
0
    first = L_MAX(0, first);
1779
0
    if (last < 0) last = n - 1;
1780
0
    if (first >= n)
1781
0
        return (PIXA *)ERROR_PTR("invalid first", __func__, NULL);
1782
0
    if (last >= n) {
1783
0
        L_WARNING("last = %d is beyond max index = %d; adjusting\n",
1784
0
                  __func__, last, n - 1);
1785
0
        last = n - 1;
1786
0
    }
1787
0
    if (first > last)
1788
0
        return (PIXA *)ERROR_PTR("first > last", __func__, NULL);
1789
1790
0
    npix = last - first + 1;
1791
0
    pixad = pixaCreate(npix);
1792
0
    for (i = first; i <= last; i++) {
1793
0
        pix = pixaGetPix(pixas, i, copyflag);
1794
0
        pixaAddPix(pixad, pix, L_INSERT);
1795
0
    }
1796
0
    return pixad;
1797
0
}
1798
1799
1800
/*!
1801
 * \brief   pixaaSelectRange()
1802
 *
1803
 * \param[in]    paas
1804
 * \param[in]    first    use 0 to select from the beginning
1805
 * \param[in]    last     use -1 to select to the end
1806
 * \param[in]    copyflag L_COPY, L_CLONE
1807
 * \return  paad, or NULL on error
1808
 *
1809
 * <pre>
1810
 * Notes:
1811
 *      (1) The copyflag specifies what we do with each pixa from paas.
1812
 *          Specifically, L_CLONE inserts a clone into paad of each
1813
 *          selected pixa from paas.
1814
 * </pre>
1815
 */
1816
PIXAA *
1817
pixaaSelectRange(PIXAA   *paas,
1818
                 l_int32  first,
1819
                 l_int32  last,
1820
                 l_int32  copyflag)
1821
0
{
1822
0
l_int32  n, npixa, i;
1823
0
PIXA    *pixa;
1824
0
PIXAA   *paad;
1825
1826
0
    if (!paas)
1827
0
        return (PIXAA *)ERROR_PTR("paas not defined", __func__, NULL);
1828
0
    if (copyflag != L_COPY && copyflag != L_CLONE)
1829
0
        return (PIXAA *)ERROR_PTR("invalid copyflag", __func__, NULL);
1830
0
    n = pixaaGetCount(paas, NULL);
1831
0
    first = L_MAX(0, first);
1832
0
    if (last < 0) last = n - 1;
1833
0
    if (first >= n)
1834
0
        return (PIXAA *)ERROR_PTR("invalid first", __func__, NULL);
1835
0
    if (last >= n) {
1836
0
        L_WARNING("last = %d is beyond max index = %d; adjusting\n",
1837
0
                  __func__, last, n - 1);
1838
0
        last = n - 1;
1839
0
    }
1840
0
    if (first > last)
1841
0
        return (PIXAA *)ERROR_PTR("first > last", __func__, NULL);
1842
1843
0
    npixa = last - first + 1;
1844
0
    paad = pixaaCreate(npixa);
1845
0
    for (i = first; i <= last; i++) {
1846
0
        pixa = pixaaGetPixa(paas, i, copyflag);
1847
0
        pixaaAddPixa(paad, pixa, L_INSERT);
1848
0
    }
1849
0
    return paad;
1850
0
}
1851
1852
1853
/*---------------------------------------------------------------------*
1854
 *                        Pixa and Pixaa scaling                       *
1855
 *---------------------------------------------------------------------*/
1856
/*!
1857
 * \brief   pixaaScaleToSize()
1858
 *
1859
 * \param[in]    paas
1860
 * \param[in]    wd    target width; use 0 if using height as target
1861
 * \param[in]    hd    target height; use 0 if using width as target
1862
 * \return  paad, or NULL on error
1863
 *
1864
 * <pre>
1865
 * Notes:
1866
 *      (1) This guarantees that each output scaled image has the
1867
 *          dimension(s) you specify.
1868
 *           ~ To specify the width with isotropic scaling, set %hd = 0.
1869
 *           ~ To specify the height with isotropic scaling, set %wd = 0.
1870
 *           ~ If both %wd and %hd are specified, the image is scaled
1871
 *             (in general, anisotropically) to that size.
1872
 *           ~ It is an error to set both %wd and %hd to 0.
1873
 * </pre>
1874
 */
1875
PIXAA *
1876
pixaaScaleToSize(PIXAA   *paas,
1877
                 l_int32  wd,
1878
                 l_int32  hd)
1879
0
{
1880
0
l_int32  n, i;
1881
0
PIXA    *pixa1, *pixa2;
1882
0
PIXAA   *paad;
1883
1884
0
    if (!paas)
1885
0
        return (PIXAA *)ERROR_PTR("paas not defined", __func__, NULL);
1886
0
    if (wd <= 0 && hd <= 0)
1887
0
        return (PIXAA *)ERROR_PTR("neither wd nor hd > 0", __func__, NULL);
1888
1889
0
    n = pixaaGetCount(paas, NULL);
1890
0
    paad = pixaaCreate(n);
1891
0
    for (i = 0; i < n; i++) {
1892
0
        pixa1 = pixaaGetPixa(paas, i, L_CLONE);
1893
0
        pixa2 = pixaScaleToSize(pixa1, wd, hd);
1894
0
        pixaaAddPixa(paad, pixa2, L_INSERT);
1895
0
        pixaDestroy(&pixa1);
1896
0
    }
1897
0
    return paad;
1898
0
}
1899
1900
1901
/*!
1902
 * \brief   pixaaScaleToSizeVar()
1903
 *
1904
 * \param[in]    paas
1905
 * \param[in]    nawd  [optional] target widths; use NULL if using height
1906
 * \param[in]    nahd  [optional] target height; use NULL if using width
1907
 * \return  paad, or NULL on error
1908
 *
1909
 * <pre>
1910
 * Notes:
1911
 *      (1) This guarantees that the scaled images in each pixa have the
1912
 *          dimension(s) you specify in the numas.
1913
 *           ~ To specify the width with isotropic scaling, set %nahd = NULL.
1914
 *           ~ To specify the height with isotropic scaling, set %nawd = NULL.
1915
 *           ~ If both %nawd and %nahd are specified, the image is scaled
1916
 *             (in general, anisotropically) to that size.
1917
 *           ~ It is an error to set both %nawd and %nahd to NULL.
1918
 *      (2) If either nawd and/or nahd is defined, it must have the same
1919
 *          count as the number of pixa in paas.
1920
 * </pre>
1921
 */
1922
PIXAA *
1923
pixaaScaleToSizeVar(PIXAA  *paas,
1924
                    NUMA   *nawd,
1925
                    NUMA   *nahd)
1926
0
{
1927
0
l_int32  n, i, wd, hd;
1928
0
PIXA    *pixa1, *pixa2;
1929
0
PIXAA   *paad;
1930
1931
0
    if (!paas)
1932
0
        return (PIXAA *)ERROR_PTR("paas not defined", __func__, NULL);
1933
0
    if (!nawd && !nahd)
1934
0
        return (PIXAA *)ERROR_PTR("!nawd && !nahd", __func__, NULL);
1935
1936
0
    n = pixaaGetCount(paas, NULL);
1937
0
    if (nawd && (n != numaGetCount(nawd)))
1938
0
        return (PIXAA *)ERROR_PTR("nawd wrong size", __func__, NULL);
1939
0
    if (nahd && (n != numaGetCount(nahd)))
1940
0
        return (PIXAA *)ERROR_PTR("nahd wrong size", __func__, NULL);
1941
0
    paad = pixaaCreate(n);
1942
0
    for (i = 0; i < n; i++) {
1943
0
        wd = hd = 0;
1944
0
        if (nawd) numaGetIValue(nawd, i, &wd);
1945
0
        if (nahd) numaGetIValue(nahd, i, &hd);
1946
0
        pixa1 = pixaaGetPixa(paas, i, L_CLONE);
1947
0
        pixa2 = pixaScaleToSize(pixa1, wd, hd);
1948
0
        pixaaAddPixa(paad, pixa2, L_INSERT);
1949
0
        pixaDestroy(&pixa1);
1950
0
    }
1951
0
    return paad;
1952
0
}
1953
1954
1955
/*!
1956
 * \brief   pixaScaleToSize()
1957
 *
1958
 * \param[in]    pixas
1959
 * \param[in]    wd    target width; use 0 if using height as target
1960
 * \param[in]    hd    target height; use 0 if using width as target
1961
 * \return  pixad, or NULL on error
1962
 *
1963
 * <pre>
1964
 * Notes:
1965
 *      (1) See pixaaScaleToSize()
1966
 * </pre>
1967
 */
1968
PIXA *
1969
pixaScaleToSize(PIXA    *pixas,
1970
                l_int32  wd,
1971
                l_int32  hd)
1972
0
{
1973
0
l_int32  n, i;
1974
0
PIX     *pix1, *pix2;
1975
0
PIXA    *pixad;
1976
1977
0
    if (!pixas)
1978
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
1979
1980
0
    if (wd <= 0 && hd <= 0)  /* no scaling requested */
1981
0
        return pixaCopy(pixas, L_CLONE);
1982
1983
0
    n = pixaGetCount(pixas);
1984
0
    pixad = pixaCreate(n);
1985
0
    for (i = 0; i < n; i++) {
1986
0
        pix1 = pixaGetPix(pixas, i, L_CLONE);
1987
0
        pix2 = pixScaleToSize(pix1, wd, hd);
1988
0
        pixCopyText(pix2, pix1);
1989
0
        pixaAddPix(pixad, pix2, L_INSERT);
1990
0
        pixDestroy(&pix1);
1991
0
    }
1992
0
    return pixad;
1993
0
}
1994
1995
1996
/*!
1997
 * \brief   pixaScaleToSizeRel()
1998
 *
1999
 * \param[in]    pixas
2000
 * \param[in]    delw   change in width, in pixels; 0 means no change
2001
 * \param[in]    delh   change in height, in pixels; 0 means no change
2002
 * return  pixad, or NULL on error
2003
 *
2004
 * <pre>
2005
 * Notes:
2006
 *      (1) If a requested change in a pix is not possible because
2007
 *          either the requested width or height is <= 0, issue a
2008
 *          warning and return a copy.
2009
 * </pre>
2010
 */
2011
PIXA *
2012
pixaScaleToSizeRel(PIXA    *pixas,
2013
                   l_int32  delw,
2014
                   l_int32  delh)
2015
0
{
2016
0
l_int32  n, i;
2017
0
PIX     *pix1, *pix2;
2018
0
PIXA    *pixad;
2019
2020
0
    if (!pixas)
2021
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2022
2023
0
    n = pixaGetCount(pixas);
2024
0
    pixad = pixaCreate(n);
2025
0
    for (i = 0; i < n; i++) {
2026
0
        pix1 = pixaGetPix(pixas, i, L_CLONE);
2027
0
        pix2 = pixScaleToSizeRel(pix1, delw, delh);
2028
0
        if (pix2) {
2029
0
            pixaAddPix(pixad, pix2, L_INSERT);
2030
0
        } else {
2031
0
            L_WARNING("relative scale to size failed; use a copy\n", __func__);
2032
0
            pixaAddPix(pixad, pix1, L_COPY);
2033
0
        }
2034
0
        pixDestroy(&pix1);
2035
0
    }
2036
0
    return pixad;
2037
0
}
2038
2039
2040
/*!
2041
 * \brief   pixaScale()
2042
 *
2043
 * \param[in]    pixas
2044
 * \param[in]    scalex
2045
 * \param[in]    scaley
2046
 * \return  pixad, or NULL on error
2047
 *
2048
 * <pre>
2049
 * Notes:
2050
 *      (1) If pixas has a full boxes, it is scaled as well.
2051
 * </pre>
2052
 */
2053
PIXA *
2054
pixaScale(PIXA      *pixas,
2055
          l_float32  scalex,
2056
          l_float32  scaley)
2057
0
{
2058
0
l_int32  i, n, nb;
2059
0
BOXA    *boxa1, *boxa2;
2060
0
PIX     *pix1, *pix2;
2061
0
PIXA    *pixad;
2062
2063
0
    if (!pixas)
2064
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2065
0
    if (scalex <= 0.0 || scaley <= 0.0)
2066
0
        return (PIXA *)ERROR_PTR("invalid scaling parameters", __func__, NULL);
2067
2068
0
    n = pixaGetCount(pixas);
2069
0
    pixad = pixaCreate(n);
2070
0
    for (i = 0; i < n; i++) {
2071
0
        pix1 = pixaGetPix(pixas, i, L_CLONE);
2072
0
        pix2 = pixScale(pix1, scalex, scaley);
2073
0
        pixCopyText(pix2, pix1);
2074
0
        pixaAddPix(pixad, pix2, L_INSERT);
2075
0
        pixDestroy(&pix1);
2076
0
    }
2077
2078
0
    boxa1 = pixaGetBoxa(pixas, L_CLONE);
2079
0
    nb = boxaGetCount(boxa1);
2080
0
    if (nb == n) {
2081
0
        boxa2 = boxaTransform(boxa1, 0, 0, scalex, scaley);
2082
0
        pixaSetBoxa(pixad, boxa2, L_INSERT);
2083
0
    }
2084
0
    boxaDestroy(&boxa1);
2085
0
    return pixad;
2086
0
}
2087
2088
2089
/*!
2090
 * \brief   pixaScaleBySampling()
2091
 *
2092
 * \param[in]    pixas
2093
 * \param[in]    scalex
2094
 * \param[in]    scaley
2095
 * \return  pixad, or NULL on error
2096
 *
2097
 * <pre>
2098
 * Notes:
2099
 *      (1) If pixas has a full boxes, it is scaled as well.
2100
 * </pre>
2101
 */
2102
PIXA *
2103
pixaScaleBySampling(PIXA      *pixas,
2104
                    l_float32  scalex,
2105
                    l_float32  scaley)
2106
0
{
2107
0
l_int32  i, n, nb;
2108
0
BOXA    *boxa1, *boxa2;
2109
0
PIX     *pix1, *pix2;
2110
0
PIXA    *pixad;
2111
2112
0
    if (!pixas)
2113
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2114
0
    if (scalex <= 0.0 || scaley <= 0.0)
2115
0
        return (PIXA *)ERROR_PTR("invalid scaling parameters", __func__, NULL);
2116
2117
0
    n = pixaGetCount(pixas);
2118
0
    pixad = pixaCreate(n);
2119
0
    for (i = 0; i < n; i++) {
2120
0
        pix1 = pixaGetPix(pixas, i, L_CLONE);
2121
0
        pix2 = pixScaleBySampling(pix1, scalex, scaley);
2122
0
        pixCopyText(pix2, pix1);
2123
0
        pixaAddPix(pixad, pix2, L_INSERT);
2124
0
        pixDestroy(&pix1);
2125
0
    }
2126
2127
0
    boxa1 = pixaGetBoxa(pixas, L_CLONE);
2128
0
    nb = boxaGetCount(boxa1);
2129
0
    if (nb == n) {
2130
0
        boxa2 = boxaTransform(boxa1, 0, 0, scalex, scaley);
2131
0
        pixaSetBoxa(pixad, boxa2, L_INSERT);
2132
0
    }
2133
0
    boxaDestroy(&boxa1);
2134
0
    return pixad;
2135
0
}
2136
2137
2138
/*---------------------------------------------------------------------*
2139
 *                     Pixa rotation and translation                   *
2140
 *---------------------------------------------------------------------*/
2141
/*!
2142
 * \brief   pixaRotate()
2143
 *
2144
 * \param[in]    pixas    1, 2, 4, 8, 32 bpp rgb
2145
 * \param[in]    angle    rotation angle in radians; clockwise is positive
2146
 * \param[in]    type     L_ROTATE_AREA_MAP, L_ROTATE_SHEAR, L_ROTATE_SAMPLING
2147
 * \param[in]    incolor  L_BRING_IN_WHITE, L_BRING_IN_BLACK
2148
 * \param[in]    width    original width; use 0 to avoid embedding
2149
 * \param[in]    height   original height; use 0 to avoid embedding
2150
 * \return  pixad, or NULL on error
2151
 *
2152
 * <pre>
2153
 * Notes:
2154
 *      (1) Each pix is rotated about its center.  See pixRotate() for details.
2155
 *      (2) The boxa array is copied.  Why is it not rotated?
2156
 *          If a boxa exists, the array of boxes is in 1-to-1
2157
 *          correspondence with the array of pix, and each box typically
2158
 *          represents the location of the pix relative to an image from
2159
 *          which it has been extracted.  Like the pix, we could rotate
2160
 *          each box around its center, and then generate a box that
2161
 *          contains all four corners, as is done in boxaRotate(), but
2162
 *          this seems unnecessary.
2163
 * </pre>
2164
 */
2165
PIXA *
2166
pixaRotate(PIXA      *pixas,
2167
           l_float32  angle,
2168
           l_int32    type,
2169
           l_int32    incolor,
2170
           l_int32    width,
2171
           l_int32    height)
2172
0
{
2173
0
l_int32  i, n;
2174
0
BOXA    *boxa;
2175
0
PIX     *pixs, *pixd;
2176
0
PIXA    *pixad;
2177
2178
0
    if (!pixas)
2179
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2180
0
    if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP &&
2181
0
        type != L_ROTATE_SAMPLING)
2182
0
        return (PIXA *)ERROR_PTR("invalid type", __func__, NULL);
2183
0
    if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
2184
0
        return (PIXA *)ERROR_PTR("invalid incolor", __func__, NULL);
2185
0
    if (L_ABS(angle) < MinAngleToRotate)
2186
0
        return pixaCopy(pixas, L_COPY);
2187
2188
0
    n = pixaGetCount(pixas);
2189
0
    if ((pixad = pixaCreate(n)) == NULL)
2190
0
        return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL);
2191
0
    boxa = pixaGetBoxa(pixad, L_COPY);
2192
0
    pixaSetBoxa(pixad, boxa, L_INSERT);
2193
0
    for (i = 0; i < n; i++) {
2194
0
        if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) {
2195
0
            pixaDestroy(&pixad);
2196
0
            return (PIXA *)ERROR_PTR("pixs not found", __func__, NULL);
2197
0
        }
2198
0
        pixd = pixRotate(pixs, angle, type, incolor, width, height);
2199
0
        pixaAddPix(pixad, pixd, L_INSERT);
2200
0
        pixDestroy(&pixs);
2201
0
    }
2202
2203
0
    return pixad;
2204
0
}
2205
2206
2207
/*!
2208
 * \brief   pixaRotateOrth()
2209
 *
2210
 * \param[in]    pixas
2211
 * \param[in]    rotation    0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg;
2212
 *                           all rotations are clockwise
2213
 * \return  pixad, or NULL on error
2214
 *
2215
 * <pre>
2216
 * Notes:
2217
 *      (1) Rotates each pix in the pixa.  Rotates and saves the boxes in
2218
 *          the boxa if the boxa is full.
2219
 * </pre>
2220
 */
2221
PIXA *
2222
pixaRotateOrth(PIXA    *pixas,
2223
               l_int32  rotation)
2224
0
{
2225
0
l_int32  i, n, nb, w, h;
2226
0
BOX     *boxs, *boxd;
2227
0
PIX     *pixs, *pixd;
2228
0
PIXA    *pixad;
2229
2230
0
    if (!pixas)
2231
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2232
0
    if (rotation < 0 || rotation > 3)
2233
0
        return (PIXA *)ERROR_PTR("rotation not in {0,1,2,3}", __func__, NULL);
2234
0
    if (rotation == 0)
2235
0
        return pixaCopy(pixas, L_COPY);
2236
2237
0
    n = pixaGetCount(pixas);
2238
0
    nb = pixaGetBoxaCount(pixas);
2239
0
    if ((pixad = pixaCreate(n)) == NULL)
2240
0
        return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL);
2241
0
    for (i = 0; i < n; i++) {
2242
0
        if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) {
2243
0
            pixaDestroy(&pixad);
2244
0
            return (PIXA *)ERROR_PTR("pixs not found", __func__, NULL);
2245
0
        }
2246
0
        pixd = pixRotateOrth(pixs, rotation);
2247
0
        pixaAddPix(pixad, pixd, L_INSERT);
2248
0
        if (n == nb) {
2249
0
            boxs = pixaGetBox(pixas, i, L_COPY);
2250
0
            pixGetDimensions(pixs, &w, &h, NULL);
2251
0
            boxd = boxRotateOrth(boxs, w, h, rotation);
2252
0
            pixaAddBox(pixad, boxd, L_INSERT);
2253
0
            boxDestroy(&boxs);
2254
0
        }
2255
0
        pixDestroy(&pixs);
2256
0
    }
2257
2258
0
    return pixad;
2259
0
}
2260
2261
2262
/*!
2263
 * \brief   pixaTranslate()
2264
 *
2265
 * \param[in]    pixas
2266
 * \param[in]    hshift   horizontal shift; hshift > 0 is to right
2267
 * \param[in]    vshift   vertical shift; vshift > 0 is down
2268
 * \param[in]    incolor  L_BRING_IN_WHITE, L_BRING_IN_BLACK
2269
 * \return  pixad, or NULL on error.
2270
 */
2271
PIXA *
2272
pixaTranslate(PIXA    *pixas,
2273
              l_int32  hshift,
2274
              l_int32  vshift,
2275
              l_int32  incolor)
2276
0
{
2277
0
l_int32  i, n, nb;
2278
0
BOXA    *boxas, *boxad;
2279
0
PIX     *pixs, *pixd;
2280
0
PIXA    *pixad;
2281
2282
0
    if (!pixas)
2283
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2284
0
    if (hshift == 0 && vshift == 0)
2285
0
        return pixaCopy(pixas, L_COPY);
2286
2287
0
    n = pixaGetCount(pixas);
2288
0
    nb = pixaGetBoxaCount(pixas);
2289
0
    if ((pixad = pixaCreate(n)) == NULL)
2290
0
        return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL);
2291
0
    for (i = 0; i < n; i++) {
2292
0
        if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) {
2293
0
            pixaDestroy(&pixad);
2294
0
            return (PIXA *)ERROR_PTR("pixs not found", __func__, NULL);
2295
0
        }
2296
0
        pixd = pixTranslate(NULL, pixs, hshift, vshift, incolor);
2297
0
        pixaAddPix(pixad, pixd, L_INSERT);
2298
0
        pixDestroy(&pixs);
2299
0
    }
2300
0
    if (n == nb) {
2301
0
        boxas = pixaGetBoxa(pixas, L_CLONE);
2302
0
        boxad = boxaTransform(boxas, hshift, vshift, 1.0, 1.0);
2303
0
        pixaSetBoxa(pixad, boxad, L_INSERT);
2304
0
        boxaDestroy(&boxas);
2305
0
    }
2306
2307
0
    return pixad;
2308
0
}
2309
2310
2311
/*---------------------------------------------------------------------*
2312
 *                        Miscellaneous functions                      *
2313
 *---------------------------------------------------------------------*/
2314
/*!
2315
 * \brief   pixaAddBorderGeneral()
2316
 *
2317
 * \param[in]    pixad    can be null or equal to pixas
2318
 * \param[in]    pixas    containing pix of all depths; colormap ok
2319
 * \param[in]    left, right, top, bot     number of pixels added
2320
 * \param[in]    val      value of added border pixels
2321
 * \return  pixad with border added to each pix, including on error
2322
 *
2323
 * <pre>
2324
 * Notes:
2325
 *      (1) For binary images:
2326
 *             white:  val = 0
2327
 *             black:  val = 1
2328
 *          For grayscale images:
2329
 *             white:  val = 2 ** d - 1
2330
 *             black:  val = 0
2331
 *          For rgb color images:
2332
 *             white:  val = 0xffffff00
2333
 *             black:  val = 0
2334
 *          For colormapped images, use 'index' found this way:
2335
 *             white: pixcmapGetRankIntensity(cmap, 1.0, &index);
2336
 *             black: pixcmapGetRankIntensity(cmap, 0.0, &index);
2337
 *      (2) For in-place replacement of each pix with a bordered version,
2338
 *          use %pixad = %pixas.  To make a new pixa, use %pixad = NULL.
2339
 *      (3) In both cases, the boxa has sides adjusted as if it were
2340
 *          expanded by the border.
2341
 * </pre>
2342
 */
2343
PIXA *
2344
pixaAddBorderGeneral(PIXA     *pixad,
2345
                     PIXA     *pixas,
2346
                     l_int32   left,
2347
                     l_int32   right,
2348
                     l_int32   top,
2349
                     l_int32   bot,
2350
                     l_uint32  val)
2351
0
{
2352
0
l_int32  i, n, nbox;
2353
0
BOX     *box;
2354
0
BOXA    *boxad;
2355
0
PIX     *pixs, *pixd;
2356
2357
0
    if (!pixas)
2358
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, pixad);
2359
0
    if (left < 0 || right < 0 || top < 0 || bot < 0)
2360
0
        return (PIXA *)ERROR_PTR("negative border added!", __func__, pixad);
2361
0
    if (pixad && (pixad != pixas))
2362
0
        return (PIXA *)ERROR_PTR("pixad defined but != pixas", __func__, pixad);
2363
2364
0
    n = pixaGetCount(pixas);
2365
0
    if (!pixad)
2366
0
        pixad = pixaCreate(n);
2367
0
    for (i = 0; i < n; i++) {
2368
0
        pixs = pixaGetPix(pixas, i, L_CLONE);
2369
0
        pixd = pixAddBorderGeneral(pixs, left, right, top, bot, val);
2370
0
        if (pixad == pixas)  /* replace */
2371
0
            pixaReplacePix(pixad, i, pixd, NULL);
2372
0
        else
2373
0
            pixaAddPix(pixad, pixd, L_INSERT);
2374
0
        pixDestroy(&pixs);
2375
0
    }
2376
2377
0
    nbox = pixaGetBoxaCount(pixas);
2378
0
    boxad = pixaGetBoxa(pixad, L_CLONE);
2379
0
    for (i = 0; i < nbox; i++) {
2380
0
        if ((box = pixaGetBox(pixas, i, L_COPY)) == NULL) {
2381
0
            L_WARNING("box %d not found\n", __func__, i);
2382
0
            break;
2383
0
        }
2384
0
        boxAdjustSides(box, box, -left, right, -top, bot);
2385
0
        if (pixad == pixas)  /* replace */
2386
0
            boxaReplaceBox(boxad, i, box);
2387
0
        else
2388
0
            boxaAddBox(boxad, box, L_INSERT);
2389
0
    }
2390
0
    boxaDestroy(&boxad);
2391
2392
0
    return pixad;
2393
0
}
2394
2395
2396
/*!
2397
 * \brief   pixaaFlattenToPixa()
2398
 *
2399
 * \param[in]    paa
2400
 * \param[out]   pnaindex  [optional] the pixa index in the pixaa
2401
 * \param[in]    copyflag  L_COPY or L_CLONE
2402
 * \return  pixa, or NULL on error
2403
 *
2404
 * <pre>
2405
 * Notes:
2406
 *      (1) This 'flattens' the pixaa to a pixa, taking the pix in
2407
 *          order in the first pixa, then the second, etc.
2408
 *      (2) If &naindex is defined, we generate a Numa that gives, for
2409
 *          each pix in the pixaa, the index of the pixa to which it belongs.
2410
 * </pre>
2411
 */
2412
PIXA *
2413
pixaaFlattenToPixa(PIXAA   *paa,
2414
                   NUMA   **pnaindex,
2415
                   l_int32  copyflag)
2416
0
{
2417
0
l_int32  i, j, m, mb, n;
2418
0
BOX     *box;
2419
0
NUMA    *naindex = NULL;
2420
0
PIX     *pix;
2421
0
PIXA    *pixa, *pixat;
2422
2423
0
    if (pnaindex) *pnaindex = NULL;
2424
0
    if (!paa)
2425
0
        return (PIXA *)ERROR_PTR("paa not defined", __func__, NULL);
2426
0
    if (copyflag != L_COPY && copyflag != L_CLONE)
2427
0
        return (PIXA *)ERROR_PTR("invalid copyflag", __func__, NULL);
2428
2429
0
    if (pnaindex) {
2430
0
        naindex = numaCreate(0);
2431
0
        *pnaindex = naindex;
2432
0
    }
2433
2434
0
    n = pixaaGetCount(paa, NULL);
2435
0
    pixa = pixaCreate(n);
2436
0
    for (i = 0; i < n; i++) {
2437
0
        pixat = pixaaGetPixa(paa, i, L_CLONE);
2438
0
        m = pixaGetCount(pixat);
2439
0
        mb = pixaGetBoxaCount(pixat);
2440
0
        for (j = 0; j < m; j++) {
2441
0
            pix = pixaGetPix(pixat, j, copyflag);
2442
0
            pixaAddPix(pixa, pix, L_INSERT);
2443
0
            if (j < mb) {
2444
0
                box = pixaGetBox(pixat, j, copyflag);
2445
0
                pixaAddBox(pixa, box, L_INSERT);
2446
0
            }
2447
0
            if (pnaindex)
2448
0
                numaAddNumber(naindex, i);  /* save 'row' number */
2449
0
        }
2450
0
        pixaDestroy(&pixat);
2451
0
    }
2452
2453
0
    return pixa;
2454
0
}
2455
2456
2457
/*!
2458
 * \brief   pixaaSizeRange()
2459
 *
2460
 * \param[in]    paa
2461
 * \param[out]   pminw, pminh, pmaxw, pmaxh   [optional] range of
2462
 *                                            dimensions of all boxes
2463
 * \return  0 if OK, 1 on error
2464
 */
2465
l_ok
2466
pixaaSizeRange(PIXAA    *paa,
2467
               l_int32  *pminw,
2468
               l_int32  *pminh,
2469
               l_int32  *pmaxw,
2470
               l_int32  *pmaxh)
2471
0
{
2472
0
l_int32  minw, minh, maxw, maxh, minpw, minph, maxpw, maxph, i, n;
2473
0
PIXA    *pixa;
2474
2475
0
    if (pminw) *pminw = 0;
2476
0
    if (pminh) *pminh = 0;
2477
0
    if (pmaxw) *pmaxw = 0;
2478
0
    if (pmaxh) *pmaxh = 0;
2479
0
    if (!paa)
2480
0
        return ERROR_INT("paa not defined", __func__, 1);
2481
0
    if (!pminw && !pmaxw && !pminh && !pmaxh)
2482
0
        return ERROR_INT("no data can be returned", __func__, 1);
2483
2484
0
    minw = minh = 100000000;
2485
0
    maxw = maxh = 0;
2486
0
    n = pixaaGetCount(paa, NULL);
2487
0
    for (i = 0; i < n; i++) {
2488
0
        pixa = pixaaGetPixa(paa, i, L_CLONE);
2489
0
        pixaSizeRange(pixa, &minpw, &minph, &maxpw, &maxph);
2490
0
        if (minpw < minw)
2491
0
            minw = minpw;
2492
0
        if (minph < minh)
2493
0
            minh = minph;
2494
0
        if (maxpw > maxw)
2495
0
            maxw = maxpw;
2496
0
        if (maxph > maxh)
2497
0
            maxh = maxph;
2498
0
        pixaDestroy(&pixa);
2499
0
    }
2500
2501
0
    if (pminw) *pminw = minw;
2502
0
    if (pminh) *pminh = minh;
2503
0
    if (pmaxw) *pmaxw = maxw;
2504
0
    if (pmaxh) *pmaxh = maxh;
2505
0
    return 0;
2506
0
}
2507
2508
2509
/*!
2510
 * \brief   pixaSizeRange()
2511
 *
2512
 * \param[in]    pixa
2513
 * \param[out]   pminw, pminh, pmaxw, pmaxh   [optional] range of
2514
 *                                            dimensions of pix in the array
2515
 * \return  0 if OK, 1 on error
2516
 */
2517
l_ok
2518
pixaSizeRange(PIXA     *pixa,
2519
              l_int32  *pminw,
2520
              l_int32  *pminh,
2521
              l_int32  *pmaxw,
2522
              l_int32  *pmaxh)
2523
0
{
2524
0
l_int32  minw, minh, maxw, maxh, i, n, w, h;
2525
0
PIX     *pix;
2526
2527
0
    if (pminw) *pminw = 0;
2528
0
    if (pminh) *pminh = 0;
2529
0
    if (pmaxw) *pmaxw = 0;
2530
0
    if (pmaxh) *pmaxh = 0;
2531
0
    if (!pixa)
2532
0
        return ERROR_INT("pixa not defined", __func__, 1);
2533
0
    if (!pminw && !pmaxw && !pminh && !pmaxh)
2534
0
        return ERROR_INT("no data can be returned", __func__, 1);
2535
2536
0
    minw = minh = 1000000;
2537
0
    maxw = maxh = 0;
2538
0
    n = pixaGetCount(pixa);
2539
0
    for (i = 0; i < n; i++) {
2540
0
        pix = pixaGetPix(pixa, i, L_CLONE);
2541
0
        w = pixGetWidth(pix);
2542
0
        h = pixGetHeight(pix);
2543
0
        if (w < minw)
2544
0
            minw = w;
2545
0
        if (h < minh)
2546
0
            minh = h;
2547
0
        if (w > maxw)
2548
0
            maxw = w;
2549
0
        if (h > maxh)
2550
0
            maxh = h;
2551
0
        pixDestroy(&pix);
2552
0
    }
2553
2554
0
    if (pminw) *pminw = minw;
2555
0
    if (pminh) *pminh = minh;
2556
0
    if (pmaxw) *pmaxw = maxw;
2557
0
    if (pmaxh) *pmaxh = maxh;
2558
2559
0
    return 0;
2560
0
}
2561
2562
2563
/*!
2564
 * \brief   pixaClipToPix()
2565
 *
2566
 * \param[in]    pixas
2567
 * \param[in]    pixs
2568
 * \return  pixad, or NULL on error
2569
 *
2570
 * <pre>
2571
 * Notes:
2572
 *      (1) This is intended for use in situations where pixas
2573
 *          was originally generated from the input pixs.
2574
 *      (2) Returns a pixad where each pix in pixas is ANDed
2575
 *          with its associated region of the input pixs.  This
2576
 *          region is specified by the the box that is associated
2577
 *          with the pix.
2578
 *      (3) In a typical application of this function, pixas has
2579
 *          a set of region masks, so this generates a pixa of
2580
 *          the parts of pixs that correspond to each region
2581
 *          mask component, along with the bounding box for
2582
 *          the region.
2583
 * </pre>
2584
 */
2585
PIXA *
2586
pixaClipToPix(PIXA  *pixas,
2587
              PIX   *pixs)
2588
0
{
2589
0
l_int32  i, n;
2590
0
BOX     *box;
2591
0
PIX     *pix, *pixc;
2592
0
PIXA    *pixad;
2593
2594
0
    if (!pixas)
2595
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2596
0
    if (!pixs)
2597
0
        return (PIXA *)ERROR_PTR("pixs not defined", __func__, NULL);
2598
2599
0
    n = pixaGetCount(pixas);
2600
0
    if ((pixad = pixaCreate(n)) == NULL)
2601
0
        return (PIXA *)ERROR_PTR("pixad not made", __func__, NULL);
2602
2603
0
    for (i = 0; i < n; i++) {
2604
0
        pix = pixaGetPix(pixas, i, L_CLONE);
2605
0
        box = pixaGetBox(pixas, i, L_COPY);
2606
0
        pixc = pixClipRectangle(pixs, box, NULL);
2607
0
        pixAnd(pixc, pixc, pix);
2608
0
        pixaAddPix(pixad, pixc, L_INSERT);
2609
0
        pixaAddBox(pixad, box, L_INSERT);
2610
0
        pixDestroy(&pix);
2611
0
    }
2612
2613
0
    return pixad;
2614
0
}
2615
2616
2617
/*!
2618
 * \brief   pixaClipToForeground()
2619
 *
2620
 * \param[in]    pixas
2621
 * \param[out]   ppixad   [optional] pixa of clipped pix returned
2622
 * \param[out]   pboxa    [optional] clipping boxes returned
2623
 * \return  0 if OK, 1 on error
2624
 *
2625
 * <pre>
2626
 * Notes:
2627
 *      (1) At least one of [&pixd, &boxa] must be specified.
2628
 *      (2) Any pix with no fg pixels is skipped.
2629
 *      (3) See pixClipToForeground().
2630
 * </pre>
2631
 */
2632
l_ok
2633
pixaClipToForeground(PIXA   *pixas,
2634
                     PIXA  **ppixad,
2635
                     BOXA  **pboxa)
2636
0
{
2637
0
l_int32  i, n;
2638
0
BOX     *box1;
2639
0
PIX     *pix1, *pix2;
2640
2641
0
    if (ppixad) *ppixad = NULL;
2642
0
    if (pboxa) *pboxa = NULL;
2643
0
    if (!pixas)
2644
0
        return ERROR_INT("pixas not defined", __func__, 1);
2645
0
    if (!ppixad && !pboxa)
2646
0
        return ERROR_INT("no output requested", __func__, 1);
2647
2648
0
    n = pixaGetCount(pixas);
2649
0
    if (ppixad) *ppixad = pixaCreate(n);
2650
0
    if (pboxa) *pboxa = boxaCreate(n);
2651
0
    for (i = 0; i < n; i++) {
2652
0
        pix1 = pixaGetPix(pixas, i, L_CLONE);
2653
0
        pixClipToForeground(pix1, &pix2, &box1);
2654
0
        pixDestroy(&pix1);
2655
0
        if (ppixad)
2656
0
            pixaAddPix(*ppixad, pix2, L_INSERT);
2657
0
        else
2658
0
            pixDestroy(&pix2);
2659
0
        if (pboxa)
2660
0
            boxaAddBox(*pboxa, box1, L_INSERT);
2661
0
        else
2662
0
            boxDestroy(&box1);
2663
0
    }
2664
2665
0
    return 0;
2666
0
}
2667
2668
2669
/*!
2670
 * \brief   pixaGetRenderingDepth()
2671
 *
2672
 * \param[in]    pixa
2673
 * \param[out]   pdepth   depth required to render if all colormaps are removed
2674
 * \return  0 if OK; 1 on error
2675
 *
2676
 * <pre>
2677
 * Notes:
2678
 *      (1) Any pix with 16 bpp will be considered as having 8 bpp.
2679
 *          If all pix have bpp = 1, this returns 1.
2680
 *          If any pix has color (either rgb or a colormap with color),
2681
 *          this return 32.
2682
 *          Otherwise, this returns the maximum of 8 and the max depth
2683
 *          of all the pix.
2684
 *      (2) This can be used to allow lossless rendering onto a single pix.
2685
 * </pre>
2686
 */
2687
l_ok
2688
pixaGetRenderingDepth(PIXA     *pixa,
2689
                      l_int32  *pdepth)
2690
0
{
2691
0
l_int32  hascolor, maxdepth;
2692
2693
0
    if (!pdepth)
2694
0
        return ERROR_INT("&depth not defined", __func__, 1);
2695
0
    *pdepth = 0;
2696
0
    if (!pixa)
2697
0
        return ERROR_INT("pixa not defined", __func__, 1);
2698
2699
0
    pixaHasColor(pixa, &hascolor);
2700
0
    if (hascolor) {
2701
0
        *pdepth = 32;
2702
0
        return 0;
2703
0
    }
2704
2705
0
    pixaGetDepthInfo(pixa, &maxdepth, NULL);
2706
0
    if (maxdepth == 1)
2707
0
        *pdepth = 1;
2708
0
    else  /* 2, 4, 8 or 16 */
2709
0
        *pdepth = 8;
2710
0
    return 0;
2711
0
}
2712
2713
2714
/*!
2715
 * \brief   pixaHasColor()
2716
 *
2717
 * \param[in]    pixa
2718
 * \param[out]   phascolor   1 if any pix is rgb or has a colormap with color;
2719
 *                           0 otherwise
2720
 * \return  0 if OK; 1 on error
2721
 */
2722
l_ok
2723
pixaHasColor(PIXA     *pixa,
2724
             l_int32  *phascolor)
2725
0
{
2726
0
l_int32   i, n, hascolor, d;
2727
0
PIX      *pix;
2728
0
PIXCMAP  *cmap;
2729
2730
0
    if (!phascolor)
2731
0
        return ERROR_INT("&hascolor not defined", __func__, 1);
2732
0
    *phascolor = 0;
2733
0
    if (!pixa)
2734
0
        return ERROR_INT("pixa not defined", __func__, 1);
2735
2736
0
    n = pixaGetCount(pixa);
2737
0
    hascolor = 0;
2738
0
    for (i = 0; i < n; i++) {
2739
0
        pix = pixaGetPix(pixa, i, L_CLONE);
2740
0
        if ((cmap = pixGetColormap(pix)) != NULL)
2741
0
            pixcmapHasColor(cmap, &hascolor);
2742
0
        d = pixGetDepth(pix);
2743
0
        pixDestroy(&pix);
2744
0
        if (d == 32 || hascolor == 1) {
2745
0
            *phascolor = 1;
2746
0
            break;
2747
0
        }
2748
0
    }
2749
2750
0
    return 0;
2751
0
}
2752
2753
2754
/*!
2755
 * \brief   pixaAnyColormaps()
2756
 *
2757
 * \param[in]    pixa
2758
 * \param[out]   phascmap    1 if any pix has a colormap; 0 otherwise
2759
 * \return  0 if OK; 1 on error
2760
 */
2761
l_ok
2762
pixaAnyColormaps(PIXA     *pixa,
2763
                 l_int32  *phascmap)
2764
0
{
2765
0
l_int32   i, n;
2766
0
PIX      *pix;
2767
0
PIXCMAP  *cmap;
2768
2769
0
    if (!phascmap)
2770
0
        return ERROR_INT("&hascmap not defined", __func__, 1);
2771
0
    *phascmap = 0;
2772
0
    if (!pixa)
2773
0
        return ERROR_INT("pixa not defined", __func__, 1);
2774
2775
0
    n = pixaGetCount(pixa);
2776
0
    for (i = 0; i < n; i++) {
2777
0
        pix = pixaGetPix(pixa, i, L_CLONE);
2778
0
        cmap = pixGetColormap(pix);
2779
0
        pixDestroy(&pix);
2780
0
        if (cmap) {
2781
0
            *phascmap = 1;
2782
0
            return 0;
2783
0
        }
2784
0
    }
2785
2786
0
    return 0;
2787
0
}
2788
2789
2790
/*!
2791
 * \brief   pixaGetDepthInfo()
2792
 *
2793
 * \param[in]    pixa
2794
 * \param[out]   pmaxdepth  [optional] max pixel depth of pix in pixa
2795
 * \param[out]   psame      [optional] true if all depths are equal
2796
 * \return  0 if OK; 1 on error
2797
 */
2798
l_ok
2799
pixaGetDepthInfo(PIXA     *pixa,
2800
                 l_int32  *pmaxdepth,
2801
                 l_int32  *psame)
2802
0
{
2803
0
l_int32  i, n, d, d0;
2804
0
l_int32  maxd, same;  /* depth info */
2805
2806
0
    if (pmaxdepth) *pmaxdepth = 0;
2807
0
    if (psame) *psame = TRUE;
2808
0
    if (!pmaxdepth && !psame) return 0;
2809
0
    if (!pixa)
2810
0
        return ERROR_INT("pixa not defined", __func__, 1);
2811
0
    if ((n = pixaGetCount(pixa)) == 0)
2812
0
        return ERROR_INT("pixa is empty", __func__, 1);
2813
2814
0
    same = TRUE;
2815
0
    maxd = 0;
2816
0
    for (i = 0; i < n; i++) {
2817
0
        pixaGetPixDimensions(pixa, i, NULL, NULL, &d);
2818
0
        if (i == 0)
2819
0
            d0 = d;
2820
0
        else if (d != d0)
2821
0
            same = FALSE;
2822
0
        if (d > maxd) maxd = d;
2823
0
    }
2824
2825
0
    if (pmaxdepth) *pmaxdepth = maxd;
2826
0
    if (psame) *psame = same;
2827
0
    return 0;
2828
0
}
2829
2830
2831
/*!
2832
 * \brief   pixaConvertToSameDepth()
2833
 *
2834
 * \param[in]    pixas
2835
 * \return  pixad, or NULL on error
2836
 *
2837
 * <pre>
2838
 * Notes:
2839
 *      (1) Any pix with 16 bpp will be converted to 8 bpp.
2840
 *          If all pix have bpp = 1, the output depth will be 1.
2841
 *          If any pix has color (either rgb or a colormap with color),
2842
 *          the output depth will be 32.
2843
 *          Otherwise, the output depth is the maximum of 8 and the
2844
 *          the max depth of all the pix.
2845
 *      (2) This can be used to allow lossless rendering onto
2846
 *          a single pix. (Except: 16 bpp gets converted to 8.)
2847
 * </pre>
2848
 */
2849
PIXA *
2850
pixaConvertToSameDepth(PIXA  *pixas)
2851
0
{
2852
0
l_int32  i, n, depth, same, hascmap, maxdepth;
2853
0
BOXA    *boxa;
2854
0
PIX     *pix1, *pix2;
2855
0
PIXA    *pixa1, *pixad;
2856
2857
0
    if (!pixas)
2858
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2859
0
    if ((n = pixaGetCount(pixas)) == 0)
2860
0
        return (PIXA *)ERROR_PTR("no components", __func__, NULL);
2861
2862
2863
        /* Remove colormaps if necessary */
2864
0
    pixaGetRenderingDepth(pixas, &depth);
2865
0
    pixaAnyColormaps(pixas, &hascmap);
2866
0
    if (hascmap) {
2867
0
        pixa1 = pixaCreate(n);
2868
0
        for (i = 0; i < n; i++) {
2869
0
            pix1 = pixaGetPix(pixas, i, L_CLONE);
2870
0
            if (depth == 32)
2871
0
                pix2 = pixConvertTo32(pix1);
2872
0
            else  /* depth = 8 */
2873
0
                pix2 = pixConvertTo8(pix1, 0);
2874
0
            pixaAddPix(pixa1, pix2, L_INSERT);
2875
0
            pixDestroy(&pix1);
2876
0
        }
2877
0
    } else {
2878
0
        pixa1 = pixaCopy(pixas, L_CLONE);
2879
0
    }
2880
2881
0
    pixaGetDepthInfo(pixa1, &maxdepth, &same);
2882
0
    if (!same) {  /* at least one pix has depth < maxdepth */
2883
0
        pixad = pixaCreate(n);
2884
0
        for (i = 0; i < n; i++) {
2885
0
            pix1 = pixaGetPix(pixa1, i, L_CLONE);
2886
0
            if (maxdepth <= 16)
2887
0
                pix2 = pixConvertTo8(pix1, 0);
2888
0
            else
2889
0
                pix2 = pixConvertTo32(pix1);
2890
0
            pixaAddPix(pixad, pix2, L_INSERT);
2891
0
            pixDestroy(&pix1);
2892
0
        }
2893
0
    } else {
2894
0
        pixad = pixaCopy(pixa1, L_CLONE);
2895
0
    }
2896
2897
0
    boxa = pixaGetBoxa(pixas, L_COPY);
2898
0
    pixaSetBoxa(pixad, boxa, L_INSERT);
2899
0
    pixaDestroy(&pixa1);
2900
0
    return pixad;
2901
0
}
2902
2903
2904
/*!
2905
 * \brief   pixaConvertToGivenDepth()
2906
 *
2907
 * \param[in]    pixas
2908
 * \param[in]    depth    specify either 8 or 32 bpp
2909
 * \return  pixad, or NULL on error
2910
 *
2911
 * <pre>
2912
 * Notes:
2913
 *      (1) Use this to remove any colormaps and convert all pix to either
2914
 *          8 or 32 bpp.
2915
 *      (2) To convert losslessly, get %depth from pixaGetRenderingDepth().
2916
 *      (3) Clone pix may be in the returned pixa if conversion is to 32 bpp.
2917
 * </pre>
2918
 */
2919
PIXA *
2920
pixaConvertToGivenDepth(PIXA    *pixas,
2921
                        l_int32  depth)
2922
0
{
2923
0
l_int32  i, n, maxd;
2924
0
BOXA    *boxa;
2925
0
PIX     *pix1, *pix2;
2926
0
PIXA    *pixad;
2927
2928
0
    if (!pixas)
2929
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
2930
0
    if ((n = pixaGetCount(pixas)) == 0)
2931
0
        return (PIXA *)ERROR_PTR("no components", __func__, NULL);
2932
0
    if (depth != 8 && depth != 32)
2933
0
        return (PIXA *)ERROR_PTR("depth not 8 or 32", __func__, NULL);
2934
2935
        /* Warn with 1 --> {8,32} or lossy conversions */
2936
0
    pixaGetRenderingDepth(pixas, &maxd);
2937
0
    if (maxd == 1)
2938
0
        L_WARNING("All pix are 1 bpp; converting to %d bpp\n", __func__, depth);
2939
0
    if (maxd > depth)
2940
0
        L_WARNING("Lossy conversion: max rendering depth %d > input %d\n",
2941
0
                  __func__, maxd, depth);
2942
2943
0
    pixad = pixaCreate(n);
2944
0
    for (i = 0; i < n; i++) {
2945
0
        pix1 = pixaGetPix(pixas, i, L_CLONE);
2946
0
        if (depth == 32) {
2947
0
            pix2 = (pixGetDepth(pix1) == 32) ? pixClone(pix1) :
2948
0
                   pixConvertTo32(pix1);
2949
0
        } else {  /* depth = 8 */
2950
0
            pix2 = pixConvertTo8(pix1, 0);
2951
0
        }
2952
0
        pixaAddPix(pixad, pix2, L_INSERT);
2953
0
        pixDestroy(&pix1);
2954
0
    }
2955
2956
0
    boxa = pixaGetBoxa(pixas, L_COPY);
2957
0
    pixaSetBoxa(pixad, boxa, L_INSERT);
2958
0
    return pixad;
2959
0
}
2960
2961
2962
/*!
2963
 * \brief   pixaEqual()
2964
 *
2965
 * \param[in]    pixa1
2966
 * \param[in]    pixa2
2967
 * \param[in]    maxdist
2968
 * \param[out]   pnaindex  [optional] index array of correspondences
2969
 * \param[out]   psame     1 if equal; 0 otherwise
2970
 * \return  0 if OK, 1 on error
2971
 *
2972
 * <pre>
2973
 * Notes:
2974
 *      (1) The two pixa are the "same" if they contain the same
2975
 *          boxa and the same ordered set of pix.  However, if they
2976
 *          have boxa, the pix in each pixa can differ in ordering
2977
 *          by an amount given by the parameter %maxdist.  If they
2978
 *          don't have a boxa, the %maxdist parameter is ignored,
2979
 *          and the ordering of the pix must be identical.
2980
 *      (2) This applies only to boxa geometry, pixels and ordering;
2981
 *          other fields in the pix are ignored.
2982
 *      (3) naindex[i] gives the position of the box in pixa2 that
2983
 *          corresponds to box i in pixa1.  It is only returned if the
2984
 *          pixa have boxa and the boxa are equal.
2985
 *      (4) In situations where the ordering is very different, so that
2986
 *          a large %maxdist is required for "equality", this should be
2987
 *          implemented with a hash function for efficiency.
2988
 * </pre>
2989
 */
2990
l_ok
2991
pixaEqual(PIXA     *pixa1,
2992
          PIXA     *pixa2,
2993
          l_int32   maxdist,
2994
          NUMA    **pnaindex,
2995
          l_int32  *psame)
2996
0
{
2997
0
l_int32   i, j, n, empty1, empty2, same, sameboxa;
2998
0
BOXA     *boxa1, *boxa2;
2999
0
NUMA     *na;
3000
0
PIX      *pix1, *pix2;
3001
3002
0
    if (pnaindex) *pnaindex = NULL;
3003
0
    if (!psame)
3004
0
        return ERROR_INT("&same not defined", __func__, 1);
3005
0
    *psame = 0;
3006
0
    sameboxa = 0;
3007
0
    na = NULL;
3008
0
    if (!pixa1 || !pixa2)
3009
0
        return ERROR_INT("pixa1 and pixa2 not both defined", __func__, 1);
3010
0
    n = pixaGetCount(pixa1);
3011
0
    if (n != pixaGetCount(pixa2))
3012
0
        return 0;
3013
3014
        /* If there are no boxes, strict ordering of the pix in each
3015
         * pixa  is required. */
3016
0
    boxa1 = pixaGetBoxa(pixa1, L_CLONE);
3017
0
    boxa2 = pixaGetBoxa(pixa2, L_CLONE);
3018
0
    empty1 = (boxaGetCount(boxa1) == 0) ? 1 : 0;
3019
0
    empty2 = (boxaGetCount(boxa2) == 0) ? 1 : 0;
3020
0
    if (!empty1 && !empty2) {
3021
0
        boxaEqual(boxa1, boxa2, maxdist, &na, &sameboxa);
3022
0
        if (!sameboxa) {
3023
0
            boxaDestroy(&boxa1);
3024
0
            boxaDestroy(&boxa2);
3025
0
            numaDestroy(&na);
3026
0
            return 0;
3027
0
        }
3028
0
    }
3029
0
    boxaDestroy(&boxa1);
3030
0
    boxaDestroy(&boxa2);
3031
0
    if ((!empty1 && empty2) || (empty1 && !empty2))
3032
0
        return 0;
3033
3034
0
    for (i = 0; i < n; i++) {
3035
0
        pix1 = pixaGetPix(pixa1, i, L_CLONE);
3036
0
        if (na)
3037
0
            numaGetIValue(na, i, &j);
3038
0
        else
3039
0
            j = i;
3040
0
        pix2 = pixaGetPix(pixa2, j, L_CLONE);
3041
0
        pixEqual(pix1, pix2, &same);
3042
0
        pixDestroy(&pix1);
3043
0
        pixDestroy(&pix2);
3044
0
        if (!same) {
3045
0
            numaDestroy(&na);
3046
0
            return 0;
3047
0
        }
3048
0
    }
3049
3050
0
    *psame = 1;
3051
0
    if (pnaindex)
3052
0
        *pnaindex = na;
3053
0
    else
3054
0
        numaDestroy(&na);
3055
0
    return 0;
3056
0
}
3057
3058
3059
/*!
3060
 * \brief   pixaSetFullSizeBoxa()
3061
 *
3062
 * \param[in]    pixa
3063
 * \return  0 if OK, 1 on error
3064
 *
3065
 * <pre>
3066
 * Notes:
3067
 *      (1) Replaces the existing boxa.  Each box gives the dimensions
3068
 *          of the corresponding pix.  This is needed for functions
3069
 *          like pixaSort() that sort based on the boxes.
3070
 * </pre>
3071
 */
3072
l_ok
3073
pixaSetFullSizeBoxa(PIXA  *pixa)
3074
0
{
3075
0
l_int32  i, n, w, h;
3076
0
BOX     *box;
3077
0
BOXA    *boxa;
3078
0
PIX     *pix;
3079
3080
0
    if (!pixa)
3081
0
        return ERROR_INT("pixa not defined", __func__, 1);
3082
0
    if ((n = pixaGetCount(pixa)) == 0) {
3083
0
        L_INFO("pixa contains no pix\n", __func__);
3084
0
        return 0;
3085
0
    }
3086
3087
0
    boxa = boxaCreate(n);
3088
0
    pixaSetBoxa(pixa, boxa, L_INSERT);
3089
0
    for (i = 0; i < n; i++) {
3090
0
        pix = pixaGetPix(pixa, i, L_CLONE);
3091
0
        pixGetDimensions(pix, &w, &h, NULL);
3092
0
        box = boxCreate(0, 0, w, h);
3093
0
        boxaAddBox(boxa, box, L_INSERT);
3094
0
        pixDestroy(&pix);
3095
0
    }
3096
0
    return 0;
3097
0
}
3098