/** 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; } }
void tmo_durand02(pfs::Array2Df& R, pfs::Array2Df& G, pfs::Array2Df& B, float sigma_s, float sigma_r, float baseContrast, int downsample, bool color_correction, pfs::Progress &ph) { int w = R.getCols(); int h = R.getRows(); int size = w*h; pfs::Array2Df I(w,h); // intensities pfs::Array2Df BASE(w,h); // base layer pfs::Array2Df DETAIL(w,h); // detail layer float min_pos = 1e10f; // minimum positive value (to avoid log(0)) for (int i = 0 ; i < size ; i++) { I(i) = 1.0f/61.0f * ( 20.0f*R(i) + 40.0f*G(i) + B(i) ); if ( I(i) < min_pos && I(i) > 0.0f ) { min_pos = I(i); } } for (int i = 0 ; i < size ; i++) { float L = I(i); if ( L <= 0.0f ) { L = min_pos; } R(i) /= L; G(i) /= L; B(i) /= L; I(i) = std::log( L ); } #ifdef HAVE_FFTW3F fastBilateralFilter( I, BASE, sigma_s, sigma_r, downsample, ph ); #else bilateralFilter( &I, &BASE, sigma_s, sigma_r, ph ); #endif //!! FIX: find minimum and maximum luminance, but skip 1% of outliers float maxB; float minB; findMaxMinPercentile(&BASE, 0.01f, 0.99f, minB, maxB); float compressionfactor = baseContrast / (maxB - minB); // Color correction factor const float k1 = 1.48f; const float k2 = 0.82f; const float s = ( (1 + k1)*pow(compressionfactor,k2) )/( 1 + k1*pow(compressionfactor,k2) ); for (int i = 0 ; i < size ; i++) { DETAIL(i) = I(i) - BASE(i); I(i) = BASE(i) * compressionfactor + DETAIL(i); //!! FIX: this to keep the output in normalized range 0.01 - 1.0 //intensitites are related only to minimum luminance because I //would say this is more stable over time than using maximum //luminance and is also robust against random peaks of very high //luminance I(i) -= 4.3f+minB*compressionfactor; if ( color_correction ) { R(i) = decode( std::pow( R(i), s ) * std::exp( I(i) ) ); G(i) = decode( std::pow( G(i), s ) * std::exp( I(i) ) ); B(i) = decode( std::pow( B(i), s ) * std::exp( I(i) ) ); } else { R(i) *= decode( std::exp( I(i) ) ); G(i) *= decode( std::exp( I(i) ) ); B(i) *= decode( std::exp( I(i) ) ); } } if (!ph.canceled()) { ph.setValue( 100 ); } }