Coverage for python / lsst / multiprofit / plotting / asinhstretchsigned.py: 31%
47 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 08:58 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 08:58 +0000
1# This file is part of multiprofit.
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/>.
22from __future__ import annotations
24import astropy.visualization as apvis
25import numpy as np
26from numpy.typing import NDArray
28# This is all hacked from astropy's AsinhStretch
30__all__ = ["AsinhStretchSigned", "SinhStretchSigned"]
33def _prepare(values: np.ndarray, clip: bool = True, out: np.ndarray | None = None) -> np.ndarray:
34 """Return clipped and/or copied values from input.
36 Parameters
37 ----------
38 values
39 The values to copy/clip from.
40 clip
41 Whether to clip values to between 0 and 1 (inclusive).
42 out
43 An existing array to assign to.
45 Returns
46 -------
47 prepared
48 The prepared values.
49 """
50 if clip:
51 return np.clip(values, 0.0, 1.0, out=out)
52 else:
53 if out is None:
54 return np.array(values, copy=True)
55 else:
56 out[:] = np.asarray(values)
57 return out
60class AsinhStretchSigned(apvis.BaseStretch):
61 r"""
62 A signed asinh stretch.
64 The stretch is given by:
66 .. math::
67 y = 0.5(1 + sign(x - 0.5)\frac{{\rm asinh}(2(x - 0.5) / a)}{{\rm asinh}(1 / a)}).
69 This is a signed version of the astropy AsinhStretch, which can be used
70 in plots of scaled model residuals (chi) to give greater contrast between
71 values around zero.
73 Parameters
74 ----------
75 a : float, optional
76 The ``a`` parameter used in the above formula. The value of
77 this parameter is where the asinh curve transitions from linear
78 to logarithmic behavior, expressed as a fraction of the
79 normalized image. Must be in the range between 0 and 1.
80 Default is 0.1.
81 """ # noqa: W505
83 def __init__(self, a: float = 0.1) -> None:
84 super().__init__()
85 self.a = a
87 def __call__(self, values: NDArray, clip: bool = True, out: np.ndarray = None) -> np.ndarray:
88 values = _prepare(values, clip=clip, out=out)
89 values *= 2
90 values -= 1
91 signs = np.sign(values)
92 np.abs(values, out=values)
93 np.true_divide(values, self.a, out=values)
94 np.arcsinh(values, out=values)
95 np.true_divide(values, np.arcsinh(1.0 / self.a), out=values)
96 np.true_divide(1.0 + signs * values, 2.0, out=values)
97 return values
99 @property
100 def inverse(self) -> SinhStretchSigned:
101 """A stretch object that performs the inverse operation.
103 Returns
104 -------
105 inverse
106 The inverse stretch.
107 """
108 return SinhStretchSigned(a=1.0 / np.arcsinh(1.0 / self.a))
111class SinhStretchSigned(apvis.BaseStretch):
112 r"""
113 A signed sinh stretch.
115 The stretch is given by:
117 .. math::
118 y = \frac{{\rm sinh}(x / a)}{{\rm sinh}(1 / a)}
120 This is a signed version of the astropy SinhStretch, which is provided for
121 completeness rather than for any particular use case.
123 Parameters
124 ----------
125 a : float, optional
126 The ``a`` parameter used in the above formula. Default is 1/3.
127 """
129 def __init__(self, a: float = 1.0 / 3.0) -> None:
130 super().__init__()
131 self.a = a
133 def __call__(self, values: NDArray, clip: bool = True, out: np.ndarray = None) -> np.ndarray:
134 values = _prepare(values, clip=clip, out=out)
135 values *= 2.0
136 values -= 1.0
137 np.true_divide(values, self.a, out=values)
138 np.sinh(values, out=values)
139 np.true_divide(values, np.sinh(1.0 / self.a), out=values)
140 values += 1.0
141 values /= 2.0
142 return values
144 @property
145 def inverse(self) -> AsinhStretchSigned:
146 """A stretch object that performs the inverse operation.
148 Returns
149 -------
150 inverse
151 The inverse stretch.
152 """
153 return AsinhStretchSigned(a=1.0 / np.sinh(1.0 / self.a))