269 discardNanFluxObjects=True,
271 statsControl=StatisticsControl(),
272 statsFlag=stringToStatisticsProperty(
"MEAN"),
273 badMaskPlanes=(
"BAD",
"SAT",
"NO_DATA"),
275 """Normalize a set of bright star stamps and initialize a
276 BrightStarStamps instance.
278 Since the center of bright stars are saturated and/or heavily affected
279 by ghosts, we measure their flux in an annulus with a large enough
280 inner radius to avoid the most severe ghosts and contain enough
281 non-saturated pixels.
285 starStamps : `collections.abc.Sequence` [`BrightStarStamp`]
286 Sequence of star stamps. Cannot contain both normalized and
289 Inner radius value, in pixels. This and ``outerRadius`` define the
290 annulus used to compute the ``"annularFlux"`` values within each
293 Outer radius value, in pixels. This and ``innerRadius`` define the
294 annulus used to compute the ``"annularFlux"`` values within each
296 nb90Rots : `int`, optional
297 Number of 90 degree rotations required to compensate for detector
299 metadata : `~lsst.daf.base.PropertyList`, optional
300 Metadata associated with the bright stars.
302 If `True` read and write mask data. Default `True`.
303 use_variance : `bool`
304 If ``True`` read and write variance data. Default ``False``.
306 If ``True`` read and write an Archive that contains a Persistable
307 associated with each stamp. In the case of bright stars, this is
308 usually a ``TransformPoint2ToPoint2``, used to warp each stamp
309 to the same pixel grid before stacking.
310 imCenter : `collections.abc.Sequence`, optional
311 Center of the object, in pixels. If not provided, the center of the
312 first stamp's pixel grid will be used.
313 discardNanFluxObjects : `bool`
314 Whether objects with NaN annular flux should be discarded.
315 If False, these objects will not be normalized.
316 forceFindFlux : `bool`
317 Whether to try to find the flux of objects with NaN annular flux
318 at a different annulus.
319 statsControl : `~lsst.afw.math.statistics.StatisticsControl`, optional
320 StatisticsControl to be used when computing flux over all pixels
322 statsFlag : `~lsst.afw.math.statistics.Property`, optional
323 statsFlag to be passed on to ``~lsst.afw.math.makeStatistics`` to
324 compute annularFlux. Defaults to a simple MEAN.
325 badMaskPlanes : `collections.abc.Collection` [`str`]
326 Collection of mask planes to ignore when computing annularFlux.
331 Raised if one of the star stamps provided does not contain the
334 Raised if there is a mix-and-match of normalized and unnormalized
335 stamps, stamps normalized with different annulus definitions, or if
336 stamps are to be normalized but annular radii were not provided.
338 stampSize = starStamps[0].stamp_im.getDimensions()
340 imCenter = stampSize[0] // 2, stampSize[1] // 2
343 outerCircle = SpanSet.fromShape(outerRadius, Stencil.CIRCLE, offset=imCenter)
344 innerCircle = SpanSet.fromShape(innerRadius, Stencil.CIRCLE, offset=imCenter)
345 annulusWidth = outerRadius - innerRadius
347 raise ValueError(
"The annulus width must be greater than 1 pixel.")
348 annulus = outerCircle.intersectNot(innerCircle)
358 use_variance=use_variance,
359 use_archive=use_archive,
363 bss._checkNormalization(
True, innerRadius, outerRadius)
364 bss._innerRadius, bss._outerRadius = innerRadius, outerRadius
369 for stamp
in bss._stamps:
371 stamp.measureAndNormalize(
372 annulus, statsControl=statsControl, statsFlag=statsFlag, badMaskPlanes=badMaskPlanes
385 stamp.optimalOuterRadius = outerRadius
386 stamp.optimalInnerRadius = innerRadius
387 except RuntimeError
as err:
392 if discardNanFluxObjects:
393 rejects.append(stamp)
395 newInnerRadius = innerRadius
396 newOuterRadius = outerRadius
398 newOuterRadius += annulusWidth
399 newInnerRadius += annulusWidth
400 if newOuterRadius > min(imCenter):
401 logger.info(
"No flux found for the star with Gaia ID of %s", stamp.gaiaId)
402 stamp.annularFlux =
None
403 badStamps.append(stamp)
405 newOuterCircle = SpanSet.fromShape(newOuterRadius, Stencil.CIRCLE, offset=imCenter)
406 newInnerCircle = SpanSet.fromShape(newInnerRadius, Stencil.CIRCLE, offset=imCenter)
407 newAnnulus = newOuterCircle.intersectNot(newInnerCircle)
409 stamp.measureAndNormalize(
411 statsControl=statsControl,
413 badMaskPlanes=badMaskPlanes,
417 stamp.annularFlux = np.nan
419 "The annular flux was not found for radii %d and %d",
423 if stamp.annularFlux
and stamp.annularFlux > 0:
424 logger.info(
"The flux is found within an optimized annulus.")
426 "The optimized annulus radii are %d and %d and the flux is %f",
431 stamp.optimalOuterRadius = newOuterRadius
432 stamp.optimalInnerRadius = newInnerRadius
435 stamp.annularFlux = np.nan
438 bss.normalized =
True
439 if discardNanFluxObjects:
440 for reject
in rejects:
441 bss._stamps.remove(reject)
443 for badStamp
in badStamps:
444 bss._stamps.remove(badStamp)
445 bss._innerRadius, bss._outerRadius =
None,
None
446 return bss, badStamps