35from lsst.geom import Angle, Box2I, Extent2I, Point2I, SpherePoint, degrees
40def writeFits(filename, stamps, metadata, type_name, write_mask, write_variance, write_archive=False):
41 """Write a single FITS file containing all stamps.
46 A string indicating the output filename
47 stamps : iterable of `BaseStamp`
48 An iterable of Stamp objects
49 metadata : `PropertyList`
50 A collection of key, value metadata pairs to be
51 written to the primary header
53 Python type name of the StampsBase subclass to use
55 Write the mask data to the output file?
56 write_variance : `bool`
57 Write the variance data to the output file?
58 write_archive : `bool`, optional
59 Write an archive to store Persistables along with each stamp?
62 metadata[
"HAS_MASK"] = write_mask
63 metadata[
"HAS_VARIANCE"] = write_variance
64 metadata[
"HAS_ARCHIVE"] = write_archive
65 metadata[
"N_STAMPS"] = len(stamps)
66 metadata[
"STAMPCLS"] = type_name
68 metadata[
"VERSION"] = 1
70 fitsFile =
Fits(filename,
"w")
71 fitsFile.createEmpty()
75 archive_ids = [oa.put(stamp.archive_element)
for stamp
in stamps]
76 metadata[
"ARCHIVE_IDS"] = archive_ids
77 fitsFile.writeMetadata(metadata)
78 oa.writeFits(fitsFile)
80 fitsFile.writeMetadata(metadata)
83 for i, stamp
in enumerate(stamps):
86 metadata.update({
"EXTVER": i + 1,
"EXTNAME":
"IMAGE"})
87 stamp.stamp_im.getImage().
writeFits(filename, metadata=metadata, mode=
"a")
90 metadata.update({
"EXTVER": i + 1,
"EXTNAME":
"MASK"})
91 stamp.stamp_im.getMask().
writeFits(filename, metadata=metadata, mode=
"a")
94 metadata.update({
"EXTVER": i + 1,
"EXTNAME":
"VARIANCE"})
95 stamp.stamp_im.getVariance().
writeFits(filename, metadata=metadata, mode=
"a")
100 """Read stamps from FITS file, allowing for only a subregion of the stamps
106 A string indicating the file to read
107 stamp_factory : classmethod
108 A factory function defined on a dataclass for constructing
109 stamp objects a la `~lsst.meas.algorithm.Stamp`
110 options : `PropertyList` or `dict`
111 A collection of parameters. If it contains a bounding box
112 (``bbox`` key), or if certain other keys (``llcX``, ``llcY``,
113 ``width``, ``height``) are available for one to be constructed,
114 the bounding box is passed to the ``FitsReader`` in order to
119 stamps : `list` of dataclass objects like `Stamp`, PropertyList
120 A tuple of a list of `Stamp`-like objects
121 metadata : `PropertyList`
126 The data are read using the data type expected by the
127 `~lsst.afw.image.MaskedImage` class attached to the `AbstractStamp`
128 dataclass associated with the factory method.
131 metadata = readMetadata(filename, hdu=0)
132 nStamps = metadata[
"N_STAMPS"]
133 has_archive = metadata[
"HAS_ARCHIVE"]
135 archive_ids = metadata.getArray(
"ARCHIVE_IDS")
136 with Fits(filename,
"r")
as f:
137 nExtensions = f.countHdus()
142 if "bbox" in options.keys():
143 kwargs[
"bbox"] = options[
"bbox"]
145 elif "llcX" in options.keys():
146 llcX = options[
"llcX"]
147 llcY = options[
"llcY"]
148 width = options[
"width"]
149 height = options[
"height"]
150 bbox =
Box2I(Point2I(llcX, llcY), Extent2I(width, height))
151 kwargs[
"bbox"] = bbox
157 masked_image_cls =
None
158 for stamp_field
in fields(stamp_factory.__self__):
159 if issubclass(stamp_field.type, MaskedImage):
160 masked_image_cls = stamp_field.type
163 raise RuntimeError(
"Stamp factory does not use MaskedImage.")
164 default_dtype = np.dtype(masked_image_cls.dtype)
165 variance_dtype = np.dtype(np.float32)
168 for idx
in range(nExtensions - 1):
170 md = readMetadata(filename, hdu=idx + 1)
171 if md[
"EXTNAME"]
in (
"IMAGE",
"VARIANCE"):
173 if md[
"EXTNAME"] ==
"VARIANCE":
174 dtype = variance_dtype
176 dtype = default_dtype
177 elif md[
"EXTNAME"] ==
"MASK":
179 elif md[
"EXTNAME"] ==
"ARCHIVE_INDEX":
181 archive = InputArchive.readFits(f)
183 elif md[
"EXTTYPE"] ==
"ARCHIVE_DATA":
186 raise ValueError(f
"Unknown extension type: {md['EXTNAME']}")
187 stamp_parts.setdefault(md[
"EXTVER"], {})[md[
"EXTNAME"].lower()] = reader.read(dtype=dtype,
189 if len(stamp_parts) != nStamps:
191 f
"Number of stamps read ({len(stamp_parts)}) does not agree with the "
192 f
"number of stamps recorded in the metadata ({nStamps})."
196 for k
in range(nStamps):
198 maskedImage = masked_image_cls(**stamp_parts[k + 1])
199 archive_element = archive.get(archive_ids[k])
if has_archive
else None
200 stamps.append(stamp_factory(maskedImage, metadata, k, archive_element))
202 return stamps, metadata