/*====================================================================* - 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. *====================================================================*/ /* * fpix2.c * * This file has these FPix utilities: * - interconversion with pix * - border functions * - simple rasterop (source --> dest) * * Interconversions between Pix and FPix * FPIX *pixConvertToFPix() * PIX *fpixConvertToPix() * PIX *fpixDisplayMaxDynamicRange() [useful for debugging] * * Border functions * FPIX *fpixAddBorder() * FPIX *fpixRemoveBorder() * FPIX *fpixAddMirroredBorder() * * Simple rasterop * l_int32 fpixRasterop() * * Arithmetic operations * FPIX *fpixLinearCombination() * l_int32 fpixAddMultConstant() */ #include <stdio.h> #include <stdlib.h> #include "allheaders.h" /*--------------------------------------------------------------------* * FPix <--> Pix conversions * *--------------------------------------------------------------------*/ /*! * pixConvertToFPix() * * Input: pix (1, 2, 4, 8, 16 or 32 bpp) * ncomps (number of components: 3 for RGB, 1 otherwise) * Return: fpix, or null on error * * 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. */ 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); 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 ((fpixd = fpixCreate(w, h)) == NULL) 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 if (d == 32) { for (j = 0; j < w; j++) { uval = GET_DATA_FOUR_BYTES(linet, j); lined[j] = (l_float32)uval; } } } pixDestroy(&pixt); return fpixd; } /*! * fpixConvertToPix() * * Input: fpixs * outdepth (0, 8, 16 or 32 bpp) * negvals (L_CLIP_TO_ZERO, L_TAKE_ABSVAL) * errorflag (1 to output error stats; 0 otherwise) * Return: pixd, or null on error * * 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. */ PIX * fpixConvertToPix(FPIX *fpixs, l_int32 outdepth, l_int32 negvals, l_int32 errorflag) { l_int32 w, h, i, j, wpls, wpld, maxval; l_uint32 vald; 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; i++) { lines = datas + i * wpls; for (j = 0; j < w; j++) { if (lines[j] > 65535.5) { outdepth = 32; break; } if (lines[j] > 255.5) outdepth = 16; } if (outdepth == 32) break; } } maxval = (1 << outdepth) - 1; /* 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_INT("Number of negative values: %d", procName, negs); if (overvals > 0) L_ERROR_INT("Number of too-large values: %d", 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; } /*! * fpixDisplayMaxDynamicRange() * * Input: 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; } /*--------------------------------------------------------------------* * Border functions * *--------------------------------------------------------------------*/ /*! * fpixAddBorder() * * Input: fpixs * left, right, top, bot (pixels on each side to be added) * Return: fpixd, or null on error * * Notes: * (1) Adds border of '0' 32-bit pixels */ 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(NULL, 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; } /*! * fpixRemoveBorder() * * Input: fpixs * 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(NULL, 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; } /*! * fpixAddMirroredBorder() * * Input: fpixs * left, right, top, bot (pixels on each side to be added) * Return: fpixd, or null on error * * Notes: * (1) See pixAddMirroredBorder() for situations of usage. */ 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; } /*--------------------------------------------------------------------* * Simple rasterop * *--------------------------------------------------------------------*/ /*! * fpixRasterop() * * Input: fpixd (dest fpix) * dx (x val of UL corner of dest rectangle) * dy (y val of UL corner of dest rectangle) * dw (width of dest rectangle) * dh (height of dest rectangle) * fpixs (src fpix) * sx (x val of UL corner of src rectangle) * sy (y val of UL corner of src rectangle) * Return: 0 if OK; 1 on error. * * Notes: * (1) This is similiar 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! */ l_int32 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; } /*--------------------------------------------------------------------* * Arithmetic operations * *--------------------------------------------------------------------*/ /*! * fpixLinearCombo() * * Input: fpixd (<optional>; this can be null, equal to fpixs1, or * different from fpixs1) * fpixs1 (can be == to fpixd) * fpixs2 * Return: pixd always * * Notes: * (1) Computes pixelwise linear combination: a * src1 + b * src2 * (2) Alignment is to UL corner. * (3) There are 3 cases. The result can go to a new dest, * in-place to fpixs1, or to an existing input dest: * * fpixd == null: (src1 + src2) --> new fpixd * * fpixd == fpixs1: (src1 + src2) --> src1 (in-place) * * fpixd != fpixs1: (src1 + src2) --> input fpixd * (4) fpixs2 must be different from both fpixd and fpixs1. */ 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 val; 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 (fpixs1 == fpixs2) return (FPIX *)ERROR_PTR("fpixs1 == fpixs2", procName, fpixd); if (fpixs2 == fpixd) return (FPIX *)ERROR_PTR("fpixs2 == fpixd", procName, fpixd); if (fpixs1 != fpixd) fpixd = fpixCopy(fpixd, 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; if (a == 1.0 && b == 1.0) { /* sum */ for (j = 0; j < w; j++) *(lined + j) += *(lines + j); } else if (a == 1.0 && b == -1.0) { /* diff */ for (j = 0; j < w; j++) *(lined + j) -= *(lines + j); } else if (a == -1.0 && b == 1.0) { /* diff */ for (j = 0; j < w; j++) { val = *(lined + j); *(lined + j) = -val + *(lines + j); } } else if (a == -1.0 && b == -1.0) { for (j = 0; j < w; j++) { val = *(lined + j); *(lined + j) = -val - *(lines + j); } } else { for (j = 0; j < w; j++) *(lined + j) = a * lined[j] + b * lines[j]; } } return fpixd; } /*! * fpixAddMultConstant() * * Input: fpix * addc (use 0.0 to skip the operation) * multc (use 1.0 to skip the operation) * Return: 0 if OK, 1 on error * * 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. */ l_int32 fpixAddMultConstant(FPIX *fpix, l_float32 addc, l_float32 multc) { l_int32 i, j, w, h, wpl; l_float32 val; 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++) { val = *(line + j); *(line + j) = multc * val + addc; } } } return 0; }