comparison mupdf-source/thirdparty/leptonica/src/pix4.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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 {
118 l_int32 i, j, w, h, d, wpl, val, size, count;
119 l_uint32 *data, *line;
120 l_float32 *array;
121 NUMA *na;
122 PIX *pixg;
123
124 if (!pixs)
125 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
126 d = pixGetDepth(pixs);
127 if (d > 16)
128 return (NUMA *)ERROR_PTR("depth not in {1,2,4,8,16}", __func__, NULL);
129 if (factor < 1)
130 return (NUMA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
131
132 if (pixGetColormap(pixs))
133 pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
134 else
135 pixg = pixClone(pixs);
136
137 pixGetDimensions(pixg, &w, &h, &d);
138 size = 1 << d;
139 if ((na = numaCreate(size)) == NULL) {
140 pixDestroy(&pixg);
141 return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
142 }
143 numaSetCount(na, size); /* all initialized to 0.0 */
144 array = numaGetFArray(na, L_NOCOPY);
145
146 if (d == 1) { /* special case */
147 pixCountPixels(pixg, &count, NULL);
148 array[0] = w * h - count;
149 array[1] = count;
150 pixDestroy(&pixg);
151 return na;
152 }
153
154 wpl = pixGetWpl(pixg);
155 data = pixGetData(pixg);
156 for (i = 0; i < h; i += factor) {
157 line = data + i * wpl;
158 if (d == 2) {
159 for (j = 0; j < w; j += factor) {
160 val = GET_DATA_DIBIT(line, j);
161 array[val] += 1.0;
162 }
163 } else if (d == 4) {
164 for (j = 0; j < w; j += factor) {
165 val = GET_DATA_QBIT(line, j);
166 array[val] += 1.0;
167 }
168 } else if (d == 8) {
169 for (j = 0; j < w; j += factor) {
170 val = GET_DATA_BYTE(line, j);
171 array[val] += 1.0;
172 }
173 } else { /* d == 16 */
174 for (j = 0; j < w; j += factor) {
175 val = GET_DATA_TWO_BYTES(line, j);
176 array[val] += 1.0;
177 }
178 }
179 }
180
181 pixDestroy(&pixg);
182 return na;
183 }
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 {
215 l_int32 i, j, w, h, wm, hm, dm, wplg, wplm, val;
216 l_uint32 *datag, *datam, *lineg, *linem;
217 l_float32 *array;
218 NUMA *na;
219 PIX *pixg;
220
221 if (!pixm)
222 return pixGetGrayHistogram(pixs, factor);
223 if (!pixs)
224 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
225 if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs))
226 return (NUMA *)ERROR_PTR("pixs neither 8 bpp nor colormapped",
227 __func__, NULL);
228 pixGetDimensions(pixm, &wm, &hm, &dm);
229 if (dm != 1)
230 return (NUMA *)ERROR_PTR("pixm not 1 bpp", __func__, NULL);
231 if (factor < 1)
232 return (NUMA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
233
234 if ((na = numaCreate(256)) == NULL)
235 return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
236 numaSetCount(na, 256); /* all initialized to 0.0 */
237 array = numaGetFArray(na, L_NOCOPY);
238
239 if (pixGetColormap(pixs))
240 pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
241 else
242 pixg = pixClone(pixs);
243 pixGetDimensions(pixg, &w, &h, NULL);
244 datag = pixGetData(pixg);
245 wplg = pixGetWpl(pixg);
246 datam = pixGetData(pixm);
247 wplm = pixGetWpl(pixm);
248
249 /* Generate the histogram */
250 for (i = 0; i < hm; i += factor) {
251 if (y + i < 0 || y + i >= h) continue;
252 lineg = datag + (y + i) * wplg;
253 linem = datam + i * wplm;
254 for (j = 0; j < wm; j += factor) {
255 if (x + j < 0 || x + j >= w) continue;
256 if (GET_DATA_BIT(linem, j)) {
257 val = GET_DATA_BYTE(lineg, x + j);
258 array[val] += 1.0;
259 }
260 }
261 }
262
263 pixDestroy(&pixg);
264 return na;
265 }
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 {
291 l_int32 i, j, bx, by, bw, bh, w, h, wplg, val;
292 l_uint32 *datag, *lineg;
293 l_float32 *array;
294 NUMA *na;
295 PIX *pixg;
296
297 if (!box)
298 return pixGetGrayHistogram(pixs, factor);
299 if (!pixs)
300 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
301 if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs))
302 return (NUMA *)ERROR_PTR("pixs neither 8 bpp nor colormapped",
303 __func__, NULL);
304 if (factor < 1)
305 return (NUMA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
306
307 if ((na = numaCreate(256)) == NULL)
308 return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
309 numaSetCount(na, 256); /* all initialized to 0.0 */
310 array = numaGetFArray(na, L_NOCOPY);
311
312 if (pixGetColormap(pixs))
313 pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
314 else
315 pixg = pixClone(pixs);
316 pixGetDimensions(pixg, &w, &h, NULL);
317 datag = pixGetData(pixg);
318 wplg = pixGetWpl(pixg);
319 boxGetGeometry(box, &bx, &by, &bw, &bh);
320
321 /* Generate the histogram */
322 for (i = 0; i < bh; i += factor) {
323 if (by + i < 0 || by + i >= h) continue;
324 lineg = datag + (by + i) * wplg;
325 for (j = 0; j < bw; j += factor) {
326 if (bx + j < 0 || bx + j >= w) continue;
327 val = GET_DATA_BYTE(lineg, bx + j);
328 array[val] += 1.0;
329 }
330 }
331
332 pixDestroy(&pixg);
333 return na;
334 }
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 {
358 l_int32 i, n;
359 NUMA *na;
360 NUMAA *naa;
361 PIX *pix1, *pix2;
362 PIXA *pixa;
363
364 if (!pixs)
365 return (NUMAA *)ERROR_PTR("pixs not defined", __func__, NULL);
366 if (factor < 1)
367 return (NUMAA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
368 if (nx < 1 || ny < 1)
369 return (NUMAA *)ERROR_PTR("nx and ny must both be > 0", __func__, NULL);
370
371 n = nx * ny;
372 if ((naa = numaaCreate(n)) == NULL)
373 return (NUMAA *)ERROR_PTR("naa not made", __func__, NULL);
374
375 pix1 = pixConvertTo8(pixs, FALSE);
376 pixa = pixaSplitPix(pix1, nx, ny, 0, 0);
377 for (i = 0; i < n; i++) {
378 pix2 = pixaGetPix(pixa, i, L_CLONE);
379 na = pixGetGrayHistogram(pix2, factor);
380 numaaAddNuma(naa, na, L_INSERT);
381 pixDestroy(&pix2);
382 }
383
384 pixDestroy(&pix1);
385 pixaDestroy(&pixa);
386 return naa;
387 }
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 {
414 l_int32 i, j, w, h, d, wpl, index, rval, gval, bval;
415 l_uint32 *data, *line;
416 l_float32 *rarray, *garray, *barray;
417 NUMA *nar, *nag, *nab;
418 PIXCMAP *cmap;
419
420 if (pnar) *pnar = NULL;
421 if (pnag) *pnag = NULL;
422 if (pnab) *pnab = NULL;
423 if (!pnar || !pnag || !pnab)
424 return ERROR_INT("&nar, &nag, &nab not all defined", __func__, 1);
425 if (!pixs)
426 return ERROR_INT("pixs not defined", __func__, 1);
427 pixGetDimensions(pixs, &w, &h, &d);
428 cmap = pixGetColormap(pixs);
429 if (cmap && (d != 2 && d != 4 && d != 8))
430 return ERROR_INT("colormap and not 2, 4, or 8 bpp", __func__, 1);
431 if (!cmap && d != 32)
432 return ERROR_INT("no colormap and not rgb", __func__, 1);
433 if (factor < 1)
434 return ERROR_INT("sampling factor must be >= 1", __func__, 1);
435
436 /* Set up the histogram arrays */
437 nar = numaCreate(256);
438 nag = numaCreate(256);
439 nab = numaCreate(256);
440 numaSetCount(nar, 256);
441 numaSetCount(nag, 256);
442 numaSetCount(nab, 256);
443 rarray = numaGetFArray(nar, L_NOCOPY);
444 garray = numaGetFArray(nag, L_NOCOPY);
445 barray = numaGetFArray(nab, L_NOCOPY);
446 *pnar = nar;
447 *pnag = nag;
448 *pnab = nab;
449
450 /* Generate the color histograms */
451 data = pixGetData(pixs);
452 wpl = pixGetWpl(pixs);
453 if (cmap) {
454 for (i = 0; i < h; i += factor) {
455 line = data + i * wpl;
456 for (j = 0; j < w; j += factor) {
457 if (d == 8)
458 index = GET_DATA_BYTE(line, j);
459 else if (d == 4)
460 index = GET_DATA_QBIT(line, j);
461 else /* 2 bpp */
462 index = GET_DATA_DIBIT(line, j);
463 pixcmapGetColor(cmap, index, &rval, &gval, &bval);
464 rarray[rval] += 1.0;
465 garray[gval] += 1.0;
466 barray[bval] += 1.0;
467 }
468 }
469 } else { /* 32 bpp rgb */
470 for (i = 0; i < h; i += factor) {
471 line = data + i * wpl;
472 for (j = 0; j < w; j += factor) {
473 extractRGBValues(line[j], &rval, &gval, &bval);
474 rarray[rval] += 1.0;
475 garray[gval] += 1.0;
476 barray[bval] += 1.0;
477 }
478 }
479 }
480
481 return 0;
482 }
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 {
517 l_int32 i, j, w, h, d, wm, hm, dm, wpls, wplm, index, rval, gval, bval;
518 l_uint32 *datas, *datam, *lines, *linem;
519 l_float32 *rarray, *garray, *barray;
520 NUMA *nar, *nag, *nab;
521 PIXCMAP *cmap;
522
523 if (!pixm)
524 return pixGetColorHistogram(pixs, factor, pnar, pnag, pnab);
525
526 if (pnar) *pnar = NULL;
527 if (pnag) *pnag = NULL;
528 if (pnab) *pnab = NULL;
529 if (!pnar || !pnag || !pnab)
530 return ERROR_INT("&nar, &nag, &nab not all defined", __func__, 1);
531 if (!pixs)
532 return ERROR_INT("pixs not defined", __func__, 1);
533 pixGetDimensions(pixs, &w, &h, &d);
534 cmap = pixGetColormap(pixs);
535 if (cmap && (d != 2 && d != 4 && d != 8))
536 return ERROR_INT("colormap and not 2, 4, or 8 bpp", __func__, 1);
537 if (!cmap && d != 32)
538 return ERROR_INT("no colormap and not rgb", __func__, 1);
539 pixGetDimensions(pixm, &wm, &hm, &dm);
540 if (dm != 1)
541 return ERROR_INT("pixm not 1 bpp", __func__, 1);
542 if (factor < 1)
543 return ERROR_INT("sampling factor must be >= 1", __func__, 1);
544
545 /* Set up the histogram arrays */
546 nar = numaCreate(256);
547 nag = numaCreate(256);
548 nab = numaCreate(256);
549 numaSetCount(nar, 256);
550 numaSetCount(nag, 256);
551 numaSetCount(nab, 256);
552 rarray = numaGetFArray(nar, L_NOCOPY);
553 garray = numaGetFArray(nag, L_NOCOPY);
554 barray = numaGetFArray(nab, L_NOCOPY);
555 *pnar = nar;
556 *pnag = nag;
557 *pnab = nab;
558
559 /* Generate the color histograms */
560 datas = pixGetData(pixs);
561 wpls = pixGetWpl(pixs);
562 datam = pixGetData(pixm);
563 wplm = pixGetWpl(pixm);
564 if (cmap) {
565 for (i = 0; i < hm; i += factor) {
566 if (y + i < 0 || y + i >= h) continue;
567 lines = datas + (y + i) * wpls;
568 linem = datam + i * wplm;
569 for (j = 0; j < wm; j += factor) {
570 if (x + j < 0 || x + j >= w) continue;
571 if (GET_DATA_BIT(linem, j)) {
572 if (d == 8)
573 index = GET_DATA_BYTE(lines, x + j);
574 else if (d == 4)
575 index = GET_DATA_QBIT(lines, x + j);
576 else /* 2 bpp */
577 index = GET_DATA_DIBIT(lines, x + j);
578 pixcmapGetColor(cmap, index, &rval, &gval, &bval);
579 rarray[rval] += 1.0;
580 garray[gval] += 1.0;
581 barray[bval] += 1.0;
582 }
583 }
584 }
585 } else { /* 32 bpp rgb */
586 for (i = 0; i < hm; i += factor) {
587 if (y + i < 0 || y + i >= h) continue;
588 lines = datas + (y + i) * wpls;
589 linem = datam + i * wplm;
590 for (j = 0; j < wm; j += factor) {
591 if (x + j < 0 || x + j >= w) continue;
592 if (GET_DATA_BIT(linem, j)) {
593 extractRGBValues(lines[x + j], &rval, &gval, &bval);
594 rarray[rval] += 1.0;
595 garray[gval] += 1.0;
596 barray[bval] += 1.0;
597 }
598 }
599 }
600 }
601
602 return 0;
603 }
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 {
624 l_int32 i, j, w, h, d, wpl, val, size;
625 l_uint32 *data, *line;
626 l_float32 *array;
627 NUMA *na;
628
629 if (!pixs)
630 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
631 if (pixGetColormap(pixs) == NULL)
632 return (NUMA *)ERROR_PTR("pixs not cmapped", __func__, NULL);
633 if (factor < 1)
634 return (NUMA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
635 pixGetDimensions(pixs, &w, &h, &d);
636 if (d != 2 && d != 4 && d != 8)
637 return (NUMA *)ERROR_PTR("d not 2, 4 or 8", __func__, NULL);
638
639 size = 1 << d;
640 if ((na = numaCreate(size)) == NULL)
641 return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
642 numaSetCount(na, size); /* all initialized to 0.0 */
643 array = numaGetFArray(na, L_NOCOPY);
644
645 wpl = pixGetWpl(pixs);
646 data = pixGetData(pixs);
647 for (i = 0; i < h; i += factor) {
648 line = data + i * wpl;
649 for (j = 0; j < w; j += factor) {
650 if (d == 8)
651 val = GET_DATA_BYTE(line, j);
652 else if (d == 4)
653 val = GET_DATA_QBIT(line, j);
654 else /* d == 2 */
655 val = GET_DATA_DIBIT(line, j);
656 array[val] += 1.0;
657 }
658 }
659
660 return na;
661 }
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 {
690 l_int32 i, j, w, h, d, wm, hm, dm, wpls, wplm, val, size;
691 l_uint32 *datas, *datam, *lines, *linem;
692 l_float32 *array;
693 NUMA *na;
694
695 if (!pixm)
696 return pixGetCmapHistogram(pixs, factor);
697
698 if (!pixs)
699 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
700 if (pixGetColormap(pixs) == NULL)
701 return (NUMA *)ERROR_PTR("pixs not cmapped", __func__, NULL);
702 pixGetDimensions(pixm, &wm, &hm, &dm);
703 if (dm != 1)
704 return (NUMA *)ERROR_PTR("pixm not 1 bpp", __func__, NULL);
705 if (factor < 1)
706 return (NUMA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
707 pixGetDimensions(pixs, &w, &h, &d);
708 if (d != 2 && d != 4 && d != 8)
709 return (NUMA *)ERROR_PTR("d not 2, 4 or 8", __func__, NULL);
710
711 size = 1 << d;
712 if ((na = numaCreate(size)) == NULL)
713 return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
714 numaSetCount(na, size); /* all initialized to 0.0 */
715 array = numaGetFArray(na, L_NOCOPY);
716
717 datas = pixGetData(pixs);
718 wpls = pixGetWpl(pixs);
719 datam = pixGetData(pixm);
720 wplm = pixGetWpl(pixm);
721
722 for (i = 0; i < hm; i += factor) {
723 if (y + i < 0 || y + i >= h) continue;
724 lines = datas + (y + i) * wpls;
725 linem = datam + i * wplm;
726 for (j = 0; j < wm; j += factor) {
727 if (x + j < 0 || x + j >= w) continue;
728 if (GET_DATA_BIT(linem, j)) {
729 if (d == 8)
730 val = GET_DATA_BYTE(lines, x + j);
731 else if (d == 4)
732 val = GET_DATA_QBIT(lines, x + j);
733 else /* d == 2 */
734 val = GET_DATA_DIBIT(lines, x + j);
735 array[val] += 1.0;
736 }
737 }
738 }
739
740 return na;
741 }
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 {
766 l_int32 i, j, bx, by, bw, bh, w, h, d, wpls, val, size;
767 l_uint32 *datas, *lines;
768 l_float32 *array;
769 NUMA *na;
770
771 if (!box)
772 return pixGetCmapHistogram(pixs, factor);
773 if (!pixs)
774 return (NUMA *)ERROR_PTR("pixs not defined", __func__, NULL);
775 if (pixGetColormap(pixs) == NULL)
776 return (NUMA *)ERROR_PTR("pixs not cmapped", __func__, NULL);
777 if (factor < 1)
778 return (NUMA *)ERROR_PTR("sampling must be >= 1", __func__, NULL);
779 pixGetDimensions(pixs, &w, &h, &d);
780 if (d != 2 && d != 4 && d != 8)
781 return (NUMA *)ERROR_PTR("d not 2, 4 or 8", __func__, NULL);
782
783 size = 1 << d;
784 if ((na = numaCreate(size)) == NULL)
785 return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
786 numaSetCount(na, size); /* all initialized to 0.0 */
787 array = numaGetFArray(na, L_NOCOPY);
788
789 datas = pixGetData(pixs);
790 wpls = pixGetWpl(pixs);
791 boxGetGeometry(box, &bx, &by, &bw, &bh);
792
793 for (i = 0; i < bh; i += factor) {
794 if (by + i < 0 || by + i >= h) continue;
795 lines = datas + (by + i) * wpls;
796 for (j = 0; j < bw; j += factor) {
797 if (bx + j < 0 || bx + j >= w) continue;
798 if (d == 8)
799 val = GET_DATA_BYTE(lines, bx + j);
800 else if (d == 4)
801 val = GET_DATA_QBIT(lines, bx + j);
802 else /* d == 2 */
803 val = GET_DATA_DIBIT(lines, bx + j);
804 array[val] += 1.0;
805 }
806 }
807
808 return na;
809 }
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 {
829 L_DNA *da1, *da2;
830
831 if (!pncolors)
832 return ERROR_INT("&ncolors not defined", __func__, 1);
833 *pncolors = 0;
834 if (!pixs || pixGetDepth(pixs) != 32)
835 return ERROR_INT("pixs not defined or not 32 bpp", __func__, 1);
836 da1 = pixConvertDataToDna(pixs);
837 l_dnaRemoveDupsByHmap(da1, &da2, NULL);
838 *pncolors = l_dnaGetCount(da2);
839 l_dnaDestroy(&da1);
840 l_dnaDestroy(&da2);
841 return 0;
842 }
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 {
864 L_AMAP *amap;
865
866 if (!pncolors)
867 return ERROR_INT("&ncolors not defined", __func__, 1);
868 *pncolors = 0;
869 if (!pixs || pixGetDepth(pixs) != 32)
870 return ERROR_INT("pixs not defined or not 32 bpp", __func__, 1);
871 if (factor <= 0)
872 return ERROR_INT("factor must be > 0", __func__, 1);
873 amap = pixGetColorAmapHistogram(pixs, factor);
874 *pncolors = l_amapSize(amap);
875 l_amapDestroy(&amap);
876 return 0;
877 }
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 {
897 l_int32 i, j, w, h, wpl;
898 l_uint32 *data, *line;
899 L_AMAP *amap;
900 RB_TYPE key, value;
901 RB_TYPE *pval;
902
903 if (!pixs)
904 return (L_AMAP *)ERROR_PTR("pixs not defined", __func__, NULL);
905 if (pixGetDepth(pixs) != 32)
906 return (L_AMAP *)ERROR_PTR("pixs not 32 bpp", __func__, NULL);
907 if (factor <= 0)
908 return (L_AMAP *)ERROR_PTR("factor must be > 0", __func__, NULL);
909 pixGetDimensions(pixs, &w, &h, NULL);
910 data = pixGetData(pixs);
911 wpl = pixGetWpl(pixs);
912 amap = l_amapCreate(L_UINT_TYPE);
913 for (i = 0; i < h; i += factor) {
914 line = data + i * wpl;
915 for (j = 0; j < w; j += factor) {
916 key.utype = line[j];
917 pval = l_amapFind(amap, key);
918 if (!pval)
919 value.itype = 1;
920 else
921 value.itype = 1 + pval->itype;
922 l_amapInsert(amap, key, value);
923 }
924 }
925
926 return amap;
927 }
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 {
946 RB_TYPE key;
947 RB_TYPE *pval;
948
949 if (!amap)
950 return ERROR_INT("amap not defined", __func__, -1);
951
952 key.utype = val;
953 pval = l_amapFind(amap, key);
954 return (pval) ? pval->itype : 0;
955 }
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 {
985 l_int32 d;
986 l_float32 val, rval, gval, bval;
987 PIX *pixt;
988 PIXCMAP *cmap;
989
990 if (!pvalue)
991 return ERROR_INT("&value not defined", __func__, 1);
992 *pvalue = 0;
993 if (!pixs)
994 return ERROR_INT("pixs not defined", __func__, 1);
995 d = pixGetDepth(pixs);
996 cmap = pixGetColormap(pixs);
997 if (d != 8 && d != 32 && !cmap)
998 return ERROR_INT("pixs not 8 or 32 bpp, or cmapped", __func__, 1);
999 if (cmap)
1000 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
1001 else
1002 pixt = pixClone(pixs);
1003 d = pixGetDepth(pixt);
1004
1005 if (d == 8) {
1006 pixGetRankValueMasked(pixt, NULL, 0, 0, factor, rank, &val, NULL);
1007 *pvalue = lept_roundftoi(val);
1008 } else {
1009 pixGetRankValueMaskedRGB(pixt, NULL, 0, 0, factor, rank,
1010 &rval, &gval, &bval);
1011 composeRGBPixel(lept_roundftoi(rval), lept_roundftoi(gval),
1012 lept_roundftoi(bval), pvalue);
1013 }
1014
1015 pixDestroy(&pixt);
1016 return 0;
1017 }
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 {
1058 l_float32 scale;
1059 PIX *pixmt, *pixt;
1060
1061 if (prval) *prval = 0.0;
1062 if (pgval) *pgval = 0.0;
1063 if (pbval) *pbval = 0.0;
1064 if (!prval && !pgval && !pbval)
1065 return ERROR_INT("no results requested", __func__, 1);
1066 if (!pixs)
1067 return ERROR_INT("pixs not defined", __func__, 1);
1068 if (pixGetDepth(pixs) != 32)
1069 return ERROR_INT("pixs not 32 bpp", __func__, 1);
1070 if (pixm && pixGetDepth(pixm) != 1)
1071 return ERROR_INT("pixm not 1 bpp", __func__, 1);
1072 if (factor < 1)
1073 return ERROR_INT("sampling factor must be >= 1", __func__, 1);
1074 if (rank < 0.0 || rank > 1.0)
1075 return ERROR_INT("rank not in [0.0 ... 1.0]", __func__, 1);
1076
1077 pixmt = NULL;
1078 if (pixm) {
1079 scale = 1.0f / (l_float32)factor;
1080 pixmt = pixScale(pixm, scale, scale);
1081 }
1082 if (prval) {
1083 pixt = pixScaleRGBToGrayFast(pixs, factor, COLOR_RED);
1084 pixGetRankValueMasked(pixt, pixmt, x / factor, y / factor,
1085 factor, rank, prval, NULL);
1086 pixDestroy(&pixt);
1087 }
1088 if (pgval) {
1089 pixt = pixScaleRGBToGrayFast(pixs, factor, COLOR_GREEN);
1090 pixGetRankValueMasked(pixt, pixmt, x / factor, y / factor,
1091 factor, rank, pgval, NULL);
1092 pixDestroy(&pixt);
1093 }
1094 if (pbval) {
1095 pixt = pixScaleRGBToGrayFast(pixs, factor, COLOR_BLUE);
1096 pixGetRankValueMasked(pixt, pixmt, x / factor, y / factor,
1097 factor, rank, pbval, NULL);
1098 pixDestroy(&pixt);
1099 }
1100 pixDestroy(&pixmt);
1101 return 0;
1102 }
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 {
1147 NUMA *na;
1148
1149 if (pna) *pna = NULL;
1150 if (!pval)
1151 return ERROR_INT("&val not defined", __func__, 1);
1152 *pval = 0.0;
1153 if (!pixs)
1154 return ERROR_INT("pixs not defined", __func__, 1);
1155 if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs))
1156 return ERROR_INT("pixs neither 8 bpp nor colormapped", __func__, 1);
1157 if (pixm && pixGetDepth(pixm) != 1)
1158 return ERROR_INT("pixm not 1 bpp", __func__, 1);
1159 if (factor < 1)
1160 return ERROR_INT("sampling factor must be >= 1", __func__, 1);
1161 if (rank < 0.0 || rank > 1.0)
1162 return ERROR_INT("rank not in [0.0 ... 1.0]", __func__, 1);
1163
1164 if ((na = pixGetGrayHistogramMasked(pixs, pixm, x, y, factor)) == NULL)
1165 return ERROR_INT("na not made", __func__, 1);
1166 numaHistogramGetValFromRank(na, rank, pval);
1167 if (pna)
1168 *pna = na;
1169 else
1170 numaDestroy(&na);
1171
1172 return 0;
1173 }
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 {
1214 l_int32 i, j, w, h, d, wm, hm, wpl1, wplm, val, rval, gval, bval, count;
1215 l_uint32 *data1, *datam, *line1, *linem;
1216 l_float64 sum, rsum, gsum, bsum;
1217 PIX *pix1;
1218
1219 if (!pval)
1220 return ERROR_INT("&val not defined", __func__, 1);
1221 *pval = 0;
1222 if (!pixs)
1223 return ERROR_INT("pixs not defined", __func__, 1);
1224 d = pixGetDepth(pixs);
1225 if (d != 32 && !pixGetColormap(pixs))
1226 return ERROR_INT("pixs not rgb or colormapped", __func__, 1);
1227 if (pixm && pixGetDepth(pixm) != 1)
1228 return ERROR_INT("pixm not 1 bpp", __func__, 1);
1229 if (factor < 1)
1230 return ERROR_INT("sampling factor must be >= 1", __func__, 1);
1231
1232 if (pixGetColormap(pixs))
1233 pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
1234 else
1235 pix1 = pixClone(pixs);
1236 pixGetDimensions(pix1, &w, &h, &d);
1237 if (d == 1) {
1238 pixDestroy(&pix1);
1239 return ERROR_INT("pix1 is just 1 bpp", __func__, 1);
1240 }
1241 data1 = pixGetData(pix1);
1242 wpl1 = pixGetWpl(pix1);
1243
1244 sum = rsum = gsum = bsum = 0.0;
1245 count = 0;
1246 if (!pixm) {
1247 for (i = 0; i < h; i += factor) {
1248 line1 = data1 + i * wpl1;
1249 for (j = 0; j < w; j += factor) {
1250 if (d == 8) {
1251 val = GET_DATA_BYTE(line1, j);
1252 sum += val;
1253 } else { /* rgb */
1254 extractRGBValues(*(line1 + j), &rval, &gval, &bval);
1255 rsum += rval;
1256 gsum += gval;
1257 bsum += bval;
1258 }
1259 count++;
1260 }
1261 }
1262 } else { /* masked */
1263 pixGetDimensions(pixm, &wm, &hm, NULL);
1264 datam = pixGetData(pixm);
1265 wplm = pixGetWpl(pixm);
1266 for (i = 0; i < hm; i += factor) {
1267 if (y + i < 0 || y + i >= h) continue;
1268 line1 = data1 + (y + i) * wpl1;
1269 linem = datam + i * wplm;
1270 for (j = 0; j < wm; j += factor) {
1271 if (x + j < 0 || x + j >= w) continue;
1272 if (GET_DATA_BIT(linem, j)) {
1273 if (d == 8) {
1274 val = GET_DATA_BYTE(line1, x + j);
1275 sum += val;
1276 } else { /* rgb */
1277 extractRGBValues(*(line1 + x + j), &rval, &gval, &bval);
1278 rsum += rval;
1279 gsum += gval;
1280 bsum += bval;
1281 }
1282 count++;
1283 }
1284 }
1285 }
1286 }
1287
1288 pixDestroy(&pix1);
1289 if (count == 0)
1290 return ERROR_INT("no pixels sampled", __func__, 1);
1291 if (d == 8) {
1292 *pval = (l_uint32)(sum / (l_float64)count);
1293 } else { /* d == 32 */
1294 rval = (l_uint32)(rsum / (l_float64)count);
1295 gval = (l_uint32)(gsum / (l_float64)count);
1296 bval = (l_uint32)(bsum / (l_float64)count);
1297 composeRGBPixel(rval, gval, bval, pval);
1298 }
1299
1300 return 0;
1301 }
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 {
1328 l_int32 d;
1329 l_float32 val, rval, gval, bval;
1330 PIX *pixt;
1331 PIXCMAP *cmap;
1332
1333 if (!pvalue)
1334 return ERROR_INT("&value not defined", __func__, 1);
1335 *pvalue = 0;
1336 if (!pixs)
1337 return ERROR_INT("pixs not defined", __func__, 1);
1338 d = pixGetDepth(pixs);
1339 cmap = pixGetColormap(pixs);
1340 if (d != 8 && d != 32 && !cmap)
1341 return ERROR_INT("pixs not 8 or 32 bpp, or cmapped", __func__, 1);
1342 if (cmap)
1343 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
1344 else
1345 pixt = pixClone(pixs);
1346 d = pixGetDepth(pixt);
1347
1348 if (d == 8) {
1349 pixGetAverageMasked(pixt, NULL, 0, 0, factor, type, &val);
1350 *pvalue = lept_roundftoi(val);
1351 } else {
1352 pixGetAverageMaskedRGB(pixt, NULL, 0, 0, factor, type,
1353 &rval, &gval, &bval);
1354 composeRGBPixel(lept_roundftoi(rval), lept_roundftoi(gval),
1355 lept_roundftoi(bval), pvalue);
1356 }
1357
1358 pixDestroy(&pixt);
1359 return 0;
1360 }
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 {
1398 l_int32 empty;
1399 PIX *pixt;
1400 PIXCMAP *cmap;
1401
1402 if (prval) *prval = 0.0;
1403 if (pgval) *pgval = 0.0;
1404 if (pbval) *pbval = 0.0;
1405 if (!prval && !pgval && !pbval)
1406 return ERROR_INT("no values requested", __func__, 1);
1407 if (!pixs)
1408 return ERROR_INT("pixs not defined", __func__, 1);
1409 cmap = pixGetColormap(pixs);
1410 if (pixGetDepth(pixs) != 32 && !cmap)
1411 return ERROR_INT("pixs neither 32 bpp nor colormapped", __func__, 1);
1412 if (pixm && pixGetDepth(pixm) != 1)
1413 return ERROR_INT("pixm not 1 bpp", __func__, 1);
1414 if (factor < 1)
1415 return ERROR_INT("sampling factor must be >= 1", __func__, 1);
1416 if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE &&
1417 type != L_STANDARD_DEVIATION && type != L_VARIANCE)
1418 return ERROR_INT("invalid measure type", __func__, 1);
1419 if (pixm) {
1420 pixZero(pixm, &empty);
1421 if (empty)
1422 return ERROR_INT("empty mask", __func__, 1);
1423 }
1424
1425 if (prval) {
1426 if (cmap)
1427 pixt = pixGetRGBComponentCmap(pixs, COLOR_RED);
1428 else
1429 pixt = pixGetRGBComponent(pixs, COLOR_RED);
1430 pixGetAverageMasked(pixt, pixm, x, y, factor, type, prval);
1431 pixDestroy(&pixt);
1432 }
1433 if (pgval) {
1434 if (cmap)
1435 pixt = pixGetRGBComponentCmap(pixs, COLOR_GREEN);
1436 else
1437 pixt = pixGetRGBComponent(pixs, COLOR_GREEN);
1438 pixGetAverageMasked(pixt, pixm, x, y, factor, type, pgval);
1439 pixDestroy(&pixt);
1440 }
1441 if (pbval) {
1442 if (cmap)
1443 pixt = pixGetRGBComponentCmap(pixs, COLOR_BLUE);
1444 else
1445 pixt = pixGetRGBComponent(pixs, COLOR_BLUE);
1446 pixGetAverageMasked(pixt, pixm, x, y, factor, type, pbval);
1447 pixDestroy(&pixt);
1448 }
1449
1450 return 0;
1451 }
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 {
1496 l_int32 i, j, w, h, d, wm, hm, wplg, wplm, val, count, empty;
1497 l_uint32 *datag, *datam, *lineg, *linem;
1498 l_float64 sumave, summs, ave, meansq, var;
1499 PIX *pixg;
1500
1501 if (!pval)
1502 return ERROR_INT("&val not defined", __func__, 1);
1503 *pval = 0.0;
1504 if (!pixs)
1505 return ERROR_INT("pixs not defined", __func__, 1);
1506 d = pixGetDepth(pixs);
1507 if (d != 8 && d != 16 && !pixGetColormap(pixs))
1508 return ERROR_INT("pixs not 8 or 16 bpp or colormapped", __func__, 1);
1509 if (pixm && pixGetDepth(pixm) != 1)
1510 return ERROR_INT("pixm not 1 bpp", __func__, 1);
1511 if (factor < 1)
1512 return ERROR_INT("sampling factor must be >= 1", __func__, 1);
1513 if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE &&
1514 type != L_STANDARD_DEVIATION && type != L_VARIANCE)
1515 return ERROR_INT("invalid measure type", __func__, 1);
1516 if (pixm) {
1517 pixZero(pixm, &empty);
1518 if (empty)
1519 return ERROR_INT("empty mask", __func__, 1);
1520 }
1521
1522 if (pixGetColormap(pixs))
1523 pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
1524 else
1525 pixg = pixClone(pixs);
1526 pixGetDimensions(pixg, &w, &h, &d);
1527 datag = pixGetData(pixg);
1528 wplg = pixGetWpl(pixg);
1529
1530 sumave = summs = 0.0;
1531 count = 0;
1532 if (!pixm) {
1533 for (i = 0; i < h; i += factor) {
1534 lineg = datag + i * wplg;
1535 for (j = 0; j < w; j += factor) {
1536 if (d == 8)
1537 val = GET_DATA_BYTE(lineg, j);
1538 else /* d == 16 */
1539 val = GET_DATA_TWO_BYTES(lineg, j);
1540 if (type != L_ROOT_MEAN_SQUARE)
1541 sumave += val;
1542 if (type != L_MEAN_ABSVAL)
1543 summs += (l_float64)(val) * val;
1544 count++;
1545 }
1546 }
1547 } else {
1548 pixGetDimensions(pixm, &wm, &hm, NULL);
1549 datam = pixGetData(pixm);
1550 wplm = pixGetWpl(pixm);
1551 for (i = 0; i < hm; i += factor) {
1552 if (y + i < 0 || y + i >= h) continue;
1553 lineg = datag + (y + i) * wplg;
1554 linem = datam + i * wplm;
1555 for (j = 0; j < wm; j += factor) {
1556 if (x + j < 0 || x + j >= w) continue;
1557 if (GET_DATA_BIT(linem, j)) {
1558 if (d == 8)
1559 val = GET_DATA_BYTE(lineg, x + j);
1560 else /* d == 16 */
1561 val = GET_DATA_TWO_BYTES(lineg, x + j);
1562 if (type != L_ROOT_MEAN_SQUARE)
1563 sumave += val;
1564 if (type != L_MEAN_ABSVAL)
1565 summs += (l_float64)(val) * val;
1566 count++;
1567 }
1568 }
1569 }
1570 }
1571
1572 pixDestroy(&pixg);
1573 if (count == 0)
1574 return ERROR_INT("no pixels sampled", __func__, 1);
1575 ave = sumave / (l_float64)count;
1576 meansq = summs / (l_float64)count;
1577 var = meansq - ave * ave;
1578 if (type == L_MEAN_ABSVAL)
1579 *pval = (l_float32)ave;
1580 else if (type == L_ROOT_MEAN_SQUARE)
1581 *pval = (l_float32)sqrt(meansq);
1582 else if (type == L_STANDARD_DEVIATION)
1583 *pval = (l_float32)sqrt(var);
1584 else /* type == L_VARIANCE */
1585 *pval = (l_float32)var;
1586
1587 return 0;
1588 }
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 {
1618 PIX *pixt;
1619 PIXCMAP *cmap;
1620
1621 if (ppixr) *ppixr = NULL;
1622 if (ppixg) *ppixg = NULL;
1623 if (ppixb) *ppixb = NULL;
1624 if (!ppixr && !ppixg && !ppixb)
1625 return ERROR_INT("no data requested", __func__, 1);
1626 if (!pixs)
1627 return ERROR_INT("pixs not defined", __func__, 1);
1628 cmap = pixGetColormap(pixs);
1629 if (pixGetDepth(pixs) != 32 && !cmap)
1630 return ERROR_INT("pixs neither 32 bpp nor colormapped", __func__, 1);
1631 if (sx < 2 || sy < 2)
1632 return ERROR_INT("sx and sy not both > 1", __func__, 1);
1633 if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE &&
1634 type != L_STANDARD_DEVIATION)
1635 return ERROR_INT("invalid measure type", __func__, 1);
1636
1637 if (ppixr) {
1638 if (cmap)
1639 pixt = pixGetRGBComponentCmap(pixs, COLOR_RED);
1640 else
1641 pixt = pixGetRGBComponent(pixs, COLOR_RED);
1642 *ppixr = pixGetAverageTiled(pixt, sx, sy, type);
1643 pixDestroy(&pixt);
1644 }
1645 if (ppixg) {
1646 if (cmap)
1647 pixt = pixGetRGBComponentCmap(pixs, COLOR_GREEN);
1648 else
1649 pixt = pixGetRGBComponent(pixs, COLOR_GREEN);
1650 *ppixg = pixGetAverageTiled(pixt, sx, sy, type);
1651 pixDestroy(&pixt);
1652 }
1653 if (ppixb) {
1654 if (cmap)
1655 pixt = pixGetRGBComponentCmap(pixs, COLOR_BLUE);
1656 else
1657 pixt = pixGetRGBComponent(pixs, COLOR_BLUE);
1658 *ppixb = pixGetAverageTiled(pixt, sx, sy, type);
1659 pixDestroy(&pixt);
1660 }
1661
1662 return 0;
1663 }
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 {
1690 l_int32 i, j, k, m, w, h, wd, hd, d, pos, wplt, wpld, valt;
1691 l_uint32 *datat, *datad, *linet, *lined, *startt;
1692 l_float64 sumave, summs, ave, meansq, normfact;
1693 PIX *pixt, *pixd;
1694
1695 if (!pixs)
1696 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1697 pixGetDimensions(pixs, &w, &h, &d);
1698 if (d != 8 && !pixGetColormap(pixs))
1699 return (PIX *)ERROR_PTR("pixs not 8 bpp or cmapped", __func__, NULL);
1700 if (sx < 2 || sy < 2)
1701 return (PIX *)ERROR_PTR("sx and sy not both > 1", __func__, NULL);
1702 wd = w / sx;
1703 hd = h / sy;
1704 if (wd < 1 || hd < 1)
1705 return (PIX *)ERROR_PTR("wd or hd == 0", __func__, NULL);
1706 if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE &&
1707 type != L_STANDARD_DEVIATION)
1708 return (PIX *)ERROR_PTR("invalid measure type", __func__, NULL);
1709
1710 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
1711 pixd = pixCreate(wd, hd, 8);
1712 datat = pixGetData(pixt);
1713 wplt = pixGetWpl(pixt);
1714 datad = pixGetData(pixd);
1715 wpld = pixGetWpl(pixd);
1716 normfact = 1. / (l_float64)(sx * sy);
1717 for (i = 0; i < hd; i++) {
1718 lined = datad + i * wpld;
1719 linet = datat + i * sy * wplt;
1720 for (j = 0; j < wd; j++) {
1721 if (type == L_MEAN_ABSVAL || type == L_STANDARD_DEVIATION) {
1722 sumave = 0.0;
1723 for (k = 0; k < sy; k++) {
1724 startt = linet + k * wplt;
1725 for (m = 0; m < sx; m++) {
1726 pos = j * sx + m;
1727 valt = GET_DATA_BYTE(startt, pos);
1728 sumave += valt;
1729 }
1730 }
1731 ave = normfact * sumave;
1732 }
1733 if (type == L_ROOT_MEAN_SQUARE || type == L_STANDARD_DEVIATION) {
1734 summs = 0.0;
1735 for (k = 0; k < sy; k++) {
1736 startt = linet + k * wplt;
1737 for (m = 0; m < sx; m++) {
1738 pos = j * sx + m;
1739 valt = GET_DATA_BYTE(startt, pos);
1740 summs += (l_float64)(valt) * valt;
1741 }
1742 }
1743 meansq = normfact * summs;
1744 }
1745 if (type == L_MEAN_ABSVAL)
1746 valt = (l_int32)(ave + 0.5);
1747 else if (type == L_ROOT_MEAN_SQUARE)
1748 valt = (l_int32)(sqrt(meansq) + 0.5);
1749 else /* type == L_STANDARD_DEVIATION */
1750 valt = (l_int32)(sqrt(meansq - ave * ave) + 0.5);
1751 SET_DATA_BYTE(lined, j, valt);
1752 }
1753 }
1754
1755 pixDestroy(&pixt);
1756 return pixd;
1757 }
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 {
1795 l_int32 i, j, k, w, h, val, wpls, sum, sumsq, target, max, modeval;
1796 l_int32 xstart, xend, ystart, yend, bw, bh;
1797 l_int32 *histo;
1798 l_uint32 *lines, *datas;
1799 l_float32 norm;
1800 l_float32 *famean, *fameansq, *favar, *farootvar;
1801 l_float32 *famedian, *famode, *famodecount;
1802
1803 if (pnamean) *pnamean = NULL;
1804 if (pnamedian) *pnamedian = NULL;
1805 if (pnamode) *pnamode = NULL;
1806 if (pnamodecount) *pnamodecount = NULL;
1807 if (pnavar) *pnavar = NULL;
1808 if (pnarootvar) *pnarootvar = NULL;
1809 if (!pixs || pixGetDepth(pixs) != 8)
1810 return ERROR_INT("pixs undefined or not 8 bpp", __func__, 1);
1811 famean = fameansq = favar = farootvar = NULL;
1812 famedian = famode = famodecount = NULL;
1813
1814 pixGetDimensions(pixs, &w, &h, NULL);
1815 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
1816 &bw, &bh) == 1)
1817 return ERROR_INT("invalid clipping box", __func__, 1);
1818
1819 /* We need the mean for variance and root variance */
1820 datas = pixGetData(pixs);
1821 wpls = pixGetWpl(pixs);
1822 if (pnamean || pnavar || pnarootvar) {
1823 norm = 1.f / (l_float32)bw;
1824 famean = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32));
1825 fameansq = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32));
1826 if (pnavar || pnarootvar) {
1827 favar = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32));
1828 if (pnarootvar)
1829 farootvar = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32));
1830 }
1831 for (i = ystart; i < yend; i++) {
1832 sum = sumsq = 0;
1833 lines = datas + i * wpls;
1834 for (j = xstart; j < xend; j++) {
1835 val = GET_DATA_BYTE(lines, j);
1836 sum += val;
1837 sumsq += val * val;
1838 }
1839 famean[i] = norm * sum;
1840 fameansq[i] = norm * sumsq;
1841 if (pnavar || pnarootvar) {
1842 favar[i] = fameansq[i] - famean[i] * famean[i];
1843 if (pnarootvar)
1844 farootvar[i] = sqrtf(favar[i]);
1845 }
1846 }
1847 LEPT_FREE(fameansq);
1848 if (pnamean)
1849 *pnamean = numaCreateFromFArray(famean, bh, L_INSERT);
1850 else
1851 LEPT_FREE(famean);
1852 if (pnavar)
1853 *pnavar = numaCreateFromFArray(favar, bh, L_INSERT);
1854 else
1855 LEPT_FREE(favar);
1856 if (pnarootvar)
1857 *pnarootvar = numaCreateFromFArray(farootvar, bh, L_INSERT);
1858 }
1859
1860 /* We need a histogram to find the median and/or mode values */
1861 if (pnamedian || pnamode || pnamodecount) {
1862 histo = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1863 if (pnamedian) {
1864 *pnamedian = numaMakeConstant(0, bh);
1865 famedian = numaGetFArray(*pnamedian, L_NOCOPY);
1866 }
1867 if (pnamode) {
1868 *pnamode = numaMakeConstant(0, bh);
1869 famode = numaGetFArray(*pnamode, L_NOCOPY);
1870 }
1871 if (pnamodecount) {
1872 *pnamodecount = numaMakeConstant(0, bh);
1873 famodecount = numaGetFArray(*pnamodecount, L_NOCOPY);
1874 }
1875 for (i = ystart; i < yend; i++) {
1876 lines = datas + i * wpls;
1877 memset(histo, 0, 1024);
1878 for (j = xstart; j < xend; j++) {
1879 val = GET_DATA_BYTE(lines, j);
1880 histo[val]++;
1881 }
1882
1883 if (pnamedian) {
1884 sum = 0;
1885 target = (bw + 1) / 2;
1886 for (k = 0; k < 256; k++) {
1887 sum += histo[k];
1888 if (sum >= target) {
1889 famedian[i] = k;
1890 break;
1891 }
1892 }
1893 }
1894
1895 if (pnamode || pnamodecount) {
1896 max = 0;
1897 modeval = 0;
1898 for (k = 0; k < 256; k++) {
1899 if (histo[k] > max) {
1900 max = histo[k];
1901 modeval = k;
1902 }
1903 }
1904 if (pnamode)
1905 famode[i] = modeval;
1906 if (pnamodecount)
1907 famodecount[i] = max;
1908 }
1909 }
1910 LEPT_FREE(histo);
1911 }
1912
1913 return 0;
1914 }
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 {
1953 l_int32 i, j, k, w, h, val, wpls, sum, sumsq, target, max, modeval;
1954 l_int32 xstart, xend, ystart, yend, bw, bh;
1955 l_int32 *histo;
1956 l_uint32 *lines, *datas;
1957 l_float32 norm;
1958 l_float32 *famean, *fameansq, *favar, *farootvar;
1959 l_float32 *famedian, *famode, *famodecount;
1960
1961 if (pnamean) *pnamean = NULL;
1962 if (pnamedian) *pnamedian = NULL;
1963 if (pnamode) *pnamode = NULL;
1964 if (pnamodecount) *pnamodecount = NULL;
1965 if (pnavar) *pnavar = NULL;
1966 if (pnarootvar) *pnarootvar = NULL;
1967 if (!pixs || pixGetDepth(pixs) != 8)
1968 return ERROR_INT("pixs undefined or not 8 bpp", __func__, 1);
1969 famean = fameansq = favar = farootvar = NULL;
1970 famedian = famode = famodecount = NULL;
1971
1972 pixGetDimensions(pixs, &w, &h, NULL);
1973 if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
1974 &bw, &bh) == 1)
1975 return ERROR_INT("invalid clipping box", __func__, 1);
1976
1977 /* We need the mean for variance and root variance */
1978 datas = pixGetData(pixs);
1979 wpls = pixGetWpl(pixs);
1980 if (pnamean || pnavar || pnarootvar) {
1981 norm = 1.f / (l_float32)bh;
1982 famean = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32));
1983 fameansq = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32));
1984 if (pnavar || pnarootvar) {
1985 favar = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32));
1986 if (pnarootvar)
1987 farootvar = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32));
1988 }
1989 for (j = xstart; j < xend; j++) {
1990 sum = sumsq = 0;
1991 for (i = ystart, lines = datas; i < yend; lines += wpls, i++) {
1992 val = GET_DATA_BYTE(lines, j);
1993 sum += val;
1994 sumsq += val * val;
1995 }
1996 famean[j] = norm * sum;
1997 fameansq[j] = norm * sumsq;
1998 if (pnavar || pnarootvar) {
1999 favar[j] = fameansq[j] - famean[j] * famean[j];
2000 if (pnarootvar)
2001 farootvar[j] = sqrtf(favar[j]);
2002 }
2003 }
2004 LEPT_FREE(fameansq);
2005 if (pnamean)
2006 *pnamean = numaCreateFromFArray(famean, bw, L_INSERT);
2007 else
2008 LEPT_FREE(famean);
2009 if (pnavar)
2010 *pnavar = numaCreateFromFArray(favar, bw, L_INSERT);
2011 else
2012 LEPT_FREE(favar);
2013 if (pnarootvar)
2014 *pnarootvar = numaCreateFromFArray(farootvar, bw, L_INSERT);
2015 }
2016
2017 /* We need a histogram to find the median and/or mode values */
2018 if (pnamedian || pnamode || pnamodecount) {
2019 histo = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
2020 if (pnamedian) {
2021 *pnamedian = numaMakeConstant(0, bw);
2022 famedian = numaGetFArray(*pnamedian, L_NOCOPY);
2023 }
2024 if (pnamode) {
2025 *pnamode = numaMakeConstant(0, bw);
2026 famode = numaGetFArray(*pnamode, L_NOCOPY);
2027 }
2028 if (pnamodecount) {
2029 *pnamodecount = numaMakeConstant(0, bw);
2030 famodecount = numaGetFArray(*pnamodecount, L_NOCOPY);
2031 }
2032 for (j = xstart; j < xend; j++) {
2033 memset(histo, 0, 1024);
2034 for (i = ystart, lines = datas; i < yend; lines += wpls, i++) {
2035 val = GET_DATA_BYTE(lines, j);
2036 histo[val]++;
2037 }
2038
2039 if (pnamedian) {
2040 sum = 0;
2041 target = (bh + 1) / 2;
2042 for (k = 0; k < 256; k++) {
2043 sum += histo[k];
2044 if (sum >= target) {
2045 famedian[j] = k;
2046 break;
2047 }
2048 }
2049 }
2050
2051 if (pnamode || pnamodecount) {
2052 max = 0;
2053 modeval = 0;
2054 for (k = 0; k < 256; k++) {
2055 if (histo[k] > max) {
2056 max = histo[k];
2057 modeval = k;
2058 }
2059 }
2060 if (pnamode)
2061 famode[j] = modeval;
2062 if (pnamodecount)
2063 famodecount[j] = max;
2064 }
2065 }
2066 LEPT_FREE(histo);
2067 }
2068
2069 return 0;
2070 }
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 {
2095 l_int32 d;
2096 PIXCMAP *cmap;
2097
2098 if (pminval) *pminval = 0;
2099 if (pmaxval) *pmaxval = 0;
2100 if (!pminval && !pmaxval)
2101 return ERROR_INT("no result requested", __func__, 1);
2102 if (!pixs)
2103 return ERROR_INT("pixs not defined", __func__, 1);
2104
2105 cmap = pixGetColormap(pixs);
2106 if (cmap)
2107 return pixcmapGetRangeValues(cmap, color, pminval, pmaxval,
2108 NULL, NULL);
2109
2110 if (factor < 1)
2111 return ERROR_INT("sampling factor must be >= 1", __func__, 1);
2112 d = pixGetDepth(pixs);
2113 if (d != 8 && d != 32)
2114 return ERROR_INT("pixs not 8 or 32 bpp", __func__, 1);
2115
2116 if (d == 8) {
2117 pixGetExtremeValue(pixs, factor, L_SELECT_MIN,
2118 NULL, NULL, NULL, pminval);
2119 pixGetExtremeValue(pixs, factor, L_SELECT_MAX,
2120 NULL, NULL, NULL, pmaxval);
2121 } else if (color == L_SELECT_RED) {
2122 pixGetExtremeValue(pixs, factor, L_SELECT_MIN,
2123 pminval, NULL, NULL, NULL);
2124 pixGetExtremeValue(pixs, factor, L_SELECT_MAX,
2125 pmaxval, NULL, NULL, NULL);
2126 } else if (color == L_SELECT_GREEN) {
2127 pixGetExtremeValue(pixs, factor, L_SELECT_MIN,
2128 NULL, pminval, NULL, NULL);
2129 pixGetExtremeValue(pixs, factor, L_SELECT_MAX,
2130 NULL, pmaxval, NULL, NULL);
2131 } else if (color == L_SELECT_BLUE) {
2132 pixGetExtremeValue(pixs, factor, L_SELECT_MIN,
2133 NULL, NULL, pminval, NULL);
2134 pixGetExtremeValue(pixs, factor, L_SELECT_MAX,
2135 NULL, NULL, pmaxval, NULL);
2136 } else {
2137 return ERROR_INT("invalid color", __func__, 1);
2138 }
2139
2140 return 0;
2141 }
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 {
2173 l_int32 i, j, w, h, d, wpl;
2174 l_int32 val, extval, rval, gval, bval, extrval, extgval, extbval;
2175 l_uint32 pixel;
2176 l_uint32 *data, *line;
2177 PIXCMAP *cmap;
2178
2179 if (prval) *prval = -1;
2180 if (pgval) *pgval = -1;
2181 if (pbval) *pbval = -1;
2182 if (pgrayval) *pgrayval = -1;
2183 if (!pixs)
2184 return ERROR_INT("pixs not defined", __func__, 1);
2185 if (type != L_SELECT_MIN && type != L_SELECT_MAX)
2186 return ERROR_INT("invalid type", __func__, 1);
2187
2188 cmap = pixGetColormap(pixs);
2189 if (cmap) {
2190 if (type == L_SELECT_MIN) {
2191 if (prval) pixcmapGetRangeValues(cmap, L_SELECT_RED, prval, NULL,
2192 NULL, NULL);
2193 if (pgval) pixcmapGetRangeValues(cmap, L_SELECT_GREEN, pgval, NULL,
2194 NULL, NULL);
2195 if (pbval) pixcmapGetRangeValues(cmap, L_SELECT_BLUE, pbval, NULL,
2196 NULL, NULL);
2197 } else { /* type == L_SELECT_MAX */
2198 if (prval) pixcmapGetRangeValues(cmap, L_SELECT_RED, NULL, prval,
2199 NULL, NULL);
2200 if (pgval) pixcmapGetRangeValues(cmap, L_SELECT_GREEN, NULL, pgval,
2201 NULL, NULL);
2202 if (pbval) pixcmapGetRangeValues(cmap, L_SELECT_BLUE, NULL, pbval,
2203 NULL, NULL);
2204 }
2205 return 0;
2206 }
2207
2208 pixGetDimensions(pixs, &w, &h, &d);
2209 if (factor < 1)
2210 return ERROR_INT("sampling factor must be >= 1", __func__, 1);
2211 if (d != 8 && d != 32)
2212 return ERROR_INT("pixs not 8 or 32 bpp", __func__, 1);
2213 if (d == 8 && !pgrayval)
2214 return ERROR_INT("can't return result in grayval", __func__, 1);
2215 if (d == 32 && !prval && !pgval && !pbval)
2216 return ERROR_INT("can't return result in r/g/b-val", __func__, 1);
2217
2218 data = pixGetData(pixs);
2219 wpl = pixGetWpl(pixs);
2220 if (d == 8) {
2221 if (type == L_SELECT_MIN)
2222 extval = 100000;
2223 else /* get max */
2224 extval = -1;
2225
2226 for (i = 0; i < h; i += factor) {
2227 line = data + i * wpl;
2228 for (j = 0; j < w; j += factor) {
2229 val = GET_DATA_BYTE(line, j);
2230 if ((type == L_SELECT_MIN && val < extval) ||
2231 (type == L_SELECT_MAX && val > extval))
2232 extval = val;
2233 }
2234 }
2235 *pgrayval = extval;
2236 return 0;
2237 }
2238
2239 /* 32 bpp rgb */
2240 if (type == L_SELECT_MIN) {
2241 extrval = 100000;
2242 extgval = 100000;
2243 extbval = 100000;
2244 } else {
2245 extrval = -1;
2246 extgval = -1;
2247 extbval = -1;
2248 }
2249 for (i = 0; i < h; i += factor) {
2250 line = data + i * wpl;
2251 for (j = 0; j < w; j += factor) {
2252 pixel = line[j];
2253 if (prval) {
2254 rval = (pixel >> L_RED_SHIFT) & 0xff;
2255 if ((type == L_SELECT_MIN && rval < extrval) ||
2256 (type == L_SELECT_MAX && rval > extrval))
2257 extrval = rval;
2258 }
2259 if (pgval) {
2260 gval = (pixel >> L_GREEN_SHIFT) & 0xff;
2261 if ((type == L_SELECT_MIN && gval < extgval) ||
2262 (type == L_SELECT_MAX && gval > extgval))
2263 extgval = gval;
2264 }
2265 if (pbval) {
2266 bval = (pixel >> L_BLUE_SHIFT) & 0xff;
2267 if ((type == L_SELECT_MIN && bval < extbval) ||
2268 (type == L_SELECT_MAX && bval > extbval))
2269 extbval = bval;
2270 }
2271 }
2272 }
2273 if (prval) *prval = extrval;
2274 if (pgval) *pgval = extgval;
2275 if (pbval) *pbval = extbval;
2276 return 0;
2277 }
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 {
2306 l_int32 i, j, w, h, d, wpl, bw, bh;
2307 l_int32 xstart, ystart, xend, yend, xmax, ymax;
2308 l_uint32 val, maxval;
2309 l_uint32 *data, *line;
2310
2311 if (pmaxval) *pmaxval = 0;
2312 if (pxmax) *pxmax = 0;
2313 if (pymax) *pymax = 0;
2314 if (!pmaxval && !pxmax && !pymax)
2315 return ERROR_INT("no data requested", __func__, 1);
2316 if (!pixs)
2317 return ERROR_INT("pixs not defined", __func__, 1);
2318 if (pixGetColormap(pixs) != NULL)
2319 return ERROR_INT("pixs has colormap", __func__, 1);
2320 pixGetDimensions(pixs, &w, &h, &d);
2321 if (d != 8 && d != 16 && d != 32)
2322 return ERROR_INT("pixs not 8, 16 or 32 bpp", __func__, 1);
2323
2324 xstart = ystart = 0;
2325 xend = w - 1;
2326 yend = h - 1;
2327 if (box) {
2328 boxGetGeometry(box, &xstart, &ystart, &bw, &bh);
2329 xend = xstart + bw - 1;
2330 yend = ystart + bh - 1;
2331 }
2332
2333 data = pixGetData(pixs);
2334 wpl = pixGetWpl(pixs);
2335 maxval = 0;
2336 xmax = ymax = 0;
2337 for (i = ystart; i <= yend; i++) {
2338 line = data + i * wpl;
2339 for (j = xstart; j <= xend; j++) {
2340 if (d == 8)
2341 val = GET_DATA_BYTE(line, j);
2342 else if (d == 16)
2343 val = GET_DATA_TWO_BYTES(line, j);
2344 else /* d == 32 */
2345 val = line[j];
2346 if (val > maxval) {
2347 maxval = val;
2348 xmax = j;
2349 ymax = i;
2350 }
2351 }
2352 }
2353 if (maxval == 0) { /* no counts; pick the center of the rectangle */
2354 xmax = (xstart + xend) / 2;
2355 ymax = (ystart + yend) / 2;
2356 }
2357
2358 if (pmaxval) *pmaxval = maxval;
2359 if (pxmax) *pxmax = xmax;
2360 if (pymax) *pymax = ymax;
2361 return 0;
2362 }
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 {
2376 l_int32 i, j, w, h, d, wpl, val, max, maxval, empty;
2377 l_uint32 *data, *line;
2378
2379 if (!pmaxindex)
2380 return ERROR_INT("&maxindex not defined", __func__, 1);
2381 *pmaxindex = 0;
2382 if (!pixs)
2383 return ERROR_INT("pixs not defined", __func__, 1);
2384 pixGetDimensions(pixs, &w, &h, &d);
2385 if (d != 1 && d != 2 && d != 4 && d != 8)
2386 return ERROR_INT("invalid pixs depth; not in (1,2,4,8}", __func__, 1);
2387
2388 wpl = pixGetWpl(pixs);
2389 data = pixGetData(pixs);
2390 max = 0;
2391 maxval = (1 << d) - 1;
2392 if (d == 1) {
2393 pixZero(pixs, &empty);
2394 if (!empty) max = 1;
2395 *pmaxindex = max;
2396 return 0;
2397 }
2398 for (i = 0; i < h; i++) {
2399 line = data + i * wpl;
2400 if (d == 2) {
2401 for (j = 0; j < w; j++) {
2402 val = GET_DATA_DIBIT(line, j);
2403 if (val > max) max = val;
2404 }
2405 } else if (d == 4) {
2406 for (j = 0; j < w; j++) {
2407 val = GET_DATA_QBIT(line, j);
2408 if (val > max) max = val;
2409 }
2410 } else if (d == 8) {
2411 for (j = 0; j < w; j++) {
2412 val = GET_DATA_BYTE(line, j);
2413 if (val > max) max = val;
2414 }
2415 }
2416 if (max == maxval) break;
2417 }
2418 *pmaxindex = max;
2419 return 0;
2420 }
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 {
2454 l_int32 i, minval, maxval, rval, gval, bval;
2455 l_uint32 *carray;
2456 PIX *pixt;
2457
2458 if (pminval) *pminval = 0;
2459 if (pmaxval) *pmaxval = 0;
2460 if (pcarray) *pcarray = NULL;
2461 if (!pminval && !pmaxval)
2462 return ERROR_INT("no result requested", __func__, 1);
2463 if (!pixs || pixGetDepth(pixs) != 32)
2464 return ERROR_INT("pixs not defined or not 32 bpp", __func__, 1);
2465 if (factor < 1)
2466 return ERROR_INT("sampling factor must be >= 1", __func__, 1);
2467 if (color != L_SELECT_RED && color != L_SELECT_GREEN &&
2468 color != L_SELECT_BLUE)
2469 return ERROR_INT("invalid color", __func__, 1);
2470 if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2)
2471 return ERROR_INT("invalid fontsize", __func__, 1);
2472
2473 pixGetRankColorArray(pixs, nbins, color, factor, &carray, NULL, 0);
2474 if (!carray)
2475 return ERROR_INT("carray not made", __func__, 1);
2476
2477 if (fontsize > 0) {
2478 for (i = 0; i < nbins; i++)
2479 L_INFO("c[%d] = %x\n", __func__, i, carray[i]);
2480 pixt = pixDisplayColorArray(carray, nbins, 200, 5, fontsize);
2481 pixDisplay(pixt, 100, 100);
2482 pixDestroy(&pixt);
2483 }
2484
2485 extractRGBValues(carray[0], &rval, &gval, &bval);
2486 minval = rval;
2487 if (color == L_SELECT_GREEN)
2488 minval = gval;
2489 else if (color == L_SELECT_BLUE)
2490 minval = bval;
2491 extractRGBValues(carray[nbins - 1], &rval, &gval, &bval);
2492 maxval = rval;
2493 if (color == L_SELECT_GREEN)
2494 maxval = gval;
2495 else if (color == L_SELECT_BLUE)
2496 maxval = bval;
2497
2498 if (pminval) *pminval = minval;
2499 if (pmaxval) *pmaxval = maxval;
2500 if (pcarray)
2501 *pcarray = carray;
2502 else
2503 LEPT_FREE(carray);
2504 return 0;
2505 }
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 {
2550 l_int32 ret, w, h, samplesperbin;
2551 l_uint32 *array;
2552 PIX *pix1, *pixc, *pixg, *pixd;
2553 PIXCMAP *cmap;
2554
2555 if (!pcarray)
2556 return ERROR_INT("&carray not defined", __func__, 1);
2557 *pcarray = NULL;
2558 if (factor < 1)
2559 return ERROR_INT("sampling factor must be >= 1", __func__, 1);
2560 if (nbins < 2)
2561 return ERROR_INT("nbins must be at least 2", __func__, 1);
2562 if (!pixs)
2563 return ERROR_INT("pixs not defined", __func__, 1);
2564 cmap = pixGetColormap(pixs);
2565 if (pixGetDepth(pixs) != 32 && !cmap)
2566 return ERROR_INT("pixs neither 32 bpp nor cmapped", __func__, 1);
2567 if (type != L_SELECT_RED && type != L_SELECT_GREEN &&
2568 type != L_SELECT_BLUE && type != L_SELECT_MIN &&
2569 type != L_SELECT_MAX && type != L_SELECT_AVERAGE &&
2570 type != L_SELECT_HUE && type != L_SELECT_SATURATION)
2571 return ERROR_INT("invalid type", __func__, 1);
2572 if (pixadb) {
2573 if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) {
2574 L_WARNING("invalid fontsize %d; setting to 6\n", __func__,
2575 fontsize);
2576 fontsize = 6;
2577 }
2578 }
2579 pixGetDimensions(pixs, &w, &h, NULL);
2580 samplesperbin = (w * h) / (factor * factor * nbins);
2581 if (samplesperbin < 10) {
2582 L_ERROR("samplesperbin = %d < 10\n", __func__, samplesperbin);
2583 return 1;
2584 }
2585
2586 /* Downscale by factor and remove colormap if it exists */
2587 pix1 = pixScaleByIntSampling(pixs, factor);
2588 if (cmap)
2589 pixc = pixRemoveColormap(pix1, REMOVE_CMAP_TO_FULL_COLOR);
2590 else
2591 pixc = pixClone(pix1);
2592 pixDestroy(&pix1);
2593
2594 /* Convert to an 8 bit version for ordering the pixels */
2595 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 pixGetBinnedColor(pixc, pixg, 1, nbins, pcarray, pixadb);
2600 ret = 0;
2601 if ((array = *pcarray) == NULL) {
2602 L_ERROR("color array not returned\n", __func__);
2603 ret = 1;
2604 }
2605 if (array && pixadb) {
2606 pixd = pixDisplayColorArray(array, nbins, 200, 5, fontsize);
2607 pixWriteDebug("/tmp/lept/regout/rankhisto.png", pixd, IFF_PNG);
2608 pixDestroy(&pixd);
2609 }
2610
2611 pixDestroy(&pixc);
2612 pixDestroy(&pixg);
2613 return ret;
2614 }
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 {
2652 l_int32 i, j, w, h, wpls, wplg;
2653 l_int32 count, bincount, binindex, binsize, npts, avepts, ntot;
2654 l_int32 rval, gval, bval, grayval, rave, gave, bave;
2655 l_uint32 *datas, *datag, *lines, *lineg, *carray;
2656 l_float64 val64, rsum, gsum, bsum;
2657 L_DNAA *daa;
2658 NUMA *naeach;
2659 PIX *pix1;
2660
2661 if (!pcarray)
2662 return ERROR_INT("&carray not defined", __func__, 1);
2663 *pcarray = NULL;
2664 if (!pixs || pixGetDepth(pixs) != 32)
2665 return ERROR_INT("pixs undefined or not 32 bpp", __func__, 1);
2666 if (!pixg || pixGetDepth(pixg) != 8)
2667 return ERROR_INT("pixg undefined or not 8 bpp", __func__, 1);
2668 if (factor < 1) {
2669 L_WARNING("sampling factor less than 1; setting to 1\n", __func__);
2670 factor = 1;
2671 }
2672 if (nbins < 1 || nbins > 100)
2673 return ERROR_INT("nbins not in [1,100]", __func__, 1);
2674
2675 /* Require that each bin has at least 5 pixels. */
2676 pixGetDimensions(pixs, &w, &h, NULL);
2677 npts = (w + factor - 1) * (h + factor - 1) / (factor * factor);
2678 avepts = (npts + nbins - 1) / nbins; /* average number of pts in a bin */
2679 if (avepts < 5) {
2680 L_ERROR("avepts = %d; must be >= 5\n", __func__, avepts);
2681 return 1;
2682 }
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 datas = pixGetData(pixs);
2693 wpls = pixGetWpl(pixs);
2694 datag = pixGetData(pixg);
2695 wplg = pixGetWpl(pixg);
2696 daa = l_dnaaCreateFull(256, 0);
2697 for (i = 0; i < h; i += factor) {
2698 lines = datas + i * wpls;
2699 lineg = datag + i * wplg;
2700 for (j = 0; j < w; j += factor) {
2701 grayval = GET_DATA_BYTE(lineg, j);
2702 l_dnaaAddNumber(daa, grayval, lines[j]);
2703 }
2704 }
2705
2706 if (pixadb) {
2707 NUMA *na, *nabinval, *narank;
2708 na = numaCreate(256); /* grayscale histogram */
2709 for (i = 0; i < 256; i++)
2710 numaAddNumber(na, l_dnaaGetDnaCount(daa, i));
2711
2712 /* Plot the gray bin value and the rank(gray) values */
2713 numaDiscretizeHistoInBins(na, nbins, &nabinval, &narank);
2714 pix1 = gplotSimplePix1(nabinval, "Gray value in each bin");
2715 pixaAddPix(pixadb, pix1, L_INSERT);
2716 pix1 = gplotSimplePix1(narank, "rank as function of gray value");
2717 pixaAddPix(pixadb, pix1, L_INSERT);
2718 numaDestroy(&na);
2719 numaDestroy(&nabinval);
2720 numaDestroy(&narank);
2721 }
2722
2723 /* Get the number of items in each bin */
2724 ntot = l_dnaaGetNumberCount(daa);
2725 if ((naeach = numaGetUniformBinSizes(ntot, nbins)) == NULL) {
2726 l_dnaaDestroy(&daa);
2727 return ERROR_INT("naeach not made", __func__, 1);
2728 }
2729
2730 /* Get the average color in each bin. This algorithm is
2731 * esssentially the same as in numaDiscretizeHistoInBins() */
2732 carray = (l_uint32 *)LEPT_CALLOC(nbins, sizeof(l_uint32));
2733 rsum = gsum = bsum = 0.0;
2734 bincount = 0;
2735 binindex = 0;
2736 numaGetIValue(naeach, 0, &binsize);
2737 for (i = 0; i < 256; i++) {
2738 count = l_dnaaGetDnaCount(daa, i);
2739 for (j = 0; j < count; j++) {
2740 bincount++;
2741 l_dnaaGetValue(daa, i, j, &val64);
2742 extractRGBValues((l_uint32)val64, &rval, &gval, &bval);
2743 rsum += rval;
2744 gsum += gval;
2745 bsum += bval;
2746 if (bincount == binsize) { /* add bin entry */
2747 rave = (l_int32)(rsum / binsize + 0.5);
2748 gave = (l_int32)(gsum / binsize + 0.5);
2749 bave = (l_int32)(bsum / binsize + 0.5);
2750 composeRGBPixel(rave, gave, bave, carray + binindex);
2751 rsum = gsum = bsum = 0.0;
2752 bincount = 0;
2753 binindex++;
2754 if (binindex == nbins) break;
2755 numaGetIValue(naeach, binindex, &binsize);
2756 }
2757 }
2758 if (binindex == nbins) break;
2759 }
2760 if (binindex != nbins)
2761 L_ERROR("binindex = %d != nbins = %d\n", __func__, binindex, nbins);
2762
2763 if (pixadb) {
2764 NUMA *nared, *nagreen, *nablue;
2765 nared = numaCreate(nbins);
2766 nagreen = numaCreate(nbins);
2767 nablue = numaCreate(nbins);
2768 for (i = 0; i < nbins; i++) {
2769 extractRGBValues(carray[i], &rval, &gval, &bval);
2770 numaAddNumber(nared, rval);
2771 numaAddNumber(nagreen, gval);
2772 numaAddNumber(nablue, bval);
2773 }
2774 lept_mkdir("lept/regout");
2775 pix1 = gplotSimplePix1(nared, "Average red val vs. rank bin");
2776 pixaAddPix(pixadb, pix1, L_INSERT);
2777 pix1 = gplotSimplePix1(nagreen, "Average green val vs. rank bin");
2778 pixaAddPix(pixadb, pix1, L_INSERT);
2779 pix1 = gplotSimplePix1(nablue, "Average blue val vs. rank bin");
2780 pixaAddPix(pixadb, pix1, L_INSERT);
2781 numaDestroy(&nared);
2782 numaDestroy(&nagreen);
2783 numaDestroy(&nablue);
2784 }
2785
2786 *pcarray = carray;
2787 numaDestroy(&naeach);
2788 l_dnaaDestroy(&daa);
2789 return 0;
2790 }
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 {
2819 char textstr[256];
2820 l_int32 i, rval, gval, bval;
2821 L_BMF *bmf;
2822 PIX *pix1, *pix2, *pix3, *pix4;
2823 PIXA *pixa;
2824
2825 if (!carray)
2826 return (PIX *)ERROR_PTR("carray not defined", __func__, NULL);
2827 if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2)
2828 return (PIX *)ERROR_PTR("invalid fontsize", __func__, NULL);
2829
2830 bmf = (fontsize == 0) ? NULL : bmfCreate(NULL, fontsize);
2831 pixa = pixaCreate(ncolors);
2832 for (i = 0; i < ncolors; i++) {
2833 pix1 = pixCreate(side, side, 32);
2834 pixSetAllArbitrary(pix1, carray[i]);
2835 pix2 = pixAddBorder(pix1, 2, 1);
2836 if (bmf) {
2837 extractRGBValues(carray[i], &rval, &gval, &bval);
2838 snprintf(textstr, sizeof(textstr),
2839 "%d: (%d %d %d)", i, rval, gval, bval);
2840 pix3 = pixAddSingleTextblock(pix2, bmf, textstr, 0xff000000,
2841 L_ADD_BELOW, NULL);
2842 } else {
2843 pix3 = pixClone(pix2);
2844 }
2845 pixaAddPix(pixa, pix3, L_INSERT);
2846 pixDestroy(&pix1);
2847 pixDestroy(&pix2);
2848 }
2849 pix4 = pixaDisplayTiledInColumns(pixa, ncols, 1.0, 20, 2);
2850 pixaDestroy(&pixa);
2851 bmfDestroy(&bmf);
2852 return pix4;
2853 }
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 {
2893 l_int32 i, j, w, h, mindim, nstrips;
2894 l_uint32 *array;
2895 BOXA *boxa;
2896 PIX *pix1, *pix2, *pixd;
2897 PIXA *pixa;
2898 PIXCMAP *cmap;
2899
2900 if (!pixs)
2901 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2902 cmap = pixGetColormap(pixs);
2903 if (pixGetDepth(pixs) != 32 && !cmap)
2904 return (PIX *)ERROR_PTR("pixs neither 32 bpp nor cmapped",
2905 __func__, NULL);
2906 if (direction != L_SCAN_HORIZONTAL && direction != L_SCAN_VERTICAL)
2907 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL);
2908 if (size < 1)
2909 return (PIX *)ERROR_PTR("size < 1", __func__, NULL);
2910 if (nbins < 2)
2911 return (PIX *)ERROR_PTR("nbins must be at least 2", __func__, NULL);
2912 if (type != L_SELECT_RED && type != L_SELECT_GREEN &&
2913 type != L_SELECT_BLUE && type != L_SELECT_MIN &&
2914 type != L_SELECT_MAX && type != L_SELECT_AVERAGE &&
2915 type != L_SELECT_HUE && type != L_SELECT_SATURATION)
2916 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
2917 pixGetDimensions(pixs, &w, &h, NULL);
2918 mindim = L_MIN(w, h);
2919 if (mindim < 20 || nbins > mindim)
2920 return (PIX *)ERROR_PTR("pix too small and/or too many bins",
2921 __func__, NULL);
2922
2923 /* Remove colormap if it exists */
2924 if (cmap)
2925 pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
2926 else
2927 pix1 = pixClone(pixs);
2928 pixGetDimensions(pixs, &w, &h, NULL);
2929
2930 pixd = NULL;
2931 boxa = makeMosaicStrips(w, h, direction, size);
2932 pixa = pixClipRectangles(pix1, boxa);
2933 nstrips = pixaGetCount(pixa);
2934 if (direction == L_SCAN_HORIZONTAL) {
2935 pixd = pixCreate(nstrips, nbins, 32);
2936 for (i = 0; i < nstrips; i++) {
2937 pix2 = pixaGetPix(pixa, i, L_CLONE);
2938 pixGetRankColorArray(pix2, nbins, type, 1, &array, NULL, 0);
2939 if (array) {
2940 for (j = 0; j < nbins; j++)
2941 pixSetPixel(pixd, i, j, array[j]);
2942 LEPT_FREE(array);
2943 }
2944 pixDestroy(&pix2);
2945 }
2946 } else { /* L_SCAN_VERTICAL */
2947 pixd = pixCreate(nbins, nstrips, 32);
2948 for (i = 0; i < nstrips; i++) {
2949 pix2 = pixaGetPix(pixa, i, L_CLONE);
2950 pixGetRankColorArray(pix2, nbins, type, 1, &array, NULL, 0);
2951 if (array) {
2952 for (j = 0; j < nbins; j++)
2953 pixSetPixel(pixd, j, i, array[j]);
2954 LEPT_FREE(array);
2955 }
2956 pixDestroy(&pix2);
2957 }
2958 }
2959 pixDestroy(&pix1);
2960 boxaDestroy(&boxa);
2961 pixaDestroy(&pixa);
2962 return pixd;
2963 }
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 {
2997 l_int32 j, n, w, h, d;
2998 l_float32 *colvect;
2999 PIX *pixt, *pixd;
3000
3001 if (!pixa)
3002 return (PIX *)ERROR_PTR("pixa not defined", __func__, NULL);
3003 if (type != L_MEAN_ABSVAL && type != L_MEDIAN_VAL &&
3004 type != L_MODE_VAL && type != L_MODE_COUNT)
3005 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
3006 n = pixaGetCount(pixa);
3007 if (n == 0)
3008 return (PIX *)ERROR_PTR("no pix in pixa", __func__, NULL);
3009 pixaGetPixDimensions(pixa, 0, &w, &h, &d);
3010 if (d != 8)
3011 return (PIX *)ERROR_PTR("pix not 8 bpp", __func__, NULL);
3012
3013 pixd = pixCreate(w, h, 8);
3014 pixt = pixCreate(n, h, 8);
3015 colvect = (l_float32 *)LEPT_CALLOC(h, sizeof(l_float32));
3016 for (j = 0; j < w; j++) {
3017 pixaExtractColumnFromEachPix(pixa, j, pixt);
3018 pixGetRowStats(pixt, type, nbins, thresh, colvect);
3019 pixSetPixelColumn(pixd, j, colvect);
3020 }
3021
3022 LEPT_FREE(colvect);
3023 pixDestroy(&pixt);
3024 return pixd;
3025 }
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 {
3041 l_int32 i, k, n, w, h, ht, val, wplt, wpld;
3042 l_uint32 *datad, *datat;
3043 PIX *pixt;
3044
3045 if (!pixa)
3046 return ERROR_INT("pixa not defined", __func__, 1);
3047 if (!pixd || pixGetDepth(pixd) != 8)
3048 return ERROR_INT("pixd not defined or not 8 bpp", __func__, 1);
3049 n = pixaGetCount(pixa);
3050 pixGetDimensions(pixd, &w, &h, NULL);
3051 if (n != w)
3052 return ERROR_INT("pix width != n", __func__, 1);
3053 pixt = pixaGetPix(pixa, 0, L_CLONE);
3054 wplt = pixGetWpl(pixt);
3055 pixGetDimensions(pixt, NULL, &ht, NULL);
3056 pixDestroy(&pixt);
3057 if (h != ht)
3058 return ERROR_INT("pixd height != column height", __func__, 1);
3059
3060 datad = pixGetData(pixd);
3061 wpld = pixGetWpl(pixd);
3062 for (k = 0; k < n; k++) {
3063 pixt = pixaGetPix(pixa, k, L_CLONE);
3064 datat = pixGetData(pixt);
3065 for (i = 0; i < h; i++) {
3066 val = GET_DATA_BYTE(datat, col);
3067 SET_DATA_BYTE(datad + i * wpld, k, val);
3068 datat += wplt;
3069 }
3070 pixDestroy(&pixt);
3071 }
3072
3073 return 0;
3074 }
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 {
3116 l_int32 i, j, k, w, h, val, wpls, sum, target, max, modeval;
3117 l_int32 *histo, *gray2bin, *bin2gray;
3118 l_uint32 *lines, *datas;
3119
3120 if (!pixs || pixGetDepth(pixs) != 8)
3121 return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
3122 if (!colvect)
3123 return ERROR_INT("colvect not defined", __func__, 1);
3124 if (type != L_MEAN_ABSVAL && type != L_MEDIAN_VAL &&
3125 type != L_MODE_VAL && type != L_MODE_COUNT)
3126 return ERROR_INT("invalid type", __func__, 1);
3127 if (type != L_MEAN_ABSVAL && (nbins < 1 || nbins > 256))
3128 return ERROR_INT("invalid nbins", __func__, 1);
3129 pixGetDimensions(pixs, &w, &h, NULL);
3130
3131 datas = pixGetData(pixs);
3132 wpls = pixGetWpl(pixs);
3133 if (type == L_MEAN_ABSVAL) {
3134 for (i = 0; i < h; i++) {
3135 sum = 0;
3136 lines = datas + i * wpls;
3137 for (j = 0; j < w; j++)
3138 sum += GET_DATA_BYTE(lines, j);
3139 colvect[i] = (l_float32)sum / (l_float32)w;
3140 }
3141 return 0;
3142 }
3143
3144 /* We need a histogram; binwidth ~ 256 / nbins */
3145 histo = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32));
3146 gray2bin = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
3147 bin2gray = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32));
3148 for (i = 0; i < 256; i++) /* gray value --> histo bin */
3149 gray2bin[i] = (i * nbins) / 256;
3150 for (i = 0; i < nbins; i++) /* histo bin --> gray value */
3151 bin2gray[i] = (i * 256 + 128) / nbins;
3152
3153 for (i = 0; i < h; i++) {
3154 lines = datas + i * wpls;
3155 for (k = 0; k < nbins; k++)
3156 histo[k] = 0;
3157 for (j = 0; j < w; j++) {
3158 val = GET_DATA_BYTE(lines, j);
3159 histo[gray2bin[val]]++;
3160 }
3161
3162 if (type == L_MEDIAN_VAL) {
3163 sum = 0;
3164 target = (w + 1) / 2;
3165 for (k = 0; k < nbins; k++) {
3166 sum += histo[k];
3167 if (sum >= target) {
3168 colvect[i] = bin2gray[k];
3169 break;
3170 }
3171 }
3172 } else if (type == L_MODE_VAL) {
3173 max = 0;
3174 modeval = 0;
3175 for (k = 0; k < nbins; k++) {
3176 if (histo[k] > max) {
3177 max = histo[k];
3178 modeval = k;
3179 }
3180 }
3181 if (max < thresh)
3182 colvect[i] = 0;
3183 else
3184 colvect[i] = bin2gray[modeval];
3185 } else { /* type == L_MODE_COUNT */
3186 max = 0;
3187 for (k = 0; k < nbins; k++) {
3188 if (histo[k] > max)
3189 max = histo[k];
3190 }
3191 colvect[i] = max;
3192 }
3193 }
3194
3195 LEPT_FREE(histo);
3196 LEPT_FREE(gray2bin);
3197 LEPT_FREE(bin2gray);
3198 return 0;
3199 }
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 {
3236 l_int32 i, j, k, w, h, val, wpls, sum, target, max, modeval;
3237 l_int32 *histo, *gray2bin, *bin2gray;
3238 l_uint32 *datas;
3239
3240 if (!pixs || pixGetDepth(pixs) != 8)
3241 return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
3242 if (!rowvect)
3243 return ERROR_INT("rowvect not defined", __func__, 1);
3244 if (type != L_MEAN_ABSVAL && type != L_MEDIAN_VAL &&
3245 type != L_MODE_VAL && type != L_MODE_COUNT)
3246 return ERROR_INT("invalid type", __func__, 1);
3247 if (type != L_MEAN_ABSVAL && (nbins < 1 || nbins > 256))
3248 return ERROR_INT("invalid nbins", __func__, 1);
3249 pixGetDimensions(pixs, &w, &h, NULL);
3250
3251 datas = pixGetData(pixs);
3252 wpls = pixGetWpl(pixs);
3253 if (type == L_MEAN_ABSVAL) {
3254 for (j = 0; j < w; j++) {
3255 sum = 0;
3256 for (i = 0; i < h; i++)
3257 sum += GET_DATA_BYTE(datas + i * wpls, j);
3258 rowvect[j] = (l_float32)sum / (l_float32)h;
3259 }
3260 return 0;
3261 }
3262
3263 /* We need a histogram; binwidth ~ 256 / nbins */
3264 histo = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32));
3265 gray2bin = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
3266 bin2gray = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32));
3267 for (i = 0; i < 256; i++) /* gray value --> histo bin */
3268 gray2bin[i] = (i * nbins) / 256;
3269 for (i = 0; i < nbins; i++) /* histo bin --> gray value */
3270 bin2gray[i] = (i * 256 + 128) / nbins;
3271
3272 for (j = 0; j < w; j++) {
3273 for (i = 0; i < h; i++) {
3274 val = GET_DATA_BYTE(datas + i * wpls, j);
3275 histo[gray2bin[val]]++;
3276 }
3277
3278 if (type == L_MEDIAN_VAL) {
3279 sum = 0;
3280 target = (h + 1) / 2;
3281 for (k = 0; k < nbins; k++) {
3282 sum += histo[k];
3283 if (sum >= target) {
3284 rowvect[j] = bin2gray[k];
3285 break;
3286 }
3287 }
3288 } else if (type == L_MODE_VAL) {
3289 max = 0;
3290 modeval = 0;
3291 for (k = 0; k < nbins; k++) {
3292 if (histo[k] > max) {
3293 max = histo[k];
3294 modeval = k;
3295 }
3296 }
3297 if (max < thresh)
3298 rowvect[j] = 0;
3299 else
3300 rowvect[j] = bin2gray[modeval];
3301 } else { /* type == L_MODE_COUNT */
3302 max = 0;
3303 for (k = 0; k < nbins; k++) {
3304 if (histo[k] > max)
3305 max = histo[k];
3306 }
3307 rowvect[j] = max;
3308 }
3309 for (k = 0; k < nbins; k++)
3310 histo[k] = 0;
3311 }
3312
3313 LEPT_FREE(histo);
3314 LEPT_FREE(gray2bin);
3315 LEPT_FREE(bin2gray);
3316 return 0;
3317 }
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 {
3333 l_int32 i, w, h, wpl;
3334 l_uint32 *data;
3335
3336 if (!pix || pixGetDepth(pix) != 8)
3337 return ERROR_INT("pix not defined or not 8 bpp", __func__, 1);
3338 if (!colvect)
3339 return ERROR_INT("colvect not defined", __func__, 1);
3340 pixGetDimensions(pix, &w, &h, NULL);
3341 if (col < 0 || col > w)
3342 return ERROR_INT("invalid col", __func__, 1);
3343
3344 data = pixGetData(pix);
3345 wpl = pixGetWpl(pix);
3346 for (i = 0; i < h; i++)
3347 SET_DATA_BYTE(data + i * wpl, col, (l_int32)colvect[i]);
3348
3349 return 0;
3350 }
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 {
3373 l_float32 fval;
3374 PIX *pixg, *pixm;
3375
3376 if (pfgval) *pfgval = 0;
3377 if (pbgval) *pbgval = 0;
3378 if (!pfgval && !pbgval)
3379 return ERROR_INT("no data requested", __func__, 1);
3380 if (!pixs)
3381 return ERROR_INT("pixs not defined", __func__, 1);
3382
3383 /* Generate a subsampled 8 bpp version and a mask over the fg */
3384 pixg = pixConvertTo8BySampling(pixs, factor, 0);
3385 pixm = pixThresholdToBinary(pixg, thresh);
3386
3387 if (pfgval) {
3388 pixGetAverageMasked(pixg, pixm, 0, 0, 1, L_MEAN_ABSVAL, &fval);
3389 *pfgval = (l_int32)(fval + 0.5);
3390 }
3391
3392 if (pbgval) {
3393 pixInvert(pixm, pixm);
3394 pixGetAverageMasked(pixg, pixm, 0, 0, 1, L_MEAN_ABSVAL, &fval);
3395 *pbgval = (l_int32)(fval + 0.5);
3396 }
3397
3398 pixDestroy(&pixg);
3399 pixDestroy(&pixm);
3400 return 0;
3401 }
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 {
3432 char buf[256];
3433 l_int32 thresh;
3434 l_float32 avefg, avebg, maxnum;
3435 GPLOT *gplot;
3436 NUMA *na, *nascore, *nax, *nay;
3437 PIX *pixg;
3438
3439 if (pthresh) *pthresh = 0;
3440 if (pfgval) *pfgval = 0;
3441 if (pbgval) *pbgval = 0;
3442 if (ppixdb) *ppixdb = NULL;
3443 if (!pthresh && !pfgval && !pbgval)
3444 return ERROR_INT("no data requested", __func__, 1);
3445 if (!pixs)
3446 return ERROR_INT("pixs not defined", __func__, 1);
3447
3448 /* Generate a subsampled 8 bpp version */
3449 pixg = pixConvertTo8BySampling(pixs, factor, 0);
3450
3451 /* Make the fg/bg estimates */
3452 na = pixGetGrayHistogram(pixg, 1);
3453 if (ppixdb) {
3454 numaSplitDistribution(na, scorefract, &thresh, &avefg, &avebg,
3455 NULL, NULL, &nascore);
3456 numaDestroy(&nascore);
3457 } else {
3458 numaSplitDistribution(na, scorefract, &thresh, &avefg, &avebg,
3459 NULL, NULL, NULL);
3460 }
3461
3462 if (pthresh) *pthresh = thresh;
3463 if (pfgval) *pfgval = (l_int32)(avefg + 0.5);
3464 if (pbgval) *pbgval = (l_int32)(avebg + 0.5);
3465
3466 if (ppixdb) {
3467 lept_mkdir("lept/redout");
3468 gplot = gplotCreate("/tmp/lept/redout/histplot", GPLOT_PNG, "Histogram",
3469 "Grayscale value", "Number of pixels");
3470 gplotAddPlot(gplot, NULL, na, GPLOT_LINES, NULL);
3471 nax = numaMakeConstant(thresh, 2);
3472 numaGetMax(na, &maxnum, NULL);
3473 nay = numaMakeConstant(0, 2);
3474 numaReplaceNumber(nay, 1, (l_int32)(0.5 * maxnum));
3475 snprintf(buf, sizeof(buf), "score fract = %3.1f", scorefract);
3476 gplotAddPlot(gplot, nax, nay, GPLOT_LINES, buf);
3477 *ppixdb = gplotMakeOutputPix(gplot);
3478 gplotDestroy(&gplot);
3479 numaDestroy(&nax);
3480 numaDestroy(&nay);
3481 }
3482
3483 pixDestroy(&pixg);
3484 numaDestroy(&na);
3485 return 0;
3486 }