22 """Collection of small images (stamps).
25 __all__ = [
"Stamp",
"Stamps",
"StampsBase",
"writeFits",
"readFitsWithOptions"]
27 from collections.abc
import Sequence
29 from dataclasses
import dataclass
34 from lsst.geom import Box2I, Point2I, Extent2I, Angle, degrees, SpherePoint
38 def writeFits(filename, stamp_ims, metadata, write_mask, write_variance):
39 """Write a single FITS file containing all stamps.
44 A string indicating the output filename
45 stamps_ims : iterable of `lsst.afw.image.MaskedImageF`
46 An iterable of masked images
47 metadata : `PropertyList`
48 A collection of key, value metadata pairs to be
49 written to the primary header
51 Write the mask data to the output file?
52 write_variance : `bool`
53 Write the variance data to the output file?
55 metadata[
'HAS_MASK'] = write_mask
56 metadata[
'HAS_VARIANCE'] = write_variance
57 metadata[
'N_STAMPS'] = len(stamp_ims)
59 fitsPrimary = afwFits.Fits(filename,
"w")
60 fitsPrimary.createEmpty()
61 fitsPrimary.writeMetadata(metadata)
62 fitsPrimary.closeFile()
65 for i, stamp
in enumerate(stamp_ims):
68 metadata.update({
'EXTVER': i+1,
'EXTNAME':
'IMAGE'})
69 stamp.getImage().
writeFits(filename, metadata=metadata, mode=
'a')
72 metadata.update({
'EXTVER': i+1,
'EXTNAME':
'MASK'})
73 stamp.getMask().
writeFits(filename, metadata=metadata, mode=
'a')
76 metadata.update({
'EXTVER': i+1,
'EXTNAME':
'VARIANCE'})
77 stamp.getVariance().
writeFits(filename, metadata=metadata, mode=
'a')
82 """Read stamps from FITS file, allowing for only a
83 subregion of the stamps to be read.
88 A string indicating the file to read
89 stamp_factory : classmethod
90 A factory function defined on a dataclass for constructing
91 stamp objects a la `lsst.meas.alrogithm.Stamp`
92 options : `PropertyList`
93 A collection of parameters. If certain keys are available
94 (``llcX``, ``llcY``, ``width``, ``height``), a bounding box
95 is constructed and passed to the ``FitsReader`` in order
96 to return a sub-image.
100 stamps : `list` of dataclass objects like `Stamp`, PropertyList
101 A tuple of a list of `Stamp`-like objects
102 metadata : `PropertyList`
106 metadata = afwFits.readMetadata(filename, hdu=0)
107 f = afwFits.Fits(filename,
'r')
108 nExtensions = f.countHdus()
109 nStamps = metadata[
"N_STAMPS"]
112 if options
and options.exists(
"llcX"):
113 llcX = options[
"llcX"]
114 llcY = options[
"llcY"]
115 width = options[
"width"]
116 height = options[
"height"]
118 kwargs[
"bbox"] = bbox
122 for idx
in range(nExtensions-1):
123 md = afwFits.readMetadata(filename, hdu=idx+1)
124 if md[
'EXTNAME']
in (
'IMAGE',
'VARIANCE'):
125 reader = afwImage.ImageFitsReader(filename, hdu=idx+1)
126 elif md[
'EXTNAME'] ==
'MASK':
127 reader = afwImage.MaskFitsReader(filename, hdu=idx+1)
129 raise ValueError(f
"Unknown extension type: {md['EXTNAME']}")
130 stamp_parts.setdefault(md[
'EXTVER'], {})[md[
'EXTNAME'].lower()] = reader.read(**kwargs)
131 if len(stamp_parts) != nStamps:
132 raise ValueError(f
'Number of stamps read ({len(stamp_parts)}) does not agree with the '
133 f
'number of stamps recorded in the metadata ({nStamps}).')
136 for k
in range(nStamps):
138 maskedImage = afwImage.MaskedImageF(**stamp_parts[k+1])
139 stamps.append(stamp_factory(maskedImage, metadata, k))
141 return stamps, metadata
146 """Single abstract stamp
150 Inherit from this class to add metadata to the stamp
156 """This method is needed to service the FITS reader.
157 We need a standard interface to construct objects like this.
158 Parameters needed to construct this object are passed in via
159 a metadata dictionary and then passed to the constructor of
164 stamp : `lsst.afw.image.MaskedImage`
165 Pixel data to pass to the constructor
167 Dictionary containing the information
168 needed by the constructor.
170 Index into the lists in ``metadata``
174 stamp : `AbstractStamp`
175 An instance of this class
177 raise NotImplementedError
186 stamp_im : `lsst.afw.image.MaskedImageF`
187 The actual pixel values for the postage stamp
188 position : `lsst.geom.SpherePoint`
189 Position of the center of the stamp. Note the user
190 must keep track of the coordinate system
192 stamp_im: afwImage.maskedImage.MaskedImageF
193 position: SpherePoint
197 """This method is needed to service the FITS reader.
198 We need a standard interface to construct objects like this.
199 Parameters needed to construct this object are passed in via
200 a metadata dictionary and then passed to the constructor of
201 this class. If lists of values are passed with the following
202 keys, they will be passed to the constructor, otherwise dummy
203 values will be passed: RA_DEG, DEC_DEG. They should
204 each point to lists of values.
208 stamp : `lsst.afw.image.MaskedImage`
209 Pixel data to pass to the constructor
211 Dictionary containing the information
212 needed by the constructor.
214 Index into the lists in ``metadata``
219 An instance of this class
221 if 'RA_DEG' in metadata
and 'DEC_DEG' in metadata:
222 return cls(stamp_im=stamp_im,
224 Angle(metadata.getArray(
'DEC_DEG')[index], degrees)))
230 """Collection of stamps and associated metadata.
235 This should be an iterable of dataclass objects
236 a la ``lsst.meas.algorithms.Stamp``.
237 metadata : `lsst.daf.base.PropertyList`, optional
238 Metadata associated with the bright stars.
239 use_mask : `bool`, optional
240 If ``True`` read and write the mask data. Default ``True``.
241 use_variance : `bool`, optional
242 If ``True`` read and write the variance data. Default ``True``.
246 A butler can be used to read only a part of the stamps,
249 >>> starSubregions = butler.get("brightStarStamps_sub", dataId, bbox=bbox)
252 def __init__(self, stamps, metadata=None, use_mask=True, use_variance=True):
253 if not hasattr(stamps,
'__iter__'):
254 raise ValueError(
'The stamps parameter must be iterable.')
256 if not isinstance(stamp, AbstractStamp):
257 raise ValueError(
'The entries in stamps must inherit from AbstractStamp. '
258 f
'Got {type(stamp)}.')
267 """Build an instance of this class from a file.
272 Name of the file to read
274 raise NotImplementedError
279 """Build an instance of this class with options.
284 Name of the file to read
285 options : `PropertyList`
286 Collection of metadata parameters
288 raise NotImplementedError
291 def _refresh_metadata(self):
292 """Make sure metadata is up to date since this object
295 raise NotImplementedError
298 """Write this object to a file.
303 Name of file to write
319 """Retrieve star images.
324 `list` [`lsst.afw.image.maskedImage.maskedImage.MaskedImageF`]
326 return [stamp.stamp_im
for stamp
in self.
_stamps]
334 def _refresh_metadata(self):
336 self.
_metadata[
'RA_DEG'] = [p.getRa().asDegrees()
for p
in positions]
337 self.
_metadata[
'DEC_DEG'] = [p.getDec().asDegrees()
for p
in positions]
340 return [s.position
for s
in self.
_stamps]
343 """Add an additional stamp.
348 Stamp object to append.
350 if not isinstance(item, Stamp):
351 raise ValueError(
"Ojbects added must be a Stamp object.")
356 """Extend Stamps instance by appending elements from another instance.
360 stamps_list : `list` [`Stamp`]
361 List of Stamp object to append.
364 if not isinstance(s, Stamp):
365 raise ValueError(
'Can only extend with Stamp objects')
370 """Build an instance of this class from a file.
375 Name of the file to read
380 An instance of this class
386 """Build an instance of this class with options.
391 Name of the file to read
392 options : `PropertyList`
393 Collection of metadata parameters
398 An instance of this class
401 return cls(stamps, metadata=metadata, use_mask=metadata[
'HAS_MASK'],
402 use_variance=metadata[
'HAS_VARIANCE'])