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