LSSTApplications  8.0.0.0+107,8.0.0.1+13,9.1+18,9.2,master-g084aeec0a4,master-g0aced2eed8+6,master-g15627eb03c,master-g28afc54ef9,master-g3391ba5ea0,master-g3d0fb8ae5f,master-g4432ae2e89+36,master-g5c3c32f3ec+17,master-g60f1e072bb+1,master-g6a3ac32d1b,master-g76a88a4307+1,master-g7bce1f4e06+57,master-g8ff4092549+31,master-g98e65bf68e,master-ga6b77976b1+53,master-gae20e2b580+3,master-gb584cd3397+53,master-gc5448b162b+1,master-gc54cf9771d,master-gc69578ece6+1,master-gcbf758c456+22,master-gcec1da163f+63,master-gcf15f11bcc,master-gd167108223,master-gf44c96c709
LSSTDataManagementBasePackage
twodg.cc
Go to the documentation of this file.
1 /*
2  * LSST Data Management System
3  * Copyright 2008, 2009, 2010 LSST Corporation.
4  *
5  * This product includes software developed by the
6  * LSST Project (http://www.lsst.org/).
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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 LSST License Statement and
19  * the GNU General Public License along with this program. If not,
20  * see <http://www.lsstcorp.org/LegalNotices/>.
21  */
22 
23 #include <algorithm>
24 #include "Eigen/Core"
25 #include "Eigen/LU"
26 
27 #include "all.h"
28 
29 #define USE_WEIGHT 0 // zweight is only set, not used. It isn't even set if this is false
30 struct Raster {
31  int x, y; // x, y index of rastered pixel
32  double zo; // pixel's value
33 #if USE_WEIGHT
34  double zweight; // weight for the pixel
35 #endif
36 };
37 
38 struct Fit2d {
39  typedef Eigen::Matrix<double, FittedModel::NPARAM, FittedModel::NPARAM> Matrix;
40  typedef Eigen::Matrix<double, FittedModel::NPARAM, 1> Vector;
41 
42  template<typename PixelT>
43  explicit Fit2d(afwImage::Image<PixelT> const& im) : wide(32), rast(*new std::vector<Raster>(wide*wide)) {
44  ncols = im.getWidth();
45  nrows = im.getHeight();
46 
47  dc2zmin = 0.3;
48  dc2zmax = 3.5;
49  tooclose = 3;
50  toosmall = 1.5;
51  nitmax = 15;
52  flamdok = 1.0e-7;
53  ratiomin = -1.0e-7;
54  lost = 3.5;
55  xlamd = 5.0;
56 
57  iter = 0;
58  flamd = 1.0;
59  }
60  ~Fit2d() {
61  delete &rast;
62  }
63 
64  int wide; // patch of image being fitted is wide*wide
65  std::vector<Raster> &rast; // The pixels being fitted, thought of as a raster scan
66  int nin;
67  int nref;
68  int iter;
69  int tooclose;
81  double chisq;
82  double chold;
83  double stold;
84  double chnew;
85  double stnew;
86  double flamd;
87  double flamdok;
88  double xlamd;
89  double ratiomin;
90  int nitmax;
91  double dc2zmin;
92  double dc2zmax;
93  double lost;
94  double toosmall; // minimum acceptable width
95  int ncols; // number of columns in image
96  int nrows; // number of rows in image
97  int status; // status of processing
98 };
99 
100 /************************************************************************************************************/
105 static void dcalc2(Fit2d *fit, // the struct carrying all the fit information
106  Fit2d::Vector const &el, // current parameters of the fit
107  Fit2d::Matrix *alpha, // desired alpha matrix
108  Fit2d::Vector *beta // desired beta vector
109  ) {
110 /*
111  * Pass arguments and initialize
112  */
113  double const a = el[FittedModel::PEAK];
114  double const b = el[FittedModel::SKY];
115  double const x0 = el[FittedModel::X0];
116  double const y0 = el[FittedModel::Y0];
117  double const s = el[FittedModel::SIGMA];
118 
119  if (a <= 0.0) {
120  fit->status = FittedModel::BAD_A;
121  return;
122  } else if (s <= 0.0) {
124  return;
125  } else {
126  fit->status = 0;
127  }
128 
129  beta->setZero();
130  alpha->setZero();
131 
132  fit->nin = 0;
133  fit->chnew = 0.0;
134 /*
135  * Examine all pixels in raster
136  */
137  for (int i = 0, nrast = fit->rast.size(); i != nrast; i++) {
138  double const dx = (fit->rast[i].x - x0)/s;
139  double const dy = (fit->rast[i].y - y0)/s;
140  double const d = hypot(dx, dy);
141 
142  if (d >= fit->dc2zmin && d <= fit->dc2zmax) {
143  double const arg = exp(-d*d/2.0);
144  double const funct = a*arg + b;
145 
146  Fit2d::Vector df;
147  df << arg, 1.0, a*arg*dx/s, a*arg*dy/s, a*arg*(dx*dx/s + dy*dy/s);
148 
149  double const r = fit->rast[i].zo - funct; // residual
150  fit->chnew += r*r;
151  *beta += r*df;
152  *alpha += df*df.transpose(); // outer product
153 
154  fit->nin++;
155  }
156  }
157  int nu = fit->nin - (FittedModel::NPARAM + 1); // number of degrees of freedom
158  if (nu <= 0) {
160  return;
161  }
162  fit->stnew = sqrt(fit->chnew/nu);
163 }
164 
165 /************************************************************************************************************/
166 /*
167  * From Bevington's CURFIT
168  */
169 static void curf2(Fit2d *fit) {
170 /*
171  * Initialization
172  */
173  fit->status = 0;
174  if (fit->iter == 0) {
175  dcalc2(fit, fit->elnew, &fit->alnew, &fit->benew);
176  if (fit->status != 0) {
177  return;
178  }
179  fit->nref = fit->nin;
180  fit->stnew = sqrt(fit->chnew/(fit->nref - (FittedModel::NPARAM + 1)));
181  }
182 /*
183  * Save Current Parameters
184  */
185  fit->chold = fit->chnew;
186  fit->stold = fit->stnew;
187  fit->elold = fit->elnew;
188  fit->sgold = fit->sgnew;
189  fit->beold = fit->benew;
190  fit->alold = fit->alnew;
191 /*
192  * Previous Call To DCALC Used To Fill Current
193  */
194  int const NPLIM = 4;
195  for (int poor = 0; poor != NPLIM; ++poor) {
196  for (int j = 0; j != FittedModel::NPARAM; ++j) {
197  if (fit->alnew(j, j) == 0.0) {
199  return;
200  }
201  for (int k = 0; k != FittedModel::NPARAM; ++k) {
202  fit->alpha(j, k) = fit->alnew(j, k)/sqrt(fit->alnew(j, j)*fit->alnew(k, k));
203  }
204  fit->alpha(j, j) = 1.0 + fit->flamd;
205  }
206 /*
207  * Inversion.
208  */
209 #if 0
210  fit->alpha.computeInverse(); // has no way to check if inverse succeeded
211 #else
212  Eigen::FullPivLU<Fit2d::Matrix> alphaLU(fit->alpha);
213  if (!alphaLU.isInvertible()) {
214  fit->status = -1;
215  return;
216  }
217  fit->alpha = alphaLU.inverse();
218 #endif
219 
220 /*
221  * New Elements
222  */
223  for (int j = 0; j != FittedModel::NPARAM; ++j) {
224  for (int k = 0; k != FittedModel::NPARAM; ++k) {
225  fit->elnew[j] += fit->benew[k]*fit->alpha(j,k)/sqrt(fit->alnew(j, j)*fit->alnew(k, k));
226  }
227  }
228 /*
229  * Compute Current Chi-Squared And Next ALPHA+BETA
230  */
231  dcalc2(fit, fit->elnew, &fit->alnew, &fit->benew);
232  if (fit->status == FittedModel::TOO_FEW) {
233  return;
234  }
235  fit->stnew = sqrt(fit->chnew/(fit->nref - (FittedModel::NPARAM + 1)));
236 
237  for (int j = 0; j != FittedModel::NPARAM; ++j) {
238  fit->sgnew[j] = fit->stnew*sqrt(fabs(fit->alpha(j, j)/fit->alnew(j, j)));
239  }
240 /*
241  * Quick Return If Solution Got Better
242  */
243  if (fit->status == 0.0 && fit->stnew <= fit->stold) {
244  fit->flamd = fit->flamd/fit->xlamd;
245  return;
246  }
247 /*
248  * Undo Solution And Try Again
249  */
250  fit->flamd = 3.0*fit->flamd;
251  fit->chnew = fit->chold;
252  fit->stnew = fit->stold;
253  fit->elnew = fit->elold;
254  fit->sgnew = fit->sgold;
255  fit->benew = fit->beold;
256  fit->alnew = fit->alold;
257  }
258 }
259 
260 /************************************************************************************************************/
261 /*
262  * Test of convergence or fatal errors
263  */
264 static void cond2(Fit2d *fit) {
265 /*
266  * Ignore CURF errors for these conditions
267  */
268  if (fit->iter <= 3) {
269  fit->status = 0;
270  return;
271  }
272  if (fit->flamd < fit->flamdok) {
274  return;
275  }
276 /*
277  * Catch Fatal Errors
278  */
279  if (fit->status < 0) {
280  return;
281  }
282  if (fit->nin < FittedModel::NPARAM + 1) {
284  return;
285  }
286  if (fit->chnew <= 0.0) {
288  return;
289  }
290  if (fit->elnew[FittedModel::X0] < 0.0 || fit->elnew[FittedModel::X0] > fit->ncols ||
291  fit->elnew[FittedModel::Y0] < 0.0 || fit->elnew[FittedModel::Y0] > fit->nrows) {
292  fit->status = FittedModel::RANGE;
293  return;
294  }
295  if (fit->elnew[FittedModel::SIGMA] < 0.0) {
297  return;
298  }
299 
300  double const ex = fabs(fit->param[FittedModel::X0] - fit->elnew[FittedModel::X0]);
301  double const ey = fabs(fit->param[FittedModel::Y0] - fit->elnew[FittedModel::Y0]);
302  if (ex > fit->lost || ey > fit->lost) {
303  fit->status = FittedModel::LOST;
304  return;
305  }
306 /*
307  * Test for convergence
308  */
309  double const ratio = (fit->stnew - fit->stold)/fit->stnew;
310  if (ratio > fit->ratiomin) {
311  fit->status = (fit->flamd < 0.001) ? FittedModel::CONVERGE : FittedModel::POOR;
312  } else if (fit->iter > fit->nitmax) {
314  } else {
315  fit->status = 0;
316  }
317 }
318 
319 /************************************************************************************************************/
320 /*
321  * First guess for 2-D Gaussian
322  */
323 template<typename PixelT>
324 static void fg2(afwImage::Image<PixelT> const& im,
325  double x0, double y0,
326  Fit2d *fit
327  ) {
328 /*
329  * Sanity Check
330  */
331  int ix0 = static_cast<int>(x0 + 0.5);
332  int iy0 = static_cast<int>(y0 + 0.5);
333  if(ix0 < fit->tooclose || im.getWidth() - ix0 < fit->tooclose ||
334  iy0 < fit->tooclose || im.getHeight() - iy0 < fit->tooclose) {
336  return;
337  }
338  int jx0 = ix0;
339  int jy0 = iy0;
340  double peak = im(jx0, jy0);
341 /*
342  * Extract interesting portion
343  */
344  double const w = fit->wide/2;
345  int xl = static_cast<int>(ix0 - w + 0.5);
346  if (xl < 0) {
347  xl = 0;
348  }
349  int xh = static_cast<int>(xl + 2*w + 0.5);
350  if (xh > im.getWidth()) {
351  xh = im.getWidth();
352  }
353  int yl = static_cast<int>(iy0 - w + 0.5);
354  if (yl < 0) {
355  yl = 0;
356  }
357  int yh = static_cast<int>(yl + 2*w + 0.5);
358  if (yh > im.getHeight()) {
359  yh = im.getHeight();
360  }
361 
362  double sky = im(xl, yl);
363  int put = 0;
364  for (int y = yl; y != yh; ++y) {
365  for (int x = xl; x != xh; ++x) {
366  fit->rast[put].x = x;
367  fit->rast[put].y = y;
368  fit->rast[put].zo = im(x, y);
369 #if USE_WEIGHT
370  fit->rast[put].zweight = 1.0;
371 #endif
372  if (im(x, y) < sky) {
373  sky = im(x, y);
374  }
375  double const ex = x - ix0;
376  double const ey = y - iy0;
377  double const er = hypot(ex, ey);
378  if (er <= fit->lost) {
379  if (im(x, y) > peak) {
380  jx0 = x;
381  jy0 = y;
382  peak = im(x, y);
383  }
384  }
385  put++;
386  }
387  }
388  fit->rast.resize(put);
389  ix0 = jx0;
390  iy0 = jy0;
391 /*
392  * Look For FWHM
393  */
394  double xmin = xl;
395  double xmax = xh - 1;
396  double ymin = yl;
397  double ymax = yh - 1;
398  double const test = 0.5*(im(ix0, iy0) + sky); // pixel value of half-maximum above sky
399  for (int x = ix0; x < xh - 1; ++x) {
400  if (im(x + 1, iy0) <= test) {
401  double a = test - im(x, iy0);
402  double b = im(x + 1, iy0) - im(x, iy0);
403 
404  xmax = x + ((b == 0.0) ? 0.5 : a/b);
405  break;
406  }
407  }
408  for (int x = ix0; x > 0; --x) {
409  if (im(x - 1, iy0) <= test) {
410  double a = test - im(x, iy0);
411  double b = im(x - 1, iy0) - im(x, iy0);
412 
413  xmin = x - ((b == 0.0) ? 0.5 : a/b);
414  break;
415  }
416  }
417  for (int y = iy0; y < yh - 1; ++y) {
418  if (im(ix0, y + 1) <= test) {
419  double a = test - im(ix0, y);
420  double b = im(ix0, y + 1) - im(ix0, y);
421 
422  ymax = y + ((b == 0.0) ? 0.5 : a/b);
423  break;
424  }
425  }
426  for (int y = iy0; y > 0; --y) {
427  if (im(ix0, y - 1) <= test) {
428  double a = test - im(ix0, y);
429  double b = im(ix0, y - 1) - im(ix0, y);
430 
431  ymin = y - ((b == 0.0) ? 0.5 : a/b);
432  break;
433  }
434  }
435 /*
436  * Assemble the fitting vector
437  */
438  fit->param[FittedModel::PEAK] = im(ix0, iy0) - sky;
439  fit->param[FittedModel::SKY] = sky;
440  fit->param[FittedModel::X0] = x0;
441  fit->param[FittedModel::Y0] = y0;
442  fit->param[FittedModel::SIGMA] = 0.5*((xmax - xmin)+(ymax - ymin))/2.354;
443  if (fit->param[FittedModel::SIGMA] < fit->toosmall) {
444  fit->param[FittedModel::SIGMA] = fit->toosmall;
445  }
446  fit->elnew = fit->param;
447 
448  fit->status = 0;
449 }
450 
451 /************************************************************************************************************/
455 template<typename PixelT>
457  double x0,
458  double y0
459  ) {
460 /*
461  * Initialize the fitting structure
462  */
463  Fit2d fit(im);
464 /*
465  * Initialization
466  */
467  fg2(im, x0, y0, &fit);
468  if (fit.status != 0) {
469  std::vector<double> params(FittedModel::NPARAM);
470  std::copy(&fit.elnew[0], &fit.elnew[0] + fit.elnew.size(), params.begin());
471 
472  return FittedModel(fit.status, params);
473  }
474 /*
475  * Find best-fit model
476  */
477  for (fit.iter = 0; fit.status == 0; ++fit.iter) {
478  curf2(&fit);
479  cond2(&fit);
480  }
481 
482 #if 0
483  twodgMinuit(&fit);
484 #endif
485 
486  std::vector<double> params(FittedModel::NPARAM);
487  std::copy(&fit.elnew[0], &fit.elnew[0] + fit.elnew.size(), params.begin());
488 
489  return FittedModel(fit.status, params, fit.iter, fit.flamd, fit.chnew);
490 }
491 //
492 // Explicit instantiations
493 //
494 #define MAKE_TWODG(IMAGE_T) \
495  template FittedModel twodg(IMAGE_T const& im, double x0, double y0)
496 
std::vector< Raster > & rast
Definition: twodg.cc:65
double lost
Definition: twodg.cc:93
double zo
Definition: twodg.cc:32
double dc2zmax
Definition: twodg.cc:92
double xlamd
Definition: twodg.cc:88
Definition: twodg.cc:38
double chnew
Definition: twodg.cc:84
double dx
Definition: ImageUtils.cc:90
int nref
Definition: twodg.cc:67
Vector elold
Definition: twodg.cc:73
int ncols
Definition: twodg.cc:95
#define MAKE_TWODG(IMAGE_T)
Definition: twodg.cc:494
int y
Definition: twodg.cc:31
int y
Vector beta
Definition: twodg.cc:72
int tooclose
Definition: twodg.cc:69
int x
Definition: twodg.cc:31
SelectEigenView< T >::Type copy(Eigen::EigenBase< T > const &other)
Copy an arbitrary Eigen expression into a new EigenView.
Definition: eigen.h:390
int wide
Definition: twodg.cc:64
Eigen::Matrix< double, FittedModel::NPARAM, FittedModel::NPARAM > Matrix
Definition: twodg.cc:39
FittedModel twodg(afwImage::Image< PixelT > const &im, double x0, double y0)
Definition: twodg.cc:456
Matrix alpha
Definition: twodg.cc:71
int status
Definition: twodg.cc:97
Vector param
Definition: twodg.cc:70
double dy
Definition: ImageUtils.cc:90
double chold
Definition: twodg.cc:82
double stold
Definition: twodg.cc:83
int d
Definition: KDTree.cc:89
int tooclose
double dc2zmin
Definition: twodg.cc:91
Definition: twodg.cc:30
int nitmax
Definition: twodg.cc:90
double w
Definition: CoaddPsf.cc:57
Vector beta
Vector beold
Definition: twodg.cc:79
int x
Matrix alpha
double dc2zmax
Fit2d(afwImage::Image< PixelT > const &im)
Definition: twodg.cc:43
int getHeight() const
Return the number of rows in the image.
Definition: Image.h:239
double stnew
Definition: twodg.cc:85
double chisq
Definition: twodg.cc:81
double lost
double flamdok
Definition: twodg.cc:87
~Fit2d()
Definition: twodg.cc:60
Eigen::Matrix< double, FittedModel::NPARAM, 1 > Vector
Definition: twodg.cc:40
Matrix alnew
Definition: twodg.cc:78
Vector sgold
Definition: twodg.cc:75
int nrows
Definition: twodg.cc:96
afw::table::Key< double > b
double toosmall
Definition: twodg.cc:94
Vector sgnew
Definition: twodg.cc:76
int iter
Definition: twodg.cc:68
int getWidth() const
Return the number of columns in the image.
Definition: Image.h:237
A class to represent a 2-dimensional array of pixels.
Definition: Image.h:415
int nin
Definition: twodg.cc:66
Vector elnew
Definition: twodg.cc:74
double flamd
Definition: twodg.cc:86
Vector benew
Definition: twodg.cc:80
Matrix alold
Definition: twodg.cc:77
double ratiomin
Definition: twodg.cc:89