diff options
Diffstat (limited to 'leptonica/src/fpix2.c')
-rw-r--r-- | leptonica/src/fpix2.c | 2447 |
1 files changed, 2447 insertions, 0 deletions
diff --git a/leptonica/src/fpix2.c b/leptonica/src/fpix2.c new file mode 100644 index 00000000..1bb8bbed --- /dev/null +++ b/leptonica/src/fpix2.c @@ -0,0 +1,2447 @@ +/*====================================================================* + - 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 fpix2.c + * <pre> + * + * ------------------------------------------ + * This file has these FPix utilities: + * ~ interconversions with pix, fpix, dpix + * ~ min and max values + * ~ integer scaling + * ~ arithmetic operations + * ~ set all + * ~ border functions + * ~ simple rasterop (source --> dest) + * ~ geometric transforms + * ------------------------------------------ + * + * Interconversions between Pix, FPix and DPix + * FPIX *pixConvertToFPix() + * DPIX *pixConvertToDPix() + * PIX *fpixConvertToPix() + * PIX *fpixDisplayMaxDynamicRange() [useful for debugging] + * DPIX *fpixConvertToDPix() + * PIX *dpixConvertToPix() + * FPIX *dpixConvertToFPix() + * + * Min/max value + * l_int32 fpixGetMin() + * l_int32 fpixGetMax() + * l_int32 dpixGetMin() + * l_int32 dpixGetMax() + * + * Integer scaling + * FPIX *fpixScaleByInteger() + * DPIX *dpixScaleByInteger() + * + * Arithmetic operations + * FPIX *fpixLinearCombination() + * l_int32 fpixAddMultConstant() + * DPIX *dpixLinearCombination() + * l_int32 dpixAddMultConstant() + * + * Set all + * l_int32 fpixSetAllArbitrary() + * l_int32 dpixSetAllArbitrary() + * + * FPix border functions + * FPIX *fpixAddBorder() + * FPIX *fpixRemoveBorder() + * FPIX *fpixAddMirroredBorder() + * FPIX *fpixAddContinuedBorder() + * FPIX *fpixAddSlopeBorder() + * + * FPix simple rasterop + * l_int32 fpixRasterop() + * + * FPix rotation by multiples of 90 degrees + * FPIX *fpixRotateOrth() + * FPIX *fpixRotate180() + * FPIX *fpixRotate90() + * FPIX *fpixFlipLR() + * FPIX *fpixFlipTB() + * + * FPix affine and projective interpolated transforms + * FPIX *fpixAffinePta() + * FPIX *fpixAffine() + * FPIX *fpixProjectivePta() + * FPIX *fpixProjective() + * l_int32 linearInterpolatePixelFloat() + * + * Thresholding to 1 bpp Pix + * PIX *fpixThresholdToPix() + * + * Generate function from components + * FPIX *pixComponentFunction() + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include "allheaders.h" + +/*--------------------------------------------------------------------* + * FPix <--> Pix conversions * + *--------------------------------------------------------------------*/ +/*! + * \brief pixConvertToFPix() + * + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \param[in] ncomps number of components: 3 for RGB, 1 otherwise + * \return fpix, or NULL on error + * + * <pre> + * Notes: + * (1) If colormapped, remove to grayscale. + * (2) If 32 bpp and %ncomps == 3, this is RGB; convert to luminance. + * In all other cases the src image is treated as having a single + * component of pixel values. + * </pre> + */ +FPIX * +pixConvertToFPix(PIX *pixs, + l_int32 ncomps) +{ +l_int32 w, h, d, i, j, val, wplt, wpld; +l_uint32 uval; +l_uint32 *datat, *linet; +l_float32 *datad, *lined; +PIX *pixt; +FPIX *fpixd; + + PROCNAME("pixConvertToFPix"); + + if (!pixs) + return (FPIX *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Convert to a single component */ + if (pixGetColormap(pixs)) + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else if (pixGetDepth(pixs) == 32 && ncomps == 3) + pixt = pixConvertRGBToLuminance(pixs); + else + pixt = pixClone(pixs); + pixGetDimensions(pixt, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) { + pixDestroy(&pixt); + return (FPIX *)ERROR_PTR("invalid depth", procName, NULL); + } + + if ((fpixd = fpixCreate(w, h)) == NULL) { + pixDestroy(&pixt); + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + } + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + datad = fpixGetData(fpixd); + wpld = fpixGetWpl(fpixd); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + if (d == 1) { + for (j = 0; j < w; j++) { + val = GET_DATA_BIT(linet, j); + lined[j] = (l_float32)val; + } + } else if (d == 2) { + for (j = 0; j < w; j++) { + val = GET_DATA_DIBIT(linet, j); + lined[j] = (l_float32)val; + } + } else if (d == 4) { + for (j = 0; j < w; j++) { + val = GET_DATA_QBIT(linet, j); + lined[j] = (l_float32)val; + } + } else if (d == 8) { + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(linet, j); + lined[j] = (l_float32)val; + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + val = GET_DATA_TWO_BYTES(linet, j); + lined[j] = (l_float32)val; + } + } else { /* d == 32 */ + for (j = 0; j < w; j++) { + uval = GET_DATA_FOUR_BYTES(linet, j); + lined[j] = (l_float32)uval; + } + } + } + + pixDestroy(&pixt); + return fpixd; +} + + +/*! + * \brief pixConvertToDPix() + * + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \param[in] ncomps number of components: 3 for RGB, 1 otherwise + * \return dpix, or NULL on error + * + * <pre> + * Notes: + * (1) If colormapped, remove to grayscale. + * (2) If 32 bpp and %ncomps == 3, this is RGB; convert to luminance. + * In all other cases the src image is treated as having a single + * component of pixel values. + * </pre> + */ +DPIX * +pixConvertToDPix(PIX *pixs, + l_int32 ncomps) +{ +l_int32 w, h, d, i, j, val, wplt, wpld; +l_uint32 uval; +l_uint32 *datat, *linet; +l_float64 *datad, *lined; +PIX *pixt; +DPIX *dpixd; + + PROCNAME("pixConvertToDPix"); + + if (!pixs) + return (DPIX *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Convert to a single component */ + if (pixGetColormap(pixs)) + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else if (pixGetDepth(pixs) == 32 && ncomps == 3) + pixt = pixConvertRGBToLuminance(pixs); + else + pixt = pixClone(pixs); + pixGetDimensions(pixt, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) { + pixDestroy(&pixt); + return (DPIX *)ERROR_PTR("invalid depth", procName, NULL); + } + + if ((dpixd = dpixCreate(w, h)) == NULL) { + pixDestroy(&pixt); + return (DPIX *)ERROR_PTR("dpixd not made", procName, NULL); + } + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + datad = dpixGetData(dpixd); + wpld = dpixGetWpl(dpixd); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + if (d == 1) { + for (j = 0; j < w; j++) { + val = GET_DATA_BIT(linet, j); + lined[j] = (l_float64)val; + } + } else if (d == 2) { + for (j = 0; j < w; j++) { + val = GET_DATA_DIBIT(linet, j); + lined[j] = (l_float64)val; + } + } else if (d == 4) { + for (j = 0; j < w; j++) { + val = GET_DATA_QBIT(linet, j); + lined[j] = (l_float64)val; + } + } else if (d == 8) { + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(linet, j); + lined[j] = (l_float64)val; + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + val = GET_DATA_TWO_BYTES(linet, j); + lined[j] = (l_float64)val; + } + } else { /* d == 32 */ + for (j = 0; j < w; j++) { + uval = GET_DATA_FOUR_BYTES(linet, j); + lined[j] = (l_float64)uval; + } + } + } + + pixDestroy(&pixt); + return dpixd; +} + + +/*! + * \brief fpixConvertToPix() + * + * \param[in] fpixs + * \param[in] outdepth 0, 8, 16 or 32 bpp + * \param[in] negvals L_CLIP_TO_ZERO, L_TAKE_ABSVAL + * \param[in] errorflag 1 to output error stats; 0 otherwise + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) Use %outdepth = 0 to programmatically determine the + * output depth. If no values are greater than 255, + * it will set outdepth = 8; otherwise to 16 or 32. + * (2) Because we are converting a float to an unsigned int + * with a specified dynamic range (8, 16 or 32 bits), errors + * can occur. If errorflag == TRUE, output the number + * of values out of range, both negative and positive. + * (3) If a pixel value is positive and out of range, clip to + * the maximum value represented at the outdepth of 8, 16 + * or 32 bits. + * </pre> + */ +PIX * +fpixConvertToPix(FPIX *fpixs, + l_int32 outdepth, + l_int32 negvals, + l_int32 errorflag) +{ +l_int32 w, h, i, j, wpls, wpld; +l_uint32 vald, maxval; +l_float32 val; +l_float32 *datas, *lines; +l_uint32 *datad, *lined; +PIX *pixd; + + PROCNAME("fpixConvertToPix"); + + if (!fpixs) + return (PIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (negvals != L_CLIP_TO_ZERO && negvals != L_TAKE_ABSVAL) + return (PIX *)ERROR_PTR("invalid negvals", procName, NULL); + if (outdepth != 0 && outdepth != 8 && outdepth != 16 && outdepth != 32) + return (PIX *)ERROR_PTR("outdepth not in {0,8,16,32}", procName, NULL); + + fpixGetDimensions(fpixs, &w, &h); + datas = fpixGetData(fpixs); + wpls = fpixGetWpl(fpixs); + + /* Adaptive determination of output depth */ + if (outdepth == 0) { + outdepth = 8; + for (i = 0; i < h && outdepth < 32; i++) { + lines = datas + i * wpls; + for (j = 0; j < w && outdepth < 32; j++) { + if (lines[j] > 65535.5) + outdepth = 32; + else if (lines[j] > 255.5) + outdepth = 16; + } + } + } + if (outdepth == 8) + maxval = 0xff; + else if (outdepth == 16) + maxval = 0xffff; + else /* outdepth == 32 */ + maxval = 0xffffffff; + + /* Gather statistics if %errorflag = TRUE */ + if (errorflag) { + l_int32 negs = 0; + l_int32 overvals = 0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + val = lines[j]; + if (val < 0.0) + negs++; + else if (val > maxval) + overvals++; + } + } + if (negs > 0) + L_ERROR("Number of negative values: %d\n", procName, negs); + if (overvals > 0) + L_ERROR("Number of too-large values: %d\n", procName, overvals); + } + + /* Make the pix and convert the data */ + if ((pixd = pixCreate(w, h, outdepth)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datad = pixGetData(pixd); + 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]; + if (val >= 0.0) + vald = (l_uint32)(val + 0.5); + else if (negvals == L_CLIP_TO_ZERO) /* and val < 0.0 */ + vald = 0; + else + vald = (l_uint32)(-val + 0.5); + if (vald > maxval) + vald = maxval; + + if (outdepth == 8) + SET_DATA_BYTE(lined, j, vald); + else if (outdepth == 16) + SET_DATA_TWO_BYTES(lined, j, vald); + else /* outdepth == 32 */ + SET_DATA_FOUR_BYTES(lined, j, vald); + } + } + + return pixd; +} + + +/*! + * \brief fpixDisplayMaxDynamicRange() + * + * \param[in] fpixs + * \return pixd 8 bpp, or NULL on error + */ +PIX * +fpixDisplayMaxDynamicRange(FPIX *fpixs) +{ +l_uint8 dval; +l_int32 i, j, w, h, wpls, wpld; +l_float32 factor, sval, maxval; +l_float32 *lines, *datas; +l_uint32 *lined, *datad; +PIX *pixd; + + PROCNAME("fpixDisplayMaxDynamicRange"); + + if (!fpixs) + return (PIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + fpixGetDimensions(fpixs, &w, &h); + datas = fpixGetData(fpixs); + wpls = fpixGetWpl(fpixs); + + maxval = 0.0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + sval = *(lines + j); + if (sval > maxval) + maxval = sval; + } + } + + pixd = pixCreate(w, h, 8); + if (maxval == 0.0) + return pixd; /* all pixels are 0 */ + + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + factor = 255. / maxval; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = *(lines + j); + if (sval < 0.0) sval = 0.0; + dval = (l_uint8)(factor * sval + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + + return pixd; +} + + +/*! + * \brief fpixConvertToDPix() + * + * \param[in] fpix + * \return dpix, or NULL on error + */ +DPIX * +fpixConvertToDPix(FPIX *fpix) +{ +l_int32 w, h, i, j, wpls, wpld; +l_float32 val; +l_float32 *datas, *lines; +l_float64 *datad, *lined; +DPIX *dpix; + + PROCNAME("fpixConvertToDPix"); + + if (!fpix) + return (DPIX *)ERROR_PTR("fpix not defined", procName, NULL); + + fpixGetDimensions(fpix, &w, &h); + if ((dpix = dpixCreate(w, h)) == NULL) + return (DPIX *)ERROR_PTR("dpix not made", procName, NULL); + + datas = fpixGetData(fpix); + datad = dpixGetData(dpix); + wpls = fpixGetWpl(fpix); + wpld = dpixGetWpl(dpix); /* 8 byte words */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j]; + lined[j] = val; + } + } + + return dpix; +} + + +/*! + * \brief dpixConvertToPix() + * + * \param[in] dpixs + * \param[in] outdepth 0, 8, 16 or 32 bpp + * \param[in] negvals L_CLIP_TO_ZERO, L_TAKE_ABSVAL + * \param[in] errorflag 1 to output error stats; 0 otherwise + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) Use %outdepth = 0 to programmatically determine the + * output depth. If no values are greater than 255, + * it will set outdepth = 8; otherwise to 16 or 32. + * (2) Because we are converting a float to an unsigned int + * with a specified dynamic range (8, 16 or 32 bits), errors + * can occur. If errorflag == TRUE, output the number + * of values out of range, both negative and positive. + * (3) If a pixel value is positive and out of range, clip to + * the maximum value represented at the outdepth of 8, 16 + * or 32 bits. + * </pre> + */ +PIX * +dpixConvertToPix(DPIX *dpixs, + l_int32 outdepth, + l_int32 negvals, + l_int32 errorflag) +{ +l_int32 w, h, i, j, wpls, wpld, maxval; +l_uint32 vald; +l_float64 val; +l_float64 *datas, *lines; +l_uint32 *datad, *lined; +PIX *pixd; + + PROCNAME("dpixConvertToPix"); + + if (!dpixs) + return (PIX *)ERROR_PTR("dpixs not defined", procName, NULL); + if (negvals != L_CLIP_TO_ZERO && negvals != L_TAKE_ABSVAL) + return (PIX *)ERROR_PTR("invalid negvals", procName, NULL); + if (outdepth != 0 && outdepth != 8 && outdepth != 16 && outdepth != 32) + return (PIX *)ERROR_PTR("outdepth not in {0,8,16,32}", procName, NULL); + + dpixGetDimensions(dpixs, &w, &h); + datas = dpixGetData(dpixs); + wpls = dpixGetWpl(dpixs); + + /* Adaptive determination of output depth */ + if (outdepth == 0) { + outdepth = 8; + for (i = 0; i < h && outdepth < 32; i++) { + lines = datas + i * wpls; + for (j = 0; j < w && outdepth < 32; j++) { + if (lines[j] > 65535.5) + outdepth = 32; + else if (lines[j] > 255.5) + outdepth = 16; + } + } + } + maxval = 0xff; + if (outdepth == 16) + maxval = 0xffff; + else /* outdepth == 32 */ + maxval = 0xffffffff; + + /* Gather statistics if %errorflag = TRUE */ + if (errorflag) { + l_int32 negs = 0; + l_int32 overvals = 0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + val = lines[j]; + if (val < 0.0) + negs++; + else if (val > maxval) + overvals++; + } + } + if (negs > 0) + L_ERROR("Number of negative values: %d\n", procName, negs); + if (overvals > 0) + L_ERROR("Number of too-large values: %d\n", procName, overvals); + } + + /* Make the pix and convert the data */ + if ((pixd = pixCreate(w, h, outdepth)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datad = pixGetData(pixd); + 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]; + if (val >= 0.0) { + vald = (l_uint32)(val + 0.5); + } else { /* val < 0.0 */ + if (negvals == L_CLIP_TO_ZERO) + vald = 0; + else + vald = (l_uint32)(-val + 0.5); + } + if (vald > maxval) + vald = maxval; + if (outdepth == 8) + SET_DATA_BYTE(lined, j, vald); + else if (outdepth == 16) + SET_DATA_TWO_BYTES(lined, j, vald); + else /* outdepth == 32 */ + SET_DATA_FOUR_BYTES(lined, j, vald); + } + } + + return pixd; +} + + +/*! + * \brief dpixConvertToFPix() + * + * \param[in] dpix + * \return fpix, or NULL on error + */ +FPIX * +dpixConvertToFPix(DPIX *dpix) +{ +l_int32 w, h, i, j, wpls, wpld; +l_float64 val; +l_float32 *datad, *lined; +l_float64 *datas, *lines; +FPIX *fpix; + + PROCNAME("dpixConvertToFPix"); + + if (!dpix) + return (FPIX *)ERROR_PTR("dpix not defined", procName, NULL); + + dpixGetDimensions(dpix, &w, &h); + if ((fpix = fpixCreate(w, h)) == NULL) + return (FPIX *)ERROR_PTR("fpix not made", procName, NULL); + + datas = dpixGetData(dpix); + datad = fpixGetData(fpix); + wpls = dpixGetWpl(dpix); /* 8 byte words */ + wpld = fpixGetWpl(fpix); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j]; + lined[j] = (l_float32)val; + } + } + + return fpix; +} + + + +/*--------------------------------------------------------------------* + * Min/max value * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixGetMin() + * + * \param[in] fpix + * \param[out] pminval [optional] min value + * \param[out] pxminloc [optional] x location of min + * \param[out] pyminloc [optional] y location of min + * \return 0 if OK; 1 on error + */ +l_ok +fpixGetMin(FPIX *fpix, + l_float32 *pminval, + l_int32 *pxminloc, + l_int32 *pyminloc) +{ +l_int32 i, j, w, h, wpl, xminloc, yminloc; +l_float32 *data, *line; +l_float32 minval; + + PROCNAME("fpixGetMin"); + + if (!pminval && !pxminloc && !pyminloc) + return ERROR_INT("no return val requested", procName, 1); + if (pminval) *pminval = 0.0; + if (pxminloc) *pxminloc = 0; + if (pyminloc) *pyminloc = 0; + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + minval = +1.0e20f; + xminloc = 0; + yminloc = 0; + fpixGetDimensions(fpix, &w, &h); + data = fpixGetData(fpix); + wpl = fpixGetWpl(fpix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + if (line[j] < minval) { + minval = line[j]; + xminloc = j; + yminloc = i; + } + } + } + + if (pminval) *pminval = minval; + if (pxminloc) *pxminloc = xminloc; + if (pyminloc) *pyminloc = yminloc; + return 0; +} + + +/*! + * \brief fpixGetMax() + * + * \param[in] fpix + * \param[out] pmaxval [optional] max value + * \param[out] pxmaxloc [optional] x location of max + * \param[out] pymaxloc [optional] y location of max + * \return 0 if OK; 1 on error + */ +l_ok +fpixGetMax(FPIX *fpix, + l_float32 *pmaxval, + l_int32 *pxmaxloc, + l_int32 *pymaxloc) +{ +l_int32 i, j, w, h, wpl, xmaxloc, ymaxloc; +l_float32 *data, *line; +l_float32 maxval; + + PROCNAME("fpixGetMax"); + + if (!pmaxval && !pxmaxloc && !pymaxloc) + return ERROR_INT("no return val requested", procName, 1); + if (pmaxval) *pmaxval = 0.0; + if (pxmaxloc) *pxmaxloc = 0; + if (pymaxloc) *pymaxloc = 0; + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + maxval = -1.0e20f; + xmaxloc = 0; + ymaxloc = 0; + fpixGetDimensions(fpix, &w, &h); + data = fpixGetData(fpix); + wpl = fpixGetWpl(fpix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + if (line[j] > maxval) { + maxval = line[j]; + xmaxloc = j; + ymaxloc = i; + } + } + } + + if (pmaxval) *pmaxval = maxval; + if (pxmaxloc) *pxmaxloc = xmaxloc; + if (pymaxloc) *pymaxloc = ymaxloc; + return 0; +} + + +/*! + * \brief dpixGetMin() + * + * \param[in] dpix + * \param[out] pminval [optional] min value + * \param[out] pxminloc [optional] x location of min + * \param[out] pyminloc [optional] y location of min + * \return 0 if OK; 1 on error + */ +l_ok +dpixGetMin(DPIX *dpix, + l_float64 *pminval, + l_int32 *pxminloc, + l_int32 *pyminloc) +{ +l_int32 i, j, w, h, wpl, xminloc, yminloc; +l_float64 *data, *line; +l_float64 minval; + + PROCNAME("dpixGetMin"); + + if (!pminval && !pxminloc && !pyminloc) + return ERROR_INT("no return val requested", procName, 1); + if (pminval) *pminval = 0.0; + if (pxminloc) *pxminloc = 0; + if (pyminloc) *pyminloc = 0; + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + minval = +1.0e300; + xminloc = 0; + yminloc = 0; + dpixGetDimensions(dpix, &w, &h); + data = dpixGetData(dpix); + wpl = dpixGetWpl(dpix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + if (line[j] < minval) { + minval = line[j]; + xminloc = j; + yminloc = i; + } + } + } + + if (pminval) *pminval = minval; + if (pxminloc) *pxminloc = xminloc; + if (pyminloc) *pyminloc = yminloc; + return 0; +} + + +/*! + * \brief dpixGetMax() + * + * \param[in] dpix + * \param[out] pmaxval [optional] max value + * \param[out] pxmaxloc [optional] x location of max + * \param[out] pymaxloc [optional] y location of max + * \return 0 if OK; 1 on error + */ +l_ok +dpixGetMax(DPIX *dpix, + l_float64 *pmaxval, + l_int32 *pxmaxloc, + l_int32 *pymaxloc) +{ +l_int32 i, j, w, h, wpl, xmaxloc, ymaxloc; +l_float64 *data, *line; +l_float64 maxval; + + PROCNAME("dpixGetMax"); + + if (!pmaxval && !pxmaxloc && !pymaxloc) + return ERROR_INT("no return val requested", procName, 1); + if (pmaxval) *pmaxval = 0.0; + if (pxmaxloc) *pxmaxloc = 0; + if (pymaxloc) *pymaxloc = 0; + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + maxval = -1.0e20; + xmaxloc = 0; + ymaxloc = 0; + dpixGetDimensions(dpix, &w, &h); + data = dpixGetData(dpix); + wpl = dpixGetWpl(dpix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + if (line[j] > maxval) { + maxval = line[j]; + xmaxloc = j; + ymaxloc = i; + } + } + } + + if (pmaxval) *pmaxval = maxval; + if (pxmaxloc) *pxmaxloc = xmaxloc; + if (pymaxloc) *pymaxloc = ymaxloc; + return 0; +} + + +/*--------------------------------------------------------------------* + * Special integer scaling * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixScaleByInteger() + * + * \param[in] fpixs typically low resolution + * \param[in] factor integer scaling factor + * \return fpixd interpolated result, or NULL on error + * + * <pre> + * Notes: + * (1) The width wd of fpixd is related to ws of fpixs by: + * wd = factor * (ws - 1) + 1 (and ditto for the height) + * We avoid special-casing boundary pixels in the interpolation + * by constructing fpixd by inserting (factor - 1) interpolated + * pixels between each pixel in fpixs. Then + * wd = ws + (ws - 1) * (factor - 1) (same as above) + * This also has the advantage that if we subsample by %factor, + * throwing out all the interpolated pixels, we regain the + * original low resolution fpix. + * </pre> + */ +FPIX * +fpixScaleByInteger(FPIX *fpixs, + l_int32 factor) +{ +l_int32 i, j, k, m, ws, hs, wd, hd, wpls, wpld; +l_float32 val0, val1, val2, val3; +l_float32 *datas, *datad, *lines, *lined, *fract; +FPIX *fpixd; + + PROCNAME("fpixScaleByInteger"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + fpixGetDimensions(fpixs, &ws, &hs); + wd = factor * (ws - 1) + 1; + hd = factor * (hs - 1) + 1; + fpixd = fpixCreate(wd, hd); + datas = fpixGetData(fpixs); + datad = fpixGetData(fpixd); + wpls = fpixGetWpl(fpixs); + wpld = fpixGetWpl(fpixd); + fract = (l_float32 *)LEPT_CALLOC(factor, sizeof(l_float32)); + for (i = 0; i < factor; i++) + fract[i] = i / (l_float32)factor; + for (i = 0; i < hs - 1; i++) { + lines = datas + i * wpls; + for (j = 0; j < ws - 1; j++) { + val0 = lines[j]; + val1 = lines[j + 1]; + val2 = lines[wpls + j]; + val3 = lines[wpls + j + 1]; + for (k = 0; k < factor; k++) { /* rows of sub-block */ + lined = datad + (i * factor + k) * wpld; + for (m = 0; m < factor; m++) { /* cols of sub-block */ + lined[j * factor + m] = + val0 * (1.0 - fract[m]) * (1.0 - fract[k]) + + val1 * fract[m] * (1.0 - fract[k]) + + val2 * (1.0 - fract[m]) * fract[k] + + val3 * fract[m] * fract[k]; + } + } + } + } + + /* Do the right-most column of fpixd, skipping LR corner */ + for (i = 0; i < hs - 1; i++) { + lines = datas + i * wpls; + val0 = lines[ws - 1]; + val1 = lines[wpls + ws - 1]; + for (k = 0; k < factor; k++) { + lined = datad + (i * factor + k) * wpld; + lined[wd - 1] = val0 * (1.0 - fract[k]) + val1 * fract[k]; + } + } + + /* Do the bottom-most row of fpixd */ + lines = datas + (hs - 1) * wpls; + lined = datad + (hd - 1) * wpld; + for (j = 0; j < ws - 1; j++) { + val0 = lines[j]; + val1 = lines[j + 1]; + for (m = 0; m < factor; m++) + lined[j * factor + m] = val0 * (1.0 - fract[m]) + val1 * fract[m]; + lined[wd - 1] = lines[ws - 1]; /* LR corner */ + } + + LEPT_FREE(fract); + return fpixd; +} + + +/*! + * \brief dpixScaleByInteger() + * + * \param[in] dpixs typically low resolution + * \param[in] factor integer scaling factor + * \return dpixd interpolated result, or NULL on error + * + * <pre> + * Notes: + * (1) The width wd of dpixd is related to ws of dpixs by: + * wd = factor * (ws - 1) + 1 (and ditto for the height) + * We avoid special-casing boundary pixels in the interpolation + * by constructing fpixd by inserting (factor - 1) interpolated + * pixels between each pixel in fpixs. Then + * wd = ws + (ws - 1) * (factor - 1) (same as above) + * This also has the advantage that if we subsample by %factor, + * throwing out all the interpolated pixels, we regain the + * original low resolution dpix. + * </pre> + */ +DPIX * +dpixScaleByInteger(DPIX *dpixs, + l_int32 factor) +{ +l_int32 i, j, k, m, ws, hs, wd, hd, wpls, wpld; +l_float64 val0, val1, val2, val3; +l_float64 *datas, *datad, *lines, *lined, *fract; +DPIX *dpixd; + + PROCNAME("dpixScaleByInteger"); + + if (!dpixs) + return (DPIX *)ERROR_PTR("dpixs not defined", procName, NULL); + + dpixGetDimensions(dpixs, &ws, &hs); + wd = factor * (ws - 1) + 1; + hd = factor * (hs - 1) + 1; + dpixd = dpixCreate(wd, hd); + datas = dpixGetData(dpixs); + datad = dpixGetData(dpixd); + wpls = dpixGetWpl(dpixs); + wpld = dpixGetWpl(dpixd); + fract = (l_float64 *)LEPT_CALLOC(factor, sizeof(l_float64)); + for (i = 0; i < factor; i++) + fract[i] = i / (l_float64)factor; + for (i = 0; i < hs - 1; i++) { + lines = datas + i * wpls; + for (j = 0; j < ws - 1; j++) { + val0 = lines[j]; + val1 = lines[j + 1]; + val2 = lines[wpls + j]; + val3 = lines[wpls + j + 1]; + for (k = 0; k < factor; k++) { /* rows of sub-block */ + lined = datad + (i * factor + k) * wpld; + for (m = 0; m < factor; m++) { /* cols of sub-block */ + lined[j * factor + m] = + val0 * (1.0 - fract[m]) * (1.0 - fract[k]) + + val1 * fract[m] * (1.0 - fract[k]) + + val2 * (1.0 - fract[m]) * fract[k] + + val3 * fract[m] * fract[k]; + } + } + } + } + + /* Do the right-most column of dpixd, skipping LR corner */ + for (i = 0; i < hs - 1; i++) { + lines = datas + i * wpls; + val0 = lines[ws - 1]; + val1 = lines[wpls + ws - 1]; + for (k = 0; k < factor; k++) { + lined = datad + (i * factor + k) * wpld; + lined[wd - 1] = val0 * (1.0 - fract[k]) + val1 * fract[k]; + } + } + + /* Do the bottom-most row of dpixd */ + lines = datas + (hs - 1) * wpls; + lined = datad + (hd - 1) * wpld; + for (j = 0; j < ws - 1; j++) { + val0 = lines[j]; + val1 = lines[j + 1]; + for (m = 0; m < factor; m++) + lined[j * factor + m] = val0 * (1.0 - fract[m]) + val1 * fract[m]; + lined[wd - 1] = lines[ws - 1]; /* LR corner */ + } + + LEPT_FREE(fract); + return dpixd; +} + + +/*--------------------------------------------------------------------* + * Arithmetic operations * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixLinearCombination() + * + * \param[in] fpixd [optional] this can be null, or equal to fpixs1 + * \param[in] fpixs1 can be equal to fpixd + * \param[in] fpixs2 + * \param[in] a, b multiplication factors on fpixs1 and fpixs2, rsp. + * \return fpixd always + * + * <pre> + * Notes: + * (1) Computes pixelwise linear combination: a * src1 + b * src2 + * (2) Alignment is to UL corner; src1 and src2 do not have to be + * the same size. + * (3) There are 2 cases. The result can go to a new dest, or + * in-place to fpixs1: + * * fpixd == null: (src1 + src2) --> new fpixd + * * fpixd == fpixs1: (src1 + src2) --> src1 (in-place) + * </pre> + */ +FPIX * +fpixLinearCombination(FPIX *fpixd, + FPIX *fpixs1, + FPIX *fpixs2, + l_float32 a, + l_float32 b) +{ +l_int32 i, j, ws, hs, w, h, wpls, wpld; +l_float32 *datas, *datad, *lines, *lined; + + PROCNAME("fpixLinearCombination"); + + if (!fpixs1) + return (FPIX *)ERROR_PTR("fpixs1 not defined", procName, fpixd); + if (!fpixs2) + return (FPIX *)ERROR_PTR("fpixs2 not defined", procName, fpixd); + if (fpixd && (fpixd != fpixs1)) + return (FPIX *)ERROR_PTR("invalid inplace operation", procName, fpixd); + + if (!fpixd) + fpixd = fpixCopy(fpixs1); + datas = fpixGetData(fpixs2); + datad = fpixGetData(fpixd); + wpls = fpixGetWpl(fpixs2); + wpld = fpixGetWpl(fpixd); + fpixGetDimensions(fpixs2, &ws, &hs); + fpixGetDimensions(fpixd, &w, &h); + w = L_MIN(ws, w); + h = L_MIN(hs, h); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) + lined[j] = a * lined[j] + b * lines[j]; + } + + return fpixd; +} + + +/*! + * \brief fpixAddMultConstant() + * + * \param[in] fpix + * \param[in] addc use 0.0 to skip the operation + * \param[in] multc use 1.0 to skip the operation + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This is an in-place operation. + * (2) It can be used to multiply each pixel by a constant, + * and also to add a constant to each pixel. Multiplication + * is done first. + * </pre> + */ +l_ok +fpixAddMultConstant(FPIX *fpix, + l_float32 addc, + l_float32 multc) +{ +l_int32 i, j, w, h, wpl; +l_float32 *line, *data; + + PROCNAME("fpixAddMultConstant"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + if (addc == 0.0 && multc == 1.0) + return 0; + + fpixGetDimensions(fpix, &w, &h); + data = fpixGetData(fpix); + wpl = fpixGetWpl(fpix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (addc == 0.0) { + for (j = 0; j < w; j++) + line[j] *= multc; + } else if (multc == 1.0) { + for (j = 0; j < w; j++) + line[j] += addc; + } else { + for (j = 0; j < w; j++) { + line[j] = multc * line[j] + addc; + } + } + } + + return 0; +} + + +/*! + * \brief dpixLinearCombination() + * + * \param[in] dpixd [optional] this can be null, or equal to dpixs1 + * \param[in] dpixs1 can be equal to dpixd + * \param[in] dpixs2 + * \param[in] a, b multiplication factors on dpixs1 and dpixs2, rsp. + * \return dpixd always + * + * <pre> + * Notes: + * (1) Computes pixelwise linear combination: a * src1 + b * src2 + * (2) Alignment is to UL corner; src1 and src2 do not have to be + * the same size. + * (3) There are 2 cases. The result can go to a new dest, or + * in-place to dpixs1: + * * dpixd == null: (src1 + src2) --> new dpixd + * * dpixd == dpixs1: (src1 + src2) --> src1 (in-place) + * </pre> + */ +DPIX * +dpixLinearCombination(DPIX *dpixd, + DPIX *dpixs1, + DPIX *dpixs2, + l_float32 a, + l_float32 b) +{ +l_int32 i, j, ws, hs, w, h, wpls, wpld; +l_float64 *datas, *datad, *lines, *lined; + + PROCNAME("dpixLinearCombination"); + + if (!dpixs1) + return (DPIX *)ERROR_PTR("dpixs1 not defined", procName, dpixd); + if (!dpixs2) + return (DPIX *)ERROR_PTR("dpixs2 not defined", procName, dpixd); + if (dpixd && (dpixd != dpixs1)) + return (DPIX *)ERROR_PTR("invalid inplace operation", procName, dpixd); + + if (!dpixd) + dpixd = dpixCopy(dpixs1); + datas = dpixGetData(dpixs2); + datad = dpixGetData(dpixd); + wpls = dpixGetWpl(dpixs2); + wpld = dpixGetWpl(dpixd); + dpixGetDimensions(dpixs2, &ws, &hs); + dpixGetDimensions(dpixd, &w, &h); + w = L_MIN(ws, w); + h = L_MIN(hs, h); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) + lined[j] = a * lined[j] + b * lines[j]; + } + + return dpixd; +} + + +/*! + * \brief dpixAddMultConstant() + * + * \param[in] dpix + * \param[in] addc use 0.0 to skip the operation + * \param[in] multc use 1.0 to skip the operation + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This is an in-place operation. + * (2) It can be used to multiply each pixel by a constant, + * and also to add a constant to each pixel. Multiplication + * is done first. + * </pre> + */ +l_ok +dpixAddMultConstant(DPIX *dpix, + l_float64 addc, + l_float64 multc) +{ +l_int32 i, j, w, h, wpl; +l_float64 *line, *data; + + PROCNAME("dpixAddMultConstant"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + if (addc == 0.0 && multc == 1.0) + return 0; + + dpixGetDimensions(dpix, &w, &h); + data = dpixGetData(dpix); + wpl = dpixGetWpl(dpix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (addc == 0.0) { + for (j = 0; j < w; j++) + line[j] *= multc; + } else if (multc == 1.0) { + for (j = 0; j < w; j++) + line[j] += addc; + } else { + for (j = 0; j < w; j++) + line[j] = multc * line[j] + addc; + } + } + + return 0; +} + + +/*--------------------------------------------------------------------* + * Set all * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixSetAllArbitrary() + * + * \param[in] fpix + * \param[in] inval to set at each pixel + * \return 0 if OK, 1 on error + */ +l_ok +fpixSetAllArbitrary(FPIX *fpix, + l_float32 inval) +{ +l_int32 i, j, w, h; +l_float32 *data, *line; + + PROCNAME("fpixSetAllArbitrary"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + fpixGetDimensions(fpix, &w, &h); + data = fpixGetData(fpix); + for (i = 0; i < h; i++) { + line = data + i * w; + for (j = 0; j < w; j++) + *(line + j) = inval; + } + + return 0; +} + + +/*! + * \brief dpixSetAllArbitrary() + * + * \param[in] dpix + * \param[in] inval to set at each pixel + * \return 0 if OK, 1 on error + */ +l_ok +dpixSetAllArbitrary(DPIX *dpix, + l_float64 inval) +{ +l_int32 i, j, w, h; +l_float64 *data, *line; + + PROCNAME("dpixSetAllArbitrary"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + dpixGetDimensions(dpix, &w, &h); + data = dpixGetData(dpix); + for (i = 0; i < h; i++) { + line = data + i * w; + for (j = 0; j < w; j++) + *(line + j) = inval; + } + + return 0; +} + + +/*--------------------------------------------------------------------* + * Border functions * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixAddBorder() + * + * \param[in] fpixs + * \param[in] left, right, top, bot pixels on each side to be added + * \return fpixd, or NULL on error + * + * <pre> + * Notes: + * (1) Adds border of '0' 32-bit pixels + * </pre> + */ +FPIX * +fpixAddBorder(FPIX *fpixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 ws, hs, wd, hd; +FPIX *fpixd; + + PROCNAME("fpixAddBorder"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + if (left <= 0 && right <= 0 && top <= 0 && bot <= 0) + return fpixCopy(fpixs); + fpixGetDimensions(fpixs, &ws, &hs); + wd = ws + left + right; + hd = hs + top + bot; + if ((fpixd = fpixCreate(wd, hd)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + + fpixCopyResolution(fpixd, fpixs); + fpixRasterop(fpixd, left, top, ws, hs, fpixs, 0, 0); + return fpixd; +} + + +/*! + * \brief fpixRemoveBorder() + * + * \param[in] fpixs + * \param[in] left, right, top, bot pixels on each side to be removed + * \return fpixd, or NULL on error + */ +FPIX * +fpixRemoveBorder(FPIX *fpixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 ws, hs, wd, hd; +FPIX *fpixd; + + PROCNAME("fpixRemoveBorder"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + if (left <= 0 && right <= 0 && top <= 0 && bot <= 0) + return fpixCopy(fpixs); + fpixGetDimensions(fpixs, &ws, &hs); + wd = ws - left - right; + hd = hs - top - bot; + if (wd <= 0 || hd <= 0) + return (FPIX *)ERROR_PTR("width & height not both > 0", procName, NULL); + if ((fpixd = fpixCreate(wd, hd)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + + fpixCopyResolution(fpixd, fpixs); + fpixRasterop(fpixd, 0, 0, wd, hd, fpixs, left, top); + return fpixd; +} + + + +/*! + * \brief fpixAddMirroredBorder() + * + * \param[in] fpixs + * \param[in] left, right, top, bot pixels on each side to be added + * \return fpixd, or NULL on error + * + * <pre> + * Notes: + * (1) See pixAddMirroredBorder() for situations of usage. + * </pre> + */ +FPIX * +fpixAddMirroredBorder(FPIX *fpixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 i, j, w, h; +FPIX *fpixd; + + PROCNAME("fpixAddMirroredBorder"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + fpixd = fpixAddBorder(fpixs, left, right, top, bot); + fpixGetDimensions(fpixs, &w, &h); + for (j = 0; j < left; j++) + fpixRasterop(fpixd, left - 1 - j, top, 1, h, + fpixd, left + j, top); + for (j = 0; j < right; j++) + fpixRasterop(fpixd, left + w + j, top, 1, h, + fpixd, left + w - 1 - j, top); + for (i = 0; i < top; i++) + fpixRasterop(fpixd, 0, top - 1 - i, left + w + right, 1, + fpixd, 0, top + i); + for (i = 0; i < bot; i++) + fpixRasterop(fpixd, 0, top + h + i, left + w + right, 1, + fpixd, 0, top + h - 1 - i); + + return fpixd; +} + + +/*! + * \brief fpixAddContinuedBorder() + * + * \param[in] fpixs + * \param[in] left, right, top, bot pixels on each side to be added + * \return fpixd, or NULL on error + * + * <pre> + * Notes: + * (1) This adds pixels on each side whose values are equal to + * the value on the closest boundary pixel. + * </pre> + */ +FPIX * +fpixAddContinuedBorder(FPIX *fpixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 i, j, w, h; +FPIX *fpixd; + + PROCNAME("fpixAddContinuedBorder"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + fpixd = fpixAddBorder(fpixs, left, right, top, bot); + fpixGetDimensions(fpixs, &w, &h); + for (j = 0; j < left; j++) + fpixRasterop(fpixd, j, top, 1, h, fpixd, left, top); + for (j = 0; j < right; j++) + fpixRasterop(fpixd, left + w + j, top, 1, h, fpixd, left + w - 1, top); + for (i = 0; i < top; i++) + fpixRasterop(fpixd, 0, i, left + w + right, 1, fpixd, 0, top); + for (i = 0; i < bot; i++) + fpixRasterop(fpixd, 0, top + h + i, left + w + right, 1, + fpixd, 0, top + h - 1); + + return fpixd; +} + + +/*! + * \brief fpixAddSlopeBorder() + * + * \param[in] fpixs + * \param[in] left, right, top, bot pixels on each side to be added + * \return fpixd, or NULL on error + * + * <pre> + * Notes: + * (1) This adds pixels on each side whose values have a normal + * derivative equal to the normal derivative at the boundary + * of fpixs. + * </pre> + */ +FPIX * +fpixAddSlopeBorder(FPIX *fpixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 i, j, w, h, fullw, fullh; +l_float32 val1, val2, del; +FPIX *fpixd; + + PROCNAME("fpixAddSlopeBorder"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + fpixd = fpixAddBorder(fpixs, left, right, top, bot); + fpixGetDimensions(fpixs, &w, &h); + + /* Left */ + for (i = top; i < top + h; i++) { + fpixGetPixel(fpixd, left, i, &val1); + fpixGetPixel(fpixd, left + 1, i, &val2); + del = val1 - val2; + for (j = 0; j < left; j++) + fpixSetPixel(fpixd, j, i, val1 + del * (left - j)); + } + + /* Right */ + fullw = left + w + right; + for (i = top; i < top + h; i++) { + fpixGetPixel(fpixd, left + w - 1, i, &val1); + fpixGetPixel(fpixd, left + w - 2, i, &val2); + del = val1 - val2; + for (j = left + w; j < fullw; j++) + fpixSetPixel(fpixd, j, i, val1 + del * (j - left - w + 1)); + } + + /* Top */ + for (j = 0; j < fullw; j++) { + fpixGetPixel(fpixd, j, top, &val1); + fpixGetPixel(fpixd, j, top + 1, &val2); + del = val1 - val2; + for (i = 0; i < top; i++) + fpixSetPixel(fpixd, j, i, val1 + del * (top - i)); + } + + /* Bottom */ + fullh = top + h + bot; + for (j = 0; j < fullw; j++) { + fpixGetPixel(fpixd, j, top + h - 1, &val1); + fpixGetPixel(fpixd, j, top + h - 2, &val2); + del = val1 - val2; + for (i = top + h; i < fullh; i++) + fpixSetPixel(fpixd, j, i, val1 + del * (i - top - h + 1)); + } + + return fpixd; +} + + +/*--------------------------------------------------------------------* + * Simple rasterop * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixRasterop() + * + * \param[in] fpixd dest fpix + * \param[in] dx x val of UL corner of dest rectangle + * \param[in] dy y val of UL corner of dest rectangle + * \param[in] dw width of dest rectangle + * \param[in] dh height of dest rectangle + * \param[in] fpixs src fpix + * \param[in] sx x val of UL corner of src rectangle + * \param[in] sy y val of UL corner of src rectangle + * \return 0 if OK; 1 on error. + * + * <pre> + * Notes: + * (1) This is similar in structure to pixRasterop(), except + * it only allows copying from the source into the destination. + * For that reason, no op code is necessary. Additionally, + * all pixels are 32 bit words (float values), which makes + * the copy very simple. + * (2) Clipping of both src and dest fpix are done automatically. + * (3) This allows in-place copying, without checking to see if + * the result is valid: use for in-place with caution! + * </pre> + */ +l_ok +fpixRasterop(FPIX *fpixd, + l_int32 dx, + l_int32 dy, + l_int32 dw, + l_int32 dh, + FPIX *fpixs, + l_int32 sx, + l_int32 sy) +{ +l_int32 fsw, fsh, fdw, fdh, dhangw, shangw, dhangh, shangh; +l_int32 i, j, wpls, wpld; +l_float32 *datas, *datad, *lines, *lined; + + PROCNAME("fpixRasterop"); + + if (!fpixs) + return ERROR_INT("fpixs not defined", procName, 1); + if (!fpixd) + return ERROR_INT("fpixd not defined", procName, 1); + + /* -------------------------------------------------------- * + * Clip to maximum rectangle with both src and dest * + * -------------------------------------------------------- */ + fpixGetDimensions(fpixs, &fsw, &fsh); + fpixGetDimensions(fpixd, &fdw, &fdh); + + /* First clip horizontally (sx, dx, dw) */ + if (dx < 0) { + sx -= dx; /* increase sx */ + dw += dx; /* reduce dw */ + dx = 0; + } + if (sx < 0) { + dx -= sx; /* increase dx */ + dw += sx; /* reduce dw */ + sx = 0; + } + dhangw = dx + dw - fdw; /* rect overhang of dest to right */ + if (dhangw > 0) + dw -= dhangw; /* reduce dw */ + shangw = sx + dw - fsw; /* rect overhang of src to right */ + if (shangw > 0) + dw -= shangw; /* reduce dw */ + + /* Then clip vertically (sy, dy, dh) */ + if (dy < 0) { + sy -= dy; /* increase sy */ + dh += dy; /* reduce dh */ + dy = 0; + } + if (sy < 0) { + dy -= sy; /* increase dy */ + dh += sy; /* reduce dh */ + sy = 0; + } + dhangh = dy + dh - fdh; /* rect overhang of dest below */ + if (dhangh > 0) + dh -= dhangh; /* reduce dh */ + shangh = sy + dh - fsh; /* rect overhang of src below */ + if (shangh > 0) + dh -= shangh; /* reduce dh */ + + /* if clipped entirely, quit */ + if ((dw <= 0) || (dh <= 0)) + return 0; + + /* -------------------------------------------------------- * + * Copy block of data * + * -------------------------------------------------------- */ + datas = fpixGetData(fpixs); + datad = fpixGetData(fpixd); + wpls = fpixGetWpl(fpixs); + wpld = fpixGetWpl(fpixd); + datas += sy * wpls + sx; /* at UL corner of block */ + datad += dy * wpld + dx; /* at UL corner of block */ + for (i = 0; i < dh; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < dw; j++) { + *lined = *lines; + lines++; + lined++; + } + } + + return 0; +} + + +/*--------------------------------------------------------------------* + * Rotation by multiples of 90 degrees * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixRotateOrth() + * + * \param[in] fpixs + * \param[in] quads 0-3; number of 90 degree cw rotations + * \return fpixd, or NULL on error + */ +FPIX * +fpixRotateOrth(FPIX *fpixs, + l_int32 quads) +{ + PROCNAME("fpixRotateOrth"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (quads < 0 || quads > 3) + return (FPIX *)ERROR_PTR("quads not in {0,1,2,3}", procName, NULL); + + if (quads == 0) + return fpixCopy(fpixs); + else if (quads == 1) + return fpixRotate90(fpixs, 1); + else if (quads == 2) + return fpixRotate180(NULL, fpixs); + else /* quads == 3 */ + return fpixRotate90(fpixs, -1); +} + + +/*! + * \brief fpixRotate180() + * + * \param[in] fpixd [optional] can be null, or equal to fpixs + * \param[in] fpixs + * \return fpixd, or NULL on error + * + * <pre> + * Notes: + * (1) This does a 180 rotation of the image about the center, + * which is equivalent to a left-right flip about a vertical + * line through the image center, followed by a top-bottom + * flip about a horizontal line through the image center. + * (2) There are 2 cases for input: + * (a) fpixd == null (creates a new fpixd) + * (b) fpixd == fpixs (in-place operation) + * (3) For clarity, use these two patterns: + * (a) fpixd = fpixRotate180(NULL, fpixs); + * (b) fpixRotate180(fpixs, fpixs); + * </pre> + */ +FPIX * +fpixRotate180(FPIX *fpixd, + FPIX *fpixs) +{ + PROCNAME("fpixRotate180"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + /* Prepare pixd for in-place operation */ + if (!fpixd) + fpixd = fpixCopy(fpixs); + + fpixFlipLR(fpixd, fpixd); + fpixFlipTB(fpixd, fpixd); + return fpixd; +} + + +/*! + * \brief fpixRotate90() + * + * \param[in] fpixs + * \param[in] direction 1 = clockwise; -1 = counter-clockwise + * \return fpixd, or NULL on error + * + * <pre> + * Notes: + * (1) This does a 90 degree rotation of the image about the center, + * either cw or ccw, returning a new pix. + * (2) The direction must be either 1 (cw) or -1 (ccw). + * </pre> + */ +FPIX * +fpixRotate90(FPIX *fpixs, + l_int32 direction) +{ +l_int32 i, j, wd, hd, wpls, wpld; +l_float32 *datas, *datad, *lines, *lined; +FPIX *fpixd; + + PROCNAME("fpixRotate90"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (direction != 1 && direction != -1) + return (FPIX *)ERROR_PTR("invalid direction", procName, NULL); + + fpixGetDimensions(fpixs, &hd, &wd); + if ((fpixd = fpixCreate(wd, hd)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + fpixCopyResolution(fpixd, fpixs); + + datas = fpixGetData(fpixs); + wpls = fpixGetWpl(fpixs); + datad = fpixGetData(fpixd); + wpld = fpixGetWpl(fpixd); + if (direction == 1) { /* clockwise */ + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas + (wd - 1) * wpls; + for (j = 0; j < wd; j++) { + lined[j] = lines[i]; + lines -= wpls; + } + } + } else { /* ccw */ + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas; + for (j = 0; j < wd; j++) { + lined[j] = lines[hd - 1 - i]; + lines += wpls; + } + } + } + + return fpixd; +} + + +/*! + * \brief pixFlipLR() + * + * \param[in] fpixd [optional] can be null, or equal to fpixs + * \param[in] fpixs + * \return fpixd, or NULL on error + * + * <pre> + * Notes: + * (1) This does a left-right flip of the image, which is + * equivalent to a rotation out of the plane about a + * vertical line through the image center. + * (2) There are 2 cases for input: + * (a) fpixd == null (creates a new fpixd) + * (b) fpixd == fpixs (in-place operation) + * (3) For clarity, use these two patterns: + * (a) fpixd = fpixFlipLR(NULL, fpixs); + * (b) fpixFlipLR(fpixs, fpixs); + * </pre> + */ +FPIX * +fpixFlipLR(FPIX *fpixd, + FPIX *fpixs) +{ +l_int32 i, j, w, h, wpl, bpl; +l_float32 *line, *data, *buffer; + + PROCNAME("fpixFlipLR"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + /* Prepare fpixd for in-place operation */ + if (!fpixd) + fpixd = fpixCopy(fpixs); + + fpixGetDimensions(fpixd, &w, &h); + data = fpixGetData(fpixd); + wpl = fpixGetWpl(fpixd); /* 4-byte words */ + bpl = 4 * wpl; + if ((buffer = (l_float32 *)LEPT_CALLOC(wpl, sizeof(l_float32))) == NULL) { + fpixDestroy(&fpixd); + return (FPIX *)ERROR_PTR("buffer not made", procName, NULL); + } + for (i = 0; i < h; i++) { + line = data + i * wpl; + memcpy(buffer, line, bpl); + for (j = 0; j < w; j++) + line[j] = buffer[w - 1 - j]; + } + LEPT_FREE(buffer); + return fpixd; +} + + +/*! + * \brief fpixFlipTB() + * + * \param[in] fpixd [optional] can be null, or equal to fpixs + * \param[in] fpixs + * \return fpixd, or NULL on error + * + * <pre> + * Notes: + * (1) This does a top-bottom flip of the image, which is + * equivalent to a rotation out of the plane about a + * horizontal line through the image center. + * (2) There are 2 cases for input: + * (a) fpixd == null (creates a new fpixd) + * (b) fpixd == fpixs (in-place operation) + * (3) For clarity, use these two patterns: + * (a) fpixd = fpixFlipTB(NULL, fpixs); + * (b) fpixFlipTB(fpixs, fpixs); + * </pre> + */ +FPIX * +fpixFlipTB(FPIX *fpixd, + FPIX *fpixs) +{ +l_int32 i, k, h, h2, wpl, bpl; +l_float32 *linet, *lineb, *data, *buffer; + + PROCNAME("fpixFlipTB"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + /* Prepare fpixd for in-place operation */ + if (!fpixd) + fpixd = fpixCopy(fpixs); + + data = fpixGetData(fpixd); + wpl = fpixGetWpl(fpixd); + fpixGetDimensions(fpixd, NULL, &h); + if ((buffer = (l_float32 *)LEPT_CALLOC(wpl, sizeof(l_float32))) == NULL) { + fpixDestroy(&fpixd); + return (FPIX *)ERROR_PTR("buffer not made", procName, NULL); + } + h2 = h / 2; + bpl = 4 * wpl; + for (i = 0, k = h - 1; i < h2; i++, k--) { + linet = data + i * wpl; + lineb = data + k * wpl; + memcpy(buffer, linet, bpl); + memcpy(linet, lineb, bpl); + memcpy(lineb, buffer, bpl); + } + LEPT_FREE(buffer); + return fpixd; +} + + +/*--------------------------------------------------------------------* + * Affine and projective interpolated transforms * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixAffinePta() + * + * \param[in] fpixs 8 bpp + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] border size of extension with constant normal derivative + * \param[in] inval value brought in; typ. 0 + * \return fpixd, or NULL on error + * + * <pre> + * Notes: + * (1) If %border > 0, all four sides are extended by that distance, + * and removed after the transformation is finished. Pixels + * that would be brought in to the trimmed result from outside + * the extended region are assigned %inval. The purpose of + * extending the image is to avoid such assignments. + * (2) On the other hand, you may want to give all pixels that + * are brought in from outside fpixs a specific value. In that + * case, set %border == 0. + * </pre> + */ +FPIX * +fpixAffinePta(FPIX *fpixs, + PTA *ptad, + PTA *ptas, + l_int32 border, + l_float32 inval) +{ +l_float32 *vc; +PTA *ptas2, *ptad2; +FPIX *fpixs2, *fpixd, *fpixd2; + + PROCNAME("fpixAffinePta"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (!ptas) + return (FPIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (FPIX *)ERROR_PTR("ptad not defined", procName, NULL); + + /* If a border is to be added, also translate the ptas */ + if (border > 0) { + ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); + ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); + fpixs2 = fpixAddSlopeBorder(fpixs, border, border, border, border); + } else { + ptas2 = ptaClone(ptas); + ptad2 = ptaClone(ptad); + fpixs2 = fpixClone(fpixs); + } + + /* Get backwards transform from dest to src, and apply it */ + getAffineXformCoeffs(ptad2, ptas2, &vc); + fpixd2 = fpixAffine(fpixs2, vc, inval); + fpixDestroy(&fpixs2); + ptaDestroy(&ptas2); + ptaDestroy(&ptad2); + LEPT_FREE(vc); + + if (border == 0) + return fpixd2; + + /* Remove the added border */ + fpixd = fpixRemoveBorder(fpixd2, border, border, border, border); + fpixDestroy(&fpixd2); + return fpixd; +} + + +/*! + * \brief fpixAffine() + * + * \param[in] fpixs 8 bpp + * \param[in] vc vector of 8 coefficients for projective transformation + * \param[in] inval value brought in; typ. 0 + * \return fpixd, or NULL on error + */ +FPIX * +fpixAffine(FPIX *fpixs, + l_float32 *vc, + l_float32 inval) +{ +l_int32 i, j, w, h, wpld; +l_float32 val; +l_float32 *datas, *datad, *lined; +l_float32 x, y; +FPIX *fpixd; + + PROCNAME("fpixAffine"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + fpixGetDimensions(fpixs, &w, &h); + if (!vc) + return (FPIX *)ERROR_PTR("vc not defined", procName, NULL); + + datas = fpixGetData(fpixs); + fpixd = fpixCreateTemplate(fpixs); + fpixSetAllArbitrary(fpixd, inval); + datad = fpixGetData(fpixd); + wpld = fpixGetWpl(fpixd); + + /* Iterate over destination pixels */ + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + /* Compute float src pixel location corresponding to (i,j) */ + affineXformPt(vc, j, i, &x, &y); + linearInterpolatePixelFloat(datas, w, h, x, y, inval, &val); + *(lined + j) = val; + } + } + + return fpixd; +} + + +/*! + * \brief fpixProjectivePta() + * + * \param[in] fpixs 8 bpp + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] border size of extension with constant normal derivative + * \param[in] inval value brought in; typ. 0 + * \return fpixd, or NULL on error + * + * <pre> + * Notes: + * (1) If %border > 0, all four sides are extended by that distance, + * and removed after the transformation is finished. Pixels + * that would be brought in to the trimmed result from outside + * the extended region are assigned %inval. The purpose of + * extending the image is to avoid such assignments. + * (2) On the other hand, you may want to give all pixels that + * are brought in from outside fpixs a specific value. In that + * case, set %border == 0. + * </pre> + */ +FPIX * +fpixProjectivePta(FPIX *fpixs, + PTA *ptad, + PTA *ptas, + l_int32 border, + l_float32 inval) +{ +l_float32 *vc; +PTA *ptas2, *ptad2; +FPIX *fpixs2, *fpixd, *fpixd2; + + PROCNAME("fpixProjectivePta"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (!ptas) + return (FPIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (FPIX *)ERROR_PTR("ptad not defined", procName, NULL); + + /* If a border is to be added, also translate the ptas */ + if (border > 0) { + ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); + ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); + fpixs2 = fpixAddSlopeBorder(fpixs, border, border, border, border); + } else { + ptas2 = ptaClone(ptas); + ptad2 = ptaClone(ptad); + fpixs2 = fpixClone(fpixs); + } + + /* Get backwards transform from dest to src, and apply it */ + getProjectiveXformCoeffs(ptad2, ptas2, &vc); + fpixd2 = fpixProjective(fpixs2, vc, inval); + fpixDestroy(&fpixs2); + ptaDestroy(&ptas2); + ptaDestroy(&ptad2); + LEPT_FREE(vc); + + if (border == 0) + return fpixd2; + + /* Remove the added border */ + fpixd = fpixRemoveBorder(fpixd2, border, border, border, border); + fpixDestroy(&fpixd2); + return fpixd; +} + + +/*! + * \brief fpixProjective() + * + * \param[in] fpixs 8 bpp + * \param[in] vc vector of 8 coefficients for projective transform + * \param[in] inval value brought in; typ. 0 + * \return fpixd, or NULL on error + */ +FPIX * +fpixProjective(FPIX *fpixs, + l_float32 *vc, + l_float32 inval) +{ +l_int32 i, j, w, h, wpld; +l_float32 val; +l_float32 *datas, *datad, *lined; +l_float32 x, y; +FPIX *fpixd; + + PROCNAME("fpixProjective"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + fpixGetDimensions(fpixs, &w, &h); + if (!vc) + return (FPIX *)ERROR_PTR("vc not defined", procName, NULL); + + datas = fpixGetData(fpixs); + fpixd = fpixCreateTemplate(fpixs); + fpixSetAllArbitrary(fpixd, inval); + datad = fpixGetData(fpixd); + wpld = fpixGetWpl(fpixd); + + /* Iterate over destination pixels */ + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + /* Compute float src pixel location corresponding to (i,j) */ + projectiveXformPt(vc, j, i, &x, &y); + linearInterpolatePixelFloat(datas, w, h, x, y, inval, &val); + *(lined + j) = val; + } + } + + return fpixd; +} + + +/*! + * \brief linearInterpolatePixelFloat() + * + * \param[in] datas ptr to beginning of float image data + * \param[in] w, h dimensions of image + * \param[in] x, y floating pt location for evaluation + * \param[in] inval float value brought in from the outside when the + * input x,y location is outside the image + * \param[out] pval interpolated float value + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This is a standard linear interpolation function. It is + * equivalent to area weighting on each component, and + * avoids "jaggies" when rendering sharp edges. + * </pre> + */ +l_ok +linearInterpolatePixelFloat(l_float32 *datas, + l_int32 w, + l_int32 h, + l_float32 x, + l_float32 y, + l_float32 inval, + l_float32 *pval) +{ +l_int32 xpm, ypm, xp, yp, xf, yf; +l_float32 v00, v01, v10, v11; +l_float32 *lines; + + PROCNAME("linearInterpolatePixelFloat"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = inval; + if (!datas) + return ERROR_INT("datas not defined", procName, 1); + + /* Skip if off the edge */ + if (x < 0.0 || y < 0.0 || x > w - 2.0 || y > h - 2.0) + return 0; + + xpm = (l_int32)(16.0 * x + 0.5); + ypm = (l_int32)(16.0 * y + 0.5); + xp = xpm >> 4; + yp = ypm >> 4; + xf = xpm & 0x0f; + yf = ypm & 0x0f; + +#if DEBUG + if (xf < 0 || yf < 0) + lept_stderr("xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf); +#endif /* DEBUG */ + + /* Interpolate by area weighting. */ + lines = datas + yp * w; + v00 = (16.0 - xf) * (16.0 - yf) * (*(lines + xp)); + v10 = xf * (16.0 - yf) * (*(lines + xp + 1)); + v01 = (16.0 - xf) * yf * (*(lines + w + xp)); + v11 = (l_float32)(xf) * yf * (*(lines + w + xp + 1)); + *pval = (v00 + v01 + v10 + v11) / 256.0; + return 0; +} + + +/*--------------------------------------------------------------------* + * Thresholding to 1 bpp Pix * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixThresholdToPix() + * + * \param[in] fpix + * \param[in] thresh + * \return pixd 1 bpp, or NULL on error + * + * <pre> + * Notes: + * (1) For all values of fpix that are <= thresh, sets the pixel + * in pixd to 1. + * </pre> + */ +PIX * +fpixThresholdToPix(FPIX *fpix, + l_float32 thresh) +{ +l_int32 i, j, w, h, wpls, wpld; +l_float32 *datas, *lines; +l_uint32 *datad, *lined; +PIX *pixd; + + PROCNAME("fpixThresholdToPix"); + + if (!fpix) + return (PIX *)ERROR_PTR("fpix not defined", procName, NULL); + + fpixGetDimensions(fpix, &w, &h); + datas = fpixGetData(fpix); + wpls = fpixGetWpl(fpix); + pixd = pixCreate(w, h, 1); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + if (lines[j] <= thresh) + SET_DATA_BIT(lined, j); + } + } + + return pixd; +} + + +/*--------------------------------------------------------------------* + * Generate function from components * + *--------------------------------------------------------------------*/ +/*! + * \brief pixComponentFunction() + * + * \param[in] pix 32 bpp rgb + * \param[in] rnum, gnum, bnum coefficients for numerator + * \param[in] rdenom, gdenom, bdenom coefficients for denominator + * \return fpixd, or NULL on error + * + * <pre> + * Notes: + * (1) This stores a function of the component values of each + * input pixel in %fpixd. + * (2) The function is a ratio of linear combinations of component values. + * There are two special cases for denominator coefficients: + * (a) The denominator is 1.0: input 0 for all denominator coefficients + * (b) Only one component is used in the denominator: input 1.0 + * for that denominator component and 0.0 for the other two. + * (3) If the denominator is 0, multiply by an arbitrary number that + * is much larger than 1. Choose 256 "arbitrarily". + * + * </pre> + */ +FPIX * +pixComponentFunction(PIX *pix, + l_float32 rnum, + l_float32 gnum, + l_float32 bnum, + l_float32 rdenom, + l_float32 gdenom, + l_float32 bdenom) +{ +l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, zerodenom, onedenom; +l_float32 fnum, fdenom; +l_uint32 *datas, *lines; +l_float32 *datad, *lined, *recip; +FPIX *fpixd; + + PROCNAME("pixComponentFunction"); + + if (!pix || pixGetDepth(pix) != 32) + return (FPIX *)ERROR_PTR("pix undefined or not 32 bpp", procName, NULL); + + pixGetDimensions(pix, &w, &h, NULL); + datas = pixGetData(pix); + wpls = pixGetWpl(pix); + fpixd = fpixCreate(w, h); + datad = fpixGetData(fpixd); + wpld = fpixGetWpl(fpixd); + zerodenom = (rdenom == 0.0 && gdenom == 0.0 && bdenom == 0.0) ? 1: 0; + onedenom = ((rdenom == 1.0 && gdenom == 0.0 && bdenom == 0.0) || + (rdenom == 0.0 && gdenom == 1.0 && bdenom == 0.0) || + (rdenom == 0.0 && gdenom == 0.0 && bdenom == 1.0)) ? 1 : 0; + recip = NULL; + if (onedenom) { + recip = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); + recip[0] = 256; /* arbitrary large number */ + for (i = 1; i < 256; i++) + recip[i] = 1.0 / (l_float32)i; + } + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (zerodenom) { + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + lined[j] = rnum * rval + gnum * gval + bnum * bval; + } + } else if (onedenom && rdenom == 1.0) { + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + lined[j] + = recip[rval] * (rnum * rval + gnum * gval + bnum * bval); + } + } else if (onedenom && gdenom == 1.0) { + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + lined[j] + = recip[gval] * (rnum * rval + gnum * gval + bnum * bval); + } + } else if (onedenom && bdenom == 1.0) { + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + lined[j] + = recip[bval] * (rnum * rval + gnum * gval + bnum * bval); + } + } else { /* general case */ + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + fnum = rnum * rval + gnum * gval + bnum * bval; + fdenom = rdenom * rval + gdenom * gval + bdenom * bval; + lined[j] = (fdenom == 0) ? 256.0 * fnum : fnum / fdenom; + } + } + } + + LEPT_FREE(recip); + return fpixd; +} |