1 /*******************************************************************************
2 **
3 ** Photivo
4 **
5 ** Copyright (C) 2008 Jos De Laender <jos.de_laender@telenet.be>
6 **
7 ** This file is part of Photivo.
8 **
9 ** Photivo is free software: you can redistribute it and/or modify
10 ** it under the terms of the GNU General Public License version 3
11 ** as published by the Free Software Foundation.
12 **
13 ** Photivo is distributed in the hope that it will be useful,
14 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ** GNU General Public License for more details.
17 **
18 ** You should have received a copy of the GNU General Public License
19 ** along with Photivo. If not, see <http://www.gnu.org/licenses/>.
20 **
21 ********************************************************************************
22 **
23 ** This file is largely based on the digiKam project.
24 ** http://www.digikam.org
25 ** Copyright (C) 2005-2007 by Gilles Caulier
26 ** <caulier dot gilles at gmail dot com>
27 ** Original implementation from Refocus Gimp plug-in
28 ** Copyright (C) 1999-2003 Ernst Lippe
29 **
30 *******************************************************************************/
31
32 // Uncomment this line to debug matrix computation.
33 //#define RF_DEBUG 1
34
35 #include "ptDefines.h"
36 #include "ptRefocusMatrix.h"
37
38 #include <cassert>
39 #include <cstring>
40 #include <cmath>
41
42 extern "C"
43 {
44 #include "clapack/f2c.h"
45 #include "clapack/clapack.h"
46 }
47
AllocateMatrix(int nRows,int nCols)48 ptMat *ptRefocusMatrix::AllocateMatrix(int nRows, int nCols) {
49 ptMat *Result = new ptMat;
50 memset (Result, 0, sizeof(Result));
51
52 Result->Cols = nCols;
53 Result->Rows = nRows;
54 Result->Data = new double [nRows * nCols];
55 memset(Result->Data, 0, nRows * nCols * sizeof(double));
56
57 return (Result);
58 }
59
FinishMatrix(ptMat * mat)60 void ptRefocusMatrix::FinishMatrix (ptMat* mat) {
61 delete [] mat->Data;
62 }
63
FinishAndFreeMatrix(ptMat * mat)64 void ptRefocusMatrix::FinishAndFreeMatrix(ptMat* mat) {
65 delete [] mat->Data;
66 delete mat;
67 }
68
mat_eltptr(ptMat * mat,const int r,const int c)69 double *ptRefocusMatrix::mat_eltptr(ptMat* mat,const int r,const int c) {
70 assert ((r >= 0) && (r < mat->Rows));
71 assert ((c >= 0) && (c < mat->Rows));
72 return (&(mat->Data[mat->Rows * c + r]));
73 }
74
mat_elt(const ptMat * mat,const int r,const int c)75 double ptRefocusMatrix::mat_elt(const ptMat* mat,const int r,const int c) {
76 assert ((r >= 0) && (r < mat->Rows));
77 assert ((c >= 0) && (c < mat->Rows));
78 return (mat->Data[mat->Rows * c + r]);
79 }
80
InitCMatrix(ptCMat * Matrix,const int MatrixRadius)81 void ptRefocusMatrix::InitCMatrix(ptCMat* Matrix,
82 const int MatrixRadius) {
83 Matrix->Radius = MatrixRadius;
84 Matrix->RowStride = 2 * MatrixRadius + 1;
85 Matrix->Data = new double [SQR (Matrix->RowStride)];
86 memset (Matrix->Data, 0, SQR (Matrix->RowStride) * sizeof(double));
87 Matrix->Center =
88 Matrix->Data + Matrix->RowStride * Matrix->Radius + Matrix->Radius;
89 }
90
AllocateCMatrix(const int MatrixRadius)91 ptCMat *ptRefocusMatrix::AllocateCMatrix(const int MatrixRadius) {
92 ptCMat *Result = new ptCMat;
93 memset(Result, 0, sizeof(Result));
94 InitCMatrix(Result, MatrixRadius);
95 return (Result);
96 }
97
FinishCMatrix(ptCMat * mat)98 void ptRefocusMatrix::FinishCMatrix(ptCMat* mat) {
99 delete [] mat->Data;
100 mat->Data = NULL;
101 }
102
c_mat_eltptr(ptCMat * mat,const int col,const int row)103 inline double *ptRefocusMatrix::c_mat_eltptr(ptCMat* mat,
104 const int col,
105 const int row) {
106 assert ((abs (row) <= mat->Radius) && (abs (col) <= mat->Radius));
107 return (mat->Center + mat->RowStride * row + col);
108 }
109
c_mat_elt(const ptCMat * const mat,const int col,const int row)110 inline double ptRefocusMatrix::c_mat_elt(const ptCMat* const mat,
111 const int col, const int row) {
112 assert ((abs (row) <= mat->Radius) && (abs (col) <= mat->Radius));
113 return (mat->Center[mat->RowStride * row + col]);
114 }
115
ConvolveMatrix(ptCMat * Result,const ptCMat * const mata,const ptCMat * const matb)116 void ptRefocusMatrix::ConvolveMatrix( ptCMat* Result,
117 const ptCMat* const mata,
118 const ptCMat* const matb) {
119 register int xr, yr, xa, ya;
120
121 for (yr = -Result->Radius; yr <= Result->Radius; yr++) {
122 for (xr = -Result->Radius; xr <= Result->Radius; xr++) {
123 const int ya_low = MAX (-mata->Radius, yr - matb->Radius);
124 const int ya_high = MIN (mata->Radius, yr + matb->Radius);
125 const int xa_low = MAX (-mata->Radius, xr - matb->Radius);
126 const int xa_high = MIN (mata->Radius, xr + matb->Radius);
127 register double val = 0.0;
128
129 for (ya = ya_low; ya <= ya_high; ya++) {
130 for (xa = xa_low; xa <= xa_high; xa++) {
131 val += c_mat_elt (mata, xa, ya) *
132 c_mat_elt (matb, xr - xa, yr - ya);
133 }
134 }
135
136 *c_mat_eltptr (Result, xr, yr) = val;
137 }
138 }
139 }
140
ConvolveStarMatrix(ptCMat * Result,const ptCMat * const mata,const ptCMat * const matb)141 void ptRefocusMatrix::ConvolveStarMatrix( ptCMat* Result,
142 const ptCMat* const mata,
143 const ptCMat* const matb) {
144 register int xr, yr, xa, ya;
145
146 for (yr = -Result->Radius; yr <= Result->Radius; yr++) {
147 for (xr = -Result->Radius; xr <= Result->Radius; xr++) {
148 const int ya_low = MAX (-mata->Radius, -matb->Radius - yr);
149 const int ya_high = MIN (mata->Radius, matb->Radius - yr);
150 const int xa_low = MAX (-mata->Radius, -matb->Radius - xr);
151 const int xa_high = MIN (mata->Radius, matb->Radius - xr);
152 register double val = 0.0;
153
154 for (ya = ya_low; ya <= ya_high; ya++) {
155 for (xa = xa_low; xa <= xa_high; xa++) {
156 val += c_mat_elt (mata, xa, ya) *
157 c_mat_elt (matb, xr + xa, yr + ya);
158 }
159 }
160
161 *c_mat_eltptr (Result, xr, yr) = val;
162 }
163 }
164 }
165
ConvolveMatrixFunction(ptCMat * Result,const ptCMat * const mata,double (f)(int,int))166 void ptRefocusMatrix::ConvolveMatrixFunction( ptCMat* Result,
167 const ptCMat* const mata,
168 double (f) (int, int)) {
169 register int xr, yr, xa, ya;
170
171 for (yr = -Result->Radius; yr <= Result->Radius; yr++) {
172 for (xr = -Result->Radius; xr <= Result->Radius; xr++) {
173 register double val = 0.0;
174
175 for (ya = -mata->Radius; ya <= mata->Radius; ya++) {
176 for (xa = -mata->Radius; xa <= mata->Radius; xa++) {
177 val += c_mat_elt (mata, xa, ya) * f (xr - xa, yr - ya);
178 }
179 }
180
181 *c_mat_eltptr (Result, xr, yr) = val;
182 }
183 }
184 }
185
as_idx(const int k,const int l,const int m)186 int ptRefocusMatrix::as_idx (const int k, const int l, const int m) {
187 return ((k + m) * (2 * m + 1) + (l + m));
188 }
189
as_cidx(const int k,const int l)190 int ptRefocusMatrix::as_cidx (const int k, const int l) {
191 const int a = MAX (abs (k), abs (l));
192 const int b = MIN (abs (k), abs (l));
193 return ((a * (a + 1)) / 2 + b);
194 }
195
MakeSMatrix(ptCMat * Matrix,int MatrixRadius,double NoiseFactor)196 ptMat *ptRefocusMatrix::MakeSMatrix(ptCMat* Matrix,
197 int MatrixRadius,
198 double NoiseFactor) {
199 const int mat_size = SQR (2 * MatrixRadius + 1);
200 ptMat *Result = AllocateMatrix (mat_size, mat_size);
201 register int yr, yc, xr, xc;
202
203 for (yr = -MatrixRadius; yr <= MatrixRadius; yr++) {
204 for (xr = -MatrixRadius; xr <= MatrixRadius; xr++) {
205 for (yc = -MatrixRadius; yc <= MatrixRadius; yc++) {
206 for (xc = -MatrixRadius; xc <= MatrixRadius; xc++) {
207 *mat_eltptr(Result,as_idx(xr,yr,MatrixRadius),
208 as_idx(xc,yc,MatrixRadius)) =
209 c_mat_elt(Matrix, xr - xc, yr - yc);
210 if ((xr == xc) && (yr == yc)) {
211 *mat_eltptr (Result,as_idx(xr,yr,MatrixRadius),
212 as_idx(xc,yc,MatrixRadius)) += NoiseFactor;
213 }
214 }
215 }
216 }
217 }
218
219 return (Result);
220 }
221
MakeSCMatrix(ptCMat * Matrix,int MatrixRadius,double NoiseFactor)222 ptMat *ptRefocusMatrix::MakeSCMatrix(ptCMat* Matrix,
223 int MatrixRadius,
224 double NoiseFactor) {
225 const int mat_size = as_cidx (MatrixRadius + 1, 0);
226 ptMat *Result = AllocateMatrix (mat_size, mat_size);
227 register int yr, yc, xr, xc;
228
229 for (yr = 0; yr <=MatrixRadius; yr++) {
230 for (xr = 0; xr <= yr; xr++) {
231 for (yc = -MatrixRadius; yc <= MatrixRadius; yc++) {
232 for (xc = -MatrixRadius; xc <= MatrixRadius; xc++) {
233 *mat_eltptr (Result, as_cidx (xr, yr), as_cidx (xc, yc)) +=
234 c_mat_elt (Matrix, xr - xc, yr - yc);
235 if ((xr == xc) && (yr == yc)) {
236 *mat_eltptr (Result, as_cidx (xr, yr),
237 as_cidx (xc, yc)) += NoiseFactor;
238 }
239 }
240 }
241 }
242 }
243
244 return (Result);
245 }
246
Correlation(const int x,const int y,const double Gamma,const double musq)247 double ptRefocusMatrix::Correlation(const int x,
248 const int y,
249 const double Gamma,
250 const double musq) {
251 return (musq + pow (Gamma, sqrt (SQR (x) + SQR (y))));
252 }
253
CopyVector(const ptCMat * const mat,const int m)254 ptMat *ptRefocusMatrix::CopyVector(const ptCMat* const mat, const int m) {
255 ptMat *Result = AllocateMatrix (SQR (2 * m + 1), 1);
256 register int x, y, index = 0;
257
258 for (y = -m; y <= m; y++) {
259 for (x = -m; x <= m; x++) {
260 *mat_eltptr (Result, index, 0) = c_mat_elt (mat, x, y);
261 index++;
262 }
263 }
264
265 assert (index == SQR (2 * m + 1));
266 return (Result);
267 }
268
CopyCVector(const ptCMat * const mat,const int m)269 ptMat *ptRefocusMatrix::CopyCVector(const ptCMat* const mat, const int m) {
270 ptMat *Result = AllocateMatrix (as_cidx (m + 1, 0), 1);
271 register int x, y, index = 0;
272
273 for (y = 0; y <= m; y++) {
274 for (x = 0; x <= y; x++) {
275 *mat_eltptr (Result, index, 0) = c_mat_elt (mat, x, y);
276 index++;
277 }
278 }
279
280 assert (index == as_cidx (m + 1, 0));
281 return (Result);
282 }
283
CopyCVector2Matrix(const ptMat * const cvec,const int m)284 ptCMat *ptRefocusMatrix::CopyCVector2Matrix(const ptMat* const cvec,
285 const int m) {
286 ptCMat *Result = AllocateCMatrix (m);
287 register int x, y;
288
289 for (y = -m; y <= m; y++) {
290 for (x = -m; x <= m; x++) {
291 *c_mat_eltptr (Result, x, y) = mat_elt (cvec, as_cidx (x, y), 0);
292 }
293 }
294
295 return (Result);
296 }
297
CopyVector2Matrix(const ptMat * const cvec,const int m)298 ptCMat *ptRefocusMatrix::CopyVector2Matrix(const ptMat* const cvec,
299 const int m) {
300 ptCMat *Result = AllocateCMatrix (m);
301 register int x, y;
302
303 for (y = -m; y <= m; y++) {
304 for (x = -m; x <= m; x++) {
305 *c_mat_eltptr (Result, x, y) = mat_elt (cvec, as_idx (x, y, m), 0);
306 }
307 }
308
309 return (Result);
310 }
311
ComputeG(const ptCMat * const Convolution,const int MatrixRadius,const double Gamma,const double NoiseFactor,const double musq,const short symmetric)312 ptCMat *ptRefocusMatrix::ComputeG(const ptCMat* const Convolution,
313 const int MatrixRadius,
314 const double Gamma,
315 const double NoiseFactor,
316 const double musq,
317 const short symmetric) {
318 ptCMat h_conv_ruv, a, corr;
319 ptCMat *Result;
320 ptMat *b;
321 ptMat *s;
322 int status;
323
324 InitCMatrix (&h_conv_ruv, 3 * MatrixRadius);
325 FillMatrix2 (&corr, 4 * MatrixRadius, Correlation, Gamma, musq);
326 ConvolveMatrix (&h_conv_ruv, Convolution, &corr);
327 InitCMatrix (&a, 2 * MatrixRadius);
328 ConvolveStarMatrix (&a, Convolution, &h_conv_ruv);
329
330 if (symmetric) {
331 s = MakeSCMatrix (&a, MatrixRadius, NoiseFactor);
332 b = CopyCVector (&h_conv_ruv, MatrixRadius);
333 } else {
334 s = MakeSMatrix (&a, MatrixRadius, NoiseFactor);
335 b = CopyVector (&h_conv_ruv, MatrixRadius);
336 }
337
338 assert (s->Cols == s->Rows);
339 assert (s->Rows == b->Rows);
340 status = dgesv (s->Rows, 1, s->Data, s->Rows, b->Data, b->Rows);
341
342 if (symmetric) {
343 Result = CopyCVector2Matrix (b, MatrixRadius);
344 } else {
345 Result = CopyVector2Matrix (b, MatrixRadius);
346 }
347
348 FinishCMatrix (&a);
349 FinishCMatrix (&h_conv_ruv);
350 FinishCMatrix (&corr);
351 FinishAndFreeMatrix (s);
352 FinishAndFreeMatrix (b);
353 return (Result);
354 }
355
ComputeGMatrix(const ptCMat * const Convolution,const int MatrixRadius,const double Gamma,const double NoiseFactor,const double musq,const short symmetric)356 ptCMat *ptRefocusMatrix::ComputeGMatrix(const ptCMat* const Convolution,
357 const int MatrixRadius,
358 const double Gamma,
359 const double NoiseFactor,
360 const double musq,
361 const short symmetric) {
362
363 ptCMat *g = ComputeG(Convolution,
364 MatrixRadius,
365 Gamma,
366 NoiseFactor,
367 musq,
368 symmetric);
369 int r, c;
370 double sum = 0.0;
371
372 /* Determine sum of array */
373 for (r = -g->Radius; r <= g->Radius; r++) {
374 for (c = -g->Radius; c <= g->Radius; c++) {
375 sum += c_mat_elt (g, r, c);
376 }
377 }
378
379 for (r = -g->Radius; r <= g->Radius; r++) {
380 for (c = -g->Radius; c <= g->Radius; c++) {
381 *c_mat_eltptr (g, r, c) /= sum;
382 }
383 }
384
385 return (g);
386 }
387
FillMatrix(ptCMat * Matrix,const int MatrixRadius,double f (const int,const int,const double),const double fun_arg)388 void ptRefocusMatrix::FillMatrix( ptCMat* Matrix,
389 const int MatrixRadius,
390 double f(const int,
391 const int,
392 const double),
393 const double fun_arg) {
394 register int x, y;
395 InitCMatrix(Matrix,MatrixRadius);
396
397 for (y = -MatrixRadius; y <= MatrixRadius; y++) {
398 for (x = -MatrixRadius; x <= MatrixRadius; x++) {
399 *c_mat_eltptr(Matrix,x,y) = f(x,y,fun_arg);
400 }
401 }
402 }
403
FillMatrix2(ptCMat * Matrix,const int MatrixRadius,double f (const int,const int,const double,const double),const double fun_arg1,const double fun_arg2)404 void ptRefocusMatrix::FillMatrix2( ptCMat* Matrix,
405 const int MatrixRadius,
406 double f (const int,
407 const int,
408 const double,
409 const double),
410 const double fun_arg1,
411 const double fun_arg2) {
412 register int x, y;
413 InitCMatrix (Matrix,MatrixRadius);
414
415 for (y = -MatrixRadius; y <= MatrixRadius; y++) {
416 for (x = -MatrixRadius; x <= MatrixRadius; x++) {
417 *c_mat_eltptr(Matrix,x,y) = f(x,y,fun_arg1,fun_arg2);
418 }
419 }
420 }
421
MakeGaussianConvolution(const double gRadius,ptCMat * Convolution,const int MatrixRadius)422 void ptRefocusMatrix::MakeGaussianConvolution(const double gRadius,
423 ptCMat* Convolution,
424 const int MatrixRadius) {
425 register int x, y;
426
427 InitCMatrix(Convolution,MatrixRadius);
428
429 if (SQR (gRadius) <= 1 / 3.40282347e38F) {
430 for (y = -MatrixRadius; y <= MatrixRadius; y++) {
431 for (x = -MatrixRadius; x <= MatrixRadius; x++) {
432 *c_mat_eltptr(Convolution,x,y) = 0;
433 }
434 }
435
436 *c_mat_eltptr (Convolution,0,0) = 1;
437 } else {
438 const double alpha = log (2.0) / SQR (gRadius);
439
440 for (y = -MatrixRadius; y <= MatrixRadius; y++) {
441 for (x = -MatrixRadius; x <= MatrixRadius; x++) {
442 *c_mat_eltptr(Convolution,x,y) =
443 exp (-alpha * (SQR (x) + SQR (y)));
444 }
445 }
446 }
447 }
448
449 /** Return the integral of sqrt(Radius^2 - z^2) for z = 0 to x. */
450
CircleIntegral(const double x,const double Radius)451 double ptRefocusMatrix::CircleIntegral(const double x, const double Radius) {
452 if (Radius == 0) {
453 // Perhaps some epsilon must be added here.
454 return (0);
455 } else {
456 const double sin = x / Radius;
457 const double sq_diff = SQR (Radius) - SQR (x);
458 // From a mathematical point of view the following is redundant.
459 // Numerically they are not equivalent!
460
461 if ((sq_diff < 0.0) || (sin < -1.0) || (sin > 1.0)) {
462 if (sin < 0) {
463 return (-0.25 * SQR (Radius) * M_PI);
464 } else {
465 return (0.25 * SQR (Radius) * M_PI);
466 }
467 } else {
468 return (0.5 * x * sqrt (sq_diff) + 0.5 * SQR (Radius) * asin (sin));
469 }
470 }
471 }
472
CircleIntensity(const int x,const int y,const double Radius)473 double ptRefocusMatrix::CircleIntensity(const int x,
474 const int y,
475 const double Radius) {
476 double ReturnValue;
477 if (Radius == 0) {
478 ReturnValue = (((x == 0) && (y == 0)) ? 1 : 0);
479 } else {
480 register double xlo = abs (x) - 0.5, xhi = abs (x) + 0.5,
481 ylo = abs (y) - 0.5, yhi = abs (y) + 0.5;
482 register double symmetry_factor = 1, xc1, xc2;
483
484 if (xlo < 0) {
485 xlo = 0;
486 symmetry_factor *= 2;
487 }
488
489 if (ylo < 0) {
490 ylo = 0;
491 symmetry_factor *= 2;
492 }
493
494 if (SQR (xlo) + SQR (yhi) > SQR (Radius)) {
495 xc1 = xlo;
496 } else if (SQR (xhi) + SQR (yhi) > SQR (Radius)) {
497 xc1 = sqrt (SQR (Radius) - SQR (yhi));
498 } else {
499 xc1 = xhi;
500 }
501
502 if (SQR (xlo) + SQR (ylo) > SQR (Radius)) {
503 xc2 = xlo;
504 } else if (SQR (xhi) + SQR (ylo) > SQR (Radius)) {
505 xc2 = sqrt (SQR (Radius) - SQR (ylo));
506 } else {
507 xc2 = xhi;
508 }
509
510 ReturnValue = (((yhi - ylo) * (xc1 - xlo) +
511 CircleIntegral(xc2, Radius) - CircleIntegral(xc1, Radius) -
512 (xc2 - xc1) * ylo) * symmetry_factor / (M_PI * SQR (Radius)));
513 }
514 return ReturnValue;
515 }
516
MakeCircleConvolution(const double Radius,ptCMat * Convolution,const int MatrixRadius)517 void ptRefocusMatrix::MakeCircleConvolution(const double Radius,
518 ptCMat* Convolution,
519 const int MatrixRadius) {
520 FillMatrix(Convolution,MatrixRadius,CircleIntensity,Radius);
521 }
522
dgesv(const int N,const int NRHS,double * A,const int lda,double * B,const int ldb)523 int ptRefocusMatrix::dgesv (const int N,
524 const int NRHS,
525 double* A,
526 const int lda,
527 double* B,
528 const int ldb) {
529 int Result = 0;
530 integer i_N = N, i_NHRS = NRHS, i_lda = lda, i_ldb = ldb, info;
531 integer *ipiv = new integer[N];
532
533 // Clapack call.
534 dgesv_ (&i_N, &i_NHRS, A, &i_lda, ipiv, B, &i_ldb, &info);
535
536 delete [] ipiv;
537 Result = info;
538 return (Result);
539 }
540
541 ////////////////////////////////////////////////////////////////////////////////
542