lsst.meas.algorithms  20.0.0-16-g760a3dc6+7dead9ab29
brightStarStamps.py
Go to the documentation of this file.
1 # This file is part of meas_algorithms.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (https://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21 #
22 """Collection of small images (stamps), each centered on a bright star.
23 """
24 
25 __all__ = ["BrightStarStamp", "BrightStarStamps"]
26 
27 from dataclasses import dataclass
28 from enum import Enum, auto
29 
30 from lsst.afw.image import MaskedImage
31 from .stamps import StampsBase, AbstractStamp, readFitsWithOptions
32 
33 
34 class RadiiEnum(Enum):
35  INNER_RADIUS = auto()
36  OUTER_RADIUS = auto()
37 
38  def __str__(self):
39  return self.name
40 
41 
42 @dataclass
44  """Single stamp centered on a bright star, normalized by its
45  annularFlux.
46 
47  Parameters
48  ----------
49  stamp_im : `lsst.afw.image.MaskedImage`
50  Pixel data for this postage stamp
51  gaiaGMag : `float`
52  Gaia G magnitude for the object in this stamp
53  gaiaId : `int`
54  Gaia object identifier
55  annularFlux : `float`
56  Flux in an annulus around the object
57  """
58  stamp_im: MaskedImage
59  gaiaGMag: float
60  gaiaId: int
61  annularFlux: float
62 
63  @classmethod
64  def factory(cls, stamp_im, metadata, idx):
65  """This method is needed to service the FITS reader.
66  We need a standard interface to construct objects like this.
67  Parameters needed to construct this object are passed in via
68  a metadata dictionary and then passed to the constructor of
69  this class. This particular factory method requires keys:
70  G_MAGS, GAIA_IDS, and ANNULAR_FLUXES. They should each
71  point to lists of values.
72 
73  Parameters
74  ----------
75  stamp_im : `lsst.afw.image.MaskedImage`
76  Pixel data to pass to the constructor
77  metadata : `dict`
78  Dictionary containing the information
79  needed by the constructor.
80  idx : `int`
81  Index into the lists in ``metadata``
82 
83  Returns
84  -------
85  brightstarstamp : `BrightStarStamp`
86  An instance of this class
87  """
88  return cls(stamp_im=stamp_im,
89  gaiaGMag=metadata['G_MAGS'][idx],
90  gaiaId=metadata['GAIA_IDS'][idx],
91  annularFlux=metadata['ANNULAR_FLUXES'][idx])
92 
93 
95  """Collection of bright star stamps and associated metadata.
96 
97  Parameters
98  ----------
99  starStamps : `collections.abc.Sequence` [`BrightStarStamp`]
100  Sequence of star stamps.
101  innerRadius : `int`, optional
102  Inner radius value, in pixels. This and ``outerRadius`` define the
103  annulus used to compute the ``"annularFlux"`` values within each
104  ``starStamp``. Must be provided if ``"INNER_RADIUS"`` and
105  ``"OUTER_RADIUS"`` are not present in ``metadata``.
106  outerRadius : `int`, optional
107  Outer radius value, in pixels. This and ``innerRadius`` define the
108  annulus used to compute the ``"annularFlux"`` values within each
109  ``starStamp``. Must be provided if ``"INNER_RADIUS"`` and
110  ``"OUTER_RADIUS"`` are not present in ``metadata``.
111  metadata : `lsst.daf.base.PropertyList`, optional
112  Metadata associated with the bright stars.
113  use_mask : `bool`
114  If `True` read and write mask data. Default `True`.
115  use_variance : `bool`
116  If ``True`` read and write variance data. Default ``False``.
117 
118  Raises
119  ------
120  ValueError
121  Raised if one of the star stamps provided does not contain the
122  required keys.
123  AttributeError
124  Raised if the definition of the annulus used to compute each star's
125  normalization factor are not provided, that is, if ``"INNER_RADIUS"``
126  and ``"OUTER_RADIUS"`` are not present in ``metadata`` _and_
127  ``innerRadius`` and ``outerRadius`` are not provided.
128 
129  Notes
130  -----
131  A (gen2) butler can be used to read only a part of the stamps,
132  specified by a bbox:
133 
134  >>> starSubregions = butler.get("brightStarStamps_sub", dataId, bbox=bbox)
135  """
136 
137  def __init__(self, starStamps, innerRadius=None, outerRadius=None,
138  metadata=None, use_mask=True, use_variance=False):
139  super().__init__(starStamps, metadata, use_mask, use_variance)
140  # Add inner and outer radii to metadata
141  self._checkRadius(innerRadius, RadiiEnum.INNER_RADIUS)
142  self._innerRadius = innerRadius
143  self._checkRadius(outerRadius, RadiiEnum.OUTER_RADIUS)
144  self._outerRadius = outerRadius
145 
146  def _refresh_metadata(self):
147  """Refresh the metadata. Should be called before writing this object out.
148  """
149  # add full list of Gaia magnitudes, IDs and annularFlxes to shared
150  # metadata
151  self._metadata["G_MAGS"] = self.getMagnitudes()
152  self._metadata["GAIA_IDS"] = self.getGaiaIds()
153  self._metadata["ANNULAR_FLUXES"] = self.getAnnularFluxes()
154  return None
155 
156  @classmethod
157  def readFits(cls, filename):
158  """Build an instance of this class from a file.
159 
160  Parameters
161  ----------
162  filename : `str`
163  Name of the file to read
164  """
165  return cls.readFitsWithOptions(filename, None)
166 
167  @classmethod
168  def readFitsWithOptions(cls, filename, options):
169  """Build an instance of this class with options.
170 
171  Parameters
172  ----------
173  filename : `str`
174  Name of the file to read
175  options : `PropertyList`
176  Collection of metadata parameters
177  """
178  stamps, metadata = readFitsWithOptions(filename, BrightStarStamp.factory, options)
179  return cls(stamps, metadata=metadata, use_mask=metadata['HAS_MASK'],
180  use_variance=metadata['HAS_VARIANCE'])
181 
182  def append(self, item, innerRadius, outerRadius):
183  """Add an additional bright star stamp.
184 
185  Parameters
186  ----------
187  item : `BrightStarStamp`
188  Bright star stamp to append.
189  innerRadius : `int`
190  Inner radius value, in pixels. This and ``outerRadius`` define the
191  annulus used to compute the ``"annularFlux"`` values within each
192  ``starStamp``.
193  outerRadius : `int`, optional
194  Outer radius value, in pixels. This and ``innerRadius`` define the
195  annulus used to compute the ``"annularFlux"`` values within each
196  ``starStamp``.
197  """
198  if not isinstance(item, BrightStarStamp):
199  raise ValueError(f"Can only add instances of BrightStarStamp, got {type(item)}.")
200  self._checkRadius(innerRadius, RadiiEnum.INNER_RADIUS)
201  self._checkRadius(outerRadius, RadiiEnum.OUTER_RADIUS)
202  self._stamps.append(item)
203  return None
204 
205  def extend(self, bss):
206  """Extend BrightStarStamps instance by appending elements from another
207  instance.
208 
209  Parameters
210  ----------
211  bss : `BrightStarStamps`
212  Other instance to concatenate.
213  """
214  if not isinstance(bss, BrightStarStamps):
215  raise ValueError('Can only extend with a BrightStarStamps object. '
216  f'Got {type(bss)}.')
217  self._checkRadius(bss._innerRadius, RadiiEnum.INNER_RADIUS)
218  self._checkRadius(bss._outerRadius, RadiiEnum.OUTER_RADIUS)
219  self._stamps += bss._stamps
220 
221  def getMagnitudes(self):
222  """Retrieve Gaia G magnitudes for each star.
223 
224  Returns
225  -------
226  gaiaGMags : `list` [`float`]
227  """
228  return [stamp.gaiaGMag for stamp in self._stamps]
229 
230  def getGaiaIds(self):
231  """Retrieve Gaia IDs for each star.
232 
233  Returns
234  -------
235  gaiaIds : `list` [`int`]
236  """
237  return [stamp.gaiaId for stamp in self._stamps]
238 
239  def getAnnularFluxes(self):
240  """Retrieve normalization factors for each star.
241 
242  These are computed by integrating the flux in annulus centered on the
243  bright star, far enough from center to be beyond most severe ghosts and
244  saturation. The inner and outer radii that define the annulus can be
245  recovered from the metadata.
246 
247  Returns
248  -------
249  annularFluxes : `list` [`float`]
250  """
251  return [stamp.annularFlux for stamp in self._stamps]
252 
253  def selectByMag(self, magMin=None, magMax=None):
254  """Return the subset of bright star stamps for objects with specified
255  magnitude cuts (in Gaia G).
256 
257  Parameters
258  ----------
259  magMin : `float`, optional
260  Keep only stars fainter than this value.
261  magMax : `float`, optional
262  Keep only stars brighter than this value.
263  """
264  subset = [stamp for stamp in self._stamps
265  if (magMin is None or stamp.gaiaGMag > magMin)
266  and (magMax is None or stamp.gaiaGMag < magMax)]
267  # This is an optimization to save looping over the init argument when
268  # it is already guaranteed to be the correct type
269  instance = BrightStarStamps((), metadata=self._metadata)
270  instance._stamps = subset
271  return instance
272 
273  def _checkRadius(self, radiusValue, metadataEnum):
274  """Ensure provided annulus radius is consistent with that present
275  in metadata. If metadata does not contain annulus radius, add it.
276  """
277  # if a radius value is already present in metadata, ensure it matches
278  # the one given
279  metadataName = str(metadataEnum)
280  if self._metadata.exists(metadataName):
281  if radiusValue is not None:
282  if self._metadata[metadataName] != radiusValue:
283  raise AttributeError("BrightStarStamps instance already contains different annulus radii "
284  + f"values ({metadataName}).")
285  # if not already in metadata, a value must be provided
286  elif radiusValue is None:
287  raise AttributeError("No radius value provided for the AnnularFlux measurement "
288  + f"({metadataName}), and none present in metadata.")
289  else:
290  self._metadata[metadataName] = radiusValue
291  return None
lsst::meas::algorithms.brightStarStamps.RadiiEnum.__str__
def __str__(self)
Definition: brightStarStamps.py:38
lsst::meas::algorithms.brightStarStamps.BrightStarStamps
Definition: brightStarStamps.py:94
lsst::afw::image
lsst::meas::algorithms.brightStarStamps.BrightStarStamps.extend
def extend(self, bss)
Definition: brightStarStamps.py:205
lsst::meas::algorithms.brightStarStamps.BrightStarStamps.getMagnitudes
def getMagnitudes(self)
Definition: brightStarStamps.py:221
lsst::meas::algorithms.brightStarStamps.RadiiEnum
Definition: brightStarStamps.py:34
lsst::meas::algorithms.brightStarStamps.BrightStarStamps.append
def append(self, item, innerRadius, outerRadius)
Definition: brightStarStamps.py:182
lsst::meas::algorithms.stamps.AbstractStamp
Definition: stamps.py:143
lsst::meas::algorithms.brightStarStamps.BrightStarStamp
Definition: brightStarStamps.py:43
lsst::meas::algorithms.brightStarStamps.BrightStarStamps._innerRadius
_innerRadius
Definition: brightStarStamps.py:141
lsst::meas::algorithms.brightStarStamps.BrightStarStamps.__init__
def __init__(self, starStamps, innerRadius=None, outerRadius=None, metadata=None, use_mask=True, use_variance=False)
Definition: brightStarStamps.py:137
lsst::meas::algorithms.brightStarStamps.BrightStarStamps.getAnnularFluxes
def getAnnularFluxes(self)
Definition: brightStarStamps.py:239
lsst::meas::algorithms.brightStarStamps.BrightStarStamps.selectByMag
def selectByMag(self, magMin=None, magMax=None)
Definition: brightStarStamps.py:253
lsst::meas::algorithms.brightStarStamps.BrightStarStamps.readFitsWithOptions
def readFitsWithOptions(cls, filename, options)
Definition: brightStarStamps.py:168
lsst::meas::algorithms.brightStarStamps.BrightStarStamps._outerRadius
_outerRadius
Definition: brightStarStamps.py:143
lsst::meas::algorithms.stamps.StampsBase._stamps
_stamps
Definition: stamps.py:257
lsst::meas::algorithms.brightStarStamps.BrightStarStamp.factory
def factory(cls, stamp_im, metadata, idx)
Definition: brightStarStamps.py:64
lsst::meas::algorithms.stamps.StampsBase
Definition: stamps.py:227
lsst::meas::algorithms.brightStarStamps.BrightStarStamps._checkRadius
def _checkRadius(self, radiusValue, metadataEnum)
Definition: brightStarStamps.py:273
lsst::meas::algorithms.brightStarStamps.BrightStarStamps.readFits
def readFits(cls, filename)
Definition: brightStarStamps.py:157
lsst::meas::algorithms.brightStarStamps.BrightStarStamps.getGaiaIds
def getGaiaIds(self)
Definition: brightStarStamps.py:230
lsst::meas::algorithms.stamps.StampsBase._metadata
_metadata
Definition: stamps.py:258