diff options
Diffstat (limited to 'plugins/FreeImage/Source/FreeImageToolkit/MultigridPoissonSolver.cpp')
| -rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/MultigridPoissonSolver.cpp | 1010 | 
1 files changed, 505 insertions, 505 deletions
diff --git a/plugins/FreeImage/Source/FreeImageToolkit/MultigridPoissonSolver.cpp b/plugins/FreeImage/Source/FreeImageToolkit/MultigridPoissonSolver.cpp index 03281a16b5..3b577cbd0a 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/MultigridPoissonSolver.cpp +++ b/plugins/FreeImage/Source/FreeImageToolkit/MultigridPoissonSolver.cpp @@ -1,505 +1,505 @@ -// ==========================================================
 -// Poisson solver based on a full multigrid algorithm
 -//
 -// Design and implementation by
 -// - Hervé Drolon (drolon@infonie.fr)
 -// Reference:
 -// PRESS, W. H., TEUKOLSKY, S. A., VETTERLING, W. T., AND FLANNERY, B. P.
 -// 1992. Numerical Recipes in C: The Art of Scientific Computing, 2nd ed. Cambridge University Press.
 -//
 -// 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"
 -
 -static const int NPRE	= 1;		// Number of relaxation sweeps before ...
 -static const int NPOST	= 1;		// ... and after the coarse-grid correction is computed
 -static const int NGMAX	= 15;		// Maximum number of grids
 -
 -/**
 -Copy src into dst
 -*/
 -static inline void fmg_copyArray(FIBITMAP *dst, FIBITMAP *src) {
 -	memcpy(FreeImage_GetBits(dst), FreeImage_GetBits(src), FreeImage_GetHeight(dst) * FreeImage_GetPitch(dst));
 -}
 -
 -/**
 -Fills src with zeros
 -*/
 -static inline void fmg_fillArrayWithZeros(FIBITMAP *src) {
 -	memset(FreeImage_GetBits(src), 0, FreeImage_GetHeight(src) * FreeImage_GetPitch(src));
 -}
 -
 -/**
 -Half-weighting restriction. nc is the coarse-grid dimension. The fine-grid solution is input in
 -uf[0..2*nc-2][0..2*nc-2], the coarse-grid solution is returned in uc[0..nc-1][0..nc-1].
 -*/
 -static void fmg_restrict(FIBITMAP *UC, FIBITMAP *UF, int nc) {
 -	int row_uc, row_uf, col_uc, col_uf;
 -
 -	const int uc_pitch  = FreeImage_GetPitch(UC) / sizeof(float);
 -	const int uf_pitch  = FreeImage_GetPitch(UF) / sizeof(float);
 -	
 -	float *uc_bits = (float*)FreeImage_GetBits(UC);
 -	const float *uf_bits = (float*)FreeImage_GetBits(UF);
 -
 -	// interior points
 -	{
 -		float *uc_scan = uc_bits + uc_pitch;
 -		for (row_uc = 1, row_uf = 2; row_uc < nc-1; row_uc++, row_uf += 2) {
 -			const float *uf_scan = uf_bits + row_uf * uf_pitch;
 -			for (col_uc = 1, col_uf = 2; col_uc < nc-1; col_uc++, col_uf += 2) { 
 -				// calculate 
 -				// UC(row_uc, col_uc) = 
 -				// 0.5 * UF(row_uf, col_uf) + 0.125 * [ UF(row_uf+1, col_uf) + UF(row_uf-1, col_uf) + UF(row_uf, col_uf+1) + UF(row_uf, col_uf-1) ]
 -				float *uc_pixel = uc_scan + col_uc;
 -				const float *uf_center = uf_scan + col_uf;
 -				*uc_pixel = 0.5F * *uf_center + 0.125F * ( *(uf_center + uf_pitch) + *(uf_center - uf_pitch) + *(uf_center + 1) + *(uf_center - 1) );
 -			}
 -			uc_scan += uc_pitch;
 -		}
 -	}
 -	// boundary points
 -	const int ncc = 2*nc-1;
 -	{
 -		/*
 -		calculate the following: 
 -		for (row_uc = 0, row_uf = 0; row_uc < nc; row_uc++, row_uf += 2) { 
 -			UC(row_uc, 0) = UF(row_uf, 0);		
 -			UC(row_uc, nc-1) = UF(row_uf, ncc-1);
 -		}
 -		*/
 -		float *uc_scan = uc_bits;
 -		for (row_uc = 0, row_uf = 0; row_uc < nc; row_uc++, row_uf += 2) { 
 -			const float *uf_scan = uf_bits + row_uf * uf_pitch;
 -			uc_scan[0] = uf_scan[0];
 -			uc_scan[nc-1] = uf_scan[ncc-1];
 -			uc_scan += uc_pitch;
 -		}
 -	}
 -	{
 -		/*
 -		calculate the following: 
 -		for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) {
 -			UC(0, col_uc) = UF(0, col_uf);
 -			UC(nc-1, col_uc) = UF(ncc-1, col_uf);
 -		}
 -		*/
 -		float *uc_scan_top = uc_bits;
 -		float *uc_scan_bottom = uc_bits + (nc-1)*uc_pitch;
 -		const float *uf_scan_top = uf_bits + (ncc-1)*uf_pitch;
 -		const float *uf_scan_bottom = uf_bits;
 -		for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) {
 -			uc_scan_top[col_uc] = uf_scan_top[col_uf];
 -			uc_scan_bottom[col_uc] = uf_scan_bottom[col_uf];
 -		}
 -	}
 -}
 -
 -/**
 -Solution of the model problem on the coarsest grid, where h = 1/2 . 
 -The right-hand side is input
 -in rhs[0..2][0..2] and the solution is returned in u[0..2][0..2].
 -*/
 -static void fmg_solve(FIBITMAP *U, FIBITMAP *RHS) {
 -	// fill U with zeros
 -	fmg_fillArrayWithZeros(U);
 -	// calculate U(1, 1) = -h*h*RHS(1, 1)/4.0 where h = 1/2
 -	float *u_scan = (float*)FreeImage_GetScanLine(U, 1);
 -	const float *rhs_scan = (float*)FreeImage_GetScanLine(RHS, 1);
 -	u_scan[1] = -rhs_scan[1] / 16;
 -}
 -
 -/**
 -Coarse-to-fine prolongation by bilinear interpolation. nf is the fine-grid dimension. The coarsegrid
 -solution is input as uc[0..nc-1][0..nc-1], where nc = nf/2 + 1. The fine-grid solution is
 -returned in uf[0..nf-1][0..nf-1].
 -*/
 -static void fmg_prolongate(FIBITMAP *UF, FIBITMAP *UC, int nf) {
 -	int row_uc, row_uf, col_uc, col_uf;
 -
 -	const int uf_pitch  = FreeImage_GetPitch(UF) / sizeof(float);
 -	const int uc_pitch  = FreeImage_GetPitch(UC) / sizeof(float);
 -	
 -	float *uf_bits = (float*)FreeImage_GetBits(UF);
 -	const float *uc_bits = (float*)FreeImage_GetBits(UC);
 -	
 -	// do elements that are copies
 -	{
 -		const int nc = nf/2 + 1;
 -
 -		float *uf_scan = uf_bits;
 -		const float *uc_scan = uc_bits;		
 -		for (row_uc = 0; row_uc < nc; row_uc++) {
 -			for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) {
 -				// calculate UF(2*row_uc, col_uf) = UC(row_uc, col_uc);
 -				uf_scan[col_uf] = uc_scan[col_uc];
 -			}
 -			uc_scan += uc_pitch;
 -			uf_scan += 2 * uf_pitch;
 -		}
 -	}
 -	// do odd-numbered columns, interpolating vertically
 -	{		
 -		for(row_uf = 1; row_uf < nf-1; row_uf += 2) {
 -			float *uf_scan = uf_bits + row_uf * uf_pitch;
 -			for (col_uf = 0; col_uf < nf; col_uf += 2) {
 -				// calculate UF(row_uf, col_uf) = 0.5 * ( UF(row_uf+1, col_uf) + UF(row_uf-1, col_uf) )
 -				uf_scan[col_uf] = 0.5F * ( *(uf_scan + uf_pitch + col_uf) + *(uf_scan - uf_pitch + col_uf) );
 -			}
 -		}
 -	}
 -	// do even-numbered columns, interpolating horizontally
 -	{
 -		float *uf_scan = uf_bits;
 -		for(row_uf = 0; row_uf < nf; row_uf++) {
 -			for (col_uf = 1; col_uf < nf-1; col_uf += 2) {
 -				// calculate UF(row_uf, col_uf) = 0.5 * ( UF(row_uf, col_uf+1) + UF(row_uf, col_uf-1) )
 -				uf_scan[col_uf] = 0.5F * ( uf_scan[col_uf + 1] + uf_scan[col_uf - 1] );
 -			}
 -			uf_scan += uf_pitch;
 -		}
 -	}
 -}
 -
 -/**
 -Red-black Gauss-Seidel relaxation for model problem. Updates the current value of the solution
 -u[0..n-1][0..n-1], using the right-hand side function rhs[0..n-1][0..n-1].
 -*/
 -static void fmg_relaxation(FIBITMAP *U, FIBITMAP *RHS, int n) {
 -	int row, col, ipass, isw, jsw;
 -	const float h = 1.0F / (n - 1);
 -	const float h2 = h*h;
 -
 -	const int u_pitch  = FreeImage_GetPitch(U) / sizeof(float);
 -	const int rhs_pitch  = FreeImage_GetPitch(RHS) / sizeof(float);
 -	
 -	float *u_bits = (float*)FreeImage_GetBits(U);
 -	const float *rhs_bits = (float*)FreeImage_GetBits(RHS);
 -
 -	for (ipass = 0, jsw = 1; ipass < 2; ipass++, jsw = 3-jsw) { // Red and black sweeps
 -		float *u_scan = u_bits + u_pitch;
 -		const float *rhs_scan = rhs_bits + rhs_pitch;
 -		for (row = 1, isw = jsw; row < n-1; row++, isw = 3-isw) {
 -			for (col = isw; col < n-1; col += 2) { 
 -				// Gauss-Seidel formula
 -				// calculate U(row, col) = 
 -				// 0.25 * [ U(row+1, col) + U(row-1, col) + U(row, col+1) + U(row, col-1) - h2 * RHS(row, col) ]		 
 -				float *u_center = u_scan + col;
 -				const float *rhs_center = rhs_scan + col;
 -				*u_center = *(u_center + u_pitch) + *(u_center - u_pitch) + *(u_center + 1) + *(u_center - 1);
 -				*u_center -= h2 * *rhs_center;
 -				*u_center *= 0.25F;
 -			}
 -			u_scan += u_pitch;
 -			rhs_scan += rhs_pitch;
 -		}
 -	}
 -}
 -
 -/**
 -Returns minus the residual for the model problem. Input quantities are u[0..n-1][0..n-1] and
 -rhs[0..n-1][0..n-1], while res[0..n-1][0..n-1] is returned.
 -*/
 -static void fmg_residual(FIBITMAP *RES, FIBITMAP *U, FIBITMAP *RHS, int n) {
 -	int row, col;
 -
 -	const float h = 1.0F / (n-1);	
 -	const float h2i = 1.0F / (h*h);
 -
 -	const int res_pitch  = FreeImage_GetPitch(RES) / sizeof(float);
 -	const int u_pitch  = FreeImage_GetPitch(U) / sizeof(float);
 -	const int rhs_pitch  = FreeImage_GetPitch(RHS) / sizeof(float);
 -	
 -	float *res_bits = (float*)FreeImage_GetBits(RES);
 -	const float *u_bits = (float*)FreeImage_GetBits(U);
 -	const float *rhs_bits = (float*)FreeImage_GetBits(RHS);
 -
 -	// interior points
 -	{
 -		float *res_scan = res_bits + res_pitch;
 -		const float *u_scan = u_bits + u_pitch;
 -		const float *rhs_scan = rhs_bits + rhs_pitch;
 -		for (row = 1; row < n-1; row++) {
 -			for (col = 1; col < n-1; col++) {
 -				// calculate RES(row, col) = 
 -				// -h2i * [ U(row+1, col) + U(row-1, col) + U(row, col+1) + U(row, col-1) - 4 * U(row, col) ] + RHS(row, col);
 -				float *res_center = res_scan + col;
 -				const float *u_center = u_scan + col;
 -				const float *rhs_center = rhs_scan + col;
 -				*res_center = *(u_center + u_pitch) + *(u_center - u_pitch) + *(u_center + 1) + *(u_center - 1) - 4 * *u_center;
 -				*res_center *= -h2i;
 -				*res_center += *rhs_center;
 -			}
 -			res_scan += res_pitch;
 -			u_scan += u_pitch;
 -			rhs_scan += rhs_pitch;
 -		}
 -	}
 -
 -	// boundary points
 -	{
 -		memset(FreeImage_GetScanLine(RES, 0), 0, FreeImage_GetPitch(RES));
 -		memset(FreeImage_GetScanLine(RES, n-1), 0, FreeImage_GetPitch(RES));
 -		float *left = res_bits;
 -		float *right = res_bits + (n-1);
 -		for(int k = 0; k < n; k++) {
 -			*left = 0;
 -			*right = 0;
 -			left += res_pitch;
 -			right += res_pitch;
 -		}
 -	}
 -}
 -
 -/**
 -Does coarse-to-fine interpolation and adds result to uf. nf is the fine-grid dimension. The
 -coarse-grid solution is input as uc[0..nc-1][0..nc-1], where nc = nf/2+1. The fine-grid solution
 -is returned in uf[0..nf-1][0..nf-1]. res[0..nf-1][0..nf-1] is used for temporary storage.
 -*/
 -static void fmg_addint(FIBITMAP *UF, FIBITMAP *UC, FIBITMAP *RES, int nf) {
 -	fmg_prolongate(RES, UC, nf);
 -
 -	const int uf_pitch  = FreeImage_GetPitch(UF) / sizeof(float);
 -	const int res_pitch  = FreeImage_GetPitch(RES) / sizeof(float);	
 -
 -	float *uf_bits = (float*)FreeImage_GetBits(UF);
 -	const float *res_bits = (float*)FreeImage_GetBits(RES);
 -
 -	for(int row = 0; row < nf; row++) {
 -		for(int col = 0; col < nf; col++) {
 -			// calculate UF(row, col) = UF(row, col) + RES(row, col);
 -			uf_bits[col] += res_bits[col];
 -		}
 -		uf_bits += uf_pitch;
 -		res_bits += res_pitch;
 -	}
 -}
 -
 -/**
 -Full Multigrid Algorithm for solution of linear elliptic equation, here the model problem (19.0.6).
 -On input u[0..n-1][0..n-1] contains the right-hand side ñ, while on output it returns the solution.
 -The dimension n must be of the form 2^j + 1 for some integer j. (j is actually the number of
 -grid levels used in the solution, called ng below.) ncycle is the number of V-cycles to be
 -used at each level.
 -*/
 -static BOOL fmg_mglin(FIBITMAP *U, int n, int ncycle) {
 -	int j, jcycle, jj, jpost, jpre, nf, ngrid;
 -
 -	FIBITMAP **IRHO = NULL;
 -	FIBITMAP **IU   = NULL;
 -	FIBITMAP **IRHS = NULL;
 -	FIBITMAP **IRES = NULL;
 -	
 -	int ng = 0;		// number of allocated grids
 -
 -// --------------------------------------------------------------------------
 -
 -#define _CREATE_ARRAY_GRID_(array, array_size) \
 -	array = (FIBITMAP**)malloc(array_size * sizeof(FIBITMAP*));\
 -	if(!array) throw(1);\
 -	memset(array, 0, array_size * sizeof(FIBITMAP*))
 -
 -#define _FREE_ARRAY_GRID_(array, array_size) \
 -	if(NULL != array) {\
 -		for(int k = 0; k < array_size; k++) {\
 -			if(NULL != array[k]) {\
 -				FreeImage_Unload(array[k]); array[k] = NULL;\
 -			}\
 -		}\
 -		free(array);\
 -	}
 -
 -// --------------------------------------------------------------------------
 -
 -	try {
 -		int nn = n;
 -		// check grid size and grid levels
 -		while (nn >>= 1) ng++;
 -		if (n != 1 + (1L << ng)) {
 -			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Multigrid algorithm: n = %d, while n-1 must be a power of 2.", n);
 -			throw(1);
 -		}
 -		if (ng > NGMAX) {
 -			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Multigrid algorithm: ng = %d while NGMAX = %d, increase NGMAX.", ng, NGMAX);
 -			throw(1);
 -		}
 -		// allocate grid arrays
 -		{
 -			_CREATE_ARRAY_GRID_(IRHO, ng);
 -			_CREATE_ARRAY_GRID_(IU, ng);
 -			_CREATE_ARRAY_GRID_(IRHS, ng);
 -			_CREATE_ARRAY_GRID_(IRES, ng);
 -		}
 -
 -		nn = n/2 + 1;
 -		ngrid = ng - 2;
 -
 -		// allocate storage for r.h.s. on grid (ng - 2) ...
 -		IRHO[ngrid] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
 -		if(!IRHO[ngrid]) throw(1);
 -
 -		// ... and fill it by restricting from the fine grid
 -		fmg_restrict(IRHO[ngrid], U, nn);	
 -
 -		// similarly allocate storage and fill r.h.s. on all coarse grids.
 -		while (nn > 3) {
 -			nn = nn/2 + 1; 
 -			ngrid--;
 -			IRHO[ngrid] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
 -			if(!IRHO[ngrid]) throw(1);
 -			fmg_restrict(IRHO[ngrid], IRHO[ngrid+1], nn);
 -		}
 -
 -		nn = 3;
 -
 -		IU[0] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
 -		if(!IU[0]) throw(1);
 -		IRHS[0] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
 -		if(!IRHS[0]) throw(1);
 -
 -		// initial solution on coarsest grid
 -		fmg_solve(IU[0], IRHO[0]);
 -		// irho[0] no longer needed ...
 -		FreeImage_Unload(IRHO[0]); IRHO[0] = NULL;
 -
 -		ngrid = ng;
 -
 -		// nested iteration loop
 -		for (j = 1; j < ngrid; j++) {
 -			nn = 2*nn - 1;
 -
 -			IU[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
 -			if(!IU[j]) throw(1);
 -			IRHS[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
 -			if(!IRHS[j]) throw(1);
 -			IRES[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
 -			if(!IRES[j]) throw(1);
 -
 -			fmg_prolongate(IU[j], IU[j-1], nn);
 -			
 -			// interpolate from coarse grid to next finer grid
 -
 -			// set up r.h.s.
 -			fmg_copyArray(IRHS[j], j != (ngrid - 1) ? IRHO[j] : U);
 -			
 -			// V-cycle loop
 -			for (jcycle = 0; jcycle < ncycle; jcycle++) {
 -				nf = nn;
 -				// downward stoke of the V
 -				for (jj = j; jj >= 1; jj--) {
 -					// pre-smoothing
 -					for (jpre = 0; jpre < NPRE; jpre++) {
 -						fmg_relaxation(IU[jj], IRHS[jj], nf);
 -					}
 -					fmg_residual(IRES[jj], IU[jj], IRHS[jj], nf);
 -					nf = nf/2 + 1;
 -					// restriction of the residual is the next r.h.s.
 -					fmg_restrict(IRHS[jj-1], IRES[jj], nf);				
 -					// zero for initial guess in next relaxation
 -					fmg_fillArrayWithZeros(IU[jj-1]);
 -				}
 -				// bottom of V: solve on coarsest grid
 -				fmg_solve(IU[0], IRHS[0]); 
 -				nf = 3; 
 -				// upward stroke of V.
 -				for (jj = 1; jj <= j; jj++) { 
 -					nf = 2*nf - 1;
 -					// use res for temporary storage inside addint
 -					fmg_addint(IU[jj], IU[jj-1], IRES[jj], nf);				
 -					// post-smoothing
 -					for (jpost = 0; jpost < NPOST; jpost++) {
 -						fmg_relaxation(IU[jj], IRHS[jj], nf);
 -					}
 -				}
 -			}
 -		}
 -
 -		// return solution in U
 -		fmg_copyArray(U, IU[ngrid-1]);
 -
 -		// delete allocated arrays
 -		_FREE_ARRAY_GRID_(IRES, ng);
 -		_FREE_ARRAY_GRID_(IRHS, ng);
 -		_FREE_ARRAY_GRID_(IU, ng);
 -		_FREE_ARRAY_GRID_(IRHO, ng);
 -
 -		return TRUE;
 -
 -	} catch(int) {
 -		// delete allocated arrays
 -		_FREE_ARRAY_GRID_(IRES, ng);
 -		_FREE_ARRAY_GRID_(IRHS, ng);
 -		_FREE_ARRAY_GRID_(IU, ng);
 -		_FREE_ARRAY_GRID_(IRHO, ng);
 -
 -		return FALSE;
 -	}
 -}
 -
 -// --------------------------------------------------------------------------
 -
 -/**
 -Poisson solver based on a multigrid 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(!FreeImage_HasPixels(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++;
 -	if((1 << size) < MAX(width, height)) {
 -		size++;
 -	}
 -	// size must be of the form 2^j + 1 for some integer j
 -	size = 1 + (1 << size);
 -
 -	// 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);
 -
 -	// copy metadata from src to dst
 -	FreeImage_CloneMetadata(U, Laplacian);
 -
 -	// return the integrated image
 -	return U;
 -}
 -
 +// ========================================================== +// Poisson solver based on a full multigrid algorithm +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// Reference: +// PRESS, W. H., TEUKOLSKY, S. A., VETTERLING, W. T., AND FLANNERY, B. P. +// 1992. Numerical Recipes in C: The Art of Scientific Computing, 2nd ed. Cambridge University Press. +// +// 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" + +static const int NPRE	= 1;		// Number of relaxation sweeps before ... +static const int NPOST	= 1;		// ... and after the coarse-grid correction is computed +static const int NGMAX	= 15;		// Maximum number of grids + +/** +Copy src into dst +*/ +static inline void fmg_copyArray(FIBITMAP *dst, FIBITMAP *src) { +	memcpy(FreeImage_GetBits(dst), FreeImage_GetBits(src), FreeImage_GetHeight(dst) * FreeImage_GetPitch(dst)); +} + +/** +Fills src with zeros +*/ +static inline void fmg_fillArrayWithZeros(FIBITMAP *src) { +	memset(FreeImage_GetBits(src), 0, FreeImage_GetHeight(src) * FreeImage_GetPitch(src)); +} + +/** +Half-weighting restriction. nc is the coarse-grid dimension. The fine-grid solution is input in +uf[0..2*nc-2][0..2*nc-2], the coarse-grid solution is returned in uc[0..nc-1][0..nc-1]. +*/ +static void fmg_restrict(FIBITMAP *UC, FIBITMAP *UF, int nc) { +	int row_uc, row_uf, col_uc, col_uf; + +	const int uc_pitch  = FreeImage_GetPitch(UC) / sizeof(float); +	const int uf_pitch  = FreeImage_GetPitch(UF) / sizeof(float); +	 +	float *uc_bits = (float*)FreeImage_GetBits(UC); +	const float *uf_bits = (float*)FreeImage_GetBits(UF); + +	// interior points +	{ +		float *uc_scan = uc_bits + uc_pitch; +		for (row_uc = 1, row_uf = 2; row_uc < nc-1; row_uc++, row_uf += 2) { +			const float *uf_scan = uf_bits + row_uf * uf_pitch; +			for (col_uc = 1, col_uf = 2; col_uc < nc-1; col_uc++, col_uf += 2) {  +				// calculate  +				// UC(row_uc, col_uc) =  +				// 0.5 * UF(row_uf, col_uf) + 0.125 * [ UF(row_uf+1, col_uf) + UF(row_uf-1, col_uf) + UF(row_uf, col_uf+1) + UF(row_uf, col_uf-1) ] +				float *uc_pixel = uc_scan + col_uc; +				const float *uf_center = uf_scan + col_uf; +				*uc_pixel = 0.5F * *uf_center + 0.125F * ( *(uf_center + uf_pitch) + *(uf_center - uf_pitch) + *(uf_center + 1) + *(uf_center - 1) ); +			} +			uc_scan += uc_pitch; +		} +	} +	// boundary points +	const int ncc = 2*nc-1; +	{ +		/* +		calculate the following:  +		for (row_uc = 0, row_uf = 0; row_uc < nc; row_uc++, row_uf += 2) {  +			UC(row_uc, 0) = UF(row_uf, 0);		 +			UC(row_uc, nc-1) = UF(row_uf, ncc-1); +		} +		*/ +		float *uc_scan = uc_bits; +		for (row_uc = 0, row_uf = 0; row_uc < nc; row_uc++, row_uf += 2) {  +			const float *uf_scan = uf_bits + row_uf * uf_pitch; +			uc_scan[0] = uf_scan[0]; +			uc_scan[nc-1] = uf_scan[ncc-1]; +			uc_scan += uc_pitch; +		} +	} +	{ +		/* +		calculate the following:  +		for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) { +			UC(0, col_uc) = UF(0, col_uf); +			UC(nc-1, col_uc) = UF(ncc-1, col_uf); +		} +		*/ +		float *uc_scan_top = uc_bits; +		float *uc_scan_bottom = uc_bits + (nc-1)*uc_pitch; +		const float *uf_scan_top = uf_bits + (ncc-1)*uf_pitch; +		const float *uf_scan_bottom = uf_bits; +		for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) { +			uc_scan_top[col_uc] = uf_scan_top[col_uf]; +			uc_scan_bottom[col_uc] = uf_scan_bottom[col_uf]; +		} +	} +} + +/** +Solution of the model problem on the coarsest grid, where h = 1/2 .  +The right-hand side is input +in rhs[0..2][0..2] and the solution is returned in u[0..2][0..2]. +*/ +static void fmg_solve(FIBITMAP *U, FIBITMAP *RHS) { +	// fill U with zeros +	fmg_fillArrayWithZeros(U); +	// calculate U(1, 1) = -h*h*RHS(1, 1)/4.0 where h = 1/2 +	float *u_scan = (float*)FreeImage_GetScanLine(U, 1); +	const float *rhs_scan = (float*)FreeImage_GetScanLine(RHS, 1); +	u_scan[1] = -rhs_scan[1] / 16; +} + +/** +Coarse-to-fine prolongation by bilinear interpolation. nf is the fine-grid dimension. The coarsegrid +solution is input as uc[0..nc-1][0..nc-1], where nc = nf/2 + 1. The fine-grid solution is +returned in uf[0..nf-1][0..nf-1]. +*/ +static void fmg_prolongate(FIBITMAP *UF, FIBITMAP *UC, int nf) { +	int row_uc, row_uf, col_uc, col_uf; + +	const int uf_pitch  = FreeImage_GetPitch(UF) / sizeof(float); +	const int uc_pitch  = FreeImage_GetPitch(UC) / sizeof(float); +	 +	float *uf_bits = (float*)FreeImage_GetBits(UF); +	const float *uc_bits = (float*)FreeImage_GetBits(UC); +	 +	// do elements that are copies +	{ +		const int nc = nf/2 + 1; + +		float *uf_scan = uf_bits; +		const float *uc_scan = uc_bits;		 +		for (row_uc = 0; row_uc < nc; row_uc++) { +			for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) { +				// calculate UF(2*row_uc, col_uf) = UC(row_uc, col_uc); +				uf_scan[col_uf] = uc_scan[col_uc]; +			} +			uc_scan += uc_pitch; +			uf_scan += 2 * uf_pitch; +		} +	} +	// do odd-numbered columns, interpolating vertically +	{		 +		for(row_uf = 1; row_uf < nf-1; row_uf += 2) { +			float *uf_scan = uf_bits + row_uf * uf_pitch; +			for (col_uf = 0; col_uf < nf; col_uf += 2) { +				// calculate UF(row_uf, col_uf) = 0.5 * ( UF(row_uf+1, col_uf) + UF(row_uf-1, col_uf) ) +				uf_scan[col_uf] = 0.5F * ( *(uf_scan + uf_pitch + col_uf) + *(uf_scan - uf_pitch + col_uf) ); +			} +		} +	} +	// do even-numbered columns, interpolating horizontally +	{ +		float *uf_scan = uf_bits; +		for(row_uf = 0; row_uf < nf; row_uf++) { +			for (col_uf = 1; col_uf < nf-1; col_uf += 2) { +				// calculate UF(row_uf, col_uf) = 0.5 * ( UF(row_uf, col_uf+1) + UF(row_uf, col_uf-1) ) +				uf_scan[col_uf] = 0.5F * ( uf_scan[col_uf + 1] + uf_scan[col_uf - 1] ); +			} +			uf_scan += uf_pitch; +		} +	} +} + +/** +Red-black Gauss-Seidel relaxation for model problem. Updates the current value of the solution +u[0..n-1][0..n-1], using the right-hand side function rhs[0..n-1][0..n-1]. +*/ +static void fmg_relaxation(FIBITMAP *U, FIBITMAP *RHS, int n) { +	int row, col, ipass, isw, jsw; +	const float h = 1.0F / (n - 1); +	const float h2 = h*h; + +	const int u_pitch  = FreeImage_GetPitch(U) / sizeof(float); +	const int rhs_pitch  = FreeImage_GetPitch(RHS) / sizeof(float); +	 +	float *u_bits = (float*)FreeImage_GetBits(U); +	const float *rhs_bits = (float*)FreeImage_GetBits(RHS); + +	for (ipass = 0, jsw = 1; ipass < 2; ipass++, jsw = 3-jsw) { // Red and black sweeps +		float *u_scan = u_bits + u_pitch; +		const float *rhs_scan = rhs_bits + rhs_pitch; +		for (row = 1, isw = jsw; row < n-1; row++, isw = 3-isw) { +			for (col = isw; col < n-1; col += 2) {  +				// Gauss-Seidel formula +				// calculate U(row, col) =  +				// 0.25 * [ U(row+1, col) + U(row-1, col) + U(row, col+1) + U(row, col-1) - h2 * RHS(row, col) ]		  +				float *u_center = u_scan + col; +				const float *rhs_center = rhs_scan + col; +				*u_center = *(u_center + u_pitch) + *(u_center - u_pitch) + *(u_center + 1) + *(u_center - 1); +				*u_center -= h2 * *rhs_center; +				*u_center *= 0.25F; +			} +			u_scan += u_pitch; +			rhs_scan += rhs_pitch; +		} +	} +} + +/** +Returns minus the residual for the model problem. Input quantities are u[0..n-1][0..n-1] and +rhs[0..n-1][0..n-1], while res[0..n-1][0..n-1] is returned. +*/ +static void fmg_residual(FIBITMAP *RES, FIBITMAP *U, FIBITMAP *RHS, int n) { +	int row, col; + +	const float h = 1.0F / (n-1);	 +	const float h2i = 1.0F / (h*h); + +	const int res_pitch  = FreeImage_GetPitch(RES) / sizeof(float); +	const int u_pitch  = FreeImage_GetPitch(U) / sizeof(float); +	const int rhs_pitch  = FreeImage_GetPitch(RHS) / sizeof(float); +	 +	float *res_bits = (float*)FreeImage_GetBits(RES); +	const float *u_bits = (float*)FreeImage_GetBits(U); +	const float *rhs_bits = (float*)FreeImage_GetBits(RHS); + +	// interior points +	{ +		float *res_scan = res_bits + res_pitch; +		const float *u_scan = u_bits + u_pitch; +		const float *rhs_scan = rhs_bits + rhs_pitch; +		for (row = 1; row < n-1; row++) { +			for (col = 1; col < n-1; col++) { +				// calculate RES(row, col) =  +				// -h2i * [ U(row+1, col) + U(row-1, col) + U(row, col+1) + U(row, col-1) - 4 * U(row, col) ] + RHS(row, col); +				float *res_center = res_scan + col; +				const float *u_center = u_scan + col; +				const float *rhs_center = rhs_scan + col; +				*res_center = *(u_center + u_pitch) + *(u_center - u_pitch) + *(u_center + 1) + *(u_center - 1) - 4 * *u_center; +				*res_center *= -h2i; +				*res_center += *rhs_center; +			} +			res_scan += res_pitch; +			u_scan += u_pitch; +			rhs_scan += rhs_pitch; +		} +	} + +	// boundary points +	{ +		memset(FreeImage_GetScanLine(RES, 0), 0, FreeImage_GetPitch(RES)); +		memset(FreeImage_GetScanLine(RES, n-1), 0, FreeImage_GetPitch(RES)); +		float *left = res_bits; +		float *right = res_bits + (n-1); +		for(int k = 0; k < n; k++) { +			*left = 0; +			*right = 0; +			left += res_pitch; +			right += res_pitch; +		} +	} +} + +/** +Does coarse-to-fine interpolation and adds result to uf. nf is the fine-grid dimension. The +coarse-grid solution is input as uc[0..nc-1][0..nc-1], where nc = nf/2+1. The fine-grid solution +is returned in uf[0..nf-1][0..nf-1]. res[0..nf-1][0..nf-1] is used for temporary storage. +*/ +static void fmg_addint(FIBITMAP *UF, FIBITMAP *UC, FIBITMAP *RES, int nf) { +	fmg_prolongate(RES, UC, nf); + +	const int uf_pitch  = FreeImage_GetPitch(UF) / sizeof(float); +	const int res_pitch  = FreeImage_GetPitch(RES) / sizeof(float);	 + +	float *uf_bits = (float*)FreeImage_GetBits(UF); +	const float *res_bits = (float*)FreeImage_GetBits(RES); + +	for(int row = 0; row < nf; row++) { +		for(int col = 0; col < nf; col++) { +			// calculate UF(row, col) = UF(row, col) + RES(row, col); +			uf_bits[col] += res_bits[col]; +		} +		uf_bits += uf_pitch; +		res_bits += res_pitch; +	} +} + +/** +Full Multigrid Algorithm for solution of linear elliptic equation, here the model problem (19.0.6). +On input u[0..n-1][0..n-1] contains the right-hand side ñ, while on output it returns the solution. +The dimension n must be of the form 2^j + 1 for some integer j. (j is actually the number of +grid levels used in the solution, called ng below.) ncycle is the number of V-cycles to be +used at each level. +*/ +static BOOL fmg_mglin(FIBITMAP *U, int n, int ncycle) { +	int j, jcycle, jj, jpost, jpre, nf, ngrid; + +	FIBITMAP **IRHO = NULL; +	FIBITMAP **IU   = NULL; +	FIBITMAP **IRHS = NULL; +	FIBITMAP **IRES = NULL; +	 +	int ng = 0;		// number of allocated grids + +// -------------------------------------------------------------------------- + +#define _CREATE_ARRAY_GRID_(array, array_size) \ +	array = (FIBITMAP**)malloc(array_size * sizeof(FIBITMAP*));\ +	if(!array) throw(1);\ +	memset(array, 0, array_size * sizeof(FIBITMAP*)) + +#define _FREE_ARRAY_GRID_(array, array_size) \ +	if(NULL != array) {\ +		for(int k = 0; k < array_size; k++) {\ +			if(NULL != array[k]) {\ +				FreeImage_Unload(array[k]); array[k] = NULL;\ +			}\ +		}\ +		free(array);\ +	} + +// -------------------------------------------------------------------------- + +	try { +		int nn = n; +		// check grid size and grid levels +		while (nn >>= 1) ng++; +		if (n != 1 + (1L << ng)) { +			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Multigrid algorithm: n = %d, while n-1 must be a power of 2.", n); +			throw(1); +		} +		if (ng > NGMAX) { +			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Multigrid algorithm: ng = %d while NGMAX = %d, increase NGMAX.", ng, NGMAX); +			throw(1); +		} +		// allocate grid arrays +		{ +			_CREATE_ARRAY_GRID_(IRHO, ng); +			_CREATE_ARRAY_GRID_(IU, ng); +			_CREATE_ARRAY_GRID_(IRHS, ng); +			_CREATE_ARRAY_GRID_(IRES, ng); +		} + +		nn = n/2 + 1; +		ngrid = ng - 2; + +		// allocate storage for r.h.s. on grid (ng - 2) ... +		IRHO[ngrid] = FreeImage_AllocateT(FIT_FLOAT, nn, nn); +		if(!IRHO[ngrid]) throw(1); + +		// ... and fill it by restricting from the fine grid +		fmg_restrict(IRHO[ngrid], U, nn);	 + +		// similarly allocate storage and fill r.h.s. on all coarse grids. +		while (nn > 3) { +			nn = nn/2 + 1;  +			ngrid--; +			IRHO[ngrid] = FreeImage_AllocateT(FIT_FLOAT, nn, nn); +			if(!IRHO[ngrid]) throw(1); +			fmg_restrict(IRHO[ngrid], IRHO[ngrid+1], nn); +		} + +		nn = 3; + +		IU[0] = FreeImage_AllocateT(FIT_FLOAT, nn, nn); +		if(!IU[0]) throw(1); +		IRHS[0] = FreeImage_AllocateT(FIT_FLOAT, nn, nn); +		if(!IRHS[0]) throw(1); + +		// initial solution on coarsest grid +		fmg_solve(IU[0], IRHO[0]); +		// irho[0] no longer needed ... +		FreeImage_Unload(IRHO[0]); IRHO[0] = NULL; + +		ngrid = ng; + +		// nested iteration loop +		for (j = 1; j < ngrid; j++) { +			nn = 2*nn - 1; + +			IU[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn); +			if(!IU[j]) throw(1); +			IRHS[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn); +			if(!IRHS[j]) throw(1); +			IRES[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn); +			if(!IRES[j]) throw(1); + +			fmg_prolongate(IU[j], IU[j-1], nn); +			 +			// interpolate from coarse grid to next finer grid + +			// set up r.h.s. +			fmg_copyArray(IRHS[j], j != (ngrid - 1) ? IRHO[j] : U); +			 +			// V-cycle loop +			for (jcycle = 0; jcycle < ncycle; jcycle++) { +				nf = nn; +				// downward stoke of the V +				for (jj = j; jj >= 1; jj--) { +					// pre-smoothing +					for (jpre = 0; jpre < NPRE; jpre++) { +						fmg_relaxation(IU[jj], IRHS[jj], nf); +					} +					fmg_residual(IRES[jj], IU[jj], IRHS[jj], nf); +					nf = nf/2 + 1; +					// restriction of the residual is the next r.h.s. +					fmg_restrict(IRHS[jj-1], IRES[jj], nf);				 +					// zero for initial guess in next relaxation +					fmg_fillArrayWithZeros(IU[jj-1]); +				} +				// bottom of V: solve on coarsest grid +				fmg_solve(IU[0], IRHS[0]);  +				nf = 3;  +				// upward stroke of V. +				for (jj = 1; jj <= j; jj++) {  +					nf = 2*nf - 1; +					// use res for temporary storage inside addint +					fmg_addint(IU[jj], IU[jj-1], IRES[jj], nf);				 +					// post-smoothing +					for (jpost = 0; jpost < NPOST; jpost++) { +						fmg_relaxation(IU[jj], IRHS[jj], nf); +					} +				} +			} +		} + +		// return solution in U +		fmg_copyArray(U, IU[ngrid-1]); + +		// delete allocated arrays +		_FREE_ARRAY_GRID_(IRES, ng); +		_FREE_ARRAY_GRID_(IRHS, ng); +		_FREE_ARRAY_GRID_(IU, ng); +		_FREE_ARRAY_GRID_(IRHO, ng); + +		return TRUE; + +	} catch(int) { +		// delete allocated arrays +		_FREE_ARRAY_GRID_(IRES, ng); +		_FREE_ARRAY_GRID_(IRHS, ng); +		_FREE_ARRAY_GRID_(IU, ng); +		_FREE_ARRAY_GRID_(IRHO, ng); + +		return FALSE; +	} +} + +// -------------------------------------------------------------------------- + +/** +Poisson solver based on a multigrid 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(!FreeImage_HasPixels(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++; +	if((1 << size) < MAX(width, height)) { +		size++; +	} +	// size must be of the form 2^j + 1 for some integer j +	size = 1 + (1 << size); + +	// 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); + +	// copy metadata from src to dst +	FreeImage_CloneMetadata(U, Laplacian); + +	// return the integrated image +	return U; +} +  | 
