• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*====================================================================*
2  -  Copyright (C) 2001 Leptonica.  All rights reserved.
3  -  This software is distributed in the hope that it will be
4  -  useful, but with NO WARRANTY OF ANY KIND.
5  -  No author or distributor accepts responsibility to anyone for the
6  -  consequences of using this software, or for whether it serves any
7  -  particular purpose or works at all, unless he or she says so in
8  -  writing.  Everyone is granted permission to copy, modify and
9  -  redistribute this source code, for commercial or non-commercial
10  -  purposes, with the following restrictions: (1) the origin of this
11  -  source code must not be misrepresented; (2) modified versions must
12  -  be plainly marked as such; and (3) this notice may not be removed
13  -  or altered from any source or modified source distribution.
14  *====================================================================*/
15 
16 
17 /*
18  *  scale.c
19  *
20  *         Top-level scaling
21  *               PIX    *pixScale()     ***
22  *               PIX    *pixScaleGeneral()     ***
23  *
24  *         Linearly interpreted (usually up-) scaling
25  *               PIX    *pixScaleLI()     ***
26  *               PIX    *pixScaleColorLI()
27  *               PIX    *pixScaleColor2xLI()   ***
28  *               PIX    *pixScaleColor4xLI()   ***
29  *               PIX    *pixScaleGrayLI()
30  *               PIX    *pixScaleGray2xLI()
31  *               PIX    *pixScaleGray4xLI()
32  *
33  *         General scaling by closest pixel sampling
34  *               PIX    *pixScaleBySampling()
35  *
36  *         Fast integer factor subsampling RGB to gray and to binary
37  *               PIX    *pixScaleRGBToGrayFast()
38  *               PIX    *pixScaleRGBToBinaryFast()
39  *               PIX    *pixScaleGrayToBinaryFast()
40  *
41  *         Downscaling with (antialias) smoothing
42  *               PIX    *pixScaleSmooth() ***
43  *               PIX    *pixScaleRGBToGray2()   [special 2x reduction to gray]
44  *
45  *         Downscaling with (antialias) area mapping
46  *               PIX    *pixScaleAreaMap()     ***
47  *               PIX    *pixScaleAreaMap2()
48  *
49  *         Binary scaling by closest pixel sampling
50  *               PIX    *pixScaleBinary()
51  *
52  *         Scale-to-gray (1 bpp --> 8 bpp, arbitrary reduction)
53  *               PIX    *pixScaleToGray()
54  *
55  *         Scale-to-gray (1 bpp --> 8 bpp, 2x reduction)
56  *               PIX    *pixScaleToGray2()
57  *
58  *         Scale-to-gray (1 bpp --> 8 bpp, 3x reduction)
59  *               PIX    *pixScaleToGray3()
60  *
61  *         Scale-to-gray (1 bpp --> 8 bpp, 4x reduction)
62  *               PIX    *pixScaleToGray4()
63  *
64  *         Scale-to-gray (1 bpp --> 8 bpp, 6x reduction)
65  *               PIX    *pixScaleToGray6()
66  *
67  *         Scale-to-gray (1 bpp --> 8 bpp, 8x reduction)
68  *               PIX    *pixScaleToGray8()
69  *
70  *         Scale-to-gray (1 bpp --> 8 bpp, 16x reduction)
71  *               PIX    *pixScaleToGray16()
72  *
73  *         Scale-to-gray by mipmap(1 bpp --> 8 bpp, arbitrary reduction)
74  *               PIX    *pixScaleToGrayMipmap()
75  *
76  *         Grayscale scaling using mipmap
77  *               PIX    *pixScaleMipmap()
78  *
79  *         Replicated (integer) expansion (all depths)
80  *               PIX    *pixExpandReplicate()
81  *
82  *         Upscale 2x followed by binarization
83  *               PIX    *pixScaleGray2xLIThresh()
84  *               PIX    *pixScaleGray2xLIDither()
85  *
86  *         Upscale 4x followed by binarization
87  *               PIX    *pixScaleGray4xLIThresh()
88  *               PIX    *pixScaleGray4xLIDither()
89  *
90  *         Grayscale downscaling using min and max
91  *               PIX    *pixScaleGrayMinMax()
92  *               PIX    *pixScaleGrayMinMax2()
93  *
94  *         Grayscale downscaling using rank value
95  *               PIX    *pixScaleGrayRankCascade()
96  *               PIX    *pixScaleGrayRank2()
97  *
98  *  *** Note: these functions make an implicit assumption about RGB
99  *            component ordering.
100  */
101 
102 #include <stdio.h>
103 #include <stdlib.h>
104 #include <string.h>
105 #include "allheaders.h"
106 
107 
108 /*------------------------------------------------------------------*
109  *                    Top level scaling dispatcher                  *
110  *------------------------------------------------------------------*/
111 /*!
112  *  pixScale()
113  *
114  *      Input:  pixs (1, 2, 4, 8, 16 and 32 bpp)
115  *              scalex, scaley
116  *      Return: pixd, or null on error
117  *
118  *  This function scales 32 bpp RGB; 2, 4 or 8 bpp palette color;
119  *  2, 4, 8 or 16 bpp gray; and binary images.
120  *
121  *  When the input has palette color, the colormap is removed and
122  *  the result is either 8 bpp gray or 32 bpp RGB, depending on whether
123  *  the colormap has color entries.  Images with 2, 4 or 16 bpp are
124  *  converted to 8 bpp.
125  *
126  *  Grayscale and color images are scaled using one of four methods,
127  *  depending on the scale factors:
128  *   (1) antialiased subsampling (lowpass filtering followed by
129  *       subsampling, implemented here by area mapping), for scale factors
130  *       less than 0.7
131  *   (2) linear interpolation with sharpening, for scale factors between
132  *       0.7 and 1.4
133  *   (3) linear interpolation alone, for scale factors >= 1.4.
134  *
135  *  One could use subsampling for scale factors very close to 1.0,
136  *  because it preserves sharp edges.  Linear interpolation blurs
137  *  edges because the dest pixels will typically straddle two src edge
138  *  pixels.  Subsmpling removes entire columns and rows, so the edge is
139  *  not blurred.  However, there are two reasons for not doing this.
140  *  First, it moves edges, so that a straight line at a large angle to
141  *  both horizontal and vertical will have noticable kinks where
142  *  horizontal and vertical rasters are removed.  Second, although it
143  *  is very fast, you get good results on sharp edges by applying
144  *  a sharpening filter.
145  *
146  *  For images with sharp edges, sharpening substantially improves the
147  *  image quality for scale factors between 0.7 and about 2.0.  However,
148  *  the generic sharpening operation is about 3 times slower than linear
149  *  interpolation, so there is a speed-vs-quality tradeoff.  (Note: the
150  *  special cases where the sharpening halfwidth is 1 or 2 are about
151  *  twice as fast as the general case).  When the scale factor is
152  *  larger than 1.4, the cost, which is proportional to image area, is very
153  *  large for the incremental quality improvement, so we cut off the
154  *  use of sharpening at 1.4.  For scale factors greater than 1.4,
155  *  these high-level scaling functions only do linear interpolation.
156  *
157  *  Because sharpening is computationally expensive, we provide the
158  *  option of not doing it.  To avoid sharpening, call pixScaleGeneral()
159  *  with @sharpfract == 0.0.  Note that pixScale() calls with default
160  *  sharpening factors: @sharpwidth = 2, @sharpfract = 0.4.  The results
161  *  are generally better with a small amount of sharpening because
162  *  it strengthens edge pixels that are weak due to anti-aliasing.
163  *
164  *  Binary images are scaled by sampling the closest pixel, without
165  *  any low-pass filtering (averaging of neighboring pixels).
166  *  This will introduce aliasing for reductions, which can be
167  *  prevented by using pixScaleToGray() instead.
168  *
169  *  *** Warning: implicit assumption about RGB component order
170  *               for LI color scaling
171  */
172 PIX *
pixScale(PIX * pixs,l_float32 scalex,l_float32 scaley)173 pixScale(PIX       *pixs,
174          l_float32  scalex,
175          l_float32  scaley)
176 {
177     return pixScaleGeneral(pixs, scalex, scaley, 0.4, 2);
178 }
179 
180 
181 /*!
182  *  pixScaleGeneral()
183  *
184  *      Input:  pixs (1, 2, 4, 8, 16 and 32 bpp)
185  *              scalex, scaley
186  *              sharpfract (use 0.0 to skip sharpening)
187  *              sharpwidth (halfwidth of low-pass filter; typ. 1 or 2)
188  *      Return: pixd, or null on error
189  *
190  *  Notes:
191  *      (1) See pixScale() for usage.
192  *      (2) This interface may change in the future, as other special
193  *          cases are added.
194  *      (3) Call this function with @sharpfract == 0.0 to avoid sharpening
195  *          for grayscale and color images with scaling factors between
196  *          0.7 and 1.4.
197  */
198 PIX *
pixScaleGeneral(PIX * pixs,l_float32 scalex,l_float32 scaley,l_float32 sharpfract,l_int32 sharpwidth)199 pixScaleGeneral(PIX       *pixs,
200                 l_float32  scalex,
201                 l_float32  scaley,
202                 l_float32  sharpfract,
203                 l_int32    sharpwidth)
204 {
205 l_int32    d;
206 l_float32  maxscale;
207 PIX       *pixt, *pixt2, *pixd;
208 
209     PROCNAME("pixScaleGeneral");
210 
211     if (!pixs)
212         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
213     d = pixGetDepth(pixs);
214     if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
215         return (PIX *)ERROR_PTR("pixs not {1,2,4,8,16,32} bpp", procName, NULL);
216     if (scalex == 1.0 && scaley == 1.0)
217         return pixCopy(NULL, pixs);
218 
219     if (d == 1)
220         return pixScaleBinary(pixs, scalex, scaley);
221 
222         /* Remove colormap; clone if possible; result is either 8 or 32 bpp */
223     if ((pixt = pixConvertTo8Or32(pixs, 0, 1)) == NULL)
224         return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
225 
226         /* Scale (up or down) */
227     d = pixGetDepth(pixt);
228     maxscale = L_MAX(scalex, scaley);
229     if (maxscale < 0.7) {  /* area mapping for anti-aliasing */
230         pixd = pixScaleAreaMap(pixt, scalex, scaley);
231     }
232     else {  /* use linear interpolation */
233         if (d == 8)
234             pixt2 = pixScaleGrayLI(pixt, scalex, scaley);
235         else  /* d == 32 */
236             pixt2 = pixScaleColorLI(pixt, scalex, scaley);
237         if (maxscale < 1.4 && sharpfract > 0.0 && sharpwidth > 0)
238             pixd = pixUnsharpMasking(pixt2, sharpwidth, sharpfract);
239         else
240             pixd = pixClone(pixt2);
241         pixDestroy(&pixt2);
242     }
243 
244     pixDestroy(&pixt);
245     return pixd;
246 }
247 
248 
249 /*------------------------------------------------------------------*
250  *                  Scaling by linear interpolation                 *
251  *------------------------------------------------------------------*/
252 /*!
253  *  pixScaleLI()
254  *
255  *      Input:  pixs (2, 4, 8 or 32 bpp; with or without colormap)
256  *              scalex, scaley (must both be >= 0.7)
257  *      Return: pixd, or null on error
258  *
259  *  Notes:
260  *      (1) This function should only be used when the scale factors are
261  *          greater than or equal to 0.7, and typically greater than 1.
262  *          If either scale factor is smaller than 0.7, we issue a warning
263  *          and invoke pixScale().
264  *      (2) This works on 2, 4, 8, 16 and 32 bpp images, as well as on
265  *          2, 4 and 8 bpp images that have a colormap.  If there is a
266  *          colormap, it is removed to either gray or RGB, depending
267  *          on the colormap.
268  *      (3) The does a linear interpolation on the src image.
269  *      (4) It dispatches to much faster implementations for
270  *          the special cases of 2x and 4x expansion.
271  *
272  *  *** Warning: implicit assumption about RGB component ordering ***
273  */
274 PIX *
pixScaleLI(PIX * pixs,l_float32 scalex,l_float32 scaley)275 pixScaleLI(PIX       *pixs,
276            l_float32  scalex,
277            l_float32  scaley)
278 {
279 l_int32  d;
280 PIX     *pixt, *pixd;
281 
282     PROCNAME("pixScaleLI");
283 
284     if (!pixs || (pixGetDepth(pixs) == 1))
285         return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", procName, NULL);
286     if (scalex < 0.7 || scaley < 0.7) {
287         L_WARNING("scaling factor < 0.7; doing regular scaling", procName);
288         return pixScale(pixs, scalex, scaley);
289     }
290     d = pixGetDepth(pixs);
291     if (d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
292         return (PIX *)ERROR_PTR("pixs not {2,4,8,16,32} bpp", procName, NULL);
293 
294         /* Remove colormap; clone if possible; result is either 8 or 32 bpp */
295     if ((pixt = pixConvertTo8Or32(pixs, 0, 1)) == NULL)
296         return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
297 
298     d = pixGetDepth(pixt);
299     if (d == 8)
300         pixd = pixScaleGrayLI(pixt, scalex, scaley);
301     else if (d == 32)
302         pixd = pixScaleColorLI(pixt, scalex, scaley);
303 
304     pixDestroy(&pixt);
305     return pixd;
306 }
307 
308 
309 /*!
310  *  pixScaleColorLI()
311  *
312  *      Input:  pixs  (32 bpp, representing rgb)
313  *              scalex, scaley
314  *      Return: pixd, or null on error
315  *
316  *  Notes:
317  *      (1) If this is used for scale factors less than 0.7,
318  *          it will suffer from antialiasing.  A warning is issued.
319  *          Particularly for document images with sharp edges,
320  *          use pixScaleSmooth() or pixScaleAreaMap() instead.
321  *      (2) For the general case, it's about 4x faster to manipulate
322  *          the color pixels directly, rather than to make images
323  *          out of each of the 3 components, scale each component
324  *          using the pixScaleGrayLI(), and combine the results back
325  *          into an rgb image.
326  *      (3) The speed on intel hardware for the general case (not 2x)
327  *          is about 10 * 10^6 dest-pixels/sec/GHz.  (The special 2x
328  *          case runs at about 80 * 10^6 dest-pixels/sec/GHz.)
329  */
330 PIX *
pixScaleColorLI(PIX * pixs,l_float32 scalex,l_float32 scaley)331 pixScaleColorLI(PIX      *pixs,
332                l_float32  scalex,
333                l_float32  scaley)
334 {
335 l_int32    ws, hs, wpls, wd, hd, wpld;
336 l_uint32  *datas, *datad;
337 PIX       *pixd;
338 
339     PROCNAME("pixScaleColorLI");
340 
341     if (!pixs || (pixGetDepth(pixs) != 32))
342         return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
343     if (scalex < 0.7 || scaley < 0.7)
344         L_WARNING("scaling factor < 0.7; should use area map", procName);
345 
346         /* Do fast special cases if possible */
347     if (scalex == 1.0 && scaley == 1.0)
348         return pixCopy(NULL, pixs);
349     if (scalex == 2.0 && scaley == 2.0)
350         return pixScaleColor2xLI(pixs);
351     if (scalex == 4.0 && scaley == 4.0)
352         return pixScaleColor4xLI(pixs);
353 
354         /* General case */
355     pixGetDimensions(pixs, &ws, &hs, NULL);
356     datas = pixGetData(pixs);
357     wpls = pixGetWpl(pixs);
358     wd = (l_int32)(scalex * (l_float32)ws + 0.5);
359     hd = (l_int32)(scaley * (l_float32)hs + 0.5);
360     if ((pixd = pixCreate(wd, hd, 32)) == NULL)
361         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
362     pixCopyResolution(pixd, pixs);
363     pixScaleResolution(pixd, scalex, scaley);
364     datad = pixGetData(pixd);
365     wpld = pixGetWpl(pixd);
366     scaleColorLILow(datad, wd, hd, wpld, datas, ws, hs, wpls);
367     return pixd;
368 }
369 
370 
371 /*!
372  *  pixScaleColor2xLI()
373  *
374  *      Input:  pixs  (32 bpp, representing rgb)
375  *      Return: pixd, or null on error
376  *
377  *  Notes:
378  *      (1) This is a special case of linear interpolated scaling,
379  *          for 2x upscaling.  It is about 8x faster than using
380  *          the generic pixScaleColorLI(), and about 4x faster than
381  *          using the special 2x scale function pixScaleGray2xLI()
382  *          on each of the three components separately.
383  *      (2) The speed on intel hardware is about
384  *          80 * 10^6 dest-pixels/sec/GHz (!!)
385  *
386  *  *** Warning: implicit assumption about RGB component ordering ***
387  */
388 PIX *
pixScaleColor2xLI(PIX * pixs)389 pixScaleColor2xLI(PIX  *pixs)
390 {
391 l_int32    ws, hs, wpls, wpld;
392 l_uint32  *datas, *datad;
393 PIX       *pixd;
394 
395     PROCNAME("pixScaleColor2xLI");
396 
397     if (!pixs || (pixGetDepth(pixs) != 32))
398         return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
399 
400     pixGetDimensions(pixs, &ws, &hs, NULL);
401     datas = pixGetData(pixs);
402     wpls = pixGetWpl(pixs);
403     if ((pixd = pixCreate(2 * ws, 2 * hs, 32)) == NULL)
404         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
405     pixCopyResolution(pixd, pixs);
406     pixScaleResolution(pixd, 2.0, 2.0);
407     datad = pixGetData(pixd);
408     wpld = pixGetWpl(pixd);
409     scaleColor2xLILow(datad, wpld, datas, ws, hs, wpls);
410     return pixd;
411 }
412 
413 
414 /*!
415  *  pixScaleColor4xLI()
416  *
417  *      Input:  pixs  (32 bpp, representing rgb)
418  *      Return: pixd, or null on error
419  *
420  *  Notes:
421  *      (1) This is a special case of color linear interpolated scaling,
422  *          for 4x upscaling.  It is about 3x faster than using
423  *          the generic pixScaleColorLI().
424  *      (2) The speed on intel hardware is about
425  *          30 * 10^6 dest-pixels/sec/GHz
426  *      (3) This scales each component separately, using pixScaleGray4xLI().
427  *          It would be about 4x faster to inline the color code properly,
428  *          in analogy to scaleColor4xLILow(), and I leave this as
429  *          an exercise for someone who really needs it.
430  */
431 PIX *
pixScaleColor4xLI(PIX * pixs)432 pixScaleColor4xLI(PIX  *pixs)
433 {
434 PIX  *pixr, *pixg, *pixb;
435 PIX  *pixrs, *pixgs, *pixbs;
436 PIX  *pixd;
437 
438     PROCNAME("pixScaleColor4xLI");
439 
440     if (!pixs || (pixGetDepth(pixs) != 32))
441         return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
442 
443     pixr = pixGetRGBComponent(pixs, COLOR_RED);
444     pixrs = pixScaleGray4xLI(pixr);
445     pixDestroy(&pixr);
446     pixg = pixGetRGBComponent(pixs, COLOR_GREEN);
447     pixgs = pixScaleGray4xLI(pixg);
448     pixDestroy(&pixg);
449     pixb = pixGetRGBComponent(pixs, COLOR_BLUE);
450     pixbs = pixScaleGray4xLI(pixb);
451     pixDestroy(&pixb);
452 
453     if ((pixd = pixCreateRGBImage(pixrs, pixgs, pixbs)) == NULL)
454         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
455 
456     pixDestroy(&pixrs);
457     pixDestroy(&pixgs);
458     pixDestroy(&pixbs);
459     return pixd;
460 }
461 
462 
463 /*!
464  *  pixScaleGrayLI()
465  *
466  *      Input:  pixs (8 bpp grayscale)
467  *              scalex
468  *              scaley
469  *      Return: pixd, or null on error
470  *
471  *  This function is appropriate for upscaling
472  *  (magnification: scale factors > 1), and for a
473  *  small amount of downscaling (reduction: scale
474  *  factors > 0.5).   For scale factors less than 0.5,
475  *  the best result is obtained by area mapping,
476  *  but this is very expensive.  So for such large
477  *  reductions, it is more appropriate to do low pass
478  *  filtering followed by subsampling, a combination
479  *  which is effectively a cheap form of area mapping.
480  *
481  *  Some details follow.
482  *
483  *  For each pixel in the dest, this does a linear
484  *  interpolation of 4 neighboring pixels in the src.
485  *  Specifically, consider the UL corner of src and
486  *  dest pixels.  The UL corner of the dest falls within
487  *  a src pixel, whose four corners are the UL corners
488  *  of 4 adjacent src pixels.  The value of the dest
489  *  is taken by linear interpolation using the values of
490  *  the four src pixels and the distance of the UL corner
491  *  of the dest from each corner.
492  *
493  *  If the image is expanded so that the dest pixel is
494  *  smaller than the src pixel, such interpolation
495  *  is a reasonable approach.  This interpolation is
496  *  also good for a small image reduction factor that
497  *  is not more than a 2x reduction.
498  *
499  *  Note that the linear interpolation algorithm for scaling
500  *  is identical in form to the area-mapping algorithm
501  *  for grayscale rotation.  The latter corresponds to a
502  *  translation of each pixel without scaling.
503  *
504  *  This function is NOT optimal if the scaling involves
505  *  a large reduction.    If the image is significantly
506  *  reduced, so that the dest pixel is much larger than
507  *  the src pixels, this interpolation, which is over src
508  *  pixels only near the UL corner of the dest pixel,
509  *  is not going to give a good area-mapping average.
510  *  Because area mapping for image scaling is considerably
511  *  more computationally intensive than linear interpolation,
512  *  we choose not to use it.   For large image reduction,
513  *  linear interpolation over adjacent src pixels
514  *  degenerates asymptotically to subsampling.  But
515  *  subsampling without a low-pass pre-filter causes
516  *  aliasing by the nyquist theorem.  To avoid aliasing,
517  *  a low-pass filter (e.g., an averaging filter) of
518  *  size roughly equal to the dest pixel (i.e., the
519  *  reduction factor) should be applied to the src before
520  *  subsampling.
521  *
522  *  As an alternative to low-pass filtering and subsampling
523  *  for large reduction factors, linear interpolation can
524  *  also be done between the (widely separated) src pixels in
525  *  which the corners of the dest pixel lie.  This also is
526  *  not optimal, as it samples src pixels only near the
527  *  corners of the dest pixel, and it is not implemented.
528  *
529  *  Summary:
530  *    (1) If this is used for scale factors less than 0.7,
531  *        it will suffer from antialiasing.  A warning is issued.
532  *        Particularly for document images with sharp edges,
533  *        use pixScaleSmooth() or pixScaleAreaMap() instead.
534  *    (2) The speed on intel hardware for the general case (not 2x)
535  *        is about 13 * 10^6 dest-pixels/sec/GHz.  (The special 2x
536  *        case runs at about 100 * 10^6 dest-pixels/sec/GHz.)
537  */
538 PIX *
pixScaleGrayLI(PIX * pixs,l_float32 scalex,l_float32 scaley)539 pixScaleGrayLI(PIX       *pixs,
540                l_float32  scalex,
541                l_float32  scaley)
542 {
543 l_int32    ws, hs, wpls, wd, hd, wpld;
544 l_uint32  *datas, *datad;
545 PIX       *pixd;
546 
547     PROCNAME("pixScaleGrayLI");
548 
549     if (!pixs || (pixGetDepth(pixs) != 8))
550         return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
551     if (scalex < 0.7 || scaley < 0.7)
552         L_WARNING("scaling factor < 0.7; should use area map", procName);
553     if (pixGetColormap(pixs))
554         L_WARNING("pix has colormap; poor results are likely", procName);
555 
556         /* Do fast special cases if possible */
557     if (scalex == 1.0 && scaley == 1.0)
558         return pixCopy(NULL, pixs);
559     if (scalex == 2.0 && scaley == 2.0)
560         return pixScaleGray2xLI(pixs);
561     if (scalex == 4.0 && scaley == 4.0)
562         return pixScaleGray4xLI(pixs);
563 
564         /* General case */
565     pixGetDimensions(pixs, &ws, &hs, NULL);
566     datas = pixGetData(pixs);
567     wpls = pixGetWpl(pixs);
568     wd = (l_int32)(scalex * (l_float32)ws + 0.5);
569     hd = (l_int32)(scaley * (l_float32)hs + 0.5);
570     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
571         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
572     pixCopyResolution(pixd, pixs);
573     pixScaleResolution(pixd, scalex, scaley);
574     datad = pixGetData(pixd);
575     wpld = pixGetWpl(pixd);
576     scaleGrayLILow(datad, wd, hd, wpld, datas, ws, hs, wpls);
577     return pixd;
578 }
579 
580 
581 /*!
582  *  pixScaleGray2xLI()
583  *
584  *      Input:  pixs (8 bpp grayscale)
585  *      Return: pixd, or null on error
586  *
587  *  Notes:
588  *      (1) This is a special case of gray linear interpolated scaling,
589  *          for 2x upscaling.  It is about 6x faster than using
590  *          the generic pixScaleGrayLI().
591  *      (2) The speed on intel hardware is about
592  *          100 * 10^6 dest-pixels/sec/GHz
593  */
594 PIX *
pixScaleGray2xLI(PIX * pixs)595 pixScaleGray2xLI(PIX  *pixs)
596 {
597 l_int32    ws, hs, wpls, wpld;
598 l_uint32  *datas, *datad;
599 PIX       *pixd;
600 
601     PROCNAME("pixScaleGray2xLI");
602 
603     if (!pixs || (pixGetDepth(pixs) != 8))
604         return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
605     if (pixGetColormap(pixs))
606         L_WARNING("pix has colormap", procName);
607 
608     pixGetDimensions(pixs, &ws, &hs, NULL);
609     datas = pixGetData(pixs);
610     wpls = pixGetWpl(pixs);
611     if ((pixd = pixCreate(2 * ws, 2 * hs, 8)) == NULL)
612         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
613     pixCopyResolution(pixd, pixs);
614     pixScaleResolution(pixd, 2.0, 2.0);
615     datad = pixGetData(pixd);
616     wpld = pixGetWpl(pixd);
617     scaleGray2xLILow(datad, wpld, datas, ws, hs, wpls);
618     return pixd;
619 }
620 
621 
622 /*!
623  *  pixScaleGray4xLI()
624  *
625  *      Input:  pixs (8 bpp grayscale)
626  *      Return: pixd, or null on error
627  *
628  *  Notes:
629  *      (1) This is a special case of gray linear interpolated scaling,
630  *          for 4x upscaling.  It is about 12x faster than using
631  *          the generic pixScaleGrayLI().
632  *      (2) The speed on intel hardware is about
633  *          160 * 10^6 dest-pixels/sec/GHz  (!!)
634  */
635 PIX *
pixScaleGray4xLI(PIX * pixs)636 pixScaleGray4xLI(PIX  *pixs)
637 {
638 l_int32    ws, hs, wpls, wpld;
639 l_uint32  *datas, *datad;
640 PIX       *pixd;
641 
642     PROCNAME("pixScaleGray4xLI");
643 
644     if (!pixs || (pixGetDepth(pixs) != 8))
645         return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
646     if (pixGetColormap(pixs))
647         L_WARNING("pix has colormap", procName);
648 
649     pixGetDimensions(pixs, &ws, &hs, NULL);
650     datas = pixGetData(pixs);
651     wpls = pixGetWpl(pixs);
652     if ((pixd = pixCreate(4 * ws, 4 * hs, 8)) == NULL)
653         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
654     pixCopyResolution(pixd, pixs);
655     pixScaleResolution(pixd, 4.0, 4.0);
656     datad = pixGetData(pixd);
657     wpld = pixGetWpl(pixd);
658     scaleGray4xLILow(datad, wpld, datas, ws, hs, wpls);
659     return pixd;
660 }
661 
662 
663 
664 /*------------------------------------------------------------------*
665  *              General scaling by closest pixel sampling           *
666  *------------------------------------------------------------------*/
667 /*!
668  *  pixScaleBySampling()
669  *
670  *      Input:  pixs (1, 2, 4, 8, 16, 32 bpp)
671  *              scalex, scaley
672  *      Return: pixd, or null on error
673  *
674  *  Notes:
675  *      (1) This function samples from the source without
676  *          filtering.  As a result, aliasing will occur for
677  *          subsampling (scalex and/or scaley < 1.0).
678  */
679 PIX *
pixScaleBySampling(PIX * pixs,l_float32 scalex,l_float32 scaley)680 pixScaleBySampling(PIX       *pixs,
681                    l_float32  scalex,
682                    l_float32  scaley)
683 {
684 l_int32    ws, hs, d, wpls, wd, hd, wpld;
685 l_uint32  *datas, *datad;
686 PIX       *pixd;
687 
688     PROCNAME("pixScaleBySampling");
689 
690     if (!pixs)
691         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
692     if (scalex == 1.0 && scaley == 1.0)
693         return pixCopy(NULL, pixs);
694     if ((d = pixGetDepth(pixs)) == 1)
695         return pixScaleBinary(pixs, scalex, scaley);
696 
697     pixGetDimensions(pixs, &ws, &hs, NULL);
698     datas = pixGetData(pixs);
699     wpls = pixGetWpl(pixs);
700     wd = (l_int32)(scalex * (l_float32)ws + 0.5);
701     hd = (l_int32)(scaley * (l_float32)hs + 0.5);
702     if ((pixd = pixCreate(wd, hd, d)) == NULL)
703         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
704     pixCopyResolution(pixd, pixs);
705     pixScaleResolution(pixd, scalex, scaley);
706     pixCopyColormap(pixd, pixs);
707     datad = pixGetData(pixd);
708     wpld = pixGetWpl(pixd);
709     scaleBySamplingLow(datad, wd, hd, wpld, datas, ws, hs, d, wpls);
710     return pixd;
711 }
712 
713 
714 /*------------------------------------------------------------------*
715  *            Fast integer factor subsampling RGB to gray           *
716  *------------------------------------------------------------------*/
717 /*!
718  *  pixScaleRGBToGrayFast()
719  *
720  *      Input:  pixs (32 bpp rgb)
721  *              factor (integer reduction factor >= 1)
722  *              color (one of COLOR_RED, COLOR_GREEN, COLOR_BLUE)
723  *      Return: pixd (8 bpp), or null on error
724  *
725  *  Notes:
726  *      (1) This does simultaneous subsampling by an integer factor and
727  *          extraction of the color from the RGB pix.
728  *      (2) It is designed for maximum speed, and is used for quickly
729  *          generating a downsized grayscale image from a higher resolution
730  *          RGB image.  This would typically be used for image analysis.
731  *      (3) The standard color byte order (RGBA) is assumed.
732  */
733 PIX *
pixScaleRGBToGrayFast(PIX * pixs,l_int32 factor,l_int32 color)734 pixScaleRGBToGrayFast(PIX     *pixs,
735                       l_int32  factor,
736                       l_int32  color)
737 {
738 l_int32    byteval, shift;
739 l_int32    i, j, ws, hs, wd, hd, wpls, wpld;
740 l_uint32  *datas, *words, *datad, *lined;
741 l_float32  scale;
742 PIX       *pixd;
743 
744     PROCNAME("pixScaleRGBToGrayFast");
745 
746     if (!pixs)
747         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
748     if (pixGetDepth(pixs) != 32)
749         return (PIX *)ERROR_PTR("depth not 32 bpp", procName, NULL);
750     if (factor < 1)
751         return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL);
752 
753     if (color == COLOR_RED)
754         shift = L_RED_SHIFT;
755     else if (color == COLOR_GREEN)
756         shift = L_GREEN_SHIFT;
757     else if (color == COLOR_BLUE)
758         shift = L_BLUE_SHIFT;
759     else
760         return (PIX *)ERROR_PTR("invalid color", procName, NULL);
761 
762     pixGetDimensions(pixs, &ws, &hs, NULL);
763     datas = pixGetData(pixs);
764     wpls = pixGetWpl(pixs);
765 
766     wd = ws / factor;
767     hd = hs / factor;
768     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
769         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
770     pixCopyResolution(pixd, pixs);
771     scale = 1. / (l_float32) factor;
772     pixScaleResolution(pixd, scale, scale);
773     datad = pixGetData(pixd);
774     wpld = pixGetWpl(pixd);
775 
776     for (i = 0; i < hd; i++) {
777         words = datas + i * factor * wpls;
778         lined = datad + i * wpld;
779         for (j = 0; j < wd; j++, words += factor) {
780             byteval = ((*words) >> shift) & 0xff;
781             SET_DATA_BYTE(lined, j, byteval);
782         }
783     }
784 
785     return pixd;
786 }
787 
788 
789 /*!
790  *  pixScaleRGBToBinaryFast()
791  *
792  *      Input:  pixs (32 bpp RGB)
793  *              factor (integer reduction factor >= 1)
794  *              thresh (binarization threshold)
795  *      Return: pixd (1 bpp), or null on error
796  *
797  *  Notes:
798  *      (1) This does simultaneous subsampling by an integer factor and
799  *          conversion from RGB to gray to binary.
800  *      (2) It is designed for maximum speed, and is used for quickly
801  *          generating a downsized binary image from a higher resolution
802  *          RGB image.  This would typically be used for image analysis.
803  *      (3) It uses the green channel to represent the RGB pixel intensity.
804  */
805 PIX *
pixScaleRGBToBinaryFast(PIX * pixs,l_int32 factor,l_int32 thresh)806 pixScaleRGBToBinaryFast(PIX     *pixs,
807                         l_int32  factor,
808                         l_int32  thresh)
809 {
810 l_int32    byteval;
811 l_int32    i, j, ws, hs, wd, hd, wpls, wpld;
812 l_uint32  *datas, *words, *datad, *lined;
813 l_float32  scale;
814 PIX       *pixd;
815 
816     PROCNAME("pixScaleRGBToBinaryFast");
817 
818     if (!pixs)
819         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
820     if (factor < 1)
821         return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL);
822     if (pixGetDepth(pixs) != 32)
823         return (PIX *)ERROR_PTR("depth not 32 bpp", procName, NULL);
824 
825     pixGetDimensions(pixs, &ws, &hs, NULL);
826     datas = pixGetData(pixs);
827     wpls = pixGetWpl(pixs);
828 
829     wd = ws / factor;
830     hd = hs / factor;
831     if ((pixd = pixCreate(wd, hd, 1)) == NULL)
832         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
833     pixCopyResolution(pixd, pixs);
834     scale = 1. / (l_float32) factor;
835     pixScaleResolution(pixd, scale, scale);
836     datad = pixGetData(pixd);
837     wpld = pixGetWpl(pixd);
838 
839     for (i = 0; i < hd; i++) {
840         words = datas + i * factor * wpls;
841         lined = datad + i * wpld;
842         for (j = 0; j < wd; j++, words += factor) {
843             byteval = ((*words) >> L_GREEN_SHIFT) & 0xff;
844             if (byteval < thresh)
845                 SET_DATA_BIT(lined, j);
846         }
847     }
848 
849     return pixd;
850 }
851 
852 
853 /*!
854  *  pixScaleGrayToBinaryFast()
855  *
856  *      Input:  pixs (8 bpp grayscale)
857  *              factor (integer reduction factor >= 1)
858  *              thresh (binarization threshold)
859  *      Return: pixd (1 bpp), or null on error
860  *
861  *  Notes:
862  *      (1) This does simultaneous subsampling by an integer factor and
863  *          thresholding from gray to binary.
864  *      (2) It is designed for maximum speed, and is used for quickly
865  *          generating a downsized binary image from a higher resolution
866  *          gray image.  This would typically be used for image analysis.
867  */
868 PIX *
pixScaleGrayToBinaryFast(PIX * pixs,l_int32 factor,l_int32 thresh)869 pixScaleGrayToBinaryFast(PIX     *pixs,
870                          l_int32  factor,
871                          l_int32  thresh)
872 {
873 l_int32    byteval;
874 l_int32    i, j, ws, hs, wd, hd, wpls, wpld, sj;
875 l_uint32  *datas, *datad, *lines, *lined;
876 l_float32  scale;
877 PIX       *pixd;
878 
879     PROCNAME("pixScaleGrayToBinaryFast");
880 
881     if (!pixs)
882         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
883     if (factor < 1)
884         return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL);
885     if (pixGetDepth(pixs) != 8)
886         return (PIX *)ERROR_PTR("depth not 8 bpp", procName, NULL);
887 
888     pixGetDimensions(pixs, &ws, &hs, NULL);
889     datas = pixGetData(pixs);
890     wpls = pixGetWpl(pixs);
891 
892     wd = ws / factor;
893     hd = hs / factor;
894     if ((pixd = pixCreate(wd, hd, 1)) == NULL)
895         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
896     pixCopyResolution(pixd, pixs);
897     scale = 1. / (l_float32) factor;
898     pixScaleResolution(pixd, scale, scale);
899     datad = pixGetData(pixd);
900     wpld = pixGetWpl(pixd);
901 
902     for (i = 0; i < hd; i++) {
903         lines = datas + i * factor * wpls;
904         lined = datad + i * wpld;
905         for (j = 0, sj = 0; j < wd; j++, sj += factor) {
906             byteval = GET_DATA_BYTE(lines, sj);
907             if (byteval < thresh)
908                 SET_DATA_BIT(lined, j);
909         }
910     }
911 
912     return pixd;
913 }
914 
915 
916 /*------------------------------------------------------------------*
917  *               Downscaling with (antialias) smoothing             *
918  *------------------------------------------------------------------*/
919 /*!
920  *  pixScaleSmooth()
921  *
922  *      Input:  pixs (2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap)
923  *              scalex, scaley (must both be <= 0.7)
924  *      Return: pixd, or null on error
925  *
926  *  Notes:
927  *      (1) This function should only be used when the scale factors are less
928  *          than or equal to 0.7 (i.e., more than about 1.42x reduction).
929  *          If either scale factor is larger than 0.7, we issue a warning
930  *          and invoke pixScale().
931  *      (2) This works only on 2, 4, 8 and 32 bpp images, and if there is
932  *          a colormap, it is removed by converting to RGB.  In other
933  *          cases, we issue a warning and invoke pixScale().
934  *      (3) It does simple (flat filter) convolution, with a filter size
935  *          commensurate with the amount of reduction, to avoid antialiasing.
936  *      (4) It does simple subsampling after smoothing, which is appropriate
937  *          for this range of scaling.  Linear interpolation gives essentially
938  *          the same result with more computation for these scale factors,
939  *          so we don't use it.
940  *      (5) The result is the same as doing a full block convolution followed by
941  *          subsampling, but this is faster because the results of the block
942  *          convolution are only computed at the subsampling locations.
943  *          In fact, the computation time is approximately independent of
944  *          the scale factor, because the convolution kernel is adjusted
945  *          so that each source pixel is summed approximately once.
946  *
947  *  *** Warning: implicit assumption about RGB component ordering ***
948  */
949 PIX *
pixScaleSmooth(PIX * pix,l_float32 scalex,l_float32 scaley)950 pixScaleSmooth(PIX       *pix,
951                l_float32  scalex,
952                l_float32  scaley)
953 {
954 l_int32    ws, hs, d, wd, hd, wpls, wpld, isize;
955 l_uint32  *datas, *datad;
956 l_float32  minscale, size;
957 PIX       *pixs, *pixd;
958 
959     PROCNAME("pixScaleSmooth");
960 
961     if (!pix)
962         return (PIX *)ERROR_PTR("pix not defined", procName, NULL);
963     if (scalex > 0.7 || scaley > 0.7) {
964         L_WARNING("scaling factor not <= 0.7; doing regular scaling", procName);
965         return pixScale(pix, scalex, scaley);
966     }
967 
968         /* Remove colormap if necessary.
969          * If 2 bpp or 4 bpp gray, convert to 8 bpp */
970     d = pixGetDepth(pix);
971     if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) {
972         L_WARNING("pix has colormap; removing", procName);
973         pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
974         d = pixGetDepth(pixs);
975     }
976     else if (d == 2 || d == 4) {
977         pixs = pixConvertTo8(pix, FALSE);
978         d = 8;
979     }
980     else
981         pixs = pixClone(pix);
982 
983     if (d != 8 && d != 32) {   /* d == 1 or d == 16 */
984         L_WARNING("depth not 8 or 32 bpp; doing regular scaling", procName);
985         pixDestroy(&pixs);
986         return pixScale(pix, scalex, scaley);
987     }
988 
989         /* If 1.42 < 1/minscale < 2.5, use isize = 2
990          * If 2.5 =< 1/minscale < 3.5, use isize = 3, etc.
991          * Under no conditions use isize < 2  */
992     minscale = L_MIN(scalex, scaley);
993     size = 1.0 / minscale;   /* ideal filter full width */
994     isize = L_MAX(2, (l_int32)(size + 0.5));
995 
996     pixGetDimensions(pixs, &ws, &hs, NULL);
997     if ((ws < isize) || (hs < isize)) {
998         pixDestroy(&pixs);
999         return (PIX *)ERROR_PTR("pixs too small", procName, NULL);
1000     }
1001     datas = pixGetData(pixs);
1002     wpls = pixGetWpl(pixs);
1003     wd = (l_int32)(scalex * (l_float32)ws + 0.5);
1004     hd = (l_int32)(scaley * (l_float32)hs + 0.5);
1005     if (wd < 1 || hd < 1) {
1006         pixDestroy(&pixs);
1007         return (PIX *)ERROR_PTR("pixd too small", procName, NULL);
1008     }
1009     if ((pixd = pixCreate(wd, hd, d)) == NULL) {
1010         pixDestroy(&pixs);
1011         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1012     }
1013     pixCopyResolution(pixd, pixs);
1014     pixScaleResolution(pixd, scalex, scaley);
1015     datad = pixGetData(pixd);
1016     wpld = pixGetWpl(pixd);
1017     scaleSmoothLow(datad, wd, hd, wpld, datas, ws, hs, d, wpls, isize);
1018 
1019     pixDestroy(&pixs);
1020     return pixd;
1021 }
1022 
1023 
1024 /*!
1025  *  pixScaleRGBToGray2()
1026  *
1027  *      Input:  pixs (32 bpp rgb)
1028  *              rwt, gwt, bwt (must sum to 1.0)
1029  *      Return: pixd, (8 bpp, 2x reduced), or null on error
1030  */
1031 PIX *
pixScaleRGBToGray2(PIX * pixs,l_float32 rwt,l_float32 gwt,l_float32 bwt)1032 pixScaleRGBToGray2(PIX       *pixs,
1033                    l_float32  rwt,
1034                    l_float32  gwt,
1035                    l_float32  bwt)
1036 {
1037 l_int32    wd, hd, wpls, wpld;
1038 l_uint32  *datas, *datad;
1039 PIX       *pixd;
1040 
1041     PROCNAME("pixScaleRGBToGray2");
1042 
1043     if (!pixs)
1044         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1045     if (pixGetDepth(pixs) != 32)
1046         return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
1047     if (rwt + gwt + bwt < 0.98 || rwt + gwt + bwt > 1.02)
1048         return (PIX *)ERROR_PTR("sum of wts should be 1.0", procName, NULL);
1049 
1050     wd = pixGetWidth(pixs) / 2;
1051     hd = pixGetHeight(pixs) / 2;
1052     wpls = pixGetWpl(pixs);
1053     datas = pixGetData(pixs);
1054     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
1055         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1056     pixCopyResolution(pixd, pixs);
1057     pixScaleResolution(pixd, 0.5, 0.5);
1058     wpld = pixGetWpl(pixd);
1059     datad = pixGetData(pixd);
1060     scaleRGBToGray2Low(datad, wd, hd, wpld, datas, wpls, rwt, gwt, bwt);
1061     return pixd;
1062 }
1063 
1064 
1065 /*------------------------------------------------------------------*
1066  *             Downscaling with (antialias) area mapping            *
1067  *------------------------------------------------------------------*/
1068 /*!
1069  *  pixScaleAreaMap()
1070  *
1071  *      Input:  pixs (2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap)
1072  *              scalex, scaley (must both be <= 0.7)
1073  *      Return: pixd, or null on error
1074  *
1075  *  Notes:
1076  *      (1) This function should only be used when the scale factors are less
1077  *          than or equal to 0.7 (i.e., more than about 1.42x reduction).
1078  *          If either scale factor is larger than 0.7, we issue a warning
1079  *          and invoke pixScale().
1080  *      (2) This works only on 2, 4, 8 and 32 bpp images.  If there is
1081  *          a colormap, it is removed by converting to RGB.  In other
1082  *          cases, we issue a warning and invoke pixScale().
1083  *      (3) It does a relatively expensive area mapping computation, to
1084  *          avoid antialiasing.  It is about 2x slower than pixScaleSmooth(),
1085  *          but the results are much better on fine text.
1086  *      (4) This is typically about 20% faster for the special cases of
1087  *          2x, 4x, 8x and 16x reduction.
1088  *      (5) Surprisingly, there is no speedup (and a slight quality
1089  *          impairment) if you do as many successive 2x reductions as
1090  *          possible, ending with a reduction with a scale factor larger
1091  *          than 0.5.
1092  *
1093  *  *** Warning: implicit assumption about RGB component ordering ***
1094  */
1095 PIX *
pixScaleAreaMap(PIX * pix,l_float32 scalex,l_float32 scaley)1096 pixScaleAreaMap(PIX       *pix,
1097                 l_float32  scalex,
1098                 l_float32  scaley)
1099 {
1100 l_int32    ws, hs, d, wd, hd, wpls, wpld;
1101 l_uint32  *datas, *datad;
1102 PIX       *pixs, *pixd, *pixt1, *pixt2, *pixt3;
1103 
1104     PROCNAME("pixScaleAreaMap");
1105 
1106     if (!pix)
1107         return (PIX *)ERROR_PTR("pix not defined", procName, NULL);
1108     d = pixGetDepth(pix);
1109     if (d != 2 && d != 4 && d != 8 && d != 32)
1110         return (PIX *)ERROR_PTR("pix not 2, 4, 8 or 32 bpp", procName, NULL);
1111     if (scalex > 0.7 || scaley > 0.7) {
1112         L_WARNING("scaling factor not <= 0.7; doing regular scaling", procName);
1113         return pixScale(pix, scalex, scaley);
1114     }
1115 
1116         /* Special cases: 2x, 4x, 8x, 16x reduction */
1117     if (scalex == 0.5 && scaley == 0.5)
1118         return pixScaleAreaMap2(pix);
1119     if (scalex == 0.25 && scaley == 0.25) {
1120         pixt1 = pixScaleAreaMap2(pix);
1121         pixd = pixScaleAreaMap2(pixt1);
1122         pixDestroy(&pixt1);
1123         return pixd;
1124     }
1125     if (scalex == 0.125 && scaley == 0.125) {
1126         pixt1 = pixScaleAreaMap2(pix);
1127         pixt2 = pixScaleAreaMap2(pixt1);
1128         pixd = pixScaleAreaMap2(pixt2);
1129         pixDestroy(&pixt1);
1130         pixDestroy(&pixt2);
1131         return pixd;
1132     }
1133     if (scalex == 0.0625 && scaley == 0.0625) {
1134         pixt1 = pixScaleAreaMap2(pix);
1135         pixt2 = pixScaleAreaMap2(pixt1);
1136         pixt3 = pixScaleAreaMap2(pixt2);
1137         pixd = pixScaleAreaMap2(pixt3);
1138         pixDestroy(&pixt1);
1139         pixDestroy(&pixt2);
1140         pixDestroy(&pixt3);
1141         return pixd;
1142     }
1143 
1144         /* Remove colormap if necessary.
1145          * If 2 bpp or 4 bpp gray, convert to 8 bpp */
1146     if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) {
1147         L_WARNING("pix has colormap; removing", procName);
1148         pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
1149         d = pixGetDepth(pixs);
1150     }
1151     else if (d == 2 || d == 4) {
1152         pixs = pixConvertTo8(pix, FALSE);
1153         d = 8;
1154     }
1155     else
1156         pixs = pixClone(pix);
1157 
1158     pixGetDimensions(pixs, &ws, &hs, NULL);
1159     datas = pixGetData(pixs);
1160     wpls = pixGetWpl(pixs);
1161     wd = (l_int32)(scalex * (l_float32)ws + 0.5);
1162     hd = (l_int32)(scaley * (l_float32)hs + 0.5);
1163     if (wd < 1 || hd < 1) {
1164         pixDestroy(&pixs);
1165         return (PIX *)ERROR_PTR("pixd too small", procName, NULL);
1166     }
1167     if ((pixd = pixCreate(wd, hd, d)) == NULL) {
1168         pixDestroy(&pixs);
1169         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1170     }
1171     pixCopyResolution(pixd, pixs);
1172     pixScaleResolution(pixd, scalex, scaley);
1173     datad = pixGetData(pixd);
1174     wpld = pixGetWpl(pixd);
1175     if (d == 8)
1176         scaleGrayAreaMapLow(datad, wd, hd, wpld, datas, ws, hs, wpls);
1177     else  /* RGB, d == 32 */
1178         scaleColorAreaMapLow(datad, wd, hd, wpld, datas, ws, hs, wpls);
1179 
1180     pixDestroy(&pixs);
1181     return pixd;
1182 }
1183 
1184 
1185 /*!
1186  *  pixScaleAreaMap2()
1187  *
1188  *      Input:  pixs (2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap)
1189  *      Return: pixd, or null on error
1190  *
1191  *  Notes:
1192  *      (1) This function does an area mapping (average) for 2x
1193  *          reduction.
1194  *      (2) This works only on 2, 4, 8 and 32 bpp images.  If there is
1195  *          a colormap, it is removed by converting to RGB.
1196  *      (3) Speed on 3 GHz processor:
1197  *             Color: 160 Mpix/sec
1198  *             Gray: 700 Mpix/sec
1199  *          This contrasts with the speed of the general pixScaleAreaMap():
1200  *             Color: 35 Mpix/sec
1201  *             Gray: 50 Mpix/sec
1202  *      (4) From (3), we see that this special function is about 4.5x
1203  *          faster for color and 14x faster for grayscale
1204  *      (5) Consequently, pixScaleAreaMap2() is incorporated into the
1205  *          general area map scaling function, for the special cases
1206  *          of 2x, 4x, 8x and 16x reduction.
1207  */
1208 PIX *
pixScaleAreaMap2(PIX * pix)1209 pixScaleAreaMap2(PIX  *pix)
1210 {
1211 l_int32    wd, hd, d, wpls, wpld;
1212 l_uint32  *datas, *datad;
1213 PIX       *pixs, *pixd;
1214 
1215     PROCNAME("pixScaleAreaMap2");
1216 
1217     if (!pix)
1218         return (PIX *)ERROR_PTR("pix not defined", procName, NULL);
1219     d = pixGetDepth(pix);
1220     if (d != 2 && d != 4 && d != 8 && d != 32)
1221         return (PIX *)ERROR_PTR("pix not 2, 4, 8 or 32 bpp", procName, NULL);
1222 
1223         /* Remove colormap if necessary.
1224          * If 2 bpp or 4 bpp gray, convert to 8 bpp */
1225     if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) {
1226         L_WARNING("pix has colormap; removing", procName);
1227         pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
1228         d = pixGetDepth(pixs);
1229     }
1230     else if (d == 2 || d == 4) {
1231         pixs = pixConvertTo8(pix, FALSE);
1232         d = 8;
1233     }
1234     else
1235         pixs = pixClone(pix);
1236 
1237     wd = pixGetWidth(pixs) / 2;
1238     hd = pixGetHeight(pixs) / 2;
1239     datas = pixGetData(pixs);
1240     wpls = pixGetWpl(pixs);
1241     pixd = pixCreate(wd, hd, d);
1242     datad = pixGetData(pixd);
1243     wpld = pixGetWpl(pixd);
1244     pixCopyResolution(pixd, pixs);
1245     pixScaleResolution(pixd, 0.5, 0.5);
1246     scaleAreaMapLow2(datad, wd, hd, wpld, datas, d, wpls);
1247     pixDestroy(&pixs);
1248     return pixd;
1249 }
1250 
1251 
1252 /*------------------------------------------------------------------*
1253  *               Binary scaling by closest pixel sampling           *
1254  *------------------------------------------------------------------*/
1255 /*!
1256  *  pixScaleBinary()
1257  *
1258  *      Input:  pixs (1 bpp)
1259  *              scalex, scaley
1260  *      Return: pixd, or null on error
1261  *
1262  *  Notes:
1263  *      (1) This function samples from the source without
1264  *          filtering.  As a result, aliasing will occur for
1265  *          subsampling (scalex and scaley < 1.0).
1266  */
1267 PIX *
pixScaleBinary(PIX * pixs,l_float32 scalex,l_float32 scaley)1268 pixScaleBinary(PIX       *pixs,
1269                l_float32  scalex,
1270                l_float32  scaley)
1271 {
1272 l_int32    ws, hs, wpls, wd, hd, wpld;
1273 l_uint32  *datas, *datad;
1274 PIX       *pixd;
1275 
1276     PROCNAME("pixScaleBinary");
1277 
1278     if (!pixs)
1279         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1280     if (pixGetDepth(pixs) != 1)
1281         return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL);
1282     if (scalex == 1.0 && scaley == 1.0)
1283         return pixCopy(NULL, pixs);
1284 
1285     pixGetDimensions(pixs, &ws, &hs, NULL);
1286     datas = pixGetData(pixs);
1287     wpls = pixGetWpl(pixs);
1288     wd = (l_int32)(scalex * (l_float32)ws + 0.5);
1289     hd = (l_int32)(scaley * (l_float32)hs + 0.5);
1290     if ((pixd = pixCreate(wd, hd, 1)) == NULL)
1291         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1292     pixCopyColormap(pixd, pixs);
1293     pixCopyResolution(pixd, pixs);
1294     pixScaleResolution(pixd, scalex, scaley);
1295     datad = pixGetData(pixd);
1296     wpld = pixGetWpl(pixd);
1297     scaleBinaryLow(datad, wd, hd, wpld, datas, ws, hs, wpls);
1298     return pixd;
1299 }
1300 
1301 
1302 
1303 /*------------------------------------------------------------------*
1304  *       Scale-to-gray (1 bpp --> 8 bpp, arbitrary reduction)       *
1305  *------------------------------------------------------------------*/
1306 /*!
1307  *  pixScaleToGray()
1308  *
1309  *      Input:  pixs (1 bpp)
1310  *              scalefactor (reduction, < 1.0)
1311  *      Return: pixd (8 bpp), scaled down by scalefactor in each direction,
1312  *              or NULL on error.
1313  *
1314  *  Notes:
1315  *
1316  *  Binary images have sharp edges, so they intrinsically have very
1317  *  high frequency content.  To avoid aliasing, they must be low-pass
1318  *  filtered, which tends to blur the edges.  How can we keep relatively
1319  *  crisp edges without aliasing?  The trick is to do binary upscaling
1320  *  followed by a power-of-2 scaleToGray.  For large reductions, where
1321  *  you don't end up with much detail, some corners can be cut.
1322  *
1323  *  The intent here is to get high quality reduced grayscale
1324  *  images with relatively little computation.  We do binary
1325  *  pre-scaling followed by scaleToGrayN() for best results,
1326  *  esp. to avoid excess blur when the scale factor is near
1327  *  an inverse power of 2.  Where a low-pass filter is required,
1328  *  we use simple convolution kernels: either the hat filter for
1329  *  linear interpolation or a flat filter for larger downscaling.
1330  *  Other choices, such as a perfect bandpass filter with infinite extent
1331  *  (the sinc) or various approximations to it (e.g., lanczos), are
1332  *  unnecessarily expensive.
1333  *
1334  *  The choices made are as follows:
1335  *      (1) Do binary upscaling before scaleToGrayN() for scalefactors > 1/8
1336  *      (2) Do binary downscaling before scaleToGray8() for scalefactors
1337  *          between 1/16 and 1/8.
1338  *      (3) Use scaleToGray16() before grayscale downscaling for
1339  *          scalefactors less than 1/16
1340  *  Another reasonable choice would be to start binary downscaling
1341  *  for scalefactors below 1/4, rather than below 1/8 as we do here.
1342  *
1343  *  The general scaling rules, not all of which are used here, go as follows:
1344  *      (1) For grayscale upscaling, use pixScaleGrayLI().  However,
1345  *          note that edges will be visibly blurred for scalefactors
1346  *          near (but above) 1.0.  Replication will avoid edge blur,
1347  *          and should be considered for factors very near 1.0.
1348  *      (2) For grayscale downscaling with a scale factor larger than
1349  *          about 0.7, use pixScaleGrayLI().  For scalefactors near
1350  *          (but below) 1.0, you tread between Scylla and Charybdis.
1351  *          pixScaleGrayLI() again gives edge blurring, but
1352  *          pixScaleBySampling() gives visible aliasing.
1353  *      (3) For grayscale downscaling with a scale factor smaller than
1354  *          about 0.7, use pixScaleSmooth()
1355  *      (4) For binary input images, do as much scale to gray as possible
1356  *          using the special integer functions (2, 3, 4, 8 and 16).
1357  *      (5) It is better to upscale in binary, followed by scaleToGrayN()
1358  *          than to do scaleToGrayN() followed by an upscale using either
1359  *          LI or oversampling.
1360  *      (6) It may be better to downscale in binary, followed by
1361  *          scaleToGrayN() than to first use scaleToGrayN() followed by
1362  *          downscaling.  For downscaling between 8x and 16x, this is
1363  *          a reasonable option.
1364  *      (7) For reductions greater than 16x, it's reasonable to use
1365  *          scaleToGray16() followed by further grayscale downscaling.
1366  */
1367 PIX *
pixScaleToGray(PIX * pixs,l_float32 scalefactor)1368 pixScaleToGray(PIX       *pixs,
1369                l_float32  scalefactor)
1370 {
1371 l_int32    w, h, minsrc, mindest;
1372 l_float32  mag, red;
1373 PIX       *pixt, *pixd;
1374 
1375     PROCNAME("pixScaleToGray");
1376 
1377     if (!pixs)
1378         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1379     if (pixGetDepth(pixs) != 1)
1380         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
1381     if (scalefactor >= 1.0)
1382         return (PIX *)ERROR_PTR("scalefactor not < 1.0", procName, NULL);
1383     pixGetDimensions(pixs, &w, &h, NULL);
1384     minsrc = L_MIN(w, h);
1385     mindest = (l_int32)((l_float32)minsrc * scalefactor);
1386     if (mindest < 2)
1387         return (PIX *)ERROR_PTR("scalefactor too small", procName, NULL);
1388 
1389     if (scalefactor > 0.5) {   /* see note (5) */
1390         mag = 2.0 * scalefactor;  /* will be < 2.0 */
1391 /*        fprintf(stderr, "2x with mag %7.3f\n", mag);  */
1392         if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL)
1393             return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
1394         pixd = pixScaleToGray2(pixt);
1395     }
1396     else if (scalefactor == 0.5)
1397         return pixd = pixScaleToGray2(pixs);
1398     else if (scalefactor > 0.33333) {   /* see note (5) */
1399         mag = 3.0 * scalefactor;   /* will be < 1.5 */
1400 /*        fprintf(stderr, "3x with mag %7.3f\n", mag);  */
1401         if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL)
1402             return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
1403         pixd = pixScaleToGray3(pixt);
1404     }
1405     else if (scalefactor > 0.25) {  /* see note (5) */
1406         mag = 4.0 * scalefactor;   /* will be < 1.3333 */
1407 /*        fprintf(stderr, "4x with mag %7.3f\n", mag);  */
1408         if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL)
1409             return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
1410         pixd = pixScaleToGray4(pixt);
1411     }
1412     else if (scalefactor == 0.25)
1413         return pixd = pixScaleToGray4(pixs);
1414     else if (scalefactor > 0.16667) {  /* see note (5) */
1415         mag = 6.0 * scalefactor;   /* will be < 1.5 */
1416 /*        fprintf(stderr, "6x with mag %7.3f\n", mag); */
1417         if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL)
1418             return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
1419         pixd = pixScaleToGray6(pixt);
1420     }
1421     else if (scalefactor == 0.16667)
1422         return pixd = pixScaleToGray6(pixs);
1423     else if (scalefactor > 0.125) {  /* see note (5) */
1424         mag = 8.0 * scalefactor;   /*  will be < 1.3333  */
1425 /*        fprintf(stderr, "8x with mag %7.3f\n", mag);  */
1426         if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL)
1427             return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
1428         pixd = pixScaleToGray8(pixt);
1429     }
1430     else if (scalefactor == 0.125)
1431         return pixd = pixScaleToGray8(pixs);
1432     else if (scalefactor > 0.0625) {  /* see note (6) */
1433         red = 8.0 * scalefactor;   /* will be > 0.5 */
1434 /*        fprintf(stderr, "8x with red %7.3f\n", red);  */
1435         if ((pixt = pixScaleBinary(pixs, red, red)) == NULL)
1436             return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
1437         pixd = pixScaleToGray8(pixt);
1438     }
1439     else if (scalefactor == 0.0625)
1440         return pixd = pixScaleToGray16(pixs);
1441     else {  /* see note (7) */
1442         red = 16.0 * scalefactor;  /* will be <= 1.0 */
1443 /*        fprintf(stderr, "16x with red %7.3f\n", red);  */
1444         if ((pixt = pixScaleToGray16(pixs)) == NULL)
1445             return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
1446         if (red < 0.7)
1447             pixd = pixScaleSmooth(pixt, red, red);  /* see note (3) */
1448         else
1449             pixd = pixScaleGrayLI(pixt, red, red);  /* see note (2) */
1450     }
1451 
1452     pixDestroy(&pixt);
1453     if (!pixd)
1454         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1455     else
1456         return pixd;
1457 }
1458 
1459 
1460 
1461 /*------------------------------------------------------------------*
1462  *           Scale-to-gray (1 bpp --> 8 bpp, 2x reduction)          *
1463  *------------------------------------------------------------------*/
1464 /*!
1465  *  pixScaleToGray2()
1466  *
1467  *      Input:  pixs (1 bpp)
1468  *      Return: pixd (8 bpp), scaled down by 2x in each direction,
1469  *              or null on error.
1470  */
1471 PIX *
pixScaleToGray2(PIX * pixs)1472 pixScaleToGray2(PIX  *pixs)
1473 {
1474 l_uint8   *valtab;
1475 l_int32    ws, hs, wd, hd;
1476 l_int32    wpld, wpls;
1477 l_uint32  *sumtab;
1478 l_uint32  *datas, *datad;
1479 PIX       *pixd;
1480 
1481     PROCNAME("pixScaleToGray2");
1482 
1483     if (!pixs)
1484         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1485     if (pixGetDepth(pixs) != 1)
1486         return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL);
1487 
1488     pixGetDimensions(pixs, &ws, &hs, NULL);
1489     wd = ws / 2;
1490     hd = hs / 2;
1491     if (wd == 0 || hd == 0)
1492         return (PIX *)ERROR_PTR("pixs too small", procName, NULL);
1493 
1494     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
1495         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1496     pixCopyResolution(pixd, pixs);
1497     pixScaleResolution(pixd, 0.5, 0.5);
1498     datas = pixGetData(pixs);
1499     datad = pixGetData(pixd);
1500     wpls = pixGetWpl(pixs);
1501     wpld = pixGetWpl(pixd);
1502 
1503     if ((sumtab = makeSumTabSG2()) == NULL)
1504         return (PIX *)ERROR_PTR("sumtab not made", procName, NULL);
1505     if ((valtab = makeValTabSG2()) == NULL)
1506         return (PIX *)ERROR_PTR("valtab not made", procName, NULL);
1507 
1508     scaleToGray2Low(datad, wd, hd, wpld, datas, wpls, sumtab, valtab);
1509 
1510     FREE(sumtab);
1511     FREE(valtab);
1512     return pixd;
1513 }
1514 
1515 
1516 /*------------------------------------------------------------------*
1517  *           Scale-to-gray (1 bpp --> 8 bpp, 3x reduction)          *
1518  *------------------------------------------------------------------*/
1519 /*!
1520  *  pixScaleToGray3()
1521  *
1522  *      Input:  pixs (1 bpp)
1523  *      Return: pixd (8 bpp), scaled down by 3x in each direction,
1524  *              or null on error.
1525  *
1526  *  Notes:
1527  *      (1) Speed is about 100 x 10^6 src-pixels/sec/GHz.
1528  *          Another way to express this is it processes 1 src pixel
1529  *          in about 10 cycles.
1530  *      (2) The width of pixd is truncated is truncated to a factor of 8.
1531  */
1532 PIX *
pixScaleToGray3(PIX * pixs)1533 pixScaleToGray3(PIX  *pixs)
1534 {
1535 l_uint8   *valtab;
1536 l_int32    ws, hs, wd, hd;
1537 l_int32    wpld, wpls;
1538 l_uint32  *sumtab;
1539 l_uint32  *datas, *datad;
1540 PIX       *pixd;
1541 
1542     PROCNAME("pixScaleToGray3");
1543 
1544     if (!pixs)
1545         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1546     if (pixGetDepth(pixs) != 1)
1547         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
1548 
1549     pixGetDimensions(pixs, &ws, &hs, NULL);
1550     wd = (ws / 3) & 0xfffffff8;    /* truncate to factor of 8 */
1551     hd = hs / 3;
1552     if (wd == 0 || hd == 0)
1553         return (PIX *)ERROR_PTR("pixs too small", procName, NULL);
1554 
1555     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
1556         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1557     pixCopyResolution(pixd, pixs);
1558     pixScaleResolution(pixd, 0.33333, 0.33333);
1559     datas = pixGetData(pixs);
1560     datad = pixGetData(pixd);
1561     wpls = pixGetWpl(pixs);
1562     wpld = pixGetWpl(pixd);
1563 
1564     if ((sumtab = makeSumTabSG3()) == NULL)
1565         return (PIX *)ERROR_PTR("sumtab not made", procName, NULL);
1566     if ((valtab = makeValTabSG3()) == NULL)
1567         return (PIX *)ERROR_PTR("valtab not made", procName, NULL);
1568 
1569     scaleToGray3Low(datad, wd, hd, wpld, datas, wpls, sumtab, valtab);
1570 
1571     FREE(sumtab);
1572     FREE(valtab);
1573     return pixd;
1574 }
1575 
1576 
1577 /*------------------------------------------------------------------*
1578  *                         Scale-to-gray 4x                         *
1579  *------------------------------------------------------------------*/
1580 /*!
1581  *  pixScaleToGray4()
1582  *
1583  *      Input:  pixs (1 bpp)
1584  *      Return: pixd (8 bpp), scaled down by 4x in each direction,
1585  *              or null on error.
1586  *
1587  *  Notes:
1588  *      (1) The width of pixd is truncated is truncated to a factor of 2.
1589  */
1590 PIX *
pixScaleToGray4(PIX * pixs)1591 pixScaleToGray4(PIX  *pixs)
1592 {
1593 l_uint8   *valtab;
1594 l_int32    ws, hs, wd, hd;
1595 l_int32    wpld, wpls;
1596 l_uint32  *sumtab;
1597 l_uint32  *datas, *datad;
1598 PIX       *pixd;
1599 
1600     PROCNAME("pixScaleToGray4");
1601 
1602     if (!pixs)
1603         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1604     if (pixGetDepth(pixs) != 1)
1605         return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL);
1606 
1607     pixGetDimensions(pixs, &ws, &hs, NULL);
1608     wd = (ws / 4) & 0xfffffffe;    /* truncate to factor of 2 */
1609     hd = hs / 4;
1610     if (wd == 0 || hd == 0)
1611         return (PIX *)ERROR_PTR("pixs too small", procName, NULL);
1612 
1613     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
1614         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1615     pixCopyResolution(pixd, pixs);
1616     pixScaleResolution(pixd, 0.25, 0.25);
1617     datas = pixGetData(pixs);
1618     datad = pixGetData(pixd);
1619     wpls = pixGetWpl(pixs);
1620     wpld = pixGetWpl(pixd);
1621 
1622     if ((sumtab = makeSumTabSG4()) == NULL)
1623         return (PIX *)ERROR_PTR("sumtab not made", procName, NULL);
1624     if ((valtab = makeValTabSG4()) == NULL)
1625         return (PIX *)ERROR_PTR("valtab not made", procName, NULL);
1626 
1627     scaleToGray4Low(datad, wd, hd, wpld, datas, wpls, sumtab, valtab);
1628 
1629     FREE(sumtab);
1630     FREE(valtab);
1631     return pixd;
1632 }
1633 
1634 
1635 
1636 /*------------------------------------------------------------------*
1637  *           Scale-to-gray (1 bpp --> 8 bpp, 6x reduction)          *
1638  *------------------------------------------------------------------*/
1639 /*!
1640  *  pixScaleToGray6()
1641  *
1642  *      Input:  pixs (1 bpp)
1643  *      Return: pixd (8 bpp), scaled down by 6x in each direction,
1644  *              or null on error.
1645  *
1646  *  Notes:
1647  *      (1) The width of pixd is truncated is truncated to a factor of 8.
1648  */
1649 PIX *
pixScaleToGray6(PIX * pixs)1650 pixScaleToGray6(PIX  *pixs)
1651 {
1652 l_uint8   *valtab;
1653 l_int32    ws, hs, wd, hd, wpld, wpls;
1654 l_int32   *tab8;
1655 l_uint32  *datas, *datad;
1656 PIX       *pixd;
1657 
1658     PROCNAME("pixScaleToGray6");
1659 
1660     if (!pixs)
1661         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1662     if (pixGetDepth(pixs) != 1)
1663         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
1664 
1665     pixGetDimensions(pixs, &ws, &hs, NULL);
1666     wd = (ws / 6) & 0xfffffff8;    /* truncate to factor of 8 */
1667     hd = hs / 6;
1668     if (wd == 0 || hd == 0)
1669         return (PIX *)ERROR_PTR("pixs too small", procName, NULL);
1670 
1671     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
1672         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1673     pixCopyResolution(pixd, pixs);
1674     pixScaleResolution(pixd, 0.16667, 0.16667);
1675     datas = pixGetData(pixs);
1676     datad = pixGetData(pixd);
1677     wpls = pixGetWpl(pixs);
1678     wpld = pixGetWpl(pixd);
1679 
1680     if ((tab8 = makePixelSumTab8()) == NULL)
1681         return (PIX *)ERROR_PTR("tab8 not made", procName, NULL);
1682     if ((valtab = makeValTabSG6()) == NULL)
1683         return (PIX *)ERROR_PTR("valtab not made", procName, NULL);
1684 
1685     scaleToGray6Low(datad, wd, hd, wpld, datas, wpls, tab8, valtab);
1686 
1687     FREE(tab8);
1688     FREE(valtab);
1689     return pixd;
1690 }
1691 
1692 
1693 
1694 /*------------------------------------------------------------------*
1695  *                         Scale-to-gray 8x                         *
1696  *------------------------------------------------------------------*/
1697 /*!
1698  *  pixScaleToGray8()
1699  *
1700  *      Input:  pixs (1 bpp)
1701  *      Return: pixd (8 bpp), scaled down by 8x in each direction,
1702  *              or null on error
1703  */
1704 PIX *
pixScaleToGray8(PIX * pixs)1705 pixScaleToGray8(PIX  *pixs)
1706 {
1707 l_uint8   *valtab;
1708 l_int32    ws, hs, wd, hd;
1709 l_int32    wpld, wpls;
1710 l_int32   *tab8;
1711 l_uint32  *datas, *datad;
1712 PIX       *pixd;
1713 
1714     PROCNAME("pixScaleToGray8");
1715 
1716     if (!pixs)
1717         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1718     if (pixGetDepth(pixs) != 1)
1719         return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL);
1720 
1721     pixGetDimensions(pixs, &ws, &hs, NULL);
1722     wd = ws / 8;  /* truncate to nearest dest byte */
1723     hd = hs / 8;
1724     if (wd == 0 || hd == 0)
1725         return (PIX *)ERROR_PTR("pixs too small", procName, NULL);
1726 
1727     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
1728         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1729     pixCopyResolution(pixd, pixs);
1730     pixScaleResolution(pixd, 0.125, 0.125);
1731     datas = pixGetData(pixs);
1732     datad = pixGetData(pixd);
1733     wpls = pixGetWpl(pixs);
1734     wpld = pixGetWpl(pixd);
1735 
1736     if ((tab8 = makePixelSumTab8()) == NULL)
1737         return (PIX *)ERROR_PTR("tab8 not made", procName, NULL);
1738     if ((valtab = makeValTabSG8()) == NULL)
1739         return (PIX *)ERROR_PTR("valtab not made", procName, NULL);
1740 
1741     scaleToGray8Low(datad, wd, hd, wpld, datas, wpls, tab8, valtab);
1742 
1743     FREE(tab8);
1744     FREE(valtab);
1745     return pixd;
1746 }
1747 
1748 
1749 /*------------------------------------------------------------------*
1750  *                         Scale-to-gray 16x                         *
1751  *------------------------------------------------------------------*/
1752 /*!
1753  *  pixScaleToGray16()
1754  *
1755  *      Input:  pixs (1 bpp)
1756  *      Return: pixd (8 bpp), scaled down by 16x in each direction,
1757  *              or null on error.
1758  */
1759 PIX *
pixScaleToGray16(PIX * pixs)1760 pixScaleToGray16(PIX  *pixs)
1761 {
1762 l_int32    ws, hs, wd, hd;
1763 l_int32    wpld, wpls;
1764 l_int32   *tab8;
1765 l_uint32  *datas, *datad;
1766 PIX       *pixd;
1767 
1768     PROCNAME("pixScaleToGray16");
1769 
1770     if (!pixs)
1771         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1772     if (pixGetDepth(pixs) != 1)
1773         return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL);
1774 
1775     pixGetDimensions(pixs, &ws, &hs, NULL);
1776     wd = ws / 16;
1777     hd = hs / 16;
1778     if (wd == 0 || hd == 0)
1779         return (PIX *)ERROR_PTR("pixs too small", procName, NULL);
1780 
1781     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
1782         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1783     pixCopyResolution(pixd, pixs);
1784     pixScaleResolution(pixd, 0.0625, 0.0625);
1785     datas = pixGetData(pixs);
1786     datad = pixGetData(pixd);
1787     wpls = pixGetWpl(pixs);
1788     wpld = pixGetWpl(pixd);
1789 
1790     if ((tab8 = makePixelSumTab8()) == NULL)
1791         return (PIX *)ERROR_PTR("tab8 not made", procName, NULL);
1792 
1793     scaleToGray16Low(datad, wd, hd, wpld, datas, wpls, tab8);
1794 
1795     FREE(tab8);
1796     return pixd;
1797 }
1798 
1799 
1800 /*------------------------------------------------------------------*
1801  *    Scale-to-gray mipmap(1 bpp --> 8 bpp, arbitrary reduction)    *
1802  *------------------------------------------------------------------*/
1803 /*!
1804  *  pixScaleToGrayMipmap()
1805  *
1806  *      Input:  pixs (1 bpp)
1807  *              scalefactor (reduction, < 1.0)
1808  *      Return: pixd (8 bpp), scaled down by scalefactor in each direction,
1809  *              or NULL on error.
1810  *
1811  *  Notes:
1812  *
1813  *  This function is here mainly for pedagogical reasons.
1814  *  Mip-mapping is widely used in graphics for texture mapping, because
1815  *  the texture changes smoothly with scale.  This is accomplished by
1816  *  constructing a multiresolution pyramid and, for each pixel,
1817  *  doing a linear interpolation between corresponding pixels in
1818  *  the two planes of the pyramid that bracket the desired resolution.
1819  *  The computation is very efficient, and is implemented in hardware
1820  *  in high-end graphics cards.
1821  *
1822  *  We can use mip-mapping for scale-to-gray by using two scale-to-gray
1823  *  reduced images (we don't need the entire pyramid) selected from
1824  *  the set {2x, 4x, ... 16x}, and interpolating.  However, we get
1825  *  severe aliasing, probably because we are subsampling from the
1826  *  higher resolution image.  The method is very fast, but the result
1827  *  is very poor.  In fact, the results don't look any better than
1828  *  either subsampling off the higher-res grayscale image or oversampling
1829  *  on the lower-res image.  Consequently, this method should NOT be used
1830  *  for generating reduced images, scale-to-gray or otherwise.
1831  */
1832 PIX *
pixScaleToGrayMipmap(PIX * pixs,l_float32 scalefactor)1833 pixScaleToGrayMipmap(PIX       *pixs,
1834                      l_float32  scalefactor)
1835 {
1836 l_int32    w, h, minsrc, mindest;
1837 l_float32  red;
1838 PIX       *pixs1, *pixs2, *pixt, *pixd;
1839 
1840     PROCNAME("pixScaleToGrayMipmap");
1841 
1842     if (!pixs)
1843         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1844     if (pixGetDepth(pixs) != 1)
1845         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
1846     if (scalefactor >= 1.0)
1847         return (PIX *)ERROR_PTR("scalefactor not < 1.0", procName, NULL);
1848     pixGetDimensions(pixs, &w, &h, NULL);
1849     minsrc = L_MIN(w, h);
1850     mindest = (l_int32)((l_float32)minsrc * scalefactor);
1851     if (mindest < 2)
1852         return (PIX *)ERROR_PTR("scalefactor too small", procName, NULL);
1853 
1854     if (scalefactor > 0.5) {
1855         pixs1 = pixConvert1To8(NULL, pixs, 255, 0);
1856         pixs2 = pixScaleToGray2(pixs);
1857         red = scalefactor;
1858     }
1859     else if (scalefactor == 0.5) {
1860         return pixScaleToGray2(pixs);
1861     }
1862     else if (scalefactor > 0.25) {
1863         pixs1 = pixScaleToGray2(pixs);
1864         pixs2 = pixScaleToGray4(pixs);
1865         red = 2. * scalefactor;
1866     }
1867     else if (scalefactor == 0.25) {
1868         return pixScaleToGray4(pixs);
1869     }
1870     else if (scalefactor > 0.125) {
1871         pixs1 = pixScaleToGray4(pixs);
1872         pixs2 = pixScaleToGray8(pixs);
1873         red = 4. * scalefactor;
1874     }
1875     else if (scalefactor == 0.125) {
1876         return pixScaleToGray8(pixs);
1877     }
1878     else if (scalefactor > 0.0625) {
1879         pixs1 = pixScaleToGray8(pixs);
1880         pixs2 = pixScaleToGray16(pixs);
1881         red = 8. * scalefactor;
1882     }
1883     else if (scalefactor == 0.0625) {
1884         return pixScaleToGray16(pixs);
1885     }
1886     else {  /* end of the pyramid; just do it */
1887         red = 16.0 * scalefactor;  /* will be <= 1.0 */
1888         if ((pixt = pixScaleToGray16(pixs)) == NULL)
1889             return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
1890         if (red < 0.7)
1891             pixd = pixScaleSmooth(pixt, red, red);
1892         else
1893             pixd = pixScaleGrayLI(pixt, red, red);
1894         pixDestroy(&pixt);
1895         return pixd;
1896     }
1897 
1898     pixd = pixScaleMipmap(pixs1, pixs2, red);
1899 
1900     pixDestroy(&pixs1);
1901     pixDestroy(&pixs2);
1902     return pixd;
1903 }
1904 
1905 
1906 /*------------------------------------------------------------------*
1907  *                  Grayscale scaling using mipmap                  *
1908  *------------------------------------------------------------------*/
1909 /*!
1910  *  pixScaleMipmap()
1911  *
1912  *      Input:  pixs1 (high res 8 bpp)
1913  *              pixs2 (low res -- 2x reduced -- 8 bpp)
1914  *              scale (reduction with respect to high res image, > 0.5)
1915  *      Return: 8 bpp pix, scaled down by reduction in each direction,
1916  *              or NULL on error.
1917  *
1918  *  Notes:
1919  *      (1) See notes in pixScaleToGrayMipmap().
1920  *      (2) This function suffers from aliasing effects that are
1921  *          easily seen in document images.
1922  */
1923 PIX *
pixScaleMipmap(PIX * pixs1,PIX * pixs2,l_float32 scale)1924 pixScaleMipmap(PIX       *pixs1,
1925                PIX       *pixs2,
1926                l_float32  scale)
1927 {
1928 l_int32    ws1, hs1, ds1, ws2, hs2, ds2, wd, hd, wpls1, wpls2, wpld;
1929 l_uint32  *datas1, *datas2, *datad;
1930 PIX       *pixd;
1931 
1932     PROCNAME("pixScaleMipmap");
1933 
1934     if (!pixs1)
1935         return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL);
1936     if (!pixs2)
1937         return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL);
1938     pixGetDimensions(pixs1, &ws1, &hs1, &ds1);
1939     pixGetDimensions(pixs2, &ws2, &hs2, &ds2);
1940     if (ds1 != 8 || ds2 != 8)
1941         return (PIX *)ERROR_PTR("pixs1, pixs2 not both 8 bpp", procName, NULL);
1942     if (scale > 1.0 || scale < 0.5)
1943         return (PIX *)ERROR_PTR("scale not in [0.5, 1.0]", procName, NULL);
1944     if (pixGetColormap(pixs1) || pixGetColormap(pixs2))
1945         L_WARNING("pixs1 or pixs2 has colormap", procName);
1946     if (ws1 < 2 * ws2)
1947         return (PIX *)ERROR_PTR("invalid width ratio", procName, NULL);
1948     if (hs1 < 2 * hs2)
1949         return (PIX *)ERROR_PTR("invalid height ratio", procName, NULL);
1950 
1951         /* Generate wd and hd from the lower resolution dimensions,
1952          * to guarantee staying within both src images */
1953     datas1 = pixGetData(pixs1);
1954     wpls1 = pixGetWpl(pixs1);
1955     datas2 = pixGetData(pixs2);
1956     wpls2 = pixGetWpl(pixs2);
1957     wd = (l_int32)(2. * scale * pixGetWidth(pixs2));
1958     hd = (l_int32)(2. * scale * pixGetHeight(pixs2));
1959     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
1960         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1961     pixCopyResolution(pixd, pixs1);
1962     pixScaleResolution(pixd, scale, scale);
1963     datad = pixGetData(pixd);
1964     wpld = pixGetWpl(pixd);
1965 
1966     scaleMipmapLow(datad, wd, hd, wpld, datas1, wpls1, datas2, wpls2, scale);
1967     return pixd;
1968 }
1969 
1970 
1971 /*------------------------------------------------------------------*
1972  *                  Replicated (integer) expansion                  *
1973  *------------------------------------------------------------------*/
1974 /*!
1975  *  pixExpandReplicate()
1976  *
1977  *      Input:  pixs (1, 2, 4, 8, 16, 32 bpp)
1978  *              factor (integer scale factor for replicative expansion)
1979  *      Return: pixd (scaled up), or null on error.
1980  */
1981 PIX *
pixExpandReplicate(PIX * pixs,l_int32 factor)1982 pixExpandReplicate(PIX     *pixs,
1983                    l_int32  factor)
1984 {
1985 l_int32    w, h, d, wd, hd, wpls, wpld, bpld, start, i, j, k;
1986 l_uint8    sval;
1987 l_uint16   sval16;
1988 l_uint32   sval32;
1989 l_uint32  *lines, *datas, *lined, *datad;
1990 PIX       *pixd;
1991 
1992     PROCNAME("pixExpandReplicate");
1993 
1994     if (!pixs)
1995         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1996     pixGetDimensions(pixs, &w, &h, &d);
1997     if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
1998         return (PIX *)ERROR_PTR("depth not in {1,2,4,8,16,32}", procName, NULL);
1999     if (factor <= 0)
2000         return (PIX *)ERROR_PTR("factor <= 0; invalid", procName, NULL);
2001     if (factor == 1)
2002         return pixCopy(NULL, pixs);
2003 
2004     if (d == 1)
2005         return pixExpandBinaryReplicate(pixs, factor);
2006 
2007     wd = factor * w;
2008     hd = factor * h;
2009     if ((pixd = pixCreate(wd, hd, d)) == NULL)
2010         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2011     pixCopyColormap(pixd, pixs);
2012     pixCopyResolution(pixd, pixs);
2013     pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor);
2014     datas = pixGetData(pixs);
2015     wpls = pixGetWpl(pixs);
2016     datad = pixGetData(pixd);
2017     wpld = pixGetWpl(pixd);
2018 
2019     switch (d) {
2020     case 2:
2021         bpld = (wd + 3) / 4;  /* occupied bytes only */
2022         for (i = 0; i < h; i++) {
2023             lines = datas + i * wpls;
2024             lined = datad + factor * i * wpld;
2025             for (j = 0; j < w; j++) {
2026                 sval = GET_DATA_DIBIT(lines, j);
2027                 start = factor * j;
2028                 for (k = 0; k < factor; k++)
2029                     SET_DATA_DIBIT(lined, start + k, sval);
2030             }
2031             for (k = 1; k < factor; k++)
2032                 memcpy(lined + k * wpld, lined, 4 * wpld);
2033         }
2034         break;
2035     case 4:
2036         bpld = (wd + 1) / 2;  /* occupied bytes only */
2037         for (i = 0; i < h; i++) {
2038             lines = datas + i * wpls;
2039             lined = datad + factor * i * wpld;
2040             for (j = 0; j < w; j++) {
2041                 sval = GET_DATA_QBIT(lines, j);
2042                 start = factor * j;
2043                 for (k = 0; k < factor; k++)
2044                     SET_DATA_QBIT(lined, start + k, sval);
2045             }
2046             for (k = 1; k < factor; k++)
2047                 memcpy(lined + k * wpld, lined, 4 * wpld);
2048         }
2049         break;
2050     case 8:
2051         for (i = 0; i < h; i++) {
2052             lines = datas + i * wpls;
2053             lined = datad + factor * i * wpld;
2054             for (j = 0; j < w; j++) {
2055                 sval = GET_DATA_BYTE(lines, j);
2056                 start = factor * j;
2057                 for (k = 0; k < factor; k++)
2058                     SET_DATA_BYTE(lined, start + k, sval);
2059             }
2060             for (k = 1; k < factor; k++)
2061                 memcpy(lined + k * wpld, lined, 4 * wpld);
2062         }
2063         break;
2064     case 16:
2065         for (i = 0; i < h; i++) {
2066             lines = datas + i * wpls;
2067             lined = datad + factor * i * wpld;
2068             for (j = 0; j < w; j++) {
2069                 sval16 = GET_DATA_TWO_BYTES(lines, j);
2070                 start = factor * j;
2071                 for (k = 0; k < factor; k++)
2072                     SET_DATA_TWO_BYTES(lined, start + k, sval16);
2073             }
2074             for (k = 1; k < factor; k++)
2075                 memcpy(lined + k * wpld, lined, 4 * wpld);
2076         }
2077         break;
2078     case 32:
2079         for (i = 0; i < h; i++) {
2080             lines = datas + i * wpls;
2081             lined = datad + factor * i * wpld;
2082             for (j = 0; j < w; j++) {
2083                 sval32 = *(lines + j);
2084                 start = factor * j;
2085                 for (k = 0; k < factor; k++)
2086                     *(lined + start + k) = sval32;
2087             }
2088             for (k = 1; k < factor; k++)
2089                 memcpy(lined + k * wpld, lined, 4 * wpld);
2090         }
2091         break;
2092     default:
2093         fprintf(stderr, "invalid depth\n");
2094     }
2095 
2096     return pixd;
2097 }
2098 
2099 
2100 /*------------------------------------------------------------------*
2101  *                Scale 2x followed by binarization                 *
2102  *------------------------------------------------------------------*/
2103 /*!
2104  *  pixScaleGray2xLIThresh()
2105  *
2106  *      Input:  pixs (8 bpp)
2107  *              thresh  (between 0 and 256)
2108  *      Return: pixd (1 bpp), or null on error
2109  *
2110  *  Notes:
2111  *      (1) This does 2x upscale on pixs, using linear interpolation,
2112  *          followed by thresholding to binary.
2113  *      (2) Buffers are used to avoid making a large grayscale image.
2114  */
2115 PIX *
pixScaleGray2xLIThresh(PIX * pixs,l_int32 thresh)2116 pixScaleGray2xLIThresh(PIX     *pixs,
2117                        l_int32  thresh)
2118 {
2119 l_int32    i, ws, hs, hsm, wd, hd, wpls, wplb, wpld;
2120 l_uint32  *datas, *datad, *lines, *lined, *lineb;
2121 PIX       *pixd;
2122 
2123     PROCNAME("pixScaleGray2xLIThresh");
2124 
2125     if (!pixs)
2126         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2127     if (pixGetDepth(pixs) != 8)
2128         return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL);
2129     if (thresh < 0 || thresh > 256)
2130         return (PIX *)ERROR_PTR("thresh must be in [0, ... 256]",
2131             procName, NULL);
2132     if (pixGetColormap(pixs))
2133         L_WARNING("pixs has colormap", procName);
2134 
2135     pixGetDimensions(pixs, &ws, &hs, NULL);
2136     wd = 2 * ws;
2137     hd = 2 * hs;
2138     hsm = hs - 1;
2139     datas = pixGetData(pixs);
2140     wpls = pixGetWpl(pixs);
2141 
2142         /* Make line buffer for 2 lines of virtual intermediate image */
2143     wplb = (wd + 3) / 4;
2144     if ((lineb = (l_uint32 *)CALLOC(2 * wplb, sizeof(l_uint32))) == NULL)
2145         return (PIX *)ERROR_PTR("lineb not made", procName, NULL);
2146 
2147         /* Make dest binary image */
2148     if ((pixd = pixCreate(wd, hd, 1)) == NULL)
2149         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2150     pixCopyResolution(pixd, pixs);
2151     pixScaleResolution(pixd, 2.0, 2.0);
2152     wpld = pixGetWpl(pixd);
2153     datad = pixGetData(pixd);
2154 
2155         /* Do all but last src line */
2156     for (i = 0; i < hsm; i++) {
2157         lines = datas + i * wpls;
2158         lined = datad + 2 * i * wpld;  /* do 2 dest lines at a time */
2159         scaleGray2xLILineLow(lineb, wplb, lines, ws, wpls, 0);
2160         thresholdToBinaryLineLow(lined, wd, lineb, 8, thresh);
2161         thresholdToBinaryLineLow(lined + wpld, wd, lineb + wplb, 8, thresh);
2162     }
2163 
2164         /* Do last src line */
2165     lines = datas + hsm * wpls;
2166     lined = datad + 2 * hsm * wpld;
2167     scaleGray2xLILineLow(lineb, wplb, lines, ws, wpls, 1);
2168     thresholdToBinaryLineLow(lined, wd, lineb, 8, thresh);
2169     thresholdToBinaryLineLow(lined + wpld, wd, lineb + wplb, 8, thresh);
2170 
2171     FREE(lineb);
2172     return pixd;
2173 }
2174 
2175 
2176 /*!
2177  *  pixScaleGray2xLIDither()
2178  *
2179  *      Input:  pixs (8 bpp)
2180  *      Return: pixd (1 bpp), or null on error
2181  *
2182  *  Notes:
2183  *      (1) This does 2x upscale on pixs, using linear interpolation,
2184  *          followed by Floyd-Steinberg dithering to binary.
2185  *      (2) Buffers are used to avoid making a large grayscale image.
2186  *          - Two line buffers are used for the src, required for the 2x
2187  *            LI upscale.
2188  *          - Three line buffers are used for the intermediate image.
2189  *            Two are filled with each 2xLI row operation; the third is
2190  *            needed because the upscale and dithering ops are out of sync.
2191  */
2192 PIX *
pixScaleGray2xLIDither(PIX * pixs)2193 pixScaleGray2xLIDither(PIX  *pixs)
2194 {
2195 l_int32    i, ws, hs, hsm, wd, hd, wpls, wplb, wpld;
2196 l_uint32  *datas, *datad;
2197 l_uint32  *lined;
2198 l_uint32  *lineb;   /* 2 intermediate buffer lines */
2199 l_uint32  *linebp;  /* 1 intermediate buffer line */
2200 l_uint32  *bufs;    /* 2 source buffer lines */
2201 PIX       *pixd;
2202 
2203     PROCNAME("pixScaleGray2xLIDither");
2204 
2205     if (!pixs)
2206         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2207     if (pixGetDepth(pixs) != 8)
2208         return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL);
2209     if (pixGetColormap(pixs))
2210         L_WARNING("pixs has colormap", procName);
2211 
2212     pixGetDimensions(pixs, &ws, &hs, NULL);
2213     wd = 2 * ws;
2214     hd = 2 * hs;
2215     hsm = hs - 1;
2216     datas = pixGetData(pixs);
2217     wpls = pixGetWpl(pixs);
2218 
2219         /* Make line buffers for 2 lines of src image */
2220     if ((bufs = (l_uint32 *)CALLOC(2 * wpls, sizeof(l_uint32))) == NULL)
2221         return (PIX *)ERROR_PTR("bufs not made", procName, NULL);
2222 
2223         /* Make line buffer for 2 lines of virtual intermediate image */
2224     wplb = (wd + 3) / 4;
2225     if ((lineb = (l_uint32 *)CALLOC(2 * wplb, sizeof(l_uint32))) == NULL)
2226         return (PIX *)ERROR_PTR("lineb not made", procName, NULL);
2227 
2228         /* Make line buffer for 1 line of virtual intermediate image */
2229     if ((linebp = (l_uint32 *)CALLOC(wplb, sizeof(l_uint32))) == NULL)
2230         return (PIX *)ERROR_PTR("linebp not made", procName, NULL);
2231 
2232         /* Make dest binary image */
2233     if ((pixd = pixCreate(wd, hd, 1)) == NULL)
2234         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2235     pixCopyResolution(pixd, pixs);
2236     pixScaleResolution(pixd, 2.0, 2.0);
2237     wpld = pixGetWpl(pixd);
2238     datad = pixGetData(pixd);
2239 
2240         /* Start with the first src and the first dest line */
2241     memcpy(bufs, datas, 4 * wpls);   /* first src line */
2242     memcpy(bufs + wpls, datas + wpls, 4 * wpls);  /* 2nd src line */
2243     scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 0);  /* 2 i lines */
2244     lined = datad;
2245     ditherToBinaryLineLow(lined, wd, lineb, lineb + wplb,
2246                           DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
2247                                                     /* 1st d line */
2248 
2249         /* Do all but last src line */
2250     for (i = 1; i < hsm; i++) {
2251         memcpy(bufs, datas + i * wpls, 4 * wpls);  /* i-th src line */
2252         memcpy(bufs + wpls, datas + (i + 1) * wpls, 4 * wpls);
2253         memcpy(linebp, lineb + wplb, 4 * wplb);
2254         scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 0);  /* 2 i lines */
2255         lined = datad + 2 * i * wpld;
2256         ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb,
2257                               DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
2258                                                    /* odd dest line */
2259         ditherToBinaryLineLow(lined, wd, lineb, lineb + wplb,
2260                               DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
2261                                                    /* even dest line */
2262     }
2263 
2264         /* Do the last src line and the last 3 dest lines */
2265     memcpy(bufs, datas + hsm * wpls, 4 * wpls);  /* hsm-th src line */
2266     memcpy(linebp, lineb + wplb, 4 * wplb);   /* 1 i line */
2267     scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 1);  /* 2 i lines */
2268     ditherToBinaryLineLow(lined + wpld, wd, linebp, lineb,
2269                           DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
2270                                                    /* odd dest line */
2271     ditherToBinaryLineLow(lined + 2 * wpld, wd, lineb, lineb + wplb,
2272                           DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
2273                                                    /* even dest line */
2274     ditherToBinaryLineLow(lined + 3 * wpld, wd, lineb + wplb, NULL,
2275                           DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 1);
2276                                                    /* last dest line */
2277 
2278     FREE(bufs);
2279     FREE(lineb);
2280     FREE(linebp);
2281     return pixd;
2282 }
2283 
2284 
2285 /*------------------------------------------------------------------*
2286  *                Scale 4x followed by binarization                 *
2287  *------------------------------------------------------------------*/
2288 /*!
2289  *  pixScaleGray4xLIThresh()
2290  *
2291  *      Input:  pixs (8 bpp)
2292  *              thresh  (between 0 and 256)
2293  *      Return: pixd (1 bpp), or null on error
2294  *
2295  *  Notes:
2296  *      (1) This does 4x upscale on pixs, using linear interpolation,
2297  *          followed by thresholding to binary.
2298  *      (2) Buffers are used to avoid making a large grayscale image.
2299  *      (3) If a full 4x expanded grayscale image can be kept in memory,
2300  *          this function is only about 10% faster than separately doing
2301  *          a linear interpolation to a large grayscale image, followed
2302  *          by thresholding to binary.
2303  */
2304 PIX *
pixScaleGray4xLIThresh(PIX * pixs,l_int32 thresh)2305 pixScaleGray4xLIThresh(PIX     *pixs,
2306                        l_int32  thresh)
2307 {
2308 l_int32    i, j, ws, hs, hsm, wd, hd, wpls, wplb, wpld;
2309 l_uint32  *datas, *datad, *lines, *lined, *lineb;
2310 PIX       *pixd;
2311 
2312     PROCNAME("pixScaleGray4xLIThresh");
2313 
2314     if (!pixs)
2315         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2316     if (pixGetDepth(pixs) != 8)
2317         return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL);
2318     if (thresh < 0 || thresh > 256)
2319         return (PIX *)ERROR_PTR("thresh must be in [0, ... 256]",
2320             procName, NULL);
2321     if (pixGetColormap(pixs))
2322         L_WARNING("pixs has colormap", procName);
2323 
2324     pixGetDimensions(pixs, &ws, &hs, NULL);
2325     wd = 4 * ws;
2326     hd = 4 * hs;
2327     hsm = hs - 1;
2328     datas = pixGetData(pixs);
2329     wpls = pixGetWpl(pixs);
2330 
2331         /* Make line buffer for 4 lines of virtual intermediate image */
2332     wplb = (wd + 3) / 4;
2333     if ((lineb = (l_uint32 *)CALLOC(4 * wplb, sizeof(l_uint32))) == NULL)
2334         return (PIX *)ERROR_PTR("lineb not made", procName, NULL);
2335 
2336         /* Make dest binary image */
2337     if ((pixd = pixCreate(wd, hd, 1)) == NULL)
2338         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2339     pixCopyResolution(pixd, pixs);
2340     pixScaleResolution(pixd, 4.0, 4.0);
2341     wpld = pixGetWpl(pixd);
2342     datad = pixGetData(pixd);
2343 
2344         /* Do all but last src line */
2345     for (i = 0; i < hsm; i++) {
2346         lines = datas + i * wpls;
2347         lined = datad + 4 * i * wpld;  /* do 4 dest lines at a time */
2348         scaleGray4xLILineLow(lineb, wplb, lines, ws, wpls, 0);
2349         for (j = 0; j < 4; j++) {
2350             thresholdToBinaryLineLow(lined + j * wpld, wd,
2351                                      lineb + j * wplb, 8, thresh);
2352         }
2353     }
2354 
2355         /* Do last src line */
2356     lines = datas + hsm * wpls;
2357     lined = datad + 4 * hsm * wpld;
2358     scaleGray4xLILineLow(lineb, wplb, lines, ws, wpls, 1);
2359     for (j = 0; j < 4; j++) {
2360         thresholdToBinaryLineLow(lined + j * wpld, wd,
2361                                  lineb + j * wplb, 8, thresh);
2362     }
2363 
2364     FREE(lineb);
2365     return pixd;
2366 }
2367 
2368 
2369 /*!
2370  *  pixScaleGray4xLIDither()
2371  *
2372  *      Input:  pixs (8 bpp)
2373  *      Return: pixd (1 bpp), or null on error
2374  *
2375  *  Notes:
2376  *      (1) This does 4x upscale on pixs, using linear interpolation,
2377  *          followed by Floyd-Steinberg dithering to binary.
2378  *      (2) Buffers are used to avoid making a large grayscale image.
2379  *          - Two line buffers are used for the src, required for the
2380  *            4xLI upscale.
2381  *          - Five line buffers are used for the intermediate image.
2382  *            Four are filled with each 4xLI row operation; the fifth
2383  *            is needed because the upscale and dithering ops are
2384  *            out of sync.
2385  *      (3) If a full 4x expanded grayscale image can be kept in memory,
2386  *          this function is only about 5% faster than separately doing
2387  *          a linear interpolation to a large grayscale image, followed
2388  *          by error-diffusion dithering to binary.
2389  */
2390 PIX *
pixScaleGray4xLIDither(PIX * pixs)2391 pixScaleGray4xLIDither(PIX  *pixs)
2392 {
2393 l_int32    i, j, ws, hs, hsm, wd, hd, wpls, wplb, wpld;
2394 l_uint32  *datas, *datad;
2395 l_uint32  *lined;
2396 l_uint32  *lineb;   /* 4 intermediate buffer lines */
2397 l_uint32  *linebp;  /* 1 intermediate buffer line */
2398 l_uint32  *bufs;    /* 2 source buffer lines */
2399 PIX       *pixd;
2400 
2401     PROCNAME("pixScaleGray4xLIDither");
2402 
2403     if (!pixs)
2404         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2405     if (pixGetDepth(pixs) != 8)
2406         return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL);
2407     if (pixGetColormap(pixs))
2408         L_WARNING("pixs has colormap", procName);
2409 
2410     pixGetDimensions(pixs, &ws, &hs, NULL);
2411     wd = 4 * ws;
2412     hd = 4 * hs;
2413     hsm = hs - 1;
2414     datas = pixGetData(pixs);
2415     wpls = pixGetWpl(pixs);
2416 
2417         /* Make line buffers for 2 lines of src image */
2418     if ((bufs = (l_uint32 *)CALLOC(2 * wpls, sizeof(l_uint32))) == NULL)
2419         return (PIX *)ERROR_PTR("bufs not made", procName, NULL);
2420 
2421         /* Make line buffer for 4 lines of virtual intermediate image */
2422     wplb = (wd + 3) / 4;
2423     if ((lineb = (l_uint32 *)CALLOC(4 * wplb, sizeof(l_uint32))) == NULL)
2424         return (PIX *)ERROR_PTR("lineb not made", procName, NULL);
2425 
2426         /* Make line buffer for 1 line of virtual intermediate image */
2427     if ((linebp = (l_uint32 *)CALLOC(wplb, sizeof(l_uint32))) == NULL)
2428         return (PIX *)ERROR_PTR("linebp not made", procName, NULL);
2429 
2430         /* Make dest binary image */
2431     if ((pixd = pixCreate(wd, hd, 1)) == NULL)
2432         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2433     pixCopyResolution(pixd, pixs);
2434     pixScaleResolution(pixd, 4.0, 4.0);
2435     wpld = pixGetWpl(pixd);
2436     datad = pixGetData(pixd);
2437 
2438         /* Start with the first src and the first 3 dest lines */
2439     memcpy(bufs, datas, 4 * wpls);   /* first src line */
2440     memcpy(bufs + wpls, datas + wpls, 4 * wpls);  /* 2nd src line */
2441     scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 0);  /* 4 b lines */
2442     lined = datad;
2443     for (j = 0; j < 3; j++) {  /* first 3 d lines of Q */
2444         ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb,
2445                               lineb + (j + 1) * wplb,
2446                               DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
2447     }
2448 
2449         /* Do all but last src line */
2450     for (i = 1; i < hsm; i++) {
2451         memcpy(bufs, datas + i * wpls, 4 * wpls);  /* i-th src line */
2452         memcpy(bufs + wpls, datas + (i + 1) * wpls, 4 * wpls);
2453         memcpy(linebp, lineb + 3 * wplb, 4 * wplb);
2454         scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 0);  /* 4 b lines */
2455         lined = datad + 4 * i * wpld;
2456         ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb,
2457                               DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
2458                                                      /* 4th dest line of Q */
2459         for (j = 0; j < 3; j++) {  /* next 3 d lines of Quad */
2460             ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb,
2461                                   lineb + (j + 1) * wplb,
2462                                  DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
2463         }
2464     }
2465 
2466         /* Do the last src line and the last 5 dest lines */
2467     memcpy(bufs, datas + hsm * wpls, 4 * wpls);  /* hsm-th src line */
2468     memcpy(linebp, lineb + 3 * wplb, 4 * wplb);   /* 1 b line */
2469     scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 1);  /* 4 b lines */
2470     lined = datad + 4 * hsm * wpld;
2471     ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb,
2472                           DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
2473                                                    /* 4th dest line of Q */
2474     for (j = 0; j < 3; j++) {  /* next 3 d lines of Quad */
2475         ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb,
2476                               lineb + (j + 1) * wplb,
2477                               DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0);
2478     }
2479         /* And finally, the last dest line */
2480     ditherToBinaryLineLow(lined + 3 * wpld, wd, lineb + 3 * wplb, NULL,
2481                               DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 1);
2482 
2483     FREE(bufs);
2484     FREE(lineb);
2485     FREE(linebp);
2486     return pixd;
2487 }
2488 
2489 
2490 /*-----------------------------------------------------------------------*
2491  *                    Downscaling using min or max                       *
2492  *-----------------------------------------------------------------------*/
2493 /*!
2494  *  pixScaleGrayMinMax()
2495  *
2496  *      Input:  pixs (8 bpp)
2497  *              xfact (x downscaling factor; integer)
2498  *              yfact (y downscaling factor; integer)
2499  *              type (L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAX_MIN_DIFF)
2500  *      Return: pixd (8 bpp)
2501  *
2502  *  Notes:
2503  *      (1) The downscaled pixels in pixd are the min, max or (max - min)
2504  *          of the corresponding set of xfact * yfact pixels in pixs.
2505  *      (2) Using L_CHOOSE_MIN is equivalent to a grayscale erosion,
2506  *          using a brick Sel of size (xfact * yfact), followed by
2507  *          subsampling within each (xfact * yfact) cell.  Using
2508  *          L_CHOOSE_MAX is equivalent to the corresponding dilation.
2509  *      (3) Using L_CHOOSE_MAX_MIN_DIFF finds the difference between max
2510  *          and min values in each cell.
2511  *      (4) For the special case of downscaling by 2x in both directions,
2512  *          pixScaleGrayMinMax2() is about 2x more efficient.
2513  */
2514 PIX *
pixScaleGrayMinMax(PIX * pixs,l_int32 xfact,l_int32 yfact,l_int32 type)2515 pixScaleGrayMinMax(PIX     *pixs,
2516                    l_int32  xfact,
2517                    l_int32  yfact,
2518                    l_int32  type)
2519 {
2520 l_int32    ws, hs, d, wd, hd, wpls, wpld, i, j, k, m;
2521 l_int32    minval, maxval, val;
2522 l_uint32  *datas, *datad, *lines, *lined;
2523 PIX       *pixd;
2524 
2525     PROCNAME("pixScaleGrayMinMax");
2526 
2527     if (!pixs)
2528         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2529     pixGetDimensions(pixs, &ws, &hs, &d);
2530     if (d != 8)
2531         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
2532     if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX &&
2533         type != L_CHOOSE_MAX_MIN_DIFF)
2534         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
2535 
2536     if (xfact == 2 && yfact == 2)
2537         return pixScaleGrayMinMax2(pixs, type);
2538 
2539     wd = L_MAX(ws / xfact, 1);
2540     hd = L_MAX(hs / yfact, 1);
2541     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
2542         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2543     datas = pixGetData(pixs);
2544     datad = pixGetData(pixd);
2545     wpls = pixGetWpl(pixs);
2546     wpld = pixGetWpl(pixd);
2547     for (i = 0; i < hd; i++) {
2548         lined = datad + i * wpld;
2549         for (j = 0; j < wd; j++) {
2550             if (type == L_CHOOSE_MIN || type == L_CHOOSE_MAX_MIN_DIFF) {
2551                 minval = 255;
2552                 for (k = 0; k < yfact; k++) {
2553                     lines = datas + (yfact * i + k) * wpls;
2554                     for (m = 0; m < xfact; m++) {
2555                         val = GET_DATA_BYTE(lines, xfact * j + m);
2556                         if (val < minval)
2557                             minval = val;
2558                     }
2559                 }
2560             }
2561             if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAX_MIN_DIFF) {
2562                 maxval = 0;
2563                 for (k = 0; k < yfact; k++) {
2564                     lines = datas + (yfact * i + k) * wpls;
2565                     for (m = 0; m < xfact; m++) {
2566                         val = GET_DATA_BYTE(lines, xfact * j + m);
2567                         if (val > maxval)
2568                             maxval = val;
2569                     }
2570                 }
2571             }
2572             if (type == L_CHOOSE_MIN)
2573                 SET_DATA_BYTE(lined, j, minval);
2574             else if (type == L_CHOOSE_MAX)
2575                 SET_DATA_BYTE(lined, j, maxval);
2576             else  /* type == L_CHOOSE_MAX_MIN_DIFF */
2577                 SET_DATA_BYTE(lined, j, maxval - minval);
2578         }
2579     }
2580 
2581     return pixd;
2582 }
2583 
2584 
2585 /*!
2586  *  pixScaleGrayMinMax2()
2587  *
2588  *      Input:  pixs (8 bpp)
2589  *              type (L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAX_MIN_DIFF)
2590  *      Return: pixd (8 bpp downscaled by 2x)
2591  *
2592  *  Notes:
2593  *      (1) Special version for 2x reduction.  The downscaled pixels
2594  *          in pixd are the min, max or (max - min) of the corresponding
2595  *          set of 4 pixels in pixs.
2596  *      (2) The max and min operations are a special case (for levels 1
2597  *          and 4) of grayscale analog to the binary rank scaling operation
2598  *          pixReduceRankBinary2().  Note, however, that because of
2599  *          the photometric definition that higher gray values are
2600  *          lighter, the erosion-like L_CHOOSE_MIN will darken
2601  *          the resulting image, corresponding to a threshold level 1
2602  *          in the binary case.  Likewise, L_CHOOSE_MAX will lighten
2603  *          the pixd, corresponding to a threshold level of 4.
2604  *      (3) To choose any of the four rank levels in a 2x grayscale
2605  *          reduction, use pixScaleGrayRank2().
2606  *      (4) This runs at about 70 MPix/sec/GHz of source data for
2607  *          erosion and dilation.
2608  */
2609 PIX *
pixScaleGrayMinMax2(PIX * pixs,l_int32 type)2610 pixScaleGrayMinMax2(PIX     *pixs,
2611                     l_int32  type)
2612 {
2613 l_int32    ws, hs, d, wd, hd, wpls, wpld, i, j, k;
2614 l_int32    minval, maxval;
2615 l_int32    val[4];
2616 l_uint32  *datas, *datad, *lines, *lined;
2617 PIX       *pixd;
2618 
2619     PROCNAME("pixScaleGrayMinMax2");
2620 
2621     if (!pixs)
2622         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2623     pixGetDimensions(pixs, &ws, &hs, &d);
2624     if (d != 8)
2625         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
2626     if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX &&
2627         type != L_CHOOSE_MAX_MIN_DIFF)
2628         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
2629 
2630     wd = ws / 2;
2631     hd = hs / 2;
2632     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
2633         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2634     datas = pixGetData(pixs);
2635     datad = pixGetData(pixd);
2636     wpls = pixGetWpl(pixs);
2637     wpld = pixGetWpl(pixd);
2638     for (i = 0; i < hd; i++) {
2639         lines = datas + 2 * i * wpls;
2640         lined = datad + i * wpld;
2641         for (j = 0; j < wd; j++) {
2642             val[0] = GET_DATA_BYTE(lines, 2 * j);
2643             val[1] = GET_DATA_BYTE(lines, 2 * j + 1);
2644             val[2] = GET_DATA_BYTE(lines + wpls, 2 * j);
2645             val[3] = GET_DATA_BYTE(lines + wpls, 2 * j + 1);
2646             if (type == L_CHOOSE_MIN || type == L_CHOOSE_MAX_MIN_DIFF) {
2647                 minval = 255;
2648                 for (k = 0; k < 4; k++) {
2649                     if (val[k] < minval)
2650                         minval = val[k];
2651                 }
2652             }
2653             if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAX_MIN_DIFF) {
2654                 maxval = 0;
2655                 for (k = 0; k < 4; k++) {
2656                     if (val[k] > maxval)
2657                         maxval = val[k];
2658                 }
2659             }
2660             if (type = L_CHOOSE_MIN)
2661                 SET_DATA_BYTE(lined, j, minval);
2662             else if (type = L_CHOOSE_MAX)
2663                 SET_DATA_BYTE(lined, j, maxval);
2664             else  /* type == L_CHOOSE_MAX_MIN_DIFF */
2665                 SET_DATA_BYTE(lined, j, maxval - minval);
2666         }
2667     }
2668 
2669     return pixd;
2670 }
2671 
2672 
2673 /*-----------------------------------------------------------------------*
2674  *                  Grayscale downscaling using rank value               *
2675  *-----------------------------------------------------------------------*/
2676 /*!
2677  *  pixScaleGrayRankCascade()
2678  *
2679  *      Input:  pixs (8 bpp)
2680  *              level1, ... level4 (rank thresholds, in set {0, 1, 2, 3, 4})
2681  *      Return: pixd (8 bpp, downscaled by up to 16x)
2682  *
2683  *  Notes:
2684  *      (1) This performs up to four cascaded 2x rank reductions.
2685  *      (2) Use level = 0 to truncate the cascade.
2686  */
2687 PIX *
pixScaleGrayRankCascade(PIX * pixs,l_int32 level1,l_int32 level2,l_int32 level3,l_int32 level4)2688 pixScaleGrayRankCascade(PIX     *pixs,
2689                         l_int32  level1,
2690                         l_int32  level2,
2691                         l_int32  level3,
2692                         l_int32  level4)
2693 {
2694 PIX  *pixt1, *pixt2, *pixt3, *pixt4;
2695 
2696     PROCNAME("pixScaleGrayRankCascade");
2697 
2698     if (!pixs)
2699         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2700     if (pixGetDepth(pixs) != 8)
2701         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
2702     if (level1 > 4 || level2 > 4 || level3 > 4 || level4 > 4)
2703         return (PIX *)ERROR_PTR("levels must not exceed 4", procName, NULL);
2704 
2705     if (level1 <= 0) {
2706         L_WARNING("no reduction because level1 not > 0", procName);
2707         return pixCopy(NULL, pixs);
2708     }
2709 
2710     pixt1 = pixScaleGrayRank2(pixs, level1);
2711     if (level2 <= 0)
2712         return pixt1;
2713 
2714     pixt2 = pixScaleGrayRank2(pixt1, level2);
2715     pixDestroy(&pixt1);
2716     if (level3 <= 0)
2717         return pixt2;
2718 
2719     pixt3 = pixScaleGrayRank2(pixt2, level3);
2720     pixDestroy(&pixt2);
2721     if (level4 <= 0)
2722         return pixt3;
2723 
2724     pixt4 = pixScaleGrayRank2(pixt3, level4);
2725     pixDestroy(&pixt3);
2726     return pixt4;
2727 }
2728 
2729 
2730 /*!
2731  *  pixScaleGrayRank2()
2732  *
2733  *      Input:  pixs (8 bpp)
2734  *              rank (1 (darkest), 2, 3, 4 (lightest))
2735  *      Return: pixd (8 bpp, downscaled by 2x)
2736  *
2737  *  Notes:
2738  *      (1) Rank 2x reduction.  If rank == 1(4), the downscaled pixels
2739  *          in pixd are the min(max) of the corresponding set of
2740  *          4 pixels in pixs.  Values 2 and 3 are intermediate.
2741  *      (2) This is the grayscale analog to the binary rank scaling operation
2742  *          pixReduceRankBinary2().  Here, because of the photometric
2743  *          definition that higher gray values are lighter, rank 1 gives
2744  *          the darkest pixel, whereas rank 4 gives the lightest pixel.
2745  *          This is opposite to the binary rank operation.
2746  *      (3) For rank = 1 and 4, this calls pixScaleGrayMinMax2(),
2747  *          which runs at about 70 MPix/sec/GHz of source data.
2748  *          For rank 2 and 3, this runs 3x slower, at about 25 MPix/sec/GHz.
2749  */
2750 PIX *
pixScaleGrayRank2(PIX * pixs,l_int32 rank)2751 pixScaleGrayRank2(PIX     *pixs,
2752                   l_int32  rank)
2753 {
2754 l_int32    d, ws, hs, wd, hd, wpls, wpld, i, j, k, m;
2755 l_int32    minval, maxval, rankval, minindex, maxindex;
2756 l_int32    val[4];
2757 l_int32    midval[4];  /* should only use 2 of these */
2758 l_uint32  *datas, *datad, *lines, *lined;
2759 PIX       *pixd;
2760 
2761     PROCNAME("pixScaleGrayRank2");
2762 
2763     if (!pixs)
2764         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2765     pixGetDimensions(pixs, &ws, &hs, &d);
2766     if (d != 8)
2767         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
2768     if (rank < 1 || rank > 4)
2769         return (PIX *)ERROR_PTR("invalid rank", procName, NULL);
2770 
2771     if (rank == 1)
2772         return pixScaleGrayMinMax2(pixs, L_CHOOSE_MIN);
2773     if (rank == 4)
2774         return pixScaleGrayMinMax2(pixs, L_CHOOSE_MAX);
2775 
2776     wd = ws / 2;
2777     hd = hs / 2;
2778     if ((pixd = pixCreate(wd, hd, 8)) == NULL)
2779         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2780     datas = pixGetData(pixs);
2781     datad = pixGetData(pixd);
2782     wpls = pixGetWpl(pixs);
2783     wpld = pixGetWpl(pixd);
2784     for (i = 0; i < hd; i++) {
2785         lines = datas + 2 * i * wpls;
2786         lined = datad + i * wpld;
2787         for (j = 0; j < wd; j++) {
2788             val[0] = GET_DATA_BYTE(lines, 2 * j);
2789             val[1] = GET_DATA_BYTE(lines, 2 * j + 1);
2790             val[2] = GET_DATA_BYTE(lines + wpls, 2 * j);
2791             val[3] = GET_DATA_BYTE(lines + wpls, 2 * j + 1);
2792             minval = maxval = val[0];
2793             minindex = maxindex = 0;
2794             for (k = 1; k < 4; k++) {
2795                 if (val[k] < minval) {
2796                     minval = val[k];
2797                     minindex = k;
2798                     continue;
2799                 }
2800                 if (val[k] > maxval) {
2801                     maxval = val[k];
2802                     maxindex = k;
2803                 }
2804             }
2805             for (k = 0, m = 0; k < 4; k++) {
2806                 if (k == minindex || k == maxindex)
2807                     continue;
2808                 midval[m++] = val[k];
2809             }
2810             if (m > 2)  /* minval == maxval; all val[k] are the same */
2811                 rankval = minval;
2812             else if (rank == 2)
2813                 rankval = L_MIN(midval[0], midval[1]);
2814             else  /* rank == 3 */
2815                 rankval = L_MAX(midval[0], midval[1]);
2816             SET_DATA_BYTE(lined, j, rankval);
2817         }
2818     }
2819 
2820     return pixd;
2821 }
2822 
2823