mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-29 06:12:18 -06:00
977 lines
32 KiB
C
977 lines
32 KiB
C
/*====================================================================*
|
|
- Copyright (C) 2001 Leptonica. All rights reserved.
|
|
- This software is distributed in the hope that it will be
|
|
- useful, but with NO WARRANTY OF ANY KIND.
|
|
- No author or distributor accepts responsibility to anyone for the
|
|
- consequences of using this software, or for whether it serves any
|
|
- particular purpose or works at all, unless he or she says so in
|
|
- writing. Everyone is granted permission to copy, modify and
|
|
- redistribute this source code, for commercial or non-commercial
|
|
- purposes, with the following restrictions: (1) the origin of this
|
|
- source code must not be misrepresented; (2) modified versions must
|
|
- be plainly marked as such; and (3) this notice may not be removed
|
|
- or altered from any source or modified source distribution.
|
|
*====================================================================*/
|
|
|
|
|
|
/*
|
|
* 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];
|
|
}
|
|
|