diff options
author | Kirill Volinsky <mataes2007@gmail.com> | 2014-12-13 20:28:24 +0000 |
---|---|---|
committer | Kirill Volinsky <mataes2007@gmail.com> | 2014-12-13 20:28:24 +0000 |
commit | d2d798d0f11abcbf141db69869e76e8d123ec1eb (patch) | |
tree | cee8645964030b4b80231952885911f724850d12 /plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp | |
parent | a400ebf572dcad9b43d1b641b1742814f8b5e8a5 (diff) |
FreeImage updated to 3.16
git-svn-id: http://svn.miranda-ng.org/main/trunk@11379 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp')
-rw-r--r-- | plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp | 958 |
1 files changed, 479 insertions, 479 deletions
diff --git a/plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp b/plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp index 66869b2806..6b103a08c1 100644 --- a/plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp +++ b/plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp @@ -1,479 +1,479 @@ -// ==========================================================
-// High Dynamic Range bitmap conversion routines
-//
-// Design and implementation by
-// - Hervé Drolon (drolon@infonie.fr)
-// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
-//
-// This file is part of FreeImage 3
-//
-// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
-// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
-// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
-// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
-// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
-// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
-// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
-// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
-// THIS DISCLAIMER.
-//
-// Use at your own risk!
-// ==========================================================
-
-#include "FreeImage.h"
-#include "Utilities.h"
-#include "ToneMapping.h"
-
-// ----------------------------------------------------------
-// Convert RGB to and from Yxy, same as in Reinhard et al. SIGGRAPH 2002
-// References :
-// [1] Radiance Home Page [Online] http://radsite.lbl.gov/radiance/HOME.html
-// [2] E. Reinhard, M. Stark, P. Shirley, and J. Ferwerda,
-// Photographic Tone Reproduction for Digital Images, ACM Transactions on Graphics,
-// 21(3):267-276, 2002 (Proceedings of SIGGRAPH 2002).
-// [3] J. Tumblin and H.E. Rushmeier,
-// Tone Reproduction for Realistic Images. IEEE Computer Graphics and Applications,
-// 13(6):42-48, 1993.
-// ----------------------------------------------------------
-
-/**
-nominal CRT primaries
-*/
-/*
-static const float CIE_x_r = 0.640F;
-static const float CIE_y_r = 0.330F;
-static const float CIE_x_g = 0.290F;
-static const float CIE_y_g = 0.600F;
-static const float CIE_x_b = 0.150F;
-static const float CIE_y_b = 0.060F;
-static const float CIE_x_w = 0.3333F; // use true white
-static const float CIE_y_w = 0.3333F;
-*/
-/**
-sRGB primaries
-*/
-static const float CIE_x_r = 0.640F;
-static const float CIE_y_r = 0.330F;
-static const float CIE_x_g = 0.300F;
-static const float CIE_y_g = 0.600F;
-static const float CIE_x_b = 0.150F;
-static const float CIE_y_b = 0.060F;
-static const float CIE_x_w = 0.3127F; // Illuminant D65
-static const float CIE_y_w = 0.3290F;
-
-static const float CIE_D = ( CIE_x_r*(CIE_y_g - CIE_y_b) + CIE_x_g*(CIE_y_b - CIE_y_r) + CIE_x_b*(CIE_y_r - CIE_y_g) );
-static const float CIE_C_rD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_g - CIE_y_b) - CIE_y_w*(CIE_x_g - CIE_x_b) + CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) );
-static const float CIE_C_gD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_b - CIE_y_r) - CIE_y_w*(CIE_x_b - CIE_x_r) - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) );
-static const float CIE_C_bD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_r - CIE_y_g) - CIE_y_w*(CIE_x_r - CIE_x_g) + CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) );
-
-/**
-RGB to XYZ (no white balance)
-*/
-static const float RGB2XYZ[3][3] = {
- { CIE_x_r*CIE_C_rD / CIE_D,
- CIE_x_g*CIE_C_gD / CIE_D,
- CIE_x_b*CIE_C_bD / CIE_D
- },
- { CIE_y_r*CIE_C_rD / CIE_D,
- CIE_y_g*CIE_C_gD / CIE_D,
- CIE_y_b*CIE_C_bD / CIE_D
- },
- { (1 - CIE_x_r-CIE_y_r)*CIE_C_rD / CIE_D,
- (1 - CIE_x_g-CIE_y_g)*CIE_C_gD / CIE_D,
- (1 - CIE_x_b-CIE_y_b)*CIE_C_bD / CIE_D
- }
-};
-
-/**
-XYZ to RGB (no white balance)
-*/
-static const float XYZ2RGB[3][3] = {
- {(CIE_y_g - CIE_y_b - CIE_x_b*CIE_y_g + CIE_y_b*CIE_x_g) / CIE_C_rD,
- (CIE_x_b - CIE_x_g - CIE_x_b*CIE_y_g + CIE_x_g*CIE_y_b) / CIE_C_rD,
- (CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) / CIE_C_rD
- },
- {(CIE_y_b - CIE_y_r - CIE_y_b*CIE_x_r + CIE_y_r*CIE_x_b) / CIE_C_gD,
- (CIE_x_r - CIE_x_b - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) / CIE_C_gD,
- (CIE_x_b*CIE_y_r - CIE_x_r*CIE_y_b) / CIE_C_gD
- },
- {(CIE_y_r - CIE_y_g - CIE_y_r*CIE_x_g + CIE_y_g*CIE_x_r) / CIE_C_bD,
- (CIE_x_g - CIE_x_r - CIE_x_g*CIE_y_r + CIE_x_r*CIE_y_g) / CIE_C_bD,
- (CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) / CIE_C_bD
- }
-};
-
-/**
-This gives approximately the following matrices :
-
-static const float RGB2XYZ[3][3] = {
- { 0.41239083F, 0.35758433F, 0.18048081F },
- { 0.21263903F, 0.71516865F, 0.072192319F },
- { 0.019330820F, 0.11919473F, 0.95053220F }
-};
-static const float XYZ2RGB[3][3] = {
- { 3.2409699F, -1.5373832F, -0.49861079F },
- { -0.96924376F, 1.8759676F, 0.041555084F },
- { 0.055630036F, -0.20397687F, 1.0569715F }
-};
-*/
-
-// ----------------------------------------------------------
-
-static const float EPSILON = 1e-06F;
-static const float INF = 1e+10F;
-
-/**
-Convert in-place floating point RGB data to Yxy.<br>
-On output, pixel->red == Y, pixel->green == x, pixel->blue == y
-@param dib Input RGBF / Output Yxy image
-@return Returns TRUE if successful, returns FALSE otherwise
-*/
-BOOL
-ConvertInPlaceRGBFToYxy(FIBITMAP *dib) {
- float result[3];
-
- if(FreeImage_GetImageType(dib) != FIT_RGBF)
- return FALSE;
-
- const unsigned width = FreeImage_GetWidth(dib);
- const unsigned height = FreeImage_GetHeight(dib);
- const unsigned pitch = FreeImage_GetPitch(dib);
-
- BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
- for(unsigned y = 0; y < height; y++) {
- FIRGBF *pixel = (FIRGBF*)bits;
- for(unsigned x = 0; x < width; x++) {
- result[0] = result[1] = result[2] = 0;
- for (int i = 0; i < 3; i++) {
- result[i] += RGB2XYZ[i][0] * pixel[x].red;
- result[i] += RGB2XYZ[i][1] * pixel[x].green;
- result[i] += RGB2XYZ[i][2] * pixel[x].blue;
- }
- const float W = result[0] + result[1] + result[2];
- const float Y = result[1];
- if(W > 0) {
- pixel[x].red = Y; // Y
- pixel[x].green = result[0] / W; // x
- pixel[x].blue = result[1] / W; // y
- } else {
- pixel[x].red = pixel[x].green = pixel[x].blue = 0;
- }
- }
- // next line
- bits += pitch;
- }
-
- return TRUE;
-}
-
-/**
-Convert in-place Yxy image to floating point RGB data.<br>
-On input, pixel->red == Y, pixel->green == x, pixel->blue == y
-@param dib Input Yxy / Output RGBF image
-@return Returns TRUE if successful, returns FALSE otherwise
-*/
-BOOL
-ConvertInPlaceYxyToRGBF(FIBITMAP *dib) {
- float result[3];
- float X, Y, Z;
-
- if(FreeImage_GetImageType(dib) != FIT_RGBF)
- return FALSE;
-
- const unsigned width = FreeImage_GetWidth(dib);
- const unsigned height = FreeImage_GetHeight(dib);
- const unsigned pitch = FreeImage_GetPitch(dib);
-
- BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
- for(unsigned y = 0; y < height; y++) {
- FIRGBF *pixel = (FIRGBF*)bits;
- for(unsigned x = 0; x < width; x++) {
- Y = pixel[x].red; // Y
- result[1] = pixel[x].green; // x
- result[2] = pixel[x].blue; // y
- if ((Y > EPSILON) && (result[1] > EPSILON) && (result[2] > EPSILON)) {
- X = (result[1] * Y) / result[2];
- Z = (X / result[1]) - X - Y;
- } else {
- X = Z = EPSILON;
- }
- pixel[x].red = X;
- pixel[x].green = Y;
- pixel[x].blue = Z;
- result[0] = result[1] = result[2] = 0;
- for (int i = 0; i < 3; i++) {
- result[i] += XYZ2RGB[i][0] * pixel[x].red;
- result[i] += XYZ2RGB[i][1] * pixel[x].green;
- result[i] += XYZ2RGB[i][2] * pixel[x].blue;
- }
- pixel[x].red = result[0]; // R
- pixel[x].green = result[1]; // G
- pixel[x].blue = result[2]; // B
- }
- // next line
- bits += pitch;
- }
-
- return TRUE;
-}
-
-/**
-Get the maximum, minimum and average luminance.<br>
-On input, pixel->red == Y, pixel->green == x, pixel->blue == y
-@param Yxy Source Yxy image to analyze
-@param maxLum Maximum luminance
-@param minLum Minimum luminance
-@param worldLum Average luminance (world adaptation luminance)
-@return Returns TRUE if successful, returns FALSE otherwise
-*/
-BOOL
-LuminanceFromYxy(FIBITMAP *Yxy, float *maxLum, float *minLum, float *worldLum) {
- if(FreeImage_GetImageType(Yxy) != FIT_RGBF)
- return FALSE;
-
- const unsigned width = FreeImage_GetWidth(Yxy);
- const unsigned height = FreeImage_GetHeight(Yxy);
- const unsigned pitch = FreeImage_GetPitch(Yxy);
-
- float max_lum = 0, min_lum = 0;
- double sum = 0;
-
- BYTE *bits = (BYTE*)FreeImage_GetBits(Yxy);
- for(unsigned y = 0; y < height; y++) {
- const FIRGBF *pixel = (FIRGBF*)bits;
- for(unsigned x = 0; x < width; x++) {
- const float Y = pixel[x].red;
- max_lum = (max_lum < Y) ? Y : max_lum; // max Luminance in the scene
- min_lum = (min_lum < Y) ? min_lum : Y; // min Luminance in the scene
- sum += log(2.3e-5F + Y); // contrast constant in Tumblin paper
- }
- // next line
- bits += pitch;
- }
- // maximum luminance
- *maxLum = max_lum;
- // minimum luminance
- *minLum = min_lum;
- // average log luminance
- double avgLogLum = (sum / (width * height));
- // world adaptation luminance
- *worldLum = (float)exp(avgLogLum);
-
- return TRUE;
-}
-
-/**
-Clamp RGBF image highest values to display white,
-then convert to 24-bit RGB
-*/
-FIBITMAP*
-ClampConvertRGBFTo24(FIBITMAP *src) {
- if(FreeImage_GetImageType(src) != FIT_RGBF)
- return FALSE;
-
- const unsigned width = FreeImage_GetWidth(src);
- const unsigned height = FreeImage_GetHeight(src);
-
- FIBITMAP *dst = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
- if(!dst) return NULL;
-
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const unsigned dst_pitch = FreeImage_GetPitch(dst);
-
- BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
- BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
-
- for(unsigned y = 0; y < height; y++) {
- const FIRGBF *src_pixel = (FIRGBF*)src_bits;
- BYTE *dst_pixel = (BYTE*)dst_bits;
- for(unsigned x = 0; x < width; x++) {
- const float red = (src_pixel[x].red > 1) ? 1 : src_pixel[x].red;
- const float green = (src_pixel[x].green > 1) ? 1 : src_pixel[x].green;
- const float blue = (src_pixel[x].blue > 1) ? 1 : src_pixel[x].blue;
-
- dst_pixel[FI_RGBA_RED] = (BYTE)(255.0F * red + 0.5F);
- dst_pixel[FI_RGBA_GREEN] = (BYTE)(255.0F * green + 0.5F);
- dst_pixel[FI_RGBA_BLUE] = (BYTE)(255.0F * blue + 0.5F);
- dst_pixel += 3;
- }
- src_bits += src_pitch;
- dst_bits += dst_pitch;
- }
-
- return dst;
-}
-
-/**
-Extract the luminance channel L from a RGBF image.
-Luminance is calculated from the sRGB model (RGB2XYZ matrix)
-using a D65 white point :
-L = ( 0.2126 * r ) + ( 0.7152 * g ) + ( 0.0722 * b )
-Reference :
-A Standard Default Color Space for the Internet - sRGB.
-[online] http://www.w3.org/Graphics/Color/sRGB
-*/
-FIBITMAP*
-ConvertRGBFToY(FIBITMAP *src) {
- if(FreeImage_GetImageType(src) != FIT_RGBF)
- return FALSE;
-
- const unsigned width = FreeImage_GetWidth(src);
- const unsigned height = FreeImage_GetHeight(src);
-
- FIBITMAP *dst = FreeImage_AllocateT(FIT_FLOAT, width, height);
- if(!dst) return NULL;
-
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const unsigned dst_pitch = FreeImage_GetPitch(dst);
-
-
- BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
- BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
-
- for(unsigned y = 0; y < height; y++) {
- const FIRGBF *src_pixel = (FIRGBF*)src_bits;
- float *dst_pixel = (float*)dst_bits;
- for(unsigned x = 0; x < width; x++) {
- const float L = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue);
- dst_pixel[x] = (L > 0) ? L : 0;
- }
- // next line
- src_bits += src_pitch;
- dst_bits += dst_pitch;
- }
-
- return dst;
-}
-
-/**
-Get the maximum, minimum, average luminance and log average luminance from a Y image
-@param dib Source Y image to analyze
-@param maxLum Maximum luminance
-@param minLum Minimum luminance
-@param Lav Average luminance
-@param Llav Log average luminance (also known as 'world adaptation luminance')
-@return Returns TRUE if successful, returns FALSE otherwise
-@see ConvertRGBFToY, FreeImage_TmoReinhard05Ex
-*/
-BOOL
-LuminanceFromY(FIBITMAP *dib, float *maxLum, float *minLum, float *Lav, float *Llav) {
- if(FreeImage_GetImageType(dib) != FIT_FLOAT)
- return FALSE;
-
- unsigned width = FreeImage_GetWidth(dib);
- unsigned height = FreeImage_GetHeight(dib);
- unsigned pitch = FreeImage_GetPitch(dib);
-
- float max_lum = -1e20F, min_lum = 1e20F;
- double sumLum = 0, sumLogLum = 0;
-
- BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
- for(unsigned y = 0; y < height; y++) {
- const float *pixel = (float*)bits;
- for(unsigned x = 0; x < width; x++) {
- const float Y = pixel[x];
- max_lum = (max_lum < Y) ? Y : max_lum; // max Luminance in the scene
- min_lum = ((Y > 0) && (min_lum < Y)) ? min_lum : Y; // min Luminance in the scene
- sumLum += Y; // average luminance
- sumLogLum += log(2.3e-5F + Y); // contrast constant in Tumblin paper
- }
- // next line
- bits += pitch;
- }
-
- // maximum luminance
- *maxLum = max_lum;
- // minimum luminance
- *minLum = min_lum;
- // average luminance
- *Lav = (float)(sumLum / (width * height));
- // average log luminance, a.k.a. world adaptation luminance
- *Llav = (float)exp(sumLogLum / (width * height));
-
- return TRUE;
-}
-// --------------------------------------------------------------------------
-
-static void findMaxMinPercentile(FIBITMAP *Y, float minPrct, float *minLum, float maxPrct, float *maxLum) {
- int x, y;
- int width = FreeImage_GetWidth(Y);
- int height = FreeImage_GetHeight(Y);
- int pitch = FreeImage_GetPitch(Y);
-
- std::vector<float> vY(width * height);
-
- BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
- for(y = 0; y < height; y++) {
- float *pixel = (float*)bits;
- for(x = 0; x < width; x++) {
- if(pixel[x] != 0) {
- vY.push_back(pixel[x]);
- }
- }
- bits += pitch;
- }
-
- std::sort(vY.begin(), vY.end());
-
- *minLum = vY.at( int(minPrct * vY.size()) );
- *maxLum = vY.at( int(maxPrct * vY.size()) );
-}
-
-/**
-Clipping function<br>
-Remove any extremely bright and/or extremely dark pixels
-and normalize between 0 and 1.
-@param Y Input/Output image
-@param minPrct Minimum percentile
-@param maxPrct Maximum percentile
-*/
-void
-NormalizeY(FIBITMAP *Y, float minPrct, float maxPrct) {
- int x, y;
- float maxLum, minLum;
-
- if(minPrct > maxPrct) {
- // swap values
- float t = minPrct; minPrct = maxPrct; maxPrct = t;
- }
- if(minPrct < 0) minPrct = 0;
- if(maxPrct > 1) maxPrct = 1;
-
- int width = FreeImage_GetWidth(Y);
- int height = FreeImage_GetHeight(Y);
- int pitch = FreeImage_GetPitch(Y);
-
- // find max & min luminance values
- if((minPrct > 0) || (maxPrct < 1)) {
- maxLum = 0, minLum = 0;
- findMaxMinPercentile(Y, minPrct, &minLum, maxPrct, &maxLum);
- } else {
- maxLum = -1e20F, minLum = 1e20F;
- BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
- for(y = 0; y < height; y++) {
- const float *pixel = (float*)bits;
- for(x = 0; x < width; x++) {
- const float value = pixel[x];
- maxLum = (maxLum < value) ? value : maxLum; // max Luminance in the scene
- minLum = (minLum < value) ? minLum : value; // min Luminance in the scene
- }
- // next line
- bits += pitch;
- }
- }
- if(maxLum == minLum) return;
-
- // normalize to range 0..1
- const float divider = maxLum - minLum;
- BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
- for(y = 0; y < height; y++) {
- float *pixel = (float*)bits;
- for(x = 0; x < width; x++) {
- pixel[x] = (pixel[x] - minLum) / divider;
- if(pixel[x] <= 0) pixel[x] = EPSILON;
- if(pixel[x] > 1) pixel[x] = 1;
- }
- // next line
- bits += pitch;
- }
-}
+// ========================================================== +// High Dynamic Range bitmap conversion routines +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// - Mihail Naydenov (mnaydenov@users.sourceforge.net) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" +#include "ToneMapping.h" + +// ---------------------------------------------------------- +// Convert RGB to and from Yxy, same as in Reinhard et al. SIGGRAPH 2002 +// References : +// [1] Radiance Home Page [Online] http://radsite.lbl.gov/radiance/HOME.html +// [2] E. Reinhard, M. Stark, P. Shirley, and J. Ferwerda, +// Photographic Tone Reproduction for Digital Images, ACM Transactions on Graphics, +// 21(3):267-276, 2002 (Proceedings of SIGGRAPH 2002). +// [3] J. Tumblin and H.E. Rushmeier, +// Tone Reproduction for Realistic Images. IEEE Computer Graphics and Applications, +// 13(6):42-48, 1993. +// ---------------------------------------------------------- + +/** +nominal CRT primaries +*/ +/* +static const float CIE_x_r = 0.640F; +static const float CIE_y_r = 0.330F; +static const float CIE_x_g = 0.290F; +static const float CIE_y_g = 0.600F; +static const float CIE_x_b = 0.150F; +static const float CIE_y_b = 0.060F; +static const float CIE_x_w = 0.3333F; // use true white +static const float CIE_y_w = 0.3333F; +*/ +/** +sRGB primaries +*/ +static const float CIE_x_r = 0.640F; +static const float CIE_y_r = 0.330F; +static const float CIE_x_g = 0.300F; +static const float CIE_y_g = 0.600F; +static const float CIE_x_b = 0.150F; +static const float CIE_y_b = 0.060F; +static const float CIE_x_w = 0.3127F; // Illuminant D65 +static const float CIE_y_w = 0.3290F; + +static const float CIE_D = ( CIE_x_r*(CIE_y_g - CIE_y_b) + CIE_x_g*(CIE_y_b - CIE_y_r) + CIE_x_b*(CIE_y_r - CIE_y_g) ); +static const float CIE_C_rD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_g - CIE_y_b) - CIE_y_w*(CIE_x_g - CIE_x_b) + CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) ); +static const float CIE_C_gD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_b - CIE_y_r) - CIE_y_w*(CIE_x_b - CIE_x_r) - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) ); +static const float CIE_C_bD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_r - CIE_y_g) - CIE_y_w*(CIE_x_r - CIE_x_g) + CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) ); + +/** +RGB to XYZ (no white balance) +*/ +static const float RGB2XYZ[3][3] = { + { CIE_x_r*CIE_C_rD / CIE_D, + CIE_x_g*CIE_C_gD / CIE_D, + CIE_x_b*CIE_C_bD / CIE_D + }, + { CIE_y_r*CIE_C_rD / CIE_D, + CIE_y_g*CIE_C_gD / CIE_D, + CIE_y_b*CIE_C_bD / CIE_D + }, + { (1 - CIE_x_r-CIE_y_r)*CIE_C_rD / CIE_D, + (1 - CIE_x_g-CIE_y_g)*CIE_C_gD / CIE_D, + (1 - CIE_x_b-CIE_y_b)*CIE_C_bD / CIE_D + } +}; + +/** +XYZ to RGB (no white balance) +*/ +static const float XYZ2RGB[3][3] = { + {(CIE_y_g - CIE_y_b - CIE_x_b*CIE_y_g + CIE_y_b*CIE_x_g) / CIE_C_rD, + (CIE_x_b - CIE_x_g - CIE_x_b*CIE_y_g + CIE_x_g*CIE_y_b) / CIE_C_rD, + (CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) / CIE_C_rD + }, + {(CIE_y_b - CIE_y_r - CIE_y_b*CIE_x_r + CIE_y_r*CIE_x_b) / CIE_C_gD, + (CIE_x_r - CIE_x_b - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) / CIE_C_gD, + (CIE_x_b*CIE_y_r - CIE_x_r*CIE_y_b) / CIE_C_gD + }, + {(CIE_y_r - CIE_y_g - CIE_y_r*CIE_x_g + CIE_y_g*CIE_x_r) / CIE_C_bD, + (CIE_x_g - CIE_x_r - CIE_x_g*CIE_y_r + CIE_x_r*CIE_y_g) / CIE_C_bD, + (CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) / CIE_C_bD + } +}; + +/** +This gives approximately the following matrices : + +static const float RGB2XYZ[3][3] = { + { 0.41239083F, 0.35758433F, 0.18048081F }, + { 0.21263903F, 0.71516865F, 0.072192319F }, + { 0.019330820F, 0.11919473F, 0.95053220F } +}; +static const float XYZ2RGB[3][3] = { + { 3.2409699F, -1.5373832F, -0.49861079F }, + { -0.96924376F, 1.8759676F, 0.041555084F }, + { 0.055630036F, -0.20397687F, 1.0569715F } +}; +*/ + +// ---------------------------------------------------------- + +static const float EPSILON = 1e-06F; +static const float INF = 1e+10F; + +/** +Convert in-place floating point RGB data to Yxy.<br> +On output, pixel->red == Y, pixel->green == x, pixel->blue == y +@param dib Input RGBF / Output Yxy image +@return Returns TRUE if successful, returns FALSE otherwise +*/ +BOOL +ConvertInPlaceRGBFToYxy(FIBITMAP *dib) { + float result[3]; + + if(FreeImage_GetImageType(dib) != FIT_RGBF) + return FALSE; + + const unsigned width = FreeImage_GetWidth(dib); + const unsigned height = FreeImage_GetHeight(dib); + const unsigned pitch = FreeImage_GetPitch(dib); + + BYTE *bits = (BYTE*)FreeImage_GetBits(dib); + for(unsigned y = 0; y < height; y++) { + FIRGBF *pixel = (FIRGBF*)bits; + for(unsigned x = 0; x < width; x++) { + result[0] = result[1] = result[2] = 0; + for (int i = 0; i < 3; i++) { + result[i] += RGB2XYZ[i][0] * pixel[x].red; + result[i] += RGB2XYZ[i][1] * pixel[x].green; + result[i] += RGB2XYZ[i][2] * pixel[x].blue; + } + const float W = result[0] + result[1] + result[2]; + const float Y = result[1]; + if(W > 0) { + pixel[x].red = Y; // Y + pixel[x].green = result[0] / W; // x + pixel[x].blue = result[1] / W; // y + } else { + pixel[x].red = pixel[x].green = pixel[x].blue = 0; + } + } + // next line + bits += pitch; + } + + return TRUE; +} + +/** +Convert in-place Yxy image to floating point RGB data.<br> +On input, pixel->red == Y, pixel->green == x, pixel->blue == y +@param dib Input Yxy / Output RGBF image +@return Returns TRUE if successful, returns FALSE otherwise +*/ +BOOL +ConvertInPlaceYxyToRGBF(FIBITMAP *dib) { + float result[3]; + float X, Y, Z; + + if(FreeImage_GetImageType(dib) != FIT_RGBF) + return FALSE; + + const unsigned width = FreeImage_GetWidth(dib); + const unsigned height = FreeImage_GetHeight(dib); + const unsigned pitch = FreeImage_GetPitch(dib); + + BYTE *bits = (BYTE*)FreeImage_GetBits(dib); + for(unsigned y = 0; y < height; y++) { + FIRGBF *pixel = (FIRGBF*)bits; + for(unsigned x = 0; x < width; x++) { + Y = pixel[x].red; // Y + result[1] = pixel[x].green; // x + result[2] = pixel[x].blue; // y + if ((Y > EPSILON) && (result[1] > EPSILON) && (result[2] > EPSILON)) { + X = (result[1] * Y) / result[2]; + Z = (X / result[1]) - X - Y; + } else { + X = Z = EPSILON; + } + pixel[x].red = X; + pixel[x].green = Y; + pixel[x].blue = Z; + result[0] = result[1] = result[2] = 0; + for (int i = 0; i < 3; i++) { + result[i] += XYZ2RGB[i][0] * pixel[x].red; + result[i] += XYZ2RGB[i][1] * pixel[x].green; + result[i] += XYZ2RGB[i][2] * pixel[x].blue; + } + pixel[x].red = result[0]; // R + pixel[x].green = result[1]; // G + pixel[x].blue = result[2]; // B + } + // next line + bits += pitch; + } + + return TRUE; +} + +/** +Get the maximum, minimum and average luminance.<br> +On input, pixel->red == Y, pixel->green == x, pixel->blue == y +@param Yxy Source Yxy image to analyze +@param maxLum Maximum luminance +@param minLum Minimum luminance +@param worldLum Average luminance (world adaptation luminance) +@return Returns TRUE if successful, returns FALSE otherwise +*/ +BOOL +LuminanceFromYxy(FIBITMAP *Yxy, float *maxLum, float *minLum, float *worldLum) { + if(FreeImage_GetImageType(Yxy) != FIT_RGBF) + return FALSE; + + const unsigned width = FreeImage_GetWidth(Yxy); + const unsigned height = FreeImage_GetHeight(Yxy); + const unsigned pitch = FreeImage_GetPitch(Yxy); + + float max_lum = 0, min_lum = 0; + double sum = 0; + + BYTE *bits = (BYTE*)FreeImage_GetBits(Yxy); + for(unsigned y = 0; y < height; y++) { + const FIRGBF *pixel = (FIRGBF*)bits; + for(unsigned x = 0; x < width; x++) { + const float Y = MAX(0.0F, pixel[x].red);// avoid negative values + max_lum = (max_lum < Y) ? Y : max_lum; // max Luminance in the scene + min_lum = (min_lum < Y) ? min_lum : Y; // min Luminance in the scene + sum += log(2.3e-5F + Y); // contrast constant in Tumblin paper + } + // next line + bits += pitch; + } + // maximum luminance + *maxLum = max_lum; + // minimum luminance + *minLum = min_lum; + // average log luminance + double avgLogLum = (sum / (width * height)); + // world adaptation luminance + *worldLum = (float)exp(avgLogLum); + + return TRUE; +} + +/** +Clamp RGBF image highest values to display white, +then convert to 24-bit RGB +*/ +FIBITMAP* +ClampConvertRGBFTo24(FIBITMAP *src) { + if(FreeImage_GetImageType(src) != FIT_RGBF) + return FALSE; + + const unsigned width = FreeImage_GetWidth(src); + const unsigned height = FreeImage_GetHeight(src); + + FIBITMAP *dst = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + if(!dst) return NULL; + + const unsigned src_pitch = FreeImage_GetPitch(src); + const unsigned dst_pitch = FreeImage_GetPitch(dst); + + BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); + BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); + + for(unsigned y = 0; y < height; y++) { + const FIRGBF *src_pixel = (FIRGBF*)src_bits; + BYTE *dst_pixel = (BYTE*)dst_bits; + for(unsigned x = 0; x < width; x++) { + const float red = (src_pixel[x].red > 1) ? 1 : src_pixel[x].red; + const float green = (src_pixel[x].green > 1) ? 1 : src_pixel[x].green; + const float blue = (src_pixel[x].blue > 1) ? 1 : src_pixel[x].blue; + + dst_pixel[FI_RGBA_RED] = (BYTE)(255.0F * red + 0.5F); + dst_pixel[FI_RGBA_GREEN] = (BYTE)(255.0F * green + 0.5F); + dst_pixel[FI_RGBA_BLUE] = (BYTE)(255.0F * blue + 0.5F); + dst_pixel += 3; + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + + return dst; +} + +/** +Extract the luminance channel L from a RGBF image. +Luminance is calculated from the sRGB model (RGB2XYZ matrix) +using a D65 white point : +L = ( 0.2126 * r ) + ( 0.7152 * g ) + ( 0.0722 * b ) +Reference : +A Standard Default Color Space for the Internet - sRGB. +[online] http://www.w3.org/Graphics/Color/sRGB +*/ +FIBITMAP* +ConvertRGBFToY(FIBITMAP *src) { + if(FreeImage_GetImageType(src) != FIT_RGBF) + return FALSE; + + const unsigned width = FreeImage_GetWidth(src); + const unsigned height = FreeImage_GetHeight(src); + + FIBITMAP *dst = FreeImage_AllocateT(FIT_FLOAT, width, height); + if(!dst) return NULL; + + const unsigned src_pitch = FreeImage_GetPitch(src); + const unsigned dst_pitch = FreeImage_GetPitch(dst); + + + BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); + BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); + + for(unsigned y = 0; y < height; y++) { + const FIRGBF *src_pixel = (FIRGBF*)src_bits; + float *dst_pixel = (float*)dst_bits; + for(unsigned x = 0; x < width; x++) { + const float L = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue); + dst_pixel[x] = (L > 0) ? L : 0; + } + // next line + src_bits += src_pitch; + dst_bits += dst_pitch; + } + + return dst; +} + +/** +Get the maximum, minimum, average luminance and log average luminance from a Y image +@param dib Source Y image to analyze +@param maxLum Maximum luminance +@param minLum Minimum luminance +@param Lav Average luminance +@param Llav Log average luminance (also known as 'world adaptation luminance') +@return Returns TRUE if successful, returns FALSE otherwise +@see ConvertRGBFToY, FreeImage_TmoReinhard05Ex +*/ +BOOL +LuminanceFromY(FIBITMAP *dib, float *maxLum, float *minLum, float *Lav, float *Llav) { + if(FreeImage_GetImageType(dib) != FIT_FLOAT) + return FALSE; + + unsigned width = FreeImage_GetWidth(dib); + unsigned height = FreeImage_GetHeight(dib); + unsigned pitch = FreeImage_GetPitch(dib); + + float max_lum = -1e20F, min_lum = 1e20F; + double sumLum = 0, sumLogLum = 0; + + BYTE *bits = (BYTE*)FreeImage_GetBits(dib); + for(unsigned y = 0; y < height; y++) { + const float *pixel = (float*)bits; + for(unsigned x = 0; x < width; x++) { + const float Y = pixel[x]; + max_lum = (max_lum < Y) ? Y : max_lum; // max Luminance in the scene + min_lum = ((Y > 0) && (min_lum < Y)) ? min_lum : Y; // min Luminance in the scene + sumLum += Y; // average luminance + sumLogLum += log(2.3e-5F + Y); // contrast constant in Tumblin paper + } + // next line + bits += pitch; + } + + // maximum luminance + *maxLum = max_lum; + // minimum luminance + *minLum = min_lum; + // average luminance + *Lav = (float)(sumLum / (width * height)); + // average log luminance, a.k.a. world adaptation luminance + *Llav = (float)exp(sumLogLum / (width * height)); + + return TRUE; +} +// -------------------------------------------------------------------------- + +static void findMaxMinPercentile(FIBITMAP *Y, float minPrct, float *minLum, float maxPrct, float *maxLum) { + int x, y; + int width = FreeImage_GetWidth(Y); + int height = FreeImage_GetHeight(Y); + int pitch = FreeImage_GetPitch(Y); + + std::vector<float> vY(width * height); + + BYTE *bits = (BYTE*)FreeImage_GetBits(Y); + for(y = 0; y < height; y++) { + float *pixel = (float*)bits; + for(x = 0; x < width; x++) { + if(pixel[x] != 0) { + vY.push_back(pixel[x]); + } + } + bits += pitch; + } + + std::sort(vY.begin(), vY.end()); + + *minLum = vY.at( int(minPrct * vY.size()) ); + *maxLum = vY.at( int(maxPrct * vY.size()) ); +} + +/** +Clipping function<br> +Remove any extremely bright and/or extremely dark pixels +and normalize between 0 and 1. +@param Y Input/Output image +@param minPrct Minimum percentile +@param maxPrct Maximum percentile +*/ +void +NormalizeY(FIBITMAP *Y, float minPrct, float maxPrct) { + int x, y; + float maxLum, minLum; + + if(minPrct > maxPrct) { + // swap values + float t = minPrct; minPrct = maxPrct; maxPrct = t; + } + if(minPrct < 0) minPrct = 0; + if(maxPrct > 1) maxPrct = 1; + + int width = FreeImage_GetWidth(Y); + int height = FreeImage_GetHeight(Y); + int pitch = FreeImage_GetPitch(Y); + + // find max & min luminance values + if((minPrct > 0) || (maxPrct < 1)) { + maxLum = 0, minLum = 0; + findMaxMinPercentile(Y, minPrct, &minLum, maxPrct, &maxLum); + } else { + maxLum = -1e20F, minLum = 1e20F; + BYTE *bits = (BYTE*)FreeImage_GetBits(Y); + for(y = 0; y < height; y++) { + const float *pixel = (float*)bits; + for(x = 0; x < width; x++) { + const float value = pixel[x]; + maxLum = (maxLum < value) ? value : maxLum; // max Luminance in the scene + minLum = (minLum < value) ? minLum : value; // min Luminance in the scene + } + // next line + bits += pitch; + } + } + if(maxLum == minLum) return; + + // normalize to range 0..1 + const float divider = maxLum - minLum; + BYTE *bits = (BYTE*)FreeImage_GetBits(Y); + for(y = 0; y < height; y++) { + float *pixel = (float*)bits; + for(x = 0; x < width; x++) { + pixel[x] = (pixel[x] - minLum) / divider; + if(pixel[x] <= 0) pixel[x] = EPSILON; + if(pixel[x] > 1) pixel[x] = 1; + } + // next line + bits += pitch; + } +} |