239 def update_psf_stats(self, summary, psf, bbox, sources=None, image_mask=None, sources_is_astropy=False):
240 """Compute all summary-statistic fields that depend on the PSF model.
241
242 Parameters
243 ----------
244 summary : `lsst.afw.image.ExposureSummaryStats`
245 Summary object to update in-place.
246 psf : `lsst.afw.detection.Psf` or `None`
247 Point spread function model. If `None`, all fields that depend on
248 the PSF will be reset (generally to NaN).
249 bbox : `lsst.geom.Box2I`
250 Bounding box of the image for which summary stats are being
251 computed.
252 sources : `lsst.afw.table.SourceCatalog` or `astropy.table.Table`
253 Catalog for quantities that are computed from source table columns.
254 If `None`, these quantities will be reset (generally to NaN).
255 The type of this table must correspond to the
256 ``sources_is_astropy`` argument.
257 image_mask : `lsst.afw.image.Mask`, optional
258 Mask image that may be used to compute distance-to-nearest-star
259 metrics.
260 sources_is_astropy : `bool`, optional
261 Whether ``sources`` is an `astropy.table.Table` instance instead
262 of an `lsst.afw.table.Catalog` instance. Default is `False` (the
263 latter).
264 """
265 nan = float("nan")
266 summary.psfSigma = nan
267 summary.psfIxx = nan
268 summary.psfIyy = nan
269 summary.psfIxy = nan
270 summary.psfArea = nan
271 summary.nPsfStar = 0
272 summary.psfStarDeltaE1Median = nan
273 summary.psfStarDeltaE2Median = nan
274 summary.psfStarDeltaE1Scatter = nan
275 summary.psfStarDeltaE2Scatter = nan
276 summary.psfStarDeltaSizeMedian = nan
277 summary.psfStarDeltaSizeScatter = nan
278 summary.psfStarScaledDeltaSizeScatter = nan
279 summary.maxDistToNearestPsf = nan
280 summary.psfTraceRadiusDelta = nan
281
282 if psf is None:
283 return
284 shape = psf.computeShape(bbox.getCenter())
285 summary.psfSigma = shape.getDeterminantRadius()
286 summary.psfIxx = shape.getIxx()
287 summary.psfIyy = shape.getIyy()
288 summary.psfIxy = shape.getIxy()
289 im = psf.computeKernelImage(bbox.getCenter())
290
291
292
293
294 summary.psfArea = float(np.sum(im.array)/np.sum(im.array**2.))
295
296 if image_mask is not None:
297 psfTraceRadiusDelta = psf_trace_radius_delta(
298 image_mask,
299 psf,
300 sampling=self.config.psfGridSampling,
301 bad_mask_bits=self.config.psfBadMaskPlanes
302 )
303 summary.psfTraceRadiusDelta = float(psfTraceRadiusDelta)
304
305 if sources is None:
306
307
308
309 return
310
311
312 nPsfStar = sources[self.config.starSelection].sum()
313 summary.nPsfStar = int(nPsfStar)
314
315 psf_mask = self.starSelector.run(sources).selected
316 nPsfStarsUsedInStats = psf_mask.sum()
317
318 if nPsfStarsUsedInStats == 0:
319
320
321 return
322
323 if sources_is_astropy:
324 psf_cat = sources[psf_mask]
325 else:
326 psf_cat = sources[psf_mask].copy(deep=True)
327
328 starXX = psf_cat[self.config.starShape + '_xx']
329 starYY = psf_cat[self.config.starShape + '_yy']
330 starXY = psf_cat[self.config.starShape + '_xy']
331 psfXX = psf_cat[self.config.psfShape + '_xx']
332 psfYY = psf_cat[self.config.psfShape + '_yy']
333 psfXY = psf_cat[self.config.psfShape + '_xy']
334
335
336 starSize = np.sqrt(starXX/2. + starYY/2.)
337
338 starE1 = (starXX - starYY)/(starXX + starYY)
339 starE2 = 2*starXY/(starXX + starYY)
340 starSizeMedian = np.median(starSize)
341
342
343 psfSize = np.sqrt(psfXX/2. + psfYY/2.)
344 psfE1 = (psfXX - psfYY)/(psfXX + psfYY)
345 psfE2 = 2*psfXY/(psfXX + psfYY)
346
347 psfStarDeltaE1Median = np.median(starE1 - psfE1)
348 psfStarDeltaE1Scatter = sigmaMad(starE1 - psfE1, scale='normal')
349 psfStarDeltaE2Median = np.median(starE2 - psfE2)
350 psfStarDeltaE2Scatter = sigmaMad(starE2 - psfE2, scale='normal')
351
352 psfStarDeltaSizeMedian = np.median(starSize - psfSize)
353 psfStarDeltaSizeScatter = sigmaMad(starSize - psfSize, scale='normal')
354 psfStarScaledDeltaSizeScatter = psfStarDeltaSizeScatter/starSizeMedian
355
356 summary.psfStarDeltaE1Median = float(psfStarDeltaE1Median)
357 summary.psfStarDeltaE2Median = float(psfStarDeltaE2Median)
358 summary.psfStarDeltaE1Scatter = float(psfStarDeltaE1Scatter)
359 summary.psfStarDeltaE2Scatter = float(psfStarDeltaE2Scatter)
360 summary.psfStarDeltaSizeMedian = float(psfStarDeltaSizeMedian)
361 summary.psfStarDeltaSizeScatter = float(psfStarDeltaSizeScatter)
362 summary.psfStarScaledDeltaSizeScatter = float(psfStarScaledDeltaSizeScatter)
363
364 if image_mask is not None:
365 maxDistToNearestPsf = maximum_nearest_psf_distance(
366 image_mask,
367 psf_cat,
368 sampling=self.config.psfSampling,
369 bad_mask_bits=self.config.psfBadMaskPlanes
370 )
371 summary.maxDistToNearestPsf = float(maxDistToNearestPsf)
372