22 """Collection of small images (stamps).
25 __all__ = [
"Stamp",
"Stamps",
"StampsBase",
"writeFits",
"readFitsWithOptions"]
29 from dataclasses
import dataclass
31 from typing
import Optional
36 from lsst.geom import Box2I, Point2I, Extent2I, Angle, degrees, SpherePoint
38 from lsst.daf.butler.core.utils
import getFullTypeName
42 def 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')
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"]
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
200 stamp : `lsst.afw.image.MaskedImage`
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
226 position : `lsst.geom.SpherePoint`, optional
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.
247 stamp : `lsst.afw.image.MaskedImage`
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
278 a la ``lsst.meas.algorithms.Stamp``.
279 metadata : `lsst.daf.base.PropertyList`, optional
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
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?"
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 = getFullTypeName(self)
377 return len(self.
_stamps_stamps)
380 return self.
_stamps_stamps[index]
386 """Retrieve star images.
391 `list` [`lsst.afw.image.maskedImage.maskedImage.MaskedImageF`]
393 return [stamp.stamp_im
for stamp
in self.
_stamps_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_stamps]
411 def _refresh_metadata(self):
413 self.
_metadata_metadata[
'RA_DEG'] = [p.getRa().asDegrees()
for p
in positions]
414 self.
_metadata_metadata[
'DEC_DEG'] = [p.getDec().asDegrees()
for p
in positions]
417 return [s.position
for s
in self.
_stamps_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')
443 self.
_stamps_stamps += stamp_list
447 """Build an instance of this class from a file.
452 Name of the file to read
457 An instance of this class
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
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.
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)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Extent< int, 2 > Extent2I
def writeFits(filename, stamps, metadata, type_name, write_mask, write_variance, write_archive=False)
def readFitsWithOptions(filename, stamp_factory, options)