| /*====================================================================* |
| - Copyright (C) 2001 Leptonica. All rights reserved. |
| - This software is distributed in the hope that it will be |
| - useful, but with NO WARRANTY OF ANY KIND. |
| - No author or distributor accepts responsibility to anyone for the |
| - consequences of using this software, or for whether it serves any |
| - particular purpose or works at all, unless he or she says so in |
| - writing. Everyone is granted permission to copy, modify and |
| - redistribute this source code, for commercial or non-commercial |
| - purposes, with the following restrictions: (1) the origin of this |
| - source code must not be misrepresented; (2) modified versions must |
| - be plainly marked as such; and (3) this notice may not be removed |
| - or altered from any source or modified source distribution. |
| *====================================================================*/ |
| |
| |
| /* |
| * pixarith.c |
| * |
| * 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() |
| * |
| * 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() |
| * |
| * Two-image min and max operations (8 and 16 bpp) |
| * PIX *pixMinOrMax() |
| * |
| * Scale pix for maximum dynamic range in 8 bpp image: |
| * PIX *pixMaxDynamicRange() |
| * |
| * 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. |
| */ |
| |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <math.h> |
| #include "allheaders.h" |
| |
| |
| /*-------------------------------------------------------------* |
| * One-image grayscale arithmetic operations * |
| *-------------------------------------------------------------*/ |
| /*! |
| * pixAddConstantGray() |
| * |
| * Input: pixs (8, 16 or 32 bpp) |
| * val (amount to add to each pixel) |
| * Return: 0 if OK, 1 on error |
| * |
| * 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. |
| */ |
| l_int32 |
| pixAddConstantGray(PIX *pixs, |
| l_int32 val) |
| { |
| l_int32 w, h, d, wpl; |
| l_uint32 *data; |
| |
| PROCNAME("pixAddConstantGray"); |
| |
| if (!pixs) |
| return ERROR_INT("pixs not defined", procName, 1); |
| pixGetDimensions(pixs, &w, &h, &d); |
| if (d != 8 && d != 16 && d != 32) |
| return ERROR_INT("pixs not 8, 16 or 32 bpp", procName, 1); |
| |
| data = pixGetData(pixs); |
| wpl = pixGetWpl(pixs); |
| addConstantGrayLow(data, w, h, d, wpl, val); |
| |
| return 0; |
| } |
| |
| |
| /*! |
| * pixMultConstantGray() |
| * |
| * Input: pixs (8, 16 or 32 bpp) |
| * val (>= 0.0; amount to multiply by each pixel) |
| * Return: 0 if OK, 1 on error |
| * |
| * 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. |
| */ |
| l_int32 |
| pixMultConstantGray(PIX *pixs, |
| l_float32 val) |
| { |
| l_int32 w, h, d, wpl; |
| l_uint32 *data; |
| |
| PROCNAME("pixMultConstantGray"); |
| |
| if (!pixs) |
| return ERROR_INT("pixs not defined", procName, 1); |
| pixGetDimensions(pixs, &w, &h, &d); |
| if (d != 8 && d != 16 && d != 32) |
| return ERROR_INT("pixs not 8, 16 or 32 bpp", procName, 1); |
| if (val < 0.0) |
| return ERROR_INT("val < 0.0", procName, 1); |
| |
| data = pixGetData(pixs); |
| wpl = pixGetWpl(pixs); |
| multConstantGrayLow(data, w, h, d, wpl, val); |
| |
| return 0; |
| } |
| |
| |
| /*-------------------------------------------------------------* |
| * Two-image grayscale arithmetic ops * |
| *-------------------------------------------------------------*/ |
| /*! |
| * pixAddGray() |
| * |
| * Input: pixd (<optional>; this can be null, equal to pixs1, or |
| * different from pixs1) |
| * pixs1 (can be == to pixd) |
| * pixs2 |
| * Return: pixd always |
| * |
| * 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. |
| */ |
| PIX * |
| pixAddGray(PIX *pixd, |
| PIX *pixs1, |
| PIX *pixs2) |
| { |
| l_int32 d, ws, hs, w, h, wpls, wpld; |
| l_uint32 *datas, *datad; |
| |
| PROCNAME("pixAddGray"); |
| |
| if (!pixs1) |
| return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); |
| if (!pixs2) |
| return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); |
| if (pixs2 == pixs1) |
| return (PIX *)ERROR_PTR("pixs2 and pixs1 must differ", procName, pixd); |
| if (pixs2 == pixd) |
| return (PIX *)ERROR_PTR("pixs2 and pixd must differ", procName, pixd); |
| d = pixGetDepth(pixs1); |
| if (d != 8 && d != 16 && d != 32) |
| return (PIX *)ERROR_PTR("pix are not 8, 16 or 32 bpp", procName, pixd); |
| if (pixGetDepth(pixs2) != d) |
| return (PIX *)ERROR_PTR("depths differ (pixs1, pixs2)", procName, pixd); |
| if (pixd && (pixGetDepth(pixd) != d)) |
| return (PIX *)ERROR_PTR("depths differ (pixs1, pixd)", procName, pixd); |
| |
| if (!pixSizesEqual(pixs1, pixs2)) |
| L_WARNING("pixs1 and pixs2 not equal in size", procName); |
| if (pixd && !pixSizesEqual(pixs1, pixd)) |
| L_WARNING("pixs1 and pixd not equal in size", procName); |
| |
| 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); |
| addGrayLow(datad, w, h, d, wpld, datas, wpls); |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixSubtractGray() |
| * |
| * Input: pixd (<optional>; this can be null, equal to pixs1, or |
| * different from pixs1) |
| * pixs1 (can be == to pixd) |
| * pixs2 |
| * Return: pixd always |
| * |
| * 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. |
| */ |
| PIX * |
| pixSubtractGray(PIX *pixd, |
| PIX *pixs1, |
| PIX *pixs2) |
| { |
| l_int32 w, h, ws, hs, d, wpls, wpld; |
| l_uint32 *datas, *datad; |
| |
| PROCNAME("pixSubtractGray"); |
| |
| if (!pixs1) |
| return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); |
| if (!pixs2) |
| return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); |
| if (pixs2 == pixs1) |
| return (PIX *)ERROR_PTR("pixs2 and pixs1 must differ", procName, pixd); |
| if (pixs2 == pixd) |
| return (PIX *)ERROR_PTR("pixs2 and pixd must differ", procName, pixd); |
| d = pixGetDepth(pixs1); |
| if (d != 8 && d != 16 && d != 32) |
| return (PIX *)ERROR_PTR("pix are not 8, 16 or 32 bpp", procName, pixd); |
| if (pixGetDepth(pixs2) != d) |
| return (PIX *)ERROR_PTR("depths differ (pixs1, pixs2)", procName, pixd); |
| if (pixd && (pixGetDepth(pixd) != d)) |
| return (PIX *)ERROR_PTR("depths differ (pixs1, pixd)", procName, pixd); |
| |
| if (!pixSizesEqual(pixs1, pixs2)) |
| L_WARNING("pixs1 and pixs2 not equal in size", procName); |
| if (pixd && !pixSizesEqual(pixs1, pixd)) |
| L_WARNING("pixs1 and pixd not equal in size", procName); |
| |
| 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); |
| subtractGrayLow(datad, w, h, d, wpld, datas, wpls); |
| |
| return pixd; |
| } |
| |
| |
| /*-------------------------------------------------------------* |
| * Grayscale threshold operation * |
| *-------------------------------------------------------------*/ |
| /*! |
| * pixThresholdToValue() |
| * |
| * Input: pixd (<optional>; if not null, must be equal to pixs) |
| * pixs (8, 16, 32 bpp) |
| * threshval |
| * setval |
| * Return: pixd always |
| * |
| * 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 |
| */ |
| PIX * |
| pixThresholdToValue(PIX *pixd, |
| PIX *pixs, |
| l_int32 threshval, |
| l_int32 setval) |
| { |
| l_int32 w, h, d, wpld; |
| l_uint32 *datad; |
| |
| PROCNAME("pixThresholdToValue"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); |
| d = pixGetDepth(pixs); |
| if (d != 8 && d != 16 && d != 32) |
| return (PIX *)ERROR_PTR("pixs not 8, 16 or 32 bpp", procName, pixd); |
| if (pixd && (pixs != pixd)) |
| return (PIX *)ERROR_PTR("pixd exists and is not pixs", procName, pixd); |
| if (threshval < 0 || setval < 0) |
| return (PIX *)ERROR_PTR("threshval & setval not < 0", procName, pixd); |
| if (d == 8 && setval > 255) |
| return (PIX *)ERROR_PTR("setval > 255 for 8 bpp", procName, pixd); |
| if (d == 16 && setval > 0xffff) |
| return (PIX *)ERROR_PTR("setval > 0xffff for 16 bpp", procName, pixd); |
| |
| if (!pixd) |
| pixd = pixCopy(NULL, pixs); |
| if (setval == threshval) { |
| L_WARNING("setval == threshval; no operation", procName); |
| return pixd; |
| } |
| |
| datad = pixGetData(pixd); |
| pixGetDimensions(pixd, &w, &h, NULL); |
| wpld = pixGetWpl(pixd); |
| |
| thresholdToValueLow(datad, w, h, d, wpld, threshval, setval); |
| return pixd; |
| } |
| |
| |
| |
| /*-------------------------------------------------------------* |
| * Image accumulator arithmetic operations * |
| *-------------------------------------------------------------*/ |
| /*! |
| * pixInitAccumulate() |
| * |
| * Input: w, h (of accumulate array) |
| * offset (initialize the 32 bpp to have this |
| * value; not more than 0x40000000) |
| * Return: pixd (32 bpp), or null on error |
| * |
| * Notes: |
| * (1) The offset must be >= 0. |
| * (2) The 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. |
| */ |
| PIX * |
| pixInitAccumulate(l_int32 w, |
| l_int32 h, |
| l_uint32 offset) |
| { |
| PIX *pixd; |
| |
| PROCNAME("pixInitAccumulate"); |
| |
| if ((pixd = pixCreate(w, h, 32)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| if (offset > 0x40000000) |
| offset = 0x40000000; |
| pixSetAllArbitrary(pixd, offset); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixFinalAccumulate() |
| * |
| * Input: pixs (32 bpp) |
| * offset (same as used for initialization) |
| * depth (8, 16 or 32 bpp, of destination) |
| * Return: pixd (8, 16 or 32 bpp), or null on error |
| * |
| * Notes: |
| * (1) The offset must be >= 0 and should not exceed 0x40000000. |
| * (2) The 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] |
| */ |
| PIX * |
| pixFinalAccumulate(PIX *pixs, |
| l_uint32 offset, |
| l_int32 depth) |
| { |
| l_int32 w, h, wpls, wpld; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixFinalAccumulate"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 32) |
| return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); |
| if (depth != 8 && depth != 16 && depth != 32) |
| return (PIX *)ERROR_PTR("dest depth not 8, 16, 32 bpp", procName, 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", procName, NULL); |
| pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */ |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| wpls = pixGetWpl(pixs); |
| wpld = pixGetWpl(pixd); |
| |
| finalAccumulateLow(datad, w, h, depth, wpld, datas, wpls, offset); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixFinalAccumulateThreshold() |
| * |
| * Input: pixs (32 bpp) |
| * offset (same as used for initialization) |
| * threshold (values less than this are set in the destination) |
| * Return: pixd (1 bpp), or null on error |
| * |
| * Notes: |
| * (1) The offset must be >= 0 and should not exceed 0x40000000. |
| * (2) The offset is subtracted from the src 32 bpp image |
| */ |
| PIX * |
| pixFinalAccumulateThreshold(PIX *pixs, |
| l_uint32 offset, |
| l_uint32 threshold) |
| { |
| l_int32 w, h, wpls, wpld; |
| l_uint32 *datas, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixFinalAccumulateThreshold"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 32) |
| return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); |
| if (offset > 0x40000000) |
| offset = 0x40000000; |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| if ((pixd = pixCreate(w, h, 1)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */ |
| datas = pixGetData(pixs); |
| datad = pixGetData(pixd); |
| wpls = pixGetWpl(pixs); |
| wpld = pixGetWpl(pixd); |
| |
| finalAccumulateThreshLow(datad, w, h, wpld, datas, wpls, offset, threshold); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixAccumulate() |
| * |
| * Input: pixd (32 bpp) |
| * pixs (1, 8, 16 or 32 bpp) |
| * op (L_ARITH_ADD or L_ARITH_SUBTRACT) |
| * Return: 0 if OK; 1 on error |
| * |
| * 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. |
| */ |
| l_int32 |
| pixAccumulate(PIX *pixd, |
| PIX *pixs, |
| l_int32 op) |
| { |
| l_int32 w, h, d, wd, hd, wpls, wpld; |
| l_uint32 *datas, *datad; |
| |
| PROCNAME("pixAccumulate"); |
| |
| if (!pixd || (pixGetDepth(pixd) != 32)) |
| return ERROR_INT("pixd not defined or not 32 bpp", procName, 1); |
| if (!pixs) |
| return ERROR_INT("pixs not defined", procName, 1); |
| d = pixGetDepth(pixs); |
| if (d != 1 && d != 8 && d != 16 && d != 32) |
| return ERROR_INT("pixs not 1, 8, 16 or 32 bpp", procName, 1); |
| if (op != L_ARITH_ADD && op != L_ARITH_SUBTRACT) |
| return ERROR_INT("op must be in {L_ARITH_ADD, L_ARITH_SUBTRACT}", |
| procName, 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); |
| |
| accumulateLow(datad, w, h, wpld, datas, d, wpls, op); |
| return 0; |
| } |
| |
| |
| /*! |
| * pixMultConstAccumulate() |
| * |
| * Input: pixs (32 bpp) |
| * factor |
| * offset (same as used for initialization) |
| * Return: 0 if OK; 1 on error |
| * |
| * Notes: |
| * (1) The offset must be >= 0 and should not exceed 0x40000000. |
| * (2) This multiplies each pixel, relative to offset, by the input factor |
| * (3) The result is returned with the offset back in place. |
| */ |
| l_int32 |
| pixMultConstAccumulate(PIX *pixs, |
| l_float32 factor, |
| l_uint32 offset) |
| { |
| l_int32 w, h, wpl; |
| l_uint32 *data; |
| |
| PROCNAME("pixMultConstAccumulate"); |
| |
| if (!pixs) |
| return ERROR_INT("pixs not defined", procName, 1); |
| if (pixGetDepth(pixs) != 32) |
| return ERROR_INT("pixs not 32 bpp", procName, 1); |
| if (offset > 0x40000000) |
| offset = 0x40000000; |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| data = pixGetData(pixs); |
| wpl = pixGetWpl(pixs); |
| |
| multConstAccumulateLow(data, w, h, wpl, factor, offset); |
| return 0; |
| } |
| |
| |
| /*-----------------------------------------------------------------------* |
| * Absolute value of difference * |
| *-----------------------------------------------------------------------*/ |
| /*! |
| * pixAbsDifference() |
| * |
| * Input: pixs1, pixs2 (both either 8 or 16 bpp gray, or 32 bpp RGB) |
| * Return: pixd, or null on error |
| * |
| * 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. |
| */ |
| PIX * |
| pixAbsDifference(PIX *pixs1, |
| PIX *pixs2) |
| { |
| l_int32 w, h, w2, h2, d, wpls, wpld; |
| l_uint32 *datas1, *datas2, *datad; |
| PIX *pixd; |
| |
| PROCNAME("pixAbsDifference"); |
| |
| if (!pixs1) |
| return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); |
| if (!pixs2) |
| return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); |
| d = pixGetDepth(pixs1); |
| if (d != pixGetDepth(pixs2)) |
| return (PIX *)ERROR_PTR("src1 and src2 depths unequal", procName, NULL); |
| if (d != 8 && d != 16 && d != 32) |
| return (PIX *)ERROR_PTR("depths not in {8, 16, 32}", procName, 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", procName, NULL); |
| pixCopyResolution(pixd, pixs1); |
| datas1 = pixGetData(pixs1); |
| datas2 = pixGetData(pixs2); |
| datad = pixGetData(pixd); |
| wpls = pixGetWpl(pixs1); |
| wpld = pixGetWpl(pixd); |
| |
| absDifferenceLow(datad, w, h, wpld, datas1, datas2, d, wpls); |
| |
| return pixd; |
| } |
| |
| |
| /*-----------------------------------------------------------------------* |
| * Two-image min and max operations (8 and 16 bpp) * |
| *-----------------------------------------------------------------------*/ |
| /*! |
| * pixMinOrMax() |
| * |
| * Input: pixd (<optional> destination: this can be null, |
| * equal to pixs1, or different from pixs1) |
| * pixs1 (can be == to pixd) |
| * pixs2 |
| * type (L_CHOOSE_MIN, L_CHOOSE_MAX) |
| * Return: pixd always |
| * |
| * Notes: |
| * (1) This gives the min or max of two images. |
| * (2) The depth can be 8 or 16 bpp. |
| * (3) There are 3 cases: |
| * - if pixd == null, Min(src1, src2) --> new pixd |
| * - if pixd == pixs1, Min(src1, src2) --> src1 (in-place) |
| * - if pixd != pixs1, Min(src1, src2) --> input pixd |
| */ |
| PIX * |
| pixMinOrMax(PIX *pixd, |
| PIX *pixs1, |
| PIX *pixs2, |
| l_int32 type) |
| { |
| l_int32 d, ws, hs, w, h, wpls, wpld, i, j; |
| l_int32 vals, vald, val; |
| l_uint32 *datas, *datad, *lines, *lined; |
| |
| PROCNAME("pixMinOrMax"); |
| |
| if (!pixs1) |
| return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); |
| if (!pixs2) |
| return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); |
| if (pixs1 == pixs2) |
| return (PIX *)ERROR_PTR("pixs1 and pixs2 must differ", procName, pixd); |
| if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX) |
| return (PIX *)ERROR_PTR("invalid type", procName, pixd); |
| d = pixGetDepth(pixs1); |
| if (pixGetDepth(pixs2) != d) |
| return (PIX *)ERROR_PTR("depths unequal", procName, pixd); |
| if (d != 8 && d != 16) |
| return (PIX *)ERROR_PTR("depth not 8 or 16 bpp", procName, 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) { |
| if (type == L_CHOOSE_MIN) { |
| for (j = 0; j < w; j++) { |
| vals = GET_DATA_BYTE(lines, j); |
| vald = GET_DATA_BYTE(lined, j); |
| val = L_MIN(vals, vald); |
| SET_DATA_BYTE(lined, j, val); |
| } |
| } else { /* type == L_CHOOSE_MAX */ |
| for (j = 0; j < w; j++) { |
| vals = GET_DATA_BYTE(lines, j); |
| vald = GET_DATA_BYTE(lined, j); |
| val = L_MAX(vals, vald); |
| SET_DATA_BYTE(lined, j, val); |
| } |
| } |
| } else { /* d == 16 */ |
| if (type == L_CHOOSE_MIN) { |
| for (j = 0; j < w; j++) { |
| vals = GET_DATA_TWO_BYTES(lines, j); |
| vald = GET_DATA_TWO_BYTES(lined, j); |
| val = L_MIN(vals, vald); |
| SET_DATA_TWO_BYTES(lined, j, val); |
| } |
| } else { /* type == L_CHOOSE_MAX */ |
| for (j = 0; j < w; j++) { |
| vals = GET_DATA_TWO_BYTES(lines, j); |
| vald = GET_DATA_TWO_BYTES(lined, j); |
| val = L_MAX(vals, vald); |
| SET_DATA_TWO_BYTES(lined, j, val); |
| } |
| } |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*-----------------------------------------------------------------------* |
| * Scale for maximum dynamic range in 8 bpp image * |
| *-----------------------------------------------------------------------*/ |
| /*! |
| * pixMaxDynamicRange() |
| * |
| * Input: pixs (4, 8, 16 or 32 bpp source) |
| * type (L_LINEAR_SCALE or L_LOG_SCALE) |
| * Return: pixd (8 bpp), or null on error |
| * |
| * Notes: |
| * (1) Scales pixel values to fit maximally within the dest 8 bpp pixd |
| * (2) Uses a LUT for log scaling |
| */ |
| PIX * |
| pixMaxDynamicRange(PIX *pixs, |
| l_int32 type) |
| { |
| l_uint8 dval; |
| l_int32 i, j, w, h, d, wpls, wpld, max, sval; |
| l_uint32 *datas, *datad; |
| l_uint32 word; |
| l_uint32 *lines, *lined; |
| l_float32 factor; |
| l_float32 *tab; |
| PIX *pixd; |
| |
| PROCNAME("pixMaxDynamicRange"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| d = pixGetDepth(pixs); |
| if (d != 4 && d != 8 && d != 16 && d != 32) |
| return (PIX *)ERROR_PTR("pixs not in {4,8,16,32} bpp", procName, NULL); |
| if (type != L_LINEAR_SCALE && type != L_LOG_SCALE) |
| return (PIX *)ERROR_PTR("invalid type", procName, NULL); |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| if ((pixd = pixCreate(w, h, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, 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 */ |
| max = L_MAX(max, word); |
| } |
| } |
| } |
| |
| /* Map to the full dynamic range of 8 bpp output */ |
| if (d == 4) { |
| if (type == L_LINEAR_SCALE) { |
| factor = 255. / (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. / 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); |
| } |
| } |
| FREE(tab); |
| } |
| } else if (d == 8) { |
| if (type == L_LINEAR_SCALE) { |
| factor = 255. / (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. / 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); |
| } |
| } |
| FREE(tab); |
| } |
| } else if (d == 16) { |
| if (type == L_LINEAR_SCALE) { |
| factor = 255. / (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. / 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); |
| } |
| } |
| FREE(tab); |
| } |
| } else { /* d == 32 */ |
| if (type == L_LINEAR_SCALE) { |
| factor = 255. / (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. / 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); |
| } |
| } |
| FREE(tab); |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*-----------------------------------------------------------------------* |
| * Log base2 lookup * |
| *-----------------------------------------------------------------------*/ |
| /* |
| * makeLogBase2Tab() |
| * |
| * Input: void |
| * Return: table (giving the log[base 2] of val) |
| */ |
| l_float32 * |
| makeLogBase2Tab(void) |
| { |
| l_int32 i; |
| l_float32 log2; |
| l_float32 *tab; |
| |
| PROCNAME("makeLogBase2Tab"); |
| |
| if ((tab = (l_float32 *)CALLOC(256, sizeof(l_float32))) == NULL) |
| return (l_float32 *)ERROR_PTR("tab not made", procName, 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; |
| } |
| |
| |
| /* |
| * getLogBase2() |
| * |
| * Input: val |
| * logtab (256-entry table of logs) |
| * Return: logdist, or 0 on error |
| */ |
| l_float32 |
| getLogBase2(l_int32 val, |
| l_float32 *logtab) |
| { |
| PROCNAME("getLogBase2"); |
| |
| if (!logtab) |
| return ERROR_INT("logtab not defined", procName, 0); |
| |
| if (val < 0x100) |
| return logtab[val]; |
| else if (val < 0x10000) |
| return 8.0 + logtab[val >> 8]; |
| else if (val < 0x1000000) |
| return 16.0 + logtab[val >> 16]; |
| else |
| return 24.0 + logtab[val >> 24]; |
| } |
| |