/src/FreeImage/Source/FreeImageToolkit/MultigridPoissonSolver.cpp

https://bitbucket.org/cabalistic/ogredeps/ · C++ · 505 lines · 297 code · 67 blank · 141 comment · 47 complexity · fb22ce7e555c960bcdc5780976223ec4 MD5 · raw file

  1. // ==========================================================
  2. // Poisson solver based on a full multigrid algorithm
  3. //
  4. // Design and implementation by
  5. // - Hervé Drolon (drolon@infonie.fr)
  6. // Reference:
  7. // PRESS, W. H., TEUKOLSKY, S. A., VETTERLING, W. T., AND FLANNERY, B. P.
  8. // 1992. Numerical Recipes in C: The Art of Scientific Computing, 2nd ed. Cambridge University Press.
  9. //
  10. // This file is part of FreeImage 3
  11. //
  12. // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
  13. // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
  14. // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
  15. // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
  16. // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
  17. // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
  18. // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
  19. // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
  20. // THIS DISCLAIMER.
  21. //
  22. // Use at your own risk!
  23. // ==========================================================
  24. #include "FreeImage.h"
  25. #include "Utilities.h"
  26. #include "ToneMapping.h"
  27. static const int NPRE = 1; // Number of relaxation sweeps before ...
  28. static const int NPOST = 1; // ... and after the coarse-grid correction is computed
  29. static const int NGMAX = 15; // Maximum number of grids
  30. /**
  31. Copy src into dst
  32. */
  33. static inline void fmg_copyArray(FIBITMAP *dst, FIBITMAP *src) {
  34. memcpy(FreeImage_GetBits(dst), FreeImage_GetBits(src), FreeImage_GetHeight(dst) * FreeImage_GetPitch(dst));
  35. }
  36. /**
  37. Fills src with zeros
  38. */
  39. static inline void fmg_fillArrayWithZeros(FIBITMAP *src) {
  40. memset(FreeImage_GetBits(src), 0, FreeImage_GetHeight(src) * FreeImage_GetPitch(src));
  41. }
  42. /**
  43. Half-weighting restriction. nc is the coarse-grid dimension. The fine-grid solution is input in
  44. uf[0..2*nc-2][0..2*nc-2], the coarse-grid solution is returned in uc[0..nc-1][0..nc-1].
  45. */
  46. static void fmg_restrict(FIBITMAP *UC, FIBITMAP *UF, int nc) {
  47. int row_uc, row_uf, col_uc, col_uf;
  48. const int uc_pitch = FreeImage_GetPitch(UC) / sizeof(float);
  49. const int uf_pitch = FreeImage_GetPitch(UF) / sizeof(float);
  50. float *uc_bits = (float*)FreeImage_GetBits(UC);
  51. const float *uf_bits = (float*)FreeImage_GetBits(UF);
  52. // interior points
  53. {
  54. float *uc_scan = uc_bits + uc_pitch;
  55. for (row_uc = 1, row_uf = 2; row_uc < nc-1; row_uc++, row_uf += 2) {
  56. const float *uf_scan = uf_bits + row_uf * uf_pitch;
  57. for (col_uc = 1, col_uf = 2; col_uc < nc-1; col_uc++, col_uf += 2) {
  58. // calculate
  59. // UC(row_uc, col_uc) =
  60. // 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) ]
  61. float *uc_pixel = uc_scan + col_uc;
  62. const float *uf_center = uf_scan + col_uf;
  63. *uc_pixel = 0.5F * *uf_center + 0.125F * ( *(uf_center + uf_pitch) + *(uf_center - uf_pitch) + *(uf_center + 1) + *(uf_center - 1) );
  64. }
  65. uc_scan += uc_pitch;
  66. }
  67. }
  68. // boundary points
  69. const int ncc = 2*nc-1;
  70. {
  71. /*
  72. calculate the following:
  73. for (row_uc = 0, row_uf = 0; row_uc < nc; row_uc++, row_uf += 2) {
  74. UC(row_uc, 0) = UF(row_uf, 0);
  75. UC(row_uc, nc-1) = UF(row_uf, ncc-1);
  76. }
  77. */
  78. float *uc_scan = uc_bits;
  79. for (row_uc = 0, row_uf = 0; row_uc < nc; row_uc++, row_uf += 2) {
  80. const float *uf_scan = uf_bits + row_uf * uf_pitch;
  81. uc_scan[0] = uf_scan[0];
  82. uc_scan[nc-1] = uf_scan[ncc-1];
  83. uc_scan += uc_pitch;
  84. }
  85. }
  86. {
  87. /*
  88. calculate the following:
  89. for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) {
  90. UC(0, col_uc) = UF(0, col_uf);
  91. UC(nc-1, col_uc) = UF(ncc-1, col_uf);
  92. }
  93. */
  94. float *uc_scan_top = uc_bits;
  95. float *uc_scan_bottom = uc_bits + (nc-1)*uc_pitch;
  96. const float *uf_scan_top = uf_bits + (ncc-1)*uf_pitch;
  97. const float *uf_scan_bottom = uf_bits;
  98. for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) {
  99. uc_scan_top[col_uc] = uf_scan_top[col_uf];
  100. uc_scan_bottom[col_uc] = uf_scan_bottom[col_uf];
  101. }
  102. }
  103. }
  104. /**
  105. Solution of the model problem on the coarsest grid, where h = 1/2 .
  106. The right-hand side is input
  107. in rhs[0..2][0..2] and the solution is returned in u[0..2][0..2].
  108. */
  109. static void fmg_solve(FIBITMAP *U, FIBITMAP *RHS) {
  110. // fill U with zeros
  111. fmg_fillArrayWithZeros(U);
  112. // calculate U(1, 1) = -h*h*RHS(1, 1)/4.0 where h = 1/2
  113. float *u_scan = (float*)FreeImage_GetScanLine(U, 1);
  114. const float *rhs_scan = (float*)FreeImage_GetScanLine(RHS, 1);
  115. u_scan[1] = -rhs_scan[1] / 16;
  116. }
  117. /**
  118. Coarse-to-fine prolongation by bilinear interpolation. nf is the fine-grid dimension. The coarsegrid
  119. solution is input as uc[0..nc-1][0..nc-1], where nc = nf/2 + 1. The fine-grid solution is
  120. returned in uf[0..nf-1][0..nf-1].
  121. */
  122. static void fmg_prolongate(FIBITMAP *UF, FIBITMAP *UC, int nf) {
  123. int row_uc, row_uf, col_uc, col_uf;
  124. const int uf_pitch = FreeImage_GetPitch(UF) / sizeof(float);
  125. const int uc_pitch = FreeImage_GetPitch(UC) / sizeof(float);
  126. float *uf_bits = (float*)FreeImage_GetBits(UF);
  127. const float *uc_bits = (float*)FreeImage_GetBits(UC);
  128. // do elements that are copies
  129. {
  130. const int nc = nf/2 + 1;
  131. float *uf_scan = uf_bits;
  132. const float *uc_scan = uc_bits;
  133. for (row_uc = 0; row_uc < nc; row_uc++) {
  134. for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) {
  135. // calculate UF(2*row_uc, col_uf) = UC(row_uc, col_uc);
  136. uf_scan[col_uf] = uc_scan[col_uc];
  137. }
  138. uc_scan += uc_pitch;
  139. uf_scan += 2 * uf_pitch;
  140. }
  141. }
  142. // do odd-numbered columns, interpolating vertically
  143. {
  144. for(row_uf = 1; row_uf < nf-1; row_uf += 2) {
  145. float *uf_scan = uf_bits + row_uf * uf_pitch;
  146. for (col_uf = 0; col_uf < nf; col_uf += 2) {
  147. // calculate UF(row_uf, col_uf) = 0.5 * ( UF(row_uf+1, col_uf) + UF(row_uf-1, col_uf) )
  148. uf_scan[col_uf] = 0.5F * ( *(uf_scan + uf_pitch + col_uf) + *(uf_scan - uf_pitch + col_uf) );
  149. }
  150. }
  151. }
  152. // do even-numbered columns, interpolating horizontally
  153. {
  154. float *uf_scan = uf_bits;
  155. for(row_uf = 0; row_uf < nf; row_uf++) {
  156. for (col_uf = 1; col_uf < nf-1; col_uf += 2) {
  157. // calculate UF(row_uf, col_uf) = 0.5 * ( UF(row_uf, col_uf+1) + UF(row_uf, col_uf-1) )
  158. uf_scan[col_uf] = 0.5F * ( uf_scan[col_uf + 1] + uf_scan[col_uf - 1] );
  159. }
  160. uf_scan += uf_pitch;
  161. }
  162. }
  163. }
  164. /**
  165. Red-black Gauss-Seidel relaxation for model problem. Updates the current value of the solution
  166. u[0..n-1][0..n-1], using the right-hand side function rhs[0..n-1][0..n-1].
  167. */
  168. static void fmg_relaxation(FIBITMAP *U, FIBITMAP *RHS, int n) {
  169. int row, col, ipass, isw, jsw;
  170. const float h = 1.0F / (n - 1);
  171. const float h2 = h*h;
  172. const int u_pitch = FreeImage_GetPitch(U) / sizeof(float);
  173. const int rhs_pitch = FreeImage_GetPitch(RHS) / sizeof(float);
  174. float *u_bits = (float*)FreeImage_GetBits(U);
  175. const float *rhs_bits = (float*)FreeImage_GetBits(RHS);
  176. for (ipass = 0, jsw = 1; ipass < 2; ipass++, jsw = 3-jsw) { // Red and black sweeps
  177. float *u_scan = u_bits + u_pitch;
  178. const float *rhs_scan = rhs_bits + rhs_pitch;
  179. for (row = 1, isw = jsw; row < n-1; row++, isw = 3-isw) {
  180. for (col = isw; col < n-1; col += 2) {
  181. // Gauss-Seidel formula
  182. // calculate U(row, col) =
  183. // 0.25 * [ U(row+1, col) + U(row-1, col) + U(row, col+1) + U(row, col-1) - h2 * RHS(row, col) ]
  184. float *u_center = u_scan + col;
  185. const float *rhs_center = rhs_scan + col;
  186. *u_center = *(u_center + u_pitch) + *(u_center - u_pitch) + *(u_center + 1) + *(u_center - 1);
  187. *u_center -= h2 * *rhs_center;
  188. *u_center *= 0.25F;
  189. }
  190. u_scan += u_pitch;
  191. rhs_scan += rhs_pitch;
  192. }
  193. }
  194. }
  195. /**
  196. Returns minus the residual for the model problem. Input quantities are u[0..n-1][0..n-1] and
  197. rhs[0..n-1][0..n-1], while res[0..n-1][0..n-1] is returned.
  198. */
  199. static void fmg_residual(FIBITMAP *RES, FIBITMAP *U, FIBITMAP *RHS, int n) {
  200. int row, col;
  201. const float h = 1.0F / (n-1);
  202. const float h2i = 1.0F / (h*h);
  203. const int res_pitch = FreeImage_GetPitch(RES) / sizeof(float);
  204. const int u_pitch = FreeImage_GetPitch(U) / sizeof(float);
  205. const int rhs_pitch = FreeImage_GetPitch(RHS) / sizeof(float);
  206. float *res_bits = (float*)FreeImage_GetBits(RES);
  207. const float *u_bits = (float*)FreeImage_GetBits(U);
  208. const float *rhs_bits = (float*)FreeImage_GetBits(RHS);
  209. // interior points
  210. {
  211. float *res_scan = res_bits + res_pitch;
  212. const float *u_scan = u_bits + u_pitch;
  213. const float *rhs_scan = rhs_bits + rhs_pitch;
  214. for (row = 1; row < n-1; row++) {
  215. for (col = 1; col < n-1; col++) {
  216. // calculate RES(row, col) =
  217. // -h2i * [ U(row+1, col) + U(row-1, col) + U(row, col+1) + U(row, col-1) - 4 * U(row, col) ] + RHS(row, col);
  218. float *res_center = res_scan + col;
  219. const float *u_center = u_scan + col;
  220. const float *rhs_center = rhs_scan + col;
  221. *res_center = *(u_center + u_pitch) + *(u_center - u_pitch) + *(u_center + 1) + *(u_center - 1) - 4 * *u_center;
  222. *res_center *= -h2i;
  223. *res_center += *rhs_center;
  224. }
  225. res_scan += res_pitch;
  226. u_scan += u_pitch;
  227. rhs_scan += rhs_pitch;
  228. }
  229. }
  230. // boundary points
  231. {
  232. memset(FreeImage_GetScanLine(RES, 0), 0, FreeImage_GetPitch(RES));
  233. memset(FreeImage_GetScanLine(RES, n-1), 0, FreeImage_GetPitch(RES));
  234. float *left = res_bits;
  235. float *right = res_bits + (n-1);
  236. for(int k = 0; k < n; k++) {
  237. *left = 0;
  238. *right = 0;
  239. left += res_pitch;
  240. right += res_pitch;
  241. }
  242. }
  243. }
  244. /**
  245. Does coarse-to-fine interpolation and adds result to uf. nf is the fine-grid dimension. The
  246. coarse-grid solution is input as uc[0..nc-1][0..nc-1], where nc = nf/2+1. The fine-grid solution
  247. is returned in uf[0..nf-1][0..nf-1]. res[0..nf-1][0..nf-1] is used for temporary storage.
  248. */
  249. static void fmg_addint(FIBITMAP *UF, FIBITMAP *UC, FIBITMAP *RES, int nf) {
  250. fmg_prolongate(RES, UC, nf);
  251. const int uf_pitch = FreeImage_GetPitch(UF) / sizeof(float);
  252. const int res_pitch = FreeImage_GetPitch(RES) / sizeof(float);
  253. float *uf_bits = (float*)FreeImage_GetBits(UF);
  254. const float *res_bits = (float*)FreeImage_GetBits(RES);
  255. for(int row = 0; row < nf; row++) {
  256. for(int col = 0; col < nf; col++) {
  257. // calculate UF(row, col) = UF(row, col) + RES(row, col);
  258. uf_bits[col] += res_bits[col];
  259. }
  260. uf_bits += uf_pitch;
  261. res_bits += res_pitch;
  262. }
  263. }
  264. /**
  265. Full Multigrid Algorithm for solution of linear elliptic equation, here the model problem (19.0.6).
  266. On input u[0..n-1][0..n-1] contains the right-hand side ñ, while on output it returns the solution.
  267. The dimension n must be of the form 2^j + 1 for some integer j. (j is actually the number of
  268. grid levels used in the solution, called ng below.) ncycle is the number of V-cycles to be
  269. used at each level.
  270. */
  271. static BOOL fmg_mglin(FIBITMAP *U, int n, int ncycle) {
  272. int j, jcycle, jj, jpost, jpre, nf, ngrid;
  273. FIBITMAP **IRHO = NULL;
  274. FIBITMAP **IU = NULL;
  275. FIBITMAP **IRHS = NULL;
  276. FIBITMAP **IRES = NULL;
  277. int ng = 0; // number of allocated grids
  278. // --------------------------------------------------------------------------
  279. #define _CREATE_ARRAY_GRID_(array, array_size) \
  280. array = (FIBITMAP**)malloc(array_size * sizeof(FIBITMAP*));\
  281. if(!array) throw(1);\
  282. memset(array, 0, array_size * sizeof(FIBITMAP*))
  283. #define _FREE_ARRAY_GRID_(array, array_size) \
  284. if(NULL != array) {\
  285. for(int k = 0; k < array_size; k++) {\
  286. if(NULL != array[k]) {\
  287. FreeImage_Unload(array[k]); array[k] = NULL;\
  288. }\
  289. }\
  290. free(array);\
  291. }
  292. // --------------------------------------------------------------------------
  293. try {
  294. int nn = n;
  295. // check grid size and grid levels
  296. while (nn >>= 1) ng++;
  297. if (n != 1 + (1L << ng)) {
  298. FreeImage_OutputMessageProc(FIF_UNKNOWN, "Multigrid algorithm: n = %d, while n-1 must be a power of 2.", n);
  299. throw(1);
  300. }
  301. if (ng > NGMAX) {
  302. FreeImage_OutputMessageProc(FIF_UNKNOWN, "Multigrid algorithm: ng = %d while NGMAX = %d, increase NGMAX.", ng, NGMAX);
  303. throw(1);
  304. }
  305. // allocate grid arrays
  306. {
  307. _CREATE_ARRAY_GRID_(IRHO, ng);
  308. _CREATE_ARRAY_GRID_(IU, ng);
  309. _CREATE_ARRAY_GRID_(IRHS, ng);
  310. _CREATE_ARRAY_GRID_(IRES, ng);
  311. }
  312. nn = n/2 + 1;
  313. ngrid = ng - 2;
  314. // allocate storage for r.h.s. on grid (ng - 2) ...
  315. IRHO[ngrid] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
  316. if(!IRHO[ngrid]) throw(1);
  317. // ... and fill it by restricting from the fine grid
  318. fmg_restrict(IRHO[ngrid], U, nn);
  319. // similarly allocate storage and fill r.h.s. on all coarse grids.
  320. while (nn > 3) {
  321. nn = nn/2 + 1;
  322. ngrid--;
  323. IRHO[ngrid] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
  324. if(!IRHO[ngrid]) throw(1);
  325. fmg_restrict(IRHO[ngrid], IRHO[ngrid+1], nn);
  326. }
  327. nn = 3;
  328. IU[0] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
  329. if(!IU[0]) throw(1);
  330. IRHS[0] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
  331. if(!IRHS[0]) throw(1);
  332. // initial solution on coarsest grid
  333. fmg_solve(IU[0], IRHO[0]);
  334. // irho[0] no longer needed ...
  335. FreeImage_Unload(IRHO[0]); IRHO[0] = NULL;
  336. ngrid = ng;
  337. // nested iteration loop
  338. for (j = 1; j < ngrid; j++) {
  339. nn = 2*nn - 1;
  340. IU[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
  341. if(!IU[j]) throw(1);
  342. IRHS[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
  343. if(!IRHS[j]) throw(1);
  344. IRES[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
  345. if(!IRES[j]) throw(1);
  346. fmg_prolongate(IU[j], IU[j-1], nn);
  347. // interpolate from coarse grid to next finer grid
  348. // set up r.h.s.
  349. fmg_copyArray(IRHS[j], j != (ngrid - 1) ? IRHO[j] : U);
  350. // V-cycle loop
  351. for (jcycle = 0; jcycle < ncycle; jcycle++) {
  352. nf = nn;
  353. // downward stoke of the V
  354. for (jj = j; jj >= 1; jj--) {
  355. // pre-smoothing
  356. for (jpre = 0; jpre < NPRE; jpre++) {
  357. fmg_relaxation(IU[jj], IRHS[jj], nf);
  358. }
  359. fmg_residual(IRES[jj], IU[jj], IRHS[jj], nf);
  360. nf = nf/2 + 1;
  361. // restriction of the residual is the next r.h.s.
  362. fmg_restrict(IRHS[jj-1], IRES[jj], nf);
  363. // zero for initial guess in next relaxation
  364. fmg_fillArrayWithZeros(IU[jj-1]);
  365. }
  366. // bottom of V: solve on coarsest grid
  367. fmg_solve(IU[0], IRHS[0]);
  368. nf = 3;
  369. // upward stroke of V.
  370. for (jj = 1; jj <= j; jj++) {
  371. nf = 2*nf - 1;
  372. // use res for temporary storage inside addint
  373. fmg_addint(IU[jj], IU[jj-1], IRES[jj], nf);
  374. // post-smoothing
  375. for (jpost = 0; jpost < NPOST; jpost++) {
  376. fmg_relaxation(IU[jj], IRHS[jj], nf);
  377. }
  378. }
  379. }
  380. }
  381. // return solution in U
  382. fmg_copyArray(U, IU[ngrid-1]);
  383. // delete allocated arrays
  384. _FREE_ARRAY_GRID_(IRES, ng);
  385. _FREE_ARRAY_GRID_(IRHS, ng);
  386. _FREE_ARRAY_GRID_(IU, ng);
  387. _FREE_ARRAY_GRID_(IRHO, ng);
  388. return TRUE;
  389. } catch(int) {
  390. // delete allocated arrays
  391. _FREE_ARRAY_GRID_(IRES, ng);
  392. _FREE_ARRAY_GRID_(IRHS, ng);
  393. _FREE_ARRAY_GRID_(IU, ng);
  394. _FREE_ARRAY_GRID_(IRHO, ng);
  395. return FALSE;
  396. }
  397. }
  398. // --------------------------------------------------------------------------
  399. /**
  400. Poisson solver based on a multigrid algorithm.
  401. This routine solves a Poisson equation, remap result pixels to [0..1] and returns the solution.
  402. 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,
  403. where j is such that 2^j is the nearest larger dimension corresponding to MAX(image width, image height).
  404. @param Laplacian Laplacian image
  405. @param ncycle Number of cycles in the multigrid algorithm (usually 2 or 3)
  406. @return Returns the solved PDE equations if successful, returns NULL otherwise
  407. */
  408. FIBITMAP* DLL_CALLCONV
  409. FreeImage_MultigridPoissonSolver(FIBITMAP *Laplacian, int ncycle) {
  410. if(!FreeImage_HasPixels(Laplacian)) return NULL;
  411. int width = FreeImage_GetWidth(Laplacian);
  412. int height = FreeImage_GetHeight(Laplacian);
  413. // get nearest larger dimension length that is acceptable by the algorithm
  414. int n = MAX(width, height);
  415. int size = 0;
  416. while((n >>= 1) > 0) size++;
  417. if((1 << size) < MAX(width, height)) {
  418. size++;
  419. }
  420. // size must be of the form 2^j + 1 for some integer j
  421. size = 1 + (1 << size);
  422. // allocate a temporary square image I
  423. FIBITMAP *I = FreeImage_AllocateT(FIT_FLOAT, size, size);
  424. if(!I) return NULL;
  425. // copy Laplacian into I and shift pixels to create a boundary
  426. FreeImage_Paste(I, Laplacian, 1, 1, 255);
  427. // solve the PDE equation
  428. fmg_mglin(I, size, ncycle);
  429. // shift pixels back
  430. FIBITMAP *U = FreeImage_Copy(I, 1, 1, width + 1, height + 1);
  431. FreeImage_Unload(I);
  432. // remap pixels to [0..1]
  433. NormalizeY(U, 0, 1);
  434. // copy metadata from src to dst
  435. FreeImage_CloneMetadata(U, Laplacian);
  436. // return the integrated image
  437. return U;
  438. }