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
PsfAttributes.cc
Go to the documentation of this file.
1 // -*- LSST-C++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008, 2009, 2010 LSST Corporation.
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <http://www.lsstcorp.org/LegalNotices/>.
23  */
24 
32 #include <cmath>
33 #include "lsst/base.h"
34 #include "lsst/afw/geom/Point.h"
35 #include "lsst/afw/geom/Angle.h"
39 #include "lsst/afw/detection/Psf.h"
43 
44 /************************************************************************************************************/
45 
46 namespace afwDetection = lsst::afw::detection;
47 namespace afwGeom = lsst::afw::geom;
48 namespace afwImage = lsst::afw::image;
49 namespace afwMath = lsst::afw::math;
50 
51 namespace lsst {
52 namespace meas {
53 namespace algorithms {
59  int const iX,
60  int const iY
61  )
62 {
63  // N.b. (iX, iY) are ints so that we know this image is centered in the central pixel of _psfImage
64  _psfImage = psf->computeImage(afwGeom::PointD(iX, iY));
65 }
66 
72  lsst::afw::geom::Point2I const& cen
73  ) :
74  // N.b. cen is a PointI so that we know this image is centered in the central pixel of _psfImage
75  _psfImage(psf->computeImage(afwGeom::PointD(cen)))
76 {
77 }
78 
79 namespace {
80 
81 /*
82  * Return an estimate of <r> == <sqrt(x^2 + y^2)> for an image (i.e. sum(I*r)/sum(I))
83  *
84  * For a Gaussian N(0, alpha^2), <r> = sqrt(pi/2) alpha
85  */
86 template<typename ImageT>
87 double
88 computeFirstMoment(ImageT const& image, // the data to process
89  float const xCen, float const yCen // centre of object
90  )
91 {
92  double sum = 0.0;
93  double norm = 0.0;
94  for (int iY = 0; iY != image->getHeight(); ++iY) {
95  int iX = 0;
96  for (afwImage::Image<double>::x_iterator ptr = image->row_begin(iY),
97  end = image->row_end(iY); ptr != end; ++ptr, ++iX) {
98  double const x = iX - xCen;
99  double const y = iY - yCen;
100  double const r = std::sqrt( x*x + y*y );
101  double const m = (*ptr)*r;
102  norm += *ptr;
103  sum += m;
104  }
105  }
106 
107  std::string errmsg("");
108  if (sum < 0.0) {
109  errmsg = "sum(I*r) is negative. ";
110  }
111  if (norm <= 0.0) {
112  errmsg += "sum(I) is <= 0.";
113  }
114  if (errmsg != "") {
115  throw LSST_EXCEPT(lsst::pex::exceptions::DomainError, errmsg);
116  }
117 
118  return sum/norm;
119 }
120 
121 /*
122  * Return an estimate of <r^2> == <x^2 + y^2> for an image (i.e. sum(I*r^2)/sum(I))
123  *
124  * For a Gaussian N(0, alpha^2), <r^2> = 2 alpha^2
125  */
126 template<typename ImageT>
127 double
128 computeSecondMoment(ImageT const& image, // the data to process
129  float const xCen, float const yCen // centre of object
130  )
131 {
132  double sum = 0.0;
133  double norm = 0.0;
134  for (int iY = 0; iY != image->getHeight(); ++iY) {
135  int iX = 0;
136  for (afwImage::Image<double>::x_iterator ptr = image->row_begin(iY),
137  end = image->row_end(iY); ptr != end; ++ptr, ++iX) {
138  double const x = iX - xCen;
139  double const y = iY - yCen;
140  double const r2 = x*x + y*y;
141  double const m = (*ptr)*r2;
142  norm += *ptr;
143  sum += m;
144  }
145  }
146 
147  std::string errmsg("");
148  if (sum < 0.0) {
149  errmsg = "sum(I*r*r) is negative. ";
150  }
151  if (norm <= 0.0) {
152  errmsg += "sum(I) is <= 0.";
153  }
154  if (errmsg != "") {
155  throw LSST_EXCEPT(lsst::pex::exceptions::DomainError, errmsg);
156  }
157 
158  return sum/norm;
159 }
160 
161 /*****************************************************************************/
162 /*
163  * Calculate weighted moments of an object up to 2nd order
164  */
165 template<typename ImageT>
166 std::pair<bool, double>
167 calcmom(ImageT const& image, // the image data
168  float const xCen, float const yCen, // centre of object
169  double const w11 // weights
170  )
171 {
172  assert(w11 >= 0); /* i.e. it was set */
173  if (fabs(w11) > 1e6) {
174  return std::make_pair(false, std::numeric_limits<double>::quiet_NaN());
175  }
176 
177  double sum = 0, sumrr = 0.0;
178 
179  for (int i = 0; i < image.getHeight(); ++i) {
180  float const y = i - yCen;
181  float const y2 = y*y;
182 
183  typename ImageT::x_iterator ptr = image.row_begin(i);
184  for (int j = 0; j < image.getWidth(); ++j, ++ptr) {
185  float const x = j - xCen;
186  float const x2 = x*x;
187  float const expon = (x2 + y2)*w11;
188 
189  if (expon <= 14.0) {
190  float const weight = exp(-0.5*expon);
191  float const tmod = *ptr;
192  float const ymod = tmod*weight;
193  sum += ymod;
194  sumrr += (x2 + y2)*ymod;
195  }
196  }
197  }
198 
199  if (sum <= 0 || sumrr < 0) {
200  return std::make_pair(false, std::numeric_limits<double>::quiet_NaN());
201  }
202 
203  return std::make_pair(true, 0.5*sumrr/sum); // 0.5: 1-D moment
204 }
205 
206 /*
207  * Return an estimate of <r^2> == <x^2 + y^2> for an image using adaptive moments
208  *
209  * For a Gaussian N(0, alpha^2), <r^2> = 2 alpha^2
210  *
211  * This is basically the SdssShape code simplified for a circularly symmetrical case. I don't want to call
212  * the shape code here as this class may well be moving to afw along with Psf
213  */
214 template<typename ImageT>
215 double
216 computeSecondMomentAdaptive(ImageT const& image, // the data to process
217  float const xCen, float const yCen // centre of object
218  )
219 {
220  int const MAXIT = 100; // \todo from Policy XXX
221  float const TOL = 0.0001;
222  double w11 = 0.5; // current weight for moments
223  float sigma11_ow_old = 1e6; // previous version of sigma11_ow
224 
225  bool unweighted = false; // do we need to use an unweighted moment?
226  int iter = 0; // iteration number
227  for (; iter < MAXIT; ++iter) {
228  std::pair<bool, double> moments = calcmom(*image, xCen, yCen, w11);
229 
230  if (not moments.first) {
231  unweighted = true;
232  break;
233  }
234 /*
235  * Did we converge?
236  */
237  float const sigma11_ow = moments.second; // quadratic moments of weight*object
238 
239  if (iter > 0 && fabs(sigma11_ow/sigma11_ow_old - 1.0) < TOL) {
240  break; /* yes; we converged */
241  }
242 
243  sigma11_ow_old = sigma11_ow;
244 /*
245  * Didn't converge, calculate new values for weighting function
246  *
247  * The product of two Gaussians is a Gaussian, the inverse-variances add
248  *
249  * We know sigma11_ow and sigma11W == 1/w11, the variances of the weighted object
250  * and of the weights themselves. We can estimate the object's variance as
251  * 1/sigma11_ow - 1/sigma11W
252  * and, as we want to find a set of weights with the _same_ covariance as the
253  * object we take this to be the an estimate of our correct weights.
254  *
255  * N.b. This assumes that the object is roughly Gaussian.
256  * Consider the object:
257  * O == delta(x + p) + delta(x - p)
258  * the covariance of the weighted object is equal to that of the unweighted
259  * object, and this prescription fails badly. If we detect this, we set
260  * unweighted, and calculate the UNweighted moments
261  * instead.
262  */
263  w11 = 1/sigma11_ow - w11; // inverse of new sigma11_ow
264 
265  if (w11 <= 0) { // product-of-Gaussians assumption failed
266  unweighted = true;
267  break;
268  }
269  }
270 /*
271  * Problems; try calculating the un-weighted moments
272  */
273  double sigma11W; // quadratic moment of the weighting function
274 
275  if (iter == MAXIT || unweighted) {
276  w11 = 0; // => unweighted
277  std::pair<bool, double> moments = calcmom(*image, xCen, yCen, w11);
278 
279  if (moments.first) {
280  sigma11W = moments.second; // estimate of object moment
281  } else {
282  sigma11W = 1/12.0; // a single pixel
283  }
284  } else {
285  sigma11W = 1/w11;
286  }
287 
288  return 2*sigma11W; // 2: <x^2> + <y^2>
289 }
290 
291 }
292 
298  /*
299  * Estimate the PSF's center. This *really* needs to be rewritten to avoid using MeasureSources;
300  * we shouldn't need to instantiate source objects just to measure an adaptive centroid!
301  */
303  typedef afwImage::Exposure<double> Exposure;
304  Exposure::Ptr exposure = makeExposure(mi);
305 
306  afwDetection::Footprint::Ptr foot = boost::make_shared<afwDetection::Footprint>(exposure->getBBox(
307  afwImage::LOCAL));
308 
310  afwGeom::Point2D center(_psfImage->getX0() + _psfImage->getWidth()/2,
311  _psfImage->getY0() + _psfImage->getHeight()/2);
315  PTR(afw::table::SourceRecord) source = table->makeRecord();
316  source->setFootprint(foot);
317  ms.apply(*source, *exposure, center);
318  afw::table::Centroid::MeasKey key = table->getSchema()[ctrl.name];
319  afw::table::Centroid::MeasValue centroid = source->get(key);
320  float const xCen = centroid.getX() - _psfImage->getX0();
321  float const yCen = centroid.getY() - _psfImage->getY0();
322 
323  switch (how) {
324  case ADAPTIVE_MOMENT:
325  return ::sqrt(0.5*computeSecondMomentAdaptive(_psfImage, xCen, yCen));
326  case FIRST_MOMENT:
327  return ::sqrt(2.0/afwGeom::PI)*computeFirstMoment(_psfImage, xCen, yCen);
328  case SECOND_MOMENT:
329  return ::sqrt(0.5*computeSecondMoment(_psfImage, xCen, yCen));
330  case NOISE_EQUIVALENT:
331  return ::sqrt(computeEffectiveArea()/(4*afwGeom::PI));
332  case BICKERTON:
333  {
334  double sum = 0.0;
335  double norm = 0.0;
336  for (int iY = 0; iY != _psfImage->getHeight(); ++iY) {
337  int iX = 0;
338  for (afwImage::Image<double>::x_iterator ptr = _psfImage->row_begin(iY),
339  end = _psfImage->row_end(iY); ptr != end;
340  ++ptr, ++iX) {
341  double const x = iX - xCen;
342  double const y = iY - yCen;
343  double const r = std::sqrt( x*x + y*y );
344  double const m = (*ptr)*r;
345  norm += (*ptr)*(*ptr);
346  sum += m*m;
347  }
348  }
349  return sqrt(sum/norm);
350  }
351  default:
352  abort();
353  }
354 }
355 
361 
362  double sum = 0.0;
363  double sumsqr = 0.0;
364  for (int iY = 0; iY != _psfImage->getHeight(); ++iY) {
366  for (afwImage::Image<double>::x_iterator ptr = _psfImage->row_begin(iY); ptr != end; ++ptr) {
367  sum += *ptr;
368  sumsqr += (*ptr)*(*ptr);
369  }
370  }
371  return sum*sum/sumsqr;
372 }
373 
374 }}}
Calculate width as sqrt(n_eff/(4 pi))
Definition: PSF.h:71
Defines the fields and offsets for a table.
Definition: Schema.h:46
Field< MeasTag >::Value MeasValue
the value type used for the measurement
Definition: slots.h:232
#define MAXIT
Definition: Function2D.cc:646
A coordinate class intended to represent absolute positions.
PsfAttributes(boost::shared_ptr< lsst::afw::detection::Psf const > psf, int const iX, int const iY)
Constructor for PsfAttributes.
#define PTR(...)
Definition: base.h:41
boost::shared_ptr< Footprint > Ptr
Definition: Footprint.h:78
int iter
int y
Support for PCA analysis of 2-D images.
A class to contain the data, WCS, and other information needed to describe an image of the sky...
Definition: Exposure.h:48
boost::shared_ptr< Image > computeImage(geom::Point2D position=makeNullPoint(), image::Color color=image::Color(), ImageOwnerEnum owner=COPY) const
Return an Image of the PSF, in a form that can be compared directly with star images.
T norm(const T &x)
Definition: Integrate.h:191
C++ control object for Gaussian centroid.
MeasureSourcesBuilder & addAlgorithm(AlgorithmControl const &algorithmControl)
Add an algorithm defined by a control object.
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
Calculate width using &lt;r^2&gt;
Definition: PSF.h:70
double const PI
The ratio of a circle&#39;s circumference to diameter.
Definition: Angle.h:18
tbl::Schema schema
Definition: CoaddPsf.cc:324
boost::shared_ptr< lsst::afw::image::Image< double > > _psfImage
Definition: PSF.h:82
int x
A class to manipulate images, masks, and variance as a single object.
Definition: MaskedImage.h:77
double computeEffectiveArea() const
Compute the effective area of the psf ( sum(I)^2/sum(I^2) )
MeasureSources build(afw::table::Schema &schema, boost::shared_ptr< daf::base::PropertyList > const &metadata=boost::shared_ptr< daf::base::PropertyList >()) const
Build a MeasureSources object.
Calculate width using &lt;r&gt;
Definition: PSF.h:69
void apply(afw::table::SourceRecord &source, afw::image::Exposure< PixelT > const &exposure, afw::geom::Point2D const &center, bool refineCenter=true) const
Apply the registered algorithms to the given source.
boost::shared_ptr< lsst::afw::image::Image< double > > _psfImage
static Schema makeMinimalSchema()
Return a minimal schema for Source tables and records.
Definition: Source.h:233
double sum
Definition: NaiveFlux.cc:137
#define CONST_PTR(...)
Definition: base.h:47
Table class that contains measurements made on a single exposure.
Definition: Source.h:194
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
static boost::shared_ptr< SourceTable > make(Schema const &schema, boost::shared_ptr< IdFactory > const &idFactory)
Construct a new table.
Weight &lt;r^2&gt; by I^2 to avoid negative fluxes.
Definition: PSF.h:72
A class used as a handle to a particular field in a table.
Definition: fwd.h:44
Calculate width using adaptive Gaussian weights.
Definition: PSF.h:68
Class to ensure constraints for spatial modeling.
Exposure< ImagePixelT, MaskPixelT, VariancePixelT >::Ptr makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, boost::shared_ptr< Wcs const > wcs=boost::shared_ptr< Wcs const >())
Definition: Exposure.h:308
Record class that contains measurements made on a single exposure.
Definition: Source.h:81
x_iterator row_begin(int y) const
Definition: Image.h:319
A polymorphic base class for representing an image&#39;s Point Spread Function.
Definition: Psf.h:68
double computeGaussianWidth(Method how=ADAPTIVE_MOMENT) const
Compute the &#39;sigma&#39; value for an equivalent gaussian psf.
A class to represent a 2-dimensional array of pixels.
Definition: Image.h:415
Point< double, 2 > PointD
Definition: Point.h:276