Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/leptonica/src/pixarith.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/leptonica/src/pixarith.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,1597 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - Redistribution and use in source and binary forms, with or without + - modification, are permitted provided that the following conditions + - are met: + - 1. Redistributions of source code must retain the above copyright + - notice, this list of conditions and the following disclaimer. + - 2. Redistributions in binary form must reproduce the above + - copyright notice, this list of conditions and the following + - disclaimer in the documentation and/or other materials + - provided with the distribution. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY + - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *====================================================================*/ + +/*! + * \file pixarith.c + * <pre> + * + * One-image grayscale arithmetic operations (8, 16, 32 bpp) + * l_int32 pixAddConstantGray() + * l_int32 pixMultConstantGray() + * + * Two-image grayscale arithmetic operations (8, 16, 32 bpp) + * PIX *pixAddGray() + * PIX *pixSubtractGray() + * PIX *pixMultiplyGray() + * + * Grayscale threshold operation (8, 16, 32 bpp) + * PIX *pixThresholdToValue() + * + * Image accumulator arithmetic operations + * PIX *pixInitAccumulate() + * PIX *pixFinalAccumulate() + * PIX *pixFinalAccumulateThreshold() + * l_int32 pixAccumulate() + * l_int32 pixMultConstAccumulate() + * + * Absolute value of difference + * PIX *pixAbsDifference() + * + * Sum of color images + * PIX *pixAddRGB() + * + * Two-image min and max operations (8 and 16 bpp) + * PIX *pixMinOrMax() + * + * Scale pix for maximum dynamic range + * PIX *pixMaxDynamicRange() + * PIX *pixMaxDynamicRangeRGB() + * + * RGB pixel value scaling + * l_uint32 linearScaleRGBVal() + * l_uint32 logScaleRGBVal() + * + * Log base2 lookup + * l_float32 *makeLogBase2Tab() + * l_float32 getLogBase2() + * + * The image accumulator operations are used when you expect + * overflow from 8 bits on intermediate results. For example, + * you might want a tophat contrast operator which is + * 3*I - opening(I,S) - closing(I,S) + * To use these operations, first use the init to generate + * a 16 bpp image, use the accumulate to add or subtract 8 bpp + * images from that, or the multiply constant to multiply + * by a small constant (much less than 256 -- we don't want + * overflow from the 16 bit images!), and when you're finished + * use final to bring the result back to 8 bpp, clipped + * if necessary. There is also a divide function, which + * can be used to divide one image by another, scaling the + * result for maximum dynamic range, and giving back the + * 8 bpp result. + * + * A simpler interface to the arithmetic operations is + * provided in pixacc.c. + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <math.h> +#include "allheaders.h" + +/*-------------------------------------------------------------* + * One-image grayscale arithmetic operations * + *-------------------------------------------------------------*/ +/*! + * \brief pixAddConstantGray() + * + * \param[in] pixs 8, 16 or 32 bpp + * \param[in] val amount to add to each pixel + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) In-place operation. + * (2) No clipping for 32 bpp. + * (3) For 8 and 16 bpp, if val > 0 the result is clipped + * to 0xff and 0xffff, rsp. + * (4) For 8 and 16 bpp, if val < 0 the result is clipped to 0. + * </pre> + */ +l_ok +pixAddConstantGray(PIX *pixs, + l_int32 val) +{ +l_int32 i, j, w, h, d, wpl, pval; +l_uint32 *data, *line; + + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 16 && d != 32) + return ERROR_INT("pixs not 8, 16 or 32 bpp", __func__, 1); + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (d == 8) { + if (val < 0) { + for (j = 0; j < w; j++) { + pval = GET_DATA_BYTE(line, j); + pval = L_MAX(0, pval + val); + SET_DATA_BYTE(line, j, pval); + } + } else { /* val >= 0 */ + for (j = 0; j < w; j++) { + pval = GET_DATA_BYTE(line, j); + pval = L_MIN(255, pval + val); + SET_DATA_BYTE(line, j, pval); + } + } + } else if (d == 16) { + if (val < 0) { + for (j = 0; j < w; j++) { + pval = GET_DATA_TWO_BYTES(line, j); + pval = L_MAX(0, pval + val); + SET_DATA_TWO_BYTES(line, j, pval); + } + } else { /* val >= 0 */ + for (j = 0; j < w; j++) { + pval = GET_DATA_TWO_BYTES(line, j); + pval = L_MIN(0xffff, pval + val); + SET_DATA_TWO_BYTES(line, j, pval); + } + } + } else { /* d == 32; no check for overflow (< 0 or > 0xffffffff) */ + for (j = 0; j < w; j++) + *(line + j) += val; + } + } + + return 0; +} + + +/*! + * \brief pixMultConstantGray() + * + * \param[in] pixs 8, 16 or 32 bpp + * \param[in] val >= 0.0; amount to multiply by each pixel + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) In-place operation; val must be >= 0. + * (2) No clipping for 32 bpp. + * (3) For 8 and 16 bpp, the result is clipped to 0xff and 0xffff, rsp. + * </pre> + */ +l_ok +pixMultConstantGray(PIX *pixs, + l_float32 val) +{ +l_int32 i, j, w, h, d, wpl, pval; +l_uint32 upval; +l_uint32 *data, *line; + + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 16 && d != 32) + return ERROR_INT("pixs not 8, 16 or 32 bpp", __func__, 1); + if (val < 0.0) + return ERROR_INT("val < 0.0", __func__, 1); + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (d == 8) { + for (j = 0; j < w; j++) { + pval = GET_DATA_BYTE(line, j); + pval = (l_int32)(val * pval); + pval = L_MIN(255, pval); + SET_DATA_BYTE(line, j, pval); + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + pval = GET_DATA_TWO_BYTES(line, j); + pval = (l_int32)(val * pval); + pval = L_MIN(0xffff, pval); + SET_DATA_TWO_BYTES(line, j, pval); + } + } else { /* d == 32; no clipping */ + for (j = 0; j < w; j++) { + upval = *(line + j); + upval = (l_uint32)(val * upval); + *(line + j) = upval; + } + } + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Two-image grayscale arithmetic ops * + *-------------------------------------------------------------*/ +/*! + * \brief pixAddGray() + * + * \param[in] pixd [optional]; this can be null, equal to pixs1, or + * different from pixs1 + * \param[in] pixs1 can be equal to pixd + * \param[in] pixs2 + * \return pixd always + * + * <pre> + * Notes: + * (1) Arithmetic addition of two 8, 16 or 32 bpp images. + * (2) For 8 and 16 bpp, we do explicit clipping to 0xff and 0xffff, + * respectively. + * (3) Alignment is to UL corner. + * (4) There are 3 cases. The result can go to a new dest, + * in-place to pixs1, or to an existing input dest: + * * pixd == null: (src1 + src2) --> new pixd + * * pixd == pixs1: (src1 + src2) --> src1 (in-place) + * * pixd != pixs1: (src1 + src2) --> input pixd + * (5) pixs2 must be different from both pixd and pixs1. + * </pre> + */ +PIX * +pixAddGray(PIX *pixd, + PIX *pixs1, + PIX *pixs2) +{ +l_int32 i, j, d, ws, hs, w, h, wpls, wpld, val, sum; +l_uint32 *datas, *datad, *lines, *lined; + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd); + if (pixs2 == pixs1) + return (PIX *)ERROR_PTR("pixs2 and pixs1 must differ", __func__, pixd); + if (pixs2 == pixd) + return (PIX *)ERROR_PTR("pixs2 and pixd must differ", __func__, pixd); + d = pixGetDepth(pixs1); + if (d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pix are not 8, 16 or 32 bpp", __func__, pixd); + if (pixGetDepth(pixs2) != d) + return (PIX *)ERROR_PTR("depths differ (pixs1, pixs2)", __func__, pixd); + if (pixd && (pixGetDepth(pixd) != d)) + return (PIX *)ERROR_PTR("depths differ (pixs1, pixd)", __func__, pixd); + + if (!pixSizesEqual(pixs1, pixs2)) + L_WARNING("pixs1 and pixs2 not equal in size\n", __func__); + if (pixd && !pixSizesEqual(pixs1, pixd)) + L_WARNING("pixs1 and pixd not equal in size\n", __func__); + + if (pixs1 != pixd) + pixd = pixCopy(pixd, pixs1); + + /* pixd + pixs2 ==> pixd */ + datas = pixGetData(pixs2); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs2); + wpld = pixGetWpl(pixd); + pixGetDimensions(pixs2, &ws, &hs, NULL); + pixGetDimensions(pixd, &w, &h, NULL); + w = L_MIN(ws, w); + h = L_MIN(hs, h); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + lines = datas + i * wpls; + if (d == 8) { + for (j = 0; j < w; j++) { + sum = GET_DATA_BYTE(lines, j) + GET_DATA_BYTE(lined, j); + val = L_MIN(sum, 255); + SET_DATA_BYTE(lined, j, val); + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + sum = GET_DATA_TWO_BYTES(lines, j) + + GET_DATA_TWO_BYTES(lined, j); + val = L_MIN(sum, 0xffff); + SET_DATA_TWO_BYTES(lined, j, val); + } + } else { /* d == 32; no clipping */ + for (j = 0; j < w; j++) + *(lined + j) += *(lines + j); + } + } + + return pixd; +} + + +/*! + * \brief pixSubtractGray() + * + * \param[in] pixd [optional]; this can be null, equal to pixs1, or + * different from pixs1 + * \param[in] pixs1 can be equal to pixd + * \param[in] pixs2 + * \return pixd always + * + * <pre> + * Notes: + * (1) Arithmetic subtraction of two 8, 16 or 32 bpp images. + * (2) Source pixs2 is always subtracted from source pixs1. + * (3) Do explicit clipping to 0. + * (4) Alignment is to UL corner. + * (5) There are 3 cases. The result can go to a new dest, + * in-place to pixs1, or to an existing input dest: + * (a) pixd == null (src1 - src2) --> new pixd + * (b) pixd == pixs1 (src1 - src2) --> src1 (in-place) + * (d) pixd != pixs1 (src1 - src2) --> input pixd + * (6) pixs2 must be different from both pixd and pixs1. + * </pre> + */ +PIX * +pixSubtractGray(PIX *pixd, + PIX *pixs1, + PIX *pixs2) +{ +l_int32 i, j, w, h, ws, hs, d, wpls, wpld, val, diff; +l_uint32 *datas, *datad, *lines, *lined; + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd); + if (pixs2 == pixs1) + return (PIX *)ERROR_PTR("pixs2 and pixs1 must differ", __func__, pixd); + if (pixs2 == pixd) + return (PIX *)ERROR_PTR("pixs2 and pixd must differ", __func__, pixd); + d = pixGetDepth(pixs1); + if (d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pix are not 8, 16 or 32 bpp", __func__, pixd); + if (pixGetDepth(pixs2) != d) + return (PIX *)ERROR_PTR("depths differ (pixs1, pixs2)", __func__, pixd); + if (pixd && (pixGetDepth(pixd) != d)) + return (PIX *)ERROR_PTR("depths differ (pixs1, pixd)", __func__, pixd); + + if (!pixSizesEqual(pixs1, pixs2)) + L_WARNING("pixs1 and pixs2 not equal in size\n", __func__); + if (pixd && !pixSizesEqual(pixs1, pixd)) + L_WARNING("pixs1 and pixd not equal in size\n", __func__); + + if (pixs1 != pixd) + pixd = pixCopy(pixd, pixs1); + + /* pixd - pixs2 ==> pixd */ + datas = pixGetData(pixs2); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs2); + wpld = pixGetWpl(pixd); + pixGetDimensions(pixs2, &ws, &hs, NULL); + pixGetDimensions(pixd, &w, &h, NULL); + w = L_MIN(ws, w); + h = L_MIN(hs, h); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + lines = datas + i * wpls; + if (d == 8) { + for (j = 0; j < w; j++) { + diff = GET_DATA_BYTE(lined, j) - GET_DATA_BYTE(lines, j); + val = L_MAX(diff, 0); + SET_DATA_BYTE(lined, j, val); + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + diff = GET_DATA_TWO_BYTES(lined, j) + - GET_DATA_TWO_BYTES(lines, j); + val = L_MAX(diff, 0); + SET_DATA_TWO_BYTES(lined, j, val); + } + } else { /* d == 32; no clipping */ + for (j = 0; j < w; j++) + *(lined + j) -= *(lines + j); + } + } + + return pixd; +} + + +/*! + * \brief pixMultiplyGray() + * + * \param[in] pixs 32 bpp rgb or 8 bpp gray + * \param[in] pixg 8 bpp gray + * \param[in] norm multiplicative factor to avoid overflow; 0 for default + * \return pixd, or null on error + * + * <pre> + * Notes: + * (1) This function can be used for correcting a scanned image + * under non-uniform illumination. For that application, + * %pixs is the scanned image, %pixg is an image whose values + * are inversely related to light from a uniform (say, white) + * target, and %norm is typically the inverse of the maximum + * pixel value in %pixg. + * (2) Set norm = 0 to get the default value, which is the inverse + * of the max value in %pixg. This avoids overflow in the product. + * (3) For 32 bpp %pixs, all 3 components are multiplied by the + * same number. + * (4) Alignment is to UL corner. + * </pre> + */ +PIX * +pixMultiplyGray(PIX *pixs, + PIX *pixg, + l_float32 norm) +{ +l_int32 i, j, w, h, d, ws, hs, ds, wpls, wplg, wpld; +l_int32 rval, gval, bval, rval2, gval2, bval2, vals, valg, val, maxgray; +l_uint32 val32; +l_uint32 *datas, *datag, *datad, *lines, *lineg, *lined; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + pixGetDimensions(pixs, &ws, &hs, &ds); + if (ds != 8 && ds != 32) + return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", __func__, NULL); + if (!pixg) + return (PIX *)ERROR_PTR("pixg not defined", __func__, NULL); + pixGetDimensions(pixg, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixg not 8 bpp", __func__, NULL); + + if (norm <= 0.0) { + pixGetExtremeValue(pixg, 1, L_SELECT_MAX, NULL, NULL, NULL, &maxgray); + norm = (maxgray > 0) ? 1.0f / (l_float32)maxgray : 1.0f; + } + + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + datas = pixGetData(pixs); + datag = pixGetData(pixg); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wplg = pixGetWpl(pixg); + wpld = pixGetWpl(pixd); + w = L_MIN(ws, w); + h = L_MIN(hs, h); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lineg = datag + i * wplg; + lined = datad + i * wpld; + if (ds == 8) { + for (j = 0; j < w; j++) { + vals = GET_DATA_BYTE(lines, j); + valg = GET_DATA_BYTE(lineg, j); + val = (l_int32)(vals * valg * norm + 0.5); + val = L_MIN(255, val); + SET_DATA_BYTE(lined, j, val); + } + } else { /* ds == 32 */ + for (j = 0; j < w; j++) { + val32 = *(lines + j); + extractRGBValues(val32, &rval, &gval, &bval); + valg = GET_DATA_BYTE(lineg, j); + rval2 = (l_int32)(rval * valg * norm + 0.5); + rval2 = L_MIN(255, rval2); + gval2 = (l_int32)(gval * valg * norm + 0.5); + gval2 = L_MIN(255, gval2); + bval2 = (l_int32)(bval * valg * norm + 0.5); + bval2 = L_MIN(255, bval2); + composeRGBPixel(rval2, gval2, bval2, lined + j); + } + } + } + + return pixd; +} + + +/*-------------------------------------------------------------* + * Grayscale threshold operation * + *-------------------------------------------------------------*/ +/*! + * \brief pixThresholdToValue() + * + * \param[in] pixd [optional]; if not null, must be equal to pixs + * \param[in] pixs 8, 16, 32 bpp + * \param[in] threshval + * \param[in] setval + * \return pixd always + * + * <pre> + * Notes: + * ~ operation can be in-place (pixs == pixd) or to a new pixd + * ~ if %setval > %threshval, sets pixels with a value >= threshval to setval + * ~ if %setval < %threshval, sets pixels with a value <= threshval to setval + * ~ if %setval == %threshval, no-op + * </pre> + */ +PIX * +pixThresholdToValue(PIX *pixd, + PIX *pixs, + l_int32 threshval, + l_int32 setval) +{ +l_int32 i, j, w, h, d, wpld, setabove; +l_uint32 *datad, *lined; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); + d = pixGetDepth(pixs); + if (d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8, 16 or 32 bpp", __func__, pixd); + if (pixd && (pixs != pixd)) + return (PIX *)ERROR_PTR("pixd exists and is not pixs", __func__, pixd); + if (threshval < 0 || setval < 0) + return (PIX *)ERROR_PTR("threshval & setval not < 0", __func__, pixd); + if (d == 8 && setval > 255) + return (PIX *)ERROR_PTR("setval > 255 for 8 bpp", __func__, pixd); + if (d == 16 && setval > 0xffff) + return (PIX *)ERROR_PTR("setval > 0xffff for 16 bpp", __func__, pixd); + + if (!pixd) + pixd = pixCopy(NULL, pixs); + if (setval == threshval) { + L_WARNING("setval == threshval; no operation\n", __func__); + return pixd; + } + + datad = pixGetData(pixd); + pixGetDimensions(pixd, &w, &h, NULL); + wpld = pixGetWpl(pixd); + if (setval > threshval) + setabove = TRUE; + else + setabove = FALSE; + + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + if (setabove == TRUE) { + if (d == 8) { + for (j = 0; j < w; j++) { + if (GET_DATA_BYTE(lined, j) - threshval >= 0) + SET_DATA_BYTE(lined, j, setval); + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + if (GET_DATA_TWO_BYTES(lined, j) - threshval >= 0) + SET_DATA_TWO_BYTES(lined, j, setval); + } + } else { /* d == 32 */ + for (j = 0; j < w; j++) { + if (*(lined + j) >= threshval) + *(lined + j) = setval; + } + } + } else { /* set if below or at threshold */ + if (d == 8) { + for (j = 0; j < w; j++) { + if (GET_DATA_BYTE(lined, j) - threshval <= 0) + SET_DATA_BYTE(lined, j, setval); + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + if (GET_DATA_TWO_BYTES(lined, j) - threshval <= 0) + SET_DATA_TWO_BYTES(lined, j, setval); + } + } else { /* d == 32 */ + for (j = 0; j < w; j++) { + if (*(lined + j) <= threshval) + *(lined + j) = setval; + } + } + } + } + + return pixd; +} + + +/*-------------------------------------------------------------* + * Image accumulator arithmetic operations * + *-------------------------------------------------------------*/ +/*! + * \brief pixInitAccumulate() + * + * \param[in] w, h of accumulate array + * \param[in] offset initialize the 32 bpp to have this + * value; not more than 0x40000000 + * \return pixd 32 bpp, or NULL on error + * + * <pre> + * Notes: + * (1) %offset must be >= 0. + * (2) %offset is used so that we can do arithmetic + * with negative number results on l_uint32 data; it + * prevents the l_uint32 data from going negative. + * (3) Because we use l_int32 intermediate data results, + * these should never exceed the max of l_int32 (0x7fffffff). + * We do not permit the offset to be above 0x40000000, + * which is half way between 0 and the max of l_int32. + * (4) The same offset should be used for initialization, + * multiplication by a constant, and final extraction! + * (5) If you're only adding positive values, %offset can be 0. + * </pre> + */ +PIX * +pixInitAccumulate(l_int32 w, + l_int32 h, + l_uint32 offset) +{ +PIX *pixd; + + if ((pixd = pixCreate(w, h, 32)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + if (offset > 0x40000000) + offset = 0x40000000; + pixSetAllArbitrary(pixd, offset); + return pixd; +} + + +/*! + * \brief pixFinalAccumulate() + * + * \param[in] pixs 32 bpp + * \param[in] offset same as used for initialization + * \param[in] depth 8, 16 or 32 bpp, of destination + * \return pixd 8, 16 or 32 bpp, or NULL on error + * + * <pre> + * Notes: + * (1) %offset must be >= 0 and should not exceed 0x40000000. + * (2) %offset is subtracted from the src 32 bpp image + * (3) For 8 bpp dest, the result is clipped to [0, 0xff] + * (4) For 16 bpp dest, the result is clipped to [0, 0xffff] + * </pre> + */ +PIX * +pixFinalAccumulate(PIX *pixs, + l_uint32 offset, + l_int32 depth) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL); + if (depth != 8 && depth != 16 && depth != 32) + return (PIX *)ERROR_PTR("dest depth not 8, 16, 32 bpp", __func__, NULL); + if (offset > 0x40000000) + offset = 0x40000000; + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, depth)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */ + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + if (depth == 8) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j] - offset; + val = L_MAX(0, val); + val = L_MIN(255, val); + SET_DATA_BYTE(lined, j, (l_uint8)val); + } + } + } else if (depth == 16) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j] - offset; + val = L_MAX(0, val); + val = L_MIN(0xffff, val); + SET_DATA_TWO_BYTES(lined, j, (l_uint16)val); + } + } + } else { /* depth == 32 */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) + lined[j] = lines[j] - offset; + } + } + + return pixd; +} + + +/*! + * \brief pixFinalAccumulateThreshold() + * + * \param[in] pixs 32 bpp + * \param[in] offset same as used for initialization + * \param[in] threshold values less than this are set in the destination + * \return pixd 1 bpp, or NULL on error + * + * <pre> + * Notes: + * (1) %offset must be >= 0 and should not exceed 0x40000000. + * (2) %offset is subtracted from the src 32 bpp image + * </pre> + */ +PIX * +pixFinalAccumulateThreshold(PIX *pixs, + l_uint32 offset, + l_uint32 threshold) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL); + if (offset > 0x40000000) + offset = 0x40000000; + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */ + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j] - offset; + if (val >= threshold) { + SET_DATA_BIT(lined, j); + } + } + } + + return pixd; +} + + +/*! + * \brief pixAccumulate() + * + * \param[in] pixd 32 bpp + * \param[in] pixs 1, 8, 16 or 32 bpp + * \param[in] op L_ARITH_ADD or L_ARITH_SUBTRACT + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) This adds or subtracts each pixs value from pixd. + * (2) This clips to the minimum of pixs and pixd, so they + * do not need to be the same size. + * (3) The alignment is to the origin [UL corner] of pixs & pixd. + * </pre> + */ +l_ok +pixAccumulate(PIX *pixd, + PIX *pixs, + l_int32 op) +{ +l_int32 i, j, w, h, d, wd, hd, wpls, wpld; +l_uint32 *datas, *datad, *lines, *lined; + + + if (!pixd || (pixGetDepth(pixd) != 32)) + return ERROR_INT("pixd not defined or not 32 bpp", __func__, 1); + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + d = pixGetDepth(pixs); + if (d != 1 && d != 8 && d != 16 && d != 32) + return ERROR_INT("pixs not 1, 8, 16 or 32 bpp", __func__, 1); + if (op != L_ARITH_ADD && op != L_ARITH_SUBTRACT) + return ERROR_INT("op must be in {L_ARITH_ADD, L_ARITH_SUBTRACT}", + __func__, 1); + + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + pixGetDimensions(pixs, &w, &h, NULL); + pixGetDimensions(pixd, &wd, &hd, NULL); + w = L_MIN(w, wd); + h = L_MIN(h, hd); + if (d == 1) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (op == L_ARITH_ADD) { + for (j = 0; j < w; j++) + lined[j] += GET_DATA_BIT(lines, j); + } else { /* op == L_ARITH_SUBTRACT */ + for (j = 0; j < w; j++) + lined[j] -= GET_DATA_BIT(lines, j); + } + } + } else if (d == 8) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (op == L_ARITH_ADD) { + for (j = 0; j < w; j++) + lined[j] += GET_DATA_BYTE(lines, j); + } else { /* op == L_ARITH_SUBTRACT */ + for (j = 0; j < w; j++) + lined[j] -= GET_DATA_BYTE(lines, j); + } + } + } else if (d == 16) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (op == L_ARITH_ADD) { + for (j = 0; j < w; j++) + lined[j] += GET_DATA_TWO_BYTES(lines, j); + } else { /* op == L_ARITH_SUBTRACT */ + for (j = 0; j < w; j++) + lined[j] -= GET_DATA_TWO_BYTES(lines, j); + } + } + } else { /* d == 32 */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (op == L_ARITH_ADD) { + for (j = 0; j < w; j++) + lined[j] += lines[j]; + } else { /* op == L_ARITH_SUBTRACT */ + for (j = 0; j < w; j++) + lined[j] -= lines[j]; + } + } + } + + return 0; +} + + +/*! + * \brief pixMultConstAccumulate() + * + * \param[in] pixs 32 bpp + * \param[in] factor + * \param[in] offset same as used for initialization + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) %offset must be >= 0 and should not exceed 0x40000000. + * (2) This multiplies each pixel, relative to offset, by %factor. + * (3) The result is returned with %offset back in place. + * </pre> + */ +l_ok +pixMultConstAccumulate(PIX *pixs, + l_float32 factor, + l_uint32 offset) +{ +l_int32 i, j, w, h, wpl, val; +l_uint32 *data, *line; + + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + if (pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not 32 bpp", __func__, 1); + if (offset > 0x40000000) + offset = 0x40000000; + + pixGetDimensions(pixs, &w, &h, NULL); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + val = line[j] - offset; + val = (l_int32)(val * factor); + val += offset; + line[j] = (l_uint32)val; + } + } + + return 0; +} + + +/*-----------------------------------------------------------------------* + * Absolute value of difference * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixAbsDifference() + * + * \param[in] pixs1, pixs2 both either 8 or 16 bpp gray, or 32 bpp RGB + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) The depth of pixs1 and pixs2 must be equal. + * (2) Clips computation to the min size, aligning the UL corners + * (3) For 8 and 16 bpp, assumes one gray component. + * (4) For 32 bpp, assumes 3 color components, and ignores the + * LSB of each word (the alpha channel) + * (5) Computes the absolute value of the difference between + * each component value. + * </pre> + */ +PIX * +pixAbsDifference(PIX *pixs1, + PIX *pixs2) +{ +l_int32 i, j, w, h, w2, h2, d, wpls1, wpls2, wpld, val1, val2, diff; +l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rdiff, gdiff, bdiff; +l_uint32 *datas1, *datas2, *datad, *lines1, *lines2, *lined; +PIX *pixd; + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", __func__, NULL); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", __func__, NULL); + d = pixGetDepth(pixs1); + if (d != pixGetDepth(pixs2)) + return (PIX *)ERROR_PTR("src1 and src2 depths unequal", __func__, NULL); + if (d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("depths not in {8, 16, 32}", __func__, NULL); + + pixGetDimensions(pixs1, &w, &h, NULL); + pixGetDimensions(pixs2, &w2, &h2, NULL); + w = L_MIN(w, w2); + h = L_MIN(h, h2); + if ((pixd = pixCreate(w, h, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + pixCopyResolution(pixd, pixs1); + datas1 = pixGetData(pixs1); + datas2 = pixGetData(pixs2); + datad = pixGetData(pixd); + wpls1 = pixGetWpl(pixs1); + wpls2 = pixGetWpl(pixs2); + wpld = pixGetWpl(pixd); + if (d == 8) { + for (i = 0; i < h; i++) { + lines1 = datas1 + i * wpls1; + lines2 = datas2 + i * wpls2; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val1 = GET_DATA_BYTE(lines1, j); + val2 = GET_DATA_BYTE(lines2, j); + diff = L_ABS(val1 - val2); + SET_DATA_BYTE(lined, j, diff); + } + } + } else if (d == 16) { + for (i = 0; i < h; i++) { + lines1 = datas1 + i * wpls1; + lines2 = datas2 + i * wpls2; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val1 = GET_DATA_TWO_BYTES(lines1, j); + val2 = GET_DATA_TWO_BYTES(lines2, j); + diff = L_ABS(val1 - val2); + SET_DATA_TWO_BYTES(lined, j, diff); + } + } + } else { /* d == 32 */ + for (i = 0; i < h; i++) { + lines1 = datas1 + i * wpls1; + lines2 = datas2 + i * wpls2; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines1[j], &rval1, &gval1, &bval1); + extractRGBValues(lines2[j], &rval2, &gval2, &bval2); + rdiff = L_ABS(rval1 - rval2); + gdiff = L_ABS(gval1 - gval2); + bdiff = L_ABS(bval1 - bval2); + composeRGBPixel(rdiff, gdiff, bdiff, lined + j); + } + } + } + + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Sum of color images * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixAddRGB() + * + * \param[in] pixs1, pixs2 32 bpp RGB, or colormapped + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) Clips computation to the minimum size, aligning the UL corners. + * (2) Removes any colormap to RGB, and ignores the LSB of each + * pixel word (the alpha channel). + * (3) Adds each component value, pixelwise, clipping to 255. + * (4) This is useful to combine two images where most of the + * pixels are essentially black, such as in pixPerceptualDiff(). + * </pre> + */ +PIX * +pixAddRGB(PIX *pixs1, + PIX *pixs2) +{ +l_int32 i, j, w, h, d, w2, h2, d2, wplc1, wplc2, wpld; +l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rval, gval, bval; +l_uint32 *datac1, *datac2, *datad, *linec1, *linec2, *lined; +PIX *pixc1, *pixc2, *pixd; + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", __func__, NULL); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", __func__, NULL); + pixGetDimensions(pixs1, &w, &h, &d); + pixGetDimensions(pixs2, &w2, &h2, &d2); + if (!pixGetColormap(pixs1) && d != 32) + return (PIX *)ERROR_PTR("pixs1 not cmapped or rgb", __func__, NULL); + if (!pixGetColormap(pixs2) && d2 != 32) + return (PIX *)ERROR_PTR("pixs2 not cmapped or rgb", __func__, NULL); + if (pixGetColormap(pixs1)) + pixc1 = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR); + else + pixc1 = pixClone(pixs1); + if (pixGetColormap(pixs2)) + pixc2 = pixRemoveColormap(pixs2, REMOVE_CMAP_TO_FULL_COLOR); + else + pixc2 = pixClone(pixs2); + + w = L_MIN(w, w2); + h = L_MIN(h, h2); + pixd = pixCreate(w, h, 32); + pixCopyResolution(pixd, pixs1); + datac1 = pixGetData(pixc1); + datac2 = pixGetData(pixc2); + datad = pixGetData(pixd); + wplc1 = pixGetWpl(pixc1); + wplc2 = pixGetWpl(pixc2); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + linec1 = datac1 + i * wplc1; + linec2 = datac2 + i * wplc2; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(linec1[j], &rval1, &gval1, &bval1); + extractRGBValues(linec2[j], &rval2, &gval2, &bval2); + rval = L_MIN(255, rval1 + rval2); + gval = L_MIN(255, gval1 + gval2); + bval = L_MIN(255, bval1 + bval2); + composeRGBPixel(rval, gval, bval, lined + j); + } + } + + pixDestroy(&pixc1); + pixDestroy(&pixc2); + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Two-image min and max operations (8 and 16 bpp) * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixMinOrMax() + * + * \param[in] pixd [optional] destination: this can be null, + * equal to pixs1, or different from pixs1 + * \param[in] pixs1 can be equal to pixd + * \param[in] pixs2 + * \param[in] type L_CHOOSE_MIN, L_CHOOSE_MAX + * \return pixd always + * + * <pre> + * Notes: + * (1) This gives the min or max of two images, component-wise. + * (2) The depth can be 8 or 16 bpp for 1 component, and 32 bpp + * for a 3 component image. For 32 bpp, ignore the LSB + * of each word (the alpha channel) + * (3) There are 3 cases: + * ~ if pixd == null, MinOrMax(src1, src2) --> new pixd + * ~ if pixd == pixs1, MinOrMax(src1, src2) --> src1 (in-place) + * ~ if pixd != pixs1, MinOrMax(src1, src2) --> input pixd + * </pre> + */ +PIX * +pixMinOrMax(PIX *pixd, + PIX *pixs1, + PIX *pixs2, + l_int32 type) +{ +l_int32 d, ws, hs, w, h, wpls, wpld, i, j, vals, vald, val; +l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rval, gval, bval; +l_uint32 *datas, *datad, *lines, *lined; + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd); + if (pixs1 == pixs2) + return (PIX *)ERROR_PTR("pixs1 and pixs2 must differ", __func__, pixd); + if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX) + return (PIX *)ERROR_PTR("invalid type", __func__, pixd); + d = pixGetDepth(pixs1); + if (pixGetDepth(pixs2) != d) + return (PIX *)ERROR_PTR("depths unequal", __func__, pixd); + if (d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("depth not 8, 16 or 32 bpp", __func__, pixd); + + if (pixs1 != pixd) + pixd = pixCopy(pixd, pixs1); + + pixGetDimensions(pixs2, &ws, &hs, NULL); + pixGetDimensions(pixd, &w, &h, NULL); + w = L_MIN(w, ws); + h = L_MIN(h, hs); + datas = pixGetData(pixs2); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs2); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (d == 8) { + for (j = 0; j < w; j++) { + vals = GET_DATA_BYTE(lines, j); + vald = GET_DATA_BYTE(lined, j); + if (type == L_CHOOSE_MIN) + val = L_MIN(vals, vald); + else /* type == L_CHOOSE_MAX */ + val = L_MAX(vals, vald); + SET_DATA_BYTE(lined, j, val); + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + vals = GET_DATA_TWO_BYTES(lines, j); + vald = GET_DATA_TWO_BYTES(lined, j); + if (type == L_CHOOSE_MIN) + val = L_MIN(vals, vald); + else /* type == L_CHOOSE_MAX */ + val = L_MAX(vals, vald); + SET_DATA_TWO_BYTES(lined, j, val); + } + } else { /* d == 32 */ + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval1, &gval1, &bval1); + extractRGBValues(lined[j], &rval2, &gval2, &bval2); + if (type == L_CHOOSE_MIN) { + rval = L_MIN(rval1, rval2); + gval = L_MIN(gval1, gval2); + bval = L_MIN(bval1, bval2); + } else { /* type == L_CHOOSE_MAX */ + rval = L_MAX(rval1, rval2); + gval = L_MAX(gval1, gval2); + bval = L_MAX(bval1, bval2); + } + composeRGBPixel(rval, gval, bval, lined + j); + } + } + } + + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Scale for maximum dynamic range * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixMaxDynamicRange() + * + * \param[in] pixs 4, 8, 16 or 32 bpp source + * \param[in] type L_LINEAR_SCALE or L_LOG_SCALE + * \return pixd 8 bpp, or NULL on error + * + * <pre> + * Notes: + * (1) Scales pixel values to fit maximally within the dest 8 bpp pixd + * (2) Assumes the source 'pixels' are a 1-component scalar. For + * a 32 bpp source, each pixel is treated as a single number -- + * not as a 3-component rgb pixel value. + * (3) Uses a LUT for log scaling. + * </pre> + */ +PIX * +pixMaxDynamicRange(PIX *pixs, + l_int32 type) +{ +l_uint8 dval; +l_int32 i, j, w, h, d, wpls, wpld, max; +l_uint32 *datas, *datad; +l_uint32 word, sval; +l_uint32 *lines, *lined; +l_float32 factor; +l_float32 *tab; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pixs not in {4,8,16,32} bpp", __func__, NULL); + if (type != L_LINEAR_SCALE && type != L_LOG_SCALE) + return (PIX *)ERROR_PTR("invalid type", __func__, NULL); + + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + pixCopyResolution(pixd, pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + /* Get max */ + max = 0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < wpls; j++) { + word = *(lines + j); + if (d == 4) { + max = L_MAX(max, word >> 28); + max = L_MAX(max, (word >> 24) & 0xf); + max = L_MAX(max, (word >> 20) & 0xf); + max = L_MAX(max, (word >> 16) & 0xf); + max = L_MAX(max, (word >> 12) & 0xf); + max = L_MAX(max, (word >> 8) & 0xf); + max = L_MAX(max, (word >> 4) & 0xf); + max = L_MAX(max, word & 0xf); + } else if (d == 8) { + max = L_MAX(max, word >> 24); + max = L_MAX(max, (word >> 16) & 0xff); + max = L_MAX(max, (word >> 8) & 0xff); + max = L_MAX(max, word & 0xff); + } else if (d == 16) { + max = L_MAX(max, word >> 16); + max = L_MAX(max, word & 0xffff); + } else { /* d == 32 (rgb) */ + max = L_MAX(max, word); + } + } + } + + /* Map to the full dynamic range */ + if (d == 4) { + if (type == L_LINEAR_SCALE) { + factor = 255.f / (l_float32)max; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_QBIT(lines, j); + dval = (l_uint8)(factor * (l_float32)sval + 0.5); + SET_DATA_QBIT(lined, j, dval); + } + } + } else { /* type == L_LOG_SCALE) */ + tab = makeLogBase2Tab(); + factor = 255.f / getLogBase2(max, tab); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_QBIT(lines, j); + dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + LEPT_FREE(tab); + } + } else if (d == 8) { + if (type == L_LINEAR_SCALE) { + factor = 255.f / (l_float32)max; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_BYTE(lines, j); + dval = (l_uint8)(factor * (l_float32)sval + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + } else { /* type == L_LOG_SCALE) */ + tab = makeLogBase2Tab(); + factor = 255.f / getLogBase2(max, tab); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_BYTE(lines, j); + dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + LEPT_FREE(tab); + } + } else if (d == 16) { + if (type == L_LINEAR_SCALE) { + factor = 255.f / (l_float32)max; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_TWO_BYTES(lines, j); + dval = (l_uint8)(factor * (l_float32)sval + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + } else { /* type == L_LOG_SCALE) */ + tab = makeLogBase2Tab(); + factor = 255.f / getLogBase2(max, tab); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_TWO_BYTES(lines, j); + dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + LEPT_FREE(tab); + } + } else { /* d == 32 */ + if (type == L_LINEAR_SCALE) { + factor = 255.f / (l_float32)max; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = lines[j]; + dval = (l_uint8)(factor * (l_float32)sval + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + } else { /* type == L_LOG_SCALE) */ + tab = makeLogBase2Tab(); + factor = 255.f / getLogBase2(max, tab); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = lines[j]; + dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + LEPT_FREE(tab); + } + } + + return pixd; +} + + +/*! + * \brief pixMaxDynamicRangeRGB() + * + * \param[in] pixs 32 bpp rgb source + * \param[in] type L_LINEAR_SCALE or L_LOG_SCALE + * \return pixd 32 bpp, or NULL on error + * + * <pre> + * Notes: + * (1) Scales pixel values to fit maximally within a 32 bpp dest pixd + * (2) All color components are scaled with the same factor, based + * on the maximum r, g or b component in the image. This should + * not be used if the 32-bit value is a single number (e.g., a + * count in a histogram generated by pixMakeHistoHS()). + * (3) Uses a LUT for log scaling. + * </pre> + */ +PIX * +pixMaxDynamicRangeRGB(PIX *pixs, + l_int32 type) +{ +l_int32 i, j, w, h, wpls, wpld, max; +l_uint32 sval, dval, word; +l_uint32 *datas, *datad; +l_uint32 *lines, *lined; +l_float32 factor; +l_float32 *tab; +PIX *pixd; + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL); + if (type != L_LINEAR_SCALE && type != L_LOG_SCALE) + return (PIX *)ERROR_PTR("invalid type", __func__, NULL); + + /* Get max */ + pixd = pixCreateTemplate(pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + pixGetDimensions(pixs, &w, &h, NULL); + max = 0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < wpls; j++) { + word = lines[j]; + max = L_MAX(max, word >> 24); + max = L_MAX(max, (word >> 16) & 0xff); + max = L_MAX(max, (word >> 8) & 0xff); + } + } + if (max == 0) { + L_WARNING("max = 0; setting to 1\n", __func__); + max = 1; + } + + /* Map to the full dynamic range */ + if (type == L_LINEAR_SCALE) { + factor = 255.f / (l_float32)max; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = lines[j]; + dval = linearScaleRGBVal(sval, factor); + lined[j] = dval; + } + } + } else { /* type == L_LOG_SCALE) */ + tab = makeLogBase2Tab(); + factor = 255.f / getLogBase2(max, tab); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = lines[j]; + dval = logScaleRGBVal(sval, tab, factor); + lined[j] = dval; + } + } + LEPT_FREE(tab); + } + + return pixd; +} + + +/*-----------------------------------------------------------------------* + * RGB pixel value scaling * + *-----------------------------------------------------------------------*/ +/*! + * \brief linearScaleRGBVal() + * + * \param[in] sval 32-bit rgb pixel value + * \param[in] factor multiplication factor on each component + * \return dval linearly scaled version of %sval + * + * <pre> + * Notes: + * (1) %factor must be chosen to be not greater than (255 / maxcomp), + * where maxcomp is the maximum value of the pixel components. + * Otherwise, the product will overflow a uint8. In use, factor + * is the same for all pixels in a pix. + * (2) No scaling is performed on the transparency ("A") component. + * </pre> + */ +l_uint32 +linearScaleRGBVal(l_uint32 sval, + l_float32 factor) +{ +l_uint32 dval; + + dval = ((l_uint8)(factor * (sval >> 24) + 0.5f) << 24) | + ((l_uint8)(factor * ((sval >> 16) & 0xff) + 0.5f) << 16) | + ((l_uint8)(factor * ((sval >> 8) & 0xff) + 0.5f) << 8) | + (sval & 0xff); + return dval; +} + + +/*! + * \brief logScaleRGBVal() + * + * \param[in] sval 32-bit rgb pixel value + * \param[in] tab 256 entry log-base-2 table + * \param[in] factor multiplication factor on each component + * \return dval log scaled version of %sval + * + * <pre> + * Notes: + * (1) %tab is made with makeLogBase2Tab(). + * (2) %factor must be chosen to be not greater than + * 255.0 / log[base2](maxcomp), where maxcomp is the maximum + * value of the pixel components. Otherwise, the product + * will overflow a uint8. In use, factor is the same for + * all pixels in a pix. + * (3) No scaling is performed on the transparency ("A") component. + * </pre> + */ +l_uint32 +logScaleRGBVal(l_uint32 sval, + l_float32 *tab, + l_float32 factor) +{ +l_uint32 dval; + + dval = ((l_uint8)(factor * getLogBase2(sval >> 24, tab) + 0.5f) << 24) | + ((l_uint8)(factor * getLogBase2(((sval >> 16) & 0xff), tab) + 0.5f) + << 16) | + ((l_uint8)(factor * getLogBase2(((sval >> 8) & 0xff), tab) + 0.5f) + << 8) | + (sval & 0xff); + return dval; +} + + +/*-----------------------------------------------------------------------* + * Log base2 lookup * + *-----------------------------------------------------------------------*/ +/* + * \brief makeLogBase2Tab() + * + * \return tab table giving the log[base2] of values from 1 to 255 + */ +l_float32 * +makeLogBase2Tab(void) +{ +l_int32 i; +l_float32 log2; +l_float32 *tab; + + if ((tab = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32))) == NULL) + return (l_float32 *)ERROR_PTR("tab not made", __func__, NULL); + + log2 = (l_float32)log((l_float32)2); + for (i = 0; i < 256; i++) + tab[i] = (l_float32)log((l_float32)i) / log2; + + return tab; +} + + +/* + * \brief getLogBase2() + * + * \param[in] val in range [0 ... 255] + * \param[in] logtab 256-entry table of logs + * \return logval log[base2] of %val, or 0 on error + */ +l_float32 +getLogBase2(l_int32 val, + l_float32 *logtab) +{ + if (!logtab) + return ERROR_INT("logtab not defined", __func__, 0); + + if (val < 0x100) + return logtab[val]; + else if (val < 0x10000) + return 8.0f + logtab[val >> 8]; + else if (val < 0x1000000) + return 16.0f + logtab[val >> 16]; + else + return 24.0f + logtab[val >> 24]; +}
