272 discardNanFluxObjects=True,
274 statsControl=StatisticsControl(),
275 statsFlag=stringToStatisticsProperty(
"MEAN"),
276 badMaskPlanes=(
"BAD",
"SAT",
"NO_DATA"),
278 """Normalize a set of bright star stamps and initialize a
279 BrightStarStamps instance.
281 Since the center of bright stars are saturated and/or heavily affected
282 by ghosts, we measure their flux in an annulus with a large enough
283 inner radius to avoid the most severe ghosts and contain enough
284 non-saturated pixels.
288 starStamps : `collections.abc.Sequence` [`BrightStarStamp`]
289 Sequence of star stamps. Cannot contain both normalized and
292 Inner radius value, in pixels. This and ``outerRadius`` define the
293 annulus used to compute the ``"annularFlux"`` values within each
296 Outer radius value, in pixels. This and ``innerRadius`` define the
297 annulus used to compute the ``"annularFlux"`` values within each
299 nb90Rots : `int`, optional
300 Number of 90 degree rotations required to compensate for detector
302 metadata : `~lsst.daf.base.PropertyList`, optional
303 Metadata associated with the bright stars.
305 If `True` read and write mask data. Default `True`.
306 use_variance : `bool`
307 If ``True`` read and write variance data. Default ``False``.
309 If ``True`` read and write an Archive that contains a Persistable
310 associated with each stamp. In the case of bright stars, this is
311 usually a ``TransformPoint2ToPoint2``, used to warp each stamp
312 to the same pixel grid before stacking.
313 imCenter : `collections.abc.Sequence`, optional
314 Center of the object, in pixels. If not provided, the center of the
315 first stamp's pixel grid will be used.
316 discardNanFluxObjects : `bool`
317 Whether objects with NaN annular flux should be discarded.
318 If False, these objects will not be normalized.
319 forceFindFlux : `bool`
320 Whether to try to find the flux of objects with NaN annular flux
321 at a different annulus.
322 statsControl : `~lsst.afw.math.statistics.StatisticsControl`, optional
323 StatisticsControl to be used when computing flux over all pixels
325 statsFlag : `~lsst.afw.math.statistics.Property`, optional
326 statsFlag to be passed on to ``~lsst.afw.math.makeStatistics`` to
327 compute annularFlux. Defaults to a simple MEAN.
328 badMaskPlanes : `collections.abc.Collection` [`str`]
329 Collection of mask planes to ignore when computing annularFlux.
334 Raised if one of the star stamps provided does not contain the
337 Raised if there is a mix-and-match of normalized and unnormalized
338 stamps, stamps normalized with different annulus definitions, or if
339 stamps are to be normalized but annular radii were not provided.
341 stampSize = starStamps[0].stamp_im.getDimensions()
343 imCenter = stampSize[0] // 2, stampSize[1] // 2
346 outerCircle = SpanSet.fromShape(outerRadius, Stencil.CIRCLE, offset=imCenter)
347 innerCircle = SpanSet.fromShape(innerRadius, Stencil.CIRCLE, offset=imCenter)
348 annulusWidth = outerRadius - innerRadius
350 raise ValueError(
"The annulus width must be greater than 1 pixel.")
351 annulus = outerCircle.intersectNot(innerCircle)
361 use_variance=use_variance,
362 use_archive=use_archive,
366 bss._checkNormalization(
True, innerRadius, outerRadius)
367 bss._innerRadius, bss._outerRadius = innerRadius, outerRadius
372 for stamp
in bss._stamps:
374 stamp.measureAndNormalize(
375 annulus, statsControl=statsControl, statsFlag=statsFlag, badMaskPlanes=badMaskPlanes
388 stamp.optimalOuterRadius = outerRadius
389 stamp.optimalInnerRadius = innerRadius
390 except RuntimeError
as err:
395 if discardNanFluxObjects:
396 rejects.append(stamp)
398 newInnerRadius = innerRadius
399 newOuterRadius = outerRadius
401 newOuterRadius += annulusWidth
402 newInnerRadius += annulusWidth
403 if newOuterRadius > min(imCenter):
404 logger.info(
"No flux found for the star with Gaia ID of %s", stamp.gaiaId)
405 stamp.annularFlux =
None
406 badStamps.append(stamp)
408 newOuterCircle = SpanSet.fromShape(newOuterRadius, Stencil.CIRCLE, offset=imCenter)
409 newInnerCircle = SpanSet.fromShape(newInnerRadius, Stencil.CIRCLE, offset=imCenter)
410 newAnnulus = newOuterCircle.intersectNot(newInnerCircle)
412 stamp.measureAndNormalize(
414 statsControl=statsControl,
416 badMaskPlanes=badMaskPlanes,
420 stamp.annularFlux = np.nan
422 "The annular flux was not found for radii %d and %d",
426 if stamp.annularFlux
and stamp.annularFlux > 0:
427 logger.info(
"The flux is found within an optimized annulus.")
429 "The optimized annulus radii are %d and %d and the flux is %f",
434 stamp.optimalOuterRadius = newOuterRadius
435 stamp.optimalInnerRadius = newInnerRadius
438 stamp.annularFlux = np.nan
441 bss.normalized =
True
442 if discardNanFluxObjects:
443 for reject
in rejects:
444 bss._stamps.remove(reject)
446 for badStamp
in badStamps:
447 bss._stamps.remove(badStamp)
448 bss._innerRadius, bss._outerRadius =
None,
None
449 return bss, badStamps