Coverage Report

Created: 2024-06-18 06:05

/src/leptonica/src/pix4.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 pix4.c
29
 * <pre>
30
 *
31
 *    This file has these operations:
32
 *
33
 *      (1) Pixel histograms
34
 *      (2) Pixel row/column statistics
35
 *      (3) Foreground/background estimation
36
 *
37
 *    Pixel histogram, rank val, averaging and min/max
38
 *           NUMA       *pixGetGrayHistogram()
39
 *           NUMA       *pixGetGrayHistogramMasked()
40
 *           NUMA       *pixGetGrayHistogramInRect()
41
 *           NUMAA      *pixGetGrayHistogramTiled()
42
 *           l_int32     pixGetColorHistogram()
43
 *           l_int32     pixGetColorHistogramMasked()
44
 *           NUMA       *pixGetCmapHistogram()
45
 *           NUMA       *pixGetCmapHistogramMasked()
46
 *           NUMA       *pixGetCmapHistogramInRect()
47
 *           l_int32     pixCountRGBColorsByHash()
48
 *           l_int32     pixCountRGBColors()
49
 *           L_AMAP     *pixGetColorAmapHistogram()
50
 *           l_int32     amapGetCountForColor()
51
 *           l_int32     pixGetRankValue()
52
 *           l_int32     pixGetRankValueMaskedRGB()
53
 *           l_int32     pixGetRankValueMasked()
54
 *           l_int32     pixGetPixelAverage()
55
 *           l_int32     pixGetPixelStats()
56
 *           l_int32     pixGetAverageMaskedRGB()
57
 *           l_int32     pixGetAverageMasked()
58
 *           l_int32     pixGetAverageTiledRGB()
59
 *           PIX        *pixGetAverageTiled()
60
 *           NUMA       *pixRowStats()
61
 *           NUMA       *pixColumnStats()
62
 *           l_int32     pixGetRangeValues()
63
 *           l_int32     pixGetExtremeValue()
64
 *           l_int32     pixGetMaxValueInRect()
65
 *           l_int32     pixGetMaxColorIndex()
66
 *           l_int32     pixGetBinnedComponentRange()
67
 *           l_int32     pixGetRankColorArray()
68
 *           l_int32     pixGetBinnedColor()
69
 *           PIX        *pixDisplayColorArray()
70
 *           PIX        *pixRankBinByStrip()
71
 *
72
 *    Pixelwise aligned statistics
73
 *           PIX        *pixaGetAlignedStats()
74
 *           l_int32     pixaExtractColumnFromEachPix()
75
 *           l_int32     pixGetRowStats()
76
 *           l_int32     pixGetColumnStats()
77
 *           l_int32     pixSetPixelColumn()
78
 *
79
 *    Foreground/background estimation
80
 *           l_int32     pixThresholdForFgBg()
81
 *           l_int32     pixSplitDistributionFgBg()
82
 * </pre>
83
 */
84
85
#ifdef HAVE_CONFIG_H
86
#include <config_auto.h>
87
#endif  /* HAVE_CONFIG_H */
88
89
#include <string.h>
90
#include <math.h>
91
#include "allheaders.h"
92
93
94
/*------------------------------------------------------------------*
95
 *                  Pixel histogram and averaging                   *
96
 *------------------------------------------------------------------*/
97
/*!
98
 * \brief   pixGetGrayHistogram()
99
 *
100
 * \param[in]   pixs     1, 2, 4, 8, 16 bpp; can be colormapped
101
 * \param[in]   factor   subsampling factor; integer >= 1
102
 * \return  na histogram, or NULL on error
103
 *
104
 * <pre>
105
 * Notes:
106
 *      (1) If pixs has a colormap, it is converted to 8 bpp gray.
107
 *          If you want a histogram of the colormap indices, use
108
 *          pixGetCmapHistogram().
109
 *      (2) If pixs does not have a colormap, the output histogram is
110
 *          of size 2^d, where d is the depth of pixs.
111
 *      (3) Set the subsampling factor > 1 to reduce the amount of computation.
112
 * </pre>
113
 */
114
NUMA *
115
pixGetGrayHistogram(PIX     *pixs,
116
                    l_int32  factor)
117
0
{
118
0
l_int32     i, j, w, h, d, wpl, val, size, count;
119
0
l_uint32   *data, *line;
120
0
l_float32  *array;
121
0
NUMA       *na;
122
0
PIX        *pixg;
123
124
0
    if (!pixs)
125
0
        return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
126
0
    d = pixGetDepth(pixs);
127
0
    if (d > 16)
128
0
        return (NUMA *)ERROR_PTR("depth not in {1,2,4,8,16}", __func__, NULL);
129
0
    if (factor < 1)
130
0
        return (NUMA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
131
132
0
    if (pixGetColormap(pixs))
133
0
        pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
134
0
    else
135
0
        pixg = pixClone(pixs);
136
137
0
    pixGetDimensions(pixg, &w, &h, &d);
138
0
    size = 1 << d;
139
0
    if ((na = numaCreate(size)) == NULL) {
140
0
        pixDestroy(&pixg);
141
0
        return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
142
0
    }
143
0
    numaSetCount(na, size);  /* all initialized to 0.0 */
144
0
    array = numaGetFArray(na, L_NOCOPY);
145
146
0
    if (d == 1) {  /* special case */
147
0
        pixCountPixels(pixg, &count, NULL);
148
0
        array[0] = w * h - count;
149
0
        array[1] = count;
150
0
        pixDestroy(&pixg);
151
0
        return na;
152
0
    }
153
154
0
    wpl = pixGetWpl(pixg);
155
0
    data = pixGetData(pixg);
156
0
    for (i = 0; i < h; i += factor) {
157
0
        line = data + i * wpl;
158
0
        if (d == 2) {
159
0
            for (j = 0; j < w; j += factor) {
160
0
                val = GET_DATA_DIBIT(line, j);
161
0
                array[val] += 1.0;
162
0
            }
163
0
        } else if (d == 4) {
164
0
            for (j = 0; j < w; j += factor) {
165
0
                val = GET_DATA_QBIT(line, j);
166
0
                array[val] += 1.0;
167
0
            }
168
0
        } else if (d == 8) {
169
0
            for (j = 0; j < w; j += factor) {
170
0
                val = GET_DATA_BYTE(line, j);
171
0
                array[val] += 1.0;
172
0
            }
173
0
        } else {  /* d == 16 */
174
0
            for (j = 0; j < w; j += factor) {
175
0
                val = GET_DATA_TWO_BYTES(line, j);
176
0
                array[val] += 1.0;
177
0
            }
178
0
        }
179
0
    }
180
181
0
    pixDestroy(&pixg);
182
0
    return na;
183
0
}
184
185
186
/*!
187
 * \brief   pixGetGrayHistogramMasked()
188
 *
189
 * \param[in]   pixs     8 bpp, or colormapped
190
 * \param[in]   pixm     [optional] 1 bpp mask over which histogram is
191
 *                       to be computed; use all pixels if null
192
 * \param[in]   x, y     UL corner of pixm relative to the UL corner of pixs;
193
 *                       can be < 0; these values are ignored if pixm is null
194
 * \param[in]   factor   subsampling factor; integer >= 1
195
 * \return  na histogram, or NULL on error
196
 *
197
 * <pre>
198
 * Notes:
199
 *      (1) If pixs is cmapped, it is converted to 8 bpp gray.
200
 *          If you want a histogram of the colormap indices, use
201
 *          pixGetCmapHistogramMasked().
202
 *      (2) This always returns a 256-value histogram of pixel values.
203
 *      (3) Set the subsampling factor > 1 to reduce the amount of computation.
204
 *      (4) Clipping of pixm (if it exists) to pixs is done in the inner loop.
205
 *      (5) Input x,y are ignored unless pixm exists.
206
 * </pre>
207
 */
208
NUMA *
209
pixGetGrayHistogramMasked(PIX        *pixs,
210
                          PIX        *pixm,
211
                          l_int32     x,
212
                          l_int32     y,
213
                          l_int32     factor)
214
0
{
215
0
l_int32     i, j, w, h, wm, hm, dm, wplg, wplm, val;
216
0
l_uint32   *datag, *datam, *lineg, *linem;
217
0
l_float32  *array;
218
0
NUMA       *na;
219
0
PIX        *pixg;
220
221
0
    if (!pixm)
222
0
        return pixGetGrayHistogram(pixs, factor);
223
0
    if (!pixs)
224
0
        return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
225
0
    if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs))
226
0
        return (NUMA *)ERROR_PTR("pixs neither 8 bpp nor colormapped",
227
0
                                 __func__, NULL);
228
0
    pixGetDimensions(pixm, &wm, &hm, &dm);
229
0
    if (dm != 1)
230
0
        return (NUMA *)ERROR_PTR("pixm not 1 bpp", __func__, NULL);
231
0
    if (factor < 1)
232
0
        return (NUMA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
233
234
0
    if ((na = numaCreate(256)) == NULL)
235
0
        return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
236
0
    numaSetCount(na, 256);  /* all initialized to 0.0 */
237
0
    array = numaGetFArray(na, L_NOCOPY);
238
239
0
    if (pixGetColormap(pixs))
240
0
        pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
241
0
    else
242
0
        pixg = pixClone(pixs);
243
0
    pixGetDimensions(pixg, &w, &h, NULL);
244
0
    datag = pixGetData(pixg);
245
0
    wplg = pixGetWpl(pixg);
246
0
    datam = pixGetData(pixm);
247
0
    wplm = pixGetWpl(pixm);
248
249
        /* Generate the histogram */
250
0
    for (i = 0; i < hm; i += factor) {
251
0
        if (y + i < 0 || y + i >= h) continue;
252
0
        lineg = datag + (y + i) * wplg;
253
0
        linem = datam + i * wplm;
254
0
        for (j = 0; j < wm; j += factor) {
255
0
            if (x + j < 0 || x + j >= w) continue;
256
0
            if (GET_DATA_BIT(linem, j)) {
257
0
                val = GET_DATA_BYTE(lineg, x + j);
258
0
                array[val] += 1.0;
259
0
            }
260
0
        }
261
0
    }
262
263
0
    pixDestroy(&pixg);
264
0
    return na;
265
0
}
266
267
268
/*!
269
 * \brief   pixGetGrayHistogramInRect()
270
 *
271
 * \param[in]   pixs    8 bpp, or colormapped
272
 * \param[in]   box     [optional] over which histogram is to be computed;
273
 *                      use full image if NULL
274
 * \param[in]   factor  subsampling factor; integer >= 1
275
 * \return  na histogram, or NULL on error
276
 *
277
 * <pre>
278
 * Notes:
279
 *      (1) If pixs is cmapped, it is converted to 8 bpp gray.
280
 *          If you want a histogram of the colormap indices, use
281
 *          pixGetCmapHistogramInRect().
282
 *      (2) This always returns a 256-value histogram of pixel values.
283
 *      (3) Set the subsampling %factor > 1 to reduce the amount of computation.
284
 * </pre>
285
 */
286
NUMA *
287
pixGetGrayHistogramInRect(PIX     *pixs,
288
                          BOX     *box,
289
                          l_int32  factor)
290
0
{
291
0
l_int32     i, j, bx, by, bw, bh, w, h, wplg, val;
292
0
l_uint32   *datag, *lineg;
293
0
l_float32  *array;
294
0
NUMA       *na;
295
0
PIX        *pixg;
296
297
0
    if (!box)
298
0
        return pixGetGrayHistogram(pixs, factor);
299
0
    if (!pixs)
300
0
        return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
301
0
    if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs))
302
0
        return (NUMA *)ERROR_PTR("pixs neither 8 bpp nor colormapped",
303
0
                                 __func__, NULL);
304
0
    if (factor < 1)
305
0
        return (NUMA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
306
307
0
    if ((na = numaCreate(256)) == NULL)
308
0
        return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
309
0
    numaSetCount(na, 256);  /* all initialized to 0.0 */
310
0
    array = numaGetFArray(na, L_NOCOPY);
311
312
0
    if (pixGetColormap(pixs))
313
0
        pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
314
0
    else
315
0
        pixg = pixClone(pixs);
316
0
    pixGetDimensions(pixg, &w, &h, NULL);
317
0
    datag = pixGetData(pixg);
318
0
    wplg = pixGetWpl(pixg);
319
0
    boxGetGeometry(box, &bx, &by, &bw, &bh);
320
321
        /* Generate the histogram */
322
0
    for (i = 0; i < bh; i += factor) {
323
0
        if (by + i < 0 || by + i >= h) continue;
324
0
        lineg = datag + (by + i) * wplg;
325
0
        for (j = 0; j < bw; j += factor) {
326
0
            if (bx + j < 0 || bx + j >= w) continue;
327
0
            val = GET_DATA_BYTE(lineg, bx + j);
328
0
            array[val] += 1.0;
329
0
        }
330
0
    }
331
332
0
    pixDestroy(&pixg);
333
0
    return na;
334
0
}
335
336
337
/*!
338
 * \brief   pixGetGrayHistogramTiled()
339
 *
340
 * \param[in]   pixs     any depth, colormap OK
341
 * \param[in]   factor   subsampling factor; integer >= 1
342
 * \param[in]   nx, ny   tiling; >= 1; typically small
343
 * \return  naa set of histograms, or NULL on error
344
 *
345
 * <pre>
346
 * Notes:
347
 *      (1) If pixs is cmapped, it is converted to 8 bpp gray.
348
 *      (2) This returns a set of 256-value histograms of pixel values.
349
 *      (3) Set the subsampling factor > 1 to reduce the amount of computation.
350
 * </pre>
351
 */
352
NUMAA *
353
pixGetGrayHistogramTiled(PIX     *pixs,
354
                         l_int32  factor,
355
                         l_int32  nx,
356
                         l_int32  ny)
357
0
{
358
0
l_int32  i, n;
359
0
NUMA    *na;
360
0
NUMAA   *naa;
361
0
PIX     *pix1, *pix2;
362
0
PIXA    *pixa;
363
364
0
    if (!pixs)
365
0
        return (NUMAA *)ERROR_PTR("pixs not defined", __func__, NULL);
366
0
    if (factor < 1)
367
0
        return (NUMAA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
368
0
    if (nx < 1 || ny < 1)
369
0
        return (NUMAA *)ERROR_PTR("nx and ny must both be > 0", __func__, NULL);
370
371
0
    n = nx * ny;
372
0
    if ((naa = numaaCreate(n)) == NULL)
373
0
        return (NUMAA *)ERROR_PTR("naa not made", __func__, NULL);
374
375
0
    pix1 = pixConvertTo8(pixs, FALSE);
376
0
    pixa = pixaSplitPix(pix1, nx, ny, 0, 0);
377
0
    for (i = 0; i < n; i++) {
378
0
        pix2 = pixaGetPix(pixa, i, L_CLONE);
379
0
        na = pixGetGrayHistogram(pix2, factor);
380
0
        numaaAddNuma(naa, na, L_INSERT);
381
0
        pixDestroy(&pix2);
382
0
    }
383
384
0
    pixDestroy(&pix1);
385
0
    pixaDestroy(&pixa);
386
0
    return naa;
387
0
}
388
389
390
/*!
391
 * \brief   pixGetColorHistogram()
392
 *
393
 * \param[in]    pixs     rgb or colormapped
394
 * \param[in]    factor   subsampling factor; integer >= 1
395
 * \param[out]   pnar     red histogram
396
 * \param[out]   pnag     green histogram
397
 * \param[out]   pnab     blue histogram
398
 * \return  0 if OK, 1 on error
399
 *
400
 * <pre>
401
 * Notes:
402
 *      (1) This generates a set of three 256 entry histograms,
403
 *          one for each color component (r,g,b).
404
 *      (2) Set the subsampling %factor > 1 to reduce the amount of computation.
405
 * </pre>
406
 */
407
l_ok
408
pixGetColorHistogram(PIX     *pixs,
409
                     l_int32  factor,
410
                     NUMA   **pnar,
411
                     NUMA   **pnag,
412
                     NUMA   **pnab)
413
0
{
414
0
l_int32     i, j, w, h, d, wpl, index, rval, gval, bval;
415
0
l_uint32   *data, *line;
416
0
l_float32  *rarray, *garray, *barray;
417
0
NUMA       *nar, *nag, *nab;
418
0
PIXCMAP    *cmap;
419
420
0
    if (pnar) *pnar = NULL;
421
0
    if (pnag) *pnag = NULL;
422
0
    if (pnab) *pnab = NULL;
423
0
    if (!pnar || !pnag || !pnab)
424
0
        return ERROR_INT("&nar, &nag, &nab not all defined", __func__, 1);
425
0
    if (!pixs)
426
0
        return ERROR_INT("pixs not defined", __func__, 1);
427
0
    pixGetDimensions(pixs, &w, &h, &d);
428
0
    cmap = pixGetColormap(pixs);
429
0
    if (cmap && (d != 2 && d != 4 && d != 8))
430
0
        return ERROR_INT("colormap and not 2, 4, or 8 bpp", __func__, 1);
431
0
    if (!cmap && d != 32)
432
0
        return ERROR_INT("no colormap and not rgb", __func__, 1);
433
0
    if (factor < 1)
434
0
        return ERROR_INT("sampling factor must be >= 1", __func__, 1);
435
436
        /* Set up the histogram arrays */
437
0
    nar = numaCreate(256);
438
0
    nag = numaCreate(256);
439
0
    nab = numaCreate(256);
440
0
    numaSetCount(nar, 256);
441
0
    numaSetCount(nag, 256);
442
0
    numaSetCount(nab, 256);
443
0
    rarray = numaGetFArray(nar, L_NOCOPY);
444
0
    garray = numaGetFArray(nag, L_NOCOPY);
445
0
    barray = numaGetFArray(nab, L_NOCOPY);
446
0
    *pnar = nar;
447
0
    *pnag = nag;
448
0
    *pnab = nab;
449
450
        /* Generate the color histograms */
451
0
    data = pixGetData(pixs);
452
0
    wpl = pixGetWpl(pixs);
453
0
    if (cmap) {
454
0
        for (i = 0; i < h; i += factor) {
455
0
            line = data + i * wpl;
456
0
            for (j = 0; j < w; j += factor) {
457
0
                if (d == 8)
458
0
                    index = GET_DATA_BYTE(line, j);
459
0
                else if (d == 4)
460
0
                    index = GET_DATA_QBIT(line, j);
461
0
                else   /* 2 bpp */
462
0
                    index = GET_DATA_DIBIT(line, j);
463
0
                pixcmapGetColor(cmap, index, &rval, &gval, &bval);
464
0
                rarray[rval] += 1.0;
465
0
                garray[gval] += 1.0;
466
0
                barray[bval] += 1.0;
467
0
            }
468
0
        }
469
0
    } else {  /* 32 bpp rgb */
470
0
        for (i = 0; i < h; i += factor) {
471
0
            line = data + i * wpl;
472
0
            for (j = 0; j < w; j += factor) {
473
0
                extractRGBValues(line[j], &rval, &gval, &bval);
474
0
                rarray[rval] += 1.0;
475
0
                garray[gval] += 1.0;
476
0
                barray[bval] += 1.0;
477
0
            }
478
0
        }
479
0
    }
480
481
0
    return 0;
482
0
}
483
484
485
/*!
486
 * \brief   pixGetColorHistogramMasked()
487
 *
488
 * \param[in]    pixs     32 bpp rgb, or colormapped
489
 * \param[in]    pixm     [optional] 1 bpp mask over which histogram is
490
 *                        to be computed; use all pixels if null
491
 * \param[in]    x, y     UL corner of pixm relative to the UL corner of pixs;
492
 *                        can be < 0; these values are ignored if pixm is null
493
 * \param[in]    factor   subsampling factor; integer >= 1
494
 * \param[out]   pnar     red histogram
495
 * \param[out]   pnag     green histogram
496
 * \param[out]   pnab     blue histogram
497
 * \return  0 if OK, 1 on error
498
 *
499
 * <pre>
500
 * Notes:
501
 *      (1) This generates a set of three 256 entry histograms,
502
 *      (2) Set the subsampling %factor > 1 to reduce the amount of computation.
503
 *      (3) Clipping of pixm (if it exists) to pixs is done in the inner loop.
504
 *      (4) Input x,y are ignored unless pixm exists.
505
 * </pre>
506
 */
507
l_ok
508
pixGetColorHistogramMasked(PIX        *pixs,
509
                           PIX        *pixm,
510
                           l_int32     x,
511
                           l_int32     y,
512
                           l_int32     factor,
513
                           NUMA      **pnar,
514
                           NUMA      **pnag,
515
                           NUMA      **pnab)
516
0
{
517
0
l_int32     i, j, w, h, d, wm, hm, dm, wpls, wplm, index, rval, gval, bval;
518
0
l_uint32   *datas, *datam, *lines, *linem;
519
0
l_float32  *rarray, *garray, *barray;
520
0
NUMA       *nar, *nag, *nab;
521
0
PIXCMAP    *cmap;
522
523
0
    if (!pixm)
524
0
        return pixGetColorHistogram(pixs, factor, pnar, pnag, pnab);
525
526
0
    if (pnar) *pnar = NULL;
527
0
    if (pnag) *pnag = NULL;
528
0
    if (pnab) *pnab = NULL;
529
0
    if (!pnar || !pnag || !pnab)
530
0
        return ERROR_INT("&nar, &nag, &nab not all defined", __func__, 1);
531
0
    if (!pixs)
532
0
        return ERROR_INT("pixs not defined", __func__, 1);
533
0
    pixGetDimensions(pixs, &w, &h, &d);
534
0
    cmap = pixGetColormap(pixs);
535
0
    if (cmap && (d != 2 && d != 4 && d != 8))
536
0
        return ERROR_INT("colormap and not 2, 4, or 8 bpp", __func__, 1);
537
0
    if (!cmap && d != 32)
538
0
        return ERROR_INT("no colormap and not rgb", __func__, 1);
539
0
    pixGetDimensions(pixm, &wm, &hm, &dm);
540
0
    if (dm != 1)
541
0
        return ERROR_INT("pixm not 1 bpp", __func__, 1);
542
0
    if (factor < 1)
543
0
        return ERROR_INT("sampling factor must be >= 1", __func__, 1);
544
545
        /* Set up the histogram arrays */
546
0
    nar = numaCreate(256);
547
0
    nag = numaCreate(256);
548
0
    nab = numaCreate(256);
549
0
    numaSetCount(nar, 256);
550
0
    numaSetCount(nag, 256);
551
0
    numaSetCount(nab, 256);
552
0
    rarray = numaGetFArray(nar, L_NOCOPY);
553
0
    garray = numaGetFArray(nag, L_NOCOPY);
554
0
    barray = numaGetFArray(nab, L_NOCOPY);
555
0
    *pnar = nar;
556
0
    *pnag = nag;
557
0
    *pnab = nab;
558
559
        /* Generate the color histograms */
560
0
    datas = pixGetData(pixs);
561
0
    wpls = pixGetWpl(pixs);
562
0
    datam = pixGetData(pixm);
563
0
    wplm = pixGetWpl(pixm);
564
0
    if (cmap) {
565
0
        for (i = 0; i < hm; i += factor) {
566
0
            if (y + i < 0 || y + i >= h) continue;
567
0
            lines = datas + (y + i) * wpls;
568
0
            linem = datam + i * wplm;
569
0
            for (j = 0; j < wm; j += factor) {
570
0
                if (x + j < 0 || x + j >= w) continue;
571
0
                if (GET_DATA_BIT(linem, j)) {
572
0
                    if (d == 8)
573
0
                        index = GET_DATA_BYTE(lines, x + j);
574
0
                    else if (d == 4)
575
0
                        index = GET_DATA_QBIT(lines, x + j);
576
0
                    else   /* 2 bpp */
577
0
                        index = GET_DATA_DIBIT(lines, x + j);
578
0
                    pixcmapGetColor(cmap, index, &rval, &gval, &bval);
579
0
                    rarray[rval] += 1.0;
580
0
                    garray[gval] += 1.0;
581
0
                    barray[bval] += 1.0;
582
0
                }
583
0
            }
584
0
        }
585
0
    } else {  /* 32 bpp rgb */
586
0
        for (i = 0; i < hm; i += factor) {
587
0
            if (y + i < 0 || y + i >= h) continue;
588
0
            lines = datas + (y + i) * wpls;
589
0
            linem = datam + i * wplm;
590
0
            for (j = 0; j < wm; j += factor) {
591
0
                if (x + j < 0 || x + j >= w) continue;
592
0
                if (GET_DATA_BIT(linem, j)) {
593
0
                    extractRGBValues(lines[x + j], &rval, &gval, &bval);
594
0
                    rarray[rval] += 1.0;
595
0
                    garray[gval] += 1.0;
596
0
                    barray[bval] += 1.0;
597
0
                }
598
0
            }
599
0
        }
600
0
    }
601
602
0
    return 0;
603
0
}
604
605
606
/*!
607
 * \brief   pixGetCmapHistogram()
608
 *
609
 * \param[in]   pixs    colormapped: d = 2, 4 or 8
610
 * \param[in]   factor  subsampling factor; integer >= 1
611
 * \return  na histogram of cmap indices, or NULL on error
612
 *
613
 * <pre>
614
 * Notes:
615
 *      (1) This generates a histogram of colormap pixel indices,
616
 *          and is of size 2^d.
617
 *      (2) Set the subsampling %factor > 1 to reduce the amount of computation.
618
 * </pre>
619
 */
620
NUMA *
621
pixGetCmapHistogram(PIX     *pixs,
622
                    l_int32  factor)
623
0
{
624
0
l_int32     i, j, w, h, d, wpl, val, size;
625
0
l_uint32   *data, *line;
626
0
l_float32  *array;
627
0
NUMA       *na;
628
629
0
    if (!pixs)
630
0
        return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
631
0
    if (pixGetColormap(pixs) == NULL)
632
0
        return (NUMA *)ERROR_PTR("pixs not cmapped", __func__, NULL);
633
0
    if (factor < 1)
634
0
        return (NUMA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
635
0
    pixGetDimensions(pixs, &w, &h, &d);
636
0
    if (d != 2 && d != 4 && d != 8)
637
0
        return (NUMA *)ERROR_PTR("d not 2, 4 or 8", __func__, NULL);
638
639
0
    size = 1 << d;
640
0
    if ((na = numaCreate(size)) == NULL)
641
0
        return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
642
0
    numaSetCount(na, size);  /* all initialized to 0.0 */
643
0
    array = numaGetFArray(na, L_NOCOPY);
644
645
0
    wpl = pixGetWpl(pixs);
646
0
    data = pixGetData(pixs);
647
0
    for (i = 0; i < h; i += factor) {
648
0
        line = data + i * wpl;
649
0
        for (j = 0; j < w; j += factor) {
650
0
            if (d == 8)
651
0
                val = GET_DATA_BYTE(line, j);
652
0
            else if (d == 4)
653
0
                val = GET_DATA_QBIT(line, j);
654
0
            else  /* d == 2 */
655
0
                val = GET_DATA_DIBIT(line, j);
656
0
            array[val] += 1.0;
657
0
        }
658
0
    }
659
660
0
    return na;
661
0
}
662
663
664
/*!
665
 * \brief   pixGetCmapHistogramMasked()
666
 *
667
 * \param[in]   pixs     colormapped: d = 2, 4 or 8
668
 * \param[in]   pixm     [optional] 1 bpp mask over which histogram is
669
 *                       to be computed; use all pixels if null
670
 * \param[in]   x, y     UL corner of pixm relative to the UL corner of pixs;
671
 *                       can be < 0; these values are ignored if pixm is null
672
 * \param[in]   factor   subsampling factor; integer >= 1
673
 * \return  na histogram, or NULL on error
674
 *
675
 * <pre>
676
 * Notes:
677
 *      (1) This generates a histogram of colormap pixel indices,
678
 *          and is of size 2^d.
679
 *      (2) Set the subsampling %factor > 1 to reduce the amount of computation.
680
 *      (3) Clipping of pixm to pixs is done in the inner loop.
681
 * </pre>
682
 */
683
NUMA *
684
pixGetCmapHistogramMasked(PIX     *pixs,
685
                          PIX     *pixm,
686
                          l_int32  x,
687
                          l_int32  y,
688
                          l_int32  factor)
689
0
{
690
0
l_int32     i, j, w, h, d, wm, hm, dm, wpls, wplm, val, size;
691
0
l_uint32   *datas, *datam, *lines, *linem;
692
0
l_float32  *array;
693
0
NUMA       *na;
694
695
0
    if (!pixm)
696
0
        return pixGetCmapHistogram(pixs, factor);
697
698
0
    if (!pixs)
699
0
        return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
700
0
    if (pixGetColormap(pixs) == NULL)
701
0
        return (NUMA *)ERROR_PTR("pixs not cmapped", __func__, NULL);
702
0
    pixGetDimensions(pixm, &wm, &hm, &dm);
703
0
    if (dm != 1)
704
0
        return (NUMA *)ERROR_PTR("pixm not 1 bpp", __func__, NULL);
705
0
    if (factor < 1)
706
0
        return (NUMA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
707
0
    pixGetDimensions(pixs, &w, &h, &d);
708
0
    if (d != 2 && d != 4 && d != 8)
709
0
        return (NUMA *)ERROR_PTR("d not 2, 4 or 8", __func__, NULL);
710
711
0
    size = 1 << d;
712
0
    if ((na = numaCreate(size)) == NULL)
713
0
        return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
714
0
    numaSetCount(na, size);  /* all initialized to 0.0 */
715
0
    array = numaGetFArray(na, L_NOCOPY);
716
717
0
    datas = pixGetData(pixs);
718
0
    wpls = pixGetWpl(pixs);
719
0
    datam = pixGetData(pixm);
720
0
    wplm = pixGetWpl(pixm);
721
722
0
    for (i = 0; i < hm; i += factor) {
723
0
        if (y + i < 0 || y + i >= h) continue;
724
0
        lines = datas + (y + i) * wpls;
725
0
        linem = datam + i * wplm;
726
0
        for (j = 0; j < wm; j += factor) {
727
0
            if (x + j < 0 || x + j >= w) continue;
728
0
            if (GET_DATA_BIT(linem, j)) {
729
0
                if (d == 8)
730
0
                    val = GET_DATA_BYTE(lines, x + j);
731
0
                else if (d == 4)
732
0
                    val = GET_DATA_QBIT(lines, x + j);
733
0
                else  /* d == 2 */
734
0
                    val = GET_DATA_DIBIT(lines, x + j);
735
0
                array[val] += 1.0;
736
0
            }
737
0
        }
738
0
    }
739
740
0
    return na;
741
0
}
742
743
744
/*!
745
 * \brief   pixGetCmapHistogramInRect()
746
 *
747
 * \param[in]   pixs     colormapped: d = 2, 4 or 8
748
 * \param[in]   box      [optional] over which histogram is to be computed;
749
 *                       use full image if NULL
750
 * \param[in]   factor   subsampling factor; integer >= 1
751
 * \return  na histogram, or NULL on error
752
 *
753
 * <pre>
754
 * Notes:
755
 *      (1) This generates a histogram of colormap pixel indices,
756
 *          and is of size 2^d.
757
 *      (2) Set the subsampling %factor > 1 to reduce the amount of computation.
758
 *      (3) Clipping to the box is done in the inner loop.
759
 * </pre>
760
 */
761
NUMA *
762
pixGetCmapHistogramInRect(PIX     *pixs,
763
                          BOX     *box,
764
                          l_int32  factor)
765
0
{
766
0
l_int32     i, j, bx, by, bw, bh, w, h, d, wpls, val, size;
767
0
l_uint32   *datas, *lines;
768
0
l_float32  *array;
769
0
NUMA       *na;
770
771
0
    if (!box)
772
0
        return pixGetCmapHistogram(pixs, factor);
773
0
    if (!pixs)
774
0
        return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
775
0
    if (pixGetColormap(pixs) == NULL)
776
0
        return (NUMA *)ERROR_PTR("pixs not cmapped", __func__, NULL);
777
0
    if (factor < 1)
778
0
        return (NUMA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
779
0
    pixGetDimensions(pixs, &w, &h, &d);
780
0
    if (d != 2 && d != 4 && d != 8)
781
0
        return (NUMA *)ERROR_PTR("d not 2, 4 or 8", __func__, NULL);
782
783
0
    size = 1 << d;
784
0
    if ((na = numaCreate(size)) == NULL)
785
0
        return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
786
0
    numaSetCount(na, size);  /* all initialized to 0.0 */
787
0
    array = numaGetFArray(na, L_NOCOPY);
788
789
0
    datas = pixGetData(pixs);
790
0
    wpls = pixGetWpl(pixs);
791
0
    boxGetGeometry(box, &bx, &by, &bw, &bh);
792
793
0
    for (i = 0; i < bh; i += factor) {
794
0
        if (by + i < 0 || by + i >= h) continue;
795
0
        lines = datas + (by + i) * wpls;
796
0
        for (j = 0; j < bw; j += factor) {
797
0
            if (bx + j < 0 || bx + j >= w) continue;
798
0
            if (d == 8)
799
0
                val = GET_DATA_BYTE(lines, bx + j);
800
0
            else if (d == 4)
801
0
                val = GET_DATA_QBIT(lines, bx + j);
802
0
            else  /* d == 2 */
803
0
                val = GET_DATA_DIBIT(lines, bx + j);
804
0
            array[val] += 1.0;
805
0
        }
806
0
    }
807
808
0
    return na;
809
0
}
810
811
812
/*!
813
 * \brief   pixCountRGBColorsByHash()
814
 *
815
 * \param[in]    pixs       rgb or rgba
816
 * \param[out]   pncolors   number of colors found
817
 * \return  0 if OK, 1 on error
818
 *
819
 * <pre>
820
 * Notes:
821
 *      (1) This is about 4x faster than pixCountRGBColors(),
822
 *          which uses an ordered map.
823
 * </pre>
824
 */
825
l_ok
826
pixCountRGBColorsByHash(PIX      *pixs,
827
                        l_int32  *pncolors)
828
0
{
829
0
L_DNA  *da1, *da2;
830
831
0
    if (!pncolors)
832
0
        return ERROR_INT("&ncolors not defined", __func__, 1);
833
0
    *pncolors = 0;
834
0
    if (!pixs || pixGetDepth(pixs) != 32)
835
0
        return ERROR_INT("pixs not defined or not 32 bpp", __func__, 1);
836
0
    da1 = pixConvertDataToDna(pixs);
837
0
    l_dnaRemoveDupsByHmap(da1, &da2, NULL);
838
0
    *pncolors = l_dnaGetCount(da2);
839
0
    l_dnaDestroy(&da1);
840
0
    l_dnaDestroy(&da2);
841
0
    return 0;
842
0
}
843
844
845
/*!
846
 * \brief   pixCountRGBColors()
847
 *
848
 * \param[in]    pixs       rgb or rgba
849
 * \param[in]    factor     subsampling factor; integer >= 1
850
 * \param[out]   pncolors   number of colors found
851
 * \return  0 if OK, 1 on error
852
 *
853
 * <pre>
854
 * Notes:
855
 *      (1) If %factor == 1, this gives the exact number of colors.
856
 *      (2) This is about 4x slower than pixCountRGBColorsByHash().
857
 * </pre>
858
 */
859
l_ok
860
pixCountRGBColors(PIX      *pixs,
861
                  l_int32   factor,
862
                  l_int32  *pncolors)
863
0
{
864
0
L_AMAP  *amap;
865
866
0
    if (!pncolors)
867
0
        return ERROR_INT("&ncolors not defined", __func__, 1);
868
0
    *pncolors = 0;
869
0
    if (!pixs || pixGetDepth(pixs) != 32)
870
0
        return ERROR_INT("pixs not defined or not 32 bpp", __func__, 1);
871
0
    if (factor <= 0)
872
0
        return ERROR_INT("factor must be > 0", __func__, 1);
873
0
    amap = pixGetColorAmapHistogram(pixs, factor);
874
0
    *pncolors = l_amapSize(amap);
875
0
    l_amapDestroy(&amap);
876
0
    return 0;
877
0
}
878
879
880
/*!
881
 * \brief   pixGetColorAmapHistogram()
882
 *
883
 * \param[in]   pixs    rgb or rgba
884
 * \param[in]   factor  subsampling factor; integer >= 1
885
 * \return  amap, or NULL on error
886
 *
887
 * <pre>
888
 * Notes:
889
 *      (1) This generates an ordered map from pixel value to histogram count.
890
 *      (2) Use amapGetCountForColor() to use the map to look up a count.
891
 * </pre>
892
 */
893
L_AMAP  *
894
pixGetColorAmapHistogram(PIX     *pixs,
895
                         l_int32  factor)
896
0
{
897
0
l_int32    i, j, w, h, wpl;
898
0
l_uint32  *data, *line;
899
0
L_AMAP    *amap;
900
0
RB_TYPE    key, value;
901
0
RB_TYPE   *pval;
902
903
0
    if (!pixs)
904
0
        return (L_AMAP *)ERROR_PTR("pixs not defined", __func__, NULL);
905
0
    if (pixGetDepth(pixs) != 32)
906
0
        return (L_AMAP *)ERROR_PTR("pixs not 32 bpp", __func__, NULL);
907
0
    if (factor <= 0)
908
0
        return (L_AMAP *)ERROR_PTR("factor must be > 0", __func__, NULL);
909
0
    pixGetDimensions(pixs, &w, &h, NULL);
910
0
    data = pixGetData(pixs);
911
0
    wpl = pixGetWpl(pixs);
912
0
    amap = l_amapCreate(L_UINT_TYPE);
913
0
    for (i = 0; i < h; i += factor) {
914
0
        line = data + i * wpl;
915
0
        for (j = 0; j < w; j += factor) {
916
0
            key.utype = line[j];
917
0
            pval = l_amapFind(amap, key);
918
0
            if (!pval)
919
0
                value.itype = 1;
920
0
            else
921
0
                value.itype = 1 + pval->itype;
922
0
            l_amapInsert(amap, key, value);
923
0
        }
924
0
    }
925
926
0
    return amap;
927
0
}
928
929
930
/*!
931
 * \brief   amapGetCountForColor()
932
 *
933
 * \param[in]   amap   map from pixel value to count
934
 * \param[in]   val    rgb or rgba pixel value
935
 * \return  count, or -1 on error
936
 *
937
 * <pre>
938
 * Notes:
939
 *      (1) The ordered map is made by pixGetColorAmapHistogram().
940
 * </pre>
941
 */
942
l_int32
943
amapGetCountForColor(L_AMAP   *amap,
944
                     l_uint32  val)
945
0
{
946
0
RB_TYPE   key;
947
0
RB_TYPE  *pval;
948
949
0
    if (!amap)
950
0
        return ERROR_INT("amap not defined", __func__, -1);
951
952
0
    key.utype = val;
953
0
    pval = l_amapFind(amap, key);
954
0
    return (pval) ? pval->itype : 0;
955
0
}
956
957
958
/*!
959
 * \brief   pixGetRankValue()
960
 *
961
 * \param[in]    pixs     8 bpp, 32 bpp or colormapped
962
 * \param[in]    factor   subsampling factor; integer >= 1
963
 * \param[in]    rank     between 0.0 and 1.0; 1.0 is brightest, 0.0 is darkest
964
 * \param[out]   pvalue   pixel value corresponding to input rank
965
 * \return  0 if OK, 1 on error
966
 *
967
 * <pre>
968
 * Notes:
969
 *      (1) Simple function to get a rank value (color) of an image.
970
 *          For a color image, the median value (rank = 0.5) can be
971
 *          used to linearly remap the colors based on the median
972
 *          of a target image, using pixLinearMapToTargetColor().
973
 *      (2) For RGB, this treats each color component independently.
974
 *          It calls pixGetGrayHistogramMasked() on each component, and
975
 *          uses the returned gray histogram to get the rank value.
976
 *          It then combines the 3 rank values into a color pixel.
977
 * </pre>
978
 */
979
l_ok
980
pixGetRankValue(PIX       *pixs,
981
                l_int32    factor,
982
                l_float32  rank,
983
                l_uint32  *pvalue)
984
0
{
985
0
l_int32    d;
986
0
l_float32  val, rval, gval, bval;
987
0
PIX       *pixt;
988
0
PIXCMAP   *cmap;
989
990
0
    if (!pvalue)
991
0
        return ERROR_INT("&value not defined", __func__, 1);
992
0
    *pvalue = 0;
993
0
    if (!pixs)
994
0
        return ERROR_INT("pixs not defined", __func__, 1);
995
0
    d = pixGetDepth(pixs);
996
0
    cmap = pixGetColormap(pixs);
997
0
    if (d != 8 && d != 32 && !cmap)
998
0
        return ERROR_INT("pixs not 8 or 32 bpp, or cmapped", __func__, 1);
999
0
    if (cmap)
1000
0
        pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
1001
0
    else
1002
0
        pixt = pixClone(pixs);
1003
0
    d = pixGetDepth(pixt);
1004
1005
0
    if (d == 8) {
1006
0
        pixGetRankValueMasked(pixt, NULL, 0, 0, factor, rank, &val, NULL);
1007
0
        *pvalue = lept_roundftoi(val);
1008
0
    } else {
1009
0
        pixGetRankValueMaskedRGB(pixt, NULL, 0, 0, factor, rank,
1010
0
                                 &rval, &gval, &bval);
1011
0
        composeRGBPixel(lept_roundftoi(rval), lept_roundftoi(gval),
1012
0
                        lept_roundftoi(bval), pvalue);
1013
0
    }
1014
1015
0
    pixDestroy(&pixt);
1016
0
    return 0;
1017
0
}
1018
1019
1020
/*!
1021
 * \brief   pixGetRankValueMaskedRGB()
1022
 *
1023
 * \param[in]    pixs     32 bpp
1024
 * \param[in]    pixm     [optional] 1 bpp mask over which rank val is to be taken;
1025
 *                        use all pixels if null
1026
 * \param[in]    x, y     UL corner of pixm relative to the UL corner of pixs;
1027
 *                        can be < 0; these values are ignored if pixm is null
1028
 * \param[in]    factor   subsampling factor; integer >= 1
1029
 * \param[in]    rank     between 0.0 and 1.0; 1.0 is brightest, 0.0 is darkest
1030
 * \param[out]   prval    [optional] red component val for input rank
1031
 * \param[out]   pgval    [optional] green component val for input rank
1032
 * \param[out]   pbval    [optional] blue component val for input rank
1033
 * \return  0 if OK, 1 on error
1034
 *
1035
 * <pre>
1036
 * Notes:
1037
 *      (1) Computes the rank component values of pixels in pixs that
1038
 *          are under the fg of the optional mask.  If the mask is null, it
1039
 *          computes the average of the pixels in pixs.
1040
 *      (2) Set the subsampling %factor > 1 to reduce the amount of
1041
 *          computation.
1042
 *      (4) Input x,y are ignored unless pixm exists.
1043
 *      (5) The rank must be in [0.0 ... 1.0], where the brightest pixel
1044
 *          has rank 1.0.  For the median pixel value, use 0.5.
1045
 * </pre>
1046
 */
1047
l_ok
1048
pixGetRankValueMaskedRGB(PIX        *pixs,
1049
                         PIX        *pixm,
1050
                         l_int32     x,
1051
                         l_int32     y,
1052
                         l_int32     factor,
1053
                         l_float32   rank,
1054
                         l_float32  *prval,
1055
                         l_float32  *pgval,
1056
                         l_float32  *pbval)
1057
0
{
1058
0
l_float32  scale;
1059
0
PIX       *pixmt, *pixt;
1060
1061
0
    if (prval) *prval = 0.0;
1062
0
    if (pgval) *pgval = 0.0;
1063
0
    if (pbval) *pbval = 0.0;
1064
0
    if (!prval && !pgval && !pbval)
1065
0
        return ERROR_INT("no results requested", __func__, 1);
1066
0
    if (!pixs)
1067
0
        return ERROR_INT("pixs not defined", __func__, 1);
1068
0
    if (pixGetDepth(pixs) != 32)
1069
0
        return ERROR_INT("pixs not 32 bpp", __func__, 1);
1070
0
    if (pixm && pixGetDepth(pixm) != 1)
1071
0
        return ERROR_INT("pixm not 1 bpp", __func__, 1);
1072
0
    if (factor < 1)
1073
0
        return ERROR_INT("sampling factor must be >= 1", __func__, 1);
1074
0
    if (rank < 0.0 || rank > 1.0)
1075
0
        return ERROR_INT("rank not in [0.0 ... 1.0]", __func__, 1);
1076
1077
0
    pixmt = NULL;
1078
0
    if (pixm) {
1079
0
        scale = 1.0f / (l_float32)factor;
1080
0
        pixmt = pixScale(pixm, scale, scale);
1081
0
    }
1082
0
    if (prval) {
1083
0
        pixt = pixScaleRGBToGrayFast(pixs, factor, COLOR_RED);
1084
0
        pixGetRankValueMasked(pixt, pixmt, x / factor, y / factor,
1085
0
                              factor, rank, prval, NULL);
1086
0
        pixDestroy(&pixt);
1087
0
    }
1088
0
    if (pgval) {
1089
0
        pixt = pixScaleRGBToGrayFast(pixs, factor, COLOR_GREEN);
1090
0
        pixGetRankValueMasked(pixt, pixmt, x / factor, y / factor,
1091
0
                              factor, rank, pgval, NULL);
1092
0
        pixDestroy(&pixt);
1093
0
    }
1094
0
    if (pbval) {
1095
0
        pixt = pixScaleRGBToGrayFast(pixs, factor, COLOR_BLUE);
1096
0
        pixGetRankValueMasked(pixt, pixmt, x / factor, y / factor,
1097
0
                              factor, rank, pbval, NULL);
1098
0
        pixDestroy(&pixt);
1099
0
    }
1100
0
    pixDestroy(&pixmt);
1101
0
    return 0;
1102
0
}
1103
1104
1105
/*!
1106
 * \brief   pixGetRankValueMasked()
1107
 *
1108
 * \param[in]    pixs     8 bpp, or colormapped
1109
 * \param[in]    pixm     [optional] 1 bpp mask, over which the rank val
1110
 *                        is to be taken; use all pixels if null
1111
 * \param[in]    x, y     UL corner of pixm relative to the UL corner of pixs;
1112
 *                        can be < 0; these values are ignored if pixm is null
1113
 * \param[in]    factor   subsampling factor; integer >= 1
1114
 * \param[in]    rank     between 0.0 and 1.0; 1.0 is brightest, 0.0 is darkest
1115
 * \param[out]   pval     pixel value corresponding to input rank
1116
 * \param[out]   pna     [optional] of histogram
1117
 * \return  0 if OK, 1 on error
1118
 *
1119
 * <pre>
1120
 * Notes:
1121
 *      (1) Computes the rank value of pixels in pixs that are under
1122
 *          the fg of the optional mask.  If the mask is null, it
1123
 *          computes the average of the pixels in pixs.
1124
 *      (2) Set the subsampling %factor > 1 to reduce the amount of
1125
 *          computation.
1126
 *      (3) Clipping of pixm (if it exists) to pixs is done in the inner loop.
1127
 *      (4) Input x,y are ignored unless pixm exists.
1128
 *      (5) The rank must be in [0.0 ... 1.0], where the brightest pixel
1129
 *          has rank 1.0.  For the median pixel value, use 0.5.
1130
 *      (6) The histogram can optionally be returned, so that other rank
1131
 *          values can be extracted without recomputing the histogram.
1132
 *          In that case, just use
1133
 *              numaHistogramGetValFromRank(na, rank, &val);
1134
 *          on the returned Numa for additional rank values.
1135
 * </pre>
1136
 */
1137
l_ok
1138
pixGetRankValueMasked(PIX        *pixs,
1139
                      PIX        *pixm,
1140
                      l_int32     x,
1141
                      l_int32     y,
1142
                      l_int32     factor,
1143
                      l_float32   rank,
1144
                      l_float32  *pval,
1145
                      NUMA      **pna)
1146
0
{
1147
0
NUMA  *na;
1148
1149
0
    if (pna) *pna = NULL;
1150
0
    if (!pval)
1151
0
        return ERROR_INT("&val not defined", __func__, 1);
1152
0
    *pval = 0.0;
1153
0
    if (!pixs)
1154
0
        return ERROR_INT("pixs not defined", __func__, 1);
1155
0
    if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs))
1156
0
        return ERROR_INT("pixs neither 8 bpp nor colormapped", __func__, 1);
1157
0
    if (pixm && pixGetDepth(pixm) != 1)
1158
0
        return ERROR_INT("pixm not 1 bpp", __func__, 1);
1159
0
    if (factor < 1)
1160
0
        return ERROR_INT("sampling factor must be >= 1", __func__, 1);
1161
0
    if (rank < 0.0 || rank > 1.0)
1162
0
        return ERROR_INT("rank not in [0.0 ... 1.0]", __func__, 1);
1163
1164
0
    if ((na = pixGetGrayHistogramMasked(pixs, pixm, x, y, factor)) == NULL)
1165
0
        return ERROR_INT("na not made", __func__, 1);
1166
0
    numaHistogramGetValFromRank(na, rank, pval);
1167
0
    if (pna)
1168
0
        *pna = na;
1169
0
    else
1170
0
        numaDestroy(&na);
1171
1172
0
    return 0;
1173
0
}
1174
1175
1176
/*!
1177
 * \brief   pixGetPixelAverage()
1178
 *
1179
 * \param[in]    pixs     8 or 32 bpp, or colormapped
1180
 * \param[in]    pixm     [optional] 1 bpp mask over which average is
1181
 *                        to be taken; use all pixels if null
1182
 * \param[in]    x, y     UL corner of pixm relative to the UL corner of pixs;
1183
 *                        can be < 0
1184
 * \param[in]    factor   subsampling factor; >= 1
1185
 * \param[out]   pval     average pixel value
1186
 * \return  0 if OK, 1 on error
1187
 *
1188
 * <pre>
1189
 * Notes:
1190
 *      (1) For rgb pix, this is a more direct computation of the
1191
 *          average value of the pixels in %pixs that are under the
1192
 *          mask %pixm. It is faster than pixGetPixelStats(), which
1193
 *          calls pixGetAverageMaskedRGB() and has the overhead of
1194
 *          generating a temporary pix of each of the three components;
1195
 *          this can take most of the time if %factor > 1.
1196
 *      (2) If %pixm is null, this gives the average value of all
1197
 *          pixels in %pixs.  The returned value is an integer.
1198
 *      (3) For color %pixs, the returned pixel value is in the standard
1199
 *          uint32 RGBA packing.
1200
 *      (4) Clipping of pixm (if it exists) to pixs is done in the inner loop.
1201
 *      (5) Input x,y are ignored if %pixm does not exist.
1202
 *      (6) For general averaging of 1, 2, 4 or 8 bpp grayscale, use
1203
 *          pixAverageInRect().
1204
 * </pre>
1205
 */
1206
l_ok
1207
pixGetPixelAverage(PIX       *pixs,
1208
                   PIX       *pixm,
1209
                   l_int32    x,
1210
                   l_int32    y,
1211
                   l_int32    factor,
1212
                   l_uint32  *pval)
1213
0
{
1214
0
l_int32    i, j, w, h, d, wm, hm, wpl1, wplm, val, rval, gval, bval, count;
1215
0
l_uint32  *data1, *datam, *line1, *linem;
1216
0
l_float64  sum, rsum, gsum, bsum;
1217
0
PIX       *pix1;
1218
1219
0
    if (!pval)
1220
0
        return ERROR_INT("&val not defined", __func__, 1);
1221
0
    *pval = 0;
1222
0
    if (!pixs)
1223
0
        return ERROR_INT("pixs not defined", __func__, 1);
1224
0
    d = pixGetDepth(pixs);
1225
0
    if (d != 32 && !pixGetColormap(pixs))
1226
0
        return ERROR_INT("pixs not rgb or colormapped", __func__, 1);
1227
0
    if (pixm && pixGetDepth(pixm) != 1)
1228
0
        return ERROR_INT("pixm not 1 bpp", __func__, 1);
1229
0
    if (factor < 1)
1230
0
        return ERROR_INT("sampling factor must be >= 1", __func__, 1);
1231
1232
0
    if (pixGetColormap(pixs))
1233
0
        pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
1234
0
    else
1235
0
        pix1 = pixClone(pixs);
1236
0
    pixGetDimensions(pix1, &w, &h, &d);
1237
0
    if (d == 1) {
1238
0
        pixDestroy(&pix1);
1239
0
        return ERROR_INT("pix1 is just 1 bpp", __func__, 1);
1240
0
    }
1241
0
    data1 = pixGetData(pix1);
1242
0
    wpl1 = pixGetWpl(pix1);
1243
1244
0
    sum = rsum = gsum = bsum = 0.0;
1245
0
    count = 0;
1246
0
    if (!pixm) {
1247
0
        for (i = 0; i < h; i += factor) {
1248
0
            line1 = data1 + i * wpl1;
1249
0
            for (j = 0; j < w; j += factor) {
1250
0
                if (d == 8) {
1251
0
                    val = GET_DATA_BYTE(line1, j);
1252
0
                    sum += val;
1253
0
                } else {  /* rgb */
1254
0
                    extractRGBValues(*(line1 + j), &rval, &gval, &bval);
1255
0
                    rsum += rval;
1256
0
                    gsum += gval;
1257
0
                    bsum += bval;
1258
0
                }
1259
0
                count++;
1260
0
            }
1261
0
        }
1262
0
    } else {  /* masked */
1263
0
        pixGetDimensions(pixm, &wm, &hm, NULL);
1264
0
        datam = pixGetData(pixm);
1265
0
        wplm = pixGetWpl(pixm);
1266
0
        for (i = 0; i < hm; i += factor) {
1267
0
            if (y + i < 0 || y + i >= h) continue;
1268
0
            line1 = data1 + (y + i) * wpl1;
1269
0
            linem = datam + i * wplm;
1270
0
            for (j = 0; j < wm; j += factor) {
1271
0
                if (x + j < 0 || x + j >= w) continue;
1272
0
                if (GET_DATA_BIT(linem, j)) {
1273
0
                    if (d == 8) {
1274
0
                        val = GET_DATA_BYTE(line1, x + j);
1275
0
                        sum += val;
1276
0
                    } else {  /* rgb */
1277
0
                        extractRGBValues(*(line1 + x + j), &rval, &gval, &bval);
1278
0
                        rsum += rval;
1279
0
                        gsum += gval;
1280
0
                        bsum += bval;
1281
0
                    }
1282
0
                    count++;
1283
0
                }
1284
0
            }
1285
0
        }
1286
0
    }
1287
1288
0
    pixDestroy(&pix1);
1289
0
    if (count == 0)
1290
0
        return ERROR_INT("no pixels sampled", __func__, 1);
1291
0
    if (d == 8) {
1292
0
        *pval = (l_uint32)(sum / (l_float64)count);
1293
0
    } else {  /* d == 32 */
1294
0
        rval = (l_uint32)(rsum / (l_float64)count);
1295
0
        gval = (l_uint32)(gsum / (l_float64)count);
1296
0
        bval = (l_uint32)(bsum / (l_float64)count);
1297
0
        composeRGBPixel(rval, gval, bval, pval);
1298
0
    }
1299
1300
0
    return 0;
1301
0
}
1302
1303
1304
/*!
1305
 * \brief   pixGetPixelStats()
1306
 *
1307
 * \param[in]    pixs     8 bpp, 32 bpp or colormapped
1308
 * \param[in]    factor   subsampling factor; integer >= 1
1309
 * \param[in]    type     L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE,
1310
 *                        L_STANDARD_DEVIATION, L_VARIANCE
1311
 * \param[out]   pvalue   pixel value corresponding to input type
1312
 * \return  0 if OK, 1 on error
1313
 *
1314
 * <pre>
1315
 * Notes:
1316
 *      (1) Simple function to get one of four statistical values of an image.
1317
 *      (2) It does not take a mask: it uses the entire image.
1318
 *      (3) To get the average pixel value of an RGB image, suggest using
1319
 *          pixGetPixelAverage(), which is considerably faster.
1320
 * </pre>
1321
 */
1322
l_ok
1323
pixGetPixelStats(PIX       *pixs,
1324
                 l_int32    factor,
1325
                 l_int32    type,
1326
                 l_uint32  *pvalue)
1327
0
{
1328
0
l_int32    d;
1329
0
l_float32  val, rval, gval, bval;
1330
0
PIX       *pixt;
1331
0
PIXCMAP   *cmap;
1332
1333
0
    if (!pvalue)
1334
0
        return ERROR_INT("&value not defined", __func__, 1);
1335
0
    *pvalue = 0;
1336
0
    if (!pixs)
1337
0
        return ERROR_INT("pixs not defined", __func__, 1);
1338
0
    d = pixGetDepth(pixs);
1339
0
    cmap = pixGetColormap(pixs);
1340
0
    if (d != 8 && d != 32 && !cmap)
1341
0
        return ERROR_INT("pixs not 8 or 32 bpp, or cmapped", __func__, 1);
1342
0
    if (cmap)
1343
0
        pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
1344
0
    else
1345
0
        pixt = pixClone(pixs);
1346
0
    d = pixGetDepth(pixt);
1347
1348
0
    if (d == 8) {
1349
0
        pixGetAverageMasked(pixt, NULL, 0, 0, factor, type, &val);
1350
0
        *pvalue = lept_roundftoi(val);
1351
0
    } else {
1352
0
        pixGetAverageMaskedRGB(pixt, NULL, 0, 0, factor, type,
1353
0
                               &rval, &gval, &bval);
1354
0
        composeRGBPixel(lept_roundftoi(rval), lept_roundftoi(gval),
1355
0
                        lept_roundftoi(bval), pvalue);
1356
0
    }
1357
1358
0
    pixDestroy(&pixt);
1359
0
    return 0;
1360
0
}
1361
1362
1363
/*!
1364
 * \brief   pixGetAverageMaskedRGB()
1365
 *
1366
 * \param[in]    pixs     32 bpp, or colormapped
1367
 * \param[in]    pixm     [optional] 1 bpp mask over which average is
1368
 *                        to be taken; use all pixels if null
1369
 * \param[in]    x, y     UL corner of pixm relative to the UL corner of pixs;
1370
 *                        can be < 0
1371
 * \param[in]    factor   subsampling factor; >= 1
1372
 * \param[in]    type     L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE,
1373
 *                        L_STANDARD_DEVIATION, L_VARIANCE
1374
 * \param[out]   prval    [optional] measured red value of given 'type'
1375
 * \param[out]   pgval    [optional] measured green value of given 'type'
1376
 * \param[out]   pbval    [optional] measured blue value of given 'type'
1377
 * \return  0 if OK, 1 on error
1378
 *
1379
 * <pre>
1380
 * Notes:
1381
 *      (1) For usage, see pixGetAverageMasked().
1382
 *      (2) If there is a colormap, it is removed before the 8 bpp
1383
 *          component images are extracted.
1384
 *      (3) A better name for this would be: pixGetPixelStatsRGB()
1385
 * </pre>
1386
 */
1387
l_ok
1388
pixGetAverageMaskedRGB(PIX        *pixs,
1389
                       PIX        *pixm,
1390
                       l_int32     x,
1391
                       l_int32     y,
1392
                       l_int32     factor,
1393
                       l_int32     type,
1394
                       l_float32  *prval,
1395
                       l_float32  *pgval,
1396
                       l_float32  *pbval)
1397
0
{
1398
0
l_int32   empty;
1399
0
PIX      *pixt;
1400
0
PIXCMAP  *cmap;
1401
1402
0
    if (prval) *prval = 0.0;
1403
0
    if (pgval) *pgval = 0.0;
1404
0
    if (pbval) *pbval = 0.0;
1405
0
    if (!prval && !pgval && !pbval)
1406
0
        return ERROR_INT("no values requested", __func__, 1);
1407
0
    if (!pixs)
1408
0
        return ERROR_INT("pixs not defined", __func__, 1);
1409
0
    cmap = pixGetColormap(pixs);
1410
0
    if (pixGetDepth(pixs) != 32 && !cmap)
1411
0
        return ERROR_INT("pixs neither 32 bpp nor colormapped", __func__, 1);
1412
0
    if (pixm && pixGetDepth(pixm) != 1)
1413
0
        return ERROR_INT("pixm not 1 bpp", __func__, 1);
1414
0
    if (factor < 1)
1415
0
        return ERROR_INT("sampling factor must be >= 1", __func__, 1);
1416
0
    if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE &&
1417
0
        type != L_STANDARD_DEVIATION && type != L_VARIANCE)
1418
0
        return ERROR_INT("invalid measure type", __func__, 1);
1419
0
    if (pixm) {
1420
0
        pixZero(pixm, &empty);
1421
0
        if (empty)
1422
0
            return ERROR_INT("empty mask", __func__, 1);
1423
0
    }
1424
1425
0
    if (prval) {
1426
0
        if (cmap)
1427
0
            pixt = pixGetRGBComponentCmap(pixs, COLOR_RED);
1428
0
        else
1429
0
            pixt = pixGetRGBComponent(pixs, COLOR_RED);
1430
0
        pixGetAverageMasked(pixt, pixm, x, y, factor, type, prval);
1431
0
        pixDestroy(&pixt);
1432
0
    }
1433
0
    if (pgval) {
1434
0
        if (cmap)
1435
0
            pixt = pixGetRGBComponentCmap(pixs, COLOR_GREEN);
1436
0
        else
1437
0
            pixt = pixGetRGBComponent(pixs, COLOR_GREEN);
1438
0
        pixGetAverageMasked(pixt, pixm, x, y, factor, type, pgval);
1439
0
        pixDestroy(&pixt);
1440
0
    }
1441
0
    if (pbval) {
1442
0
        if (cmap)
1443
0
            pixt = pixGetRGBComponentCmap(pixs, COLOR_BLUE);
1444
0
        else
1445
0
            pixt = pixGetRGBComponent(pixs, COLOR_BLUE);
1446
0
        pixGetAverageMasked(pixt, pixm, x, y, factor, type, pbval);
1447
0
        pixDestroy(&pixt);
1448
0
    }
1449
1450
0
    return 0;
1451
0
}
1452
1453
1454
/*!
1455
 * \brief   pixGetAverageMasked()
1456
 *
1457
 * \param[in]   pixs     8 or 16 bpp, or colormapped
1458
 * \param[in]   pixm     [optional] 1 bpp mask over which average is
1459
 *                       to be taken; use all pixels if null
1460
 * \param[in]   x, y     UL corner of pixm relative to the UL corner of pixs;
1461
 *                       can be < 0
1462
 * \param[in]   factor   subsampling factor; >= 1
1463
 * \param[in]   type     L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE,
1464
 *                       L_STANDARD_DEVIATION, L_VARIANCE
1465
 * \param[out]  pval     measured value of given 'type'
1466
 * \return  0 if OK, 1 on error
1467
 *
1468
 * <pre>
1469
 * Notes:
1470
 *      (1) Use L_MEAN_ABSVAL to get the average value of pixels in pixs
1471
 *          that are under the fg of the optional mask.  If the mask
1472
 *          is null, it finds the average of the pixels in pixs.
1473
 *      (2) Likewise, use L_ROOT_MEAN_SQUARE to get the rms value of
1474
 *          pixels in pixs, either masked or not; L_STANDARD_DEVIATION
1475
 *          to get the standard deviation from the mean of the pixels;
1476
 *          L_VARIANCE to get the average squared difference from the
1477
 *          expected value.  The variance is the square of the stdev.
1478
 *          For the standard deviation, we use
1479
 *              sqrt([([x] - x)]^2) = sqrt([x^2] - [x]^2)
1480
 *      (3) Set the subsampling %factor > 1 to reduce the amount of
1481
 *          computation.
1482
 *      (4) Clipping of pixm (if it exists) to pixs is done in the inner loop.
1483
 *      (5) Input x,y are ignored unless pixm exists.
1484
 *      (6) A better name for this would be: pixGetPixelStatsGray()
1485
 * </pre>
1486
 */
1487
l_ok
1488
pixGetAverageMasked(PIX        *pixs,
1489
                    PIX        *pixm,
1490
                    l_int32     x,
1491
                    l_int32     y,
1492
                    l_int32     factor,
1493
                    l_int32     type,
1494
                    l_float32  *pval)
1495
0
{
1496
0
l_int32    i, j, w, h, d, wm, hm, wplg, wplm, val, count, empty;
1497
0
l_uint32  *datag, *datam, *lineg, *linem;
1498
0
l_float64  sumave, summs, ave, meansq, var;
1499
0
PIX       *pixg;
1500
1501
0
    if (!pval)
1502
0
        return ERROR_INT("&val not defined", __func__, 1);
1503
0
    *pval = 0.0;
1504
0
    if (!pixs)
1505
0
        return ERROR_INT("pixs not defined", __func__, 1);
1506
0
    d = pixGetDepth(pixs);
1507
0
    if (d != 8 && d != 16 && !pixGetColormap(pixs))
1508
0
        return ERROR_INT("pixs not 8 or 16 bpp or colormapped", __func__, 1);
1509
0
    if (pixm && pixGetDepth(pixm) != 1)
1510
0
        return ERROR_INT("pixm not 1 bpp", __func__, 1);
1511
0
    if (factor < 1)
1512
0
        return ERROR_INT("sampling factor must be >= 1", __func__, 1);
1513
0
    if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE &&
1514
0
        type != L_STANDARD_DEVIATION && type != L_VARIANCE)
1515
0
        return ERROR_INT("invalid measure type", __func__, 1);
1516
0
    if (pixm) {
1517
0
        pixZero(pixm, &empty);
1518
0
        if (empty)
1519
0
            return ERROR_INT("empty mask", __func__, 1);
1520
0
    }
1521
1522
0
    if (pixGetColormap(pixs))
1523
0
        pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
1524
0
    else
1525
0
        pixg = pixClone(pixs);
1526
0
    pixGetDimensions(pixg, &w, &h, &d);
1527
0
    datag = pixGetData(pixg);
1528
0
    wplg = pixGetWpl(pixg);
1529
1530
0
    sumave = summs = 0.0;
1531
0
    count = 0;
1532
0
    if (!pixm) {
1533
0
        for (i = 0; i < h; i += factor) {
1534
0
            lineg = datag + i * wplg;
1535
0
            for (j = 0; j < w; j += factor) {
1536
0
                if (d == 8)
1537
0
                    val = GET_DATA_BYTE(lineg, j);
1538
0
                else  /* d == 16 */
1539
0
                    val = GET_DATA_TWO_BYTES(lineg, j);
1540
0
                if (type != L_ROOT_MEAN_SQUARE)
1541
0
                    sumave += val;
1542
0
                if (type != L_MEAN_ABSVAL)
1543
0
                    summs += (l_float64)(val) * val;
1544
0
                count++;
1545
0
            }
1546
0
        }
1547
0
    } else {
1548
0
        pixGetDimensions(pixm, &wm, &hm, NULL);
1549
0
        datam = pixGetData(pixm);
1550
0
        wplm = pixGetWpl(pixm);
1551
0
        for (i = 0; i < hm; i += factor) {
1552
0
            if (y + i < 0 || y + i >= h) continue;
1553
0
            lineg = datag + (y + i) * wplg;
1554
0
            linem = datam + i * wplm;
1555
0
            for (j = 0; j < wm; j += factor) {
1556
0
                if (x + j < 0 || x + j >= w) continue;
1557
0
                if (GET_DATA_BIT(linem, j)) {
1558
0
                    if (d == 8)
1559
0
                        val = GET_DATA_BYTE(lineg, x + j);
1560
0
                    else  /* d == 16 */
1561
0
                        val = GET_DATA_TWO_BYTES(lineg, x + j);
1562
0
                    if (type != L_ROOT_MEAN_SQUARE)
1563
0
                        sumave += val;
1564
0
                    if (type != L_MEAN_ABSVAL)
1565
0
                        summs += (l_float64)(val) * val;
1566
0
                    count++;
1567
0
                }
1568
0
            }
1569
0
        }
1570
0
    }
1571
1572
0
    pixDestroy(&pixg);
1573
0
    if (count == 0)
1574
0
        return ERROR_INT("no pixels sampled", __func__, 1);
1575
0
    ave = sumave / (l_float64)count;
1576
0
    meansq = summs / (l_float64)count;
1577
0
    var = meansq - ave * ave;
1578
0
    if (type == L_MEAN_ABSVAL)
1579
0
        *pval = (l_float32)ave;
1580
0
    else if (type == L_ROOT_MEAN_SQUARE)
1581
0
        *pval = (l_float32)sqrt(meansq);
1582
0
    else if (type == L_STANDARD_DEVIATION)
1583
0
        *pval = (l_float32)sqrt(var);
1584
0
    else  /* type == L_VARIANCE */
1585
0
        *pval = (l_float32)var;
1586
1587
0
    return 0;
1588
0
}
1589
1590
1591
/*!
1592
 * \brief   pixGetAverageTiledRGB()
1593
 *
1594
 * \param[in]   pixs     32 bpp, or colormapped
1595
 * \param[in]   sx, sy   tile size; must be at least 2 x 2
1596
 * \param[in]   type     L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, L_STANDARD_DEVIATION
1597
 * \param[out]  ppixr    [optional] tiled 'average' of red component
1598
 * \param[out]  ppixg    [optional] tiled 'average' of green component
1599
 * \param[out]  ppixb    [optional] tiled 'average' of blue component
1600
 * \return  0 if OK, 1 on error
1601
 *
1602
 * <pre>
1603
 * Notes:
1604
 *      (1) For usage, see pixGetAverageTiled().
1605
 *      (2) If there is a colormap, it is removed before the 8 bpp
1606
 *          component images are extracted.
1607
 * </pre>
1608
 */
1609
l_ok
1610
pixGetAverageTiledRGB(PIX     *pixs,
1611
                      l_int32  sx,
1612
                      l_int32  sy,
1613
                      l_int32  type,
1614
                      PIX    **ppixr,
1615
                      PIX    **ppixg,
1616
                      PIX    **ppixb)
1617
0
{
1618
0
PIX      *pixt;
1619
0
PIXCMAP  *cmap;
1620
1621
0
    if (ppixr) *ppixr = NULL;
1622
0
    if (ppixg) *ppixg = NULL;
1623
0
    if (ppixb) *ppixb = NULL;
1624
0
    if (!ppixr && !ppixg && !ppixb)
1625
0
        return ERROR_INT("no data requested", __func__, 1);
1626
0
    if (!pixs)
1627
0
        return ERROR_INT("pixs not defined", __func__, 1);
1628
0
    cmap = pixGetColormap(pixs);
1629
0
    if (pixGetDepth(pixs) != 32 && !cmap)
1630
0
        return ERROR_INT("pixs neither 32 bpp nor colormapped", __func__, 1);
1631
0
    if (sx < 2 || sy < 2)
1632
0
        return ERROR_INT("sx and sy not both > 1", __func__, 1);
1633
0
    if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE &&
1634
0
        type != L_STANDARD_DEVIATION)
1635
0
        return ERROR_INT("invalid measure type", __func__, 1);
1636
1637
0
    if (ppixr) {
1638
0
        if (cmap)
1639
0
            pixt = pixGetRGBComponentCmap(pixs, COLOR_RED);
1640
0
        else
1641
0
            pixt = pixGetRGBComponent(pixs, COLOR_RED);
1642
0
        *ppixr = pixGetAverageTiled(pixt, sx, sy, type);
1643
0
        pixDestroy(&pixt);
1644
0
    }
1645
0
    if (ppixg) {
1646
0
        if (cmap)
1647
0
            pixt = pixGetRGBComponentCmap(pixs, COLOR_GREEN);
1648
0
        else
1649
0
            pixt = pixGetRGBComponent(pixs, COLOR_GREEN);
1650
0
        *ppixg = pixGetAverageTiled(pixt, sx, sy, type);
1651
0
        pixDestroy(&pixt);
1652
0
    }
1653
0
    if (ppixb) {
1654
0
        if (cmap)
1655
0
            pixt = pixGetRGBComponentCmap(pixs, COLOR_BLUE);
1656
0
        else
1657
0
            pixt = pixGetRGBComponent(pixs, COLOR_BLUE);
1658
0
        *ppixb = pixGetAverageTiled(pixt, sx, sy, type);
1659
0
        pixDestroy(&pixt);
1660
0
    }
1661
1662
0
    return 0;
1663
0
}
1664
1665
1666
/*!
1667
 * \brief   pixGetAverageTiled()
1668
 *
1669
 * \param[in]   pixs    8 bpp, or colormapped
1670
 * \param[in]   sx, sy  tile size; must be at least 2 x 2
1671
 * \param[in]   type    L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, L_STANDARD_DEVIATION
1672
 * \return  pixd average values in each tile, or NULL on error
1673
 *
1674
 * <pre>
1675
 * Notes:
1676
 *      (1) Only computes for tiles that are entirely contained in pixs.
1677
 *      (2) Use L_MEAN_ABSVAL to get the average abs value within the tile;
1678
 *          L_ROOT_MEAN_SQUARE to get the rms value within each tile;
1679
 *          L_STANDARD_DEVIATION to get the standard dev. from the average
1680
 *          within each tile.
1681
 *      (3) If colormapped, converts to 8 bpp gray.
1682
 * </pre>
1683
 */
1684
PIX *
1685
pixGetAverageTiled(PIX     *pixs,
1686
                   l_int32  sx,
1687
                   l_int32  sy,
1688
                   l_int32  type)
1689
0
{
1690
0
l_int32    i, j, k, m, w, h, wd, hd, d, pos, wplt, wpld, valt;
1691
0
l_uint32  *datat, *datad, *linet, *lined, *startt;
1692
0
l_float64  sumave, summs, ave, meansq, normfact;
1693
0
PIX       *pixt, *pixd;
1694
1695
0
    if (!pixs)
1696
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1697
0
    pixGetDimensions(pixs, &w, &h, &d);
1698
0
    if (d != 8 && !pixGetColormap(pixs))
1699
0
        return (PIX *)ERROR_PTR("pixs not 8 bpp or cmapped", __func__, NULL);
1700
0
    if (sx < 2 || sy < 2)
1701
0
        return (PIX *)ERROR_PTR("sx and sy not both > 1", __func__, NULL);
1702
0
    wd = w / sx;
1703
0
    hd = h / sy;
1704
0
    if (wd < 1 || hd < 1)
1705
0
        return (PIX *)ERROR_PTR("wd or hd == 0", __func__, NULL);
1706
0
    if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE &&
1707
0
        type != L_STANDARD_DEVIATION)
1708
0
        return (PIX *)ERROR_PTR("invalid measure type", __func__, NULL);
1709
1710
0
    pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
1711
0
    pixd = pixCreate(wd, hd, 8);
1712
0
    datat = pixGetData(pixt);
1713
0
    wplt = pixGetWpl(pixt);
1714
0
    datad = pixGetData(pixd);
1715
0
    wpld = pixGetWpl(pixd);
1716
0
    normfact = 1. / (l_float64)(sx * sy);
1717
0
    for (i = 0; i < hd; i++) {
1718
0
        lined = datad + i * wpld;
1719
0
        linet = datat + i * sy * wplt;
1720
0
        for (j = 0; j < wd; j++) {
1721
0
            if (type == L_MEAN_ABSVAL || type == L_STANDARD_DEVIATION) {
1722
0
                sumave = 0.0;
1723
0
                for (k = 0; k < sy; k++) {
1724
0
                    startt = linet + k * wplt;
1725
0
                    for (m = 0; m < sx; m++) {
1726
0
                        pos = j * sx + m;
1727
0
                        valt = GET_DATA_BYTE(startt, pos);
1728
0
                        sumave += valt;
1729
0
                    }
1730
0
                }
1731
0
                ave = normfact * sumave;
1732
0
            }
1733
0
            if (type == L_ROOT_MEAN_SQUARE || type == L_STANDARD_DEVIATION) {
1734
0
                summs = 0.0;
1735
0
                for (k = 0; k < sy; k++) {
1736
0
                    startt = linet + k * wplt;
1737
0
                    for (m = 0; m < sx; m++) {
1738
0
                        pos = j * sx + m;
1739
0
                        valt = GET_DATA_BYTE(startt, pos);
1740
0
                        summs += (l_float64)(valt) * valt;
1741
0
                    }
1742
0
                }
1743
0
                meansq = normfact * summs;
1744
0
            }
1745
0
            if (type == L_MEAN_ABSVAL)
1746
0
                valt = (l_int32)(ave + 0.5);
1747
0
            else if (type == L_ROOT_MEAN_SQUARE)
1748
0
                valt = (l_int32)(sqrt(meansq) + 0.5);
1749
0
            else  /* type == L_STANDARD_DEVIATION */
1750
0
                valt = (l_int32)(sqrt(meansq - ave * ave) + 0.5);
1751
0
            SET_DATA_BYTE(lined, j, valt);
1752
0
        }
1753
0
    }
1754
1755
0
    pixDestroy(&pixt);
1756
0
    return pixd;
1757
0
}
1758
1759
1760
/*!
1761
 * \brief   pixRowStats()
1762
 *
1763
 * \param[in]    pixs          8 bpp; not cmapped
1764
 * \param[in]    box           [optional] clipping box; can be null
1765
 * \param[out]   pnamean       [optional] numa of mean values
1766
 * \param[out]   pnamedian     [optional] numa of median values
1767
 * \param[out]   pnamode       [optional] numa of mode intensity values
1768
 * \param[out]   pnamodecount  [optional] numa of mode counts
1769
 * \param[out]   pnavar        [optional] numa of variance
1770
 * \param[out]   pnarootvar    [optional] numa of square root of variance
1771
 * \return  na numa of requested statistic for each row, or NULL on error
1772
 *
1773
 * <pre>
1774
 * Notes:
1775
 *      (1) This computes numas that represent column vectors of statistics,
1776
 *          with each of its values derived from the corresponding row of a Pix.
1777
 *      (2) Use NULL on input to prevent computation of any of the 5 numas.
1778
 *      (3) Other functions that compute pixel row statistics are:
1779
 *             pixCountPixelsByRow()
1780
 *             pixAverageByRow()
1781
 *             pixVarianceByRow()
1782
 *             pixGetRowStats()
1783
 * </pre>
1784
 */
1785
l_int32
1786
pixRowStats(PIX    *pixs,
1787
            BOX    *box,
1788
            NUMA  **pnamean,
1789
            NUMA  **pnamedian,
1790
            NUMA  **pnamode,
1791
            NUMA  **pnamodecount,
1792
            NUMA  **pnavar,
1793
            NUMA  **pnarootvar)
1794
0
{
1795
0
l_int32     i, j, k, w, h, val, wpls, sum, sumsq, target, max, modeval;
1796
0
l_int32     xstart, xend, ystart, yend, bw, bh;
1797
0
l_int32    *histo;
1798
0
l_uint32   *lines, *datas;
1799
0
l_float32   norm;
1800
0
l_float32  *famean, *fameansq, *favar, *farootvar;
1801
0
l_float32  *famedian, *famode, *famodecount;
1802
1803
0
    if (pnamean) *pnamean = NULL;
1804
0
    if (pnamedian) *pnamedian = NULL;
1805
0
    if (pnamode) *pnamode = NULL;
1806
0
    if (pnamodecount) *pnamodecount = NULL;
1807
0
    if (pnavar) *pnavar = NULL;
1808
0
    if (pnarootvar) *pnarootvar = NULL;
1809
0
    if (!pixs || pixGetDepth(pixs) != 8)
1810
0
        return ERROR_INT("pixs undefined or not 8 bpp", __func__, 1);
1811
0
    famean = fameansq = favar = farootvar = NULL;
1812
0
    famedian = famode = famodecount = NULL;
1813
1814
0
    pixGetDimensions(pixs, &w, &h, NULL);
1815
0
    if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
1816
0
                                 &bw, &bh) == 1)
1817
0
        return ERROR_INT("invalid clipping box", __func__, 1);
1818
1819
        /* We need the mean for variance and root variance */
1820
0
    datas = pixGetData(pixs);
1821
0
    wpls = pixGetWpl(pixs);
1822
0
    if (pnamean || pnavar || pnarootvar) {
1823
0
        norm = 1.f / (l_float32)bw;
1824
0
        famean = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32));
1825
0
        fameansq = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32));
1826
0
        if (pnavar || pnarootvar) {
1827
0
            favar = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32));
1828
0
            if (pnarootvar)
1829
0
                farootvar = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32));
1830
0
        }
1831
0
        for (i = ystart; i < yend; i++) {
1832
0
            sum = sumsq = 0;
1833
0
            lines = datas + i * wpls;
1834
0
            for (j = xstart; j < xend; j++) {
1835
0
                val = GET_DATA_BYTE(lines, j);
1836
0
                sum += val;
1837
0
                sumsq += val * val;
1838
0
            }
1839
0
            famean[i] = norm * sum;
1840
0
            fameansq[i] = norm * sumsq;
1841
0
            if (pnavar || pnarootvar) {
1842
0
                favar[i] = fameansq[i] - famean[i] * famean[i];
1843
0
                if (pnarootvar)
1844
0
                    farootvar[i] = sqrtf(favar[i]);
1845
0
            }
1846
0
        }
1847
0
        LEPT_FREE(fameansq);
1848
0
        if (pnamean)
1849
0
            *pnamean = numaCreateFromFArray(famean, bh, L_INSERT);
1850
0
        else
1851
0
            LEPT_FREE(famean);
1852
0
        if (pnavar)
1853
0
            *pnavar = numaCreateFromFArray(favar, bh, L_INSERT);
1854
0
        else
1855
0
            LEPT_FREE(favar);
1856
0
        if (pnarootvar)
1857
0
            *pnarootvar = numaCreateFromFArray(farootvar, bh, L_INSERT);
1858
0
    }
1859
1860
        /* We need a histogram to find the median and/or mode values */
1861
0
    if (pnamedian || pnamode || pnamodecount) {
1862
0
        histo = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1863
0
        if (pnamedian) {
1864
0
            *pnamedian = numaMakeConstant(0, bh);
1865
0
            famedian = numaGetFArray(*pnamedian, L_NOCOPY);
1866
0
        }
1867
0
        if (pnamode) {
1868
0
            *pnamode = numaMakeConstant(0, bh);
1869
0
            famode = numaGetFArray(*pnamode, L_NOCOPY);
1870
0
        }
1871
0
        if (pnamodecount) {
1872
0
            *pnamodecount = numaMakeConstant(0, bh);
1873
0
            famodecount = numaGetFArray(*pnamodecount, L_NOCOPY);
1874
0
        }
1875
0
        for (i = ystart; i < yend; i++) {
1876
0
            lines = datas + i * wpls;
1877
0
            memset(histo, 0, 1024);
1878
0
            for (j = xstart; j < xend; j++) {
1879
0
                val = GET_DATA_BYTE(lines, j);
1880
0
                histo[val]++;
1881
0
            }
1882
1883
0
            if (pnamedian) {
1884
0
                sum = 0;
1885
0
                target = (bw + 1) / 2;
1886
0
                for (k = 0; k < 256; k++) {
1887
0
                    sum += histo[k];
1888
0
                    if (sum >= target) {
1889
0
                        famedian[i] = k;
1890
0
                        break;
1891
0
                    }
1892
0
                }
1893
0
            }
1894
1895
0
            if (pnamode || pnamodecount) {
1896
0
                max = 0;
1897
0
                modeval = 0;
1898
0
                for (k = 0; k < 256; k++) {
1899
0
                    if (histo[k] > max) {
1900
0
                        max = histo[k];
1901
0
                        modeval = k;
1902
0
                    }
1903
0
                }
1904
0
                if (pnamode)
1905
0
                    famode[i] = modeval;
1906
0
                if (pnamodecount)
1907
0
                    famodecount[i] = max;
1908
0
            }
1909
0
        }
1910
0
        LEPT_FREE(histo);
1911
0
    }
1912
1913
0
    return 0;
1914
0
}
1915
1916
1917
/*!
1918
 * \brief   pixColumnStats()
1919
 *
1920
 * \param[in]    pixs          8 bpp; not cmapped
1921
 * \param[in]    box           [optional] clipping box; can be null
1922
 * \param[out]   pnamean       [optional] numa of mean values
1923
 * \param[out]   pnamedian     [optional] numa of median values
1924
 * \param[out]   pnamode       [optional] numa of mode intensity values
1925
 * \param[out]   pnamodecount  [optional] numa of mode counts
1926
 * \param[out]   pnavar        [optional] numa of variance
1927
 * \param[out]   pnarootvar    [optional] numa of square root of variance
1928
 * \return  na numa of requested statistic for each column,
1929
 *                  or NULL on error
1930
 *
1931
 * <pre>
1932
 * Notes:
1933
 *      (1) This computes numas that represent row vectors of statistics,
1934
 *          with each of its values derived from the corresponding col of a Pix.
1935
 *      (2) Use NULL on input to prevent computation of any of the 5 numas.
1936
 *      (3) Other functions that compute pixel column statistics are:
1937
 *             pixCountPixelsByColumn()
1938
 *             pixAverageByColumn()
1939
 *             pixVarianceByColumn()
1940
 *             pixGetColumnStats()
1941
 * </pre>
1942
 */
1943
l_int32
1944
pixColumnStats(PIX    *pixs,
1945
               BOX    *box,
1946
               NUMA  **pnamean,
1947
               NUMA  **pnamedian,
1948
               NUMA  **pnamode,
1949
               NUMA  **pnamodecount,
1950
               NUMA  **pnavar,
1951
               NUMA  **pnarootvar)
1952
0
{
1953
0
l_int32     i, j, k, w, h, val, wpls, sum, sumsq, target, max, modeval;
1954
0
l_int32     xstart, xend, ystart, yend, bw, bh;
1955
0
l_int32    *histo;
1956
0
l_uint32   *lines, *datas;
1957
0
l_float32   norm;
1958
0
l_float32  *famean, *fameansq, *favar, *farootvar;
1959
0
l_float32  *famedian, *famode, *famodecount;
1960
1961
0
    if (pnamean) *pnamean = NULL;
1962
0
    if (pnamedian) *pnamedian = NULL;
1963
0
    if (pnamode) *pnamode = NULL;
1964
0
    if (pnamodecount) *pnamodecount = NULL;
1965
0
    if (pnavar) *pnavar = NULL;
1966
0
    if (pnarootvar) *pnarootvar = NULL;
1967
0
    if (!pixs || pixGetDepth(pixs) != 8)
1968
0
        return ERROR_INT("pixs undefined or not 8 bpp", __func__, 1);
1969
0
    famean = fameansq = favar = farootvar = NULL;
1970
0
    famedian = famode = famodecount = NULL;
1971
1972
0
    pixGetDimensions(pixs, &w, &h, NULL);
1973
0
    if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
1974
0
                                 &bw, &bh) == 1)
1975
0
        return ERROR_INT("invalid clipping box", __func__, 1);
1976
1977
        /* We need the mean for variance and root variance */
1978
0
    datas = pixGetData(pixs);
1979
0
    wpls = pixGetWpl(pixs);
1980
0
    if (pnamean || pnavar || pnarootvar) {
1981
0
        norm = 1.f / (l_float32)bh;
1982
0
        famean = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32));
1983
0
        fameansq = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32));
1984
0
        if (pnavar || pnarootvar) {
1985
0
            favar = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32));
1986
0
            if (pnarootvar)
1987
0
                farootvar = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32));
1988
0
        }
1989
0
        for (j = xstart; j < xend; j++) {
1990
0
            sum = sumsq = 0;
1991
0
            for (i = ystart, lines = datas; i < yend; lines += wpls, i++) {
1992
0
                val = GET_DATA_BYTE(lines, j);
1993
0
                sum += val;
1994
0
                sumsq += val * val;
1995
0
            }
1996
0
            famean[j] = norm * sum;
1997
0
            fameansq[j] = norm * sumsq;
1998
0
            if (pnavar || pnarootvar) {
1999
0
                favar[j] = fameansq[j] - famean[j] * famean[j];
2000
0
                if (pnarootvar)
2001
0
                    farootvar[j] = sqrtf(favar[j]);
2002
0
            }
2003
0
        }
2004
0
        LEPT_FREE(fameansq);
2005
0
        if (pnamean)
2006
0
            *pnamean = numaCreateFromFArray(famean, bw, L_INSERT);
2007
0
        else
2008
0
            LEPT_FREE(famean);
2009
0
        if (pnavar)
2010
0
            *pnavar = numaCreateFromFArray(favar, bw, L_INSERT);
2011
0
        else
2012
0
            LEPT_FREE(favar);
2013
0
        if (pnarootvar)
2014
0
            *pnarootvar = numaCreateFromFArray(farootvar, bw, L_INSERT);
2015
0
    }
2016
2017
        /* We need a histogram to find the median and/or mode values */
2018
0
    if (pnamedian || pnamode || pnamodecount) {
2019
0
        histo = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
2020
0
        if (pnamedian) {
2021
0
            *pnamedian = numaMakeConstant(0, bw);
2022
0
            famedian = numaGetFArray(*pnamedian, L_NOCOPY);
2023
0
        }
2024
0
        if (pnamode) {
2025
0
            *pnamode = numaMakeConstant(0, bw);
2026
0
            famode = numaGetFArray(*pnamode, L_NOCOPY);
2027
0
        }
2028
0
        if (pnamodecount) {
2029
0
            *pnamodecount = numaMakeConstant(0, bw);
2030
0
            famodecount = numaGetFArray(*pnamodecount, L_NOCOPY);
2031
0
        }
2032
0
        for (j = xstart; j < xend; j++) {
2033
0
            memset(histo, 0, 1024);
2034
0
            for (i = ystart, lines = datas; i < yend; lines += wpls, i++) {
2035
0
                val = GET_DATA_BYTE(lines, j);
2036
0
                histo[val]++;
2037
0
            }
2038
2039
0
            if (pnamedian) {
2040
0
                sum = 0;
2041
0
                target = (bh + 1) / 2;
2042
0
                for (k = 0; k < 256; k++) {
2043
0
                    sum += histo[k];
2044
0
                    if (sum >= target) {
2045
0
                        famedian[j] = k;
2046
0
                        break;
2047
0
                    }
2048
0
                }
2049
0
            }
2050
2051
0
            if (pnamode || pnamodecount) {
2052
0
                max = 0;
2053
0
                modeval = 0;
2054
0
                for (k = 0; k < 256; k++) {
2055
0
                    if (histo[k] > max) {
2056
0
                        max = histo[k];
2057
0
                        modeval = k;
2058
0
                    }
2059
0
                }
2060
0
                if (pnamode)
2061
0
                    famode[j] = modeval;
2062
0
                if (pnamodecount)
2063
0
                    famodecount[j] = max;
2064
0
            }
2065
0
        }
2066
0
        LEPT_FREE(histo);
2067
0
    }
2068
2069
0
    return 0;
2070
0
}
2071
2072
2073
/*!
2074
 * \brief   pixGetRangeValues()
2075
 *
2076
 * \param[in]    pixs     8 bpp grayscale, 32 bpp rgb, or colormapped
2077
 * \param[in]    factor   subsampling factor; >= 1; ignored if colormapped
2078
 * \param[in]    color    L_SELECT_RED, L_SELECT_GREEN or L_SELECT_BLUE
2079
 * \param[out]   pminval  [optional] minimum value of component
2080
 * \param[out]   pmaxval  [optional] maximum value of component
2081
 * \return  0 if OK, 1 on error
2082
 *
2083
 * <pre>
2084
 * Notes:
2085
 *      (1) If pixs is 8 bpp grayscale, the color selection type is ignored.
2086
 * </pre>
2087
 */
2088
l_ok
2089
pixGetRangeValues(PIX      *pixs,
2090
                  l_int32   factor,
2091
                  l_int32   color,
2092
                  l_int32  *pminval,
2093
                  l_int32  *pmaxval)
2094
0
{
2095
0
l_int32   d;
2096
0
PIXCMAP  *cmap;
2097
2098
0
    if (pminval) *pminval = 0;
2099
0
    if (pmaxval) *pmaxval = 0;
2100
0
    if (!pminval && !pmaxval)
2101
0
        return ERROR_INT("no result requested", __func__, 1);
2102
0
    if (!pixs)
2103
0
        return ERROR_INT("pixs not defined", __func__, 1);
2104
2105
0
    cmap = pixGetColormap(pixs);
2106
0
    if (cmap)
2107
0
        return pixcmapGetRangeValues(cmap, color, pminval, pmaxval,
2108
0
                                     NULL, NULL);
2109
2110
0
    if (factor < 1)
2111
0
        return ERROR_INT("sampling factor must be >= 1", __func__, 1);
2112
0
    d = pixGetDepth(pixs);
2113
0
    if (d != 8 && d != 32)
2114
0
        return ERROR_INT("pixs not 8 or 32 bpp", __func__, 1);
2115
2116
0
    if (d == 8) {
2117
0
        pixGetExtremeValue(pixs, factor, L_SELECT_MIN,
2118
0
                           NULL, NULL, NULL, pminval);
2119
0
        pixGetExtremeValue(pixs, factor, L_SELECT_MAX,
2120
0
                           NULL, NULL, NULL, pmaxval);
2121
0
    } else if (color == L_SELECT_RED) {
2122
0
        pixGetExtremeValue(pixs, factor, L_SELECT_MIN,
2123
0
                           pminval, NULL, NULL, NULL);
2124
0
        pixGetExtremeValue(pixs, factor, L_SELECT_MAX,
2125
0
                           pmaxval, NULL, NULL, NULL);
2126
0
    } else if (color == L_SELECT_GREEN) {
2127
0
        pixGetExtremeValue(pixs, factor, L_SELECT_MIN,
2128
0
                           NULL, pminval, NULL, NULL);
2129
0
        pixGetExtremeValue(pixs, factor, L_SELECT_MAX,
2130
0
                           NULL, pmaxval, NULL, NULL);
2131
0
    } else if (color == L_SELECT_BLUE) {
2132
0
        pixGetExtremeValue(pixs, factor, L_SELECT_MIN,
2133
0
                           NULL, NULL, pminval, NULL);
2134
0
        pixGetExtremeValue(pixs, factor, L_SELECT_MAX,
2135
0
                           NULL, NULL, pmaxval, NULL);
2136
0
    } else {
2137
0
        return ERROR_INT("invalid color", __func__, 1);
2138
0
    }
2139
2140
0
    return 0;
2141
0
}
2142
2143
2144
/*!
2145
 * \brief   pixGetExtremeValue()
2146
 *
2147
 * \param[in]    pixs      8 bpp grayscale, 32 bpp rgb, or colormapped
2148
 * \param[in]    factor    subsampling factor; >= 1; ignored if colormapped
2149
 * \param[in]    type      L_SELECT_MIN or L_SELECT_MAX
2150
 * \param[out]   prval     [optional] red component
2151
 * \param[out]   pgval     [optional] green component
2152
 * \param[out]   pbval     [optional] blue component
2153
 * \param[out]   pgrayval  [optional] min or max gray value
2154
 * \return  0 if OK, 1 on error
2155
 *
2156
 * <pre>
2157
 * Notes:
2158
 *      (1) If pixs is grayscale, the result is returned in &grayval.
2159
 *          Otherwise, if there is a colormap or d == 32,
2160
 *          each requested color component is returned.  At least
2161
 *          one color component (address) must be input.
2162
 * </pre>
2163
 */
2164
l_ok
2165
pixGetExtremeValue(PIX      *pixs,
2166
                   l_int32   factor,
2167
                   l_int32   type,
2168
                   l_int32  *prval,
2169
                   l_int32  *pgval,
2170
                   l_int32  *pbval,
2171
                   l_int32  *pgrayval)
2172
0
{
2173
0
l_int32    i, j, w, h, d, wpl;
2174
0
l_int32    val, extval, rval, gval, bval, extrval, extgval, extbval;
2175
0
l_uint32   pixel;
2176
0
l_uint32  *data, *line;
2177
0
PIXCMAP   *cmap;
2178
2179
0
    if (prval) *prval = -1;
2180
0
    if (pgval) *pgval = -1;
2181
0
    if (pbval) *pbval = -1;
2182
0
    if (pgrayval) *pgrayval = -1;
2183
0
    if (!pixs)
2184
0
        return ERROR_INT("pixs not defined", __func__, 1);
2185
0
    if (type != L_SELECT_MIN && type != L_SELECT_MAX)
2186
0
        return ERROR_INT("invalid type", __func__, 1);
2187
2188
0
    cmap = pixGetColormap(pixs);
2189
0
    if (cmap) {
2190
0
        if (type == L_SELECT_MIN) {
2191
0
            if (prval) pixcmapGetRangeValues(cmap, L_SELECT_RED, prval, NULL,
2192
0
                                             NULL, NULL);
2193
0
            if (pgval) pixcmapGetRangeValues(cmap, L_SELECT_GREEN, pgval, NULL,
2194
0
                                             NULL, NULL);
2195
0
            if (pbval) pixcmapGetRangeValues(cmap, L_SELECT_BLUE, pbval, NULL,
2196
0
                                             NULL, NULL);
2197
0
        } else {  /* type == L_SELECT_MAX */
2198
0
            if (prval) pixcmapGetRangeValues(cmap, L_SELECT_RED, NULL, prval,
2199
0
                                             NULL, NULL);
2200
0
            if (pgval) pixcmapGetRangeValues(cmap, L_SELECT_GREEN, NULL, pgval,
2201
0
                                             NULL, NULL);
2202
0
            if (pbval) pixcmapGetRangeValues(cmap, L_SELECT_BLUE, NULL, pbval,
2203
0
                                             NULL, NULL);
2204
0
        }
2205
0
        return 0;
2206
0
    }
2207
2208
0
    pixGetDimensions(pixs, &w, &h, &d);
2209
0
    if (factor < 1)
2210
0
        return ERROR_INT("sampling factor must be >= 1", __func__, 1);
2211
0
    if (d != 8 && d != 32)
2212
0
        return ERROR_INT("pixs not 8 or 32 bpp", __func__, 1);
2213
0
    if (d == 8 && !pgrayval)
2214
0
        return ERROR_INT("can't return result in grayval", __func__, 1);
2215
0
    if (d == 32 && !prval && !pgval && !pbval)
2216
0
        return ERROR_INT("can't return result in r/g/b-val", __func__, 1);
2217
2218
0
    data = pixGetData(pixs);
2219
0
    wpl = pixGetWpl(pixs);
2220
0
    if (d == 8) {
2221
0
        if (type == L_SELECT_MIN)
2222
0
            extval = 100000;
2223
0
        else  /* get max */
2224
0
            extval = -1;
2225
2226
0
        for (i = 0; i < h; i += factor) {
2227
0
            line = data + i * wpl;
2228
0
            for (j = 0; j < w; j += factor) {
2229
0
                val = GET_DATA_BYTE(line, j);
2230
0
                if ((type == L_SELECT_MIN && val < extval) ||
2231
0
                    (type == L_SELECT_MAX && val > extval))
2232
0
                    extval = val;
2233
0
            }
2234
0
        }
2235
0
        *pgrayval = extval;
2236
0
        return 0;
2237
0
    }
2238
2239
        /* 32 bpp rgb */
2240
0
    if (type == L_SELECT_MIN) {
2241
0
        extrval = 100000;
2242
0
        extgval = 100000;
2243
0
        extbval = 100000;
2244
0
    } else {
2245
0
        extrval = -1;
2246
0
        extgval = -1;
2247
0
        extbval = -1;
2248
0
    }
2249
0
    for (i = 0; i < h; i += factor) {
2250
0
        line = data + i * wpl;
2251
0
        for (j = 0; j < w; j += factor) {
2252
0
            pixel = line[j];
2253
0
            if (prval) {
2254
0
                rval = (pixel >> L_RED_SHIFT) & 0xff;
2255
0
                if ((type == L_SELECT_MIN && rval < extrval) ||
2256
0
                    (type == L_SELECT_MAX && rval > extrval))
2257
0
                    extrval = rval;
2258
0
            }
2259
0
            if (pgval) {
2260
0
                gval = (pixel >> L_GREEN_SHIFT) & 0xff;
2261
0
                if ((type == L_SELECT_MIN && gval < extgval) ||
2262
0
                    (type == L_SELECT_MAX && gval > extgval))
2263
0
                    extgval = gval;
2264
0
            }
2265
0
            if (pbval) {
2266
0
                bval = (pixel >> L_BLUE_SHIFT) & 0xff;
2267
0
                if ((type == L_SELECT_MIN && bval < extbval) ||
2268
0
                    (type == L_SELECT_MAX && bval > extbval))
2269
0
                    extbval = bval;
2270
0
            }
2271
0
        }
2272
0
    }
2273
0
    if (prval) *prval = extrval;
2274
0
    if (pgval) *pgval = extgval;
2275
0
    if (pbval) *pbval = extbval;
2276
0
    return 0;
2277
0
}
2278
2279
2280
/*!
2281
 * \brief   pixGetMaxValueInRect()
2282
 *
2283
 * \param[in]    pixs     8, 16 or 32 bpp grayscale; no color space components
2284
 * \param[in]    box      [optional] region; set box = NULL to use entire pixs
2285
 * \param[out]   pmaxval  [optional] max value in region
2286
 * \param[out]   pxmax    [optional] x location of max value
2287
 * \param[out]   pymax    [optional] y location of max value
2288
 * \return  0 if OK, 1 on error
2289
 *
2290
 * <pre>
2291
 * Notes:
2292
 *      (1) This can be used to find the maximum and its location
2293
 *          in a 2-dimensional histogram, where the x and y directions
2294
 *          represent two color components (e.g., saturation and hue).
2295
 *      (2) Note that here a 32 bpp pixs has pixel values that are simply
2296
 *          numbers.  They are not 8 bpp components in a colorspace.
2297
 * </pre>
2298
 */
2299
l_ok
2300
pixGetMaxValueInRect(PIX       *pixs,
2301
                     BOX       *box,
2302
                     l_uint32  *pmaxval,
2303
                     l_int32   *pxmax,
2304
                     l_int32   *pymax)
2305
0
{
2306
0
l_int32    i, j, w, h, d, wpl, bw, bh;
2307
0
l_int32    xstart, ystart, xend, yend, xmax, ymax;
2308
0
l_uint32   val, maxval;
2309
0
l_uint32  *data, *line;
2310
2311
0
    if (pmaxval) *pmaxval = 0;
2312
0
    if (pxmax) *pxmax = 0;
2313
0
    if (pymax) *pymax = 0;
2314
0
    if (!pmaxval && !pxmax && !pymax)
2315
0
        return ERROR_INT("no data requested", __func__, 1);
2316
0
    if (!pixs)
2317
0
        return ERROR_INT("pixs not defined", __func__, 1);
2318
0
    if (pixGetColormap(pixs) != NULL)
2319
0
        return ERROR_INT("pixs has colormap", __func__, 1);
2320
0
    pixGetDimensions(pixs, &w, &h, &d);
2321
0
    if (d != 8 && d != 16 && d != 32)
2322
0
        return ERROR_INT("pixs not 8, 16 or 32 bpp", __func__, 1);
2323
2324
0
    xstart = ystart = 0;
2325
0
    xend = w - 1;
2326
0
    yend = h - 1;
2327
0
    if (box) {
2328
0
        boxGetGeometry(box, &xstart, &ystart, &bw, &bh);
2329
0
        xend = xstart + bw - 1;
2330
0
        yend = ystart + bh - 1;
2331
0
    }
2332
2333
0
    data = pixGetData(pixs);
2334
0
    wpl = pixGetWpl(pixs);
2335
0
    maxval = 0;
2336
0
    xmax = ymax = 0;
2337
0
    for (i = ystart; i <= yend; i++) {
2338
0
        line = data + i * wpl;
2339
0
        for (j = xstart; j <= xend; j++) {
2340
0
            if (d == 8)
2341
0
                val = GET_DATA_BYTE(line, j);
2342
0
            else if (d == 16)
2343
0
                val = GET_DATA_TWO_BYTES(line, j);
2344
0
            else  /* d == 32 */
2345
0
                val = line[j];
2346
0
            if (val > maxval) {
2347
0
                maxval = val;
2348
0
                xmax = j;
2349
0
                ymax = i;
2350
0
            }
2351
0
        }
2352
0
    }
2353
0
    if (maxval == 0) {  /* no counts; pick the center of the rectangle */
2354
0
        xmax = (xstart + xend) / 2;
2355
0
        ymax = (ystart + yend) / 2;
2356
0
    }
2357
2358
0
    if (pmaxval) *pmaxval = maxval;
2359
0
    if (pxmax) *pxmax = xmax;
2360
0
    if (pymax) *pymax = ymax;
2361
0
    return 0;
2362
0
}
2363
2364
2365
/*!
2366
 * \brief   pixGetMaxColorIndex()
2367
 *
2368
 * \param[in]    pixs          1, 2, 4 or 8 bpp colormapped
2369
 * \param[out]   pmaxindex     max colormap index value
2370
 * \return  0 if OK, 1 on error
2371
 */
2372
l_ok
2373
pixGetMaxColorIndex(PIX      *pixs,
2374
                    l_int32  *pmaxindex)
2375
266
{
2376
266
l_int32    i, j, w, h, d, wpl, val, max, maxval, empty;
2377
266
l_uint32  *data, *line;
2378
2379
266
    if (!pmaxindex)
2380
0
        return ERROR_INT("&maxindex not defined", __func__, 1);
2381
266
    *pmaxindex = 0;
2382
266
    if (!pixs)
2383
0
        return ERROR_INT("pixs not defined", __func__, 1);
2384
266
    pixGetDimensions(pixs, &w, &h, &d);
2385
266
    if (d != 1 && d != 2 && d != 4 && d != 8)
2386
0
        return ERROR_INT("invalid pixs depth; not in (1,2,4,8}", __func__, 1);
2387
2388
266
    wpl = pixGetWpl(pixs);
2389
266
    data = pixGetData(pixs);
2390
266
    max = 0;
2391
266
    maxval = (1 << d) - 1;
2392
266
    if (d == 1) {
2393
166
        pixZero(pixs, &empty);
2394
166
        if (!empty) max = 1;
2395
166
        *pmaxindex = max;
2396
166
        return 0;
2397
166
    }
2398
1.40k
    for (i = 0; i < h; i++) {
2399
1.34k
        line = data + i * wpl;
2400
1.34k
        if (d == 2) {
2401
22.8k
            for (j = 0; j < w; j++) {
2402
22.4k
                val = GET_DATA_DIBIT(line, j);
2403
22.4k
                if (val > max) max = val;
2404
22.4k
            }
2405
998
        } else if (d == 4) {
2406
1.30k
            for (j = 0; j < w; j++) {
2407
1.00k
                val = GET_DATA_QBIT(line, j);
2408
1.00k
                if (val > max) max = val;
2409
1.00k
            }
2410
700
        } else if (d == 8) {
2411
22.0k
            for (j = 0; j < w; j++) {
2412
21.3k
                val = GET_DATA_BYTE(line, j);
2413
21.3k
                if (val > max) max = val;
2414
21.3k
            }
2415
700
        }
2416
1.34k
        if (max == maxval) break;
2417
1.34k
    }
2418
100
    *pmaxindex = max;
2419
100
    return 0;
2420
266
}
2421
2422
2423
/*!
2424
 * \brief   pixGetBinnedComponentRange()
2425
 *
2426
 * \param[in]    pixs      32 bpp rgb
2427
 * \param[in]    nbins     number of equal population bins; must be > 1
2428
 * \param[in]    factor    subsampling factor; >= 1
2429
 * \param[in]    color     L_SELECT_RED, L_SELECT_GREEN or L_SELECT_BLUE
2430
 * \param[out]   pminval   [optional] minimum value of component
2431
 * \param[out]   pmaxval   [optional] maximum value of component
2432
 * \param[out]   pcarray   [optional] color array of bins
2433
 * \param[in]    fontsize  [optional] 0 for no debug; for debug, valid set
2434
 *                         is {4,6,8,10,12,14,16,18,20}.
2435
 * \return  0 if OK, 1 on error
2436
 *
2437
 * <pre>
2438
 * Notes:
2439
 *      (1) This returns the min and max average values of the
2440
 *          selected color component in the set of rank bins,
2441
 *          where the ranking is done using the specified component.
2442
 * </pre>
2443
 */
2444
l_ok
2445
pixGetBinnedComponentRange(PIX        *pixs,
2446
                           l_int32     nbins,
2447
                           l_int32     factor,
2448
                           l_int32     color,
2449
                           l_int32    *pminval,
2450
                           l_int32    *pmaxval,
2451
                           l_uint32  **pcarray,
2452
                           l_int32     fontsize)
2453
0
{
2454
0
l_int32    i, minval, maxval, rval, gval, bval;
2455
0
l_uint32  *carray;
2456
0
PIX       *pixt;
2457
2458
0
    if (pminval) *pminval = 0;
2459
0
    if (pmaxval) *pmaxval = 0;
2460
0
    if (pcarray) *pcarray = NULL;
2461
0
    if (!pminval && !pmaxval)
2462
0
        return ERROR_INT("no result requested", __func__, 1);
2463
0
    if (!pixs || pixGetDepth(pixs) != 32)
2464
0
        return ERROR_INT("pixs not defined or not 32 bpp", __func__, 1);
2465
0
    if (factor < 1)
2466
0
        return ERROR_INT("sampling factor must be >= 1", __func__, 1);
2467
0
    if (color != L_SELECT_RED && color != L_SELECT_GREEN &&
2468
0
        color != L_SELECT_BLUE)
2469
0
        return ERROR_INT("invalid color", __func__, 1);
2470
0
    if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2)
2471
0
        return ERROR_INT("invalid fontsize", __func__, 1);
2472
2473
0
    pixGetRankColorArray(pixs, nbins, color, factor, &carray, NULL, 0);
2474
0
    if (!carray)
2475
0
        return ERROR_INT("carray not made", __func__, 1);
2476
2477
0
    if (fontsize > 0) {
2478
0
        for (i = 0; i < nbins; i++)
2479
0
            L_INFO("c[%d] = %x\n", __func__, i, carray[i]);
2480
0
        pixt = pixDisplayColorArray(carray, nbins, 200, 5, fontsize);
2481
0
        pixDisplay(pixt, 100, 100);
2482
0
        pixDestroy(&pixt);
2483
0
    }
2484
2485
0
    extractRGBValues(carray[0], &rval, &gval, &bval);
2486
0
    minval = rval;
2487
0
    if (color == L_SELECT_GREEN)
2488
0
        minval = gval;
2489
0
    else if (color == L_SELECT_BLUE)
2490
0
        minval = bval;
2491
0
    extractRGBValues(carray[nbins - 1], &rval, &gval, &bval);
2492
0
    maxval = rval;
2493
0
    if (color == L_SELECT_GREEN)
2494
0
        maxval = gval;
2495
0
    else if (color == L_SELECT_BLUE)
2496
0
        maxval = bval;
2497
2498
0
    if (pminval) *pminval = minval;
2499
0
    if (pmaxval) *pmaxval = maxval;
2500
0
    if (pcarray)
2501
0
        *pcarray = carray;
2502
0
    else
2503
0
        LEPT_FREE(carray);
2504
0
    return 0;
2505
0
}
2506
2507
2508
/*!
2509
 * \brief   pixGetRankColorArray()
2510
 *
2511
 * \param[in]    pixs       32 bpp or cmapped
2512
 * \param[in]    nbins      number of equal population bins; must be > 1
2513
 * \param[in]    type       color selection flag
2514
 * \param[in]    factor     subsampling factor; integer >= 1
2515
 * \param[out]   pcarray    array of colors, ranked by intensity
2516
 * \param[in]    pixadb     [optional] debug: caller passes this in.
2517
 *                          Use to display color squares and to
2518
 *                          capture plots of color components
2519
 * \param[in]    fontsize   [optional] debug: only used if pixadb exists.
2520
 *                          Valid set is {4,6,8,10,12,14,16,18,20}.
2521
 *                          fontsize == 6 is typical.
2522
 * \return  0 if OK, 1 on error
2523
 *
2524
 * <pre>
2525
 * Notes:
2526
 *      (1) The color selection flag is one of: L_SELECT_RED, L_SELECT_GREEN,
2527
 *          L_SELECT_BLUE, L_SELECT_MIN, L_SELECT_MAX, L_SELECT_AVERAGE,
2528
 *          L_SELECT_HUE, L_SELECT_SATURATION.
2529
 *      (2) The pixels are ordered by the value of the selected color
2530
            value, and an equal number are placed in %nbins.  The average
2531
 *          color in each bin is returned in a color array with %nbins colors.
2532
 *      (3) Set the subsampling factor > 1 to reduce the amount of
2533
 *          computation.  Typically you want at least 10,000 pixels
2534
 *          for reasonable statistics.  Must be at least 10 samples/bin.
2535
 *      (4) A crude "rank color" as a function of rank can be found from
2536
 *             rankint = (l_int32)(rank * (nbins - 1) + 0.5);
2537
 *             extractRGBValues(array[rankint], &rval, &gval, &bval);
2538
 *          where the rank is in [0.0 ... 1.0].
2539
 * </pre>
2540
 */
2541
l_ok
2542
pixGetRankColorArray(PIX        *pixs,
2543
                     l_int32     nbins,
2544
                     l_int32     type,
2545
                     l_int32     factor,
2546
                     l_uint32  **pcarray,
2547
                     PIXA       *pixadb,
2548
                     l_int32     fontsize)
2549
0
{
2550
0
l_int32    ret, w, h, samplesperbin;
2551
0
l_uint32  *array;
2552
0
PIX       *pix1, *pixc, *pixg, *pixd;
2553
0
PIXCMAP   *cmap;
2554
2555
0
    if (!pcarray)
2556
0
        return ERROR_INT("&carray not defined", __func__, 1);
2557
0
    *pcarray = NULL;
2558
0
    if (factor < 1)
2559
0
        return ERROR_INT("sampling factor must be >= 1", __func__, 1);
2560
0
    if (nbins < 2)
2561
0
        return ERROR_INT("nbins must be at least 2", __func__, 1);
2562
0
    if (!pixs)
2563
0
        return ERROR_INT("pixs not defined", __func__, 1);
2564
0
    cmap = pixGetColormap(pixs);
2565
0
    if (pixGetDepth(pixs) != 32 && !cmap)
2566
0
        return ERROR_INT("pixs neither 32 bpp nor cmapped", __func__, 1);
2567
0
    if (type != L_SELECT_RED && type != L_SELECT_GREEN &&
2568
0
        type != L_SELECT_BLUE && type != L_SELECT_MIN &&
2569
0
        type != L_SELECT_MAX && type != L_SELECT_AVERAGE &&
2570
0
        type != L_SELECT_HUE && type != L_SELECT_SATURATION)
2571
0
        return ERROR_INT("invalid type", __func__, 1);
2572
0
    if (pixadb) {
2573
0
        if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) {
2574
0
            L_WARNING("invalid fontsize %d; setting to 6\n", __func__,
2575
0
                      fontsize);
2576
0
            fontsize = 6;
2577
0
        }
2578
0
    }
2579
0
    pixGetDimensions(pixs, &w, &h, NULL);
2580
0
    samplesperbin = (w * h) / (factor * factor * nbins);
2581
0
    if (samplesperbin < 10) {
2582
0
        L_ERROR("samplesperbin = %d < 10\n", __func__, samplesperbin);
2583
0
        return 1;
2584
0
    }
2585
2586
        /* Downscale by factor and remove colormap if it exists */
2587
0
    pix1 = pixScaleByIntSampling(pixs, factor);
2588
0
    if (cmap)
2589
0
        pixc = pixRemoveColormap(pix1, REMOVE_CMAP_TO_FULL_COLOR);
2590
0
    else
2591
0
        pixc = pixClone(pix1);
2592
0
    pixDestroy(&pix1);
2593
2594
        /* Convert to an 8 bit version for ordering the pixels */
2595
0
    pixg = pixConvertRGBToGrayGeneral(pixc, type, 0.0, 0.0, 0.0);
2596
2597
        /* Get the average color in each bin for pixels whose grayscale
2598
         * values are in the range for that bin. */
2599
0
    pixGetBinnedColor(pixc, pixg, 1, nbins, pcarray, pixadb);
2600
0
    ret = 0;
2601
0
    if ((array = *pcarray) == NULL) {
2602
0
        L_ERROR("color array not returned\n", __func__);
2603
0
        ret = 1;
2604
0
    }
2605
0
    if (array && pixadb) {
2606
0
        pixd = pixDisplayColorArray(array, nbins, 200, 5, fontsize);
2607
0
        pixWriteDebug("/tmp/lept/regout/rankhisto.png", pixd, IFF_PNG);
2608
0
        pixDestroy(&pixd);
2609
0
    }
2610
2611
0
    pixDestroy(&pixc);
2612
0
    pixDestroy(&pixg);
2613
0
    return ret;
2614
0
}
2615
2616
2617
/*!
2618
 * \brief   pixGetBinnedColor()
2619
 *
2620
 * \param[in]    pixs       32 bpp
2621
 * \param[in]    pixg       8 bpp grayscale version of pixs
2622
 * \param[in]    factor     sampling factor along pixel counting direction
2623
 * \param[in]    nbins      number of bins based on grayscale value {1,...,100}
2624
 * \param[out]   pcarray    array of average color values in each bin
2625
 * \param[in]    pixadb     [optional] debug: caller passes this in.
2626
 *                          Use to display output color squares and plots of
2627
 *                          color components.
2628
 * \return  0 if OK; 1 on error
2629
 *
2630
 * <pre>
2631
 * Notes:
2632
 *      (1) This takes a color image, a grayscale version, and the number
2633
 *          of requested bins.  The pixels are ordered by the corresponding
2634
 *          gray value and an equal number of pixels are put in each bin.
2635
 *          The average color for each bin is returned as an array
2636
 *          of l_uint32 colors in our standard RGBA ordering.  We require
2637
 *          at least 5 pixels in each bin.
2638
 *      (2) This is used by pixGetRankColorArray(), which generates the
2639
 *          grayscale image %pixg from the color image %pixs.
2640
 *      (3) Arrays of float64 are used for intermediate storage, without
2641
 *          loss of precision, of the sampled uint32 pixel values.
2642
 * </pre>
2643
 */
2644
l_ok
2645
pixGetBinnedColor(PIX        *pixs,
2646
                  PIX        *pixg,
2647
                  l_int32     factor,
2648
                  l_int32     nbins,
2649
                  l_uint32  **pcarray,
2650
                  PIXA       *pixadb)
2651
0
{
2652
0
l_int32     i, j, w, h, wpls, wplg;
2653
0
l_int32     count, bincount, binindex, binsize, npts, avepts, ntot;
2654
0
l_int32     rval, gval, bval, grayval, rave, gave, bave;
2655
0
l_uint32   *datas, *datag, *lines, *lineg, *carray;
2656
0
l_float64   val64, rsum, gsum, bsum;
2657
0
L_DNAA     *daa;
2658
0
NUMA       *naeach;
2659
0
PIX        *pix1;
2660
2661
0
    if (!pcarray)
2662
0
        return ERROR_INT("&carray not defined", __func__, 1);
2663
0
    *pcarray = NULL;
2664
0
    if (!pixs || pixGetDepth(pixs) != 32)
2665
0
        return ERROR_INT("pixs undefined or not 32 bpp", __func__, 1);
2666
0
    if (!pixg || pixGetDepth(pixg) != 8)
2667
0
        return ERROR_INT("pixg undefined or not 8 bpp", __func__, 1);
2668
0
    if (factor < 1) {
2669
0
        L_WARNING("sampling factor less than 1; setting to 1\n", __func__);
2670
0
        factor = 1;
2671
0
    }
2672
0
    if (nbins < 1 || nbins > 100)
2673
0
        return ERROR_INT("nbins not in [1,100]", __func__, 1);
2674
2675
        /* Require that each bin has at least 5 pixels. */
2676
0
    pixGetDimensions(pixs, &w, &h, NULL);
2677
0
    npts = (w + factor - 1) * (h + factor - 1) / (factor * factor);
2678
0
    avepts = (npts + nbins - 1) / nbins;  /* average number of pts in a bin */
2679
0
    if (avepts < 5) {
2680
0
        L_ERROR("avepts = %d; must be >= 5\n", __func__, avepts);
2681
0
        return 1;
2682
0
    }
2683
2684
    /* ------------------------------------------------------------ *
2685
     * Find the average color for each bin.  The colors are ordered *
2686
     * by the gray value in the corresponding pixel in %pixg.       *
2687
     * The bins have equal numbers of pixels (within 1).            *
2688
     * ------------------------------------------------------------ */
2689
2690
        /* Generate a dnaa, where each dna has the colors corresponding
2691
         * to the grayscale value given by the index of the dna in the dnaa */
2692
0
    datas = pixGetData(pixs);
2693
0
    wpls = pixGetWpl(pixs);
2694
0
    datag = pixGetData(pixg);
2695
0
    wplg = pixGetWpl(pixg);
2696
0
    daa = l_dnaaCreateFull(256, 0);
2697
0
    for (i = 0; i < h; i += factor) {
2698
0
        lines = datas + i * wpls;
2699
0
        lineg = datag + i * wplg;
2700
0
        for (j = 0; j < w; j += factor) {
2701
0
            grayval = GET_DATA_BYTE(lineg, j);
2702
0
            l_dnaaAddNumber(daa, grayval, lines[j]);
2703
0
        }
2704
0
    }
2705
2706
0
    if (pixadb) {
2707
0
        NUMA  *na, *nabinval, *narank;
2708
0
        na = numaCreate(256);  /* grayscale histogram */
2709
0
        for (i = 0; i < 256; i++)
2710
0
            numaAddNumber(na, l_dnaaGetDnaCount(daa, i));
2711
2712
            /* Plot the gray bin value and the rank(gray) values */
2713
0
        numaDiscretizeHistoInBins(na, nbins, &nabinval, &narank);
2714
0
        pix1 = gplotSimplePix1(nabinval, "Gray value in each bin");
2715
0
        pixaAddPix(pixadb, pix1, L_INSERT);
2716
0
        pix1 = gplotSimplePix1(narank, "rank as function of gray value");
2717
0
        pixaAddPix(pixadb, pix1, L_INSERT);
2718
0
        numaDestroy(&na);
2719
0
        numaDestroy(&nabinval);
2720
0
        numaDestroy(&narank);
2721
0
    }
2722
2723
        /* Get the number of items in each bin */
2724
0
    ntot = l_dnaaGetNumberCount(daa);
2725
0
    if ((naeach = numaGetUniformBinSizes(ntot, nbins)) == NULL) {
2726
0
        l_dnaaDestroy(&daa);
2727
0
        return ERROR_INT("naeach not made", __func__, 1);
2728
0
    }
2729
2730
        /* Get the average color in each bin.  This algorithm is
2731
         * esssentially the same as in numaDiscretizeHistoInBins() */
2732
0
    carray = (l_uint32 *)LEPT_CALLOC(nbins, sizeof(l_uint32));
2733
0
    rsum = gsum = bsum = 0.0;
2734
0
    bincount = 0;
2735
0
    binindex = 0;
2736
0
    numaGetIValue(naeach, 0, &binsize);
2737
0
    for (i = 0; i < 256; i++) {
2738
0
        count = l_dnaaGetDnaCount(daa, i);
2739
0
        for (j = 0; j < count; j++) {
2740
0
            bincount++;
2741
0
            l_dnaaGetValue(daa, i, j, &val64);
2742
0
            extractRGBValues((l_uint32)val64, &rval, &gval, &bval);
2743
0
            rsum += rval;
2744
0
            gsum += gval;
2745
0
            bsum += bval;
2746
0
            if (bincount == binsize) {  /* add bin entry */
2747
0
                rave = (l_int32)(rsum / binsize + 0.5);
2748
0
                gave = (l_int32)(gsum / binsize + 0.5);
2749
0
                bave = (l_int32)(bsum / binsize + 0.5);
2750
0
                composeRGBPixel(rave, gave, bave, carray + binindex);
2751
0
                rsum = gsum = bsum = 0.0;
2752
0
                bincount = 0;
2753
0
                binindex++;
2754
0
                if (binindex == nbins) break;
2755
0
                numaGetIValue(naeach, binindex, &binsize);
2756
0
            }
2757
0
        }
2758
0
        if (binindex == nbins) break;
2759
0
    }
2760
0
    if (binindex != nbins)
2761
0
        L_ERROR("binindex = %d != nbins = %d\n", __func__, binindex, nbins);
2762
2763
0
    if (pixadb) {
2764
0
        NUMA  *nared, *nagreen, *nablue;
2765
0
        nared = numaCreate(nbins);
2766
0
        nagreen = numaCreate(nbins);
2767
0
        nablue = numaCreate(nbins);
2768
0
        for (i = 0; i < nbins; i++) {
2769
0
            extractRGBValues(carray[i], &rval, &gval, &bval);
2770
0
            numaAddNumber(nared, rval);
2771
0
            numaAddNumber(nagreen, gval);
2772
0
            numaAddNumber(nablue, bval);
2773
0
        }
2774
0
        lept_mkdir("lept/regout");
2775
0
        pix1 = gplotSimplePix1(nared, "Average red val vs. rank bin");
2776
0
        pixaAddPix(pixadb, pix1, L_INSERT);
2777
0
        pix1 = gplotSimplePix1(nagreen, "Average green val vs. rank bin");
2778
0
        pixaAddPix(pixadb, pix1, L_INSERT);
2779
0
        pix1 = gplotSimplePix1(nablue, "Average blue val vs. rank bin");
2780
0
        pixaAddPix(pixadb, pix1, L_INSERT);
2781
0
        numaDestroy(&nared);
2782
0
        numaDestroy(&nagreen);
2783
0
        numaDestroy(&nablue);
2784
0
    }
2785
2786
0
    *pcarray = carray;
2787
0
    numaDestroy(&naeach);
2788
0
    l_dnaaDestroy(&daa);
2789
0
    return 0;
2790
0
}
2791
2792
2793
/*!
2794
 * \brief   pixDisplayColorArray()
2795
 *
2796
 * \param[in]   carray    array of colors: 0xrrggbb00
2797
 * \param[in]   ncolors   size of array
2798
 * \param[in]   side      size of each color square; suggest 200
2799
 * \param[in]   ncols     number of columns in output color matrix
2800
 * \param[in]   fontsize  to label each square with text.
2801
 *                        Valid set is {4,6,8,10,12,14,16,18,20}.
2802
 *                        Suggest 6 for 200x200 square. Use 0 to disable.
2803
 * \return  pixd color array, or NULL on error
2804
 *
2805
 * <pre>
2806
 * Notes:
2807
 *      (1) This generates an array of labeled color squares from an
2808
 *          array of color values.
2809
 *      (2) To make a single color square, use pixMakeColorSquare().
2810
 * </pre>
2811
 */
2812
PIX *
2813
pixDisplayColorArray(l_uint32  *carray,
2814
                     l_int32    ncolors,
2815
                     l_int32    side,
2816
                     l_int32    ncols,
2817
                     l_int32    fontsize)
2818
0
{
2819
0
char     textstr[256];
2820
0
l_int32  i, rval, gval, bval;
2821
0
L_BMF   *bmf;
2822
0
PIX     *pix1, *pix2, *pix3, *pix4;
2823
0
PIXA    *pixa;
2824
2825
0
    if (!carray)
2826
0
        return (PIX *)ERROR_PTR("carray not defined", __func__, NULL);
2827
0
    if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2)
2828
0
        return (PIX *)ERROR_PTR("invalid fontsize", __func__, NULL);
2829
2830
0
    bmf = (fontsize == 0) ? NULL : bmfCreate(NULL, fontsize);
2831
0
    pixa = pixaCreate(ncolors);
2832
0
    for (i = 0; i < ncolors; i++) {
2833
0
        pix1 = pixCreate(side, side, 32);
2834
0
        pixSetAllArbitrary(pix1, carray[i]);
2835
0
        pix2 = pixAddBorder(pix1, 2, 1);
2836
0
        if (bmf) {
2837
0
            extractRGBValues(carray[i], &rval, &gval, &bval);
2838
0
            snprintf(textstr, sizeof(textstr),
2839
0
                     "%d: (%d %d %d)", i, rval, gval, bval);
2840
0
            pix3 = pixAddSingleTextblock(pix2, bmf, textstr, 0xff000000,
2841
0
                                         L_ADD_BELOW, NULL);
2842
0
        } else {
2843
0
            pix3 = pixClone(pix2);
2844
0
        }
2845
0
        pixaAddPix(pixa, pix3, L_INSERT);
2846
0
        pixDestroy(&pix1);
2847
0
        pixDestroy(&pix2);
2848
0
    }
2849
0
    pix4 = pixaDisplayTiledInColumns(pixa, ncols, 1.0, 20, 2);
2850
0
    pixaDestroy(&pixa);
2851
0
    bmfDestroy(&bmf);
2852
0
    return pix4;
2853
0
}
2854
2855
2856
/*!
2857
 * \brief   pixRankBinByStrip()
2858
 *
2859
 * \param[in]   pixs       32 bpp or cmapped
2860
 * \param[in]   direction  L_SCAN_HORIZONTAL or L_SCAN_VERTICAL
2861
 * \param[in]   size       of strips in scan direction
2862
 * \param[in]   nbins      number of equal population bins; must be > 1
2863
 * \param[in]   type       color selection flag
2864
 * \return  pixd result, or NULL on error
2865
 *
2866
 * <pre>
2867
 * Notes:
2868
 *      (1) This generates a pix of height %nbins, where each column
2869
 *          represents a horizontal or vertical strip of the input image.
2870
 *          If %direction == L_SCAN_HORIZONTAL, the input image is
2871
 *          tiled into vertical strips of width %size, where %size is
2872
 *          chosen as a compromise between getting better spatial
2873
 *          columnwise resolution (small %size) and getting better
2874
 *          columnwise statistical information (larger %size).  Likewise
2875
 *          with rows of the image if %direction == L_SCAN_VERTICAL.
2876
 *      (2) For L_HORIZONTAL_SCAN, the output pix contains rank binned
2877
 *          median colors in each column that correspond to a vertical
2878
 *          strip of width %size in the input image.
2879
 *      (3) The color selection flag is one of: L_SELECT_RED, L_SELECT_GREEN,
2880
 *          L_SELECT_BLUE, L_SELECT_MIN, L_SELECT_MAX, L_SELECT_AVERAGE,
2881
 *          L_SELECT_HUE, L_SELECT_SATURATION.
2882
 *          It determines how the rank ordering is done.
2883
 *      (4) Typical input values might be %size = 5, %nbins = 10.
2884
 * </pre>
2885
 */
2886
PIX *
2887
pixRankBinByStrip(PIX     *pixs,
2888
                  l_int32  direction,
2889
                  l_int32  size,
2890
                  l_int32  nbins,
2891
                  l_int32  type)
2892
0
{
2893
0
l_int32    i, j, w, h, mindim, nstrips;
2894
0
l_uint32  *array;
2895
0
BOXA      *boxa;
2896
0
PIX       *pix1, *pix2, *pixd;
2897
0
PIXA      *pixa;
2898
0
PIXCMAP   *cmap;
2899
2900
0
    if (!pixs)
2901
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2902
0
    cmap = pixGetColormap(pixs);
2903
0
    if (pixGetDepth(pixs) != 32 && !cmap)
2904
0
        return (PIX *)ERROR_PTR("pixs neither 32 bpp nor cmapped",
2905
0
                                __func__, NULL);
2906
0
    if (direction != L_SCAN_HORIZONTAL && direction != L_SCAN_VERTICAL)
2907
0
        return (PIX *)ERROR_PTR("invalid direction", __func__, NULL);
2908
0
    if (size < 1)
2909
0
        return (PIX *)ERROR_PTR("size < 1", __func__, NULL);
2910
0
    if (nbins < 2)
2911
0
        return (PIX *)ERROR_PTR("nbins must be at least 2", __func__, NULL);
2912
0
    if (type != L_SELECT_RED && type != L_SELECT_GREEN &&
2913
0
        type != L_SELECT_BLUE && type != L_SELECT_MIN &&
2914
0
        type != L_SELECT_MAX && type != L_SELECT_AVERAGE &&
2915
0
        type != L_SELECT_HUE && type != L_SELECT_SATURATION)
2916
0
        return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
2917
0
    pixGetDimensions(pixs, &w, &h, NULL);
2918
0
    mindim = L_MIN(w, h);
2919
0
    if (mindim < 20 || nbins > mindim)
2920
0
        return (PIX *)ERROR_PTR("pix too small and/or too many bins",
2921
0
                                __func__, NULL);
2922
2923
        /* Remove colormap if it exists */
2924
0
    if (cmap)
2925
0
        pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
2926
0
    else
2927
0
        pix1 = pixClone(pixs);
2928
0
    pixGetDimensions(pixs, &w, &h, NULL);
2929
2930
0
    pixd = NULL;
2931
0
    boxa = makeMosaicStrips(w, h, direction, size);
2932
0
    pixa = pixClipRectangles(pix1, boxa);
2933
0
    nstrips = pixaGetCount(pixa);
2934
0
    if (direction == L_SCAN_HORIZONTAL) {
2935
0
        pixd = pixCreate(nstrips, nbins, 32);
2936
0
        for (i = 0; i < nstrips; i++) {
2937
0
            pix2 = pixaGetPix(pixa, i, L_CLONE);
2938
0
            pixGetRankColorArray(pix2, nbins, type, 1, &array, NULL, 0);
2939
0
            if (array) {
2940
0
                for (j = 0; j < nbins; j++)
2941
0
                    pixSetPixel(pixd, i, j, array[j]);
2942
0
                LEPT_FREE(array);
2943
0
            }
2944
0
            pixDestroy(&pix2);
2945
0
        }
2946
0
    } else {  /* L_SCAN_VERTICAL */
2947
0
        pixd = pixCreate(nbins, nstrips, 32);
2948
0
        for (i = 0; i < nstrips; i++) {
2949
0
            pix2 = pixaGetPix(pixa, i, L_CLONE);
2950
0
            pixGetRankColorArray(pix2, nbins, type, 1, &array, NULL, 0);
2951
0
            if (array) {
2952
0
                for (j = 0; j < nbins; j++)
2953
0
                    pixSetPixel(pixd, j, i, array[j]);
2954
0
                LEPT_FREE(array);
2955
0
            }
2956
0
            pixDestroy(&pix2);
2957
0
        }
2958
0
    }
2959
0
    pixDestroy(&pix1);
2960
0
    boxaDestroy(&boxa);
2961
0
    pixaDestroy(&pixa);
2962
0
    return pixd;
2963
0
}
2964
2965
2966
2967
/*-------------------------------------------------------------*
2968
 *                 Pixelwise aligned statistics                *
2969
 *-------------------------------------------------------------*/
2970
/*!
2971
 * \brief   pixaGetAlignedStats()
2972
 *
2973
 * \param[in]   pixa    of identically sized, 8 bpp pix; not cmapped
2974
 * \param[in]   type    L_MEAN_ABSVAL, L_MEDIAN_VAL, L_MODE_VAL, L_MODE_COUNT
2975
 * \param[in]   nbins   of histogram for median and mode; ignored for mean
2976
 * \param[in]   thresh  on histogram for mode val; ignored for all other types
2977
 * \return  pix with pixelwise aligned stats, or NULL on error.
2978
 *
2979
 * <pre>
2980
 * Notes:
2981
 *      (1) Each pixel in the returned pix represents an average
2982
 *          (or median, or mode) over the corresponding pixels in each
2983
 *          pix in the pixa.
2984
 *      (2) The %thresh parameter works with L_MODE_VAL only, and
2985
 *          sets a minimum occupancy of the mode bin.
2986
 *          If the occupancy of the mode bin is less than %thresh, the
2987
 *          mode value is returned as 0.  To always return the actual
2988
 *          mode value, set %thresh = 0.  See pixGetRowStats().
2989
 * </pre>
2990
 */
2991
PIX *
2992
pixaGetAlignedStats(PIXA     *pixa,
2993
                    l_int32   type,
2994
                    l_int32   nbins,
2995
                    l_int32   thresh)
2996
0
{
2997
0
l_int32     j, n, w, h, d;
2998
0
l_float32  *colvect;
2999
0
PIX        *pixt, *pixd;
3000
3001
0
    if (!pixa)
3002
0
        return (PIX *)ERROR_PTR("pixa not defined", __func__, NULL);
3003
0
    if (type != L_MEAN_ABSVAL && type != L_MEDIAN_VAL &&
3004
0
        type != L_MODE_VAL && type != L_MODE_COUNT)
3005
0
        return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
3006
0
    n = pixaGetCount(pixa);
3007
0
    if (n == 0)
3008
0
        return (PIX *)ERROR_PTR("no pix in pixa", __func__, NULL);
3009
0
    pixaGetPixDimensions(pixa, 0, &w, &h, &d);
3010
0
    if (d != 8)
3011
0
        return (PIX *)ERROR_PTR("pix not 8 bpp", __func__, NULL);
3012
3013
0
    pixd = pixCreate(w, h, 8);
3014
0
    pixt = pixCreate(n, h, 8);
3015
0
    colvect = (l_float32 *)LEPT_CALLOC(h, sizeof(l_float32));
3016
0
    for (j = 0; j < w; j++) {
3017
0
        pixaExtractColumnFromEachPix(pixa, j, pixt);
3018
0
        pixGetRowStats(pixt, type, nbins, thresh, colvect);
3019
0
        pixSetPixelColumn(pixd, j, colvect);
3020
0
    }
3021
3022
0
    LEPT_FREE(colvect);
3023
0
    pixDestroy(&pixt);
3024
0
    return pixd;
3025
0
}
3026
3027
3028
/*!
3029
 * \brief   pixaExtractColumnFromEachPix()
3030
 *
3031
 * \param[in]   pixa   of identically sized, 8 bpp; not cmapped
3032
 * \param[in]   col    column index
3033
 * \param[in]   pixd   pix into which each column is inserted
3034
 * \return  0 if OK, 1 on error
3035
 */
3036
l_ok
3037
pixaExtractColumnFromEachPix(PIXA    *pixa,
3038
                             l_int32  col,
3039
                             PIX     *pixd)
3040
0
{
3041
0
l_int32    i, k, n, w, h, ht, val, wplt, wpld;
3042
0
l_uint32  *datad, *datat;
3043
0
PIX       *pixt;
3044
3045
0
    if (!pixa)
3046
0
        return ERROR_INT("pixa not defined", __func__, 1);
3047
0
    if (!pixd || pixGetDepth(pixd) != 8)
3048
0
        return ERROR_INT("pixd not defined or not 8 bpp", __func__, 1);
3049
0
    n = pixaGetCount(pixa);
3050
0
    pixGetDimensions(pixd, &w, &h, NULL);
3051
0
    if (n != w)
3052
0
        return ERROR_INT("pix width != n", __func__, 1);
3053
0
    pixt = pixaGetPix(pixa, 0, L_CLONE);
3054
0
    wplt = pixGetWpl(pixt);
3055
0
    pixGetDimensions(pixt, NULL, &ht, NULL);
3056
0
    pixDestroy(&pixt);
3057
0
    if (h != ht)
3058
0
        return ERROR_INT("pixd height != column height", __func__, 1);
3059
3060
0
    datad = pixGetData(pixd);
3061
0
    wpld = pixGetWpl(pixd);
3062
0
    for (k = 0; k < n; k++) {
3063
0
        pixt = pixaGetPix(pixa, k, L_CLONE);
3064
0
        datat = pixGetData(pixt);
3065
0
        for (i = 0; i < h; i++) {
3066
0
            val = GET_DATA_BYTE(datat, col);
3067
0
            SET_DATA_BYTE(datad + i * wpld, k, val);
3068
0
            datat += wplt;
3069
0
        }
3070
0
        pixDestroy(&pixt);
3071
0
    }
3072
3073
0
    return 0;
3074
0
}
3075
3076
3077
/*!
3078
 * \brief   pixGetRowStats()
3079
 *
3080
 * \param[in]   pixs     8 bpp; not cmapped
3081
 * \param[in]   type     L_MEAN_ABSVAL, L_MEDIAN_VAL, L_MODE_VAL, L_MODE_COUNT
3082
 * \param[in]   nbins    of histogram for median and mode; ignored for mean
3083
 * \param[in]   thresh   on histogram for mode; ignored for mean and median
3084
 * \param[in]   colvect  vector of results gathered across the rows of pixs
3085
 * \return  0 if OK, 1 on error
3086
 *
3087
 * <pre>
3088
 * Notes:
3089
 *      (1) This computes a column vector of statistics using each
3090
 *          row of a Pix.  The result is put in %colvect.
3091
 *      (2) The %thresh parameter works with L_MODE_VAL only, and
3092
 *          sets a minimum occupancy of the mode bin.
3093
 *          If the occupancy of the mode bin is less than %thresh, the
3094
 *          mode value is returned as 0.  To always return the actual
3095
 *          mode value, set %thresh = 0.
3096
 *      (3) What is the meaning of this %thresh parameter?
3097
 *          For each row, the total count in the histogram is w, the
3098
 *          image width.  So %thresh, relative to w, gives a measure
3099
 *          of the ratio of the bin width to the width of the distribution.
3100
 *          The larger %thresh, the narrower the distribution must be
3101
 *          for the mode value to be returned (instead of returning 0).
3102
 *      (4) If the Pix consists of a set of corresponding columns,
3103
 *          one for each Pix in a Pixa, the width of the Pix is the
3104
 *          number of Pix in the Pixa and the column vector can
3105
 *          be stored as a column in a Pix of the same size as
3106
 *          each Pix in the Pixa.
3107
 * </pre>
3108
 */
3109
l_ok
3110
pixGetRowStats(PIX        *pixs,
3111
               l_int32     type,
3112
               l_int32     nbins,
3113
               l_int32     thresh,
3114
               l_float32  *colvect)
3115
0
{
3116
0
l_int32    i, j, k, w, h, val, wpls, sum, target, max, modeval;
3117
0
l_int32   *histo, *gray2bin, *bin2gray;
3118
0
l_uint32  *lines, *datas;
3119
3120
0
    if (!pixs || pixGetDepth(pixs) != 8)
3121
0
        return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
3122
0
    if (!colvect)
3123
0
        return ERROR_INT("colvect not defined", __func__, 1);
3124
0
    if (type != L_MEAN_ABSVAL && type != L_MEDIAN_VAL &&
3125
0
        type != L_MODE_VAL && type != L_MODE_COUNT)
3126
0
        return ERROR_INT("invalid type", __func__, 1);
3127
0
    if (type != L_MEAN_ABSVAL && (nbins < 1 || nbins > 256))
3128
0
        return ERROR_INT("invalid nbins", __func__, 1);
3129
0
    pixGetDimensions(pixs, &w, &h, NULL);
3130
3131
0
    datas = pixGetData(pixs);
3132
0
    wpls = pixGetWpl(pixs);
3133
0
    if (type == L_MEAN_ABSVAL) {
3134
0
        for (i = 0; i < h; i++) {
3135
0
            sum = 0;
3136
0
            lines = datas + i * wpls;
3137
0
            for (j = 0; j < w; j++)
3138
0
                sum += GET_DATA_BYTE(lines, j);
3139
0
            colvect[i] = (l_float32)sum / (l_float32)w;
3140
0
        }
3141
0
        return 0;
3142
0
    }
3143
3144
        /* We need a histogram; binwidth ~ 256 / nbins */
3145
0
    histo = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32));
3146
0
    gray2bin = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
3147
0
    bin2gray = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32));
3148
0
    for (i = 0; i < 256; i++)  /* gray value --> histo bin */
3149
0
        gray2bin[i] = (i * nbins) / 256;
3150
0
    for (i = 0; i < nbins; i++)  /* histo bin --> gray value */
3151
0
        bin2gray[i] = (i * 256 + 128) / nbins;
3152
3153
0
    for (i = 0; i < h; i++) {
3154
0
        lines = datas + i * wpls;
3155
0
        for (k = 0; k < nbins; k++)
3156
0
            histo[k] = 0;
3157
0
        for (j = 0; j < w; j++) {
3158
0
            val = GET_DATA_BYTE(lines, j);
3159
0
            histo[gray2bin[val]]++;
3160
0
        }
3161
3162
0
        if (type == L_MEDIAN_VAL) {
3163
0
            sum = 0;
3164
0
            target = (w + 1) / 2;
3165
0
            for (k = 0; k < nbins; k++) {
3166
0
                sum += histo[k];
3167
0
                if (sum >= target) {
3168
0
                    colvect[i] = bin2gray[k];
3169
0
                    break;
3170
0
                }
3171
0
            }
3172
0
        } else if (type == L_MODE_VAL) {
3173
0
            max = 0;
3174
0
            modeval = 0;
3175
0
            for (k = 0; k < nbins; k++) {
3176
0
                if (histo[k] > max) {
3177
0
                    max = histo[k];
3178
0
                    modeval = k;
3179
0
                }
3180
0
            }
3181
0
            if (max < thresh)
3182
0
                colvect[i] = 0;
3183
0
            else
3184
0
                colvect[i] = bin2gray[modeval];
3185
0
        } else {  /* type == L_MODE_COUNT */
3186
0
            max = 0;
3187
0
            for (k = 0; k < nbins; k++) {
3188
0
                if (histo[k] > max)
3189
0
                    max = histo[k];
3190
0
            }
3191
0
            colvect[i] = max;
3192
0
        }
3193
0
    }
3194
3195
0
    LEPT_FREE(histo);
3196
0
    LEPT_FREE(gray2bin);
3197
0
    LEPT_FREE(bin2gray);
3198
0
    return 0;
3199
0
}
3200
3201
3202
/*!
3203
 * \brief   pixGetColumnStats()
3204
 *
3205
 * \param[in]   pixs     8 bpp; not cmapped
3206
 * \param[in]   type     L_MEAN_ABSVAL, L_MEDIAN_VAL, L_MODE_VAL, L_MODE_COUNT
3207
 * \param[in]   nbins    of histogram for median and mode; ignored for mean
3208
 * \param[in]   thresh   on histogram for mode val; ignored for all other types
3209
 * \param[in]   rowvect  vector of results gathered down the columns of pixs
3210
 * \return  0 if OK, 1 on error
3211
 *
3212
 * <pre>
3213
 * Notes:
3214
 *      (1) This computes a row vector of statistics using each
3215
 *          column of a Pix.  The result is put in %rowvect.
3216
 *      (2) The %thresh parameter works with L_MODE_VAL only, and
3217
 *          sets a minimum occupancy of the mode bin.
3218
 *          If the occupancy of the mode bin is less than %thresh, the
3219
 *          mode value is returned as 0.  To always return the actual
3220
 *          mode value, set %thresh = 0.
3221
 *      (3) What is the meaning of this %thresh parameter?
3222
 *          For each column, the total count in the histogram is h, the
3223
 *          image height.  So %thresh, relative to h, gives a measure
3224
 *          of the ratio of the bin width to the width of the distribution.
3225
 *          The larger %thresh, the narrower the distribution must be
3226
 *          for the mode value to be returned (instead of returning 0).
3227
 * </pre>
3228
 */
3229
l_ok
3230
pixGetColumnStats(PIX        *pixs,
3231
                  l_int32     type,
3232
                  l_int32     nbins,
3233
                  l_int32     thresh,
3234
                  l_float32  *rowvect)
3235
0
{
3236
0
l_int32    i, j, k, w, h, val, wpls, sum, target, max, modeval;
3237
0
l_int32   *histo, *gray2bin, *bin2gray;
3238
0
l_uint32  *datas;
3239
3240
0
    if (!pixs || pixGetDepth(pixs) != 8)
3241
0
        return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
3242
0
    if (!rowvect)
3243
0
        return ERROR_INT("rowvect not defined", __func__, 1);
3244
0
    if (type != L_MEAN_ABSVAL && type != L_MEDIAN_VAL &&
3245
0
        type != L_MODE_VAL && type != L_MODE_COUNT)
3246
0
        return ERROR_INT("invalid type", __func__, 1);
3247
0
    if (type != L_MEAN_ABSVAL && (nbins < 1 || nbins > 256))
3248
0
        return ERROR_INT("invalid nbins", __func__, 1);
3249
0
    pixGetDimensions(pixs, &w, &h, NULL);
3250
3251
0
    datas = pixGetData(pixs);
3252
0
    wpls = pixGetWpl(pixs);
3253
0
    if (type == L_MEAN_ABSVAL) {
3254
0
        for (j = 0; j < w; j++) {
3255
0
            sum = 0;
3256
0
            for (i = 0; i < h; i++)
3257
0
                sum += GET_DATA_BYTE(datas + i * wpls, j);
3258
0
            rowvect[j] = (l_float32)sum / (l_float32)h;
3259
0
        }
3260
0
        return 0;
3261
0
    }
3262
3263
        /* We need a histogram; binwidth ~ 256 / nbins */
3264
0
    histo = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32));
3265
0
    gray2bin = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
3266
0
    bin2gray = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32));
3267
0
    for (i = 0; i < 256; i++)  /* gray value --> histo bin */
3268
0
        gray2bin[i] = (i * nbins) / 256;
3269
0
    for (i = 0; i < nbins; i++)  /* histo bin --> gray value */
3270
0
        bin2gray[i] = (i * 256 + 128) / nbins;
3271
3272
0
    for (j = 0; j < w; j++) {
3273
0
        for (i = 0; i < h; i++) {
3274
0
            val = GET_DATA_BYTE(datas + i * wpls, j);
3275
0
            histo[gray2bin[val]]++;
3276
0
        }
3277
3278
0
        if (type == L_MEDIAN_VAL) {
3279
0
            sum = 0;
3280
0
            target = (h + 1) / 2;
3281
0
            for (k = 0; k < nbins; k++) {
3282
0
                sum += histo[k];
3283
0
                if (sum >= target) {
3284
0
                    rowvect[j] = bin2gray[k];
3285
0
                    break;
3286
0
                }
3287
0
            }
3288
0
        } else if (type == L_MODE_VAL) {
3289
0
            max = 0;
3290
0
            modeval = 0;
3291
0
            for (k = 0; k < nbins; k++) {
3292
0
                if (histo[k] > max) {
3293
0
                    max = histo[k];
3294
0
                    modeval = k;
3295
0
                }
3296
0
            }
3297
0
            if (max < thresh)
3298
0
                rowvect[j] = 0;
3299
0
            else
3300
0
                rowvect[j] = bin2gray[modeval];
3301
0
        } else {  /* type == L_MODE_COUNT */
3302
0
            max = 0;
3303
0
            for (k = 0; k < nbins; k++) {
3304
0
                if (histo[k] > max)
3305
0
                    max = histo[k];
3306
0
            }
3307
0
            rowvect[j] = max;
3308
0
        }
3309
0
        for (k = 0; k < nbins; k++)
3310
0
            histo[k] = 0;
3311
0
    }
3312
3313
0
    LEPT_FREE(histo);
3314
0
    LEPT_FREE(gray2bin);
3315
0
    LEPT_FREE(bin2gray);
3316
0
    return 0;
3317
0
}
3318
3319
3320
/*!
3321
 * \brief   pixSetPixelColumn()
3322
 *
3323
 * \param[in]   pix      8 bpp; not cmapped
3324
 * \param[in]   col      column index
3325
 * \param[in]   colvect  vector of floats
3326
 * \return  0 if OK, 1 on error
3327
 */
3328
l_ok
3329
pixSetPixelColumn(PIX        *pix,
3330
                  l_int32     col,
3331
                  l_float32  *colvect)
3332
0
{
3333
0
l_int32    i, w, h, wpl;
3334
0
l_uint32  *data;
3335
3336
0
    if (!pix || pixGetDepth(pix) != 8)
3337
0
        return ERROR_INT("pix not defined or not 8 bpp", __func__, 1);
3338
0
    if (!colvect)
3339
0
        return ERROR_INT("colvect not defined", __func__, 1);
3340
0
    pixGetDimensions(pix, &w, &h, NULL);
3341
0
    if (col < 0 || col > w)
3342
0
        return ERROR_INT("invalid col", __func__, 1);
3343
3344
0
    data = pixGetData(pix);
3345
0
    wpl = pixGetWpl(pix);
3346
0
    for (i = 0; i < h; i++)
3347
0
        SET_DATA_BYTE(data + i * wpl, col, (l_int32)colvect[i]);
3348
3349
0
    return 0;
3350
0
}
3351
3352
3353
/*-------------------------------------------------------------*
3354
 *              Foreground/background estimation               *
3355
 *-------------------------------------------------------------*/
3356
/*!
3357
 * \brief   pixThresholdForFgBg()
3358
 *
3359
 * \param[in]    pixs    any depth; cmapped ok
3360
 * \param[in]    factor  subsampling factor; integer >= 1
3361
 * \param[in]    thresh  threshold for generating foreground mask
3362
 * \param[out]   pfgval  [optional] average foreground value
3363
 * \param[out]   pbgval  [optional] average background value
3364
 * \return  0 if OK, 1 on error
3365
 */
3366
l_ok
3367
pixThresholdForFgBg(PIX      *pixs,
3368
                    l_int32   factor,
3369
                    l_int32   thresh,
3370
                    l_int32  *pfgval,
3371
                    l_int32  *pbgval)
3372
0
{
3373
0
l_float32  fval;
3374
0
PIX       *pixg, *pixm;
3375
3376
0
    if (pfgval) *pfgval = 0;
3377
0
    if (pbgval) *pbgval = 0;
3378
0
    if (!pfgval && !pbgval)
3379
0
        return ERROR_INT("no data requested", __func__, 1);
3380
0
    if (!pixs)
3381
0
        return ERROR_INT("pixs not defined", __func__, 1);
3382
3383
        /* Generate a subsampled 8 bpp version and a mask over the fg */
3384
0
    pixg = pixConvertTo8BySampling(pixs, factor, 0);
3385
0
    pixm = pixThresholdToBinary(pixg, thresh);
3386
3387
0
    if (pfgval) {
3388
0
        pixGetAverageMasked(pixg, pixm, 0, 0, 1, L_MEAN_ABSVAL, &fval);
3389
0
        *pfgval = (l_int32)(fval + 0.5);
3390
0
    }
3391
3392
0
    if (pbgval) {
3393
0
        pixInvert(pixm, pixm);
3394
0
        pixGetAverageMasked(pixg, pixm, 0, 0, 1, L_MEAN_ABSVAL, &fval);
3395
0
        *pbgval = (l_int32)(fval + 0.5);
3396
0
    }
3397
3398
0
    pixDestroy(&pixg);
3399
0
    pixDestroy(&pixm);
3400
0
    return 0;
3401
0
}
3402
3403
3404
/*!
3405
 * \brief   pixSplitDistributionFgBg()
3406
 *
3407
 * \param[in]    pixs        any depth; cmapped ok
3408
 * \param[in]    scorefract  fraction of the max score, used to determine
3409
 *                           the range over which the histogram min is searched
3410
 * \param[in]    factor      subsampling factor; integer >= 1
3411
 * \param[out]   pthresh     [optional] best threshold for separating
3412
 * \param[out]   pfgval      [optional] average foreground value
3413
 * \param[out]   pbgval      [optional] average background value
3414
 * \param[out]   ppixdb      [optional] plot of distribution and split point
3415
 * \return  0 if OK, 1 on error
3416
 *
3417
 * <pre>
3418
 * Notes:
3419
 *      (1) See numaSplitDistribution() for details on the underlying
3420
 *          method of choosing a threshold.
3421
 * </pre>
3422
 */
3423
l_ok
3424
pixSplitDistributionFgBg(PIX       *pixs,
3425
                         l_float32  scorefract,
3426
                         l_int32    factor,
3427
                         l_int32   *pthresh,
3428
                         l_int32   *pfgval,
3429
                         l_int32   *pbgval,
3430
                         PIX      **ppixdb)
3431
0
{
3432
0
char       buf[256];
3433
0
l_int32    thresh;
3434
0
l_float32  avefg, avebg, maxnum;
3435
0
GPLOT     *gplot;
3436
0
NUMA      *na, *nascore, *nax, *nay;
3437
0
PIX       *pixg;
3438
3439
0
    if (pthresh) *pthresh = 0;
3440
0
    if (pfgval) *pfgval = 0;
3441
0
    if (pbgval) *pbgval = 0;
3442
0
    if (ppixdb) *ppixdb = NULL;
3443
0
    if (!pthresh && !pfgval && !pbgval)
3444
0
        return ERROR_INT("no data requested", __func__, 1);
3445
0
    if (!pixs)
3446
0
        return ERROR_INT("pixs not defined", __func__, 1);
3447
3448
        /* Generate a subsampled 8 bpp version */
3449
0
    pixg = pixConvertTo8BySampling(pixs, factor, 0);
3450
3451
        /* Make the fg/bg estimates */
3452
0
    na = pixGetGrayHistogram(pixg, 1);
3453
0
    if (ppixdb) {
3454
0
        numaSplitDistribution(na, scorefract, &thresh, &avefg, &avebg,
3455
0
                              NULL, NULL, &nascore);
3456
0
        numaDestroy(&nascore);
3457
0
    } else {
3458
0
        numaSplitDistribution(na, scorefract, &thresh, &avefg, &avebg,
3459
0
                              NULL, NULL, NULL);
3460
0
    }
3461
3462
0
    if (pthresh) *pthresh = thresh;
3463
0
    if (pfgval) *pfgval = (l_int32)(avefg + 0.5);
3464
0
    if (pbgval) *pbgval = (l_int32)(avebg + 0.5);
3465
3466
0
    if (ppixdb) {
3467
0
        lept_mkdir("lept/redout");
3468
0
        gplot = gplotCreate("/tmp/lept/redout/histplot", GPLOT_PNG, "Histogram",
3469
0
                            "Grayscale value", "Number of pixels");
3470
0
        gplotAddPlot(gplot, NULL, na, GPLOT_LINES, NULL);
3471
0
        nax = numaMakeConstant(thresh, 2);
3472
0
        numaGetMax(na, &maxnum, NULL);
3473
0
        nay = numaMakeConstant(0, 2);
3474
0
        numaReplaceNumber(nay, 1, (l_int32)(0.5 * maxnum));
3475
0
        snprintf(buf, sizeof(buf), "score fract = %3.1f", scorefract);
3476
0
        gplotAddPlot(gplot, nax, nay, GPLOT_LINES, buf);
3477
0
        *ppixdb = gplotMakeOutputPix(gplot);
3478
0
        gplotDestroy(&gplot);
3479
0
        numaDestroy(&nax);
3480
0
        numaDestroy(&nay);
3481
0
    }
3482
3483
0
    pixDestroy(&pixg);
3484
0
    numaDestroy(&na);
3485
0
    return 0;
3486
0
}