22 """Collection of small images (stamps), each centered on a bright star.
25 __all__ = [
"BrightStarStamp",
"BrightStarStamps"]
27 import collections.abc
28 from typing
import NamedTuple
29 from enum
import Enum, auto
33 from lsst.geom import Box2I, Point2I, Extent2I
46 """Single stamp centered on a bright star, normalized by its
49 starStamp: afwImage.maskedImage.MaskedImageF
56 """Collection of bright star stamps and associated metadata.
60 starStamps : `collections.abc.Sequence` [`BrightStarStamp`]
61 Sequence of star stamps.
62 innerRadius : `int`, optional
63 Inner radius value, in pixels. This and ``outerRadius`` define the
64 annulus used to compute the ``"annularFlux"`` values within each
65 ``starStamp``. Must be provided if ``"INNER_RADIUS"`` and
66 ``"OUTER_RADIUS"`` are not present in ``metadata``.
67 outerRadius : `int`, optional
68 Outer radius value, in pixels. This and ``innerRadius`` define the
69 annulus used to compute the ``"annularFlux"`` values within each
70 ``starStamp``. Must be provided if ``"INNER_RADIUS"`` and
71 ``"OUTER_RADIUS"`` are not present in ``metadata``.
72 metadata : `lsst.daf.base.PropertyList`, optional
73 Metadata associated with the bright stars.
78 Raised if one of the star stamps provided does not contain the
81 Raised if the definition of the annulus used to compute each star's
82 normalization factor are not provided, that is, if ``"INNER_RADIUS"``
83 and ``"OUTER_RADIUS"`` are not present in ``metadata`` _and_
84 ``innerRadius`` and ``outerRadius`` are not provided.
88 A (gen2) butler can be used to read only a part of the stamps,
91 >>> starSubregions = butler.get("brightStarStamps_sub", dataId, bbox=bbox)
94 def __init__(self, starStamps, innerRadius=None, outerRadius=None,
96 for item
in starStamps:
97 if not isinstance(item, BrightStarStamp):
98 raise ValueError(f
"Can only add instances of BrightStarStamp, got {type(item)}")
116 def append(self, item, innerRadius, outerRadius):
117 """Add an additional bright star stamp.
121 item : `BrightStarStamp`
122 Bright star stamp to append.
124 Inner radius value, in4 pixels. This and ``outerRadius`` define the
125 annulus used to compute the ``"annularFlux"`` values within each
127 outerRadius : `int`, optional
128 Outer radius value, in pixels. This and ``innerRadius`` define the
129 annulus used to compute the ``"annularFlux"`` values within each
132 if not isinstance(item, BrightStarStamp):
133 raise ValueError(f
"Can only add instances of BrightStarStamp, got {type(item)}.")
140 """Extend BrightStarStamps instance by appending elements from another
145 bss : `BrightStarStamps`
146 Other instance to concatenate.
148 self.
_checkRadius(bss._innerRadius, RadiiEnum.INNER_RADIUS)
149 self.
_checkRadius(bss._outerRadius, RadiiEnum.OUTER_RADIUS)
153 """Retrieve star images.
158 `list` [`lsst.afw.image.maskedImage.maskedImage.MaskedImageF`]
160 return [stamp.starStamp
for stamp
in self.
_starStamps]
163 """Retrieve Gaia G magnitudes for each star.
167 gaiaGMags : `list` [`float`]
169 return [stamp.gaiaGMag
for stamp
in self.
_starStamps]
172 """Retrieve Gaia IDs for each star.
176 gaiaIds : `list` [`int`]
178 return [stamp.gaiaId
for stamp
in self.
_starStamps]
181 """Retrieve normalization factors for each star.
183 These are computed by integrating the flux in annulus centered on the
184 bright star, far enough from center to be beyond most severe ghosts and
185 saturation. The inner and outer radii that define the annulus can be
186 recovered from the metadata.
190 annularFluxes : list[`float`]
192 return [stamp.annularFlux
for stamp
in self.
_starStamps]
195 """Return the subset of bright star stamps for objects with specified
196 magnitude cuts (in Gaia G).
200 magMin : `float`, optional
201 Keep only stars fainter than this value.
202 magMax : `float`, optional
203 Keep only stars brighter than this value.
206 if (magMin
is None or stamp.gaiaGMag > magMin)
207 and (magMax
is None or stamp.gaiaGMag < magMax)]
211 instance._starStamps = subset
218 def _checkRadius(self, radiusValue, metadataEnum):
219 """Ensure provided annulus radius is consistent with that present
220 in metadata. If metadata does not contain annulus radius, add it.
224 metadataName = str(metadataEnum)
226 if radiusValue
is not None:
227 if self.
_metadata[metadataName] != radiusValue:
228 raise AttributeError(
"BrightStarStamps instance already contains different annulus radii "
229 + f
"values ({metadataName}).")
231 elif radiusValue
is None:
232 raise AttributeError(
"No radius value provided for the AnnularFlux measurement "
233 + f
"({metadataName}), and none present in metadata.")
235 self.
_metadata[metadataName] = radiusValue
239 """Write a single FITS file containing all bright star stamps.
251 fitsPrimary = afwFits.Fits(filename,
"w")
252 fitsPrimary.createEmpty()
253 fitsPrimary.writeMetadata(self.
_metadata)
254 fitsPrimary.closeFile()
258 stamp.getImage().
writeFits(filename, mode=
'a')
259 stamp.getMask().
writeFits(filename, mode=
'a')
264 """Read bright star stamps from FITS file.
268 bss : `BrightStarStamps`
269 Collection of bright star stamps.
276 """Read bright star stamps from FITS file, allowing for only a
277 subregion of the stamps to be read.
281 bss : `BrightStarStamps`
282 Collection of bright star stamps.
285 visitMetadata = afwFits.readMetadata(filename, hdu=0)
286 nbStarStamps = visitMetadata[
"N_STARS"]
287 gaiaGMags = visitMetadata.getArray(
"G_MAGS")
288 gaiaIds = visitMetadata.getArray(
"GAIA_IDS")
289 annularFluxes = visitMetadata.getArray(
"ANNULAR_FLUXES")
292 if options
and options.exists(
"llcX"):
293 llcX = options[
"llcX"]
294 llcY = options[
"llcY"]
295 width = options[
"width"]
296 height = options[
"height"]
298 kwargs[
"bbox"] = bbox
301 for bStarIdx
in range(nbStarStamps):
302 imReader = afwImage.ImageFitsReader(filename, hdu=2*bStarIdx + 1)
303 maskReader = afwImage.MaskFitsReader(filename, hdu=2*(bStarIdx + 1))
304 maskedImage = afwImage.MaskedImageF(image=imReader.read(**kwargs),
305 mask=maskReader.read(**kwargs))
307 gaiaGMag=gaiaGMags[bStarIdx],
308 gaiaId=gaiaIds[bStarIdx],
309 annularFlux=annularFluxes[bStarIdx]))
310 bss = cls(starStamps, metadata=visitMetadata)