LSST Applications g00274db5b6+edbf708997,g00d0e8bbd7+edbf708997,g199a45376c+5137f08352,g1fd858c14a+1d4b6db739,g262e1987ae+f4d9505c4f,g29ae962dfc+7156fb1a53,g2cef7863aa+73c82f25e4,g35bb328faa+edbf708997,g3e17d7035e+5b3adc59f5,g3fd5ace14f+852fa6fbcb,g47891489e3+6dc8069a4c,g53246c7159+edbf708997,g64539dfbff+9f17e571f4,g67b6fd64d1+6dc8069a4c,g74acd417e5+ae494d68d9,g786e29fd12+af89c03590,g7ae74a0b1c+a25e60b391,g7aefaa3e3d+536efcc10a,g7cc15d900a+d121454f8d,g87389fa792+a4172ec7da,g89139ef638+6dc8069a4c,g8d7436a09f+28c28d8d6d,g8ea07a8fe4+db21c37724,g92c671f44c+9f17e571f4,g98df359435+b2e6376b13,g99af87f6a8+b0f4ad7b8d,gac66b60396+966efe6077,gb88ae4c679+7dec8f19df,gbaa8f7a6c5+38b34f4976,gbf99507273+edbf708997,gc24b5d6ed1+9f17e571f4,gca7fc764a6+6dc8069a4c,gcc769fe2a4+97d0256649,gd7ef33dd92+6dc8069a4c,gdab6d2f7ff+ae494d68d9,gdbb4c4dda9+9f17e571f4,ge410e46f29+6dc8069a4c,geaed405ab2+e194be0d2b,w.2025.47
LSST Data Management Base Package
Loading...
Searching...
No Matches
piffPsf.py
Go to the documentation of this file.
1# This file is part of meas_extensions_piff.
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# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
21
22__all__ = ["PiffPsf"]
23
24import pickle
25import piff
26from packaging.version import Version
27import numpy as np
28from lsst.afw.typehandling import StorableHelperFactory
29from lsst.meas.algorithms import ImagePsf
30from lsst.afw.image import Image, Color
31from lsst.geom import Box2I, Point2I, Extent2I, Point2D
32import logging
33
34
36 _factory = StorableHelperFactory(
37 "lsst.meas.extensions.piff.piffPsf",
38 "PiffPsf"
39 )
40
41 def __init__(self, width, height, piffResult, log=None):
42 assert width == height
43 ImagePsf.__init__(self)
44 self.width = width
45 self.height = height
46 self.dimensions = Extent2I(width, height)
47 self._piffResult = piffResult
48 self._averagePosition = None
49 self._averageColor = None
50 self.log = log or logging.getLogger(__name__)
51
52 try:
53 color_type = [s.data.properties['colorType'] for s in self._piffResult.stars
54 if not s.is_flagged and not s.is_reserve]
55 color_type = list(set(color_type))
56 if len(color_type) > 1:
57 self.log.warning(f"More than one color type was given during training: {color_type}")
58 self._color_type = None
59 else:
60 self._color_type = color_type[0]
61 except Exception:
62 self._color_type = None
63
64 @property
65 def piffResult(self):
66 return self._piffResult
67
68 # Storable overrides
69
70 def isPersistable(self):
71 return True
72
74 return "PiffPsf"
75
77 return "lsst.meas.extensions.piff.piffPsf"
78
79 def _write(self):
80 return pickle.dumps((self.width, self.height, self._piffResult))
81
82 @staticmethod
83 def _read(pkl):
84 width, height, piffResult = pickle.loads(pkl)
85 # We need to do surgery on pickles created with earlier versions of
86 # piff when using piff version 1.4 or later.
87 if Version(piff.version) >= Version("1.4"):
88 if not hasattr(piffResult, "_num"):
89 piffResult._num = None
90 piffResult.model._num = None
91 piffResult.model._fit_flux = None
92 piffResult.interp._num = None
93 if not hasattr(piffResult.model, "_maxk"):
94 piffResult.model._maxk = 0
95 if not hasattr(piffResult.model, "_stepk"):
96 piffResult.model._stepk = 0
97 return PiffPsf(width, height, piffResult)
98
99 # ImagePsf overrides
100
101 def __deepcopy__(self, meta=None):
102 return PiffPsf(self.width, self.height, self._piffResult)
103
104 def resized(self, width, height):
105 assert width == height
106 return PiffPsf(width, height, self._piffResult)
107
108 def _doComputeImage(self, position, color):
109 return self._doImage(position, center=None, color=color)
110
111 def _doComputeKernelImage(self, position, color):
112 return self._doImage(position, center=True, color=color)
113
114 def _doComputeBBox(self, position, color):
115 return self._doBBox(Point2I(0, 0), center=True)
116
118 if self._averagePosition is None:
119 x = np.mean([s.image_pos.x for s in self._piffResult.stars
120 if not s.is_flagged and not s.is_reserve])
121 y = np.mean([s.image_pos.y for s in self._piffResult.stars
122 if not s.is_flagged and not s.is_reserve])
123 self._averagePosition = Point2D(x, y)
124 return self._averagePosition
125
127 if self._averageColor is None:
128 if 'colorValue' in self._piffResult.interp_property_names:
129 c = np.nanmean([s.data.properties['colorValue'] for s in self._piffResult.stars
130 if not s.is_flagged and not s.is_reserve])
131 self._averageColor = Color(colorValue=c, colorType=self._color_type)
132 else:
133 self._averageColor = Color() # set value to nan.
134 return self._averageColor
135
136 # Internal private methods
137
138 def _doImage(self, position, center, color=None):
139 # Follow Piff conventions for center.
140 # None => draw as if star at position
141 # True => draw in center of image
142 if 'colorValue' in self._piffResult.interp_property_names:
143 if color is None or color.isIndeterminate():
144 meanColor = np.nan
145 if self._averageColor is None:
146 meanColor = self.getAverageColor().getColorValue()
147 else:
148 meanColor = self._averageColor.getColorValue()
149 kwargs = {'colorValue': meanColor}
150 # PSF model need a color information.
151 # Set to mean color from PSF fit right now.
152 else:
153 ctype = color.getColorType()
154 if self._color_type != color.getColorType():
155 raise ValueError(f"Invalid Color type. Need {self._color_type} but {ctype} is provided")
156 kwargs = {'colorValue': color.getColorValue()}
157 else:
158 kwargs = {}
159 gsimg = self._piffResult.draw(
160 position.x, position.y, stamp_size=self.width, center=center, **kwargs,
161 )
162 bbox = self._doBBox(position, center)
163 img = Image(bbox, dtype=np.float64)
164 img.array[:] = gsimg.array
165 img.array /= np.sum(img.array)
166 return img
167
168 def _doBBox(self, position, center):
169 origin = -(self.dimensions//2)
170 if center is None:
171 origin = Point2I(position) + origin
172 return Box2I(Point2I(origin), self.dimensions)
image::Color getAverageColor() const
Return the average Color of the stars used to construct the Psf.
Definition Psf.h:245
virtual lsst::geom::Point2D getAveragePosition() const
Return the average position of the stars used to construct the Psf.
Definition Psf.cc:189
Describe the colour of a source.
Definition Color.h:23
A class to represent a 2-dimensional array of pixels.
Definition Image.h:51
virtual bool isPersistable() const noexcept
Return true if this particular object can be persisted using afw::table::io.
An integer coordinate rectangle.
Definition Box.h:55
An intermediate base class for Psfs that use an image representation.
Definition ImagePsf.h:40
_doComputeKernelImage(self, position, color)
Definition piffPsf.py:111
_doComputeBBox(self, position, color)
Definition piffPsf.py:114
_doImage(self, position, center, color=None)
Definition piffPsf.py:138
_doComputeImage(self, position, color)
Definition piffPsf.py:108
__init__(self, width, height, piffResult, log=None)
Definition piffPsf.py:41