mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 14:16:10 -06:00
2822 lines
98 KiB
C
2822 lines
98 KiB
C
/*====================================================================*
|
|
- 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;
|
|
}
|
|
|