lsst.meas.algorithms  14.0-9-g82279ae0+10
PsfAttributes.cc
Go to the documentation of this file.
1 
2 // -*- LSST-C++ -*-
3 
4 /*
5  * LSST Data Management System
6  * Copyright 2008, 2009, 2010 LSST Corporation.
7  *
8  * This product includes software developed by the
9  * LSST Project (http://www.lsst.org/).
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the LSST License Statement and
22  * the GNU General Public License along with this program. If not,
23  * see <http://www.lsstcorp.org/LegalNotices/>.
24  */
25 
33 #include <cmath>
34 #include "lsst/base.h"
35 #include "lsst/afw/geom/Point.h"
36 #include "lsst/afw/geom/Angle.h"
37 #include "lsst/afw/geom/SpanSet.h"
41 #include "lsst/afw/detection/Psf.h"
43 #include "lsst/afw/table/Source.h"
46 
47 /************************************************************************************************************/
48 
50 namespace afwGeom = lsst::afw::geom;
51 namespace afwImage = lsst::afw::image;
52 namespace afwMath = lsst::afw::math;
53 
54 namespace lsst {
55 namespace meas {
56 namespace algorithms {
62  int const iX,
63  int const iY
64  )
65 {
66  // N.b. (iX, iY) are ints so that we know this image is centered in the central pixel of _psfImage
67  _psfImage = psf->computeImage(afwGeom::PointD(iX, iY));
68 }
69 
75  lsst::afw::geom::Point2I const& cen
76  ) :
77  // N.b. cen is a PointI so that we know this image is centered in the central pixel of _psfImage
78  _psfImage(psf->computeImage(afwGeom::PointD(cen)))
79 {
80 }
81 
82 namespace {
83 
84 /*
85  * Return an estimate of <r> == <sqrt(x^2 + y^2)> for an image (i.e. sum(I*r)/sum(I))
86  *
87  * For a Gaussian N(0, alpha^2), <r> = sqrt(pi/2) alpha
88  */
89 template<typename ImageT>
90 double
91 computeFirstMoment(ImageT const& image, // the data to process
92  float const xCen, float const yCen // centre of object
93  )
94 {
95  double sum = 0.0;
96  double norm = 0.0;
97  for (int iY = 0; iY != image->getHeight(); ++iY) {
98  int iX = 0;
99  for (afwImage::Image<double>::x_iterator ptr = image->row_begin(iY),
100  end = image->row_end(iY); ptr != end; ++ptr, ++iX) {
101  double const x = iX - xCen;
102  double const y = iY - yCen;
103  double const r = std::sqrt( x*x + y*y );
104  double const m = (*ptr)*r;
105  norm += *ptr;
106  sum += m;
107  }
108  }
109 
110  std::string errmsg("");
111  if (sum < 0.0) {
112  errmsg = "sum(I*r) is negative. ";
113  }
114  if (norm <= 0.0) {
115  errmsg += "sum(I) is <= 0.";
116  }
117  if (errmsg != "") {
119  }
120 
121  return sum/norm;
122 }
123 
124 /*
125  * Return an estimate of <r^2> == <x^2 + y^2> for an image (i.e. sum(I*r^2)/sum(I))
126  *
127  * For a Gaussian N(0, alpha^2), <r^2> = 2 alpha^2
128  */
129 template<typename ImageT>
130 double
131 computeSecondMoment(ImageT const& image, // the data to process
132  float const xCen, float const yCen // centre of object
133  )
134 {
135  double sum = 0.0;
136  double norm = 0.0;
137  for (int iY = 0; iY != image->getHeight(); ++iY) {
138  int iX = 0;
139  for (afwImage::Image<double>::x_iterator ptr = image->row_begin(iY),
140  end = image->row_end(iY); ptr != end; ++ptr, ++iX) {
141  double const x = iX - xCen;
142  double const y = iY - yCen;
143  double const r2 = x*x + y*y;
144  double const m = (*ptr)*r2;
145  norm += *ptr;
146  sum += m;
147  }
148  }
149 
150  std::string errmsg("");
151  if (sum < 0.0) {
152  errmsg = "sum(I*r*r) is negative. ";
153  }
154  if (norm <= 0.0) {
155  errmsg += "sum(I) is <= 0.";
156  }
157  if (errmsg != "") {
159  }
160 
161  return sum/norm;
162 }
163 
164 /*****************************************************************************/
165 /*
166  * Calculate weighted moments of an object up to 2nd order
167  */
168 template<typename ImageT>
170 calcmom(ImageT const& image, // the image data
171  float const xCen, float const yCen, // centre of object
172  double const w11 // weights
173  )
174 {
175  assert(w11 >= 0); /* i.e. it was set */
176  if (fabs(w11) > 1e6) {
178  }
179 
180  double sum = 0, sumrr = 0.0;
181 
182  for (int i = 0; i < image.getHeight(); ++i) {
183  float const y = i - yCen;
184  float const y2 = y*y;
185 
186  typename ImageT::x_iterator ptr = image.row_begin(i);
187  for (int j = 0; j < image.getWidth(); ++j, ++ptr) {
188  float const x = j - xCen;
189  float const x2 = x*x;
190  float const expon = (x2 + y2)*w11;
191 
192  if (expon <= 14.0) {
193  float const weight = exp(-0.5*expon);
194  float const tmod = *ptr;
195  float const ymod = tmod*weight;
196  sum += ymod;
197  sumrr += (x2 + y2)*ymod;
198  }
199  }
200  }
201 
202  if (sum <= 0 || sumrr < 0) {
204  }
205 
206  return std::make_pair(true, 0.5*sumrr/sum); // 0.5: 1-D moment
207 }
208 
209 /*
210  * Return an estimate of <r^2> == <x^2 + y^2> for an image using adaptive moments
211  *
212  * For a Gaussian N(0, alpha^2), <r^2> = 2 alpha^2
213  *
214  * This is basically the SdssShape code simplified for a circularly symmetrical case. I don't want to call
215  * the shape code here as this class may well be moving to afw along with Psf
216  */
217 template<typename ImageT>
218 double
219 computeSecondMomentAdaptive(ImageT const& image, // the data to process
220  float const xCen, float const yCen // centre of object
221  )
222 {
223  int const MAXIT = 100; // \todo from Policy XXX
224  float const TOL = 0.0001;
225  double w11 = 0.5; // current weight for moments
226  float sigma11_ow_old = 1e6; // previous version of sigma11_ow
227 
228  bool unweighted = false; // do we need to use an unweighted moment?
229  int iter = 0; // iteration number
230  for (; iter < MAXIT; ++iter) {
231  std::pair<bool, double> moments = calcmom(*image, xCen, yCen, w11);
232 
233  if (not moments.first) {
234  unweighted = true;
235  break;
236  }
237 /*
238  * Did we converge?
239  */
240  float const sigma11_ow = moments.second; // quadratic moments of weight*object
241 
242  if (iter > 0 && fabs(sigma11_ow/sigma11_ow_old - 1.0) < TOL) {
243  break; /* yes; we converged */
244  }
245 
246  sigma11_ow_old = sigma11_ow;
247 /*
248  * Didn't converge, calculate new values for weighting function
249  *
250  * The product of two Gaussians is a Gaussian, the inverse-variances add
251  *
252  * We know sigma11_ow and sigma11W == 1/w11, the variances of the weighted object
253  * and of the weights themselves. We can estimate the object's variance as
254  * 1/sigma11_ow - 1/sigma11W
255  * and, as we want to find a set of weights with the _same_ covariance as the
256  * object we take this to be the an estimate of our correct weights.
257  *
258  * N.b. This assumes that the object is roughly Gaussian.
259  * Consider the object:
260  * O == delta(x + p) + delta(x - p)
261  * the covariance of the weighted object is equal to that of the unweighted
262  * object, and this prescription fails badly. If we detect this, we set
263  * unweighted, and calculate the UNweighted moments
264  * instead.
265  */
266  w11 = 1/sigma11_ow - w11; // inverse of new sigma11_ow
267 
268  if (w11 <= 0) { // product-of-Gaussians assumption failed
269  unweighted = true;
270  break;
271  }
272  }
273 /*
274  * Problems; try calculating the un-weighted moments
275  */
276  double sigma11W; // quadratic moment of the weighting function
277 
278  if (iter == MAXIT || unweighted) {
279  w11 = 0; // => unweighted
280  std::pair<bool, double> moments = calcmom(*image, xCen, yCen, w11);
281 
282  if (moments.first) {
283  sigma11W = moments.second; // estimate of object moment
284  } else {
285  sigma11W = 1/12.0; // a single pixel
286  }
287  } else {
288  sigma11W = 1/w11;
289  }
290 
291  return 2*sigma11W; // 2: <x^2> + <y^2>
292 }
293 
294 }
295 
301  /*
302  * Estimate the PSF's center. This *really* needs to be rewritten to avoid using MeasureSources;
303  * we shouldn't need to instantiate source objects just to measure an adaptive centroid!
304  */
306  typedef afwImage::Exposure<double> Exposure;
308  auto foot = std::make_shared<afwDetection::Footprint>(std::make_shared<afwGeom::SpanSet>(exposure->getBBox(
309  afwImage::LOCAL)));
310 
311  afwGeom::Point2D center(_psfImage->getX0() + _psfImage->getWidth()/2,
312  _psfImage->getY0() + _psfImage->getHeight()/2);
313  double x(center.getX());
314  double y(center.getY());
316  double const xCen = fittedCenter.getX();
317  double const yCen = fittedCenter.getY();
318  switch (how) {
319  case ADAPTIVE_MOMENT:
320  return ::sqrt(0.5*computeSecondMomentAdaptive(_psfImage, xCen, yCen));
321  case FIRST_MOMENT:
322  return ::sqrt(2.0/afwGeom::PI)*computeFirstMoment(_psfImage, xCen, yCen);
323  case SECOND_MOMENT:
324  return ::sqrt(0.5*computeSecondMoment(_psfImage, xCen, yCen));
325  case NOISE_EQUIVALENT:
326  return ::sqrt(computeEffectiveArea()/(4*afwGeom::PI));
327  case BICKERTON:
328  {
329  double sum = 0.0;
330  double norm = 0.0;
331  for (int iY = 0; iY != _psfImage->getHeight(); ++iY) {
332  int iX = 0;
333  for (afwImage::Image<double>::x_iterator ptr = _psfImage->row_begin(iY),
334  end = _psfImage->row_end(iY); ptr != end;
335  ++ptr, ++iX) {
336  double const x = iX - xCen;
337  double const y = iY - yCen;
338  double const r = std::sqrt( x*x + y*y );
339  double const m = (*ptr)*r;
340  norm += (*ptr)*(*ptr);
341  sum += m*m;
342  }
343  }
344  return sqrt(sum/norm);
345  }
346  default:
347  abort();
348  }
349 }
350 
356 
357  double sum = 0.0;
358  double sumsqr = 0.0;
359  for (int iY = 0; iY != _psfImage->getHeight(); ++iY) {
360  afwImage::Image<double>::x_iterator end = _psfImage->row_end(iY);
361  for (afwImage::Image<double>::x_iterator ptr = _psfImage->row_begin(iY); ptr != end; ++ptr) {
362  sum += *ptr;
363  sumsqr += (*ptr)*(*ptr);
364  }
365  }
366  return sum*sum/sumsqr;
367 }
368 
369 }}}
int y
int iter
Calculate width as sqrt(n_eff/(4 pi))
Definition: PSF.h:71
tbl::Key< double > weight
T norm(const T &x)
static afw::geom::Point2D fitCentroid(afw::image::Image< PixelT > const &im, double x0, double y0)
#define CONST_PTR(...)
x_iterator row_begin(int y) const
STL class.
Calculate width using <r^2>
Definition: PSF.h:70
std::shared_ptr< Exposure< ImagePixelT, MaskPixelT, VariancePixelT > > makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, std::shared_ptr< Wcs const > wcs=std::shared_ptr< Wcs const >())
int end
T make_pair(T... args)
double x
double computeGaussianWidth(Method how=ADAPTIVE_MOMENT) const
Compute the &#39;sigma&#39; value for an equivalent gaussian psf.
#define LSST_EXCEPT(type,...)
Weight <r^2> by I^2 to avoid negative fluxes.
Definition: PSF.h:72
PsfAttributes(boost::shared_ptr< lsst::afw::detection::Psf const > psf, int const iX, int const iY)
Constructor for PsfAttributes.
Calculate width using adaptive Gaussian weights.
Definition: PSF.h:68
std::shared_ptr< Image > computeImage(geom::Point2D position=makeNullPoint(), image::Color color=image::Color(), ImageOwnerEnum owner=COPY) const
T sqrt(T... args)
m
double computeEffectiveArea() const
Compute the effective area of the psf ( sum(I)^2/sum(I^2) )