22 """Collection of small images (stamps).
25 __all__ = [
"Stamp",
"Stamps",
"StampsBase",
"writeFits",
"readFitsWithOptions"]
27 from collections.abc
import Sequence
29 from dataclasses
import dataclass
31 from typing
import Optional
35 from lsst.geom import Box2I, Point2I, Extent2I, Angle, degrees, SpherePoint
37 from lsst.daf.butler.core.utils
import getFullTypeName
41 def writeFits(filename, stamp_ims, metadata, type_name, write_mask, write_variance):
42 """Write a single FITS file containing all stamps.
47 A string indicating the output filename
48 stamps_ims : iterable of `lsst.afw.image.MaskedImageF`
49 An iterable of masked images
50 metadata : `PropertyList`
51 A collection of key, value metadata pairs to be
52 written to the primary header
54 Python type name of the StampsBase subclass to use
56 Write the mask data to the output file?
57 write_variance : `bool`
58 Write the variance data to the output file?
60 metadata[
'HAS_MASK'] = write_mask
61 metadata[
'HAS_VARIANCE'] = write_variance
62 metadata[
'N_STAMPS'] = len(stamp_ims)
63 metadata[
'STAMPCLS'] = type_name
65 metadata[
'VERSION'] = 1
67 fitsPrimary = afwFits.Fits(filename,
"w")
68 fitsPrimary.createEmpty()
69 fitsPrimary.writeMetadata(metadata)
70 fitsPrimary.closeFile()
73 for i, stamp
in enumerate(stamp_ims):
76 metadata.update({
'EXTVER': i+1,
'EXTNAME':
'IMAGE'})
77 stamp.getImage().
writeFits(filename, metadata=metadata, mode=
'a')
80 metadata.update({
'EXTVER': i+1,
'EXTNAME':
'MASK'})
81 stamp.getMask().
writeFits(filename, metadata=metadata, mode=
'a')
84 metadata.update({
'EXTVER': i+1,
'EXTNAME':
'VARIANCE'})
85 stamp.getVariance().
writeFits(filename, metadata=metadata, mode=
'a')
90 """Read stamps from FITS file, allowing for only a
91 subregion of the stamps to be read.
96 A string indicating the file to read
97 stamp_factory : classmethod
98 A factory function defined on a dataclass for constructing
99 stamp objects a la `lsst.meas.alrogithm.Stamp`
100 options : `PropertyList` or `dict`
101 A collection of parameters. If it contains a bounding box
102 (``bbox`` key), or if certain other keys (``llcX``, ``llcY``,
103 ``width``, ``height``) are available for one to be constructed,
104 the bounding box is passed to the ``FitsReader`` in order to
109 stamps : `list` of dataclass objects like `Stamp`, PropertyList
110 A tuple of a list of `Stamp`-like objects
111 metadata : `PropertyList`
115 metadata = afwFits.readMetadata(filename, hdu=0)
116 f = afwFits.Fits(filename,
'r')
117 nExtensions = f.countHdus()
118 nStamps = metadata[
"N_STAMPS"]
123 if "bbox" in options.keys():
124 kwargs[
"bbox"] = options[
"bbox"]
126 elif "llcX" in options.keys():
127 llcX = options[
"llcX"]
128 llcY = options[
"llcY"]
129 width = options[
"width"]
130 height = options[
"height"]
132 kwargs[
"bbox"] = bbox
136 for idx
in range(nExtensions-1):
137 md = afwFits.readMetadata(filename, hdu=idx+1)
138 if md[
'EXTNAME']
in (
'IMAGE',
'VARIANCE'):
139 reader = afwImage.ImageFitsReader(filename, hdu=idx+1)
140 elif md[
'EXTNAME'] ==
'MASK':
141 reader = afwImage.MaskFitsReader(filename, hdu=idx+1)
143 raise ValueError(f
"Unknown extension type: {md['EXTNAME']}")
144 stamp_parts.setdefault(md[
'EXTVER'], {})[md[
'EXTNAME'].lower()] = reader.read(**kwargs)
145 if len(stamp_parts) != nStamps:
146 raise ValueError(f
'Number of stamps read ({len(stamp_parts)}) does not agree with the '
147 f
'number of stamps recorded in the metadata ({nStamps}).')
150 for k
in range(nStamps):
152 maskedImage = afwImage.MaskedImageF(**stamp_parts[k+1])
153 stamps.append(stamp_factory(maskedImage, metadata, k))
155 return stamps, metadata
160 """Single abstract stamp
164 Inherit from this class to add metadata to the stamp
170 """This method is needed to service the FITS reader.
171 We need a standard interface to construct objects like this.
172 Parameters needed to construct this object are passed in via
173 a metadata dictionary and then passed to the constructor of
178 stamp : `lsst.afw.image.MaskedImage`
179 Pixel data to pass to the constructor
181 Dictionary containing the information
182 needed by the constructor.
184 Index into the lists in ``metadata``
188 stamp : `AbstractStamp`
189 An instance of this class
191 raise NotImplementedError
200 stamp_im : `lsst.afw.image.MaskedImageF`
201 The actual pixel values for the postage stamp
202 position : `lsst.geom.SpherePoint`
203 Position of the center of the stamp. Note the user
204 must keep track of the coordinate system
206 stamp_im: afwImage.maskedImage.MaskedImageF
211 """This method is needed to service the FITS reader.
212 We need a standard interface to construct objects like this.
213 Parameters needed to construct this object are passed in via
214 a metadata dictionary and then passed to the constructor of
215 this class. If lists of values are passed with the following
216 keys, they will be passed to the constructor, otherwise dummy
217 values will be passed: RA_DEG, DEC_DEG. They should
218 each point to lists of values.
222 stamp : `lsst.afw.image.MaskedImage`
223 Pixel data to pass to the constructor
225 Dictionary containing the information
226 needed by the constructor.
228 Index into the lists in ``metadata``
233 An instance of this class
235 if 'RA_DEG' in metadata
and 'DEC_DEG' in metadata:
236 return cls(stamp_im=stamp_im,
238 Angle(metadata.getArray(
'DEC_DEG')[index], degrees)))
244 """Collection of stamps and associated metadata.
249 This should be an iterable of dataclass objects
250 a la ``lsst.meas.algorithms.Stamp``.
251 metadata : `lsst.daf.base.PropertyList`, optional
252 Metadata associated with the bright stars.
253 use_mask : `bool`, optional
254 If ``True`` read and write the mask data. Default ``True``.
255 use_variance : `bool`, optional
256 If ``True`` read and write the variance data. Default ``True``.
260 A butler can be used to read only a part of the stamps,
263 >>> starSubregions = butler.get("brightStarStamps_sub", dataId, bbox=bbox)
266 def __init__(self, stamps, metadata=None, use_mask=True, use_variance=True):
267 if not hasattr(stamps,
'__iter__'):
268 raise ValueError(
'The stamps parameter must be iterable.')
270 if not isinstance(stamp, AbstractStamp):
271 raise ValueError(
'The entries in stamps must inherit from AbstractStamp. '
272 f
'Got {type(stamp)}.')
280 """Build an instance of this class from a file.
285 Name of the file to read
292 """Build an instance of this class with options.
297 Name of the file to read
298 options : `PropertyList`
299 Collection of metadata parameters
302 if cls
is not StampsBase:
303 raise NotImplementedError(
304 f
"Please implement specific FITS reader for class {cls}"
308 metadata = afwFits.readMetadata(filename, hdu=0)
309 type_name = metadata.get(
"STAMPCLS")
310 if type_name
is None:
312 f
"No class name in file {filename}. Unable to instantiate correct"
313 " stamps subclass. Is this an old version format Stamps file?"
317 stamp_type = doImport(type_name)
323 def _refresh_metadata(self):
324 """Make sure metadata is up to date since this object
327 raise NotImplementedError
330 """Write this object to a file.
335 Name of file to write
339 type_name = getFullTypeName(self)
343 return len(self.
_stamps_stamps)
346 return self.
_stamps_stamps[index]
349 return iter(self.
_stamps_stamps)
352 """Retrieve star images.
357 `list` [`lsst.afw.image.maskedImage.maskedImage.MaskedImageF`]
359 return [stamp.stamp_im
for stamp
in self.
_stamps_stamps]
367 def _refresh_metadata(self):
369 self.
_metadata_metadata[
'RA_DEG'] = [p.getRa().asDegrees()
for p
in positions]
370 self.
_metadata_metadata[
'DEC_DEG'] = [p.getDec().asDegrees()
for p
in positions]
373 return [s.position
for s
in self.
_stamps_stamps]
376 """Add an additional stamp.
381 Stamp object to append.
383 if not isinstance(item, Stamp):
384 raise ValueError(
"Objects added must be a Stamp object.")
389 """Extend Stamps instance by appending elements from another instance.
393 stamps_list : `list` [`Stamp`]
394 List of Stamp object to append.
397 if not isinstance(s, Stamp):
398 raise ValueError(
'Can only extend with Stamp objects')
399 self.
_stamps_stamps += stamp_list
403 """Build an instance of this class from a file.
408 Name of the file to read
413 An instance of this class
419 """Build an instance of this class with options.
424 Name of the file to read
425 options : `PropertyList` or `dict`
426 Collection of metadata parameters
431 An instance of this class
434 return cls(stamps, metadata=metadata, use_mask=metadata[
'HAS_MASK'],
435 use_variance=metadata[
'HAS_VARIANCE'])
def factory(cls, stamp_im, metadata, index)
def factory(cls, stamp_im, metadata, index)
def readFits(cls, filename)
def __init__(self, stamps, metadata=None, use_mask=True, use_variance=True)
def __getitem__(self, index)
def writeFits(self, filename)
def _refresh_metadata(self)
def readFitsWithOptions(cls, filename, options)
def getMaskedImages(self)
def extend(self, stamp_list)
def readFits(cls, filename)
def readFitsWithOptions(cls, filename, options)
def writeFits(filename, stamp_ims, metadata, type_name, write_mask, write_variance)
def readFitsWithOptions(filename, stamp_factory, options)