Leptonica 1.85.0
Image processing and image analysis suite
Loading...
Searching...
No Matches
grayquant.c
Go to the documentation of this file.
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
100#ifdef HAVE_CONFIG_H
101#include <config_auto.h>
102#endif /* HAVE_CONFIG_H */
103
104#include <string.h>
105#include <math.h>
106#include "allheaders.h"
107
108static void ditherToBinaryLow(l_uint32 *datad, l_int32 w, l_int32 h,
109 l_int32 wpld, l_uint32 *datas, l_int32 wpls,
110 l_uint32 *bufs1, l_uint32 *bufs2,
111 l_int32 lowerclip, l_int32 upperclip);
112static void thresholdToBinaryLow(l_uint32 *datad, l_int32 w, l_int32 h,
113 l_int32 wpld, l_uint32 *datas, l_int32 d,
114 l_int32 wpls, l_int32 thresh);
115static void ditherTo2bppLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld,
116 l_uint32 *datas, l_int32 wpls, l_uint32 *bufs1,
117 l_uint32 *bufs2, l_int32 *tabval, l_int32 *tab38,
118 l_int32 *tab14);
119static void ditherTo2bppLineLow(l_uint32 *lined, l_int32 w, l_uint32 *bufs1,
120 l_uint32 *bufs2, l_int32 *tabval,
121 l_int32 *tab38, l_int32 *tab14,
122 l_int32 lastlineflag);
123static l_int32 make8To2DitherTables(l_int32 **ptabval, l_int32 **ptab38,
124 l_int32 **ptab14, l_int32 cliptoblack,
125 l_int32 cliptowhite);
126static void thresholdTo2bppLow(l_uint32 *datad, l_int32 h, l_int32 wpld,
127 l_uint32 *datas, l_int32 wpls, l_int32 *tab);
128static void thresholdTo4bppLow(l_uint32 *datad, l_int32 h, l_int32 wpld,
129 l_uint32 *datas, l_int32 wpls, l_int32 *tab);
130static l_int32 *makeGrayQuantTargetTable(l_int32 nlevels, l_int32 depth);
131static l_int32 makeGrayQuantColormapArb(PIX *pixs, l_int32 *tab,
132 l_int32 outdepth, PIXCMAP **pcmap);
133static l_int32 numaFillCmapFromHisto(NUMA *na, PIXCMAP *cmap,
134 l_float32 minfract, l_int32 maxsize,
135 l_int32 **plut);
136
137#ifndef NO_CONSOLE_IO
138#define DEBUG_UNROLLING 0
139#endif /* ~NO_CONSOLE_IO */
140
141/*------------------------------------------------------------------*
142 * Binarization by Floyd-Steinberg dithering *
143 *------------------------------------------------------------------*/
174PIX *
176{
177 if (!pixs)
178 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
179 if (pixGetDepth(pixs) != 8)
180 return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL);
181
184}
185
186
204PIX *
206 l_int32 lowerclip,
207 l_int32 upperclip)
208{
209l_int32 w, h, d, wplt, wpld;
210l_uint32 *datat, *datad;
211l_uint32 *bufs1, *bufs2;
212PIX *pixt, *pixd;
213
214 if (!pixs)
215 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
216 pixGetDimensions(pixs, &w, &h, &d);
217 if (d != 8)
218 return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL);
219 if (lowerclip < 0 || lowerclip > 255)
220 return (PIX *)ERROR_PTR("invalid value for lowerclip", __func__, NULL);
221 if (upperclip < 0 || upperclip > 255)
222 return (PIX *)ERROR_PTR("invalid value for upperclip", __func__, NULL);
223
224 if ((pixd = pixCreate(w, h, 1)) == NULL)
225 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
226 pixCopyResolution(pixd, pixs);
227 pixCopyInputFormat(pixd, pixs);
228 datad = pixGetData(pixd);
229 wpld = pixGetWpl(pixd);
230
231 /* Remove colormap if it exists */
232 if ((pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE)) == NULL) {
233 pixDestroy(&pixd);
234 return (PIX *)ERROR_PTR("pixt not made", __func__, NULL);
235 }
236 datat = pixGetData(pixt);
237 wplt = pixGetWpl(pixt);
238
239 /* Two line buffers, 1 for current line and 2 for next line */
240 bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32));
241 bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32));
242 if (!bufs1 || !bufs2) {
243 LEPT_FREE(bufs1);
244 LEPT_FREE(bufs2);
245 pixDestroy(&pixd);
246 pixDestroy(&pixt);
247 return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", __func__, NULL);
248 }
249
250 ditherToBinaryLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2,
251 lowerclip, upperclip);
252
253 LEPT_FREE(bufs1);
254 LEPT_FREE(bufs2);
255 pixDestroy(&pixt);
256 return pixd;
257}
258
259
265static void
266ditherToBinaryLow(l_uint32 *datad,
267 l_int32 w,
268 l_int32 h,
269 l_int32 wpld,
270 l_uint32 *datas,
271 l_int32 wpls,
272 l_uint32 *bufs1,
273 l_uint32 *bufs2,
274 l_int32 lowerclip,
275 l_int32 upperclip)
276{
277l_int32 i;
278l_uint32 *lined;
279
280 /* do all lines except last line */
281 memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */
282 for (i = 0; i < h - 1; i++) {
283 memcpy(bufs1, bufs2, 4 * wpls);
284 memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls);
285 lined = datad + i * wpld;
286 ditherToBinaryLineLow(lined, w, bufs1, bufs2, lowerclip, upperclip, 0);
287 }
288
289 /* do last line */
290 memcpy(bufs1, bufs2, 4 * wpls);
291 lined = datad + (h - 1) * wpld;
292 ditherToBinaryLineLow(lined, w, bufs1, bufs2, lowerclip, upperclip, 1);
293}
294
295
321void
322ditherToBinaryLineLow(l_uint32 *lined,
323 l_int32 w,
324 l_uint32 *bufs1,
325 l_uint32 *bufs2,
326 l_int32 lowerclip,
327 l_int32 upperclip,
328 l_int32 lastlineflag)
329{
330l_int32 j;
331l_int32 oval, eval;
332l_uint8 fval1, fval2, rval, bval, dval;
333
334 if (lastlineflag == 0) {
335 for (j = 0; j < w - 1; j++) {
336 oval = GET_DATA_BYTE(bufs1, j);
337 if (oval > 127) { /* binarize to OFF */
338 if ((eval = 255 - oval) > upperclip) {
339 /* subtract from neighbors */
340 fval1 = (3 * eval) / 8;
341 fval2 = eval / 4;
342 rval = GET_DATA_BYTE(bufs1, j + 1);
343 rval = L_MAX(0, rval - fval1);
344 SET_DATA_BYTE(bufs1, j + 1, rval);
345 bval = GET_DATA_BYTE(bufs2, j);
346 bval = L_MAX(0, bval - fval1);
347 SET_DATA_BYTE(bufs2, j, bval);
348 dval = GET_DATA_BYTE(bufs2, j + 1);
349 dval = L_MAX(0, dval - fval2);
350 SET_DATA_BYTE(bufs2, j + 1, dval);
351 }
352 } else { /* oval <= 127; binarize to ON */
353 SET_DATA_BIT(lined, j); /* ON pixel */
354 if (oval > lowerclip) {
355 /* add to neighbors */
356 fval1 = (3 * oval) / 8;
357 fval2 = oval / 4;
358 rval = GET_DATA_BYTE(bufs1, j + 1);
359 rval = L_MIN(255, rval + fval1);
360 SET_DATA_BYTE(bufs1, j + 1, rval);
361 bval = GET_DATA_BYTE(bufs2, j);
362 bval = L_MIN(255, bval + fval1);
363 SET_DATA_BYTE(bufs2, j, bval);
364 dval = GET_DATA_BYTE(bufs2, j + 1);
365 dval = L_MIN(255, dval + fval2);
366 SET_DATA_BYTE(bufs2, j + 1, dval);
367 }
368 }
369 }
370
371 /* do last column: j = w - 1 */
372 oval = GET_DATA_BYTE(bufs1, j);
373 if (oval > 127) { /* binarize to OFF */
374 if ((eval = 255 - oval) > upperclip) {
375 /* subtract from neighbors */
376 fval1 = (3 * eval) / 8;
377 bval = GET_DATA_BYTE(bufs2, j);
378 bval = L_MAX(0, bval - fval1);
379 SET_DATA_BYTE(bufs2, j, bval);
380 }
381 } else { /*oval <= 127; binarize to ON */
382 SET_DATA_BIT(lined, j); /* ON pixel */
383 if (oval > lowerclip) {
384 /* add to neighbors */
385 fval1 = (3 * oval) / 8;
386 bval = GET_DATA_BYTE(bufs2, j);
387 bval = L_MIN(255, bval + fval1);
388 SET_DATA_BYTE(bufs2, j, bval);
389 }
390 }
391 } else { /* lastlineflag == 1 */
392 for (j = 0; j < w - 1; j++) {
393 oval = GET_DATA_BYTE(bufs1, j);
394 if (oval > 127) { /* binarize to OFF */
395 if ((eval = 255 - oval) > upperclip) {
396 /* subtract from neighbors */
397 fval1 = (3 * eval) / 8;
398 rval = GET_DATA_BYTE(bufs1, j + 1);
399 rval = L_MAX(0, rval - fval1);
400 SET_DATA_BYTE(bufs1, j + 1, rval);
401 }
402 } else { /* oval <= 127; binarize to ON */
403 SET_DATA_BIT(lined, j); /* ON pixel */
404 if (oval > lowerclip) {
405 /* add to neighbors */
406 fval1 = (3 * oval) / 8;
407 rval = GET_DATA_BYTE(bufs1, j + 1);
408 rval = L_MIN(255, rval + fval1);
409 SET_DATA_BYTE(bufs1, j + 1, rval);
410 }
411 }
412 }
413
414 /* do last pixel: (i, j) = (h - 1, w - 1) */
415 oval = GET_DATA_BYTE(bufs1, j);
416 if (oval < 128)
417 SET_DATA_BIT(lined, j); /* ON pixel */
418 }
419}
420
421
422/*------------------------------------------------------------------*
423 * Simple (pixelwise) binarization with fixed threshold *
424 *------------------------------------------------------------------*/
442PIX *
444 l_int32 thresh)
445{
446l_int32 d, w, h, wplt, wpld;
447l_uint32 *datat, *datad;
448PIX *pixt, *pixd;
449
450 if (!pixs)
451 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
452 pixGetDimensions(pixs, &w, &h, &d);
453 if (d != 4 && d != 8)
454 return (PIX *)ERROR_PTR("pixs must be 4 or 8 bpp", __func__, NULL);
455 if (thresh < 0)
456 return (PIX *)ERROR_PTR("thresh must be non-negative", __func__, NULL);
457 if (d == 4 && thresh > 16)
458 return (PIX *)ERROR_PTR("4 bpp thresh not in {0-16}", __func__, NULL);
459 if (d == 8 && thresh > 256)
460 return (PIX *)ERROR_PTR("8 bpp thresh not in {0-256}", __func__, NULL);
461
462 if ((pixd = pixCreate(w, h, 1)) == NULL)
463 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
464 pixCopyResolution(pixd, pixs);
465 pixCopyInputFormat(pixd, pixs);
466 datad = pixGetData(pixd);
467 wpld = pixGetWpl(pixd);
468
469 /* Remove colormap if it exists. If there is a colormap,
470 * pixt will be 8 bpp regardless of the depth of pixs. */
471 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
472 datat = pixGetData(pixt);
473 wplt = pixGetWpl(pixt);
474 if (pixGetColormap(pixs) && d == 4) { /* promoted to 8 bpp */
475 d = 8;
476 thresh *= 16;
477 }
478
479 thresholdToBinaryLow(datad, w, h, wpld, datat, d, wplt, thresh);
480 pixDestroy(&pixt);
481 return pixd;
482}
483
484
491static void
492thresholdToBinaryLow(l_uint32 *datad,
493 l_int32 w,
494 l_int32 h,
495 l_int32 wpld,
496 l_uint32 *datas,
497 l_int32 d,
498 l_int32 wpls,
499 l_int32 thresh)
500{
501l_int32 i;
502l_uint32 *lines, *lined;
503
504 for (i = 0; i < h; i++) {
505 lines = datas + i * wpls;
506 lined = datad + i * wpld;
507 thresholdToBinaryLineLow(lined, w, lines, d, thresh);
508 }
509}
510
511
512/*
513 * thresholdToBinaryLineLow()
514 *
515 */
516void
517thresholdToBinaryLineLow(l_uint32 *lined,
518 l_int32 w,
519 l_uint32 *lines,
520 l_int32 d,
521 l_int32 thresh)
522{
523l_int32 j, k, gval, scount, dcount;
524l_uint32 sword, dword;
525
526 switch (d)
527 {
528 case 4:
529 /* Unrolled as 4 source words, 1 dest word */
530 for (j = 0, scount = 0, dcount = 0; j + 31 < w; j += 32) {
531 dword = 0;
532 for (k = 0; k < 4; k++) {
533 sword = lines[scount++];
534 dword <<= 8;
535 gval = (sword >> 28) & 0xf;
536 /* Trick used here and below: if gval < thresh then
537 * gval - thresh < 0, so its high-order bit is 1, and
538 * ((gval - thresh) >> 31) & 1 == 1; likewise, if
539 * gval >= thresh, then ((gval - thresh) >> 31) & 1 == 0
540 * Doing it this way avoids a random (and thus easily
541 * mispredicted) branch on each pixel. */
542 dword |= ((gval - thresh) >> 24) & 128;
543 gval = (sword >> 24) & 0xf;
544 dword |= ((gval - thresh) >> 25) & 64;
545 gval = (sword >> 20) & 0xf;
546 dword |= ((gval - thresh) >> 26) & 32;
547 gval = (sword >> 16) & 0xf;
548 dword |= ((gval - thresh) >> 27) & 16;
549 gval = (sword >> 12) & 0xf;
550 dword |= ((gval - thresh) >> 28) & 8;
551 gval = (sword >> 8) & 0xf;
552 dword |= ((gval - thresh) >> 29) & 4;
553 gval = (sword >> 4) & 0xf;
554 dword |= ((gval - thresh) >> 30) & 2;
555 gval = sword & 0xf;
556 dword |= ((gval - thresh) >> 31) & 1;
557 }
558 lined[dcount++] = dword;
559 }
560
561 if (j < w) {
562 dword = 0;
563 for (; j < w; j++) {
564 if ((j & 7) == 0) {
565 sword = lines[scount++];
566 }
567 gval = (sword >> 28) & 0xf;
568 sword <<= 4;
569 dword |= (((gval - thresh) >> 31) & 1) << (31 - (j & 31));
570 }
571 lined[dcount] = dword;
572 }
573#if DEBUG_UNROLLING
574#define CHECK_BIT(a, b, c) if (GET_DATA_BIT(a, b) != c) { \
575 lept_stderr("Error: mismatch at %d/%d(%d), %d vs %d\n", \
576 j, w, d, GET_DATA_BIT(a, b), c); }
577 for (j = 0; j < w; j++) {
578 gval = GET_DATA_QBIT(lines, j);
579 CHECK_BIT(lined, j, gval < thresh ? 1 : 0);
580 }
581#endif
582 break;
583 case 8:
584 /* Unrolled as 8 source words, 1 dest word */
585 for (j = 0, scount = 0, dcount = 0; j + 31 < w; j += 32) {
586 dword = 0;
587 for (k = 0; k < 8; k++) {
588 sword = lines[scount++];
589 dword <<= 4;
590 gval = (sword >> 24) & 0xff;
591 dword |= ((gval - thresh) >> 28) & 8;
592 gval = (sword >> 16) & 0xff;
593 dword |= ((gval - thresh) >> 29) & 4;
594 gval = (sword >> 8) & 0xff;
595 dword |= ((gval - thresh) >> 30) & 2;
596 gval = sword & 0xff;
597 dword |= ((gval - thresh) >> 31) & 1;
598 }
599 lined[dcount++] = dword;
600 }
601
602 if (j < w) {
603 dword = 0;
604 for (; j < w; j++) {
605 if ((j & 3) == 0) {
606 sword = lines[scount++];
607 }
608 gval = (sword >> 24) & 0xff;
609 sword <<= 8;
610 dword |= (l_uint64)(((gval - thresh) >> 31) & 1)
611 << (31 - (j & 31));
612 }
613 lined[dcount] = dword;
614 }
615#if DEBUG_UNROLLING
616 for (j = 0; j < w; j++) {
617 gval = GET_DATA_BYTE(lines, j);
618 CHECK_BIT(lined, j, gval < thresh ? 1 : 0);
619 }
620#undef CHECK_BIT
621#endif
622 break;
623 default:
624 L_ERROR("src depth not 4 or 8 bpp\n", __func__);
625 break;
626 }
627}
628
629
630/*------------------------------------------------------------------*
631 * Binarization with variable threshold *
632 *------------------------------------------------------------------*/
646PIX *
648 PIX *pixg)
649{
650l_int32 i, j, vals, valg, w, h, d, wpls, wplg, wpld;
651l_uint32 *datas, *datag, *datad, *lines, *lineg, *lined;
652PIX *pixd;
653
654 if (!pixs)
655 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
656 if (!pixg)
657 return (PIX *)ERROR_PTR("pixg not defined", __func__, NULL);
658 if (!pixSizesEqual(pixs, pixg))
659 return (PIX *)ERROR_PTR("pix sizes not equal", __func__, NULL);
660 pixGetDimensions(pixs, &w, &h, &d);
661 if (d != 8)
662 return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL);
663
664 pixd = pixCreate(w, h, 1);
665 pixCopyResolution(pixd, pixs);
666 pixCopyInputFormat(pixd, pixs);
667 datad = pixGetData(pixd);
668 wpld = pixGetWpl(pixd);
669 datas = pixGetData(pixs);
670 wpls = pixGetWpl(pixs);
671 datag = pixGetData(pixg);
672 wplg = pixGetWpl(pixg);
673 for (i = 0; i < h; i++) {
674 lines = datas + i * wpls;
675 lineg = datag + i * wplg;
676 lined = datad + i * wpld;
677 for (j = 0; j < w; j++) {
678 vals = GET_DATA_BYTE(lines, j);
679 valg = GET_DATA_BYTE(lineg, j);
680 if (vals < valg)
681 SET_DATA_BIT(lined, j);
682 }
683 }
684
685 return pixd;
686}
687
688
689/*------------------------------------------------------------------*
690 * Binarization by adaptive mapping *
691 *------------------------------------------------------------------*/
721PIX *
723 PIX *pixm,
724 l_float32 gamma)
725{
726 if (!pixs || pixGetDepth(pixs) != 8)
727 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL);
728
729 return pixAdaptThresholdToBinaryGen(pixs, pixm, gamma, 50, 170, 200);
730}
731
732
759PIX *
761 PIX *pixm,
762 l_float32 gamma,
763 l_int32 blackval,
764 l_int32 whiteval,
765 l_int32 thresh)
766{
767PIX *pix1, *pixd;
768
769 if (!pixs || pixGetDepth(pixs) != 8)
770 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL);
771
772 if ((pix1 = pixBackgroundNormSimple(pixs, pixm, NULL)) == NULL)
773 return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL);
774 pixGammaTRC(pix1, pix1, gamma, blackval, whiteval);
775 pixd = pixThresholdToBinary(pix1, thresh);
776 pixDestroy(&pix1);
777 return pixd;
778}
779
780
781/*--------------------------------------------------------------------*
782 * Generate a binary mask from pixels of particular value(s) *
783 *--------------------------------------------------------------------*/
803PIX *
805 l_int32 val,
806 l_int32 usecmap)
807{
808l_int32 i, j, w, h, d, wplg, wpld;
809l_uint32 *datag, *datad, *lineg, *lined;
810PIX *pixg, *pixd;
811
812 if (!pixs)
813 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
814 d = pixGetDepth(pixs);
815 if (d != 2 && d != 4 && d != 8)
816 return (PIX *)ERROR_PTR("not 2, 4 or 8 bpp", __func__, NULL);
817
818 if (!usecmap && pixGetColormap(pixs))
819 pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
820 else
821 pixg = pixClone(pixs);
822 pixGetDimensions(pixg, &w, &h, &d);
823 if (d == 8 && (val < 0 || val > 255)) {
824 pixDestroy(&pixg);
825 return (PIX *)ERROR_PTR("val out of 8 bpp range", __func__, NULL);
826 }
827 if (d == 4 && (val < 0 || val > 15)) {
828 pixDestroy(&pixg);
829 return (PIX *)ERROR_PTR("val out of 4 bpp range", __func__, NULL);
830 }
831 if (d == 2 && (val < 0 || val > 3)) {
832 pixDestroy(&pixg);
833 return (PIX *)ERROR_PTR("val out of 2 bpp range", __func__, NULL);
834 }
835
836 pixd = pixCreate(w, h, 1);
837 pixCopyResolution(pixd, pixg);
838 pixCopyInputFormat(pixd, pixs);
839 datag = pixGetData(pixg);
840 wplg = pixGetWpl(pixg);
841 datad = pixGetData(pixd);
842 wpld = pixGetWpl(pixd);
843 for (i = 0; i < h; i++) {
844 lineg = datag + i * wplg;
845 lined = datad + i * wpld;
846 for (j = 0; j < w; j++) {
847 if (d == 8) {
848 if (GET_DATA_BYTE(lineg, j) == val)
849 SET_DATA_BIT(lined, j);
850 } else if (d == 4) {
851 if (GET_DATA_QBIT(lineg, j) == val)
852 SET_DATA_BIT(lined, j);
853 } else { /* d == 2 */
854 if (GET_DATA_DIBIT(lineg, j) == val)
855 SET_DATA_BIT(lined, j);
856 }
857 }
858 }
859
860 pixDestroy(&pixg);
861 return pixd;
862}
863
864
892PIX *
894 l_int32 lower,
895 l_int32 upper,
896 l_int32 inband,
897 l_int32 usecmap)
898{
899l_int32 i, j, w, h, d, wplg, wpld, val;
900l_uint32 *datag, *datad, *lineg, *lined;
901PIX *pixg, *pixd;
902
903 if (!pixs)
904 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
905 d = pixGetDepth(pixs);
906 if (d != 2 && d != 4 && d != 8)
907 return (PIX *)ERROR_PTR("not 2, 4 or 8 bpp", __func__, NULL);
908 if (lower < 0 || lower > upper)
909 return (PIX *)ERROR_PTR("lower < 0 or lower > upper!", __func__, NULL);
910
911 if (!usecmap && pixGetColormap(pixs))
912 pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
913 else
914 pixg = pixClone(pixs);
915 pixGetDimensions(pixg, &w, &h, &d);
916 if (d == 8 && upper > 255) {
917 pixDestroy(&pixg);
918 return (PIX *)ERROR_PTR("d == 8 and upper > 255", __func__, NULL);
919 }
920 if (d == 4 && upper > 15) {
921 pixDestroy(&pixg);
922 return (PIX *)ERROR_PTR("d == 4 and upper > 15", __func__, NULL);
923 }
924 if (d == 2 && upper > 3) {
925 pixDestroy(&pixg);
926 return (PIX *)ERROR_PTR("d == 2 and upper > 3", __func__, NULL);
927 }
928
929 pixd = pixCreate(w, h, 1);
930 pixCopyResolution(pixd, pixg);
931 pixCopyInputFormat(pixd, pixs);
932 datag = pixGetData(pixg);
933 wplg = pixGetWpl(pixg);
934 datad = pixGetData(pixd);
935 wpld = pixGetWpl(pixd);
936 for (i = 0; i < h; i++) {
937 lineg = datag + i * wplg;
938 lined = datad + i * wpld;
939 for (j = 0; j < w; j++) {
940 if (d == 8)
941 val = GET_DATA_BYTE(lineg, j);
942 else if (d == 4)
943 val = GET_DATA_QBIT(lineg, j);
944 else /* d == 2 */
945 val = GET_DATA_DIBIT(lineg, j);
946 if (inband) {
947 if (val >= lower && val <= upper)
948 SET_DATA_BIT(lined, j);
949 } else { /* out of band */
950 if (val < lower || val > upper)
951 SET_DATA_BIT(lined, j);
952 }
953 }
954 }
955
956 pixDestroy(&pixg);
957 return pixd;
958}
959
960
961/*------------------------------------------------------------------*
962 * Thresholding to 2 bpp by dithering *
963 *------------------------------------------------------------------*/
1003PIX *
1005 l_int32 cmapflag)
1006{
1007 if (!pixs)
1008 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1009 if (pixGetDepth(pixs) != 8)
1010 return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL);
1011
1013 DEFAULT_CLIP_UPPER_2, cmapflag);
1014}
1015
1016
1035PIX *
1037 l_int32 lowerclip,
1038 l_int32 upperclip,
1039 l_int32 cmapflag)
1040{
1041l_int32 w, h, d, wplt, wpld;
1042l_int32 *tabval, *tab38, *tab14;
1043l_uint32 *datat, *datad;
1044l_uint32 *bufs1, *bufs2;
1045PIX *pixt, *pixd;
1046PIXCMAP *cmap;
1047
1048 if (!pixs)
1049 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1050 pixGetDimensions(pixs, &w, &h, &d);
1051 if (d != 8)
1052 return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL);
1053 if (lowerclip < 0 || lowerclip > 255)
1054 return (PIX *)ERROR_PTR("invalid value for lowerclip", __func__, NULL);
1055 if (upperclip < 0 || upperclip > 255)
1056 return (PIX *)ERROR_PTR("invalid value for upperclip", __func__, NULL);
1057
1058 if ((pixd = pixCreate(w, h, 2)) == NULL)
1059 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1060 pixCopyResolution(pixd, pixs);
1061 pixCopyInputFormat(pixd, pixs);
1062 datad = pixGetData(pixd);
1063 wpld = pixGetWpl(pixd);
1064
1065 /* If there is a colormap, remove it */
1066 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
1067 datat = pixGetData(pixt);
1068 wplt = pixGetWpl(pixt);
1069
1070 /* Two line buffers, 1 for current line and 2 for next line */
1071 bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32));
1072 bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32));
1073 if (!bufs1 || !bufs2) {
1074 LEPT_FREE(bufs1);
1075 LEPT_FREE(bufs2);
1076 pixDestroy(&pixd);
1077 pixDestroy(&pixt);
1078 return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", __func__, NULL);
1079 }
1080
1081 /* 3 lookup tables: 2-bit value, (3/8)excess, and (1/4)excess */
1082 make8To2DitherTables(&tabval, &tab38, &tab14, lowerclip, upperclip);
1083
1084 ditherTo2bppLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2,
1085 tabval, tab38, tab14);
1086
1087 if (cmapflag) {
1088 cmap = pixcmapCreateLinear(2, 4);
1089 pixSetColormap(pixd, cmap);
1090 }
1091
1092 LEPT_FREE(bufs1);
1093 LEPT_FREE(bufs2);
1094 LEPT_FREE(tabval);
1095 LEPT_FREE(tab38);
1096 LEPT_FREE(tab14);
1097 pixDestroy(&pixt);
1098 return pixd;
1099}
1100
1101
1116static void
1117ditherTo2bppLow(l_uint32 *datad,
1118 l_int32 w,
1119 l_int32 h,
1120 l_int32 wpld,
1121 l_uint32 *datas,
1122 l_int32 wpls,
1123 l_uint32 *bufs1,
1124 l_uint32 *bufs2,
1125 l_int32 *tabval,
1126 l_int32 *tab38,
1127 l_int32 *tab14)
1128{
1129l_int32 i;
1130l_uint32 *lined;
1131
1132 /* do all lines except last line */
1133 memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */
1134 for (i = 0; i < h - 1; i++) {
1135 memcpy(bufs1, bufs2, 4 * wpls);
1136 memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls);
1137 lined = datad + i * wpld;
1138 ditherTo2bppLineLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 0);
1139 }
1140
1141 /* do last line */
1142 memcpy(bufs1, bufs2, 4 * wpls);
1143 lined = datad + (h - 1) * wpld;
1144 ditherTo2bppLineLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 1);
1145}
1146
1147
1174static void
1175ditherTo2bppLineLow(l_uint32 *lined,
1176 l_int32 w,
1177 l_uint32 *bufs1,
1178 l_uint32 *bufs2,
1179 l_int32 *tabval,
1180 l_int32 *tab38,
1181 l_int32 *tab14,
1182 l_int32 lastlineflag)
1183{
1184l_int32 j;
1185l_int32 oval, tab38val, tab14val;
1186l_uint8 rval, bval, dval;
1187
1188 if (lastlineflag == 0) {
1189 for (j = 0; j < w - 1; j++) {
1190 oval = GET_DATA_BYTE(bufs1, j);
1191 SET_DATA_DIBIT(lined, j, tabval[oval]);
1192 rval = GET_DATA_BYTE(bufs1, j + 1);
1193 bval = GET_DATA_BYTE(bufs2, j);
1194 dval = GET_DATA_BYTE(bufs2, j + 1);
1195 tab38val = tab38[oval];
1196 tab14val = tab14[oval];
1197 if (tab38val < 0) {
1198 rval = L_MAX(0, rval + tab38val);
1199 bval = L_MAX(0, bval + tab38val);
1200 dval = L_MAX(0, dval + tab14val);
1201 } else {
1202 rval = L_MIN(255, rval + tab38val);
1203 bval = L_MIN(255, bval + tab38val);
1204 dval = L_MIN(255, dval + tab14val);
1205 }
1206 SET_DATA_BYTE(bufs1, j + 1, rval);
1207 SET_DATA_BYTE(bufs2, j, bval);
1208 SET_DATA_BYTE(bufs2, j + 1, dval);
1209 }
1210
1211 /* do last column: j = w - 1 */
1212 oval = GET_DATA_BYTE(bufs1, j);
1213 SET_DATA_DIBIT(lined, j, tabval[oval]);
1214 bval = GET_DATA_BYTE(bufs2, j);
1215 tab38val = tab38[oval];
1216 if (tab38val < 0)
1217 bval = L_MAX(0, bval + tab38val);
1218 else
1219 bval = L_MIN(255, bval + tab38val);
1220 SET_DATA_BYTE(bufs2, j, bval);
1221 } else { /* lastlineflag == 1 */
1222 for (j = 0; j < w - 1; j++) {
1223 oval = GET_DATA_BYTE(bufs1, j);
1224 SET_DATA_DIBIT(lined, j, tabval[oval]);
1225 rval = GET_DATA_BYTE(bufs1, j + 1);
1226 tab38val = tab38[oval];
1227 if (tab38val < 0)
1228 rval = L_MAX(0, rval + tab38val);
1229 else
1230 rval = L_MIN(255, rval + tab38val);
1231 SET_DATA_BYTE(bufs1, j + 1, rval);
1232 }
1233
1234 /* do last pixel: (i, j) = (h - 1, w - 1) */
1235 oval = GET_DATA_BYTE(bufs1, j);
1236 SET_DATA_DIBIT(lined, j, tabval[oval]);
1237 }
1238}
1239
1240
1252static l_int32
1253make8To2DitherTables(l_int32 **ptabval,
1254 l_int32 **ptab38,
1255 l_int32 **ptab14,
1256 l_int32 cliptoblack,
1257 l_int32 cliptowhite)
1258{
1259l_int32 i;
1260l_int32 *tabval, *tab38, *tab14;
1261
1262 /* 3 lookup tables: 2-bit value, (3/8)excess, and (1/4)excess */
1263 tabval = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1264 tab38 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1265 tab14 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1266 *ptabval = tabval;
1267 *ptab38 = tab38;
1268 *ptab14 = tab14;
1269
1270 for (i = 0; i < 256; i++) {
1271 if (i <= cliptoblack) {
1272 tabval[i] = 0;
1273 tab38[i] = 0;
1274 tab14[i] = 0;
1275 } else if (i < 43) {
1276 tabval[i] = 0;
1277 tab38[i] = (3 * i + 4) / 8;
1278 tab14[i] = (i + 2) / 4;
1279 } else if (i < 85) {
1280 tabval[i] = 1;
1281 tab38[i] = (3 * (i - 85) - 4) / 8;
1282 tab14[i] = ((i - 85) - 2) / 4;
1283 } else if (i < 128) {
1284 tabval[i] = 1;
1285 tab38[i] = (3 * (i - 85) + 4) / 8;
1286 tab14[i] = ((i - 85) + 2) / 4;
1287 } else if (i < 170) {
1288 tabval[i] = 2;
1289 tab38[i] = (3 * (i - 170) - 4) / 8;
1290 tab14[i] = ((i - 170) - 2) / 4;
1291 } else if (i < 213) {
1292 tabval[i] = 2;
1293 tab38[i] = (3 * (i - 170) + 4) / 8;
1294 tab14[i] = ((i - 170) + 2) / 4;
1295 } else if (i < 255 - cliptowhite) {
1296 tabval[i] = 3;
1297 tab38[i] = (3 * (i - 255) - 4) / 8;
1298 tab14[i] = ((i - 255) - 2) / 4;
1299 } else { /* i >= 255 - cliptowhite */
1300 tabval[i] = 3;
1301 tab38[i] = 0;
1302 tab14[i] = 0;
1303 }
1304 }
1305
1306 return 0;
1307}
1308
1309
1310/*--------------------------------------------------------------------*
1311 * Simple (pixelwise) thresholding to 2 bpp with optional colormap *
1312 *--------------------------------------------------------------------*/
1357PIX *
1359 l_int32 nlevels,
1360 l_int32 cmapflag)
1361{
1362l_int32 *qtab;
1363l_int32 w, h, d, wplt, wpld;
1364l_uint32 *datat, *datad;
1365PIX *pixt, *pixd;
1366PIXCMAP *cmap;
1367
1368 if (!pixs)
1369 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1370 pixGetDimensions(pixs, &w, &h, &d);
1371 if (d != 8)
1372 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL);
1373 if (nlevels < 2 || nlevels > 4)
1374 return (PIX *)ERROR_PTR("nlevels not in {2, 3, 4}", __func__, NULL);
1375
1376 if ((pixd = pixCreate(w, h, 2)) == NULL)
1377 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1378 pixCopyResolution(pixd, pixs);
1379 pixCopyInputFormat(pixd, pixs);
1380 datad = pixGetData(pixd);
1381 wpld = pixGetWpl(pixd);
1382
1383 if (cmapflag) { /* hold out (4 - nlevels) cmap entries */
1384 cmap = pixcmapCreateLinear(2, nlevels);
1385 pixSetColormap(pixd, cmap);
1386 }
1387
1388 /* If there is a colormap in the src, remove it */
1389 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
1390 datat = pixGetData(pixt);
1391 wplt = pixGetWpl(pixt);
1392
1393 /* Make the appropriate table */
1394 if (cmapflag)
1395 qtab = makeGrayQuantIndexTable(nlevels);
1396 else
1397 qtab = makeGrayQuantTargetTable(4, 2);
1398
1399 thresholdTo2bppLow(datad, h, wpld, datat, wplt, qtab);
1400
1401 LEPT_FREE(qtab);
1402 pixDestroy(&pixt);
1403 return pixd;
1404}
1405
1406
1419static void
1420thresholdTo2bppLow(l_uint32 *datad,
1421 l_int32 h,
1422 l_int32 wpld,
1423 l_uint32 *datas,
1424 l_int32 wpls,
1425 l_int32 *tab)
1426{
1427l_uint8 sval1, sval2, sval3, sval4, dval;
1428l_int32 i, j, k;
1429l_uint32 *lines, *lined;
1430
1431 for (i = 0; i < h; i++) {
1432 lines = datas + i * wpls;
1433 lined = datad + i * wpld;
1434 for (j = 0; j < wpls; j++) {
1435 k = 4 * j;
1436 sval1 = GET_DATA_BYTE(lines, k);
1437 sval2 = GET_DATA_BYTE(lines, k + 1);
1438 sval3 = GET_DATA_BYTE(lines, k + 2);
1439 sval4 = GET_DATA_BYTE(lines, k + 3);
1440 dval = (tab[sval1] << 6) | (tab[sval2] << 4) |
1441 (tab[sval3] << 2) | tab[sval4];
1442 SET_DATA_BYTE(lined, j, dval);
1443 }
1444 }
1445}
1446
1447
1448/*----------------------------------------------------------------------*
1449 * Simple (pixelwise) thresholding to 4 bpp *
1450 *----------------------------------------------------------------------*/
1497PIX *
1499 l_int32 nlevels,
1500 l_int32 cmapflag)
1501{
1502l_int32 *qtab;
1503l_int32 w, h, d, wplt, wpld;
1504l_uint32 *datat, *datad;
1505PIX *pixt, *pixd;
1506PIXCMAP *cmap;
1507
1508 if (!pixs)
1509 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1510 pixGetDimensions(pixs, &w, &h, &d);
1511 if (d != 8)
1512 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL);
1513 if (nlevels < 2 || nlevels > 16)
1514 return (PIX *)ERROR_PTR("nlevels not in [2,...,16]", __func__, NULL);
1515
1516 if ((pixd = pixCreate(w, h, 4)) == NULL)
1517 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1518 pixCopyResolution(pixd, pixs);
1519 pixCopyInputFormat(pixd, pixs);
1520 datad = pixGetData(pixd);
1521 wpld = pixGetWpl(pixd);
1522
1523 if (cmapflag) { /* hold out (16 - nlevels) cmap entries */
1524 cmap = pixcmapCreateLinear(4, nlevels);
1525 pixSetColormap(pixd, cmap);
1526 }
1527
1528 /* If there is a colormap in the src, remove it */
1529 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
1530 datat = pixGetData(pixt);
1531 wplt = pixGetWpl(pixt);
1532
1533 /* Make the appropriate table */
1534 if (cmapflag)
1535 qtab = makeGrayQuantIndexTable(nlevels);
1536 else
1537 qtab = makeGrayQuantTargetTable(16, 4);
1538
1539 thresholdTo4bppLow(datad, h, wpld, datat, wplt, qtab);
1540
1541 LEPT_FREE(qtab);
1542 pixDestroy(&pixt);
1543 return pixd;
1544}
1545
1546
1559static void
1560thresholdTo4bppLow(l_uint32 *datad,
1561 l_int32 h,
1562 l_int32 wpld,
1563 l_uint32 *datas,
1564 l_int32 wpls,
1565 l_int32 *tab)
1566{
1567l_uint8 sval1, sval2, sval3, sval4;
1568l_uint16 dval;
1569l_int32 i, j, k;
1570l_uint32 *lines, *lined;
1571
1572 for (i = 0; i < h; i++) {
1573 lines = datas + i * wpls;
1574 lined = datad + i * wpld;
1575 for (j = 0; j < wpls; j++) {
1576 k = 4 * j;
1577 sval1 = GET_DATA_BYTE(lines, k);
1578 sval2 = GET_DATA_BYTE(lines, k + 1);
1579 sval3 = GET_DATA_BYTE(lines, k + 2);
1580 sval4 = GET_DATA_BYTE(lines, k + 3);
1581 dval = (tab[sval1] << 12) | (tab[sval2] << 8) |
1582 (tab[sval3] << 4) | tab[sval4];
1583 SET_DATA_TWO_BYTES(lined, j, dval);
1584 }
1585 }
1586}
1587
1588
1589/*----------------------------------------------------------------------*
1590 * Simple (pixelwise) thresholding on 8 bpp with optional colormap *
1591 *----------------------------------------------------------------------*/
1612PIX *
1614 l_int32 nlevels,
1615 l_int32 cmapflag)
1616{
1617l_int32 *qtab; /* quantization table */
1618l_int32 i, j, w, h, wpld, val, newval;
1619l_uint32 *datad, *lined;
1620PIX *pixd;
1621PIXCMAP *cmap;
1622
1623 if (!pixs)
1624 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1625 if (pixGetDepth(pixs) != 8)
1626 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL);
1627 if (nlevels < 2 || nlevels > 256)
1628 return (PIX *)ERROR_PTR("nlevels not in [2,...,256]", __func__, NULL);
1629
1630 /* Get a new pixd; if there is a colormap in the src, remove it */
1631 if (pixGetColormap(pixs))
1632 pixd = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
1633 else
1634 pixd = pixCopy(NULL, pixs);
1635
1636 if (cmapflag) { /* hold out (256 - nlevels) cmap entries */
1637 cmap = pixcmapCreateLinear(8, nlevels);
1638 pixSetColormap(pixd, cmap);
1639 }
1640
1641 if (cmapflag)
1642 qtab = makeGrayQuantIndexTable(nlevels);
1643 else
1644 qtab = makeGrayQuantTargetTable(nlevels, 8);
1645
1646 pixGetDimensions(pixd, &w, &h, NULL);
1647 pixCopyResolution(pixd, pixs);
1648 pixCopyInputFormat(pixd, pixs);
1649 datad = pixGetData(pixd);
1650 wpld = pixGetWpl(pixd);
1651 for (i = 0; i < h; i++) {
1652 lined = datad + i * wpld;
1653 for (j = 0; j < w; j++) {
1654 val = GET_DATA_BYTE(lined, j);
1655 newval = qtab[val];
1656 SET_DATA_BYTE(lined, j, newval);
1657 }
1658 }
1659
1660 LEPT_FREE(qtab);
1661 return pixd;
1662}
1663
1664
1665/*----------------------------------------------------------------------*
1666 * Arbitrary (pixelwise) thresholding from 8 bpp to 2, 4 or 8 bpp *
1667 *----------------------------------------------------------------------*/
1711PIX *
1713 const char *edgevals,
1714 l_int32 outdepth,
1715 l_int32 use_average,
1716 l_int32 setblack,
1717 l_int32 setwhite)
1718{
1719l_int32 *qtab;
1720l_int32 w, h, d, i, j, n, wplt, wpld, val, newval;
1721l_uint32 *datat, *datad, *linet, *lined;
1722NUMA *na;
1723PIX *pixt, *pixd;
1724PIXCMAP *cmap;
1725
1726 if (!pixs)
1727 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1728 pixGetDimensions(pixs, &w, &h, &d);
1729 if (d != 8)
1730 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL);
1731 if (!edgevals)
1732 return (PIX *)ERROR_PTR("edgevals not defined", __func__, NULL);
1733 if (outdepth != 0 && outdepth != 2 && outdepth != 4 && outdepth != 8)
1734 return (PIX *)ERROR_PTR("invalid outdepth", __func__, NULL);
1735
1736 /* Parse and sort (if required) the bin edge values */
1737 na = parseStringForNumbers(edgevals, " \t\n,");
1738 n = numaGetCount(na);
1739 if (n > 255) {
1740 numaDestroy(&na);
1741 return (PIX *)ERROR_PTR("more than 256 levels", __func__, NULL);
1742 }
1743 if (outdepth == 0) {
1744 if (n <= 3)
1745 outdepth = 2;
1746 else if (n <= 15)
1747 outdepth = 4;
1748 else
1749 outdepth = 8;
1750 } else if (n + 1 > (1 << outdepth)) {
1751 L_WARNING("outdepth too small; setting to 8 bpp\n", __func__);
1752 outdepth = 8;
1753 }
1754 numaSort(na, na, L_SORT_INCREASING);
1755
1756 /* Make the quantization LUT and the colormap */
1757 makeGrayQuantTableArb(na, outdepth, &qtab, &cmap);
1758 if (use_average) { /* use the average value in each bin */
1759 pixcmapDestroy(&cmap);
1760 makeGrayQuantColormapArb(pixs, qtab, outdepth, &cmap);
1761 }
1762 pixcmapSetBlackAndWhite(cmap, setblack, setwhite);
1763 numaDestroy(&na);
1764
1765 if ((pixd = pixCreate(w, h, outdepth)) == NULL) {
1766 LEPT_FREE(qtab);
1767 pixcmapDestroy(&cmap);
1768 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1769 }
1770 pixCopyResolution(pixd, pixs);
1771 pixCopyInputFormat(pixd, pixs);
1772 pixSetColormap(pixd, cmap);
1773 datad = pixGetData(pixd);
1774 wpld = pixGetWpl(pixd);
1775
1776 /* If there is a colormap in the src, remove it */
1777 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
1778 datat = pixGetData(pixt);
1779 wplt = pixGetWpl(pixt);
1780
1781 if (outdepth == 2) {
1782 thresholdTo2bppLow(datad, h, wpld, datat, wplt, qtab);
1783 } else if (outdepth == 4) {
1784 thresholdTo4bppLow(datad, h, wpld, datat, wplt, qtab);
1785 } else {
1786 for (i = 0; i < h; i++) {
1787 lined = datad + i * wpld;
1788 linet = datat + i * wplt;
1789 for (j = 0; j < w; j++) {
1790 val = GET_DATA_BYTE(linet, j);
1791 newval = qtab[val];
1792 SET_DATA_BYTE(lined, j, newval);
1793 }
1794 }
1795 }
1796
1797 LEPT_FREE(qtab);
1798 pixDestroy(&pixt);
1799 return pixd;
1800}
1801
1802
1803/*----------------------------------------------------------------------*
1804 * Quantization tables for linear thresholds of grayscale images *
1805 *----------------------------------------------------------------------*/
1819l_int32 *
1821{
1822l_int32 *tab;
1823l_int32 i, j, thresh;
1824
1825 tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1826 for (i = 0; i < 256; i++) {
1827 for (j = 0; j < nlevels; j++) {
1828 thresh = 255 * (2 * j + 1) / (2 * nlevels - 2);
1829 if (i <= thresh) {
1830 tab[i] = j;
1831/* lept_stderr("tab[%d] = %d\n", i, j); */
1832 break;
1833 }
1834 }
1835 }
1836 return tab;
1837}
1838
1839
1868static l_int32 *
1870 l_int32 depth)
1871{
1872l_int32 *tab;
1873l_int32 i, j, thresh, maxval, quantval;
1874
1875 tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1876 maxval = (1 << depth) - 1;
1877 if (depth < 8)
1878 nlevels = 1 << depth;
1879 for (i = 0; i < 256; i++) {
1880 for (j = 0; j < nlevels; j++) {
1881 thresh = 255 * (2 * j + 1) / (2 * nlevels - 2);
1882 if (i <= thresh) {
1883 quantval = maxval * j / (nlevels - 1);
1884 tab[i] = quantval;
1885/* lept_stderr("tab[%d] = %d\n", i, tab[i]); */
1886 break;
1887 }
1888 }
1889 }
1890 return tab;
1891}
1892
1893
1894/*----------------------------------------------------------------------*
1895 * Quantization table for arbitrary thresholding of grayscale images *
1896 *----------------------------------------------------------------------*/
1921l_ok
1923 l_int32 outdepth,
1924 l_int32 **ptab,
1925 PIXCMAP **pcmap)
1926{
1927l_int32 i, j, n, jstart, ave, val;
1928l_int32 *tab;
1929PIXCMAP *cmap;
1930
1931 if (!ptab)
1932 return ERROR_INT("&tab not defined", __func__, 1);
1933 *ptab = NULL;
1934 if (!pcmap)
1935 return ERROR_INT("&cmap not defined", __func__, 1);
1936 *pcmap = NULL;
1937 if (!na)
1938 return ERROR_INT("na not defined", __func__, 1);
1939 n = numaGetCount(na);
1940 if (n + 1 > (1 << outdepth))
1941 return ERROR_INT("more bins than cmap levels", __func__, 1);
1942
1943 if ((cmap = pixcmapCreate(outdepth)) == NULL)
1944 return ERROR_INT("cmap not made", __func__, 1);
1945 tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1946 *ptab = tab;
1947 *pcmap = cmap;
1948
1949 /* First n bins */
1950 jstart = 0;
1951 for (i = 0; i < n; i++) {
1952 numaGetIValue(na, i, &val);
1953 ave = (jstart + val) / 2;
1954 pixcmapAddColor(cmap, ave, ave, ave);
1955 for (j = jstart; j < val; j++)
1956 tab[j] = i;
1957 jstart = val;
1958 }
1959
1960 /* Last bin */
1961 ave = (jstart + 255) / 2;
1962 pixcmapAddColor(cmap, ave, ave, ave);
1963 for (j = jstart; j < 256; j++)
1964 tab[j] = n;
1965
1966 return 0;
1967}
1968
1969
1991static l_int32
1993 l_int32 *tab,
1994 l_int32 outdepth,
1995 PIXCMAP **pcmap)
1996{
1997l_int32 i, j, index, w, h, d, nbins, wpl, factor, val;
1998l_int32 *bincount, *binave, *binstart;
1999l_uint32 *line, *data;
2000
2001 if (!pcmap)
2002 return ERROR_INT("&cmap not defined", __func__, 1);
2003 *pcmap = NULL;
2004 if (!pixs)
2005 return ERROR_INT("pixs not defined", __func__, 1);
2006 pixGetDimensions(pixs, &w, &h, &d);
2007 if (d != 8)
2008 return ERROR_INT("pixs not 8 bpp", __func__, 1);
2009 if (!tab)
2010 return ERROR_INT("tab not defined", __func__, 1);
2011 nbins = tab[255] + 1;
2012 if (nbins > (1 << outdepth))
2013 return ERROR_INT("more bins than cmap levels", __func__, 1);
2014
2015 /* Find the count and weighted count for each bin */
2016 if ((bincount = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32))) == NULL)
2017 return ERROR_INT("calloc fail for bincount", __func__, 1);
2018 if ((binave = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32))) == NULL) {
2019 LEPT_FREE(bincount);
2020 return ERROR_INT("calloc fail for binave", __func__, 1);
2021 }
2022 factor = (l_int32)(sqrt((l_float64)(w * h) / 30000.) + 0.5);
2023 factor = L_MAX(1, factor);
2024 data = pixGetData(pixs);
2025 wpl = pixGetWpl(pixs);
2026 for (i = 0; i < h; i += factor) {
2027 line = data + i * wpl;
2028 for (j = 0; j < w; j += factor) {
2029 val = GET_DATA_BYTE(line, j);
2030 bincount[tab[val]]++;
2031 binave[tab[val]] += val;
2032 }
2033 }
2034
2035 /* Find the smallest gray values in each bin */
2036 binstart = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32));
2037 for (i = 1, index = 1; i < 256; i++) {
2038 if (tab[i] < index) continue;
2039 if (tab[i] == index)
2040 binstart[index++] = i;
2041 }
2042
2043 /* Get the averages. If there are no samples in a bin, use
2044 * the center value of the bin. */
2045 *pcmap = pixcmapCreate(outdepth);
2046 for (i = 0; i < nbins; i++) {
2047 if (bincount[i]) {
2048 val = binave[i] / bincount[i];
2049 } else { /* no samples in the bin */
2050 if (i < nbins - 1)
2051 val = (binstart[i] + binstart[i + 1]) / 2;
2052 else /* last bin */
2053 val = (binstart[i] + 255) / 2;
2054 }
2055 pixcmapAddColor(*pcmap, val, val, val);
2056 }
2057
2058 LEPT_FREE(bincount);
2059 LEPT_FREE(binave);
2060 LEPT_FREE(binstart);
2061 return 0;
2062}
2063
2064
2065/*--------------------------------------------------------------------*
2066 * Thresholding from 32 bpp rgb to 1 bpp *
2067 *--------------------------------------------------------------------*/
2094PIX *
2096 l_uint32 refval,
2097 l_int32 delm,
2098 l_int32 delp,
2099 l_float32 fractm,
2100 l_float32 fractp)
2101{
2102l_int32 i, j, w, h, d, wpls, wpld;
2103l_int32 rref, gref, bref, rval, gval, bval;
2104l_int32 rmin, gmin, bmin, rmax, gmax, bmax;
2105l_uint32 pixel;
2106l_uint32 *datas, *datad, *lines, *lined;
2107PIX *pixd;
2108
2109 if (!pixs)
2110 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2111 pixGetDimensions(pixs, &w, &h, &d);
2112 if (d != 32)
2113 return (PIX *)ERROR_PTR("not 32 bpp", __func__, NULL);
2114 if (delm < 0 || delp < 0)
2115 return (PIX *)ERROR_PTR("delm and delp must be >= 0", __func__, NULL);
2116 if (fractm < 0.0 || fractm > 1.0 || fractp < 0.0 || fractp > 1.0)
2117 return (PIX *)ERROR_PTR("fractm and/or fractp invalid", __func__, NULL);
2118
2119 extractRGBValues(refval, &rref, &gref, &bref);
2120 if (fractm == 0.0 && fractp == 0.0) {
2121 rmin = rref - delm;
2122 gmin = gref - delm;
2123 bmin = bref - delm;
2124 rmax = rref + delm;
2125 gmax = gref + delm;
2126 bmax = bref + delm;
2127 } else if (delm == 0 && delp == 0) {
2128 rmin = (l_int32)((1.0 - fractm) * rref);
2129 gmin = (l_int32)((1.0 - fractm) * gref);
2130 bmin = (l_int32)((1.0 - fractm) * bref);
2131 rmax = rref + (l_int32)(fractp * (255 - rref));
2132 gmax = gref + (l_int32)(fractp * (255 - gref));
2133 bmax = bref + (l_int32)(fractp * (255 - bref));
2134 } else {
2135 L_ERROR("bad input: either (delm, delp) or (fractm, fractp) "
2136 "must be 0\n", __func__);
2137 return NULL;
2138 }
2139
2140 pixd = pixCreate(w, h, 1);
2141 pixCopyResolution(pixd, pixs);
2142 pixCopyInputFormat(pixd, pixs);
2143 datas = pixGetData(pixs);
2144 wpls = pixGetWpl(pixs);
2145 datad = pixGetData(pixd);
2146 wpld = pixGetWpl(pixd);
2147 for (i = 0; i < h; i++) {
2148 lines = datas + i * wpls;
2149 lined = datad + i * wpld;
2150 for (j = 0; j < w; j++) {
2151 pixel = lines[j];
2152 rval = (pixel >> L_RED_SHIFT) & 0xff;
2153 if (rval < rmin || rval > rmax)
2154 continue;
2155 gval = (pixel >> L_GREEN_SHIFT) & 0xff;
2156 if (gval < gmin || gval > gmax)
2157 continue;
2158 bval = (pixel >> L_BLUE_SHIFT) & 0xff;
2159 if (bval < bmin || bval > bmax)
2160 continue;
2161 SET_DATA_BIT(lined, j);
2162 }
2163 }
2164
2165 return pixd;
2166}
2167
2168
2190PIX *
2192 l_uint32 refval1,
2193 l_uint32 refval2,
2194 l_int32 distflag)
2195{
2196l_int32 i, j, w, h, d, wpls, wpld;
2197l_int32 rref1, gref1, bref1, rref2, gref2, bref2, rval, gval, bval;
2198l_uint32 pixel, dist1, dist2;
2199l_uint32 *datas, *datad, *lines, *lined;
2200PIX *pixd;
2201
2202 if (!pixs)
2203 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2204 pixGetDimensions(pixs, &w, &h, &d);
2205 if (d != 32)
2206 return (PIX *)ERROR_PTR("not 32 bpp", __func__, NULL);
2207 if (distflag != L_MANHATTAN_DISTANCE && distflag != L_EUCLIDEAN_DISTANCE)
2208 return (PIX *)ERROR_PTR("invalid distflag", __func__, NULL);
2209
2210 extractRGBValues(refval1, &rref1, &gref1, &bref1);
2211 extractRGBValues(refval2, &rref2, &gref2, &bref2);
2212 pixd = pixCreate(w, h, 1);
2213 pixCopyResolution(pixd, pixs);
2214 pixCopyInputFormat(pixd, pixs);
2215 datas = pixGetData(pixs);
2216 wpls = pixGetWpl(pixs);
2217 datad = pixGetData(pixd);
2218 wpld = pixGetWpl(pixd);
2219 for (i = 0; i < h; i++) {
2220 lines = datas + i * wpls;
2221 lined = datad + i * wpld;
2222 for (j = 0; j < w; j++) {
2223 pixel = lines[j];
2224 extractRGBValues(pixel, &rval, &gval, &bval);
2225 if (distflag == L_MANHATTAN_DISTANCE) {
2226 dist1 = L_ABS(rref1 - rval);
2227 dist2 = L_ABS(rref2 - rval);
2228 dist1 += L_ABS(gref1 - gval);
2229 dist2 += L_ABS(gref2 - gval);
2230 dist1 += L_ABS(bref1 - bval);
2231 dist2 += L_ABS(bref2 - bval);
2232 } else {
2233 dist1 = (rref1 - rval) * (rref1 - rval);
2234 dist2 = (rref2 - rval) * (rref2 - rval);
2235 dist1 += (gref1 - gval) * (gref1 - gval);
2236 dist2 += (gref2 - gval) * (gref2 - gval);
2237 dist1 += (bref1 - bval) * (bref1 - bval);
2238 dist2 += (bref2 - bval) * (bref2 - bval);
2239 }
2240 if (dist1 < dist2)
2241 SET_DATA_BIT(lined, j);
2242 }
2243 }
2244
2245 return pixd;
2246}
2247
2248
2249/*----------------------------------------------------------------------*
2250 * Histogram-based grayscale quantization *
2251 *----------------------------------------------------------------------*/
2302PIX *
2304 PIX *pixs,
2305 PIX *pixm,
2306 l_float32 minfract,
2307 l_int32 maxsize)
2308{
2309l_int32 w, h, wd, hd, wm, hm, wpls, wplm, wpld;
2310l_int32 nc, nestim, i, j, vals, vald;
2311l_int32 *lut;
2312l_uint32 *datas, *datam, *datad, *lines, *linem, *lined;
2313NUMA *na;
2314PIX *pixmr = NULL; /* resized mask */
2315PIXCMAP *cmap;
2316
2317 if (!pixs || pixGetDepth(pixs) != 8)
2318 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL);
2319 if (minfract < 0.01) {
2320 L_WARNING("minfract < 0.01; setting to 0.05\n", __func__);
2321 minfract = 0.05f;
2322 }
2323 if (maxsize < 2) {
2324 L_WARNING("maxsize < 2; setting to 10\n", __func__);
2325 maxsize = 10;
2326 }
2327 if ((pixd && !pixm) || (!pixd && pixm))
2328 return (PIX *)ERROR_PTR("(pixd,pixm) not defined together",
2329 __func__, NULL);
2330 pixGetDimensions(pixs, &w, &h, NULL);
2331 if (pixd) {
2332 if (pixGetDepth(pixm) != 1)
2333 return (PIX *)ERROR_PTR("pixm not 1 bpp", __func__, NULL);
2334 if ((cmap = pixGetColormap(pixd)) == NULL)
2335 return (PIX *)ERROR_PTR("pixd not cmapped", __func__, NULL);
2336 pixGetDimensions(pixd, &wd, &hd, NULL);
2337 if (w != wd || h != hd)
2338 return (PIX *)ERROR_PTR("pixs, pixd sizes differ", __func__, NULL);
2339 nc = pixcmapGetCount(cmap);
2340 nestim = nc + (l_int32)(1.5 * 255 / maxsize);
2341 lept_stderr( "nestim = %d\n", nestim);
2342 if (nestim > 255) {
2343 L_ERROR("Estimate %d colors!\n", __func__, nestim);
2344 return (PIX *)ERROR_PTR("probably too many colors", __func__, NULL);
2345 }
2346 pixGetDimensions(pixm, &wm, &hm, NULL);
2347 if (w != wm || h != hm) { /* resize the mask */
2348 L_WARNING("mask and dest sizes not equal\n", __func__);
2349 pixmr = pixCreate(w, h, 1);
2350 pixRasterop(pixmr, 0, 0, wm, hm, PIX_SRC, pixm, 0, 0);
2351 pixRasterop(pixmr, wm, 0, w - wm, h, PIX_SET, NULL, 0, 0);
2352 pixRasterop(pixmr, 0, hm, wm, h - hm, PIX_SET, NULL, 0, 0);
2353 } else {
2354 pixmr = pixClone(pixm);
2355 }
2356 } else {
2357 pixd = pixCreateTemplate(pixs);
2358 cmap = pixcmapCreate(8);
2359 pixSetColormap(pixd, cmap);
2360 }
2361 pixCopyResolution(pixd, pixs);
2362 pixCopyInputFormat(pixd, pixs);
2363
2364 /* Use original mask, if it exists, to select gray pixels */
2365 na = pixGetGrayHistogramMasked(pixs, pixm, 0, 0, 1);
2366
2367 /* Fill out the cmap with gray colors, and generate the lut
2368 * for pixel assignment. Issue a warning on failure. */
2369 if (numaFillCmapFromHisto(na, cmap, minfract, maxsize, &lut))
2370 L_ERROR("ran out of colors in cmap!\n", __func__);
2371 numaDestroy(&na);
2372
2373 /* Assign the gray pixels to their cmap indices */
2374 datas = pixGetData(pixs);
2375 datad = pixGetData(pixd);
2376 wpls = pixGetWpl(pixs);
2377 wpld = pixGetWpl(pixd);
2378 if (!pixm) {
2379 for (i = 0; i < h; i++) {
2380 lines = datas + i * wpls;
2381 lined = datad + i * wpld;
2382 for (j = 0; j < w; j++) {
2383 vals = GET_DATA_BYTE(lines, j);
2384 vald = lut[vals];
2385 SET_DATA_BYTE(lined, j, vald);
2386 }
2387 }
2388 LEPT_FREE(lut);
2389 return pixd;
2390 }
2391
2392 datam = pixGetData(pixmr);
2393 wplm = pixGetWpl(pixmr);
2394 for (i = 0; i < h; i++) {
2395 lines = datas + i * wpls;
2396 linem = datam + i * wplm;
2397 lined = datad + i * wpld;
2398 for (j = 0; j < w; j++) {
2399 if (!GET_DATA_BIT(linem, j))
2400 continue;
2401 vals = GET_DATA_BYTE(lines, j);
2402 vald = lut[vals];
2403 SET_DATA_BYTE(lined, j, vald);
2404 }
2405 }
2406 pixDestroy(&pixmr);
2407 LEPT_FREE(lut);
2408 return pixd;
2409}
2410
2411
2432static l_int32
2434 PIXCMAP *cmap,
2435 l_float32 minfract,
2436 l_int32 maxsize,
2437 l_int32 **plut)
2438{
2439l_int32 mincount, index, sum, wtsum, span, istart, i, val, ret;
2440l_int32 *iahisto, *lut;
2441l_float32 total;
2442
2443 if (!plut)
2444 return ERROR_INT("&lut not defined", __func__, 1);
2445 *plut = NULL;
2446 if (!na)
2447 return ERROR_INT("na not defined", __func__, 1);
2448 if (!cmap)
2449 return ERROR_INT("cmap not defined", __func__, 1);
2450
2451 numaGetSum(na, &total);
2452 mincount = (l_int32)(minfract * total);
2453 iahisto = numaGetIArray(na);
2454 lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
2455 *plut = lut;
2456 index = pixcmapGetCount(cmap); /* start with number of colors
2457 * already reserved */
2458
2459 /* March through, associating colors with sets of adjacent
2460 * gray levels. During the process, the LUT that gives
2461 * the colormap index for each gray level is computed.
2462 * To complete a color, either the total count must equal
2463 * or exceed %mincount, or the current span of colors must
2464 * equal or exceed %maxsize. An empty span is not converted
2465 * into a color; it is simply ignored. When a span is completed for a
2466 * color, the weighted color in the span is added to the colormap. */
2467 sum = 0;
2468 wtsum = 0;
2469 istart = 0;
2470 ret = 0;
2471 for (i = 0; i < 256; i++) {
2472 lut[i] = index;
2473 sum += iahisto[i];
2474 wtsum += i * iahisto[i];
2475 span = i - istart + 1;
2476 if (sum < mincount && span < maxsize)
2477 continue;
2478
2479 if (sum == 0) { /* empty span; don't save */
2480 istart = i + 1;
2481 continue;
2482 }
2483
2484 /* Found new color; sum > 0 */
2485 val = (l_int32)((l_float32)wtsum / (l_float32)sum + 0.5);
2486 ret = pixcmapAddColor(cmap, val, val, val);
2487 istart = i + 1;
2488 sum = 0;
2489 wtsum = 0;
2490 index++;
2491 }
2492 if (istart < 256 && sum > 0) { /* last one */
2493 span = 256 - istart;
2494 val = (l_int32)((l_float32)wtsum / (l_float32)sum + 0.5);
2495 ret = pixcmapAddColor(cmap, val, val, val);
2496 }
2497
2498 LEPT_FREE(iahisto);
2499 return ret;
2500}
2501
2502
2503/*----------------------------------------------------------------------*
2504 * Color quantize grayscale image using existing colormap *
2505 *----------------------------------------------------------------------*/
2521PIX *
2523 PIXCMAP *cmap,
2524 l_int32 mindepth)
2525{
2526l_int32 i, j, index, w, h, d, depth, wpls, wpld;
2527l_int32 hascolor, vals, vald;
2528l_int32 *tab;
2529l_uint32 *datas, *datad, *lines, *lined;
2530PIXCMAP *cmapd;
2531PIX *pixd;
2532
2533 if (!pixs)
2534 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2535 if (pixGetColormap(pixs) != NULL) {
2536 L_WARNING("pixs already has a colormap; returning a copy\n", __func__);
2537 return pixCopy(NULL, pixs);
2538 }
2539 pixGetDimensions(pixs, &w, &h, &d);
2540 if (d != 8)
2541 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL);
2542 if (!cmap)
2543 return (PIX *)ERROR_PTR("cmap not defined", __func__, NULL);
2544 if (mindepth != 2 && mindepth != 4 && mindepth != 8)
2545 return (PIX *)ERROR_PTR("invalid mindepth", __func__, NULL);
2546
2547 /* Make sure the colormap is gray */
2548 pixcmapHasColor(cmap, &hascolor);
2549 if (hascolor) {
2550 L_WARNING("Converting colormap colors to gray\n", __func__);
2551 cmapd = pixcmapColorToGray(cmap, 0.3f, 0.5f, 0.2f);
2552 } else {
2553 cmapd = pixcmapCopy(cmap);
2554 }
2555
2556 /* Make LUT into colormap */
2557 tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
2558 for (i = 0; i < 256; i++) {
2559 pixcmapGetNearestGrayIndex(cmapd, i, &index);
2560 tab[i] = index;
2561 }
2562
2563 pixcmapGetMinDepth(cmap, &depth);
2564 depth = L_MAX(depth, mindepth);
2565 pixd = pixCreate(w, h, depth);
2566 pixSetColormap(pixd, cmapd);
2567 pixCopyResolution(pixd, pixs);
2568 pixCopyInputFormat(pixd, pixs);
2569 datas = pixGetData(pixs);
2570 datad = pixGetData(pixd);
2571 wpls = pixGetWpl(pixs);
2572 wpld = pixGetWpl(pixd);
2573 for (i = 0; i < h; i++) {
2574 lines = datas + i * wpls;
2575 lined = datad + i * wpld;
2576 for (j = 0; j < w; j++) {
2577 vals = GET_DATA_BYTE(lines, j);
2578 vald = tab[vals];
2579 if (depth == 2)
2580 SET_DATA_DIBIT(lined, j, vald);
2581 else if (depth == 4)
2582 SET_DATA_QBIT(lined, j, vald);
2583 else /* depth == 8 */
2584 SET_DATA_BYTE(lined, j, vald);
2585 }
2586 }
2587
2588 LEPT_FREE(tab);
2589 return pixd;
2590}
2591
2592
2593#if 0 /* Documentation */
2594/*--------------------------------------------------------------------*
2595 * Implementation of binarization by dithering using LUTs *
2596 * It is archived here. *
2597 *--------------------------------------------------------------------*/
2612PIX *
2613pixDitherToBinaryLUT(PIX *pixs,
2614 l_int32 lowerclip,
2615 l_int32 upperclip)
2616{
2617l_int32 w, h, d, wplt, wpld;
2618l_int32 *tabval, *tab38, *tab14;
2619l_uint32 *datat, *datad;
2620l_uint32 *bufs1, *bufs2;
2621PIX *pixt, *pixd;
2622
2623 if (!pixs)
2624 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2625 pixGetDimensions(pixs, &w, &h, &d);
2626 if (d != 8)
2627 return (PIX *)ERROR_PTR("must be 8 bpp for dithering", __func__, NULL);
2628 if (lowerclip < 0)
2629 lowerclip = DEFAULT_CLIP_LOWER_1;
2630 if (upperclip < 0)
2631 upperclip = DEFAULT_CLIP_UPPER_1;
2632
2633 if ((pixd = pixCreate(w, h, 1)) == NULL)
2634 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
2635 pixCopyResolution(pixd, pixs);
2636 pixCopyInputFormat(pixd, pixs);
2637 datad = pixGetData(pixd);
2638 wpld = pixGetWpl(pixd);
2639
2640 /* Remove colormap if it exists */
2641 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
2642 datat = pixGetData(pixt);
2643 wplt = pixGetWpl(pixt);
2644
2645 /* Two line buffers, 1 for current line and 2 for next line */
2646 bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32));
2647 bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32));
2648 if (!bufs1 || !bufs2) {
2649 LEPT_FREE(bufs1);
2650 LEPT_FREE(bufs2);
2651 pixDestroy(&pixd);
2652 pixDestroy(&pixt);
2653 return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", __func__, NULL);
2654 }
2655
2656 /* 3 lookup tables: 1-bit value, (3/8)excess, and (1/4)excess */
2657 make8To1DitherTables(&tabval, &tab38, &tab14, lowerclip, upperclip);
2658
2659 ditherToBinaryLUTLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2,
2660 tabval, tab38, tab14);
2661
2662 LEPT_FREE(bufs1);
2663 LEPT_FREE(bufs2);
2664 LEPT_FREE(tabval);
2665 LEPT_FREE(tab38);
2666 LEPT_FREE(tab14);
2667 pixDestroy(&pixt);
2668 return pixd;
2669}
2670
2684void
2685ditherToBinaryLUTLow(l_uint32 *datad,
2686 l_int32 w,
2687 l_int32 h,
2688 l_int32 wpld,
2689 l_uint32 *datas,
2690 l_int32 wpls,
2691 l_uint32 *bufs1,
2692 l_uint32 *bufs2,
2693 l_int32 *tabval,
2694 l_int32 *tab38,
2695 l_int32 *tab14)
2696{
2697l_int32 i;
2698l_uint32 *lined;
2699
2700 /* do all lines except last line */
2701 memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */
2702 for (i = 0; i < h - 1; i++) {
2703 memcpy(bufs1, bufs2, 4 * wpls);
2704 memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls);
2705 lined = datad + i * wpld;
2706 ditherToBinaryLineLUTLow(lined, w, bufs1, bufs2,
2707 tabval, tab38, tab14, 0);
2708 }
2709
2710 /* do last line */
2711 memcpy(bufs1, bufs2, 4 * wpls);
2712 lined = datad + (h - 1) * wpld;
2713 ditherToBinaryLineLUTLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 1);
2714 return;
2715}
2716
2730void
2731ditherToBinaryLineLUTLow(l_uint32 *lined,
2732 l_int32 w,
2733 l_uint32 *bufs1,
2734 l_uint32 *bufs2,
2735 l_int32 *tabval,
2736 l_int32 *tab38,
2737 l_int32 *tab14,
2738 l_int32 lastlineflag)
2739{
2740l_int32 j;
2741l_int32 oval, tab38val, tab14val;
2742l_uint8 rval, bval, dval;
2743
2744 if (lastlineflag == 0) {
2745 for (j = 0; j < w - 1; j++) {
2746 oval = GET_DATA_BYTE(bufs1, j);
2747 if (tabval[oval])
2748 SET_DATA_BIT(lined, j);
2749 rval = GET_DATA_BYTE(bufs1, j + 1);
2750 bval = GET_DATA_BYTE(bufs2, j);
2751 dval = GET_DATA_BYTE(bufs2, j + 1);
2752 tab38val = tab38[oval];
2753 if (tab38val == 0)
2754 continue;
2755 tab14val = tab14[oval];
2756 if (tab38val < 0) {
2757 rval = L_MAX(0, rval + tab38val);
2758 bval = L_MAX(0, bval + tab38val);
2759 dval = L_MAX(0, dval + tab14val);
2760 } else {
2761 rval = L_MIN(255, rval + tab38val);
2762 bval = L_MIN(255, bval + tab38val);
2763 dval = L_MIN(255, dval + tab14val);
2764 }
2765 SET_DATA_BYTE(bufs1, j + 1, rval);
2766 SET_DATA_BYTE(bufs2, j, bval);
2767 SET_DATA_BYTE(bufs2, j + 1, dval);
2768 }
2769
2770 /* do last column: j = w - 1 */
2771 oval = GET_DATA_BYTE(bufs1, j);
2772 if (tabval[oval])
2773 SET_DATA_BIT(lined, j);
2774 bval = GET_DATA_BYTE(bufs2, j);
2775 tab38val = tab38[oval];
2776 if (tab38val < 0) {
2777 bval = L_MAX(0, bval + tab38val);
2778 SET_DATA_BYTE(bufs2, j, bval);
2779 } else if (tab38val > 0 ) {
2780 bval = L_MIN(255, bval + tab38val);
2781 SET_DATA_BYTE(bufs2, j, bval);
2782 }
2783 } else { /* lastlineflag == 1 */
2784 for (j = 0; j < w - 1; j++) {
2785 oval = GET_DATA_BYTE(bufs1, j);
2786 if (tabval[oval])
2787 SET_DATA_BIT(lined, j);
2788 rval = GET_DATA_BYTE(bufs1, j + 1);
2789 tab38val = tab38[oval];
2790 if (tab38val == 0)
2791 continue;
2792 if (tab38val < 0)
2793 rval = L_MAX(0, rval + tab38val);
2794 else
2795 rval = L_MIN(255, rval + tab38val);
2796 SET_DATA_BYTE(bufs1, j + 1, rval);
2797 }
2798
2799 /* do last pixel: (i, j) = (h - 1, w - 1) */
2800 oval = GET_DATA_BYTE(bufs1, j);
2801 if (tabval[oval])
2802 SET_DATA_BIT(lined, j);
2803 }
2804
2805 return;
2806}
2807
2819l_ok
2820make8To1DitherTables(l_int32 **ptabval,
2821 l_int32 **ptab38,
2822 l_int32 **ptab14,
2823 l_int32 lowerclip,
2824 l_int32 upperclip)
2825{
2826l_int32 i;
2827l_int32 *tabval, *tab38, *tab14;
2828
2829 if (ptabval) *ptabval = NULL;
2830 if (ptab38) *ptab38 = NULL;
2831 if (ptab14) *ptab14 = NULL;
2832 if (!ptabval || !ptab38 || !ptab14)
2833 return ERROR_INT("table ptrs not all defined", __func__, 1);
2834
2835 /* 3 lookup tables: 1-bit value, (3/8)excess, and (1/4)excess */
2836 tabval = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
2837 tab38 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
2838 tab14 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
2839 if (!tabval || !tab38 || !tab14)
2840 return ERROR_INT("calloc failure to make small table", __func__, 1);
2841 *ptabval = tabval;
2842 *ptab38 = tab38;
2843 *ptab14 = tab14;
2844
2845 for (i = 0; i < 256; i++) {
2846 if (i <= lowerclip) {
2847 tabval[i] = 1;
2848 tab38[i] = 0;
2849 tab14[i] = 0;
2850 } else if (i < 128) {
2851 tabval[i] = 1;
2852 tab38[i] = (3 * i + 4) / 8;
2853 tab14[i] = (i + 2) / 4;
2854 } else if (i < 255 - upperclip) {
2855 tabval[i] = 0;
2856 tab38[i] = (3 * (i - 255) + 4) / 8;
2857 tab14[i] = ((i - 255) + 2) / 4;
2858 } else { /* i >= 255 - upperclip */
2859 tabval[i] = 0;
2860 tab38[i] = 0;
2861 tab14[i] = 0;
2862 }
2863 }
2864
2865 return 0;
2866}
2867#endif /* Documentation */
PIX * pixBackgroundNormSimple(PIX *pixs, PIX *pixim, PIX *pixg)
pixBackgroundNormSimple()
Definition adaptmap.c:263
#define GET_DATA_QBIT(pdata, n)
#define SET_DATA_BIT(pdata, n)
#define SET_DATA_DIBIT(pdata, n, val)
#define SET_DATA_TWO_BYTES(pdata, n, val)
#define GET_DATA_BYTE(pdata, n)
#define GET_DATA_DIBIT(pdata, n)
#define SET_DATA_BYTE(pdata, n, val)
#define GET_DATA_BIT(pdata, n)
#define SET_DATA_QBIT(pdata, n, val)
PIX * pixThresholdGrayArb(PIX *pixs, const char *edgevals, l_int32 outdepth, l_int32 use_average, l_int32 setblack, l_int32 setwhite)
pixThresholdGrayArb()
Definition grayquant.c:1712
l_int32 * makeGrayQuantIndexTable(l_int32 nlevels)
makeGrayQuantIndexTable()
Definition grayquant.c:1820
static void thresholdToBinaryLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 d, l_int32 wpls, l_int32 thresh)
thresholdToBinaryLow()
Definition grayquant.c:492
void ditherToBinaryLineLow(l_uint32 *lined, l_int32 w, l_uint32 *bufs1, l_uint32 *bufs2, l_int32 lowerclip, l_int32 upperclip, l_int32 lastlineflag)
ditherToBinaryLineLow()
Definition grayquant.c:322
PIX * pixDitherTo2bppSpec(PIX *pixs, l_int32 lowerclip, l_int32 upperclip, l_int32 cmapflag)
pixDitherTo2bppSpec()
Definition grayquant.c:1036
PIX * pixDitherToBinarySpec(PIX *pixs, l_int32 lowerclip, l_int32 upperclip)
pixDitherToBinarySpec()
Definition grayquant.c:205
PIX * pixThresholdTo4bpp(PIX *pixs, l_int32 nlevels, l_int32 cmapflag)
pixThresholdTo4bpp()
Definition grayquant.c:1498
PIX * pixAdaptThresholdToBinaryGen(PIX *pixs, PIX *pixm, l_float32 gamma, l_int32 blackval, l_int32 whiteval, l_int32 thresh)
pixAdaptThresholdToBinaryGen()
Definition grayquant.c:760
static l_int32 make8To2DitherTables(l_int32 **ptabval, l_int32 **ptab38, l_int32 **ptab14, l_int32 cliptoblack, l_int32 cliptowhite)
make8To2DitherTables()
Definition grayquant.c:1253
PIX * pixGenerateMaskByBand32(PIX *pixs, l_uint32 refval, l_int32 delm, l_int32 delp, l_float32 fractm, l_float32 fractp)
pixGenerateMaskByBand32()
Definition grayquant.c:2095
static void thresholdTo4bppLow(l_uint32 *datad, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 wpls, l_int32 *tab)
thresholdTo4bppLow()
Definition grayquant.c:1560
PIX * pixAdaptThresholdToBinary(PIX *pixs, PIX *pixm, l_float32 gamma)
pixAdaptThresholdToBinary()
Definition grayquant.c:722
static l_int32 makeGrayQuantColormapArb(PIX *pixs, l_int32 *tab, l_int32 outdepth, PIXCMAP **pcmap)
makeGrayQuantColormapArb()
Definition grayquant.c:1992
PIX * pixGrayQuantFromHisto(PIX *pixd, PIX *pixs, PIX *pixm, l_float32 minfract, l_int32 maxsize)
pixGrayQuantFromHisto()
Definition grayquant.c:2303
static void ditherTo2bppLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 wpls, l_uint32 *bufs1, l_uint32 *bufs2, l_int32 *tabval, l_int32 *tab38, l_int32 *tab14)
ditherTo2bppLow()
Definition grayquant.c:1117
PIX * pixVarThresholdToBinary(PIX *pixs, PIX *pixg)
pixVarThresholdToBinary()
Definition grayquant.c:647
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition grayquant.c:443
PIX * pixGenerateMaskByDiscr32(PIX *pixs, l_uint32 refval1, l_uint32 refval2, l_int32 distflag)
pixGenerateMaskByDiscr32()
Definition grayquant.c:2191
PIX * pixThresholdOn8bpp(PIX *pixs, l_int32 nlevels, l_int32 cmapflag)
pixThresholdOn8bpp()
Definition grayquant.c:1613
PIX * pixDitherTo2bpp(PIX *pixs, l_int32 cmapflag)
pixDitherTo2bpp()
Definition grayquant.c:1004
static void ditherToBinaryLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 wpls, l_uint32 *bufs1, l_uint32 *bufs2, l_int32 lowerclip, l_int32 upperclip)
ditherToBinaryLow()
Definition grayquant.c:266
static l_int32 * makeGrayQuantTargetTable(l_int32 nlevels, l_int32 depth)
makeGrayQuantTargetTable()
Definition grayquant.c:1869
PIX * pixGenerateMaskByBand(PIX *pixs, l_int32 lower, l_int32 upper, l_int32 inband, l_int32 usecmap)
pixGenerateMaskByBand()
Definition grayquant.c:893
PIX * pixDitherToBinary(PIX *pixs)
pixDitherToBinary()
Definition grayquant.c:175
PIX * pixGrayQuantFromCmap(PIX *pixs, PIXCMAP *cmap, l_int32 mindepth)
pixGrayQuantFromCmap()
Definition grayquant.c:2522
static l_int32 numaFillCmapFromHisto(NUMA *na, PIXCMAP *cmap, l_float32 minfract, l_int32 maxsize, l_int32 **plut)
numaFillCmapFromHisto()
Definition grayquant.c:2433
static void thresholdTo2bppLow(l_uint32 *datad, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 wpls, l_int32 *tab)
thresholdTo2bppLow()
Definition grayquant.c:1420
l_ok makeGrayQuantTableArb(NUMA *na, l_int32 outdepth, l_int32 **ptab, PIXCMAP **pcmap)
makeGrayQuantTableArb()
Definition grayquant.c:1922
PIX * pixGenerateMaskByValue(PIX *pixs, l_int32 val, l_int32 usecmap)
pixGenerateMaskByValue()
Definition grayquant.c:804
static void ditherTo2bppLineLow(l_uint32 *lined, l_int32 w, l_uint32 *bufs1, l_uint32 *bufs2, l_int32 *tabval, l_int32 *tab38, l_int32 *tab14, l_int32 lastlineflag)
ditherTo2bppLineLow()
Definition grayquant.c:1175
PIX * pixThresholdTo2bpp(PIX *pixs, l_int32 nlevels, l_int32 cmapflag)
pixThresholdTo2bpp()
Definition grayquant.c:1358
@ DEFAULT_CLIP_UPPER_2
Definition pix.h:731
@ DEFAULT_CLIP_LOWER_1
Definition pix.h:728
@ DEFAULT_CLIP_LOWER_2
Definition pix.h:730
@ DEFAULT_CLIP_UPPER_1
Definition pix.h:729
@ REMOVE_CMAP_TO_GRAYSCALE
Definition pix.h:381
#define PIX_SRC
Definition pix.h:444
@ L_SORT_INCREASING
Definition pix.h:522
#define PIX_SET
Definition pix.h:448
@ L_EUCLIDEAN_DISTANCE
Definition pix.h:740
@ L_MANHATTAN_DISTANCE
Definition pix.h:739