22"""Collection of small images (stamps).
25__all__ = [
"Stamp",
"Stamps",
"StampsBase",
"writeFits",
"readFitsWithOptions"]
29from dataclasses
import dataclass
31from typing
import Optional
36from lsst.geom import Box2I, Point2I, Extent2I, Angle, degrees, SpherePoint
38from lsst.utils.introspection
import get_full_type_name
42def writeFits(filename, stamps, metadata, type_name, write_mask, write_variance, write_archive=False):
43 """Write a single FITS file containing all stamps.
48 A string indicating the output filename
49 stamps : iterable of `BaseStamp`
50 An iterable of Stamp objects
51 metadata : `PropertyList`
52 A collection of key, value metadata pairs to be
53 written to the primary header
55 Python type name of the StampsBase subclass to use
57 Write the mask data to the output file?
58 write_variance : `bool`
59 Write the variance data to the output file?
60 write_archive : `bool`, optional
61 Write an archive to store Persistables along with each stamp?
64 metadata['HAS_MASK'] = write_mask
65 metadata[
'HAS_VARIANCE'] = write_variance
66 metadata[
'HAS_ARCHIVE'] = write_archive
67 metadata[
'N_STAMPS'] = len(stamps)
68 metadata[
'STAMPCLS'] = type_name
70 metadata[
'VERSION'] = 1
72 fitsFile = afwFits.Fits(filename,
"w")
73 fitsFile.createEmpty()
76 oa = afwTable.io.OutputArchive()
77 archive_ids = [oa.put(stamp.archive_element)
for stamp
in stamps]
78 metadata[
"ARCHIVE_IDS"] = archive_ids
79 fitsFile.writeMetadata(metadata)
80 oa.writeFits(fitsFile)
82 fitsFile.writeMetadata(metadata)
85 for i, stamp
in enumerate(stamps):
88 metadata.update({
'EXTVER': i+1,
'EXTNAME':
'IMAGE'})
89 stamp.stamp_im.getImage().
writeFits(filename, metadata=metadata, mode=
'a')
92 metadata.update({
'EXTVER': i+1,
'EXTNAME':
'MASK'})
93 stamp.stamp_im.getMask().
writeFits(filename, metadata=metadata, mode=
'a')
96 metadata.update({
'EXTVER': i+1,
'EXTNAME':
'VARIANCE'})
97 stamp.stamp_im.getVariance().
writeFits(filename, metadata=metadata, mode=
'a')
101def readFitsWithOptions(filename, stamp_factory, options):
102 """Read stamps from FITS file, allowing for only a
103 subregion of the stamps to be read.
108 A string indicating the file to read
109 stamp_factory : classmethod
110 A factory function defined on a dataclass for constructing
111 stamp objects a la `lsst.meas.alrogithm.Stamp`
112 options : `PropertyList`
or `dict`
113 A collection of parameters. If it contains a bounding box
114 (``bbox`` key),
or if certain other keys (``llcX``, ``llcY``,
115 ``width``, ``height``) are available
for one to be constructed,
116 the bounding box
is passed to the ``FitsReader``
in order to
121 stamps : `list` of dataclass objects like `Stamp`, PropertyList
122 A tuple of a list of `Stamp`-like objects
123 metadata : `PropertyList`
127 metadata = afwFits.readMetadata(filename, hdu=0)
128 nStamps = metadata[
"N_STAMPS"]
129 has_archive = metadata[
"HAS_ARCHIVE"]
131 archive_ids = metadata.getArray(
"ARCHIVE_IDS")
132 with afwFits.Fits(filename,
'r')
as f:
133 nExtensions = f.countHdus()
138 if "bbox" in options.keys():
139 kwargs[
"bbox"] = options[
"bbox"]
141 elif "llcX" in options.keys():
142 llcX = options[
"llcX"]
143 llcY = options[
"llcY"]
144 width = options[
"width"]
145 height = options[
"height"]
146 bbox =
Box2I(Point2I(llcX, llcY), Extent2I(width, height))
147 kwargs[
"bbox"] = bbox
151 for idx
in range(nExtensions-1):
152 md = afwFits.readMetadata(filename, hdu=idx+1)
153 if md[
'EXTNAME']
in (
'IMAGE',
'VARIANCE'):
155 elif md[
'EXTNAME'] ==
'MASK':
157 elif md[
'EXTNAME'] ==
'ARCHIVE_INDEX':
159 archive = afwTable.io.InputArchive.readFits(f)
161 elif md[
'EXTTYPE'] ==
'ARCHIVE_DATA':
164 raise ValueError(f
"Unknown extension type: {md['EXTNAME']}")
165 stamp_parts.setdefault(md[
'EXTVER'], {})[md[
'EXTNAME'].lower()] = reader.read(**kwargs)
166 if len(stamp_parts) != nStamps:
167 raise ValueError(f
'Number of stamps read ({len(stamp_parts)}) does not agree with the '
168 f
'number of stamps recorded in the metadata ({nStamps}).')
171 for k
in range(nStamps):
173 maskedImage = afwImage.MaskedImageF(**stamp_parts[k+1])
174 archive_element = archive.get(archive_ids[k])
if has_archive
else None
175 stamps.append(stamp_factory(maskedImage, metadata, k, archive_element))
177 return stamps, metadata
182 """Single abstract stamp
186 Inherit from this
class to add metadata
to the stamp
191 def factory(cls, stamp_im, metadata, index, archive_element=None):
192 """This method is needed to service the FITS reader.
193 We need a standard interface to construct objects like this.
194 Parameters needed to construct this object are passed in via
195 a metadata dictionary
and then passed to the constructor of
201 Pixel data to
pass to the constructor
203 Dictionary containing the information
204 needed by the constructor.
206 Index into the lists
in ``metadata``
207 archive_element : `lsst.afwTable.io.Persistable`, optional
208 Archive element (e.g. Transform
or WCS) associated
with this stamp.
212 stamp : `AbstractStamp`
213 An instance of this
class
215 raise NotImplementedError
224 stamp_im : `lsst.afw.image.MaskedImageF`
225 The actual pixel values for the postage stamp
227 Position of the center of the stamp. Note the user
228 must keep track of the coordinate system
230 stamp_im: afwImage.maskedImage.MaskedImageF
231 archive_element: Optional[afwTable.io.Persistable] = None
235 def factory(cls, stamp_im, metadata, index, archive_element=None):
236 """This method is needed to service the FITS reader.
237 We need a standard interface to construct objects like this.
238 Parameters needed to construct this object are passed in via
239 a metadata dictionary
and then passed to the constructor of
240 this
class. If lists of values are passed
with the following
241 keys, they will be passed to the constructor, otherwise dummy
242 values will be passed: RA_DEG, DEC_DEG. They should
243 each point to lists of values.
248 Pixel data to
pass to the constructor
250 Dictionary containing the information
251 needed by the constructor.
253 Index into the lists
in ``metadata``
254 archive_element : `afwTable.io.Persistable`, optional
255 Archive element (e.g. Transform
or WCS) associated
with this stamp.
260 An instance of this
class
262 if 'RA_DEG' in metadata
and 'DEC_DEG' in metadata:
263 return cls(stamp_im=stamp_im, archive_element=archive_element,
265 Angle(metadata.getArray(
'DEC_DEG')[index], degrees)))
267 return cls(stamp_im=stamp_im, archive_element=archive_element,
272 """Collection of stamps and associated metadata.
277 This should be an iterable of dataclass objects
280 Metadata associated
with the objects within the stamps.
281 use_mask : `bool`, optional
282 If ``
True`` read
and write the mask data. Default ``
True``.
283 use_variance : `bool`, optional
284 If ``
True`` read
and write the variance data. Default ``
True``.
285 use_archive : `bool`, optional
286 If ``
True``, read
and write an Archive that contains a Persistable
287 associated
with each stamp,
for example a Transform
or a WCS.
292 A butler can be used to read only a part of the stamps,
295 >>> starSubregions = butler.get(
"brightStarStamps", dataId, parameters={
'bbox': bbox})
298 def __init__(self, stamps, metadata=None, use_mask=True, use_variance=True,
300 if not hasattr(stamps,
'__iter__'):
301 raise ValueError(
'The stamps parameter must be iterable.')
303 if not isinstance(stamp, AbstractStamp):
304 raise ValueError(
'The entries in stamps must inherit from AbstractStamp. '
305 f
'Got {type(stamp)}.')
314 """Build an instance of this class from a file.
319 Name of the file to read
325 def readFitsWithOptions(cls, filename, options):
326 """Build an instance of this class with options.
331 Name of the file to read
332 options : `PropertyList`
333 Collection of metadata parameters
336 if cls
is not StampsBase:
337 raise NotImplementedError(
338 f
"Please implement specific FITS reader for class {cls}"
342 metadata = afwFits.readMetadata(filename, hdu=0)
343 type_name = metadata.get(
"STAMPCLS")
344 if type_name
is None:
346 f
"No class name in file {filename}. Unable to instantiate correct"
347 " stamps subclass. Is this an old version format Stamps file?"
351 stamp_type = doImport(type_name)
357 def _refresh_metadata(self):
358 """Make sure metadata is up to date since this object
361 raise NotImplementedError
364 """Write this object to a file.
369 Name of file to write
372 type_name = get_full_type_name(self)
386 """Retrieve star images.
391 `list` [`lsst.afw.image.maskedImage.maskedImage.MaskedImageF`]
393 return [stamp.stamp_im
for stamp
in self.
_stamps]
396 """Retrieve archive elements associated with each stamp.
401 `list` [`lsst.afwTable.io.Persistable`]
403 return [stamp.archive_element
for stamp
in self.
_stamps]
411 def _refresh_metadata(self):
413 self.
_metadata[
'RA_DEG'] = [p.getRa().asDegrees()
for p
in positions]
414 self.
_metadata[
'DEC_DEG'] = [p.getDec().asDegrees()
for p
in positions]
417 return [s.position
for s
in self.
_stamps]
420 """Add an additional stamp.
425 Stamp object to append.
427 if not isinstance(item, Stamp):
428 raise ValueError(
"Objects added must be a Stamp object.")
433 """Extend Stamps instance by appending elements from another instance.
437 stamps_list : `list` [`Stamp`]
438 List of Stamp object to append.
441 if not isinstance(s, Stamp):
442 raise ValueError(
'Can only extend with Stamp objects')
447 """Build an instance of this class from a file.
452 Name of the file to read
457 An instance of this class
462 def readFitsWithOptions(cls, filename, options):
463 """Build an instance of this class with options.
468 Name of the file to read
469 options : `PropertyList` or `dict`
470 Collection of metadata parameters
475 An instance of this
class
477 stamps, metadata = readFitsWithOptions(filename, Stamp.factory, options)
478 return cls(stamps, metadata=metadata, use_mask=metadata[
'HAS_MASK'],
479 use_variance=metadata[
'HAS_VARIANCE'], use_archive=metadata[
'HAS_ARCHIVE'])
A FITS reader class for regular Images.
A FITS reader class for Masks.
A class to manipulate images, masks, and variance as a single object.
Class for storing ordered metadata with comments.
A class representing an angle.
An integer coordinate rectangle.
Point in an unspecified spherical coordinate system.
def factory(cls, stamp_im, metadata, index, archive_element=None)
def factory(cls, stamp_im, metadata, index, archive_element=None)
def getArchiveElements(self)
def readFits(cls, filename)
def __getitem__(self, index)
def writeFits(self, filename)
def _refresh_metadata(self)
def readFitsWithOptions(cls, filename, options)
def getMaskedImages(self)
def __init__(self, stamps, metadata=None, use_mask=True, use_variance=True, use_archive=False)
def extend(self, stamp_list)
def readFits(cls, filename)
def readFitsWithOptions(cls, filename, options)
def writeFits(filename, stamps, metadata, type_name, write_mask, write_variance, write_archive=False)