| /*====================================================================* |
| - 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. |
| *====================================================================*/ |
| |
| /* |
| * pixconv.c |
| * |
| * These functions convert between images of different types |
| * without scaling. |
| * |
| * Conversion from 8 bpp grayscale to 1, 2, 4 and 8 bpp |
| * PIX *pixThreshold8() |
| * |
| * Conversion from colormap to full color or grayscale |
| * PIX *pixRemoveColormap() |
| * |
| * Add colormap losslessly (8 to 8) |
| * l_int32 pixAddGrayColormap8() |
| * PIX *pixAddMinimalGrayColormap8() |
| * |
| * Conversion from RGB color to grayscale |
| * PIX *pixConvertRGBToLuminance() |
| * PIX *pixConvertRGBToGray() |
| * PIX *pixConvertRGBToGrayFast() |
| * PIX *pixConvertRGBToGrayMinMax() |
| * |
| * Conversion from grayscale to colormap |
| * PIX *pixConvertGrayToColormap() -- 2, 4, 8 bpp |
| * PIX *pixConvertGrayToColormap8() -- 8 bpp only |
| * |
| * Colorizing conversion from grayscale to color |
| * PIX *pixColorizeGray() -- 8 bpp or cmapped |
| * |
| * Conversion from RGB color to colormap |
| * PIX *pixConvertRGBToColormap() |
| * |
| * Quantization for relatively small number of colors in source |
| * l_int32 pixQuantizeIfFewColors() |
| * |
| * Conversion from 16 bpp to 8 bpp |
| * PIX *pixConvert16To8() |
| * |
| * Conversion from grayscale to false color |
| * PIX *pixConvertGrayToFalseColor() |
| * |
| * Unpacking conversion from 1 bpp to 2, 4, 8, 16 and 32 bpp |
| * PIX *pixUnpackBinary() |
| * PIX *pixConvert1To16() |
| * PIX *pixConvert1To32() |
| * |
| * Unpacking conversion from 1 bpp to 2 bpp |
| * PIX *pixConvert1To2Cmap() |
| * PIX *pixConvert1To2() |
| * |
| * Unpacking conversion from 1 bpp to 4 bpp |
| * PIX *pixConvert1To4Cmap() |
| * PIX *pixConvert1To4() |
| * |
| * Unpacking conversion from 1, 2 and 4 bpp to 8 bpp |
| * PIX *pixConvert1To8() |
| * PIX *pixConvert2To8() |
| * PIX *pixConvert4To8() |
| * |
| * Unpacking conversion from 8 bpp to 16 bpp |
| * PIX *pixConvert8To16() |
| * |
| * Top-level conversion to 1 bpp |
| * PIX *pixConvertTo1() |
| * PIX *pixConvertTo1BySampling() |
| * |
| * Top-level conversion to 8 bpp |
| * PIX *pixConvertTo8() |
| * PIX *pixConvertTo8BySampling() |
| * |
| * Top-level conversion to 16 bpp |
| * PIX *pixConvertTo16() |
| * |
| * Top-level conversion to 32 bpp (RGB) |
| * PIX *pixConvertTo32() *** |
| * PIX *pixConvertTo32BySampling() *** |
| * PIX *pixConvert8To32() *** |
| * |
| * Top-level conversion to 8 or 32 bpp, without colormap |
| * PIX *pixConvertTo8Or32 |
| * |
| * Lossless depth conversion (unpacking) |
| * PIX *pixConvertLossless() |
| * |
| * Conversion for printing in PostScript |
| * PIX *pixConvertForPSWrap() |
| * |
| * Colorspace conversion between RGB and HSV |
| * PIX *pixConvertRGBToHSV() |
| * PIX *pixConvertHSVToRGB() |
| * l_int32 convertRGBToHSV() |
| * l_int32 convertHSVToRGB() |
| * PIX *pixConvertRGBToHue() |
| * PIX *pixConvertRGBToSaturation() |
| * PIX *pixConvertRGBToValue() |
| * |
| * |
| * *** indicates implicit assumption about RGB component ordering |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <math.h> |
| #include "allheaders.h" |
| |
| /* These numbers are ad-hoc, but at least they add up |
| to 1. Unlike, for example, the weighting factor for |
| conversion of RGB to luminance, or more specifically |
| to Y in the YUV colorspace. Those numbers come |
| from the International Telecommunications Union, via ITU-R |
| (and formerly ITU CCIR 601). */ |
| static const l_float32 L_RED_WEIGHT = 0.3; |
| static const l_float32 L_GREEN_WEIGHT = 0.5; |
| static const l_float32 L_BLUE_WEIGHT = 0.2; |
| |
| |
| #ifndef NO_CONSOLE_IO |
| #define DEBUG_CONVERT_TO_COLORMAP 0 |
| #define DEBUG_UNROLLING 0 |
| #endif /* ~NO_CONSOLE_IO */ |
| |
| |
| /*-------------------------------------------------------------* |
| * Conversion from 8 bpp grayscale to 1, 2 4 and 8 bpp * |
| *-------------------------------------------------------------*/ |
| /*! |
| * pixThreshold8() |
| * |
| * Input: pix (8 bpp grayscale) |
| * d (destination depth: 1, 2, 4 or 8) |
| * nlevels (number of levels to be used for colormap) |
| * cmapflag (1 if makes colormap; 0 otherwise) |
| * Return: pixd (thresholded with standard dest thresholds), |
| * or null on error |
| * |
| * Notes: |
| * (1) This uses, by default, equally spaced "target" values |
| * that depend on the number of levels, with thresholds |
| * halfway between. For N levels, with separation (N-1)/255, |
| * there are N-1 fixed thresholds. |
| * (2) For 1 bpp destination, the number of levels can only be 2 |
| * and if a cmap is made, black is (0,0,0) and white |
| * is (255,255,255), which is opposite to the convention |
| * without a colormap. |
| * (3) For 1, 2 and 4 bpp, the nlevels arg is used if a colormap |
| * is made; otherwise, we take the most significant bits |
| * from the src that will fit in the dest. |
| * (4) For 8 bpp, the input pixs is quantized to nlevels. The |
| * dest quantized with that mapping, either through a colormap |
| * table or directly with 8 bit values. |
| * (5) Typically you should not use make a colormap for 1 bpp dest. |
| * (6) This is not dithering. Each pixel is treated independently. |
| */ |
| PIX * |
| pixThreshold8(PIX *pixs, |
| l_int32 d, |
| l_int32 nlevels, |
| l_int32 cmapflag) |
| { |
| PIX *pixd; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixThreshold8"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 8) |
| return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); |
| if (cmapflag && nlevels < 2) |
| return (PIX *)ERROR_PTR("nlevels must be at least 2", procName, NULL); |
| |
| switch (d) { |
| case 1: |
| pixd = pixThresholdToBinary(pixs, 128); |
| if (cmapflag) { |
| cmap = pixcmapCreateLinear(1, 2); |
| pixSetColormap(pixd, cmap); |
| } |
| break; |
| case 2: |
| pixd = pixThresholdTo2bpp(pixs, nlevels, cmapflag); |
| break; |
| case 4: |
| pixd = pixThresholdTo4bpp(pixs, nlevels, cmapflag); |
| break; |
| case 8: |
| pixd = pixThresholdOn8bpp(pixs, nlevels, cmapflag); |
| break; |
| default: |
| return (PIX *)ERROR_PTR("d must be in {1,2,4,8}", procName, NULL); |
| } |
| |
| if (!pixd) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| return pixd; |
| } |
| |
| |
| /*-------------------------------------------------------------* |
| * Conversion from colormapped pix * |
| *-------------------------------------------------------------*/ |
| /*! |
| * pixRemoveColormap() |
| * |
| * Input: pixs (see restrictions below) |
| * type (REMOVE_CMAP_TO_BINARY, |
| * REMOVE_CMAP_TO_GRAYSCALE, |
| * REMOVE_CMAP_TO_FULL_COLOR, |
| * REMOVE_CMAP_BASED_ON_SRC) |
| * Return: new pix, or null on error |
| * |
| * Notes: |
| * (1) If there is no colormap, a clone is returned. |
| * (2) Otherwise, the input pixs is restricted to 1, 2, 4 or 8 bpp. |
| * (3) Use REMOVE_CMAP_TO_BINARY only on 1 bpp pix. |
| * (4) For grayscale conversion from RGB, use a weighted average |
| * of RGB values, and always return an 8 bpp pix, regardless |
| * of whether the input pixs depth is 2, 4 or 8 bpp. |
| */ |
| PIX * |
| pixRemoveColormap(PIX *pixs, |
| l_int32 type) |
| { |
| l_int32 sval, rval, gval, bval; |
| l_int32 i, j, k, w, h, d, wpls, wpld, ncolors, count; |
| l_int32 colorfound; |
| l_int32 *rmap, *gmap, *bmap, *graymap; |
| l_uint32 *datas, *lines, *datad, *lined, *lut; |
| l_uint32 sword, dword; |
| PIXCMAP *cmap; |
| PIX *pixd; |
| |
| PROCNAME("pixRemoveColormap"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if ((cmap = pixGetColormap(pixs)) == NULL) |
| return pixClone(pixs); |
| |
| if (type != REMOVE_CMAP_TO_BINARY && |
| type != REMOVE_CMAP_TO_GRAYSCALE && |
| type != REMOVE_CMAP_TO_FULL_COLOR && |
| type != REMOVE_CMAP_BASED_ON_SRC) { |
| L_WARNING("Invalid type; converting based on src", procName); |
| type = REMOVE_CMAP_BASED_ON_SRC; |
| } |
| |
| pixGetDimensions(pixs, &w, &h, &d); |
| if (d != 1 && d != 2 && d != 4 && d != 8) |
| return (PIX *)ERROR_PTR("pixs must be {1,2,4,8} bpp", procName, NULL); |
| |
| if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap)) |
| return (PIX *)ERROR_PTR("colormap arrays not made", procName, NULL); |
| |
| if (d != 1 && type == REMOVE_CMAP_TO_BINARY) { |
| L_WARNING("not 1 bpp; can't remove cmap to binary", procName); |
| type = REMOVE_CMAP_BASED_ON_SRC; |
| } |
| |
| if (type == REMOVE_CMAP_BASED_ON_SRC) { |
| /* select output type depending on colormap */ |
| pixcmapHasColor(cmap, &colorfound); |
| if (!colorfound) { |
| if (d == 1) |
| type = REMOVE_CMAP_TO_BINARY; |
| else |
| type = REMOVE_CMAP_TO_GRAYSCALE; |
| } |
| else |
| type = REMOVE_CMAP_TO_FULL_COLOR; |
| } |
| |
| ncolors = pixcmapGetCount(cmap); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| if (type == REMOVE_CMAP_TO_BINARY) { |
| if ((pixd = pixCopy(NULL, pixs)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixcmapGetColor(cmap, 0, &rval, &gval, &bval); |
| if (rval == 0) /* photometrically inverted from standard */ |
| pixInvert(pixd, pixd); |
| pixDestroyColormap(pixd); |
| } |
| else if (type == REMOVE_CMAP_TO_GRAYSCALE) { |
| if ((pixd = pixCreate(w, h, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| if ((graymap = (l_int32 *)CALLOC(ncolors, sizeof(l_int32))) == NULL) |
| return (PIX *)ERROR_PTR("calloc fail for graymap", procName, NULL); |
| for (i = 0; i < pixcmapGetCount(cmap); i++) { |
| graymap[i] = (rmap[i] + 2 * gmap[i] + bmap[i]) / 4; |
| } |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + i * wpld; |
| switch (d) /* depth test above; no default permitted */ |
| { |
| case 8: |
| /* Unrolled 4x */ |
| for (j = 0, count = 0; j + 3 < w; j += 4, count++) { |
| sword = lines[count]; |
| dword = (graymap[(sword >> 24) & 0xff] << 24) | |
| (graymap[(sword >> 16) & 0xff] << 16) | |
| (graymap[(sword >> 8) & 0xff] << 8) | |
| graymap[sword & 0xff]; |
| lined[count] = dword; |
| } |
| /* Cleanup partial word */ |
| for (; j < w; j++) { |
| sval = GET_DATA_BYTE(lines, j); |
| gval = graymap[sval]; |
| SET_DATA_BYTE(lined, j, gval); |
| } |
| #if DEBUG_UNROLLING |
| #define CHECK_VALUE(a, b, c) if (GET_DATA_BYTE(a, b) != c) { \ |
| fprintf(stderr, "Error: mismatch at %d, %d vs %d\n", \ |
| j, GET_DATA_BYTE(a, b), c); } |
| for (j = 0; j < w; j++) { |
| sval = GET_DATA_BYTE(lines, j); |
| gval = graymap[sval]; |
| CHECK_VALUE(lined, j, gval); |
| } |
| #endif |
| break; |
| case 4: |
| /* Unrolled 8x */ |
| for (j = 0, count = 0; j + 7 < w; j += 8, count++) { |
| sword = lines[count]; |
| dword = (graymap[(sword >> 28) & 0xf] << 24) | |
| (graymap[(sword >> 24) & 0xf] << 16) | |
| (graymap[(sword >> 20) & 0xf] << 8) | |
| graymap[(sword >> 16) & 0xf]; |
| lined[2 * count] = dword; |
| dword = (graymap[(sword >> 12) & 0xf] << 24) | |
| (graymap[(sword >> 8) & 0xf] << 16) | |
| (graymap[(sword >> 4) & 0xf] << 8) | |
| graymap[sword & 0xf]; |
| lined[2 * count + 1] = dword; |
| } |
| /* Cleanup partial word */ |
| for (; j < w; j++) { |
| sval = GET_DATA_QBIT(lines, j); |
| gval = graymap[sval]; |
| SET_DATA_BYTE(lined, j, gval); |
| } |
| #if DEBUG_UNROLLING |
| for (j = 0; j < w; j++) { |
| sval = GET_DATA_QBIT(lines, j); |
| gval = graymap[sval]; |
| CHECK_VALUE(lined, j, gval); |
| } |
| #endif |
| break; |
| case 2: |
| /* Unrolled 16x */ |
| for (j = 0, count = 0; j + 15 < w; j += 16, count++) { |
| sword = lines[count]; |
| dword = (graymap[(sword >> 30) & 0x3] << 24) | |
| (graymap[(sword >> 28) & 0x3] << 16) | |
| (graymap[(sword >> 26) & 0x3] << 8) | |
| graymap[(sword >> 24) & 0x3]; |
| lined[4 * count] = dword; |
| dword = (graymap[(sword >> 22) & 0x3] << 24) | |
| (graymap[(sword >> 20) & 0x3] << 16) | |
| (graymap[(sword >> 18) & 0x3] << 8) | |
| graymap[(sword >> 16) & 0x3]; |
| lined[4 * count + 1] = dword; |
| dword = (graymap[(sword >> 14) & 0x3] << 24) | |
| (graymap[(sword >> 12) & 0x3] << 16) | |
| (graymap[(sword >> 10) & 0x3] << 8) | |
| graymap[(sword >> 8) & 0x3]; |
| lined[4 * count + 2] = dword; |
| dword = (graymap[(sword >> 6) & 0x3] << 24) | |
| (graymap[(sword >> 4) & 0x3] << 16) | |
| (graymap[(sword >> 2) & 0x3] << 8) | |
| graymap[sword & 0x3]; |
| lined[4 * count + 3] = dword; |
| } |
| /* Cleanup partial word */ |
| for (; j < w; j++) { |
| sval = GET_DATA_DIBIT(lines, j); |
| gval = graymap[sval]; |
| SET_DATA_BYTE(lined, j, gval); |
| } |
| #if DEBUG_UNROLLING |
| for (j = 0; j < w; j++) { |
| sval = GET_DATA_DIBIT(lines, j); |
| gval = graymap[sval]; |
| CHECK_VALUE(lined, j, gval); |
| } |
| #endif |
| break; |
| case 1: |
| /* Unrolled 8x */ |
| for (j = 0, count = 0; j + 31 < w; j += 32, count++) { |
| sword = lines[count]; |
| for (k = 0; k < 4; k++) { |
| /* The top byte is always the relevant one */ |
| dword = (graymap[(sword >> 31) & 0x1] << 24) | |
| (graymap[(sword >> 30) & 0x1] << 16) | |
| (graymap[(sword >> 29) & 0x1] << 8) | |
| graymap[(sword >> 28) & 0x1]; |
| lined[8 * count + 2 * k] = dword; |
| dword = (graymap[(sword >> 27) & 0x1] << 24) | |
| (graymap[(sword >> 26) & 0x1] << 16) | |
| (graymap[(sword >> 25) & 0x1] << 8) | |
| graymap[(sword >> 24) & 0x1]; |
| lined[8 * count + 2 * k + 1] = dword; |
| sword <<= 8; /* Move up the next byte */ |
| } |
| } |
| /* Cleanup partial word */ |
| for (; j < w; j++) { |
| sval = GET_DATA_BIT(lines, j); |
| gval = graymap[sval]; |
| SET_DATA_BYTE(lined, j, gval); |
| } |
| #if DEBUG_UNROLLING |
| for (j = 0; j < w; j++) { |
| sval = GET_DATA_BIT(lines, j); |
| gval = graymap[sval]; |
| CHECK_VALUE(lined, j, gval); |
| } |
| #undef CHECK_VALUE |
| #endif |
| break; |
| default: |
| return NULL; |
| } |
| } |
| if (graymap) |
| FREE(graymap); |
| } |
| else { /* type == REMOVE_CMAP_TO_FULL_COLOR */ |
| if ((pixd = pixCreate(w, h, 32)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| if ((lut = (l_uint32 *)CALLOC(ncolors, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("calloc fail for lut", procName, NULL); |
| for (i = 0; i < ncolors; i++) |
| composeRGBPixel(rmap[i], gmap[i], bmap[i], lut + i); |
| |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < w; j++) { |
| if (d == 8) |
| sval = GET_DATA_BYTE(lines, j); |
| else if (d == 4) |
| sval = GET_DATA_QBIT(lines, j); |
| else if (d == 2) |
| sval = GET_DATA_DIBIT(lines, j); |
| else if (d == 1) |
| sval = GET_DATA_BIT(lines, j); |
| else |
| return NULL; |
| if (sval >= ncolors) |
| L_WARNING("pixel value out of bounds", procName); |
| else |
| lined[j] = lut[sval]; |
| } |
| } |
| FREE(lut); |
| } |
| |
| FREE(rmap); |
| FREE(gmap); |
| FREE(bmap); |
| return pixd; |
| } |
| |
| |
| /*-------------------------------------------------------------* |
| * Add colormap losslessly (8 to 8) * |
| *-------------------------------------------------------------*/ |
| /*! |
| * pixAddGrayColormap8() |
| * |
| * Input: pixs (8 bpp) |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) If pixs has a colormap, this is a no-op. |
| */ |
| l_int32 |
| pixAddGrayColormap8(PIX *pixs) |
| { |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixAddGrayColormap8"); |
| |
| if (!pixs || pixGetDepth(pixs) != 8) |
| return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); |
| if (pixGetColormap(pixs)) |
| return 0; |
| |
| cmap = pixcmapCreateLinear(8, 256); |
| pixSetColormap(pixs, cmap); |
| return 0; |
| } |
| |
| |
| /*! |
| * pixAddMinimalGrayColormap8() |
| * |
| * Input: pixs (8 bpp) |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) This generates a colormapped version of the input image |
| * that has the same number of colormap entries as the |
| * input image has unique gray levels. |
| */ |
| PIX * |
| pixAddMinimalGrayColormap8(PIX *pixs) |
| { |
| l_int32 ncolors, w, h, i, j, wplt, wpld, index, val; |
| l_int32 *inta, *revmap; |
| l_uint32 *datat, *datad, *linet, *lined; |
| PIX *pixt, *pixd; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixAddMinimalGrayColormap8"); |
| |
| if (!pixs || pixGetDepth(pixs) != 8) |
| return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); |
| |
| /* Eliminate the easy cases */ |
| pixNumColors(pixs, 1, &ncolors); |
| cmap = pixGetColormap(pixs); |
| if (cmap) { |
| if (pixcmapGetCount(cmap) == ncolors) /* irreducible */ |
| return pixCopy(NULL, pixs); |
| else |
| pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
| } |
| else { |
| if (ncolors == 256) { |
| pixt = pixCopy(NULL, pixs); |
| pixAddGrayColormap8(pixt); |
| return pixt; |
| } |
| pixt = pixClone(pixs); |
| } |
| |
| /* Find the gray levels and make a reverse map */ |
| pixGetDimensions(pixt, &w, &h, NULL); |
| datat = pixGetData(pixt); |
| wplt = pixGetWpl(pixt); |
| inta = (l_int32 *)CALLOC(256, sizeof(l_int32)); |
| for (i = 0; i < h; i++) { |
| linet = datat + i * wplt; |
| for (j = 0; j < w; j++) { |
| val = GET_DATA_BYTE(linet, j); |
| inta[val] = 1; |
| } |
| } |
| cmap = pixcmapCreate(8); |
| revmap = (l_int32 *)CALLOC(256, sizeof(l_int32)); |
| for (i = 0, index = 0; i < 256; i++) { |
| if (inta[i]) { |
| pixcmapAddColor(cmap, i, i, i); |
| revmap[i] = index++; |
| } |
| } |
| |
| /* Set all pixels in pixd to the colormap index */ |
| pixd = pixCreateTemplate(pixt); |
| pixSetColormap(pixd, cmap); |
| pixCopyResolution(pixd, pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| for (i = 0; i < h; i++) { |
| linet = datat + i * wplt; |
| lined = datad + i * wpld; |
| for (j = 0; j < w; j++) { |
| val = GET_DATA_BYTE(linet, j); |
| SET_DATA_BYTE(lined, j, revmap[val]); |
| } |
| } |
| |
| pixDestroy(&pixt); |
| FREE(inta); |
| FREE(revmap); |
| return pixd; |
| } |
| |
| |
| /*-------------------------------------------------------------* |
| * Conversion from RGB color to grayscale * |
| *-------------------------------------------------------------*/ |
| /*! |
| * pixConvertRGBToLuminance() |
| * |
| * Input: pix (32 bpp RGB) |
| * Return: 8 bpp pix, or null on error |
| * |
| * Notes: |
| * (1) Use a standard luminance conversion. |
| */ |
| PIX * |
| pixConvertRGBToLuminance(PIX *pixs) |
| { |
| return pixConvertRGBToGray(pixs, 0.0, 0.0, 0.0); |
| } |
| |
| |
| /*! |
| * pixConvertRGBToGray() |
| * |
| * Input: pix (32 bpp RGB) |
| * rwt, gwt, bwt (non-negative; these should add to 1.0, |
| * or use 0.0 for default) |
| * Return: 8 bpp pix, or null on error |
| * |
| * Notes: |
| * (1) Use a weighted average of the RGB values. |
| */ |
| PIX * |
| pixConvertRGBToGray(PIX *pixs, |
| l_float32 rwt, |
| l_float32 gwt, |
| l_float32 bwt) |
| { |
| l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, val; |
| l_uint32 *datas, *lines, *datad, *lined; |
| l_float32 sum; |
| PIX *pixd; |
| |
| PROCNAME("pixConvertRGBToGray"); |
| |
| 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 (rwt < 0.0 || gwt < 0.0 || bwt < 0.0) |
| return (PIX *)ERROR_PTR("weights not all >= 0.0", procName, NULL); |
| |
| /* Make sure the sum of weights is 1.0; otherwise, you can get |
| * overflow in the gray value. */ |
| if (rwt == 0.0 && gwt == 0.0 && bwt == 0.0) { |
| rwt = L_RED_WEIGHT; |
| gwt = L_GREEN_WEIGHT; |
| bwt = L_BLUE_WEIGHT; |
| } |
| sum = rwt + gwt + bwt; |
| if (L_ABS(sum - 1.0) > 0.0001) { /* maintain ratios with sum == 1.0 */ |
| L_WARNING("weights don't sum to 1; maintaining ratios", procName); |
| rwt = rwt / sum; |
| gwt = gwt / sum; |
| bwt = bwt / sum; |
| } |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| if ((pixd = pixCreate(w, h, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| 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++) { |
| extractRGBValues(lines[j], &rval, &gval, &bval); |
| val = (l_int32)(rwt * rval + gwt * gval + bwt * bval + 0.5); |
| SET_DATA_BYTE(lined, j, val); |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvertRGBToGrayFast() |
| * |
| * Input: pix (32 bpp RGB) |
| * Return: 8 bpp pix, or null on error |
| * |
| * Notes: |
| * (1) This function should be used if speed of conversion |
| * is paramount, and the green channel can be used as |
| * a fair representative of the RGB intensity. It is |
| * several times faster than pixConvertRGBToGray(). |
| * (2) To combine RGB to gray conversion with subsampling, |
| * use pixScaleRGBToGrayFast() instead. |
| */ |
| PIX * |
| pixConvertRGBToGrayFast(PIX *pixs) |
| { |
| l_int32 i, j, w, h, wpls, wpld, val; |
| l_uint32 *datas, *lines, *datad, *lined; |
| PIX *pixd; |
| |
| PROCNAME("pixConvertRGBToGrayFast"); |
| |
| 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); |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| if ((pixd = pixCreate(w, h, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| 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++, lines++) { |
| val = ((*lines) >> L_GREEN_SHIFT) & 0xff; |
| SET_DATA_BYTE(lined, j, val); |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvertRGBToGrayMinMax() |
| * |
| * Input: pix (32 bpp RGB) |
| * type (L_CHOOSE_MIN or L_CHOOSE_MAX) |
| * Return: 8 bpp pix, or null on error |
| * |
| * Notes: |
| * (1) @type chooses among the 3 color components for each pixel |
| * (2) This is useful when looking for the maximum deviation |
| * of a component from either 0 or 255. For finding the |
| * deviation of a single component, it is more sensitive |
| * than using a weighted average. |
| */ |
| PIX * |
| pixConvertRGBToGrayMinMax(PIX *pixs, |
| l_int32 type) |
| { |
| l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, val; |
| l_uint32 *datas, *lines, *datad, *lined; |
| PIX *pixd; |
| |
| PROCNAME("pixConvertRGBToGrayMinMax"); |
| |
| 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 (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX) |
| return (PIX *)ERROR_PTR("invalid type", procName, NULL); |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| if ((pixd = pixCreate(w, h, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| 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++) { |
| extractRGBValues(lines[j], &rval, &gval, &bval); |
| if (type == L_CHOOSE_MIN) { |
| val = L_MIN(rval, gval); |
| val = L_MIN(val, bval); |
| } |
| else { /* type == L_CHOOSE_MAX */ |
| val = L_MAX(rval, gval); |
| val = L_MAX(val, bval); |
| } |
| SET_DATA_BYTE(lined, j, val); |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Conversion from grayscale to colormap * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvertGrayToColormap() |
| * |
| * Input: pixs (2, 4 or 8 bpp grayscale) |
| * Return: pixd (2, 4 or 8 bpp with colormap), or null on error |
| * |
| * Notes: |
| * (1) This is a simple interface for adding a colormap to a |
| * 2, 4 or 8 bpp grayscale image without causing any |
| * quantization. There is some similarity to operations |
| * in grayquant.c, such as pixThresholdOn8bpp(), where |
| * the emphasis is on quantization with an arbitrary number |
| * of levels, and a colormap is an option. |
| * (2) Returns a copy if pixs already has a colormap. |
| * (3) For 8 bpp src, this is a lossless transformation. |
| * (4) For 2 and 4 bpp src, this generates a colormap that |
| * assumes full coverage of the gray space, with equally spaced |
| * levels: 4 levels for d = 2 and 16 levels for d = 4. |
| * (5) In all cases, the depth of the dest is the same as the src. |
| */ |
| PIX * |
| pixConvertGrayToColormap(PIX *pixs) |
| { |
| l_int32 d; |
| PIX *pixd; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixConvertGrayToColormap"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| d = pixGetDepth(pixs); |
| if (d != 2 && d != 4 && d != 8) |
| return (PIX *)ERROR_PTR("pixs not 2, 4 or 8 bpp", procName, NULL); |
| |
| if (pixGetColormap(pixs)) { |
| L_WARNING("pixs already has a colormap", procName); |
| return pixCopy(NULL, pixs); |
| } |
| |
| if (d == 8) /* lossless conversion */ |
| return pixConvertGrayToColormap8(pixs, 2); |
| |
| /* Build a cmap with equally spaced target values over the |
| * full 8 bpp range. */ |
| pixd = pixCopy(NULL, pixs); |
| cmap = pixcmapCreateLinear(d, 1 << d); |
| pixSetColormap(pixd, cmap); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvertGrayToColormap8() |
| * |
| * Input: pixs (8 bpp grayscale) |
| * mindepth (of pixd; valid values are 2, 4 and 8) |
| * Return: pixd (2, 4 or 8 bpp with colormap), or null on error |
| * |
| * Notes: |
| * (1) Returns a copy if pixs already has a colormap. |
| * (2) This is a lossless transformation; there is no quantization. |
| * We compute the number of different gray values in pixs, |
| * and construct a colormap that has exactly these values. |
| * (3) 'mindepth' is the minimum depth of pixd. If mindepth == 8, |
| * pixd will always be 8 bpp. Let the number of different |
| * gray values in pixs be ngray. If mindepth == 4, we attempt |
| * to save pixd as a 4 bpp image, but if ngray > 16, |
| * pixd must be 8 bpp. Likewise, if mindepth == 2, |
| * the depth of pixd will be 2 if ngray <= 4 and 4 if ngray > 4 |
| * but <= 16. |
| */ |
| PIX * |
| pixConvertGrayToColormap8(PIX *pixs, |
| l_int32 mindepth) |
| { |
| l_int32 ncolors, w, h, depth, i, j, wpls, wpld; |
| l_int32 index, num, val, newval; |
| l_int32 array[256]; |
| l_uint32 *lines, *lined, *datas, *datad; |
| NUMA *na; |
| PIX *pixd; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixConvertGrayToColormap8"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 8) |
| return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); |
| if (mindepth != 2 && mindepth != 4 && mindepth != 8) { |
| L_WARNING("invalid value of mindepth; setting to 8", procName); |
| mindepth = 8; |
| } |
| |
| if (pixGetColormap(pixs)) { |
| L_WARNING("pixs already has a colormap", procName); |
| return pixCopy(NULL, pixs); |
| } |
| |
| na = pixGetGrayHistogram(pixs, 1); |
| numaGetCountRelativeToZero(na, L_GREATER_THAN_ZERO, &ncolors); |
| if (mindepth == 8 || ncolors > 16) |
| depth = 8; |
| else if (mindepth == 4 || ncolors > 4) |
| depth = 4; |
| else |
| depth = 2; |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| pixd = pixCreate(w, h, depth); |
| cmap = pixcmapCreate(depth); |
| pixSetColormap(pixd, cmap); |
| pixCopyResolution(pixd, pixs); |
| |
| index = 0; |
| for (i = 0; i < 256; i++) { |
| numaGetIValue(na, i, &num); |
| if (num > 0) { |
| pixcmapAddColor(cmap, i, i, i); |
| array[i] = index; |
| index++; |
| } |
| } |
| |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| 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 = GET_DATA_BYTE(lines, j); |
| newval = array[val]; |
| if (depth == 2) |
| SET_DATA_DIBIT(lined, j, newval); |
| else if (depth == 4) |
| SET_DATA_QBIT(lined, j, newval); |
| else /* depth == 8 */ |
| SET_DATA_BYTE(lined, j, newval); |
| } |
| } |
| |
| numaDestroy(&na); |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Colorizing conversion from grayscale to color * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixColorizeGray() |
| * |
| * Input: pixs (8 bpp gray; 2, 4 or 8 bpp colormapped) |
| * color (32 bit rgba pixel) |
| * cmapflag (1 for result to have colormap; 0 for RGB) |
| * Return: pixd (8 bpp colormapped or 32 bpp rgb), or null on error |
| * |
| * Notes: |
| * (1) This applies the specific color to the grayscale image. |
| * (2) If pixs already has a colormap, it is removed to gray |
| * before colorizing. |
| */ |
| PIX * |
| pixColorizeGray(PIX *pixs, |
| l_uint32 color, |
| l_int32 cmapflag) |
| { |
| l_int32 i, j, w, h, wplt, wpld, val8; |
| l_uint32 *datad, *datat, *lined, *linet, *tab; |
| PIX *pixt, *pixd; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixColorizeGray"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) |
| return (PIX *)ERROR_PTR("pixs not 8 bpp or cmapped", procName, NULL); |
| |
| if (pixGetColormap(pixs)) |
| pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
| else |
| pixt = pixClone(pixs); |
| |
| cmap = pixcmapGrayToColor(color); |
| if (cmapflag) { |
| pixd = pixCopy(NULL, pixt); |
| pixSetColormap(pixd, cmap); |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| |
| /* Make an RGB pix */ |
| pixcmapToRGBTable(cmap, &tab, NULL); |
| pixGetDimensions(pixt, &w, &h, NULL); |
| pixd = pixCreate(w, h, 32); |
| pixCopyResolution(pixd, pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| datat = pixGetData(pixt); |
| wplt = pixGetWpl(pixt); |
| for (i = 0; i < h; i++) { |
| lined = datad + i * wpld; |
| linet = datat + i * wplt; |
| for (j = 0; j < w; j++) { |
| val8 = GET_DATA_BYTE(linet, j); |
| lined[j] = tab[val8]; |
| } |
| } |
| |
| pixDestroy(&pixt); |
| pixcmapDestroy(&cmap); |
| FREE(tab); |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Conversion from RGB color to colormap * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvertRGBToColormap() |
| * |
| * Input: pixs (32 bpp rgb) |
| * ditherflag (1 to dither, 0 otherwise) |
| * Return: pixd (2, 4 or 8 bpp with colormap), or null on error |
| * |
| * Notes: |
| * (1) This function has two relatively simple modes of color |
| * quantization: |
| * (a) If the image is made orthographically and has not more |
| * than 256 'colors' at the level 4 octcube leaves, |
| * it is quantized nearly exactly. The ditherflag |
| * is ignored. |
| * (b) Most natural images have more than 256 different colors; |
| * in that case we use adaptive octree quantization, |
| * with dithering if requested. |
| * (2) If there are not more than 256 occupied level 4 octcubes, |
| * the color in the colormap that represents all pixels in |
| * one of those octcubes is given by the first pixel that |
| * falls into that octcube. |
| * (3) If there are more than 256 colors, we use adaptive octree |
| * color quantization. |
| * (4) Dithering gives better visual results on images where |
| * there is a color wash (a slow variation of color), but it |
| * is about twice as slow and results in significantly larger |
| * files when losslessly compressed (e.g., into png). |
| */ |
| PIX * |
| pixConvertRGBToColormap(PIX *pixs, |
| l_int32 ditherflag) |
| { |
| l_int32 ncolors; |
| NUMA *na; |
| PIX *pixd; |
| |
| PROCNAME("pixConvertRGBToColormap"); |
| |
| 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); |
| |
| /* Get the histogram and count the number of occupied level 4 |
| * leaf octcubes. We don't yet know if this is the number of |
| * actual colors, but if it's not, all pixels falling into |
| * the same leaf octcube will be assigned to the color of the |
| * first pixel that lands there. */ |
| na = pixOctcubeHistogram(pixs, 4, &ncolors); |
| |
| /* If there are too many occupied leaf octcubes to be |
| * represented directly in a colormap, fall back to octree |
| * quantization with dithering. */ |
| if (ncolors > 256) { |
| L_INFO("More than 256 colors; using octree quant with dithering", |
| procName); |
| numaDestroy(&na); |
| return pixOctreeColorQuant(pixs, 240, ditherflag); |
| } |
| |
| /* There are not more than 256 occupied leaf octcubes. |
| * Quantize to those octcubes. */ |
| pixd = pixFewColorsOctcubeQuant2(pixs, 4, na, ncolors, NULL); |
| numaDestroy(&na); |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Quantization for relatively small number of colors in source * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixQuantizeIfFewColors() |
| * |
| * Input: pixs (8 bpp gray or 32 bpp rgb) |
| * maxcolors (max number of colors allowed to be returned |
| * from pixColorsForQuantization(); use 0 for default) |
| * mingraycolors (min number of gray levels that a grayscale |
| * image is quantized to; use 0 for default) |
| * octlevel (for octcube quantization: 3 or 4) |
| * &pixd (2, 4 or 8 bpp quantized; null if too many colors) |
| * Return: 0 if OK, 1 on error or if pixs can't be quantized into |
| * a small number of colors. |
| * |
| * Notes: |
| * (1) This is a wrapper that tests if the pix can be quantized |
| * with good quality using a small number of colors. If so, |
| * it does the quantization, defining a colormap and using |
| * pixels whose value is an index into the colormap. |
| * (2) If the image has color, it is quantized with 8 bpp pixels. |
| * If the image is essentially grayscale, the pixels are |
| * either 4 or 8 bpp, depending on the size of the required |
| * colormap. |
| * (3) @octlevel = 3 works well for most images. However, for best |
| * quality, at a cost of more colors in the colormap, use |
| * @octlevel = 4. |
| * (4) If the image already has a colormap, it returns a clone. |
| */ |
| l_int32 |
| pixQuantizeIfFewColors(PIX *pixs, |
| l_int32 maxcolors, |
| l_int32 mingraycolors, |
| l_int32 octlevel, |
| PIX **ppixd) |
| { |
| l_int32 d, ncolors, iscolor, graycolors; |
| PIX *pixg, *pixd; |
| |
| PROCNAME("pixQuantizeIfFewColors"); |
| |
| if (!ppixd) |
| return ERROR_INT("&pixd not defined", procName, 1); |
| *ppixd = NULL; |
| if (!pixs) |
| return ERROR_INT("pixs not defined", procName, 1); |
| d = pixGetDepth(pixs); |
| if (d != 8 && d != 32) |
| return ERROR_INT("pixs not defined", procName, 1); |
| if (pixGetColormap(pixs) != NULL) { |
| *ppixd = pixClone(pixs); |
| return 0; |
| } |
| if (maxcolors <= 0) |
| maxcolors = 15; /* default */ |
| if (maxcolors > 50) |
| L_WARNING("maxcolors > 50; very large!", procName); |
| if (mingraycolors <= 0) |
| mingraycolors = 10; /* default */ |
| if (mingraycolors > 30) |
| L_WARNING("mingraycolors > 30; very large!", procName); |
| if (octlevel != 3 && octlevel != 4) { |
| L_WARNING("invalid octlevel; setting to 3", procName); |
| octlevel = 3; |
| } |
| |
| /* Test the number of colors. For color, the octcube leaves |
| * are at level 4. */ |
| pixColorsForQuantization(pixs, 0, &ncolors, &iscolor, 0); |
| if (ncolors > maxcolors) |
| return ERROR_INT("too many colors", procName, 1); |
| |
| /* Quantize! |
| * (1) For color: |
| * If octlevel == 4, try to quantize to an octree where |
| * the octcube leaves are at level 4. If that fails, |
| * back off to level 3. |
| * If octlevel == 3, quantize to level 3 directly. |
| * For level 3, the quality is usually good enough and there |
| * is negligible chance of getting more than 256 colors. |
| * (2) For grayscale, multiply ncolors by 1.5 for extra quality, |
| * but use at least mingraycolors. */ |
| if (iscolor) { |
| pixd = pixFewColorsOctcubeQuant1(pixs, octlevel); |
| if (!pixd) { /* backoff */ |
| pixd = pixFewColorsOctcubeQuant1(pixs, octlevel - 1); |
| if (octlevel == 3) /* shouldn't happen */ |
| L_WARNING("quantized at level 2; low quality", procName); |
| } |
| } |
| else { /* image is really grayscale */ |
| if (d == 32) |
| pixg = pixConvertRGBToLuminance(pixs); |
| else |
| pixg = pixClone(pixs); |
| graycolors = L_MAX(mingraycolors, (l_int32)(1.5 * ncolors)); |
| if (graycolors < 16) |
| pixd = pixThresholdTo4bpp(pixg, graycolors, 1); |
| else |
| pixd = pixThresholdOn8bpp(pixg, graycolors, 1); |
| pixDestroy(&pixg); |
| } |
| *ppixd = pixd; |
| |
| if (!pixd) |
| return ERROR_INT("pixd not made", procName, 1); |
| else |
| return 0; |
| } |
| |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Conversion from 16 bpp to 8 bpp * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvert16To8() |
| * |
| * Input: pixs (16 bpp) |
| * whichbyte (1 for MSB, 0 for LSB) |
| * Return: pixd (8 bpp), or null on error |
| * |
| * Notes: |
| * (1) For each dest pixel, use either the MSB or LSB of each src pixel. |
| */ |
| PIX * |
| pixConvert16To8(PIX *pixs, |
| l_int32 whichbyte) |
| { |
| l_uint16 dsword; |
| l_int32 w, h, wpls, wpld, i, j; |
| l_uint32 sword; |
| l_uint32 *datas, *datad, *lines, *lined; |
| PIX *pixd; |
| |
| PROCNAME("pixConvert16To8"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 16) |
| return (PIX *)ERROR_PTR("pixs not 16 bpp", 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); |
| wpls = pixGetWpl(pixs); |
| datas = pixGetData(pixs); |
| wpld = pixGetWpl(pixd); |
| datad = pixGetData(pixd); |
| |
| /* Convert 2 pixels at a time */ |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + i * wpld; |
| if (whichbyte == 0) { /* LSB */ |
| for (j = 0; j < wpls; j++) { |
| sword = *(lines + j); |
| dsword = ((sword >> 8) & 0xff00) | (sword & 0xff); |
| SET_DATA_TWO_BYTES(lined, j, dsword); |
| } |
| } |
| else { /* MSB */ |
| for (j = 0; j < wpls; j++) { |
| sword = *(lines + j); |
| dsword = ((sword >> 16) & 0xff00) | ((sword >> 8) & 0xff); |
| SET_DATA_TWO_BYTES(lined, j, dsword); |
| } |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Conversion from grayscale to false color |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvertGrayToFalseColor() |
| * |
| * Input: pixs (8 or 16 bpp grayscale) |
| * gamma factor (0.0 or 1.0 for default; > 1.0 for brighter; |
| * 2.0 is quite nice) |
| * Return: pixd (8 bpp with colormap), or null on error |
| * |
| * Notes: |
| * (1) For 8 bpp input, this simply adds a colormap to the input image. |
| * (2) For 16 bpp input, it first converts to 8 bpp and then |
| * adds the colormap. |
| * (3) The colormap is modeled after the Matlab "jet" configuration. |
| */ |
| PIX * |
| pixConvertGrayToFalseColor(PIX *pixs, |
| l_float32 gamma) |
| { |
| l_int32 d, i, rval, bval, gval; |
| l_int32 *curve; |
| l_float32 invgamma, x; |
| PIX *pixd; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixConvertGrayToFalseColor"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| d = pixGetDepth(pixs); |
| if (d != 8 && d != 16) |
| return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", procName, NULL); |
| |
| if (d == 16) |
| pixd = pixConvert16To8(pixs, 1); |
| else { /* d == 8 */ |
| if (pixGetColormap(pixs)) |
| pixd = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
| else |
| pixd = pixCopy(NULL, pixs); |
| } |
| if (!pixd) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| if ((cmap = pixcmapCreate(8)) == NULL) |
| return (PIX *)ERROR_PTR("cmap not made", procName, NULL); |
| pixSetColormap(pixd, cmap); |
| pixCopyResolution(pixd, pixs); |
| |
| /* Generate curve for transition part of color map */ |
| if ((curve = (l_int32 *)CALLOC(64, sizeof(l_int32)))== NULL) |
| return (PIX *)ERROR_PTR("curve not made", procName, NULL); |
| if (gamma == 0.0) gamma = 1.0; |
| invgamma = 1. / gamma; |
| for (i = 0; i < 64; i++) { |
| x = (l_float32)i / 64.; |
| curve[i] = (l_int32)(255. * powf(x, invgamma) + 0.5); |
| } |
| |
| for (i = 0; i < 256; i++) { |
| if (i < 32) { |
| rval = 0; |
| gval = 0; |
| bval = curve[i + 32]; |
| } |
| else if (i < 96) { /* 32 - 95 */ |
| rval = 0; |
| gval = curve[i - 32]; |
| bval = 255; |
| } |
| else if (i < 160) { /* 96 - 159 */ |
| rval = curve[i - 96]; |
| gval = 255; |
| bval = curve[159 - i]; |
| } |
| else if (i < 224) { /* 160 - 223 */ |
| rval = 255; |
| gval = curve[223 - i]; |
| bval = 0; |
| } |
| else { /* 224 - 255 */ |
| rval = curve[287 - i]; |
| gval = 0; |
| bval = 0; |
| } |
| pixcmapAddColor(cmap, rval, gval, bval); |
| } |
| |
| FREE(curve); |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Unpacking conversion from 1 bpp to 2, 4, 8, 16 and 32 bpp * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixUnpackBinary() |
| * |
| * Input: pixs (1 bpp) |
| * depth (of destination: 2, 4, 8, 16 or 32 bpp) |
| * invert (0: binary 0 --> grayscale 0 |
| * binary 1 --> grayscale 0xff... |
| * 1: binary 0 --> grayscale 0xff... |
| * binary 1 --> grayscale 0) |
| * Return: pixd (2, 4, 8, 16 or 32 bpp), or null on error |
| * |
| * Notes: |
| * (1) This function calls special cases of pixConvert1To*(), |
| * for 2, 4, 8, 16 and 32 bpp destinations. |
| */ |
| PIX * |
| pixUnpackBinary(PIX *pixs, |
| l_int32 depth, |
| l_int32 invert) |
| { |
| PIX *pixd; |
| |
| PROCNAME("pixUnpackBinary"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); |
| if (depth != 2 && depth != 4 && depth != 8 && depth != 16 && depth != 32) |
| return (PIX *)ERROR_PTR("depth not 2, 4, 8, 16 or 32 bpp", |
| procName, NULL); |
| |
| if (depth == 2) { |
| if (invert == 0) |
| pixd = pixConvert1To2(NULL, pixs, 0, 3); |
| else /* invert bits */ |
| pixd = pixConvert1To2(NULL, pixs, 3, 0); |
| } |
| else if (depth == 4) { |
| if (invert == 0) |
| pixd = pixConvert1To4(NULL, pixs, 0, 15); |
| else /* invert bits */ |
| pixd = pixConvert1To4(NULL, pixs, 15, 0); |
| } |
| else if (depth == 8) { |
| if (invert == 0) |
| pixd = pixConvert1To8(NULL, pixs, 0, 255); |
| else /* invert bits */ |
| pixd = pixConvert1To8(NULL, pixs, 255, 0); |
| } |
| else if (depth == 16) { |
| if (invert == 0) |
| pixd = pixConvert1To16(NULL, pixs, 0, 0xffff); |
| else /* invert bits */ |
| pixd = pixConvert1To16(NULL, pixs, 0xffff, 0); |
| } |
| else { |
| if (invert == 0) |
| pixd = pixConvert1To32(NULL, pixs, 0, 0xffffffff); |
| else /* invert bits */ |
| pixd = pixConvert1To32(NULL, pixs, 0xffffffff, 0); |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvert1To16() |
| * |
| * Input: pixd (<optional> 16 bpp, can be null) |
| * pixs (1 bpp) |
| * val0 (16 bit value to be used for 0s in pixs) |
| * val1 (16 bit value to be used for 1s in pixs) |
| * Return: pixd (16 bpp) |
| * |
| * Notes: |
| * (1) If pixd is null, a new pix is made. |
| * (2) If pixd is not null, it must be of equal width and height |
| * as pixs. It is always returned. |
| */ |
| PIX * |
| pixConvert1To16(PIX *pixd, |
| PIX *pixs, |
| l_uint16 val0, |
| l_uint16 val1) |
| { |
| l_int32 w, h, i, j, dibit, ndibits, wpls, wpld; |
| l_uint16 val[2]; |
| l_uint32 index; |
| l_uint32 *tab, *datas, *datad, *lines, *lined; |
| |
| PROCNAME("pixConvert1To16"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); |
| |
| w = pixGetWidth(pixs); |
| h = pixGetHeight(pixs); |
| if (pixd) { |
| if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) |
| return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); |
| if (pixGetDepth(pixd) != 16) |
| return (PIX *)ERROR_PTR("pixd not 16 bpp", procName, pixd); |
| } |
| else { |
| if ((pixd = pixCreate(w, h, 16)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| } |
| pixCopyResolution(pixd, pixs); |
| |
| /* Use a table to convert 2 src bits at a time */ |
| if ((tab = (l_uint32 *)CALLOC(4, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("tab not made", procName, NULL); |
| val[0] = val0; |
| val[1] = val1; |
| for (index = 0; index < 4; index++) { |
| tab[index] = (val[(index >> 1) & 1] << 16) | val[index & 1]; |
| } |
| |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| ndibits = (w + 1) / 2; |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < ndibits; j++) { |
| dibit = GET_DATA_DIBIT(lines, j); |
| lined[j] = tab[dibit]; |
| } |
| } |
| |
| FREE(tab); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvert1To32() |
| * |
| * Input: pixd (<optional> 32 bpp, can be null) |
| * pixs (1 bpp) |
| * val0 (32 bit value to be used for 0s in pixs) |
| * val1 (32 bit value to be used for 1s in pixs) |
| * Return: pixd (32 bpp) |
| * |
| * Notes: |
| * (1) If pixd is null, a new pix is made. |
| * (2) If pixd is not null, it must be of equal width and height |
| * as pixs. It is always returned. |
| */ |
| PIX * |
| pixConvert1To32(PIX *pixd, |
| PIX *pixs, |
| l_uint32 val0, |
| l_uint32 val1) |
| { |
| l_int32 w, h, i, j, wpls, wpld, bit; |
| l_uint32 val[2]; |
| l_uint32 *datas, *datad, *lines, *lined; |
| |
| PROCNAME("pixConvert1To32"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| if (pixd) { |
| if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) |
| return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); |
| if (pixGetDepth(pixd) != 32) |
| return (PIX *)ERROR_PTR("pixd not 32 bpp", procName, pixd); |
| } |
| else { |
| if ((pixd = pixCreate(w, h, 32)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| } |
| pixCopyResolution(pixd, pixs); |
| |
| val[0] = val0; |
| val[1] = val1; |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| 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++) { |
| bit = GET_DATA_BIT(lines, j); |
| lined[j] = val[bit]; |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Conversion from 1 bpp to 2 bpp * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvert1To2Cmap() |
| * |
| * Input: pixs (1 bpp) |
| * Return: pixd (2 bpp, cmapped) |
| * |
| * Notes: |
| * (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0) |
| */ |
| PIX * |
| pixConvert1To2Cmap(PIX *pixs) |
| { |
| PIX *pixd; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixConvert1To2Cmap"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); |
| |
| if ((pixd = pixConvert1To2(NULL, pixs, 0, 1)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| cmap = pixcmapCreate(2); |
| pixcmapAddColor(cmap, 255, 255, 255); |
| pixcmapAddColor(cmap, 0, 0, 0); |
| pixSetColormap(pixd, cmap); |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvert1To2() |
| * |
| * Input: pixd (<optional> 2 bpp, can be null) |
| * pixs (1 bpp) |
| * val0 (2 bit value to be used for 0s in pixs) |
| * val1 (2 bit value to be used for 1s in pixs) |
| * Return: pixd (2 bpp) |
| * |
| * Notes: |
| * (1) If pixd is null, a new pix is made. |
| * (2) If pixd is not null, it must be of equal width and height |
| * as pixs. It is always returned. |
| * (3) A simple unpacking might use val0 = 0 and val1 = 3. |
| * (4) If you want a colormapped pixd, use pixConvert1To2Cmap(). |
| */ |
| PIX * |
| pixConvert1To2(PIX *pixd, |
| PIX *pixs, |
| l_int32 val0, |
| l_int32 val1) |
| { |
| l_int32 w, h, i, j, byteval, nbytes, wpls, wpld; |
| l_uint8 val[2]; |
| l_uint32 index; |
| l_uint16 *tab; |
| l_uint32 *datas, *datad, *lines, *lined; |
| |
| PROCNAME("pixConvert1To2"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| if (pixd) { |
| if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) |
| return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); |
| if (pixGetDepth(pixd) != 2) |
| return (PIX *)ERROR_PTR("pixd not 2 bpp", procName, pixd); |
| } |
| else { |
| if ((pixd = pixCreate(w, h, 2)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| } |
| pixCopyResolution(pixd, pixs); |
| |
| /* Use a table to convert 8 src bits to 16 dest bits */ |
| if ((tab = (l_uint16 *)CALLOC(256, sizeof(l_uint16))) == NULL) |
| return (PIX *)ERROR_PTR("tab not made", procName, NULL); |
| val[0] = val0; |
| val[1] = val1; |
| for (index = 0; index < 256; index++) { |
| tab[index] = (val[(index >> 7) & 1] << 14) | |
| (val[(index >> 6) & 1] << 12) | |
| (val[(index >> 5) & 1] << 10) | |
| (val[(index >> 4) & 1] << 8) | |
| (val[(index >> 3) & 1] << 6) | |
| (val[(index >> 2) & 1] << 4) | |
| (val[(index >> 1) & 1] << 2) | val[index & 1]; |
| } |
| |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| nbytes = (w + 7) / 8; |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < nbytes; j++) { |
| byteval = GET_DATA_BYTE(lines, j); |
| SET_DATA_TWO_BYTES(lined, j, tab[byteval]); |
| } |
| } |
| |
| FREE(tab); |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Conversion from 1 bpp to 4 bpp * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvert1To4Cmap() |
| * |
| * Input: pixs (1 bpp) |
| * Return: pixd (4 bpp, cmapped) |
| * |
| * Notes: |
| * (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0) |
| */ |
| PIX * |
| pixConvert1To4Cmap(PIX *pixs) |
| { |
| PIX *pixd; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixConvert1To4Cmap"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); |
| |
| if ((pixd = pixConvert1To4(NULL, pixs, 0, 1)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| cmap = pixcmapCreate(4); |
| pixcmapAddColor(cmap, 255, 255, 255); |
| pixcmapAddColor(cmap, 0, 0, 0); |
| pixSetColormap(pixd, cmap); |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvert1To4() |
| * |
| * Input: pixd (<optional> 4 bpp, can be null) |
| * pixs (1 bpp) |
| * val0 (4 bit value to be used for 0s in pixs) |
| * val1 (4 bit value to be used for 1s in pixs) |
| * Return: pixd (4 bpp) |
| * |
| * Notes: |
| * (1) If pixd is null, a new pix is made. |
| * (2) If pixd is not null, it must be of equal width and height |
| * as pixs. It is always returned. |
| * (3) A simple unpacking might use val0 = 0 and val1 = 15, or v.v. |
| * (4) If you want a colormapped pixd, use pixConvert1To4Cmap(). |
| */ |
| PIX * |
| pixConvert1To4(PIX *pixd, |
| PIX *pixs, |
| l_int32 val0, |
| l_int32 val1) |
| { |
| l_int32 w, h, i, j, byteval, nbytes, wpls, wpld; |
| l_uint8 val[2]; |
| l_uint32 index; |
| l_uint32 *tab, *datas, *datad, *lines, *lined; |
| |
| PROCNAME("pixConvert1To4"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| if (pixd) { |
| if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) |
| return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); |
| if (pixGetDepth(pixd) != 4) |
| return (PIX *)ERROR_PTR("pixd not 4 bpp", procName, pixd); |
| } |
| else { |
| if ((pixd = pixCreate(w, h, 4)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| } |
| pixCopyResolution(pixd, pixs); |
| |
| /* Use a table to convert 8 src bits to 32 bit dest word */ |
| if ((tab = (l_uint32 *)CALLOC(256, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("tab not made", procName, NULL); |
| val[0] = val0; |
| val[1] = val1; |
| for (index = 0; index < 256; index++) { |
| tab[index] = (val[(index >> 7) & 1] << 28) | |
| (val[(index >> 6) & 1] << 24) | |
| (val[(index >> 5) & 1] << 20) | |
| (val[(index >> 4) & 1] << 16) | |
| (val[(index >> 3) & 1] << 12) | |
| (val[(index >> 2) & 1] << 8) | |
| (val[(index >> 1) & 1] << 4) | val[index & 1]; |
| } |
| |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| nbytes = (w + 7) / 8; |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < nbytes; j++) { |
| byteval = GET_DATA_BYTE(lines, j); |
| lined[j] = tab[byteval]; |
| } |
| } |
| |
| FREE(tab); |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Conversion from 1, 2 and 4 bpp to 8 bpp * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvert1To8() |
| * |
| * Input: pixd (<optional> 8 bpp, can be null) |
| * pixs (1 bpp) |
| * val0 (8 bit value to be used for 0s in pixs) |
| * val1 (8 bit value to be used for 1s in pixs) |
| * Return: pixd (8 bpp) |
| * |
| * Notes: |
| * (1) If pixd is null, a new pix is made. |
| * (2) If pixd is not null, it must be of equal width and height |
| * as pixs. It is always returned. |
| * (3) A simple unpacking might use val0 = 0 and val1 = 255, or v.v. |
| * (4) In a typical application where one wants to use a colormap |
| * with the dest, you can use val0 = 0, val1 = 1 to make a |
| * non-cmapped 8 bpp pix, and then make a colormap and set 0 |
| * and 1 to the desired colors. Here is an example: |
| * pixd = pixConvert1To8(NULL, pixs, 0, 1); |
| * cmap = pixCreate(8); |
| * pixcmapAddColor(cmap, 255, 255, 255); |
| * pixcmapAddColor(cmap, 0, 0, 0); |
| * pixSetColormap(pixd, cmap); |
| */ |
| PIX * |
| pixConvert1To8(PIX *pixd, |
| PIX *pixs, |
| l_uint8 val0, |
| l_uint8 val1) |
| { |
| l_int32 w, h, i, j, qbit, nqbits, wpls, wpld; |
| l_uint8 val[2]; |
| l_uint32 index; |
| l_uint32 *tab, *datas, *datad, *lines, *lined; |
| |
| PROCNAME("pixConvert1To8"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); |
| if (pixGetDepth(pixs) != 1) |
| return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| if (pixd) { |
| if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) |
| return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); |
| if (pixGetDepth(pixd) != 8) |
| return (PIX *)ERROR_PTR("pixd not 8 bpp", procName, pixd); |
| } |
| else { |
| if ((pixd = pixCreate(w, h, 8)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| } |
| pixCopyResolution(pixd, pixs); |
| |
| /* Use a table to convert 4 src bits at a time */ |
| if ((tab = (l_uint32 *)CALLOC(16, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("tab not made", procName, NULL); |
| val[0] = val0; |
| val[1] = val1; |
| for (index = 0; index < 16; index++) { |
| tab[index] = (val[(index >> 3) & 1] << 24) | |
| (val[(index >> 2) & 1] << 16) | |
| (val[(index >> 1) & 1] << 8) | val[index & 1]; |
| } |
| |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| nqbits = (w + 3) / 4; |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < nqbits; j++) { |
| qbit = GET_DATA_QBIT(lines, j); |
| lined[j] = tab[qbit]; |
| } |
| } |
| |
| FREE(tab); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvert2To8() |
| * |
| * Input: pixs (2 bpp) |
| * val0 (8 bit value to be used for 00 in pixs) |
| * val1 (8 bit value to be used for 01 in pixs) |
| * val2 (8 bit value to be used for 10 in pixs) |
| * val3 (8 bit value to be used for 11 in pixs) |
| * cmapflag (TRUE if pixd is to have a colormap; FALSE otherwise) |
| * Return: pixd (8 bpp), or null on error |
| * |
| * Notes: |
| * - A simple unpacking might use val0 = 0, |
| * val1 = 85 (0x55), val2 = 170 (0xaa), val3 = 255. |
| * - If cmapflag is TRUE: |
| * - The 8 bpp image is made with a colormap. |
| * - If pixs has a colormap, the input values are ignored and |
| * the 8 bpp image is made using the colormap |
| * - If pixs does not have a colormap, the input values are |
| * used to build the colormap. |
| * - If cmapflag is FALSE: |
| * - The 8 bpp image is made without a colormap. |
| * - If pixs has a colormap, the input values are ignored, |
| * the colormap is removed, and the values stored in the 8 bpp |
| * image are from the colormap. |
| * - If pixs does not have a colormap, the input values are |
| * used to populate the 8 bpp image. |
| */ |
| PIX * |
| pixConvert2To8(PIX *pixs, |
| l_uint8 val0, |
| l_uint8 val1, |
| l_uint8 val2, |
| l_uint8 val3, |
| l_int32 cmapflag) |
| { |
| l_int32 w, h, i, j, nbytes, wpls, wpld, dibit, ncolor; |
| l_int32 rval, gval, bval, byte; |
| l_uint8 val[4]; |
| l_uint32 index; |
| l_uint32 *tab, *datas, *datad, *lines, *lined; |
| PIX *pixd; |
| PIXCMAP *cmaps, *cmapd; |
| |
| PROCNAME("pixConvert2To8"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 2) |
| return (PIX *)ERROR_PTR("pixs not 2 bpp", procName, NULL); |
| |
| cmaps = pixGetColormap(pixs); |
| if (cmaps && cmapflag == FALSE) |
| return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
| |
| 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); |
| wpls = pixGetWpl(pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| |
| if (cmapflag == TRUE) { /* pixd will have a colormap */ |
| cmapd = pixcmapCreate(8); /* 8 bpp standard cmap */ |
| if (cmaps) { /* use the existing colormap from pixs */ |
| ncolor = pixcmapGetCount(cmaps); |
| for (i = 0; i < ncolor; i++) { |
| pixcmapGetColor(cmaps, i, &rval, &gval, &bval); |
| pixcmapAddColor(cmapd, rval, gval, bval); |
| } |
| } |
| else { /* make a colormap from the input values */ |
| pixcmapAddColor(cmapd, val0, val0, val0); |
| pixcmapAddColor(cmapd, val1, val1, val1); |
| pixcmapAddColor(cmapd, val2, val2, val2); |
| pixcmapAddColor(cmapd, val3, val3, val3); |
| } |
| pixSetColormap(pixd, cmapd); |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < w; j++) { |
| dibit = GET_DATA_DIBIT(lines, j); |
| SET_DATA_BYTE(lined, j, dibit); |
| } |
| } |
| return pixd; |
| } |
| |
| /* Last case: no colormap in either pixs or pixd. |
| * Use input values and build a table to convert 1 src byte |
| * (4 src pixels) at a time */ |
| if ((tab = (l_uint32 *)CALLOC(256, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("tab not made", procName, NULL); |
| val[0] = val0; |
| val[1] = val1; |
| val[2] = val2; |
| val[3] = val3; |
| for (index = 0; index < 256; index++) { |
| tab[index] = (val[(index >> 6) & 3] << 24) | |
| (val[(index >> 4) & 3] << 16) | |
| (val[(index >> 2) & 3] << 8) | val[index & 3]; |
| } |
| |
| nbytes = (w + 3) / 4; |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < nbytes; j++) { |
| byte = GET_DATA_BYTE(lines, j); |
| lined[j] = tab[byte]; |
| } |
| } |
| |
| FREE(tab); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvert4To8() |
| * |
| * Input: pixs (4 bpp) |
| * cmapflag (TRUE if pixd is to have a colormap; FALSE otherwise) |
| * Return: pixd (8 bpp), or null on error |
| * |
| * Notes: |
| * - If cmapflag is TRUE: |
| * - pixd is made with a colormap. |
| * - If pixs has a colormap, it is copied and the colormap |
| * index values are placed in pixd. |
| * - If pixs does not have a colormap, a colormap with linear |
| * trc is built and the pixel values in pixs are placed in |
| * pixd as colormap index values. |
| * - If cmapflag is FALSE: |
| * - pixd is made without a colormap. |
| * - If pixs has a colormap, it is removed and the values stored |
| * in pixd are from the colormap (converted to gray). |
| * - If pixs does not have a colormap, the pixel values in pixs |
| * are used, with shift replication, to populate pixd. |
| */ |
| PIX * |
| pixConvert4To8(PIX *pixs, |
| l_int32 cmapflag) |
| { |
| l_int32 w, h, i, j, wpls, wpld, ncolor; |
| l_int32 rval, gval, bval, byte, qbit; |
| l_uint32 *datas, *datad, *lines, *lined; |
| PIX *pixd; |
| PIXCMAP *cmaps, *cmapd; |
| |
| PROCNAME("pixConvert4To8"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 4) |
| return (PIX *)ERROR_PTR("pixs not 4 bpp", procName, NULL); |
| |
| cmaps = pixGetColormap(pixs); |
| if (cmaps && cmapflag == FALSE) |
| return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
| |
| 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); |
| wpls = pixGetWpl(pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| |
| if (cmapflag == TRUE) { /* pixd will have a colormap */ |
| cmapd = pixcmapCreate(8); |
| if (cmaps) { /* use the existing colormap from pixs */ |
| ncolor = pixcmapGetCount(cmaps); |
| for (i = 0; i < ncolor; i++) { |
| pixcmapGetColor(cmaps, i, &rval, &gval, &bval); |
| pixcmapAddColor(cmapd, rval, gval, bval); |
| } |
| } |
| else { /* make a colormap with a linear trc */ |
| for (i = 0; i < 16; i++) |
| pixcmapAddColor(cmapd, 17 * i, 17 * i, 17 * i); |
| } |
| pixSetColormap(pixd, cmapd); |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < w; j++) { |
| qbit = GET_DATA_QBIT(lines, j); |
| SET_DATA_BYTE(lined, j, qbit); |
| } |
| } |
| return pixd; |
| } |
| |
| /* Last case: no colormap in either pixs or pixd. |
| * Replicate the qbit value into 8 bits. */ |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + i * wpld; |
| for (j = 0; j < w; j++) { |
| qbit = GET_DATA_QBIT(lines, j); |
| byte = (qbit << 4) | qbit; |
| SET_DATA_BYTE(lined, j, byte); |
| } |
| } |
| return pixd; |
| } |
| |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Unpacking conversion from 8 bpp to 16 bpp * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvert8To16() |
| * |
| * Input: pixs (8 bpp; colormap removed to gray) |
| * leftshift (number of bits: 0 is no shift; |
| * 8 replicates in MSB and LSB of dest) |
| * Return: pixd (16 bpp), or null on error |
| * |
| * Notes: |
| * (1) For left shift of 8, the 8 bit value is replicated in both |
| * the MSB and the LSB of the pixels in pixd. That way, we get |
| * proportional mapping, with a correct map from 8 bpp white |
| * (0xff) to 16 bpp white (0xffff). |
| */ |
| PIX * |
| pixConvert8To16(PIX *pixs, |
| l_int32 leftshift) |
| { |
| l_int32 i, j, w, h, d, wplt, wpld, val; |
| l_uint32 *datat, *datad, *linet, *lined; |
| PIX *pixt, *pixd; |
| |
| PROCNAME("pixConvert8To16"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| pixGetDimensions(pixs, &w, &h, &d); |
| if (d != 8) |
| return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); |
| if (leftshift < 0 || leftshift > 8) |
| return (PIX *)ERROR_PTR("leftshift not in [0 ... 8]", procName, NULL); |
| |
| if (pixGetColormap(pixs) != NULL) |
| pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
| else |
| pixt = pixClone(pixs); |
| |
| pixd = pixCreate(w, h, 16); |
| datat = pixGetData(pixt); |
| datad = pixGetData(pixd); |
| wplt = pixGetWpl(pixt); |
| wpld = pixGetWpl(pixd); |
| for (i = 0; i < h; i++) { |
| linet = datat + i * wplt; |
| lined = datad + i * wpld; |
| for (j = 0; j < w; j++) { |
| val = GET_DATA_BYTE(linet, j); |
| if (leftshift == 8) |
| val = val | (val << leftshift); |
| else |
| val <<= leftshift; |
| SET_DATA_TWO_BYTES(lined, j, val); |
| } |
| } |
| |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Top-level conversion to 1 bpp * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvertTo1() |
| * |
| * Input: pixs (1, 2, 4, 8, 16 or 32 bpp) |
| * threshold (for final binarization, relative to 8 bpp) |
| * Return: pixd (1 bpp), or null on error |
| * |
| * Notes: |
| * (1) This is a top-level function, with simple default values |
| * used in pixConvertTo8() if unpacking is necessary. |
| * (2) Any existing colormap is removed. |
| * (3) If the input image has 1 bpp and no colormap, the operation is |
| * lossless and a copy is returned. |
| */ |
| PIX * |
| pixConvertTo1(PIX *pixs, |
| l_int32 threshold) |
| { |
| l_int32 d, color0, color1, rval, gval, bval; |
| PIX *pixg, *pixd; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixConvertTo1"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| d = pixGetDepth(pixs); |
| if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) |
| return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL); |
| |
| cmap = pixGetColormap(pixs); |
| if (d == 1) { |
| if (!cmap) |
| return pixCopy(NULL, pixs); |
| else { /* strip the colormap off, and invert if reasonable |
| for standard binary photometry. */ |
| pixcmapGetColor(cmap, 0, &rval, &gval, &bval); |
| color0 = rval + gval + bval; |
| pixcmapGetColor(cmap, 1, &rval, &gval, &bval); |
| color1 = rval + gval + bval; |
| pixd = pixCopy(NULL, pixs); |
| pixDestroyColormap(pixd); |
| if (color1 > color0) |
| pixInvert(pixd, pixd); |
| return pixd; |
| } |
| } |
| |
| /* For all other depths, use 8 bpp as an intermediary */ |
| pixg = pixConvertTo8(pixs, FALSE); |
| pixd = pixThresholdToBinary(pixg, threshold); |
| pixDestroy(&pixg); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvertTo1BySampling() |
| * |
| * Input: pixs (1, 2, 4, 8, 16 or 32 bpp) |
| * factor (submsampling factor; integer >= 1) |
| * threshold (for final binarization, relative to 8 bpp) |
| * Return: pixd (1 bpp), or null on error |
| * |
| * Notes: |
| * (1) This is a fast, quick/dirty, top-level converter. |
| * (2) See pixConvertTo1() for default values. |
| */ |
| PIX * |
| pixConvertTo1BySampling(PIX *pixs, |
| l_int32 factor, |
| l_int32 threshold) |
| { |
| l_float32 scalefactor; |
| PIX *pixt, *pixd; |
| |
| PROCNAME("pixConvertTo1BySampling"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (factor < 1) |
| return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); |
| |
| scalefactor = 1. / (l_float32)factor; |
| pixt = pixScaleBySampling(pixs, scalefactor, scalefactor); |
| pixd = pixConvertTo1(pixt, threshold); |
| |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Top-level conversion to 8 bpp * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvertTo8() |
| * |
| * Input: pixs (1, 2, 4, 8, 16 or 32 bpp) |
| * cmapflag (TRUE if pixd is to have a colormap; FALSE otherwise) |
| * Return: pixd (8 bpp), or null on error |
| * |
| * Notes: |
| * (1) This is a top-level function, with simple default values |
| * for unpacking. |
| * (2) The result, pixd, is made with a colormap if specified. |
| * (3) If d == 8, and cmapflag matches the existence of a cmap |
| * in pixs, the operation is lossless and it returns a copy. |
| * (4) The default values used are: |
| * - 1 bpp: val0 = 255, val1 = 0 |
| * - 2 bpp: 4 bpp: even increments over dynamic range |
| * - 8 bpp: lossless if cmap matches cmapflag |
| * - 16 bpp: use most significant byte |
| * (5) If 32 bpp RGB, this is converted to gray. If you want |
| * to do color quantization, you must specify the type |
| * explicitly, using the color quantization code. |
| */ |
| PIX * |
| pixConvertTo8(PIX *pixs, |
| l_int32 cmapflag) |
| { |
| l_int32 d; |
| PIX *pixd; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixConvertTo8"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| d = pixGetDepth(pixs); |
| if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) |
| return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL); |
| |
| if (d == 1) { |
| if (!cmapflag) |
| return pixConvert1To8(NULL, pixs, 255, 0); |
| else { |
| pixd = pixConvert1To8(NULL, pixs, 0, 1); |
| cmap = pixcmapCreate(8); |
| pixcmapAddColor(cmap, 255, 255, 255); |
| pixcmapAddColor(cmap, 0, 0, 0); |
| pixSetColormap(pixd, cmap); |
| return pixd; |
| } |
| } |
| else if (d == 2) |
| return pixConvert2To8(pixs, 0, 85, 170, 255, cmapflag); |
| else if (d == 4) |
| return pixConvert4To8(pixs, cmapflag); |
| else if (d == 8) { |
| cmap = pixGetColormap(pixs); |
| if ((cmap && cmapflag) || (!cmap && !cmapflag)) |
| return pixCopy(NULL, pixs); |
| else if (cmap) /* !cmapflag */ |
| return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); |
| else { /* !cmap && cmapflag; add colormap to pixd */ |
| pixd = pixCopy(NULL, pixs); |
| pixAddGrayColormap8(pixd); |
| return pixd; |
| } |
| } |
| else if (d == 16) { |
| pixd = pixConvert16To8(pixs, 1); |
| if (cmapflag) |
| pixAddGrayColormap8(pixd); |
| return pixd; |
| } |
| else { /* d == 32 */ |
| pixd = pixConvertRGBToLuminance(pixs); |
| if (cmapflag) |
| pixAddGrayColormap8(pixd); |
| return pixd; |
| } |
| } |
| |
| |
| /*! |
| * pixConvertTo8BySampling() |
| * |
| * Input: pixs (1, 2, 4, 8, 16 or 32 bpp) |
| * factor (submsampling factor; integer >= 1) |
| * cmapflag (TRUE if pixd is to have a colormap; FALSE otherwise) |
| * Return: pixd (8 bpp), or null on error |
| * |
| * Notes: |
| * (1) This is a fast, quick/dirty, top-level converter. |
| * (2) See pixConvertTo8() for default values. |
| */ |
| PIX * |
| pixConvertTo8BySampling(PIX *pixs, |
| l_int32 factor, |
| l_int32 cmapflag) |
| { |
| l_float32 scalefactor; |
| PIX *pixt, *pixd; |
| |
| PROCNAME("pixConvertTo8BySampling"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (factor < 1) |
| return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); |
| |
| scalefactor = 1. / (l_float32)factor; |
| pixt = pixScaleBySampling(pixs, scalefactor, scalefactor); |
| pixd = pixConvertTo8(pixt, cmapflag); |
| |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Top-level conversion to 16 bpp * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvertTo16() |
| * |
| * Input: pixs (1, 8 bpp) |
| * Return: pixd (16 bpp), or null on error |
| * |
| * Usage: Top-level function, with simple default values for unpacking. |
| * 1 bpp: val0 = 0xffff, val1 = 0 |
| * 8 bpp: replicates the 8 bit value in both the MSB and LSB |
| * of the 16 bit pixel. |
| */ |
| PIX * |
| pixConvertTo16(PIX *pixs) |
| { |
| l_int32 d; |
| |
| PROCNAME("pixConvertTo16"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| |
| d = pixGetDepth(pixs); |
| if (d == 1) |
| return pixConvert1To16(NULL, pixs, 0xffff, 0); |
| else if (d == 8) |
| return pixConvert8To16(pixs, 8); |
| else |
| return (PIX *)ERROR_PTR("src depth not 1 or 8 bpp", procName, NULL); |
| } |
| |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Top-level conversion to 32 bpp * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvertTo32() |
| * |
| * Input: pixs (1, 2, 4, 8, 16 or 32 bpp) |
| * Return: pixd (32 bpp), or null on error |
| * |
| * Usage: Top-level function, with simple default values for unpacking. |
| * 1 bpp: val0 = 255, val1 = 0 |
| * and then replication into R, G and B components |
| * 2 bpp: if colormapped, use the colormap values; otherwise, |
| * use val0 = 0, val1 = 0x55, val2 = 0xaa, val3 = 255 |
| * and replicate gray into R, G and B components |
| * 4 bpp: if colormapped, use the colormap values; otherwise, |
| * replicate 2 nybs into a byte, and then into R,G,B components |
| * 8 bpp: if colormapped, use the colormap values; otherwise, |
| * replicate gray values into R, G and B components |
| * 16 bpp: replicate MSB into R, G and B components |
| * 32 bpp: makes a copy |
| * |
| * Notes: |
| * (1) Implicit assumption about RGB component ordering. |
| */ |
| PIX * |
| pixConvertTo32(PIX *pixs) |
| { |
| l_int32 d; |
| PIX *pixt, *pixd; |
| |
| PROCNAME("pixConvertTo32"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| |
| d = pixGetDepth(pixs); |
| if (d == 1) |
| return pixConvert1To32(NULL, pixs, 0xffffffff, 0); |
| else if (d == 2) { |
| pixt = pixConvert2To8(pixs, 0, 85, 170, 255, TRUE); |
| pixd = pixConvert8To32(pixt); |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| else if (d == 4) { |
| pixt = pixConvert4To8(pixs, TRUE); |
| pixd = pixConvert8To32(pixt); |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| else if (d == 8) |
| return pixConvert8To32(pixs); |
| else if (d == 16) { |
| pixt = pixConvert16To8(pixs, 1); |
| pixd = pixConvert8To32(pixt); |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| else if (d == 32) |
| return pixCopy(NULL, pixs); |
| else |
| return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8, 16, 32 bpp", |
| procName, NULL); |
| } |
| |
| |
| /*! |
| * pixConvertTo32BySampling() |
| * |
| * Input: pixs (1, 2, 4, 8, 16 or 32 bpp) |
| * factor (submsampling factor; integer >= 1) |
| * Return: pixd (32 bpp), or null on error |
| * |
| * Notes: |
| * (1) This is a fast, quick/dirty, top-level converter. |
| * (2) See pixConvertTo32() for default values. |
| */ |
| PIX * |
| pixConvertTo32BySampling(PIX *pixs, |
| l_int32 factor) |
| { |
| l_float32 scalefactor; |
| PIX *pixt, *pixd; |
| |
| PROCNAME("pixConvertTo32BySampling"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (factor < 1) |
| return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); |
| |
| scalefactor = 1. / (l_float32)factor; |
| pixt = pixScaleBySampling(pixs, scalefactor, scalefactor); |
| pixd = pixConvertTo32(pixt); |
| |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvert8To32() |
| * |
| * Input: pix (8 bpp) |
| * Return: 32 bpp rgb pix, or null on error |
| * |
| * Notes: |
| * (1) If there is no colormap, replicates the gray value |
| * into the 3 MSB of the dest pixel. |
| * (2) Implicit assumption about RGB component ordering. |
| */ |
| PIX * |
| pixConvert8To32(PIX *pixs) |
| { |
| l_int32 i, j, w, h, wpls, wpld, val; |
| l_uint32 *datas, *datad, *lines, *lined; |
| l_uint32 *tab; |
| PIX *pixd; |
| |
| PROCNAME("pixConvert8To32"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetDepth(pixs) != 8) |
| return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); |
| |
| if (pixGetColormap(pixs)) |
| return pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); |
| |
| /* Replication table */ |
| if ((tab = (l_uint32 *)CALLOC(256, sizeof(l_uint32))) == NULL) |
| return (PIX *)ERROR_PTR("tab not made", procName, NULL); |
| for (i = 0; i < 256; i++) |
| tab[i] = (i << 24) | (i << 16) | (i << 8); |
| |
| pixGetDimensions(pixs, &w, &h, NULL); |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| if ((pixd = pixCreate(w, h, 32)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| 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 = GET_DATA_BYTE(lines, j); |
| lined[j] = tab[val]; |
| } |
| } |
| |
| FREE(tab); |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Top-level conversion to 8 or 32 bpp, without colormap * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvertTo8Or32() |
| * |
| * Input: pixs (1, 2, 4, 8, 16, with or without colormap; or 32 bpp rgb) |
| * copyflag (use 0 to return clone if pixs does not need to |
| * be changed; 1 to return a copy in those situations) |
| * warnflag (1 to issue warning if colormap is removed; else 0) |
| * Return: pixd (8 bpp grayscale or 32 bpp rgb), or null on error |
| * |
| * Notes: |
| * (1) If there is a colormap, the colormap is removed to 8 or 32 bpp, |
| * depending on whether the colors in the colormap are all gray. |
| * (2) If the input is either rgb or 8 bpp without a colormap, |
| * this returns either a clone or a copy, depending on @copyflag. |
| * (3) Otherwise, the pix is converted to 8 bpp grayscale. |
| * In all cases, pixd does not have a colormap. |
| */ |
| PIX * |
| pixConvertTo8Or32(PIX *pixs, |
| l_int32 copyflag, |
| l_int32 warnflag) |
| { |
| l_int32 d; |
| PIX *pixd; |
| |
| PROCNAME("pixConvertTo8Or32"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| |
| d = pixGetDepth(pixs); |
| if (pixGetColormap(pixs)) { |
| if (warnflag) L_WARNING("pix has colormap; removing", procName); |
| pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); |
| } |
| else if (d == 8 || d == 32) { |
| if (copyflag == 0) |
| pixd = pixClone(pixs); |
| else |
| pixd = pixCopy(NULL, pixs); |
| } |
| else |
| pixd = pixConvertTo8(pixs, 0); |
| |
| /* Sanity check on result */ |
| d = pixGetDepth(pixd); |
| if (d != 8 && d != 32) { |
| pixDestroy(&pixd); |
| return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL); |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Lossless depth conversion (unpacking) * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvertLossless() |
| * |
| * Input: pixs (1, 2, 4, 8 bpp, not cmapped) |
| * d (destination depth: 2, 4 or 8) |
| * Return: pixd (2, 4 or 8 bpp), or null on error |
| * |
| * Notes: |
| * (1) This is a lossless unpacking (depth-increasing) |
| * conversion. If ds is the depth of pixs, then |
| * - if d < ds, returns NULL |
| * - if d == ds, returns a copy |
| * - if d > ds, does the unpacking conversion |
| * (2) If pixs has a colormap, this is an error. |
| */ |
| PIX * |
| pixConvertLossless(PIX *pixs, |
| l_int32 d) |
| { |
| l_int32 w, h, ds, wpls, wpld, i, j, val; |
| l_uint32 *datas, *datad, *lines, *lined; |
| PIX *pixd; |
| |
| PROCNAME("pixConvertLossless"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (pixGetColormap(pixs)) |
| return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); |
| if (d != 2 && d != 4 && d != 8) |
| return (PIX *)ERROR_PTR("invalid dest depth", procName, NULL); |
| |
| pixGetDimensions(pixs, &w, &h, &ds); |
| if (d < ds) |
| return (PIX *)ERROR_PTR("depth > d", procName, NULL); |
| else if (d == ds) |
| return pixCopy(NULL, pixs); |
| |
| if ((pixd = pixCreate(w, h, d)) == NULL) |
| return (PIX *)ERROR_PTR("pixd not made", procName, NULL); |
| pixCopyResolution(pixd, pixs); |
| |
| /* Unpack the bits */ |
| datas = pixGetData(pixs); |
| wpls = pixGetWpl(pixs); |
| datad = pixGetData(pixd); |
| wpld = pixGetWpl(pixd); |
| for (i = 0; i < h; i++) { |
| lines = datas + i * wpls; |
| lined = datad + i * wpld; |
| switch (ds) |
| { |
| case 1: |
| for (j = 0; j < w; j++) { |
| val = GET_DATA_BIT(lines, j); |
| if (d == 8) |
| SET_DATA_BYTE(lined, j, val); |
| else if (d == 4) |
| SET_DATA_QBIT(lined, j, val); |
| else /* d == 2 */ |
| SET_DATA_DIBIT(lined, j, val); |
| } |
| break; |
| case 2: |
| for (j = 0; j < w; j++) { |
| val = GET_DATA_DIBIT(lines, j); |
| if (d == 8) |
| SET_DATA_BYTE(lined, j, val); |
| else /* d == 4 */ |
| SET_DATA_QBIT(lined, j, val); |
| } |
| case 4: |
| for (j = 0; j < w; j++) { |
| val = GET_DATA_DIBIT(lines, j); |
| SET_DATA_BYTE(lined, j, val); |
| } |
| break; |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Conversion for printing in PostScript * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvertForPSWrap() |
| * |
| * Input: pixs (1, 2, 4, 8, 16, 32 bpp) |
| * Return: pixd (1, 8, or 32 bpp), or null on error |
| * |
| * Notes: |
| * (1) For wrapping in PostScript, we convert pixs to |
| * 1 bpp, 8 bpp (gray) and 32 bpp (RGB color). |
| * (2) Colormaps are removed. For pixs with colormaps, the |
| * images are converted to either 8 bpp gray or 32 bpp |
| * RGB, depending on whether the colormap has color content. |
| * (3) Images without colormaps, that are not 1 bpp or 32 bpp, |
| * are converted to 8 bpp gray. |
| */ |
| PIX * |
| pixConvertForPSWrap(PIX *pixs) |
| { |
| l_int32 d; |
| PIX *pixd; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixConvertForPSWrap"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| |
| cmap = pixGetColormap(pixs); |
| d = pixGetDepth(pixs); |
| switch (d) |
| { |
| case 1: |
| case 32: |
| pixd = pixClone(pixs); |
| break; |
| case 2: |
| if (cmap) |
| pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); |
| else |
| pixd = pixConvert2To8(pixs, 0, 0x55, 0xaa, 0xff, FALSE); |
| break; |
| case 4: |
| if (cmap) |
| pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); |
| else |
| pixd = pixConvert4To8(pixs, FALSE); |
| break; |
| case 8: |
| pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); |
| break; |
| case 16: |
| pixd = pixConvert16To8(pixs, 1); |
| break; |
| default: |
| fprintf(stderr, "depth not in {1, 2, 4, 8, 16, 32}"); |
| return NULL; |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*---------------------------------------------------------------------------* |
| * Colorspace conversion between RGB and HSB * |
| *---------------------------------------------------------------------------*/ |
| /*! |
| * pixConvertRGBToHSV() |
| * |
| * Input: pixd (can be NULL; if not NULL, must == pixs) |
| * pixs |
| * Return: pixd always |
| * |
| * Notes: |
| * (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL. |
| * (2) The definition of our HSV space is given in convertRGBToHSV(). |
| * (3) The h, s and v values are stored in the same places as |
| * the r, g and b values, respectively. Here, they are explicitly |
| * placed in the 3 MS bytes in the pixel. |
| * (4) Normalizing to 1 and considering the r,g,b components, |
| * a simple way to understand the HSV space is: |
| * - v = max(r,g,b) |
| * - s = (max - min) / max |
| * - h ~ (mid - min) / (max - min) [apart from signs and constants] |
| * (5) Normalizing to 1, some properties of the HSV space are: |
| * - For gray values (r = g = b) along the continuum between |
| * black and white: |
| * s = 0 (becoming undefined as you approach black) |
| * h is undefined everywhere |
| * - Where one component is saturated and the others are zero: |
| * v = 1 |
| * s = 1 |
| * h = 0 (r = max), 1/3 (g = max), 2/3 (b = max) |
| * - Where two components are saturated and the other is zero: |
| * v = 1 |
| * s = 1 |
| * h = 1/2 (if r = 0), 5/6 (if g = 0), 1/6 (if b = 0) |
| */ |
| PIX * |
| pixConvertRGBToHSV(PIX *pixd, |
| PIX *pixs) |
| { |
| l_int32 w, h, d, wpl, i, j, rval, gval, bval, hval, sval, vval; |
| l_uint32 pixel; |
| l_uint32 *line, *data; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixConvertRGBToHSV"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); |
| if (pixd && pixd != pixs) |
| return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd); |
| |
| d = pixGetDepth(pixs); |
| cmap = pixGetColormap(pixs); |
| if (!cmap && d != 32) |
| return (PIX *)ERROR_PTR("not cmapped or rgb", procName, pixd); |
| |
| if (!pixd) |
| pixd = pixCopy(NULL, pixs); |
| |
| cmap = pixGetColormap(pixd); |
| if (cmap) { /* just convert the colormap */ |
| pixcmapConvertRGBToHSV(cmap); |
| return pixd; |
| } |
| |
| /* Convert RGB image */ |
| pixGetDimensions(pixd, &w, &h, NULL); |
| wpl = pixGetWpl(pixd); |
| data = pixGetData(pixd); |
| for (i = 0; i < h; i++) { |
| line = data + i * wpl; |
| for (j = 0; j < w; j++) { |
| pixel = line[j]; |
| extractRGBValues(pixel, &rval, &gval, &bval); |
| convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); |
| line[j] = (hval << 24) | (sval << 16) | (vval << 8); |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvertHSVToRGB() |
| * |
| * Input: pixd (can be NULL; if not NULL, must == pixs) |
| * pixs |
| * Return: pixd always |
| * |
| * Notes: |
| * (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL. |
| * (2) The user takes responsibility for making sure that pixs is |
| * in our HSV space. The definition of our HSV space is given |
| * in convertRGBToHSV(). |
| * (3) The h, s and v values are stored in the same places as |
| * the r, g and b values, respectively. Here, they are explicitly |
| * placed in the 3 MS bytes in the pixel. |
| */ |
| PIX * |
| pixConvertHSVToRGB(PIX *pixd, |
| PIX *pixs) |
| { |
| l_int32 w, h, d, wpl, i, j, rval, gval, bval, hval, sval, vval; |
| l_uint32 pixel; |
| l_uint32 *line, *data; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixConvertHSVToRGB"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); |
| if (pixd && pixd != pixs) |
| return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd); |
| |
| d = pixGetDepth(pixs); |
| cmap = pixGetColormap(pixs); |
| if (!cmap && d != 32) |
| return (PIX *)ERROR_PTR("not cmapped or hsv", procName, pixd); |
| |
| if (!pixd) |
| pixd = pixCopy(NULL, pixs); |
| |
| cmap = pixGetColormap(pixd); |
| if (cmap) { /* just convert the colormap */ |
| pixcmapConvertHSVToRGB(cmap); |
| return pixd; |
| } |
| |
| /* Convert HSV image */ |
| pixGetDimensions(pixd, &w, &h, NULL); |
| wpl = pixGetWpl(pixd); |
| data = pixGetData(pixd); |
| for (i = 0; i < h; i++) { |
| line = data + i * wpl; |
| for (j = 0; j < w; j++) { |
| pixel = line[j]; |
| hval = pixel >> 24; |
| sval = (pixel >> 16) & 0xff; |
| vval = (pixel >> 8) & 0xff; |
| convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); |
| composeRGBPixel(rval, gval, bval, line + j); |
| } |
| } |
| |
| return pixd; |
| } |
| |
| |
| /*! |
| * convertRGBToHSV() |
| * |
| * Input: rval, gval, bval (RGB input) |
| * &hval, &sval, &vval (<return> HSV values) |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) The range of returned values is: |
| * h [0 ... 239] |
| * s [0 ... 255] |
| * v [0 ... 255] |
| * (2) If r = g = b, the pixel is gray (s = 0), and we define h = 0. |
| * (3) h wraps around, so that h = 0 and h = 240 are equivalent |
| * in hue space. |
| * (4) h has the following correspondence to color: |
| * h = 0 magenta |
| * h = 40 red |
| * h = 80 yellow |
| * h = 120 green |
| * h = 160 cyan |
| * h = 200 blue |
| */ |
| l_int32 |
| convertRGBToHSV(l_int32 rval, |
| l_int32 gval, |
| l_int32 bval, |
| l_int32 *phval, |
| l_int32 *psval, |
| l_int32 *pvval) |
| { |
| l_int32 minrg, maxrg, min, max, delta; |
| l_float32 h; |
| |
| PROCNAME("convertRGBToHSV"); |
| |
| if (!phval || !psval || !pvval) |
| return ERROR_INT("&hval, &sval, &vval not all defined", procName, 1); |
| |
| minrg = L_MIN(rval, gval); |
| min = L_MIN(minrg, bval); |
| maxrg = L_MAX(rval, gval); |
| max = L_MAX(maxrg, bval); |
| delta = max - min; |
| |
| *pvval = max; |
| if (delta == 0) { /* gray; no chroma */ |
| *phval = 0; |
| *psval = 0; |
| } |
| else { |
| *psval = (l_int32)(255. * (l_float32)delta / (l_float32)max + 0.5); |
| if (rval == max) /* between magenta and yellow */ |
| h = (l_float32)(gval - bval) / (l_float32)delta; |
| else if (gval == max) /* between yellow and cyan */ |
| h = 2. + (l_float32)(bval - rval) / (l_float32)delta; |
| else /* between cyan and magenta */ |
| h = 4. + (l_float32)(rval - gval) / (l_float32)delta; |
| h *= 40.0; |
| if (h < 0.0) |
| h += 240.0; |
| if (h >= 239.5) |
| h = 0.0; |
| *phval = (l_int32)(h + 0.5); |
| } |
| |
| return 0; |
| } |
| |
| |
| /*! |
| * convertHSVToRGB() |
| * |
| * Input: hval, sval, vval |
| * &rval, &gval, &bval (<return> RGB values) |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) See convertRGBToHSV() for valid input range of HSV values |
| * and their interpretation in color space. |
| */ |
| l_int32 |
| convertHSVToRGB(l_int32 hval, |
| l_int32 sval, |
| l_int32 vval, |
| l_int32 *prval, |
| l_int32 *pgval, |
| l_int32 *pbval) |
| { |
| l_int32 i, x, y, z; |
| l_float32 h, f, s; |
| |
| PROCNAME("convertHSVToRGB"); |
| |
| if (!prval || !pgval || !pbval) |
| return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1); |
| |
| if (sval == 0) { /* gray */ |
| *prval = vval; |
| *pgval = vval; |
| *pbval = vval; |
| } |
| else { |
| if (hval < 0 || hval > 240) |
| return ERROR_INT("invalid hval", procName, 1); |
| if (hval == 240) |
| hval = 0; |
| h = (l_float32)hval / 40.; |
| i = (l_int32)h; |
| f = h - i; |
| s = (l_float32)sval / 255.; |
| x = (l_int32)(vval * (1. - s) + 0.5); |
| y = (l_int32)(vval * (1. - s * f) + 0.5); |
| z = (l_int32)(vval * (1. - s * (1. - f)) + 0.5); |
| switch (i) |
| { |
| case 0: |
| *prval = vval; |
| *pgval = z; |
| *pbval = x; |
| break; |
| case 1: |
| *prval = y; |
| *pgval = vval; |
| *pbval = x; |
| break; |
| case 2: |
| *prval = x; |
| *pgval = vval; |
| *pbval = z; |
| break; |
| case 3: |
| *prval = x; |
| *pgval = y; |
| *pbval = vval; |
| break; |
| case 4: |
| *prval = z; |
| *pgval = x; |
| *pbval = vval; |
| break; |
| case 5: |
| *prval = vval; |
| *pgval = x; |
| *pbval = y; |
| break; |
| default: /* none possible */ |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| /*! |
| * pixConvertRGBToHue() |
| * |
| * Input: pixs (32 bpp RGB or 8 bpp with colormap) |
| * Return: pixd (8 bpp hue of HSV), or null on error |
| * |
| * Notes: |
| * (1) The conversion to HSV hue is in-lined here. |
| * (2) If there is a colormap, it is removed. |
| * (3) If you just want the hue component, this does it |
| * at about 10 Mpixels/sec/GHz, which is about |
| * 2x faster than using pixConvertRGBToHSV() |
| */ |
| PIX * |
| pixConvertRGBToHue(PIX *pixs) |
| { |
| l_int32 w, h, d, wplt, wpld; |
| l_int32 i, j, rval, gval, bval, hval, minrg, min, maxrg, max, delta; |
| l_float32 fh; |
| l_uint32 pixel; |
| l_uint32 *linet, *lined, *datat, *datad; |
| PIX *pixt, *pixd; |
| |
| PROCNAME("pixConvertRGBToHue"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| |
| pixGetDimensions(pixs, &w, &h, &d); |
| if (d != 32 && !pixGetColormap(pixs)) |
| return (PIX *)ERROR_PTR("not cmapped or rgb", procName, NULL); |
| pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); |
| |
| /* Convert RGB image */ |
| pixd = pixCreate(w, h, 8); |
| pixCopyResolution(pixd, pixs); |
| wplt = pixGetWpl(pixt); |
| datat = pixGetData(pixt); |
| wpld = pixGetWpl(pixd); |
| datad = pixGetData(pixd); |
| for (i = 0; i < h; i++) { |
| linet = datat + i * wplt; |
| lined = datad + i * wpld; |
| for (j = 0; j < w; j++) { |
| pixel = linet[j]; |
| extractRGBValues(pixel, &rval, &gval, &bval); |
| minrg = L_MIN(rval, gval); |
| min = L_MIN(minrg, bval); |
| maxrg = L_MAX(rval, gval); |
| max = L_MAX(maxrg, bval); |
| delta = max - min; |
| if (delta == 0) /* gray; no chroma */ |
| hval = 0; |
| else { |
| if (rval == max) /* between magenta and yellow */ |
| fh = (l_float32)(gval - bval) / (l_float32)delta; |
| else if (gval == max) /* between yellow and cyan */ |
| fh = 2. + (l_float32)(bval - rval) / (l_float32)delta; |
| else /* between cyan and magenta */ |
| fh = 4. + (l_float32)(rval - gval) / (l_float32)delta; |
| fh *= 40.0; |
| if (fh < 0.0) |
| fh += 240.0; |
| hval = (l_int32)(fh + 0.5); |
| } |
| SET_DATA_BYTE(lined, j, hval); |
| } |
| } |
| pixDestroy(&pixt); |
| |
| return pixd; |
| } |
| |
| |
| |
| /*! |
| * pixConvertRGBToSaturation() |
| * |
| * Input: pixs (32 bpp RGB or 8 bpp with colormap) |
| * Return: pixd (8 bpp sat of HSV), or null on error |
| * |
| * Notes: |
| * (1) The conversion to HSV sat is in-lined here. |
| * (2) If there is a colormap, it is removed. |
| * (3) If you just want the saturation component, this does it |
| * at about 12 Mpixels/sec/GHz. |
| */ |
| PIX * |
| pixConvertRGBToSaturation(PIX *pixs) |
| { |
| l_int32 w, h, d, wplt, wpld; |
| l_int32 i, j, rval, gval, bval, sval, minrg, min, maxrg, max, delta; |
| l_uint32 pixel; |
| l_uint32 *linet, *lined, *datat, *datad; |
| PIX *pixt, *pixd; |
| |
| PROCNAME("pixConvertRGBToSaturation"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| |
| pixGetDimensions(pixs, &w, &h, &d); |
| if (d != 32 && !pixGetColormap(pixs)) |
| return (PIX *)ERROR_PTR("not cmapped or rgb", procName, NULL); |
| pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); |
| |
| /* Convert RGB image */ |
| pixd = pixCreate(w, h, 8); |
| pixCopyResolution(pixd, pixs); |
| wplt = pixGetWpl(pixt); |
| datat = pixGetData(pixt); |
| wpld = pixGetWpl(pixd); |
| datad = pixGetData(pixd); |
| for (i = 0; i < h; i++) { |
| linet = datat + i * wplt; |
| lined = datad + i * wpld; |
| for (j = 0; j < w; j++) { |
| pixel = linet[j]; |
| extractRGBValues(pixel, &rval, &gval, &bval); |
| minrg = L_MIN(rval, gval); |
| min = L_MIN(minrg, bval); |
| maxrg = L_MAX(rval, gval); |
| max = L_MAX(maxrg, bval); |
| delta = max - min; |
| if (delta == 0) /* gray; no chroma */ |
| sval = 0; |
| else |
| sval = (l_int32)(255. * |
| (l_float32)delta / (l_float32)max + 0.5); |
| SET_DATA_BYTE(lined, j, sval); |
| } |
| } |
| |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| |
| |
| /*! |
| * pixConvertRGBToValue() |
| * |
| * Input: pixs (32 bpp RGB or 8 bpp with colormap) |
| * Return: pixd (8 bpp max component intensity of HSV), or null on error |
| * |
| * Notes: |
| * (1) The conversion to HSV sat is in-lined here. |
| * (2) If there is a colormap, it is removed. |
| * (3) If you just want the value component, this does it |
| * at about 35 Mpixels/sec/GHz. |
| */ |
| PIX * |
| pixConvertRGBToValue(PIX *pixs) |
| { |
| l_int32 w, h, d, wplt, wpld; |
| l_int32 i, j, rval, gval, bval, maxrg, max; |
| l_uint32 pixel; |
| l_uint32 *linet, *lined, *datat, *datad; |
| PIX *pixt, *pixd; |
| |
| PROCNAME("pixConvertRGBToValue"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| |
| pixGetDimensions(pixs, &w, &h, &d); |
| if (d != 32 && !pixGetColormap(pixs)) |
| return (PIX *)ERROR_PTR("not cmapped or rgb", procName, NULL); |
| pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); |
| |
| /* Convert RGB image */ |
| pixd = pixCreate(w, h, 8); |
| pixCopyResolution(pixd, pixs); |
| wplt = pixGetWpl(pixt); |
| datat = pixGetData(pixt); |
| wpld = pixGetWpl(pixd); |
| datad = pixGetData(pixd); |
| for (i = 0; i < h; i++) { |
| linet = datat + i * wplt; |
| lined = datad + i * wpld; |
| for (j = 0; j < w; j++) { |
| pixel = linet[j]; |
| extractRGBValues(pixel, &rval, &gval, &bval); |
| maxrg = L_MAX(rval, gval); |
| max = L_MAX(maxrg, bval); |
| SET_DATA_BYTE(lined, j, max); |
| } |
| } |
| |
| pixDestroy(&pixt); |
| return pixd; |
| } |
| |
| |
| |
| |
| |