22from __future__
import annotations
25 "ExtendedPsfCandidateInfo",
26 "ExtendedPsfCandidateSerializationModel",
27 "ExtendedPsfCandidatesSerializationModel",
28 "ExtendedPsfCandidate",
29 "ExtendedPsfCandidates",
33from collections.abc
import Sequence
34from types
import EllipsisType
35from typing
import Any, ClassVar
37from pydantic
import BaseModel, Field
39from lsst.images
import (
42 ImageSerializationModel,
45 MaskedImageSerializationModel,
50from lsst.images.serialization
import ArchiveTree, InputArchive, MetadataValue, OutputArchive, Quantity
51from lsst.images.utils
import is_none
52from lsst.resources
import ResourcePathExpression
56 """Information about a star in an `ExtendedPsfCandidate`.
60 visit : `int`, optional
61 The visit during which the star was observed.
62 detector : `int`, optional
63 The detector on which the star was observed.
64 ref_id : `int`, optional
65 The reference catalog ID for the star.
66 ref_mag : `float`, optional
67 The reference magnitude for the star.
68 position_x : `float`, optional
69 The x-coordinate of the star in the focal plane.
70 position_y : `float`, optional
71 The y-coordinate of the star in the focal plane.
72 focal_plane_radius : `~lsst.images.utils.Quantity`, optional
73 The radius of the star from the center of the focal plane.
74 focal_plane_angle : `~lsst.images.utils.Quantity`, optional
75 The angle of the star in the focal plane, measured from the +x axis.
78 visit: int |
None =
None
79 detector: int |
None =
None
80 ref_id: int |
None =
None
81 ref_mag: float |
None =
None
82 position_x: float |
None =
None
83 position_y: float |
None =
None
84 focal_plane_radius: Quantity |
None =
None
85 focal_plane_angle: Quantity |
None =
None
88 attrs =
", ".join(f
"{k}={v!r}" for k, v
in self.__dict__.items())
89 return f
"ExtendedPsfCandidateInfo({attrs})"
95 """A cutout centered on a star, with associated metadata.
99 image : `~lsst.images.Image`
100 The main data image for this star cutout.
101 mask : `~lsst.images.Mask`, optional
102 Bitmask that annotates the main image's pixels.
103 variance : `~lsst.images.Image`, optional
104 Per-pixel variance estimates for the image.
105 mask_schema : `~lsst.images.MaskSchema`, optional
106 Schema for the mask, required if a mask is provided.
107 sky_projection : `~lsst.images.SkyProjection`, optional
108 Projection to map pixels to the sky.
109 metadata : `dict` [`str`, `MetadataValue`], optional
110 Additional metadata to associate with this cutout.
111 psf_kernel_image : `~lsst.images.Image`, optional
112 Kernel image of the PSF at the cutout center.
113 star_info : `ExtendedPsfCandidateInfo`, optional
114 Information about the star in the cutout.
118 psf_kernel_image : `~lsst.images.Image`
119 Kernel image of the PSF at the cutout center.
120 star_info : `ExtendedPsfCandidateInfo`
121 Information about the star in this cutout.
128 mask: Mask |
None =
None,
129 variance: Image |
None =
None,
130 mask_schema: MaskSchema |
None =
None,
131 sky_projection: SkyProjection |
None =
None,
132 metadata: dict[str, MetadataValue] |
None =
None,
133 psf_kernel_image: Image |
None =
None,
134 star_info: ExtendedPsfCandidateInfo |
None =
None,
140 mask_schema=mask_schema,
141 sky_projection=sky_projection,
148 def __getitem__(self, bbox: Box | EllipsisType) -> ExtendedPsfCandidate:
152 return self._transfer_metadata(
156 mask=self.mask[bbox],
157 variance=self.variance[bbox],
165 return f
"ExtendedPsfCandidate({self.image!s}, {list(self.mask.schema.names)}, {self.star_info})"
169 f
"ExtendedPsfCandidate({self.image!r}, mask_schema={self.mask.schema!r}, "
170 f
"star_info={self.star_info!r})"
175 """Kernel image of the PSF at the cutout center."""
177 raise RuntimeError(
"No PSF kernel image is attached to this ExtendedPsfCandidate.")
182 """Return the ExtendedPsfCandidateInfo associated with this star."""
185 def copy(self) -> ExtendedPsfCandidate:
186 """Deep-copy the star cutout, metadata, and star info."""
187 return self._transfer_metadata(
189 image=self._image.
copy(),
190 mask=self._mask.
copy(),
191 variance=self._variance.
copy(),
198 def serialize(self, archive: OutputArchive[Any]) -> ExtendedPsfCandidateSerializationModel:
199 masked_image_model = super().
serialize(archive)
200 serialized_psf_kernel_image = (
201 archive.serialize_direct(
209 **masked_image_model.model_dump(),
210 psf_kernel_image=serialized_psf_kernel_image,
215 def _get_archive_tree_type[P: BaseModel](
216 pointer_type: type[P],
217 ) -> type[ExtendedPsfCandidateSerializationModel[P]]:
218 return ExtendedPsfCandidateSerializationModel[pointer_type]
222 """A Pydantic model to represent a serialized `ExtendedPsfCandidate`."""
224 SCHEMA_NAME: ClassVar[str] =
"extended_psf_candidate"
225 SCHEMA_VERSION: ClassVar[str] =
"1.0.0"
226 MIN_READ_VERSION: ClassVar[int] = 1
227 PUBLIC_TYPE: ClassVar[type] = ExtendedPsfCandidate
229 psf_kernel_image: ImageSerializationModel[P] |
None = Field(
232 description=
"Kernel image of the PSF at the cutout center.",
234 star_info: ExtendedPsfCandidateInfo = Field(
235 description=
"Information about the star in the cutout.",
238 def deserialize(self, archive: InputArchive[Any], *, bbox: Box |
None =
None) -> ExtendedPsfCandidate:
239 masked_image = super().
deserialize(archive, bbox=bbox)
241 self.psf_kernel_image.
deserialize(archive)
if self.psf_kernel_image
is not None else None
245 mask=masked_image.mask,
246 variance=masked_image.variance,
247 psf_kernel_image=psf_kernel_image,
248 star_info=self.star_info,
249 )._finish_deserialize(self)
253 """A collection of star cutouts.
257 candidates : `Iterable` [`ExtendedPsfCandidate`]
258 Collection of `ExtendedPsfCandidate` instances.
259 metadata : `dict` [`str`, `MetadataValue`], optional
260 Global metadata associated with the collection.
264 metadata : `dict` [`str`, `MetadataValue`]
265 Global metadata associated with the collection.
266 ref_id_map : `dict` [`int`, `ExtendedPsfCandidate`]
267 A mapping from reference IDs to `ExtendedPsfCandidate` objects.
268 Only includes candidates with valid reference IDs.
273 candidates: Sequence[ExtendedPsfCandidate],
274 metadata: dict[str, MetadataValue] |
None =
None,
277 self.
_metadata = {}
if metadata
is None else dict(metadata)
279 candidate.star_info.ref_id: candidate
280 for candidate
in self
281 if candidate.star_info.ref_id
is not None
288 if isinstance(index, slice):
296 return f
"ExtendedPsfCandidates(length={len(self)})"
302 """Return the collection's global metadata as a dict."""
307 """Map reference IDs to `ExtendedPsfCandidate` objects."""
311 def read_fits(cls, url: ResourcePathExpression) -> ExtendedPsfCandidates:
312 """Read a collection from a FITS file.
317 URL of the file to read; may be any type supported by
318 `lsst.resources.ResourcePath`.
320 return fits.read(cls, url).deserialized
323 """Write the collection to a FITS file.
328 Name of the file to write to. Must not already exist.
330 fits.write(self, filename)
332 def serialize(self, archive: OutputArchive[Any]) -> ExtendedPsfCandidatesSerializationModel:
335 archive.serialize_direct(f
"candidate_{index}", candidate.serialize)
336 for index, candidate
in enumerate(self.
_candidates)
342 def _get_archive_tree_type[P: BaseModel](
343 pointer_type: type[P],
344 ) -> type[ExtendedPsfCandidatesSerializationModel[P]]:
345 return ExtendedPsfCandidatesSerializationModel[pointer_type]
349 """A Pydantic model to represent serialized `ExtendedPsfCandidates`."""
351 SCHEMA_NAME: ClassVar[str] =
"extended_psf_candidates"
352 SCHEMA_VERSION: ClassVar[str] =
"1.0.0"
353 MIN_READ_VERSION: ClassVar[int] = 1
354 PUBLIC_TYPE: ClassVar[type] = ExtendedPsfCandidates
356 candidates: list[ExtendedPsfCandidateSerializationModel[P]] = Field(
357 default_factory=list,
358 description=
"The candidate cutouts in this collection.",
361 def deserialize(self, archive: InputArchive[Any]) -> ExtendedPsfCandidates:
363 [candidate_model.deserialize(archive)
for candidate_model
in self.candidates],
364 metadata=self.metadata,
__init__(self, Image image, *, Mask|None mask=None, Image|None variance=None, MaskSchema|None mask_schema=None, SkyProjection|None sky_projection=None, dict[str, MetadataValue]|None metadata=None, Image|None psf_kernel_image=None, ExtendedPsfCandidateInfo|None star_info=None)
ExtendedPsfCandidateInfo _star_info
ExtendedPsfCandidate copy(self)
ExtendedPsfCandidateSerializationModel serialize(self, OutputArchive[Any] archive)
Image psf_kernel_image(self)
ExtendedPsfCandidateInfo star_info(self)
ExtendedPsfCandidate __getitem__(self, Box|EllipsisType bbox)
ExtendedPsfCandidatesSerializationModel serialize(self, OutputArchive[Any] archive)
None write_fits(self, str filename)
ExtendedPsfCandidates read_fits(cls, ResourcePathExpression url)
__init__(self, Sequence[ExtendedPsfCandidate] candidates, dict[str, MetadataValue]|None metadata=None)
ExtendedPsfCandidate deserialize(self, InputArchive[Any] archive, *, Box|None bbox=None)
ExtendedPsfCandidateInfo star_info
ImageSerializationModel psf_kernel_image