22from __future__
import annotations
25 "ExtendedPsfImageInfo",
26 "ExtendedPsfImageSerializationModel",
31from types
import EllipsisType
32from typing
import Any, ClassVar
35from astropy.units
import UnitBase
36from pydantic
import BaseModel, Field
38from lsst.images
import Box, GeneralizedImage, Image, ImageSerializationModel
39from lsst.images.serialization
import ArchiveTree, InputArchive, MetadataValue, OutputArchive
41from .extended_psf_fit
import ExtendedPsfFit, ExtendedPsfMoffatFit
45 """Additional information about an `ExtendedPsfImage`.
49 n_stars : `int`, optional
50 Number of stars used to construct the extended PSF image.
53 n_stars: int |
None =
None
56 attrs =
", ".join(f
"{k}={v!r}" for k, v
in self.__dict__.items())
57 return f
"ExtendedPsfImageInfo({attrs})"
63 """A multi-plane image with data (image) and variance planes, and the
64 results of a profile fit to the image.
68 image : `~lsst.images.Image`
70 variance : `~lsst.images.Image`, optional
71 The per-pixel uncertainty of the main image as an image of variance
72 values. Must have the same bounding box as ``image`` if provided, and
73 its units must be the square of ``image.unit`` or `None`.
74 Values default to ``1.0``. Any attached projection is replaced
76 info : `ExtendedPsfImageInfo`, optional
77 Additional information about how the extended PSF image was
79 fit : `ExtendedPsfFit`, optional
80 The results of a profile fit to the image.
81 metadata : `dict` [`str`, `MetadataValue`], optional
82 Arbitrary flexible metadata to associate with the image.
86 image : `~lsst.images.Image`
88 variance : `~lsst.images.Image`
89 The per-pixel uncertainty of the main image as an image of variance
91 bbox : `~lsst.images.Box`
92 The bounding box shared by both image planes.
93 unit : `astropy.units.Unit` or `None`
94 The units of the image plane, or `None` if the image is dimensionless.
96 The projection that maps the pixel grid to the sky. Always `None` for
98 info : `ExtendedPsfImageInfo`
99 Additional information about how the extended PSF image was
101 fit : `ExtendedPsfFit`
102 The results of a profile fit to the image.
109 variance: Image |
None =
None,
110 info: ExtendedPsfImageInfo |
None =
None,
111 fit: ExtendedPsfFit |
None =
None,
112 metadata: dict[str, MetadataValue] |
None =
None,
120 unit=
None if image.unit
is None else image.unit**2,
123 if image.bbox != variance.bbox:
124 raise ValueError(f
"Image ({image.bbox}) and variance ({variance.bbox}) bboxes do not agree.")
125 if image.unit
is None:
126 if variance.unit
is not None:
127 raise ValueError(f
"Image has no units but variance does ({variance.unit}).")
128 elif variance.unit
is None:
129 variance = variance.view(unit=image.unit**2)
130 elif variance.unit != image.unit**2:
132 f
"Variance unit ({variance.unit}) should be the square of the image unit ({image.unit})."
145 """The main image plane (`Image`)."""
150 """The variance plane (`Image`)."""
155 """The bounding box shared by both image planes (`Box`)."""
159 def unit(self) -> UnitBase | None:
160 """The units of the image plane (`astropy.units.Unit` | `None`)."""
165 """The projection that maps the pixel grid to the sky.
167 ExtendedPsfImage does not support attached projections,
168 so this always returns `None`.
173 def info(self) -> ExtendedPsfImageInfo:
174 """Additional information about the image (`ExtendedPsfImageInfo`)."""
178 def fit(self) -> ExtendedPsfFit:
179 """The results of a profile fit to the image."""
182 def __getitem__(self, bbox: Box | EllipsisType) -> ExtendedPsfImage:
186 return self._transfer_metadata(
196 def __setitem__(self, bbox: Box | EllipsisType, value: ExtendedPsfImage) ->
None:
197 self.
_image[bbox] = value.image
201 return f
"ExtendedPsfImage({self.image!s}, info={self.info!r}, fit={self.fit!r})"
205 def copy(self) -> ExtendedPsfImage:
206 """Deep-copy the profile image and metadata."""
207 return self._transfer_metadata(
211 info=self.
_info.model_copy(),
212 fit=self.
_fit.model_copy(),
217 def serialize(self, archive: OutputArchive[Any]) -> ExtendedPsfImageSerializationModel:
218 """Serialize the Extended PSF image to an output archive.
225 serialized_image = archive.serialize_direct(
226 "image", functools.partial(self.
image.serialize, save_projection=
False)
228 serialized_variance = archive.serialize_direct(
229 "variance", functools.partial(self.
variance.serialize, save_projection=
False)
231 serialized_info = self.
info
232 serialized_fit = self.
fit
234 image=serialized_image,
235 variance=serialized_variance,
236 info=serialized_info,
238 metadata=self.metadata,
243 model: ExtendedPsfImageSerializationModel[Any], archive: InputArchive[Any], *, bbox: Box |
None =
None
244 ) -> ExtendedPsfImage:
245 """Deserialize an image from an input archive.
250 A Pydantic model representation of the image, holding references
251 to data stored in the archive.
253 Archive to read from.
255 Bounding box of a subimage to read instead.
257 return model.deserialize(archive, bbox=bbox)
260 def _get_archive_tree_type[P: BaseModel](
261 pointer_type: type[P],
262 ) -> type[ExtendedPsfImageSerializationModel[P]]:
263 """Return the serialization model type for this object for an archive
264 type that uses the given pointer type.
266 return ExtendedPsfImageSerializationModel[pointer_type]
270 """A Pydantic model used to represent a serialized `ExtendedPsfImage`."""
272 SCHEMA_NAME: ClassVar[str] =
"extended_psf_image"
273 SCHEMA_VERSION: ClassVar[str] =
"1.0.0"
274 MIN_READ_VERSION: ClassVar[int] = 1
275 PUBLIC_TYPE: ClassVar[type] = ExtendedPsfImage
277 image: ImageSerializationModel[P] = Field(
278 description=
"The main data image.",
280 variance: ImageSerializationModel[P] = Field(
281 description=
"Per-pixel variance estimates for the main image."
283 info: ExtendedPsfImageInfo = Field(
284 description=
"Additional information about the extended PSF image.",
286 fit: ExtendedPsfMoffatFit | ExtendedPsfFit = Field(
287 description=
"The results of an extended PSF fit to the image.",
292 """The bounding box of the image."""
293 return self.image.bbox
295 def deserialize(self, archive: InputArchive[Any], *, bbox: Box |
None =
None) -> ExtendedPsfImage:
296 """Deserialize an image from an input archive.
301 Archive to read from.
303 Bounding box of a subimage to read instead.
306 variance = self.variance.
deserialize(archive, bbox=bbox)
312 )._finish_deserialize(self)
None __setitem__(self, Box|EllipsisType bbox, ExtendedPsfImage value)
ExtendedPsfImageInfo info(self)
ExtendedPsfImage __getitem__(self, Box|EllipsisType bbox)
ExtendedPsfImage copy(self)
ExtendedPsfImage deserialize(ExtendedPsfImageSerializationModel[Any] model, InputArchive[Any] archive, *, Box|None bbox=None)
ExtendedPsfImageSerializationModel serialize(self, OutputArchive[Any] archive)
__init__(self, Image image, *, Image|None variance=None, ExtendedPsfImageInfo|None info=None, ExtendedPsfFit|None fit=None, dict[str, MetadataValue]|None metadata=None)
ExtendedPsfImageInfo _info
ExtendedPsfImage deserialize(self, InputArchive[Any] archive, *, Box|None bbox=None)
ImageSerializationModel variance
ExtendedPsfImageInfo info
ImageSerializationModel image