Coverage for python/lsst/images/psfs/_base.py: 2%
40 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-30 08:38 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-30 08:38 +0000
1# This file is part of lsst-images.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
12from __future__ import annotations
14__all__ = ("PointSpreadFunction",)
16from abc import ABC, abstractmethod
17from typing import Any
19from .._geom import Bounds, Box
20from .._image import Image
21from ..describe import DescribableMixin, DescribeOptions, FieldRole, Report, ReportField
24class PointSpreadFunction(DescribableMixin, ABC):
25 """Base class for point-spread function models."""
27 @property
28 @abstractmethod
29 def bounds(self) -> Bounds:
30 """The region where this PSF model is valid."""
31 raise NotImplementedError()
33 @property
34 @abstractmethod
35 def kernel_bbox(self) -> Box:
36 """Bounding box of all images returned by `compute_kernel_image`."""
37 raise NotImplementedError()
39 @abstractmethod
40 def compute_kernel_image(self, *, x: float, y: float) -> Image:
41 """Evaluate the PSF model into an image suitable for convolution.
43 Parameters
44 ----------
45 x
46 Column position coordinate to evaluate at.
47 y
48 Row position coordinate to evaluate at.
50 Returns
51 -------
52 Image
53 An image of the PSF, centered on the center of the center pixel,
54 which is defined to be ``(0, 0)`` by the image's origin.
55 """
56 raise NotImplementedError()
58 @abstractmethod
59 def compute_stellar_image(self, *, x: float, y: float) -> Image:
60 """Evaluate the PSF model into an image suitable for comparison with
61 the image of an astrophysical point source.
63 Parameters
64 ----------
65 x
66 Column position coordinate to evaluate at.
67 y
68 Row position coordinate to evaluate at.
70 Returns
71 -------
72 Image
73 An image of the PSF, centered on the given coordinates, just like
74 the postage stamp of a star would be.
75 """
76 raise NotImplementedError()
78 @abstractmethod
79 def compute_stellar_bbox(self, *, x: float, y: float) -> Box:
80 """Return the bounding box of the image that would be returned by
81 `compute_stellar_image`.
83 Parameters
84 ----------
85 x
86 Column position coordinate to evaluate at.
87 y
88 Row position coordinate to evaluate at.
90 Returns
91 -------
92 Box
93 The bounding box of the image that would be returned by
94 `compute_stellar_image` at the given point.
95 """
96 raise NotImplementedError()
98 @classmethod
99 def from_legacy(cls, legacy_psf: Any, bounds: Bounds) -> PointSpreadFunction:
100 """Make a PSF object from a legacy `lsst.afw.detection.Psf` instance.
102 Parameters
103 ----------
104 legacy_psf
105 Legacy PSF object.
106 bounds
107 The region where this PSF model is valid.
109 Returns
110 -------
111 `~lsst.images.psfs.PointSpreadFunction`
112 The converted PSF object.
114 Notes
115 -----
116 This base class method is a factory dispatch function that
117 automatically selects the right
118 `~lsst.images.psfs.PointSpreadFunction` subclass to use. When that is
119 already known, a subclass `from_legacy` method can be called instead.
120 """
121 from lsst.afw.detection import Psf
122 from lsst.cell_coadds import StitchedPsf
123 from lsst.meas.extensions.piff.piffPsf import PiffPsf
125 match legacy_psf:
126 case PiffPsf():
127 from ._piff import PiffWrapper
129 return PiffWrapper.from_legacy(legacy_psf, bounds)
130 case StitchedPsf():
131 from ..cells import CellPointSpreadFunction
133 return CellPointSpreadFunction.from_legacy(legacy_psf, bounds)
134 case Psf():
135 from ._legacy import LegacyPointSpreadFunction
137 return LegacyPointSpreadFunction.from_legacy(legacy_psf, bounds)
138 case _:
139 raise TypeError(f"{type(legacy_psf).__name__!r} is not a recognized legacy PSF type.")
141 def _describe(self, options: DescribeOptions = DescribeOptions(), /) -> Report:
142 """Return a `Report` describing this PSF.
144 Parameters
145 ----------
146 options : `DescribeOptions`, optional
147 Unused; accepted for interface compatibility.
148 """
149 return Report(
150 type_name=type(self).__name__,
151 summary=f"{type(self).__name__} over {self.bounds}",
152 fields=[
153 ReportField(label="bounds", value=self.bounds, role=FieldRole.DERIVED),
154 ReportField(label="kernel_bbox", value=self.kernel_bbox, role=FieldRole.DERIVED),
155 ],
156 )
158 def to_legacy(self) -> Any:
159 """Convert to a legacy `lsst.afw.detection.Psf`, if possible."""
160 raise NotImplementedError("This PSF does not support conversion to lsst.afw.detection.Psf.")