| /*====================================================================* |
| - Copyright (C) 2001 Leptonica. All rights reserved. |
| - This software is distributed in the hope that it will be |
| - useful, but with NO WARRANTY OF ANY KIND. |
| - No author or distributor accepts responsibility to anyone for the |
| - consequences of using this software, or for whether it serves any |
| - particular purpose or works at all, unless he or she says so in |
| - writing. Everyone is granted permission to copy, modify and |
| - redistribute this source code, for commercial or non-commercial |
| - purposes, with the following restrictions: (1) the origin of this |
| - source code must not be misrepresented; (2) modified versions must |
| - be plainly marked as such; and (3) this notice may not be removed |
| - or altered from any source or modified source distribution. |
| *====================================================================*/ |
| |
| |
| /* |
| * scale.c |
| * |
| * Top-level scaling |
| * PIX *pixScale() *** |
| * PIX *pixScaleGeneral() *** |
| * |
| * Linearly interpreted (usually up-) scaling |
| * PIX *pixScaleLI() *** |
| * PIX *pixScaleColorLI() |
| * PIX *pixScaleColor2xLI() *** |
| * PIX *pixScaleColor4xLI() *** |
| * PIX *pixScaleGrayLI() |
| * PIX *pixScaleGray2xLI() |
| * PIX *pixScaleGray4xLI() |
| * |
| * General scaling by closest pixel sampling |
| * PIX *pixScaleBySampling() |
| * |
| * Fast integer factor subsampling RGB to gray and to binary |
| * PIX *pixScaleRGBToGrayFast() |
| * PIX *pixScaleRGBToBinaryFast() |
| * PIX *pixScaleGrayToBinaryFast() |
| * |
| * Downscaling with (antialias) smoothing |
| * PIX *pixScaleSmooth() *** |
| * PIX *pixScaleRGBToGray2() [special 2x reduction to gray] |
| * |
| * Downscaling with (antialias) area mapping |
| * PIX *pixScaleAreaMap() *** |
| * PIX *pixScaleAreaMap2() |
| * |
| * Binary scaling by closest pixel sampling |
| * PIX *pixScaleBinary() |
| * |
| * Scale-to-gray (1 bpp --> 8 bpp, arbitrary reduction) |
| * PIX *pixScaleToGray() |
| * |
| * Scale-to-gray (1 bpp --> 8 bpp, 2x reduction) |
| * PIX *pixScaleToGray2() |
| * |
| * Scale-to-gray (1 bpp --> 8 bpp, 3x reduction) |
| * PIX *pixScaleToGray3() |
| * |
| * Scale-to-gray (1 bpp --> 8 bpp, 4x reduction) |
| * PIX *pixScaleToGray4() |
| * |
| * Scale-to-gray (1 bpp --> 8 bpp, 6x reduction) |
| * PIX *pixScaleToGray6() |
| * |
| * Scale-to-gray (1 bpp --> 8 bpp, 8x reduction) |
| * PIX *pixScaleToGray8() |
| * |
| * Scale-to-gray (1 bpp --> 8 bpp, 16x reduction) |
| * PIX *pixScaleToGray16() |
| * |
| * Scale-to-gray by mipmap(1 bpp --> 8 bpp, arbitrary reduction) |
| * PIX *pixScaleToGrayMipmap() |
| * |
| * Grayscale scaling using mipmap |
| * PIX *pixScaleMipmap() |
| * |
| * Replicated (integer) expansion (all depths) |
| * PIX *pixExpandReplicate() |
| * |
| * Upscale 2x followed by binarization |
| * PIX *pixScaleGray2xLIThresh() |
| * PIX *pixScaleGray2xLIDither() |
| * |
| * Upscale 4x followed by binarization |
| * PIX *pixScaleGray4xLIThresh() |
| * PIX *pixScaleGray4xLIDither() |
| * |
| * Grayscale downscaling using min and max |
| * PIX *pixScaleGrayMinMax() |
| * PIX *pixScaleGrayMinMax2() |
| * |
| * Grayscale downscaling using rank value |
| * PIX *pixScaleGrayRankCascade() |
| * PIX *pixScaleGrayRank2() |
| * |
| * *** Note: these functions make an implicit assumption about RGB |
| * component ordering. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "allheaders.h" |
| |
| |
| /*------------------------------------------------------------------* |
| * Top level scaling dispatcher * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScale() |
| * |
| * Input: pixs (1, 2, 4, 8, 16 and 32 bpp) |
| * scalex, scaley |
| * Return: pixd, or null on error |
| * |
| * This function scales 32 bpp RGB; 2, 4 or 8 bpp palette color; |
| * 2, 4, 8 or 16 bpp gray; and binary images. |
| * |
| * When the input has palette color, the colormap is removed and |
| * the result is either 8 bpp gray or 32 bpp RGB, depending on whether |
| * the colormap has color entries. Images with 2, 4 or 16 bpp are |
| * converted to 8 bpp. |
| * |
| * Grayscale and color images are scaled using one of four methods, |
| * depending on the scale factors: |
| * (1) antialiased subsampling (lowpass filtering followed by |
| * subsampling, implemented here by area mapping), for scale factors |
| * less than 0.7 |
| * (2) linear interpolation with sharpening, for scale factors between |
| * 0.7 and 1.4 |
| * (3) linear interpolation alone, for scale factors >= 1.4. |
| * |
| * One could use subsampling for scale factors very close to 1.0, |
| * because it preserves sharp edges. Linear interpolation blurs |
| * edges because the dest pixels will typically straddle two src edge |
| * pixels. Subsmpling removes entire columns and rows, so the edge is |
| * not blurred. However, there are two reasons for not doing this. |
| * First, it moves edges, so that a straight line at a large angle to |
| * both horizontal and vertical will have noticable kinks where |
| * horizontal and vertical rasters are removed. Second, although it |
| * is very fast, you get good results on sharp edges by applying |
| * a sharpening filter. |
| * |
| * For images with sharp edges, sharpening substantially improves the |
| * image quality for scale factors between 0.7 and about 2.0. However, |
| * the generic sharpening operation is about 3 times slower than linear |
| * interpolation, so there is a speed-vs-quality tradeoff. (Note: the |
| * special cases where the sharpening halfwidth is 1 or 2 are about |
| * twice as fast as the general case). When the scale factor is |
| * larger than 1.4, the cost, which is proportional to image area, is very |
| * large for the incremental quality improvement, so we cut off the |
| * use of sharpening at 1.4. For scale factors greater than 1.4, |
| * these high-level scaling functions only do linear interpolation. |
| * |
| * Because sharpening is computationally expensive, we provide the |
| * option of not doing it. To avoid sharpening, call pixScaleGeneral() |
| * with @sharpfract == 0.0. Note that pixScale() calls with default |
| * sharpening factors: @sharpwidth = 2, @sharpfract = 0.4. The results |
| * are generally better with a small amount of sharpening because |
| * it strengthens edge pixels that are weak due to anti-aliasing. |
| * |
| * Binary images are scaled by sampling the closest pixel, without |
| * any low-pass filtering (averaging of neighboring pixels). |
| * This will introduce aliasing for reductions, which can be |
| * prevented by using pixScaleToGray() instead. |
| * |
| * *** Warning: implicit assumption about RGB component order |
| * for LI color scaling |
| */ |
| PIX * |
| pixScale(PIX *pixs, |
| l_float32 scalex, |
| l_float32 scaley) |
| { |
| return pixScaleGeneral(pixs, scalex, scaley, 0.4, 2); |
| } |
| |
| |
| /*! |
| * pixScaleGeneral() |
| * |
| * Input: pixs (1, 2, 4, 8, 16 and 32 bpp) |
| * scalex, scaley |
| * sharpfract (use 0.0 to skip sharpening) |
| * sharpwidth (halfwidth of low-pass filter; typ. 1 or 2) |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) See pixScale() for usage. |
| * (2) This interface may change in the future, as other special |
| * cases are added. |
| * (3) Call this function with @sharpfract == 0.0 to avoid sharpening |
| * for grayscale and color images with scaling factors between |
| * 0.7 and 1.4. |
| */ |
| PIX * |
| pixScaleGeneral(PIX *pixs, |
| l_float32 scalex, |
| l_float32 scaley, |
| l_float32 sharpfract, |
| l_int32 sharpwidth) |
| { |
| l_int32 d; |
| l_float32 maxscale; |
| PIX *pixt, *pixt2, *pixd; |
| |
| PROCNAME("pixScaleGeneral"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| d = pixGetDepth(pixs); |
| if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) |
| return (PIX *)ERROR_PTR("pixs not {1,2,4,8,16,32} bpp", procName, NULL); |
| if (scalex == 1.0 && scaley == 1.0) |
| return pixCopy(NULL, pixs); |
| |
| if (d == 1) |
| return pixScaleBinary(pixs, scalex, scaley); |
| |
| /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ |
| if ((pixt = pixConvertTo8Or32(pixs, 0, 1)) == NULL) |
| return (PIX *)ERROR_PTR("pixt not made", procName, NULL); |
| |
| /* Scale (up or down) */ |
| d = pixGetDepth(pixt); |
| maxscale = L_MAX(scalex, scaley); |
| if (maxscale < 0.7) { /* area mapping for anti-aliasing */ |
| pixd = pixScaleAreaMap(pixt, scalex, scaley); |
| } |
| else { /* use linear interpolation */ |
| if (d == 8) |
| pixt2 = pixScaleGrayLI(pixt, scalex, scaley); |
| else /* d == 32 */ |
| pixt2 = pixScaleColorLI(pixt, scalex, scaley); |
| if (maxscale < 1.4 && sharpfract > 0.0 && sharpwidth > 0) |
| pixd = pixUnsharpMasking(pixt2, sharpwidth, sharpfract); |
| else |
| pixd = pixClone(pixt2); |
| pixDestroy(&pixt2); |
| } |
| |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Scaling by linear interpolation * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleLI() |
| * |
| * Input: pixs (2, 4, 8 or 32 bpp; with or without colormap) |
| * scalex, scaley (must both be >= 0.7) |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) This function should only be used when the scale factors are |
| * greater than or equal to 0.7, and typically greater than 1. |
| * If either scale factor is smaller than 0.7, we issue a warning |
| * and invoke pixScale(). |
| * (2) This works on 2, 4, 8, 16 and 32 bpp images, as well as on |
| * 2, 4 and 8 bpp images that have a colormap. If there is a |
| * colormap, it is removed to either gray or RGB, depending |
| * on the colormap. |
| * (3) The does a linear interpolation on the src image. |
| * (4) It dispatches to much faster implementations for |
| * the special cases of 2x and 4x expansion. |
| * |
| * *** Warning: implicit assumption about RGB component ordering *** |
| */ |
| PIX * |
| pixScaleLI(PIX *pixs, |
| l_float32 scalex, |
| l_float32 scaley) |
| { |
| l_int32 d; |
| PIX *pixt, *pixd; |
| |
| PROCNAME("pixScaleLI"); |
| |
| if (!pixs || (pixGetDepth(pixs) == 1)) |
| return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", procName, NULL); |
| if (scalex < 0.7 || scaley < 0.7) { |
| L_WARNING("scaling factor < 0.7; doing regular scaling", procName); |
| return pixScale(pixs, scalex, scaley); |
| } |
| d = pixGetDepth(pixs); |
| if (d != 2 && d != 4 && d != 8 && d != 16 && d != 32) |
| return (PIX *)ERROR_PTR("pixs not {2,4,8,16,32} bpp", procName, NULL); |
| |
| /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ |
| if ((pixt = pixConvertTo8Or32(pixs, 0, 1)) == NULL) |
| return (PIX *)ERROR_PTR("pixt not made", procName, NULL); |
| |
| d = pixGetDepth(pixt); |
| if (d == 8) |
| pixd = pixScaleGrayLI(pixt, scalex, scaley); |
| else if (d == 32) |
| pixd = pixScaleColorLI(pixt, scalex, scaley); |
| |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleColorLI() |
| * |
| * Input: pixs (32 bpp, representing rgb) |
| * scalex, scaley |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) If this is used for scale factors less than 0.7, |
| * it will suffer from antialiasing. A warning is issued. |
| * Particularly for document images with sharp edges, |
| * use pixScaleSmooth() or pixScaleAreaMap() instead. |
| * (2) For the general case, it's about 4x faster to manipulate |
| * the color pixels directly, rather than to make images |
| * out of each of the 3 components, scale each component |
| * using the pixScaleGrayLI(), and combine the results back |
| * into an rgb image. |
| * (3) The speed on intel hardware for the general case (not 2x) |
| * is about 10 * 10^6 dest-pixels/sec/GHz. (The special 2x |
| * case runs at about 80 * 10^6 dest-pixels/sec/GHz.) |
| */ |
| PIX * |
| pixScaleColorLI(PIX *pixs, |
| l_float32 scalex, |
| l_float32 scaley) |
| { |
| l_int32 ws, hs, wpls, wd, hd, wpld; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleColorLI"); |
| |
| if (!pixs || (pixGetDepth(pixs) != 32)) |
| return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); |
| if (scalex < 0.7 || scaley < 0.7) |
| L_WARNING("scaling factor < 0.7; should use area map", procName); |
| |
| /* Do fast special cases if possible */ |
| if (scalex == 1.0 && scaley == 1.0) |
| return pixCopy(NULL, pixs); |
| if (scalex == 2.0 && scaley == 2.0) |
| return pixScaleColor2xLI(pixs); |
| if (scalex == 4.0 && scaley == 4.0) |
| return pixScaleColor4xLI(pixs); |
| |
| /* General case */ |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| wd = (l_int32)(scalex * (l_float32)ws + 0.5); |
| hd = (l_int32)(scaley * (l_float32)hs + 0.5); |
| if ((pixd = pixCreate(wd, hd, 32)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, scalex, scaley); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| scaleColorLILow(datad, wd, hd, wpld, datas, ws, hs, wpls); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleColor2xLI() |
| * |
| * Input: pixs (32 bpp, representing rgb) |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) This is a special case of linear interpolated scaling, |
| * for 2x upscaling. It is about 8x faster than using |
| * the generic pixScaleColorLI(), and about 4x faster than |
| * using the special 2x scale function pixScaleGray2xLI() |
| * on each of the three components separately. |
| * (2) The speed on intel hardware is about |
| * 80 * 10^6 dest-pixels/sec/GHz (!!) |
| * |
| * *** Warning: implicit assumption about RGB component ordering *** |
| */ |
| PIX * |
| pixScaleColor2xLI(PIX *pixs) |
| { |
| l_int32 ws, hs, wpls, wpld; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleColor2xLI"); |
| |
| if (!pixs || (pixGetDepth(pixs) != 32)) |
| return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| if ((pixd = pixCreate(2 * ws, 2 * hs, 32)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 2.0, 2.0); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| scaleColor2xLILow(datad, wpld, datas, ws, hs, wpls); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleColor4xLI() |
| * |
| * Input: pixs (32 bpp, representing rgb) |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) This is a special case of color linear interpolated scaling, |
| * for 4x upscaling. It is about 3x faster than using |
| * the generic pixScaleColorLI(). |
| * (2) The speed on intel hardware is about |
| * 30 * 10^6 dest-pixels/sec/GHz |
| * (3) This scales each component separately, using pixScaleGray4xLI(). |
| * It would be about 4x faster to inline the color code properly, |
| * in analogy to scaleColor4xLILow(), and I leave this as |
| * an exercise for someone who really needs it. |
| */ |
| PIX * |
| pixScaleColor4xLI(PIX *pixs) |
| { |
| PIX *pixr, *pixg, *pixb; |
| PIX *pixrs, *pixgs, *pixbs; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleColor4xLI"); |
| |
| if (!pixs || (pixGetDepth(pixs) != 32)) |
| return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); |
| |
| pixr = pixGetRGBComponent(pixs, COLOR_RED); |
| pixrs = pixScaleGray4xLI(pixr); |
| pixDestroy(&pixr); |
| pixg = pixGetRGBComponent(pixs, COLOR_GREEN); |
| pixgs = pixScaleGray4xLI(pixg); |
| pixDestroy(&pixg); |
| pixb = pixGetRGBComponent(pixs, COLOR_BLUE); |
| pixbs = pixScaleGray4xLI(pixb); |
| pixDestroy(&pixb); |
| |
| if ((pixd = pixCreateRGBImage(pixrs, pixgs, pixbs)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| |
| pixDestroy(&pixrs); |
| pixDestroy(&pixgs); |
| pixDestroy(&pixbs); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleGrayLI() |
| * |
| * Input: pixs (8 bpp grayscale) |
| * scalex |
| * scaley |
| * Return: pixd, or null on error |
| * |
| * This function is appropriate for upscaling |
| * (magnification: scale factors > 1), and for a |
| * small amount of downscaling (reduction: scale |
| * factors > 0.5). For scale factors less than 0.5, |
| * the best result is obtained by area mapping, |
| * but this is very expensive. So for such large |
| * reductions, it is more appropriate to do low pass |
| * filtering followed by subsampling, a combination |
| * which is effectively a cheap form of area mapping. |
| * |
| * Some details follow. |
| * |
| * For each pixel in the dest, this does a linear |
| * interpolation of 4 neighboring pixels in the src. |
| * Specifically, consider the UL corner of src and |
| * dest pixels. The UL corner of the dest falls within |
| * a src pixel, whose four corners are the UL corners |
| * of 4 adjacent src pixels. The value of the dest |
| * is taken by linear interpolation using the values of |
| * the four src pixels and the distance of the UL corner |
| * of the dest from each corner. |
| * |
| * If the image is expanded so that the dest pixel is |
| * smaller than the src pixel, such interpolation |
| * is a reasonable approach. This interpolation is |
| * also good for a small image reduction factor that |
| * is not more than a 2x reduction. |
| * |
| * Note that the linear interpolation algorithm for scaling |
| * is identical in form to the area-mapping algorithm |
| * for grayscale rotation. The latter corresponds to a |
| * translation of each pixel without scaling. |
| * |
| * This function is NOT optimal if the scaling involves |
| * a large reduction. If the image is significantly |
| * reduced, so that the dest pixel is much larger than |
| * the src pixels, this interpolation, which is over src |
| * pixels only near the UL corner of the dest pixel, |
| * is not going to give a good area-mapping average. |
| * Because area mapping for image scaling is considerably |
| * more computationally intensive than linear interpolation, |
| * we choose not to use it. For large image reduction, |
| * linear interpolation over adjacent src pixels |
| * degenerates asymptotically to subsampling. But |
| * subsampling without a low-pass pre-filter causes |
| * aliasing by the nyquist theorem. To avoid aliasing, |
| * a low-pass filter (e.g., an averaging filter) of |
| * size roughly equal to the dest pixel (i.e., the |
| * reduction factor) should be applied to the src before |
| * subsampling. |
| * |
| * As an alternative to low-pass filtering and subsampling |
| * for large reduction factors, linear interpolation can |
| * also be done between the (widely separated) src pixels in |
| * which the corners of the dest pixel lie. This also is |
| * not optimal, as it samples src pixels only near the |
| * corners of the dest pixel, and it is not implemented. |
| * |
| * Summary: |
| * (1) If this is used for scale factors less than 0.7, |
| * it will suffer from antialiasing. A warning is issued. |
| * Particularly for document images with sharp edges, |
| * use pixScaleSmooth() or pixScaleAreaMap() instead. |
| * (2) The speed on intel hardware for the general case (not 2x) |
| * is about 13 * 10^6 dest-pixels/sec/GHz. (The special 2x |
| * case runs at about 100 * 10^6 dest-pixels/sec/GHz.) |
| */ |
| PIX * |
| pixScaleGrayLI(PIX *pixs, |
| l_float32 scalex, |
| l_float32 scaley) |
| { |
| l_int32 ws, hs, wpls, wd, hd, wpld; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleGrayLI"); |
| |
| if (!pixs || (pixGetDepth(pixs) != 8)) |
| return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); |
| if (scalex < 0.7 || scaley < 0.7) |
| L_WARNING("scaling factor < 0.7; should use area map", procName); |
| if (pixGetColormap(pixs)) |
| L_WARNING("pix has colormap; poor results are likely", procName); |
| |
| /* Do fast special cases if possible */ |
| if (scalex == 1.0 && scaley == 1.0) |
| return pixCopy(NULL, pixs); |
| if (scalex == 2.0 && scaley == 2.0) |
| return pixScaleGray2xLI(pixs); |
| if (scalex == 4.0 && scaley == 4.0) |
| return pixScaleGray4xLI(pixs); |
| |
| /* General case */ |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| wd = (l_int32)(scalex * (l_float32)ws + 0.5); |
| hd = (l_int32)(scaley * (l_float32)hs + 0.5); |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, scalex, scaley); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| scaleGrayLILow(datad, wd, hd, wpld, datas, ws, hs, wpls); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleGray2xLI() |
| * |
| * Input: pixs (8 bpp grayscale) |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) This is a special case of gray linear interpolated scaling, |
| * for 2x upscaling. It is about 6x faster than using |
| * the generic pixScaleGrayLI(). |
| * (2) The speed on intel hardware is about |
| * 100 * 10^6 dest-pixels/sec/GHz |
| */ |
| PIX * |
| pixScaleGray2xLI(PIX *pixs) |
| { |
| l_int32 ws, hs, wpls, wpld; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleGray2xLI"); |
| |
| if (!pixs || (pixGetDepth(pixs) != 8)) |
| return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); |
| if (pixGetColormap(pixs)) |
| L_WARNING("pix has colormap", procName); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| if ((pixd = pixCreate(2 * ws, 2 * hs, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 2.0, 2.0); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| scaleGray2xLILow(datad, wpld, datas, ws, hs, wpls); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleGray4xLI() |
| * |
| * Input: pixs (8 bpp grayscale) |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) This is a special case of gray linear interpolated scaling, |
| * for 4x upscaling. It is about 12x faster than using |
| * the generic pixScaleGrayLI(). |
| * (2) The speed on intel hardware is about |
| * 160 * 10^6 dest-pixels/sec/GHz (!!) |
| */ |
| PIX * |
| pixScaleGray4xLI(PIX *pixs) |
| { |
| l_int32 ws, hs, wpls, wpld; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleGray4xLI"); |
| |
| if (!pixs || (pixGetDepth(pixs) != 8)) |
| return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); |
| if (pixGetColormap(pixs)) |
| L_WARNING("pix has colormap", procName); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| if ((pixd = pixCreate(4 * ws, 4 * hs, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 4.0, 4.0); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| scaleGray4xLILow(datad, wpld, datas, ws, hs, wpls); |
| return pixd; |
| } |
| |
| |
| |
| /*------------------------------------------------------------------* |
| * General scaling by closest pixel sampling * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleBySampling() |
| * |
| * Input: pixs (1, 2, 4, 8, 16, 32 bpp) |
| * scalex, scaley |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) This function samples from the source without |
| * filtering. As a result, aliasing will occur for |
| * subsampling (scalex and/or scaley < 1.0). |
| */ |
| PIX * |
| pixScaleBySampling(PIX *pixs, |
| l_float32 scalex, |
| l_float32 scaley) |
| { |
| l_int32 ws, hs, d, wpls, wd, hd, wpld; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleBySampling"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (scalex == 1.0 && scaley == 1.0) |
| return pixCopy(NULL, pixs); |
| if ((d = pixGetDepth(pixs)) == 1) |
| return pixScaleBinary(pixs, scalex, scaley); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| wd = (l_int32)(scalex * (l_float32)ws + 0.5); |
| hd = (l_int32)(scaley * (l_float32)hs + 0.5); |
| if ((pixd = pixCreate(wd, hd, d)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, scalex, scaley); |
| pixCopyColormap(pixd, pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| scaleBySamplingLow(datad, wd, hd, wpld, datas, ws, hs, d, wpls); |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Fast integer factor subsampling RGB to gray * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleRGBToGrayFast() |
| * |
| * Input: pixs (32 bpp rgb) |
| * factor (integer reduction factor >= 1) |
| * color (one of COLOR_RED, COLOR_GREEN, COLOR_BLUE) |
| * Return: pixd (8 bpp), or null on error |
| * |
| * Notes: |
| * (1) This does simultaneous subsampling by an integer factor and |
| * extraction of the color from the RGB pix. |
| * (2) It is designed for maximum speed, and is used for quickly |
| * generating a downsized grayscale image from a higher resolution |
| * RGB image. This would typically be used for image analysis. |
| * (3) The standard color byte order (RGBA) is assumed. |
| */ |
| PIX * |
| pixScaleRGBToGrayFast(PIX *pixs, |
| l_int32 factor, |
| l_int32 color) |
| { |
| l_int32 byteval, shift; |
| l_int32 i, j, ws, hs, wd, hd, wpls, wpld; |
| l_uint32 *datas, *words, *datad, *lined; |
| l_float32 scale; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleRGBToGrayFast"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 32) |
| return (PIX *)ERROR_PTR("depth not 32 bpp", procName, NULL); |
| if (factor < 1) |
| return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); |
| |
| if (color == COLOR_RED) |
| shift = L_RED_SHIFT; |
| else if (color == COLOR_GREEN) |
| shift = L_GREEN_SHIFT; |
| else if (color == COLOR_BLUE) |
| shift = L_BLUE_SHIFT; |
| else |
| return (PIX *)ERROR_PTR("invalid color", procName, NULL); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| |
| wd = ws / factor; |
| hd = hs / factor; |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| scale = 1. / (l_float32) factor; |
| pixScaleResolution(pixd, scale, scale); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| |
| for (i = 0; i < hd; i++) { |
| words = datas + i * factor * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < wd; j++, words += factor) { |
| byteval = ((*words) >> shift) & 0xff; |
| SET_DATA_BYTE(lined, j, byteval); |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleRGBToBinaryFast() |
| * |
| * Input: pixs (32 bpp RGB) |
| * factor (integer reduction factor >= 1) |
| * thresh (binarization threshold) |
| * Return: pixd (1 bpp), or null on error |
| * |
| * Notes: |
| * (1) This does simultaneous subsampling by an integer factor and |
| * conversion from RGB to gray to binary. |
| * (2) It is designed for maximum speed, and is used for quickly |
| * generating a downsized binary image from a higher resolution |
| * RGB image. This would typically be used for image analysis. |
| * (3) It uses the green channel to represent the RGB pixel intensity. |
| */ |
| PIX * |
| pixScaleRGBToBinaryFast(PIX *pixs, |
| l_int32 factor, |
| l_int32 thresh) |
| { |
| l_int32 byteval; |
| l_int32 i, j, ws, hs, wd, hd, wpls, wpld; |
| l_uint32 *datas, *words, *datad, *lined; |
| l_float32 scale; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleRGBToBinaryFast"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (factor < 1) |
| return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); |
| if (pixGetDepth(pixs) != 32) |
| return (PIX *)ERROR_PTR("depth not 32 bpp", procName, NULL); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| |
| wd = ws / factor; |
| hd = hs / factor; |
| if ((pixd = pixCreate(wd, hd, 1)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| scale = 1. / (l_float32) factor; |
| pixScaleResolution(pixd, scale, scale); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| |
| for (i = 0; i < hd; i++) { |
| words = datas + i * factor * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < wd; j++, words += factor) { |
| byteval = ((*words) >> L_GREEN_SHIFT) & 0xff; |
| if (byteval < thresh) |
| SET_DATA_BIT(lined, j); |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleGrayToBinaryFast() |
| * |
| * Input: pixs (8 bpp grayscale) |
| * factor (integer reduction factor >= 1) |
| * thresh (binarization threshold) |
| * Return: pixd (1 bpp), or null on error |
| * |
| * Notes: |
| * (1) This does simultaneous subsampling by an integer factor and |
| * thresholding from gray to binary. |
| * (2) It is designed for maximum speed, and is used for quickly |
| * generating a downsized binary image from a higher resolution |
| * gray image. This would typically be used for image analysis. |
| */ |
| PIX * |
| pixScaleGrayToBinaryFast(PIX *pixs, |
| l_int32 factor, |
| l_int32 thresh) |
| { |
| l_int32 byteval; |
| l_int32 i, j, ws, hs, wd, hd, wpls, wpld, sj; |
| l_uint32 *datas, *datad, *lines, *lined; |
| l_float32 scale; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleGrayToBinaryFast"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (factor < 1) |
| return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); |
| if (pixGetDepth(pixs) != 8) |
| return (PIX *)ERROR_PTR("depth not 8 bpp", procName, NULL); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| |
| wd = ws / factor; |
| hd = hs / factor; |
| if ((pixd = pixCreate(wd, hd, 1)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| scale = 1. / (l_float32) factor; |
| pixScaleResolution(pixd, scale, scale); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| |
| for (i = 0; i < hd; i++) { |
| lines = datas + i * factor * wpls; |
| lined = datad + i * wpld; |
| for (j = 0, sj = 0; j < wd; j++, sj += factor) { |
| byteval = GET_DATA_BYTE(lines, sj); |
| if (byteval < thresh) |
| SET_DATA_BIT(lined, j); |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Downscaling with (antialias) smoothing * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleSmooth() |
| * |
| * Input: pixs (2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap) |
| * scalex, scaley (must both be <= 0.7) |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) This function should only be used when the scale factors are less |
| * than or equal to 0.7 (i.e., more than about 1.42x reduction). |
| * If either scale factor is larger than 0.7, we issue a warning |
| * and invoke pixScale(). |
| * (2) This works only on 2, 4, 8 and 32 bpp images, and if there is |
| * a colormap, it is removed by converting to RGB. In other |
| * cases, we issue a warning and invoke pixScale(). |
| * (3) It does simple (flat filter) convolution, with a filter size |
| * commensurate with the amount of reduction, to avoid antialiasing. |
| * (4) It does simple subsampling after smoothing, which is appropriate |
| * for this range of scaling. Linear interpolation gives essentially |
| * the same result with more computation for these scale factors, |
| * so we don't use it. |
| * (5) The result is the same as doing a full block convolution followed by |
| * subsampling, but this is faster because the results of the block |
| * convolution are only computed at the subsampling locations. |
| * In fact, the computation time is approximately independent of |
| * the scale factor, because the convolution kernel is adjusted |
| * so that each source pixel is summed approximately once. |
| * |
| * *** Warning: implicit assumption about RGB component ordering *** |
| */ |
| PIX * |
| pixScaleSmooth(PIX *pix, |
| l_float32 scalex, |
| l_float32 scaley) |
| { |
| l_int32 ws, hs, d, wd, hd, wpls, wpld, isize; |
| l_uint32 *datas, *datad; |
| l_float32 minscale, size; |
| PIX *pixs, *pixd; |
| |
| PROCNAME("pixScaleSmooth"); |
| |
| if (!pix) |
| return (PIX *)ERROR_PTR("pix not defined", procName, NULL); |
| if (scalex > 0.7 || scaley > 0.7) { |
| L_WARNING("scaling factor not <= 0.7; doing regular scaling", procName); |
| return pixScale(pix, scalex, scaley); |
| } |
| |
| /* Remove colormap if necessary. |
| * If 2 bpp or 4 bpp gray, convert to 8 bpp */ |
| d = pixGetDepth(pix); |
| if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { |
| L_WARNING("pix has colormap; removing", procName); |
| pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); |
| d = pixGetDepth(pixs); |
| } |
| else if (d == 2 || d == 4) { |
| pixs = pixConvertTo8(pix, FALSE); |
| d = 8; |
| } |
| else |
| pixs = pixClone(pix); |
| |
| if (d != 8 && d != 32) { /* d == 1 or d == 16 */ |
| L_WARNING("depth not 8 or 32 bpp; doing regular scaling", procName); |
| pixDestroy(&pixs); |
| return pixScale(pix, scalex, scaley); |
| } |
| |
| /* If 1.42 < 1/minscale < 2.5, use isize = 2 |
| * If 2.5 =< 1/minscale < 3.5, use isize = 3, etc. |
| * Under no conditions use isize < 2 */ |
| minscale = L_MIN(scalex, scaley); |
| size = 1.0 / minscale; /* ideal filter full width */ |
| isize = L_MAX(2, (l_int32)(size + 0.5)); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| if ((ws < isize) || (hs < isize)) { |
| pixDestroy(&pixs); |
| return (PIX *)ERROR_PTR("pixs too small", procName, NULL); |
| } |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| wd = (l_int32)(scalex * (l_float32)ws + 0.5); |
| hd = (l_int32)(scaley * (l_float32)hs + 0.5); |
| if (wd < 1 || hd < 1) { |
| pixDestroy(&pixs); |
| return (PIX *)ERROR_PTR("pixd too small", procName, NULL); |
| } |
| if ((pixd = pixCreate(wd, hd, d)) == NULL) { |
| pixDestroy(&pixs); |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| } |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, scalex, scaley); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| scaleSmoothLow(datad, wd, hd, wpld, datas, ws, hs, d, wpls, isize); |
| |
| pixDestroy(&pixs); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleRGBToGray2() |
| * |
| * Input: pixs (32 bpp rgb) |
| * rwt, gwt, bwt (must sum to 1.0) |
| * Return: pixd, (8 bpp, 2x reduced), or null on error |
| */ |
| PIX * |
| pixScaleRGBToGray2(PIX *pixs, |
| l_float32 rwt, |
| l_float32 gwt, |
| l_float32 bwt) |
| { |
| l_int32 wd, hd, wpls, wpld; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleRGBToGray2"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 32) |
| return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); |
| if (rwt + gwt + bwt < 0.98 || rwt + gwt + bwt > 1.02) |
| return (PIX *)ERROR_PTR("sum of wts should be 1.0", procName, NULL); |
| |
| wd = pixGetWidth(pixs) / 2; |
| hd = pixGetHeight(pixs) / 2; |
| wpls = pixGetWpl(pixs); |
| datas = pixGetData(pixs); |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 0.5, 0.5); |
| wpld = pixGetWpl(pixd); |
| datad = pixGetData(pixd); |
| scaleRGBToGray2Low(datad, wd, hd, wpld, datas, wpls, rwt, gwt, bwt); |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Downscaling with (antialias) area mapping * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleAreaMap() |
| * |
| * Input: pixs (2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap) |
| * scalex, scaley (must both be <= 0.7) |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) This function should only be used when the scale factors are less |
| * than or equal to 0.7 (i.e., more than about 1.42x reduction). |
| * If either scale factor is larger than 0.7, we issue a warning |
| * and invoke pixScale(). |
| * (2) This works only on 2, 4, 8 and 32 bpp images. If there is |
| * a colormap, it is removed by converting to RGB. In other |
| * cases, we issue a warning and invoke pixScale(). |
| * (3) It does a relatively expensive area mapping computation, to |
| * avoid antialiasing. It is about 2x slower than pixScaleSmooth(), |
| * but the results are much better on fine text. |
| * (4) This is typically about 20% faster for the special cases of |
| * 2x, 4x, 8x and 16x reduction. |
| * (5) Surprisingly, there is no speedup (and a slight quality |
| * impairment) if you do as many successive 2x reductions as |
| * possible, ending with a reduction with a scale factor larger |
| * than 0.5. |
| * |
| * *** Warning: implicit assumption about RGB component ordering *** |
| */ |
| PIX * |
| pixScaleAreaMap(PIX *pix, |
| l_float32 scalex, |
| l_float32 scaley) |
| { |
| l_int32 ws, hs, d, wd, hd, wpls, wpld; |
| l_uint32 *datas, *datad; |
| PIX *pixs, *pixd, *pixt1, *pixt2, *pixt3; |
| |
| PROCNAME("pixScaleAreaMap"); |
| |
| if (!pix) |
| return (PIX *)ERROR_PTR("pix not defined", procName, NULL); |
| d = pixGetDepth(pix); |
| if (d != 2 && d != 4 && d != 8 && d != 32) |
| return (PIX *)ERROR_PTR("pix not 2, 4, 8 or 32 bpp", procName, NULL); |
| if (scalex > 0.7 || scaley > 0.7) { |
| L_WARNING("scaling factor not <= 0.7; doing regular scaling", procName); |
| return pixScale(pix, scalex, scaley); |
| } |
| |
| /* Special cases: 2x, 4x, 8x, 16x reduction */ |
| if (scalex == 0.5 && scaley == 0.5) |
| return pixScaleAreaMap2(pix); |
| if (scalex == 0.25 && scaley == 0.25) { |
| pixt1 = pixScaleAreaMap2(pix); |
| pixd = pixScaleAreaMap2(pixt1); |
| pixDestroy(&pixt1); |
| return pixd; |
| } |
| if (scalex == 0.125 && scaley == 0.125) { |
| pixt1 = pixScaleAreaMap2(pix); |
| pixt2 = pixScaleAreaMap2(pixt1); |
| pixd = pixScaleAreaMap2(pixt2); |
| pixDestroy(&pixt1); |
| pixDestroy(&pixt2); |
| return pixd; |
| } |
| if (scalex == 0.0625 && scaley == 0.0625) { |
| pixt1 = pixScaleAreaMap2(pix); |
| pixt2 = pixScaleAreaMap2(pixt1); |
| pixt3 = pixScaleAreaMap2(pixt2); |
| pixd = pixScaleAreaMap2(pixt3); |
| pixDestroy(&pixt1); |
| pixDestroy(&pixt2); |
| pixDestroy(&pixt3); |
| return pixd; |
| } |
| |
| /* Remove colormap if necessary. |
| * If 2 bpp or 4 bpp gray, convert to 8 bpp */ |
| if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { |
| L_WARNING("pix has colormap; removing", procName); |
| pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); |
| d = pixGetDepth(pixs); |
| } |
| else if (d == 2 || d == 4) { |
| pixs = pixConvertTo8(pix, FALSE); |
| d = 8; |
| } |
| else |
| pixs = pixClone(pix); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| wd = (l_int32)(scalex * (l_float32)ws + 0.5); |
| hd = (l_int32)(scaley * (l_float32)hs + 0.5); |
| if (wd < 1 || hd < 1) { |
| pixDestroy(&pixs); |
| return (PIX *)ERROR_PTR("pixd too small", procName, NULL); |
| } |
| if ((pixd = pixCreate(wd, hd, d)) == NULL) { |
| pixDestroy(&pixs); |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| } |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, scalex, scaley); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| if (d == 8) |
| scaleGrayAreaMapLow(datad, wd, hd, wpld, datas, ws, hs, wpls); |
| else /* RGB, d == 32 */ |
| scaleColorAreaMapLow(datad, wd, hd, wpld, datas, ws, hs, wpls); |
| |
| pixDestroy(&pixs); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleAreaMap2() |
| * |
| * Input: pixs (2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap) |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) This function does an area mapping (average) for 2x |
| * reduction. |
| * (2) This works only on 2, 4, 8 and 32 bpp images. If there is |
| * a colormap, it is removed by converting to RGB. |
| * (3) Speed on 3 GHz processor: |
| * Color: 160 Mpix/sec |
| * Gray: 700 Mpix/sec |
| * This contrasts with the speed of the general pixScaleAreaMap(): |
| * Color: 35 Mpix/sec |
| * Gray: 50 Mpix/sec |
| * (4) From (3), we see that this special function is about 4.5x |
| * faster for color and 14x faster for grayscale |
| * (5) Consequently, pixScaleAreaMap2() is incorporated into the |
| * general area map scaling function, for the special cases |
| * of 2x, 4x, 8x and 16x reduction. |
| */ |
| PIX * |
| pixScaleAreaMap2(PIX *pix) |
| { |
| l_int32 wd, hd, d, wpls, wpld; |
| l_uint32 *datas, *datad; |
| PIX *pixs, *pixd; |
| |
| PROCNAME("pixScaleAreaMap2"); |
| |
| if (!pix) |
| return (PIX *)ERROR_PTR("pix not defined", procName, NULL); |
| d = pixGetDepth(pix); |
| if (d != 2 && d != 4 && d != 8 && d != 32) |
| return (PIX *)ERROR_PTR("pix not 2, 4, 8 or 32 bpp", procName, NULL); |
| |
| /* Remove colormap if necessary. |
| * If 2 bpp or 4 bpp gray, convert to 8 bpp */ |
| if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { |
| L_WARNING("pix has colormap; removing", procName); |
| pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); |
| d = pixGetDepth(pixs); |
| } |
| else if (d == 2 || d == 4) { |
| pixs = pixConvertTo8(pix, FALSE); |
| d = 8; |
| } |
| else |
| pixs = pixClone(pix); |
| |
| wd = pixGetWidth(pixs) / 2; |
| hd = pixGetHeight(pixs) / 2; |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| pixd = pixCreate(wd, hd, d); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 0.5, 0.5); |
| scaleAreaMapLow2(datad, wd, hd, wpld, datas, d, wpls); |
| pixDestroy(&pixs); |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Binary scaling by closest pixel sampling * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleBinary() |
| * |
| * Input: pixs (1 bpp) |
| * scalex, scaley |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) This function samples from the source without |
| * filtering. As a result, aliasing will occur for |
| * subsampling (scalex and scaley < 1.0). |
| */ |
| PIX * |
| pixScaleBinary(PIX *pixs, |
| l_float32 scalex, |
| l_float32 scaley) |
| { |
| l_int32 ws, hs, wpls, wd, hd, wpld; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleBinary"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL); |
| if (scalex == 1.0 && scaley == 1.0) |
| return pixCopy(NULL, pixs); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| wd = (l_int32)(scalex * (l_float32)ws + 0.5); |
| hd = (l_int32)(scaley * (l_float32)hs + 0.5); |
| if ((pixd = pixCreate(wd, hd, 1)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyColormap(pixd, pixs); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, scalex, scaley); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| scaleBinaryLow(datad, wd, hd, wpld, datas, ws, hs, wpls); |
| return pixd; |
| } |
| |
| |
| |
| /*------------------------------------------------------------------* |
| * Scale-to-gray (1 bpp --> 8 bpp, arbitrary reduction) * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleToGray() |
| * |
| * Input: pixs (1 bpp) |
| * scalefactor (reduction, < 1.0) |
| * Return: pixd (8 bpp), scaled down by scalefactor in each direction, |
| * or NULL on error. |
| * |
| * Notes: |
| * |
| * Binary images have sharp edges, so they intrinsically have very |
| * high frequency content. To avoid aliasing, they must be low-pass |
| * filtered, which tends to blur the edges. How can we keep relatively |
| * crisp edges without aliasing? The trick is to do binary upscaling |
| * followed by a power-of-2 scaleToGray. For large reductions, where |
| * you don't end up with much detail, some corners can be cut. |
| * |
| * The intent here is to get high quality reduced grayscale |
| * images with relatively little computation. We do binary |
| * pre-scaling followed by scaleToGrayN() for best results, |
| * esp. to avoid excess blur when the scale factor is near |
| * an inverse power of 2. Where a low-pass filter is required, |
| * we use simple convolution kernels: either the hat filter for |
| * linear interpolation or a flat filter for larger downscaling. |
| * Other choices, such as a perfect bandpass filter with infinite extent |
| * (the sinc) or various approximations to it (e.g., lanczos), are |
| * unnecessarily expensive. |
| * |
| * The choices made are as follows: |
| * (1) Do binary upscaling before scaleToGrayN() for scalefactors > 1/8 |
| * (2) Do binary downscaling before scaleToGray8() for scalefactors |
| * between 1/16 and 1/8. |
| * (3) Use scaleToGray16() before grayscale downscaling for |
| * scalefactors less than 1/16 |
| * Another reasonable choice would be to start binary downscaling |
| * for scalefactors below 1/4, rather than below 1/8 as we do here. |
| * |
| * The general scaling rules, not all of which are used here, go as follows: |
| * (1) For grayscale upscaling, use pixScaleGrayLI(). However, |
| * note that edges will be visibly blurred for scalefactors |
| * near (but above) 1.0. Replication will avoid edge blur, |
| * and should be considered for factors very near 1.0. |
| * (2) For grayscale downscaling with a scale factor larger than |
| * about 0.7, use pixScaleGrayLI(). For scalefactors near |
| * (but below) 1.0, you tread between Scylla and Charybdis. |
| * pixScaleGrayLI() again gives edge blurring, but |
| * pixScaleBySampling() gives visible aliasing. |
| * (3) For grayscale downscaling with a scale factor smaller than |
| * about 0.7, use pixScaleSmooth() |
| * (4) For binary input images, do as much scale to gray as possible |
| * using the special integer functions (2, 3, 4, 8 and 16). |
| * (5) It is better to upscale in binary, followed by scaleToGrayN() |
| * than to do scaleToGrayN() followed by an upscale using either |
| * LI or oversampling. |
| * (6) It may be better to downscale in binary, followed by |
| * scaleToGrayN() than to first use scaleToGrayN() followed by |
| * downscaling. For downscaling between 8x and 16x, this is |
| * a reasonable option. |
| * (7) For reductions greater than 16x, it's reasonable to use |
| * scaleToGray16() followed by further grayscale downscaling. |
| */ |
| PIX * |
| pixScaleToGray(PIX *pixs, |
| l_float32 scalefactor) |
| { |
| l_int32 w, h, minsrc, mindest; |
| l_float32 mag, red; |
| PIX *pixt, *pixd; |
| |
| PROCNAME("pixScaleToGray"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); |
| if (scalefactor >= 1.0) |
| return (PIX *)ERROR_PTR("scalefactor not < 1.0", procName, NULL); |
| pixGetDimensions(pixs, &w, &h, NULL); |
| minsrc = L_MIN(w, h); |
| mindest = (l_int32)((l_float32)minsrc * scalefactor); |
| if (mindest < 2) |
| return (PIX *)ERROR_PTR("scalefactor too small", procName, NULL); |
| |
| if (scalefactor > 0.5) { /* see note (5) */ |
| mag = 2.0 * scalefactor; /* will be < 2.0 */ |
| /* fprintf(stderr, "2x with mag %7.3f\n", mag); */ |
| if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) |
| return (PIX *)ERROR_PTR("pixt not made", procName, NULL); |
| pixd = pixScaleToGray2(pixt); |
| } |
| else if (scalefactor == 0.5) |
| return pixd = pixScaleToGray2(pixs); |
| else if (scalefactor > 0.33333) { /* see note (5) */ |
| mag = 3.0 * scalefactor; /* will be < 1.5 */ |
| /* fprintf(stderr, "3x with mag %7.3f\n", mag); */ |
| if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) |
| return (PIX *)ERROR_PTR("pixt not made", procName, NULL); |
| pixd = pixScaleToGray3(pixt); |
| } |
| else if (scalefactor > 0.25) { /* see note (5) */ |
| mag = 4.0 * scalefactor; /* will be < 1.3333 */ |
| /* fprintf(stderr, "4x with mag %7.3f\n", mag); */ |
| if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) |
| return (PIX *)ERROR_PTR("pixt not made", procName, NULL); |
| pixd = pixScaleToGray4(pixt); |
| } |
| else if (scalefactor == 0.25) |
| return pixd = pixScaleToGray4(pixs); |
| else if (scalefactor > 0.16667) { /* see note (5) */ |
| mag = 6.0 * scalefactor; /* will be < 1.5 */ |
| /* fprintf(stderr, "6x with mag %7.3f\n", mag); */ |
| if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) |
| return (PIX *)ERROR_PTR("pixt not made", procName, NULL); |
| pixd = pixScaleToGray6(pixt); |
| } |
| else if (scalefactor == 0.16667) |
| return pixd = pixScaleToGray6(pixs); |
| else if (scalefactor > 0.125) { /* see note (5) */ |
| mag = 8.0 * scalefactor; /* will be < 1.3333 */ |
| /* fprintf(stderr, "8x with mag %7.3f\n", mag); */ |
| if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) |
| return (PIX *)ERROR_PTR("pixt not made", procName, NULL); |
| pixd = pixScaleToGray8(pixt); |
| } |
| else if (scalefactor == 0.125) |
| return pixd = pixScaleToGray8(pixs); |
| else if (scalefactor > 0.0625) { /* see note (6) */ |
| red = 8.0 * scalefactor; /* will be > 0.5 */ |
| /* fprintf(stderr, "8x with red %7.3f\n", red); */ |
| if ((pixt = pixScaleBinary(pixs, red, red)) == NULL) |
| return (PIX *)ERROR_PTR("pixt not made", procName, NULL); |
| pixd = pixScaleToGray8(pixt); |
| } |
| else if (scalefactor == 0.0625) |
| return pixd = pixScaleToGray16(pixs); |
| else { /* see note (7) */ |
| red = 16.0 * scalefactor; /* will be <= 1.0 */ |
| /* fprintf(stderr, "16x with red %7.3f\n", red); */ |
| if ((pixt = pixScaleToGray16(pixs)) == NULL) |
| return (PIX *)ERROR_PTR("pixt not made", procName, NULL); |
| if (red < 0.7) |
| pixd = pixScaleSmooth(pixt, red, red); /* see note (3) */ |
| else |
| pixd = pixScaleGrayLI(pixt, red, red); /* see note (2) */ |
| } |
| |
| pixDestroy(&pixt); |
| if (!pixd) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| else |
| return pixd; |
| } |
| |
| |
| |
| /*------------------------------------------------------------------* |
| * Scale-to-gray (1 bpp --> 8 bpp, 2x reduction) * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleToGray2() |
| * |
| * Input: pixs (1 bpp) |
| * Return: pixd (8 bpp), scaled down by 2x in each direction, |
| * or null on error. |
| */ |
| PIX * |
| pixScaleToGray2(PIX *pixs) |
| { |
| l_uint8 *valtab; |
| l_int32 ws, hs, wd, hd; |
| l_int32 wpld, wpls; |
| l_uint32 *sumtab; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleToGray2"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| wd = ws / 2; |
| hd = hs / 2; |
| if (wd == 0 || hd == 0) |
| return (PIX *)ERROR_PTR("pixs too small", procName, NULL); |
| |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 0.5, 0.5); |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| wpls = pixGetWpl(pixs); |
| wpld = pixGetWpl(pixd); |
| |
| if ((sumtab = makeSumTabSG2()) == NULL) |
| return (PIX *)ERROR_PTR("sumtab not made", procName, NULL); |
| if ((valtab = makeValTabSG2()) == NULL) |
| return (PIX *)ERROR_PTR("valtab not made", procName, NULL); |
| |
| scaleToGray2Low(datad, wd, hd, wpld, datas, wpls, sumtab, valtab); |
| |
| FREE(sumtab); |
| FREE(valtab); |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Scale-to-gray (1 bpp --> 8 bpp, 3x reduction) * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleToGray3() |
| * |
| * Input: pixs (1 bpp) |
| * Return: pixd (8 bpp), scaled down by 3x in each direction, |
| * or null on error. |
| * |
| * Notes: |
| * (1) Speed is about 100 x 10^6 src-pixels/sec/GHz. |
| * Another way to express this is it processes 1 src pixel |
| * in about 10 cycles. |
| * (2) The width of pixd is truncated is truncated to a factor of 8. |
| */ |
| PIX * |
| pixScaleToGray3(PIX *pixs) |
| { |
| l_uint8 *valtab; |
| l_int32 ws, hs, wd, hd; |
| l_int32 wpld, wpls; |
| l_uint32 *sumtab; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleToGray3"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| wd = (ws / 3) & 0xfffffff8; /* truncate to factor of 8 */ |
| hd = hs / 3; |
| if (wd == 0 || hd == 0) |
| return (PIX *)ERROR_PTR("pixs too small", procName, NULL); |
| |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 0.33333, 0.33333); |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| wpls = pixGetWpl(pixs); |
| wpld = pixGetWpl(pixd); |
| |
| if ((sumtab = makeSumTabSG3()) == NULL) |
| return (PIX *)ERROR_PTR("sumtab not made", procName, NULL); |
| if ((valtab = makeValTabSG3()) == NULL) |
| return (PIX *)ERROR_PTR("valtab not made", procName, NULL); |
| |
| scaleToGray3Low(datad, wd, hd, wpld, datas, wpls, sumtab, valtab); |
| |
| FREE(sumtab); |
| FREE(valtab); |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Scale-to-gray 4x * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleToGray4() |
| * |
| * Input: pixs (1 bpp) |
| * Return: pixd (8 bpp), scaled down by 4x in each direction, |
| * or null on error. |
| * |
| * Notes: |
| * (1) The width of pixd is truncated is truncated to a factor of 2. |
| */ |
| PIX * |
| pixScaleToGray4(PIX *pixs) |
| { |
| l_uint8 *valtab; |
| l_int32 ws, hs, wd, hd; |
| l_int32 wpld, wpls; |
| l_uint32 *sumtab; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleToGray4"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| wd = (ws / 4) & 0xfffffffe; /* truncate to factor of 2 */ |
| hd = hs / 4; |
| if (wd == 0 || hd == 0) |
| return (PIX *)ERROR_PTR("pixs too small", procName, NULL); |
| |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 0.25, 0.25); |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| wpls = pixGetWpl(pixs); |
| wpld = pixGetWpl(pixd); |
| |
| if ((sumtab = makeSumTabSG4()) == NULL) |
| return (PIX *)ERROR_PTR("sumtab not made", procName, NULL); |
| if ((valtab = makeValTabSG4()) == NULL) |
| return (PIX *)ERROR_PTR("valtab not made", procName, NULL); |
| |
| scaleToGray4Low(datad, wd, hd, wpld, datas, wpls, sumtab, valtab); |
| |
| FREE(sumtab); |
| FREE(valtab); |
| return pixd; |
| } |
| |
| |
| |
| /*------------------------------------------------------------------* |
| * Scale-to-gray (1 bpp --> 8 bpp, 6x reduction) * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleToGray6() |
| * |
| * Input: pixs (1 bpp) |
| * Return: pixd (8 bpp), scaled down by 6x in each direction, |
| * or null on error. |
| * |
| * Notes: |
| * (1) The width of pixd is truncated is truncated to a factor of 8. |
| */ |
| PIX * |
| pixScaleToGray6(PIX *pixs) |
| { |
| l_uint8 *valtab; |
| l_int32 ws, hs, wd, hd, wpld, wpls; |
| l_int32 *tab8; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleToGray6"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| wd = (ws / 6) & 0xfffffff8; /* truncate to factor of 8 */ |
| hd = hs / 6; |
| if (wd == 0 || hd == 0) |
| return (PIX *)ERROR_PTR("pixs too small", procName, NULL); |
| |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 0.16667, 0.16667); |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| wpls = pixGetWpl(pixs); |
| wpld = pixGetWpl(pixd); |
| |
| if ((tab8 = makePixelSumTab8()) == NULL) |
| return (PIX *)ERROR_PTR("tab8 not made", procName, NULL); |
| if ((valtab = makeValTabSG6()) == NULL) |
| return (PIX *)ERROR_PTR("valtab not made", procName, NULL); |
| |
| scaleToGray6Low(datad, wd, hd, wpld, datas, wpls, tab8, valtab); |
| |
| FREE(tab8); |
| FREE(valtab); |
| return pixd; |
| } |
| |
| |
| |
| /*------------------------------------------------------------------* |
| * Scale-to-gray 8x * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleToGray8() |
| * |
| * Input: pixs (1 bpp) |
| * Return: pixd (8 bpp), scaled down by 8x in each direction, |
| * or null on error |
| */ |
| PIX * |
| pixScaleToGray8(PIX *pixs) |
| { |
| l_uint8 *valtab; |
| l_int32 ws, hs, wd, hd; |
| l_int32 wpld, wpls; |
| l_int32 *tab8; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleToGray8"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| wd = ws / 8; /* truncate to nearest dest byte */ |
| hd = hs / 8; |
| if (wd == 0 || hd == 0) |
| return (PIX *)ERROR_PTR("pixs too small", procName, NULL); |
| |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 0.125, 0.125); |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| wpls = pixGetWpl(pixs); |
| wpld = pixGetWpl(pixd); |
| |
| if ((tab8 = makePixelSumTab8()) == NULL) |
| return (PIX *)ERROR_PTR("tab8 not made", procName, NULL); |
| if ((valtab = makeValTabSG8()) == NULL) |
| return (PIX *)ERROR_PTR("valtab not made", procName, NULL); |
| |
| scaleToGray8Low(datad, wd, hd, wpld, datas, wpls, tab8, valtab); |
| |
| FREE(tab8); |
| FREE(valtab); |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Scale-to-gray 16x * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleToGray16() |
| * |
| * Input: pixs (1 bpp) |
| * Return: pixd (8 bpp), scaled down by 16x in each direction, |
| * or null on error. |
| */ |
| PIX * |
| pixScaleToGray16(PIX *pixs) |
| { |
| l_int32 ws, hs, wd, hd; |
| l_int32 wpld, wpls; |
| l_int32 *tab8; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleToGray16"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| wd = ws / 16; |
| hd = hs / 16; |
| if (wd == 0 || hd == 0) |
| return (PIX *)ERROR_PTR("pixs too small", procName, NULL); |
| |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 0.0625, 0.0625); |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| wpls = pixGetWpl(pixs); |
| wpld = pixGetWpl(pixd); |
| |
| if ((tab8 = makePixelSumTab8()) == NULL) |
| return (PIX *)ERROR_PTR("tab8 not made", procName, NULL); |
| |
| scaleToGray16Low(datad, wd, hd, wpld, datas, wpls, tab8); |
| |
| FREE(tab8); |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Scale-to-gray mipmap(1 bpp --> 8 bpp, arbitrary reduction) * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleToGrayMipmap() |
| * |
| * Input: pixs (1 bpp) |
| * scalefactor (reduction, < 1.0) |
| * Return: pixd (8 bpp), scaled down by scalefactor in each direction, |
| * or NULL on error. |
| * |
| * Notes: |
| * |
| * This function is here mainly for pedagogical reasons. |
| * Mip-mapping is widely used in graphics for texture mapping, because |
| * the texture changes smoothly with scale. This is accomplished by |
| * constructing a multiresolution pyramid and, for each pixel, |
| * doing a linear interpolation between corresponding pixels in |
| * the two planes of the pyramid that bracket the desired resolution. |
| * The computation is very efficient, and is implemented in hardware |
| * in high-end graphics cards. |
| * |
| * We can use mip-mapping for scale-to-gray by using two scale-to-gray |
| * reduced images (we don't need the entire pyramid) selected from |
| * the set {2x, 4x, ... 16x}, and interpolating. However, we get |
| * severe aliasing, probably because we are subsampling from the |
| * higher resolution image. The method is very fast, but the result |
| * is very poor. In fact, the results don't look any better than |
| * either subsampling off the higher-res grayscale image or oversampling |
| * on the lower-res image. Consequently, this method should NOT be used |
| * for generating reduced images, scale-to-gray or otherwise. |
| */ |
| PIX * |
| pixScaleToGrayMipmap(PIX *pixs, |
| l_float32 scalefactor) |
| { |
| l_int32 w, h, minsrc, mindest; |
| l_float32 red; |
| PIX *pixs1, *pixs2, *pixt, *pixd; |
| |
| PROCNAME("pixScaleToGrayMipmap"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); |
| if (scalefactor >= 1.0) |
| return (PIX *)ERROR_PTR("scalefactor not < 1.0", procName, NULL); |
| pixGetDimensions(pixs, &w, &h, NULL); |
| minsrc = L_MIN(w, h); |
| mindest = (l_int32)((l_float32)minsrc * scalefactor); |
| if (mindest < 2) |
| return (PIX *)ERROR_PTR("scalefactor too small", procName, NULL); |
| |
| if (scalefactor > 0.5) { |
| pixs1 = pixConvert1To8(NULL, pixs, 255, 0); |
| pixs2 = pixScaleToGray2(pixs); |
| red = scalefactor; |
| } |
| else if (scalefactor == 0.5) { |
| return pixScaleToGray2(pixs); |
| } |
| else if (scalefactor > 0.25) { |
| pixs1 = pixScaleToGray2(pixs); |
| pixs2 = pixScaleToGray4(pixs); |
| red = 2. * scalefactor; |
| } |
| else if (scalefactor == 0.25) { |
| return pixScaleToGray4(pixs); |
| } |
| else if (scalefactor > 0.125) { |
| pixs1 = pixScaleToGray4(pixs); |
| pixs2 = pixScaleToGray8(pixs); |
| red = 4. * scalefactor; |
| } |
| else if (scalefactor == 0.125) { |
| return pixScaleToGray8(pixs); |
| } |
| else if (scalefactor > 0.0625) { |
| pixs1 = pixScaleToGray8(pixs); |
| pixs2 = pixScaleToGray16(pixs); |
| red = 8. * scalefactor; |
| } |
| else if (scalefactor == 0.0625) { |
| return pixScaleToGray16(pixs); |
| } |
| else { /* end of the pyramid; just do it */ |
| red = 16.0 * scalefactor; /* will be <= 1.0 */ |
| if ((pixt = pixScaleToGray16(pixs)) == NULL) |
| return (PIX *)ERROR_PTR("pixt not made", procName, NULL); |
| if (red < 0.7) |
| pixd = pixScaleSmooth(pixt, red, red); |
| else |
| pixd = pixScaleGrayLI(pixt, red, red); |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| |
| pixd = pixScaleMipmap(pixs1, pixs2, red); |
| |
| pixDestroy(&pixs1); |
| pixDestroy(&pixs2); |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Grayscale scaling using mipmap * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleMipmap() |
| * |
| * Input: pixs1 (high res 8 bpp) |
| * pixs2 (low res -- 2x reduced -- 8 bpp) |
| * scale (reduction with respect to high res image, > 0.5) |
| * Return: 8 bpp pix, scaled down by reduction in each direction, |
| * or NULL on error. |
| * |
| * Notes: |
| * (1) See notes in pixScaleToGrayMipmap(). |
| * (2) This function suffers from aliasing effects that are |
| * easily seen in document images. |
| */ |
| PIX * |
| pixScaleMipmap(PIX *pixs1, |
| PIX *pixs2, |
| l_float32 scale) |
| { |
| l_int32 ws1, hs1, ds1, ws2, hs2, ds2, wd, hd, wpls1, wpls2, wpld; |
| l_uint32 *datas1, *datas2, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleMipmap"); |
| |
| if (!pixs1) |
| return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); |
| if (!pixs2) |
| return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); |
| pixGetDimensions(pixs1, &ws1, &hs1, &ds1); |
| pixGetDimensions(pixs2, &ws2, &hs2, &ds2); |
| if (ds1 != 8 || ds2 != 8) |
| return (PIX *)ERROR_PTR("pixs1, pixs2 not both 8 bpp", procName, NULL); |
| if (scale > 1.0 || scale < 0.5) |
| return (PIX *)ERROR_PTR("scale not in [0.5, 1.0]", procName, NULL); |
| if (pixGetColormap(pixs1) || pixGetColormap(pixs2)) |
| L_WARNING("pixs1 or pixs2 has colormap", procName); |
| if (ws1 < 2 * ws2) |
| return (PIX *)ERROR_PTR("invalid width ratio", procName, NULL); |
| if (hs1 < 2 * hs2) |
| return (PIX *)ERROR_PTR("invalid height ratio", procName, NULL); |
| |
| /* Generate wd and hd from the lower resolution dimensions, |
| * to guarantee staying within both src images */ |
| datas1 = pixGetData(pixs1); |
| wpls1 = pixGetWpl(pixs1); |
| datas2 = pixGetData(pixs2); |
| wpls2 = pixGetWpl(pixs2); |
| wd = (l_int32)(2. * scale * pixGetWidth(pixs2)); |
| hd = (l_int32)(2. * scale * pixGetHeight(pixs2)); |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs1); |
| pixScaleResolution(pixd, scale, scale); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| |
| scaleMipmapLow(datad, wd, hd, wpld, datas1, wpls1, datas2, wpls2, scale); |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Replicated (integer) expansion * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixExpandReplicate() |
| * |
| * Input: pixs (1, 2, 4, 8, 16, 32 bpp) |
| * factor (integer scale factor for replicative expansion) |
| * Return: pixd (scaled up), or null on error. |
| */ |
| PIX * |
| pixExpandReplicate(PIX *pixs, |
| l_int32 factor) |
| { |
| l_int32 w, h, d, wd, hd, wpls, wpld, bpld, start, i, j, k; |
| l_uint8 sval; |
| l_uint16 sval16; |
| l_uint32 sval32; |
| l_uint32 *lines, *datas, *lined, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixExpandReplicate"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| pixGetDimensions(pixs, &w, &h, &d); |
| if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) |
| return (PIX *)ERROR_PTR("depth not in {1,2,4,8,16,32}", procName, NULL); |
| if (factor <= 0) |
| return (PIX *)ERROR_PTR("factor <= 0; invalid", procName, NULL); |
| if (factor == 1) |
| return pixCopy(NULL, pixs); |
| |
| if (d == 1) |
| return pixExpandBinaryReplicate(pixs, factor); |
| |
| wd = factor * w; |
| hd = factor * h; |
| if ((pixd = pixCreate(wd, hd, d)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyColormap(pixd, pixs); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| |
| switch (d) { |
| case 2: |
| bpld = (wd + 3) / 4; /* occupied bytes only */ |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + factor * i * wpld; |
| for (j = 0; j < w; j++) { |
| sval = GET_DATA_DIBIT(lines, j); |
| start = factor * j; |
| for (k = 0; k < factor; k++) |
| SET_DATA_DIBIT(lined, start + k, sval); |
| } |
| for (k = 1; k < factor; k++) |
| memcpy(lined + k * wpld, lined, 4 * wpld); |
| } |
| break; |
| case 4: |
| bpld = (wd + 1) / 2; /* occupied bytes only */ |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + factor * i * wpld; |
| for (j = 0; j < w; j++) { |
| sval = GET_DATA_QBIT(lines, j); |
| start = factor * j; |
| for (k = 0; k < factor; k++) |
| SET_DATA_QBIT(lined, start + k, sval); |
| } |
| for (k = 1; k < factor; k++) |
| memcpy(lined + k * wpld, lined, 4 * wpld); |
| } |
| break; |
| case 8: |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + factor * i * wpld; |
| for (j = 0; j < w; j++) { |
| sval = GET_DATA_BYTE(lines, j); |
| start = factor * j; |
| for (k = 0; k < factor; k++) |
| SET_DATA_BYTE(lined, start + k, sval); |
| } |
| for (k = 1; k < factor; k++) |
| memcpy(lined + k * wpld, lined, 4 * wpld); |
| } |
| break; |
| case 16: |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + factor * i * wpld; |
| for (j = 0; j < w; j++) { |
| sval16 = GET_DATA_TWO_BYTES(lines, j); |
| start = factor * j; |
| for (k = 0; k < factor; k++) |
| SET_DATA_TWO_BYTES(lined, start + k, sval16); |
| } |
| for (k = 1; k < factor; k++) |
| memcpy(lined + k * wpld, lined, 4 * wpld); |
| } |
| break; |
| case 32: |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + factor * i * wpld; |
| for (j = 0; j < w; j++) { |
| sval32 = *(lines + j); |
| start = factor * j; |
| for (k = 0; k < factor; k++) |
| *(lined + start + k) = sval32; |
| } |
| for (k = 1; k < factor; k++) |
| memcpy(lined + k * wpld, lined, 4 * wpld); |
| } |
| break; |
| default: |
| fprintf(stderr, "invalid depth\n"); |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Scale 2x followed by binarization * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleGray2xLIThresh() |
| * |
| * Input: pixs (8 bpp) |
| * thresh (between 0 and 256) |
| * Return: pixd (1 bpp), or null on error |
| * |
| * Notes: |
| * (1) This does 2x upscale on pixs, using linear interpolation, |
| * followed by thresholding to binary. |
| * (2) Buffers are used to avoid making a large grayscale image. |
| */ |
| PIX * |
| pixScaleGray2xLIThresh(PIX *pixs, |
| l_int32 thresh) |
| { |
| l_int32 i, ws, hs, hsm, wd, hd, wpls, wplb, wpld; |
| l_uint32 *datas, *datad, *lines, *lined, *lineb; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleGray2xLIThresh"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 8) |
| return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); |
| if (thresh < 0 || thresh > 256) |
| return (PIX *)ERROR_PTR("thresh must be in [0, ... 256]", |
| procName, NULL); |
| if (pixGetColormap(pixs)) |
| L_WARNING("pixs has colormap", procName); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| wd = 2 * ws; |
| hd = 2 * hs; |
| hsm = hs - 1; |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| |
| /* Make line buffer for 2 lines of virtual intermediate image */ |
| wplb = (wd + 3) / 4; |
| if ((lineb = (l_uint32 *)CALLOC(2 * wplb, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("lineb not made", procName, NULL); |
| |
| /* Make dest binary image */ |
| if ((pixd = pixCreate(wd, hd, 1)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 2.0, 2.0); |
| wpld = pixGetWpl(pixd); |
| datad = pixGetData(pixd); |
| |
| /* Do all but last src line */ |
| for (i = 0; i < hsm; i++) { |
| lines = datas + i * wpls; |
| lined = datad + 2 * i * wpld; /* do 2 dest lines at a time */ |
| scaleGray2xLILineLow(lineb, wplb, lines, ws, wpls, 0); |
| thresholdToBinaryLineLow(lined, wd, lineb, 8, thresh); |
| thresholdToBinaryLineLow(lined + wpld, wd, lineb + wplb, 8, thresh); |
| } |
| |
| /* Do last src line */ |
| lines = datas + hsm * wpls; |
| lined = datad + 2 * hsm * wpld; |
| scaleGray2xLILineLow(lineb, wplb, lines, ws, wpls, 1); |
| thresholdToBinaryLineLow(lined, wd, lineb, 8, thresh); |
| thresholdToBinaryLineLow(lined + wpld, wd, lineb + wplb, 8, thresh); |
| |
| FREE(lineb); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleGray2xLIDither() |
| * |
| * Input: pixs (8 bpp) |
| * Return: pixd (1 bpp), or null on error |
| * |
| * Notes: |
| * (1) This does 2x upscale on pixs, using linear interpolation, |
| * followed by Floyd-Steinberg dithering to binary. |
| * (2) Buffers are used to avoid making a large grayscale image. |
| * - Two line buffers are used for the src, required for the 2x |
| * LI upscale. |
| * - Three line buffers are used for the intermediate image. |
| * Two are filled with each 2xLI row operation; the third is |
| * needed because the upscale and dithering ops are out of sync. |
| */ |
| PIX * |
| pixScaleGray2xLIDither(PIX *pixs) |
| { |
| l_int32 i, ws, hs, hsm, wd, hd, wpls, wplb, wpld; |
| l_uint32 *datas, *datad; |
| l_uint32 *lined; |
| l_uint32 *lineb; /* 2 intermediate buffer lines */ |
| l_uint32 *linebp; /* 1 intermediate buffer line */ |
| l_uint32 *bufs; /* 2 source buffer lines */ |
| PIX *pixd; |
| |
| PROCNAME("pixScaleGray2xLIDither"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 8) |
| return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); |
| if (pixGetColormap(pixs)) |
| L_WARNING("pixs has colormap", procName); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| wd = 2 * ws; |
| hd = 2 * hs; |
| hsm = hs - 1; |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| |
| /* Make line buffers for 2 lines of src image */ |
| if ((bufs = (l_uint32 *)CALLOC(2 * wpls, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("bufs not made", procName, NULL); |
| |
| /* Make line buffer for 2 lines of virtual intermediate image */ |
| wplb = (wd + 3) / 4; |
| if ((lineb = (l_uint32 *)CALLOC(2 * wplb, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("lineb not made", procName, NULL); |
| |
| /* Make line buffer for 1 line of virtual intermediate image */ |
| if ((linebp = (l_uint32 *)CALLOC(wplb, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("linebp not made", procName, NULL); |
| |
| /* Make dest binary image */ |
| if ((pixd = pixCreate(wd, hd, 1)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 2.0, 2.0); |
| wpld = pixGetWpl(pixd); |
| datad = pixGetData(pixd); |
| |
| /* Start with the first src and the first dest line */ |
| memcpy(bufs, datas, 4 * wpls); /* first src line */ |
| memcpy(bufs + wpls, datas + wpls, 4 * wpls); /* 2nd src line */ |
| scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 2 i lines */ |
| lined = datad; |
| ditherToBinaryLineLow(lined, wd, lineb, lineb + wplb, |
| DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
| /* 1st d line */ |
| |
| /* Do all but last src line */ |
| for (i = 1; i < hsm; i++) { |
| memcpy(bufs, datas + i * wpls, 4 * wpls); /* i-th src line */ |
| memcpy(bufs + wpls, datas + (i + 1) * wpls, 4 * wpls); |
| memcpy(linebp, lineb + wplb, 4 * wplb); |
| scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 2 i lines */ |
| lined = datad + 2 * i * wpld; |
| ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb, |
| DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
| /* odd dest line */ |
| ditherToBinaryLineLow(lined, wd, lineb, lineb + wplb, |
| DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
| /* even dest line */ |
| } |
| |
| /* Do the last src line and the last 3 dest lines */ |
| memcpy(bufs, datas + hsm * wpls, 4 * wpls); /* hsm-th src line */ |
| memcpy(linebp, lineb + wplb, 4 * wplb); /* 1 i line */ |
| scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 1); /* 2 i lines */ |
| ditherToBinaryLineLow(lined + wpld, wd, linebp, lineb, |
| DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
| /* odd dest line */ |
| ditherToBinaryLineLow(lined + 2 * wpld, wd, lineb, lineb + wplb, |
| DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
| /* even dest line */ |
| ditherToBinaryLineLow(lined + 3 * wpld, wd, lineb + wplb, NULL, |
| DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 1); |
| /* last dest line */ |
| |
| FREE(bufs); |
| FREE(lineb); |
| FREE(linebp); |
| return pixd; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Scale 4x followed by binarization * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixScaleGray4xLIThresh() |
| * |
| * Input: pixs (8 bpp) |
| * thresh (between 0 and 256) |
| * Return: pixd (1 bpp), or null on error |
| * |
| * Notes: |
| * (1) This does 4x upscale on pixs, using linear interpolation, |
| * followed by thresholding to binary. |
| * (2) Buffers are used to avoid making a large grayscale image. |
| * (3) If a full 4x expanded grayscale image can be kept in memory, |
| * this function is only about 10% faster than separately doing |
| * a linear interpolation to a large grayscale image, followed |
| * by thresholding to binary. |
| */ |
| PIX * |
| pixScaleGray4xLIThresh(PIX *pixs, |
| l_int32 thresh) |
| { |
| l_int32 i, j, ws, hs, hsm, wd, hd, wpls, wplb, wpld; |
| l_uint32 *datas, *datad, *lines, *lined, *lineb; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleGray4xLIThresh"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 8) |
| return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); |
| if (thresh < 0 || thresh > 256) |
| return (PIX *)ERROR_PTR("thresh must be in [0, ... 256]", |
| procName, NULL); |
| if (pixGetColormap(pixs)) |
| L_WARNING("pixs has colormap", procName); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| wd = 4 * ws; |
| hd = 4 * hs; |
| hsm = hs - 1; |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| |
| /* Make line buffer for 4 lines of virtual intermediate image */ |
| wplb = (wd + 3) / 4; |
| if ((lineb = (l_uint32 *)CALLOC(4 * wplb, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("lineb not made", procName, NULL); |
| |
| /* Make dest binary image */ |
| if ((pixd = pixCreate(wd, hd, 1)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 4.0, 4.0); |
| wpld = pixGetWpl(pixd); |
| datad = pixGetData(pixd); |
| |
| /* Do all but last src line */ |
| for (i = 0; i < hsm; i++) { |
| lines = datas + i * wpls; |
| lined = datad + 4 * i * wpld; /* do 4 dest lines at a time */ |
| scaleGray4xLILineLow(lineb, wplb, lines, ws, wpls, 0); |
| for (j = 0; j < 4; j++) { |
| thresholdToBinaryLineLow(lined + j * wpld, wd, |
| lineb + j * wplb, 8, thresh); |
| } |
| } |
| |
| /* Do last src line */ |
| lines = datas + hsm * wpls; |
| lined = datad + 4 * hsm * wpld; |
| scaleGray4xLILineLow(lineb, wplb, lines, ws, wpls, 1); |
| for (j = 0; j < 4; j++) { |
| thresholdToBinaryLineLow(lined + j * wpld, wd, |
| lineb + j * wplb, 8, thresh); |
| } |
| |
| FREE(lineb); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleGray4xLIDither() |
| * |
| * Input: pixs (8 bpp) |
| * Return: pixd (1 bpp), or null on error |
| * |
| * Notes: |
| * (1) This does 4x upscale on pixs, using linear interpolation, |
| * followed by Floyd-Steinberg dithering to binary. |
| * (2) Buffers are used to avoid making a large grayscale image. |
| * - Two line buffers are used for the src, required for the |
| * 4xLI upscale. |
| * - Five line buffers are used for the intermediate image. |
| * Four are filled with each 4xLI row operation; the fifth |
| * is needed because the upscale and dithering ops are |
| * out of sync. |
| * (3) If a full 4x expanded grayscale image can be kept in memory, |
| * this function is only about 5% faster than separately doing |
| * a linear interpolation to a large grayscale image, followed |
| * by error-diffusion dithering to binary. |
| */ |
| PIX * |
| pixScaleGray4xLIDither(PIX *pixs) |
| { |
| l_int32 i, j, ws, hs, hsm, wd, hd, wpls, wplb, wpld; |
| l_uint32 *datas, *datad; |
| l_uint32 *lined; |
| l_uint32 *lineb; /* 4 intermediate buffer lines */ |
| l_uint32 *linebp; /* 1 intermediate buffer line */ |
| l_uint32 *bufs; /* 2 source buffer lines */ |
| PIX *pixd; |
| |
| PROCNAME("pixScaleGray4xLIDither"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 8) |
| return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); |
| if (pixGetColormap(pixs)) |
| L_WARNING("pixs has colormap", procName); |
| |
| pixGetDimensions(pixs, &ws, &hs, NULL); |
| wd = 4 * ws; |
| hd = 4 * hs; |
| hsm = hs - 1; |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| |
| /* Make line buffers for 2 lines of src image */ |
| if ((bufs = (l_uint32 *)CALLOC(2 * wpls, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("bufs not made", procName, NULL); |
| |
| /* Make line buffer for 4 lines of virtual intermediate image */ |
| wplb = (wd + 3) / 4; |
| if ((lineb = (l_uint32 *)CALLOC(4 * wplb, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("lineb not made", procName, NULL); |
| |
| /* Make line buffer for 1 line of virtual intermediate image */ |
| if ((linebp = (l_uint32 *)CALLOC(wplb, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("linebp not made", procName, NULL); |
| |
| /* Make dest binary image */ |
| if ((pixd = pixCreate(wd, hd, 1)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| pixScaleResolution(pixd, 4.0, 4.0); |
| wpld = pixGetWpl(pixd); |
| datad = pixGetData(pixd); |
| |
| /* Start with the first src and the first 3 dest lines */ |
| memcpy(bufs, datas, 4 * wpls); /* first src line */ |
| memcpy(bufs + wpls, datas + wpls, 4 * wpls); /* 2nd src line */ |
| scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 4 b lines */ |
| lined = datad; |
| for (j = 0; j < 3; j++) { /* first 3 d lines of Q */ |
| ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb, |
| lineb + (j + 1) * wplb, |
| DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
| } |
| |
| /* Do all but last src line */ |
| for (i = 1; i < hsm; i++) { |
| memcpy(bufs, datas + i * wpls, 4 * wpls); /* i-th src line */ |
| memcpy(bufs + wpls, datas + (i + 1) * wpls, 4 * wpls); |
| memcpy(linebp, lineb + 3 * wplb, 4 * wplb); |
| scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 4 b lines */ |
| lined = datad + 4 * i * wpld; |
| ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb, |
| DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
| /* 4th dest line of Q */ |
| for (j = 0; j < 3; j++) { /* next 3 d lines of Quad */ |
| ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb, |
| lineb + (j + 1) * wplb, |
| DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
| } |
| } |
| |
| /* Do the last src line and the last 5 dest lines */ |
| memcpy(bufs, datas + hsm * wpls, 4 * wpls); /* hsm-th src line */ |
| memcpy(linebp, lineb + 3 * wplb, 4 * wplb); /* 1 b line */ |
| scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 1); /* 4 b lines */ |
| lined = datad + 4 * hsm * wpld; |
| ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb, |
| DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
| /* 4th dest line of Q */ |
| for (j = 0; j < 3; j++) { /* next 3 d lines of Quad */ |
| ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb, |
| lineb + (j + 1) * wplb, |
| DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); |
| } |
| /* And finally, the last dest line */ |
| ditherToBinaryLineLow(lined + 3 * wpld, wd, lineb + 3 * wplb, NULL, |
| DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 1); |
| |
| FREE(bufs); |
| FREE(lineb); |
| FREE(linebp); |
| return pixd; |
| } |
| |
| |
| /*-----------------------------------------------------------------------* |
| * Downscaling using min or max * |
| *-----------------------------------------------------------------------*/ |
| /*! |
| * pixScaleGrayMinMax() |
| * |
| * Input: pixs (8 bpp) |
| * xfact (x downscaling factor; integer) |
| * yfact (y downscaling factor; integer) |
| * type (L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAX_MIN_DIFF) |
| * Return: pixd (8 bpp) |
| * |
| * Notes: |
| * (1) The downscaled pixels in pixd are the min, max or (max - min) |
| * of the corresponding set of xfact * yfact pixels in pixs. |
| * (2) Using L_CHOOSE_MIN is equivalent to a grayscale erosion, |
| * using a brick Sel of size (xfact * yfact), followed by |
| * subsampling within each (xfact * yfact) cell. Using |
| * L_CHOOSE_MAX is equivalent to the corresponding dilation. |
| * (3) Using L_CHOOSE_MAX_MIN_DIFF finds the difference between max |
| * and min values in each cell. |
| * (4) For the special case of downscaling by 2x in both directions, |
| * pixScaleGrayMinMax2() is about 2x more efficient. |
| */ |
| PIX * |
| pixScaleGrayMinMax(PIX *pixs, |
| l_int32 xfact, |
| l_int32 yfact, |
| l_int32 type) |
| { |
| l_int32 ws, hs, d, wd, hd, wpls, wpld, i, j, k, m; |
| l_int32 minval, maxval, val; |
| l_uint32 *datas, *datad, *lines, *lined; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleGrayMinMax"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| pixGetDimensions(pixs, &ws, &hs, &d); |
| if (d != 8) |
| return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); |
| if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX && |
| type != L_CHOOSE_MAX_MIN_DIFF) |
| return (PIX *)ERROR_PTR("invalid type", procName, NULL); |
| |
| if (xfact == 2 && yfact == 2) |
| return pixScaleGrayMinMax2(pixs, type); |
| |
| wd = L_MAX(ws / xfact, 1); |
| hd = L_MAX(hs / yfact, 1); |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| wpls = pixGetWpl(pixs); |
| wpld = pixGetWpl(pixd); |
| for (i = 0; i < hd; i++) { |
| lined = datad + i * wpld; |
| for (j = 0; j < wd; j++) { |
| if (type == L_CHOOSE_MIN || type == L_CHOOSE_MAX_MIN_DIFF) { |
| minval = 255; |
| for (k = 0; k < yfact; k++) { |
| lines = datas + (yfact * i + k) * wpls; |
| for (m = 0; m < xfact; m++) { |
| val = GET_DATA_BYTE(lines, xfact * j + m); |
| if (val < minval) |
| minval = val; |
| } |
| } |
| } |
| if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAX_MIN_DIFF) { |
| maxval = 0; |
| for (k = 0; k < yfact; k++) { |
| lines = datas + (yfact * i + k) * wpls; |
| for (m = 0; m < xfact; m++) { |
| val = GET_DATA_BYTE(lines, xfact * j + m); |
| if (val > maxval) |
| maxval = val; |
| } |
| } |
| } |
| if (type == L_CHOOSE_MIN) |
| SET_DATA_BYTE(lined, j, minval); |
| else if (type == L_CHOOSE_MAX) |
| SET_DATA_BYTE(lined, j, maxval); |
| else /* type == L_CHOOSE_MAX_MIN_DIFF */ |
| SET_DATA_BYTE(lined, j, maxval - minval); |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixScaleGrayMinMax2() |
| * |
| * Input: pixs (8 bpp) |
| * type (L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAX_MIN_DIFF) |
| * Return: pixd (8 bpp downscaled by 2x) |
| * |
| * Notes: |
| * (1) Special version for 2x reduction. The downscaled pixels |
| * in pixd are the min, max or (max - min) of the corresponding |
| * set of 4 pixels in pixs. |
| * (2) The max and min operations are a special case (for levels 1 |
| * and 4) of grayscale analog to the binary rank scaling operation |
| * pixReduceRankBinary2(). Note, however, that because of |
| * the photometric definition that higher gray values are |
| * lighter, the erosion-like L_CHOOSE_MIN will darken |
| * the resulting image, corresponding to a threshold level 1 |
| * in the binary case. Likewise, L_CHOOSE_MAX will lighten |
| * the pixd, corresponding to a threshold level of 4. |
| * (3) To choose any of the four rank levels in a 2x grayscale |
| * reduction, use pixScaleGrayRank2(). |
| * (4) This runs at about 70 MPix/sec/GHz of source data for |
| * erosion and dilation. |
| */ |
| PIX * |
| pixScaleGrayMinMax2(PIX *pixs, |
| l_int32 type) |
| { |
| l_int32 ws, hs, d, wd, hd, wpls, wpld, i, j, k; |
| l_int32 minval, maxval; |
| l_int32 val[4]; |
| l_uint32 *datas, *datad, *lines, *lined; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleGrayMinMax2"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| pixGetDimensions(pixs, &ws, &hs, &d); |
| if (d != 8) |
| return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); |
| if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX && |
| type != L_CHOOSE_MAX_MIN_DIFF) |
| return (PIX *)ERROR_PTR("invalid type", procName, NULL); |
| |
| wd = ws / 2; |
| hd = hs / 2; |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| wpls = pixGetWpl(pixs); |
| wpld = pixGetWpl(pixd); |
| for (i = 0; i < hd; i++) { |
| lines = datas + 2 * i * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < wd; j++) { |
| val[0] = GET_DATA_BYTE(lines, 2 * j); |
| val[1] = GET_DATA_BYTE(lines, 2 * j + 1); |
| val[2] = GET_DATA_BYTE(lines + wpls, 2 * j); |
| val[3] = GET_DATA_BYTE(lines + wpls, 2 * j + 1); |
| if (type == L_CHOOSE_MIN || type == L_CHOOSE_MAX_MIN_DIFF) { |
| minval = 255; |
| for (k = 0; k < 4; k++) { |
| if (val[k] < minval) |
| minval = val[k]; |
| } |
| } |
| if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAX_MIN_DIFF) { |
| maxval = 0; |
| for (k = 0; k < 4; k++) { |
| if (val[k] > maxval) |
| maxval = val[k]; |
| } |
| } |
| if (type = L_CHOOSE_MIN) |
| SET_DATA_BYTE(lined, j, minval); |
| else if (type = L_CHOOSE_MAX) |
| SET_DATA_BYTE(lined, j, maxval); |
| else /* type == L_CHOOSE_MAX_MIN_DIFF */ |
| SET_DATA_BYTE(lined, j, maxval - minval); |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*-----------------------------------------------------------------------* |
| * Grayscale downscaling using rank value * |
| *-----------------------------------------------------------------------*/ |
| /*! |
| * pixScaleGrayRankCascade() |
| * |
| * Input: pixs (8 bpp) |
| * level1, ... level4 (rank thresholds, in set {0, 1, 2, 3, 4}) |
| * Return: pixd (8 bpp, downscaled by up to 16x) |
| * |
| * Notes: |
| * (1) This performs up to four cascaded 2x rank reductions. |
| * (2) Use level = 0 to truncate the cascade. |
| */ |
| PIX * |
| pixScaleGrayRankCascade(PIX *pixs, |
| l_int32 level1, |
| l_int32 level2, |
| l_int32 level3, |
| l_int32 level4) |
| { |
| PIX *pixt1, *pixt2, *pixt3, *pixt4; |
| |
| PROCNAME("pixScaleGrayRankCascade"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 8) |
| return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); |
| if (level1 > 4 || level2 > 4 || level3 > 4 || level4 > 4) |
| return (PIX *)ERROR_PTR("levels must not exceed 4", procName, NULL); |
| |
| if (level1 <= 0) { |
| L_WARNING("no reduction because level1 not > 0", procName); |
| return pixCopy(NULL, pixs); |
| } |
| |
| pixt1 = pixScaleGrayRank2(pixs, level1); |
| if (level2 <= 0) |
| return pixt1; |
| |
| pixt2 = pixScaleGrayRank2(pixt1, level2); |
| pixDestroy(&pixt1); |
| if (level3 <= 0) |
| return pixt2; |
| |
| pixt3 = pixScaleGrayRank2(pixt2, level3); |
| pixDestroy(&pixt2); |
| if (level4 <= 0) |
| return pixt3; |
| |
| pixt4 = pixScaleGrayRank2(pixt3, level4); |
| pixDestroy(&pixt3); |
| return pixt4; |
| } |
| |
| |
| /*! |
| * pixScaleGrayRank2() |
| * |
| * Input: pixs (8 bpp) |
| * rank (1 (darkest), 2, 3, 4 (lightest)) |
| * Return: pixd (8 bpp, downscaled by 2x) |
| * |
| * Notes: |
| * (1) Rank 2x reduction. If rank == 1(4), the downscaled pixels |
| * in pixd are the min(max) of the corresponding set of |
| * 4 pixels in pixs. Values 2 and 3 are intermediate. |
| * (2) This is the grayscale analog to the binary rank scaling operation |
| * pixReduceRankBinary2(). Here, because of the photometric |
| * definition that higher gray values are lighter, rank 1 gives |
| * the darkest pixel, whereas rank 4 gives the lightest pixel. |
| * This is opposite to the binary rank operation. |
| * (3) For rank = 1 and 4, this calls pixScaleGrayMinMax2(), |
| * which runs at about 70 MPix/sec/GHz of source data. |
| * For rank 2 and 3, this runs 3x slower, at about 25 MPix/sec/GHz. |
| */ |
| PIX * |
| pixScaleGrayRank2(PIX *pixs, |
| l_int32 rank) |
| { |
| l_int32 d, ws, hs, wd, hd, wpls, wpld, i, j, k, m; |
| l_int32 minval, maxval, rankval, minindex, maxindex; |
| l_int32 val[4]; |
| l_int32 midval[4]; /* should only use 2 of these */ |
| l_uint32 *datas, *datad, *lines, *lined; |
| PIX *pixd; |
| |
| PROCNAME("pixScaleGrayRank2"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| pixGetDimensions(pixs, &ws, &hs, &d); |
| if (d != 8) |
| return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); |
| if (rank < 1 || rank > 4) |
| return (PIX *)ERROR_PTR("invalid rank", procName, NULL); |
| |
| if (rank == 1) |
| return pixScaleGrayMinMax2(pixs, L_CHOOSE_MIN); |
| if (rank == 4) |
| return pixScaleGrayMinMax2(pixs, L_CHOOSE_MAX); |
| |
| wd = ws / 2; |
| hd = hs / 2; |
| if ((pixd = pixCreate(wd, hd, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| wpls = pixGetWpl(pixs); |
| wpld = pixGetWpl(pixd); |
| for (i = 0; i < hd; i++) { |
| lines = datas + 2 * i * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < wd; j++) { |
| val[0] = GET_DATA_BYTE(lines, 2 * j); |
| val[1] = GET_DATA_BYTE(lines, 2 * j + 1); |
| val[2] = GET_DATA_BYTE(lines + wpls, 2 * j); |
| val[3] = GET_DATA_BYTE(lines + wpls, 2 * j + 1); |
| minval = maxval = val[0]; |
| minindex = maxindex = 0; |
| for (k = 1; k < 4; k++) { |
| if (val[k] < minval) { |
| minval = val[k]; |
| minindex = k; |
| continue; |
| } |
| if (val[k] > maxval) { |
| maxval = val[k]; |
| maxindex = k; |
| } |
| } |
| for (k = 0, m = 0; k < 4; k++) { |
| if (k == minindex || k == maxindex) |
| continue; |
| midval[m++] = val[k]; |
| } |
| if (m > 2) /* minval == maxval; all val[k] are the same */ |
| rankval = minval; |
| else if (rank == 2) |
| rankval = L_MIN(midval[0], midval[1]); |
| else /* rank == 3 */ |
| rankval = L_MAX(midval[0], midval[1]); |
| SET_DATA_BYTE(lined, j, rankval); |
| } |
| } |
| |
| return pixd; |
| } |
| |