LSST Applications g0603fd7c41+d727c1d375,g124d44cf3d+604aa34adb,g180d380827+5ffeb7c294,g1afd7665f7+eb25d4c773,g2079a07aa2+86d27d4dc4,g2305ad1205+89e8e9cf63,g2bbee38e9b+44a02a0554,g337abbeb29+44a02a0554,g33d1c0ed96+44a02a0554,g3a166c0a6a+44a02a0554,g3d1719c13e+9a7b30876f,g487adcacf7+719c5abdea,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+85bc5ede60,g858d7b2824+9a7b30876f,g991b906543+9a7b30876f,g99cad8db69+8994cf29ec,g9b9dfce982+0fdee8fa3c,g9ddcbc5298+9a081db1e4,ga1e77700b3+03d07e1c1f,gb0e22166c9+60f28cb32d,gb23b769143+9a7b30876f,gb3a676b8dc+e2510deafe,gb4b16eec92+8df1cf93fe,gba4ed39666+c2a2e4ac27,gbb8dafda3b+7edb7fc777,gbd998247f1+585e252eca,gc120e1dc64+79b8551dca,gc28159a63d+44a02a0554,gc3e9b769f7+20d5ea8805,gcf0d15dbbd+0fdee8fa3c,gdaeeff99f8+f9a426f77a,ge79ae78c31+44a02a0554,gee10cc3b42+585e252eca,w.2024.19
LSST Data Management Base Package
Loading...
Searching...
No Matches
utils.py
Go to the documentation of this file.
1# This file is part of scarlet_lite.
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
22import sys
23
24import numpy as np
25import numpy.typing as npt
26from scipy.special import erfc
27
28ScalarLike = bool | int | float | complex
29ScalarTypes = (bool, int, float, complex)
30
31
32sqrt2 = np.sqrt(2)
33
34
35def integrated_gaussian_value(x: np.ndarray, sigma: float) -> np.ndarray:
36 """A Gaussian function evaluated at `x`
37
38 Parameters
39 ----------
40 x:
41 The coordinates to evaluate the integrated Gaussian
42 (ie. the centers of pixels).
43 sigma:
44 The standard deviation of the Gaussian.
45
46 Returns
47 -------
48 gaussian:
49 A Gaussian function integrated over `x`
50 """
51 lhs = erfc((0.5 - x) / (sqrt2 * sigma))
52 rhs = erfc((2 * x + 1) / (2 * sqrt2 * sigma))
53 return np.sqrt(np.pi / 2) * sigma * (1 - lhs + 1 - rhs)
54
55
57 x: np.ndarray | None = None, y: np.ndarray | None = None, sigma: float = 0.8
58) -> np.ndarray:
59 """Create a circular Gaussian that is integrated over pixels
60
61 This is typically used for the model PSF,
62 working well with the default parameters.
63
64 Parameters
65 ----------
66 x, y:
67 The x,y-coordinates to evaluate the integrated Gaussian.
68 If `X` and `Y` are `None` then they will both be given the
69 default value `numpy.arange(-7, 8)`, resulting in a
70 `15x15` centered image.
71 sigma:
72 The standard deviation of the Gaussian.
73
74 Returns
75 -------
76 image:
77 A Gaussian function integrated over `X` and `Y`.
78 """
79 if x is None:
80 if y is None:
81 x = np.arange(-7, 8)
82 y = x
83 else:
84 raise ValueError(
85 f"Either X and Y must be specified, or neither must be specified, got {x=} and {y=}"
86 )
87 elif y is None:
88 raise ValueError(f"Either X and Y must be specified, or neither must be specified, got {x=} and {y=}")
89
90 result = integrated_gaussian_value(x, sigma)[None, :] * integrated_gaussian_value(y, sigma)[:, None]
91 return result / np.sum(result)
92
93
94def get_circle_mask(diameter: int, dtype: npt.DTypeLike = np.float64):
95 """Get a boolean image of a circle
96
97 Parameters
98 ----------
99 diameter:
100 The diameter of the circle and width
101 of the image.
102 dtype:
103 The `dtype` of the image.
104
105 Returns
106 -------
107 circle:
108 A boolean array with ones for the pixels with centers
109 inside of the circle and zeros
110 outside of the circle.
111 """
112 c = (diameter - 1) / 2
113 # The center of the circle and its radius are
114 # off by half a pixel for circles with
115 # even numbered diameter
116 if diameter % 2 == 0:
117 radius = diameter / 2
118 else:
119 radius = c
120 x = np.arange(diameter)
121 x, y = np.meshgrid(x, x)
122 r = np.sqrt((x - c) ** 2 + (y - c) ** 2)
123
124 circle = np.ones((diameter, diameter), dtype=dtype)
125 circle[r > radius] = 0
126 return circle
127
128
129INTRINSIC_SPECIAL_ATTRIBUTES = frozenset(
130 (
131 "__qualname__",
132 "__module__",
133 "__metaclass__",
134 "__dict__",
135 "__weakref__",
136 "__class__",
137 "__subclasshook__",
138 "__name__",
139 "__doc__",
140 )
141)
142
143
145 """Return True if an attribute is safe to monkeypatch-transfer to another
146 class.
147 This rejects special methods that are defined automatically for all
148 classes, leaving only those explicitly defined in a class decorated by
149 `continueClass` or registered with an instance of `TemplateMeta`.
150 """
151 if name.startswith("__") and (
152 value is getattr(object, name, None) or name in INTRINSIC_SPECIAL_ATTRIBUTES
153 ):
154 return False
155 return True
156
157
159 """Re-open the decorated class, adding any new definitions into the
160 original.
161 For example:
162 .. code-block:: python
163 class Foo:
164 pass
165 @continueClass
166 class Foo:
167 def run(self):
168 return None
169 is equivalent to:
170 .. code-block:: python
171 class Foo:
172 def run(self):
173 return None
174 .. warning::
175 Python's built-in `super` function does not behave properly in classes
176 decorated with `continue_class`. Base class methods must be invoked
177 directly using their explicit types instead.
178
179 This is copied directly from lsst.utils. If any additional functions are
180 used from that repo we should remove this function and make lsst.utils
181 a dependency. But for now, it is easier to copy this single wrapper
182 than to include lsst.utils and all of its dependencies.
183 """
184 orig = getattr(sys.modules[cls.__module__], cls.__name__)
185 for name in dir(cls):
186 # Common descriptors like classmethod and staticmethod can only be
187 # accessed without invoking their magic if we use __dict__; if we use
188 # getattr on those we'll get e.g. a bound method instance on the dummy
189 # class rather than a classmethod instance we can put on the target
190 # class.
191 attr = cls.__dict__.get(name, None) or getattr(cls, name)
192 if is_attribute_safe_to_transfer(name, attr):
193 setattr(orig, name, attr)
194 return orig
np.ndarray integrated_gaussian_value(np.ndarray x, float sigma)
Definition utils.py:35
get_circle_mask(int diameter, npt.DTypeLike dtype=np.float64)
Definition utils.py:94
is_attribute_safe_to_transfer(name, value)
Definition utils.py:144
np.ndarray integrated_circular_gaussian(np.ndarray|None x=None, np.ndarray|None y=None, float sigma=0.8)
Definition utils.py:58