/** Poisson solver based on a multigrig algorithm. This routine solves a Poisson equation, remap result pixels to [0..1] and returns the solution. NB: The input image is first stored inside a square image whose size is (2^j + 1)x(2^j + 1) for some integer j, where j is such that 2^j is the nearest larger dimension corresponding to MAX(image width, image height). @param Laplacian Laplacian image @param ncycle Number of cycles in the multigrid algorithm (usually 2 or 3) @return Returns the solved PDE equations if successful, returns NULL otherwise */ FIBITMAP* DLL_CALLCONV FreeImage_MultigridPoissonSolver(FIBITMAP *Laplacian, int ncycle) { if(!Laplacian) return NULL; int width = FreeImage_GetWidth(Laplacian); int height = FreeImage_GetHeight(Laplacian); // get nearest larger dimension length that is acceptable by the algorithm int n = MAX(width, height); int size = 0; while((n >>= 1) > 0) size++; // size must be of the form 2^j + 1 for some integer j size = 1 + (1 << (size + 1)); // allocate a temporary square image I FIBITMAP *I = FreeImage_AllocateT(FIT_FLOAT, size, size); if(!I) return NULL; // copy Laplacian into I and shift pixels to create a boundary FreeImage_Paste(I, Laplacian, 1, 1, 255); // solve the PDE equation fmg_mglin(I, size, ncycle); // shift pixels back FIBITMAP *U = FreeImage_Copy(I, 1, 1, width + 1, height + 1); FreeImage_Unload(I); // remap pixels to [0..1] NormalizeY(U, 0, 1); // return the integrated image return U; }
/** Apply the Gradient Domain High Dynamic Range Compression to a RGBF image and convert to 24-bit RGB @param dib Input RGBF / RGB16 image @param color_saturation Color saturation (s parameter in the paper) in [0.4..0.6] @param attenuation Atenuation factor (beta parameter in the paper) in [0.8..0.9] @return Returns a 24-bit RGB image if successful, returns NULL otherwise */ FIBITMAP* DLL_CALLCONV FreeImage_TmoFattal02(FIBITMAP *dib, double color_saturation, double attenuation) { const float alpha = 0.1F; // parameter alpha = 0.1 const float beta = (float)MAX(0.8, MIN(0.9, attenuation)); // parameter beta = [0.8..0.9] const float s = (float)MAX(0.4, MIN(0.6, color_saturation));// exponent s controls color saturation = [0.4..0.6] FIBITMAP *src = NULL; FIBITMAP *Yin = NULL; FIBITMAP *Yout = NULL; FIBITMAP *dst = NULL; if(!FreeImage_HasPixels(dib)) return NULL; try { // convert to RGBF src = FreeImage_ConvertToRGBF(dib); if(!src) throw(1); // get the luminance channel Yin = ConvertRGBFToY(src); if(!Yin) throw(1); // perform the tone mapping Yout = tmoFattal02(Yin, alpha, beta); if(!Yout) throw(1); // clip low and high values and normalize to [0..1] //NormalizeY(Yout, 0.001F, 0.995F); NormalizeY(Yout, 0, 1); // compress the dynamic range const unsigned width = FreeImage_GetWidth(src); const unsigned height = FreeImage_GetHeight(src); const unsigned rgb_pitch = FreeImage_GetPitch(src); const unsigned y_pitch = FreeImage_GetPitch(Yin); BYTE *bits = (BYTE*)FreeImage_GetBits(src); BYTE *bits_yin = (BYTE*)FreeImage_GetBits(Yin); BYTE *bits_yout = (BYTE*)FreeImage_GetBits(Yout); for(unsigned y = 0; y < height; y++) { float *Lin = (float*)bits_yin; float *Lout = (float*)bits_yout; float *color = (float*)bits; for(unsigned x = 0; x < width; x++) { for(unsigned c = 0; c < 3; c++) { *color = (Lin[x] > 0) ? pow(*color/Lin[x], s) * Lout[x] : 0; color++; } } bits += rgb_pitch; bits_yin += y_pitch; bits_yout += y_pitch; } // not needed anymore FreeImage_Unload(Yin); Yin = NULL; FreeImage_Unload(Yout); Yout = NULL; // clamp image highest values to display white, then convert to 24-bit RGB dst = ClampConvertRGBFTo24(src); // clean-up and return FreeImage_Unload(src); src = NULL; // copy metadata from src to dst FreeImage_CloneMetadata(dst, dib); return dst; } catch(int) { if(src) FreeImage_Unload(src); if(Yin) FreeImage_Unload(Yin); if(Yout) FreeImage_Unload(Yout); return NULL; } }