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 nStamps = metadata[
"N_STAMPS"]
110 if options
and options.exists(
"llcX"):
111 llcX = options[
"llcX"]
112 llcY = options[
"llcY"]
113 width = options[
"width"]
114 height = options[
"height"]
116 kwargs[
"bbox"] = bbox
119 while len(stamp_parts) < nStamps:
120 md = afwFits.readMetadata(filename, hdu=idx)
121 if md[
'EXTNAME']
in (
'IMAGE',
'VARIANCE'):
122 reader = afwImage.ImageFitsReader(filename, hdu=idx)
123 elif md[
'EXTNAME'] ==
'MASK':
124 reader = afwImage.MaskFitsReader(filename, hdu=idx)
126 raise ValueError(f
"Unknown extension type: {md['EXTNAME']}")
127 stamp_parts.setdefault(md[
'EXTVER'], {})[md[
'EXTNAME'].lower()] = reader.read(**kwargs)
133 meta_dict = metadata.toDict()
134 for k
in range(nStamps):
136 maskedImage = afwImage.MaskedImageF(**stamp_parts[k+1])
137 stamps.append(stamp_factory(maskedImage, meta_dict, k))
139 return stamps, metadata
144 """Single abstract stamp
148 Inherit from this class to add metadata to the stamp
154 """This method is needed to service the FITS reader.
155 We need a standard interface to construct objects like this.
156 Parameters needed to construct this object are passed in via
157 a metadata dictionary and then passed to the constructor of
162 stamp : `lsst.afw.image.MaskedImage`
163 Pixel data to pass to the constructor
165 Dictionary containing the information
166 needed by the constructor.
168 Index into the lists in ``metadata``
172 stamp : `AbstractStamp`
173 An instance of this class
175 raise NotImplementedError
184 stamp_im : `lsst.afw.image.MaskedImageF`
185 The actual pixel values for the postage stamp
186 position : `lsst.geom.SpherePoint`
187 Position of the center of the stamp. Note the user
188 must keep track of the coordinate system
190 stamp_im: afwImage.maskedImage.MaskedImageF
191 position: SpherePoint
195 """This method is needed to service the FITS reader.
196 We need a standard interface to construct objects like this.
197 Parameters needed to construct this object are passed in via
198 a metadata dictionary and then passed to the constructor of
199 this class. If lists of values are passed with the following
200 keys, they will be passed to the constructor, otherwise dummy
201 values will be passed: RA_DEG, DEC_DEG. They should
202 each point to lists of values.
206 stamp : `lsst.afw.image.MaskedImage`
207 Pixel data to pass to the constructor
209 Dictionary containing the information
210 needed by the constructor.
212 Index into the lists in ``metadata``
217 An instance of this class
219 if 'RA_DEG' in metadata
and 'DEC_DEG' in metadata:
220 return cls(stamp_im=stamp_im,
222 Angle(metadata[
'DEC_DEG'][index], degrees)))
228 """Collection of stamps and associated metadata.
233 This should be an iterable of dataclass objects
234 a la ``lsst.meas.algorithms.Stamp``.
235 metadata : `lsst.daf.base.PropertyList`, optional
236 Metadata associated with the bright stars.
237 use_mask : `bool`, optional
238 If ``True`` read and write the mask data. Default ``True``.
239 use_variance : `bool`, optional
240 If ``True`` read and write the variance data. Default ``True``.
244 A butler can be used to read only a part of the stamps,
247 >>> starSubregions = butler.get("brightStarStamps_sub", dataId, bbox=bbox)
250 def __init__(self, stamps, metadata=None, use_mask=True, use_variance=True):
251 if not hasattr(stamps,
'__iter__'):
252 raise ValueError(
'The stamps parameter must be iterable.')
254 if not isinstance(stamp, AbstractStamp):
255 raise ValueError(
'The entries in stamps must inherit from AbstractStamp. '
256 f
'Got {type(stamp)}.')
265 """Build an instance of this class from a file.
270 Name of the file to read
272 raise NotImplementedError
277 """Build an instance of this class with options.
282 Name of the file to read
283 options : `PropertyList`
284 Collection of metadata parameters
286 raise NotImplementedError
289 def _refresh_metadata(self):
290 """Make sure metadata is up to date since this object
293 raise NotImplementedError
296 """Write this object to a file.
301 Name of file to write
317 """Retrieve star images.
322 `list` [`lsst.afw.image.maskedImage.maskedImage.MaskedImageF`]
324 return [stamp.stamp_im
for stamp
in self.
_stamps]
332 def _refresh_metadata(self):
334 self.
_metadata[
'RA_DEG'] = [p.getRa().asDegrees()
for p
in positions]
335 self.
_metadata[
'DEC_DEG'] = [p.getDec().asDegrees()
for p
in positions]
338 return [s.position
for s
in self.
_stamps]
341 """Add an additional stamp.
346 Stamp object to append.
348 if not isinstance(item, Stamp):
349 raise ValueError(
"Ojbects added must be a Stamp object.")
354 """Extend Stamps instance by appending elements from another instance.
358 stamps_list : `list` [`Stamp`]
359 List of Stamp object to append.
362 if not isinstance(s, Stamp):
363 raise ValueError(
'Can only extend with Stamp objects')
368 """Build an instance of this class from a file.
373 Name of the file to read
378 An instance of this class
384 """Build an instance of this class with options.
389 Name of the file to read
390 options : `PropertyList`
391 Collection of metadata parameters
396 An instance of this class
399 return cls(stamps, metadata=metadata, use_mask=metadata[
'HAS_MASK'],
400 use_variance=metadata[
'HAS_VARIANCE'])