lsst.meas.algorithms  14.0-7-g23fdbe95+10
gaussianPsfFactory.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 #
4 # Copyright 2008-2017 AURA/LSST.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
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 LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <https://www.lsstcorp.org/LegalNotices/>.
22 #
23 from __future__ import absolute_import, division
24 
25 __all__ = ["GaussianPsfFactory", "SigmaPerFwhm"]
26 
27 import math
28 
29 from lsst.pex.config import Config, Field, ConfigurableField
30 from .singleGaussianPsf import SingleGaussianPsf
31 from .doubleGaussianPsf import DoubleGaussianPsf
32 
33 SigmaPerFwhm = 1.0 / (2.0 * math.sqrt(2.0 * math.log(2.0)))
34 
35 
36 def isPositive(x):
37  return x > 0
38 
39 
40 class GaussianPsfFactory(Config):
41  """Factory for simple Gaussian PSF models
42 
43  Provides a high-level interface to DoubleGaussianPsf and SingleGaussianPsf
44  by specifying Gaussian PSF model width in FWHM instead of sigma,
45  and supporting computing kernel size as a multiple of PSF width.
46  This makes it suitable for tasks where PSF width is not known in advance.
47  """
48  size = Field(
49  doc="Kernel size (width and height) (pixels); if None then sizeFactor is used",
50  dtype=int,
51  optional=True,
52  default=None,
53  check=isPositive,
54  )
55  sizeFactor = Field(
56  doc = "Kernel size as a factor of fwhm (dimensionless); " +
57  "size = sizeFactor * fwhm; ignored if size is not None",
58  dtype=float,
59  optional=False,
60  default=3.0,
61  check=isPositive,
62  )
63  minSize = Field(
64  doc="Minimum kernel size if using sizeFactor (pixels); ignored if size is not None",
65  dtype=int,
66  optional=True,
67  default=5,
68  check=isPositive,
69  )
70  maxSize = Field(
71  doc="Maximum kernel size if using sizeFactor (pixels); ignored if size is not None",
72  dtype=int,
73  optional=True,
74  default=None,
75  check=isPositive,
76  )
77  defaultFwhm = Field(
78  doc="Default FWHM of Gaussian model of core of star (pixels)",
79  dtype=float,
80  default=3.0,
81  check=isPositive,
82  )
83  addWing = Field(
84  doc="Add a Gaussian to represent wings?",
85  dtype=bool,
86  optional=False,
87  default=True,
88  )
89  wingFwhmFactor = Field(
90  doc="wing width, as a multiple of core width (dimensionless); ignored if addWing false",
91  dtype=float,
92  optional=False,
93  default=2.5,
94  check=isPositive,
95  )
96  wingAmplitude = Field(
97  doc="wing amplitude, as a multiple of core amplitude (dimensionless); ignored if addWing false",
98  dtype=float,
99  optional=False,
100  default=0.1,
101  check=isPositive,
102  )
103 
104  def computeSizeAndSigma(self, fwhm=None):
105  """Compute kernel size and star width as sigma
106 
107  kernel size will be odd unless minSize or maxSize is used and that value is even.
108 
109  @param[in] fwhm: FWHM of core star (pixels); if None then defaultFwhm is used
110  @return two values:
111  - kernel size (width == height) in pixels
112  - sigma equivalent to supplied fwhm, assuming a Gaussian (pixels)
113 
114  @warning assumes a valid config
115  """
116  if fwhm is None:
117  fwhm = self.defaultFwhm
118 
119  if self.size is not None:
120  size = self.size
121  else:
122  desSize = (int(self.sizeFactor * fwhm) // 2) * 2 + 1 # make result odd
123  if self.minSize and self.minSize > desSize:
124  size = self.minSize
125  elif self.maxSize and self.maxSize < desSize:
126  size = self.maxSize
127  else:
128  size = desSize
129 
130  return size, fwhm * SigmaPerFwhm
131 
132  def validate(self):
133  Config.validate(self)
134  if self.minSize and self.maxSize and self.minSize > self.maxSize:
135  raise RuntimeError("minSize=%s > maxSize=%s" % (self.minSize, self.maxSize))
136 
137  def apply(self, fwhm=None):
138  """Construct a GaussianPsf
139 
140  @param[in] self: an instance of ConfigClass
141  @param[in] fwhm: FWHM of core of star (pixels); if None then self.defaultFwhm is used
142  @return a DoubleGaussianPsf if self.addWing is True, else a SingleGaussianPsf
143  """
144  kernelSize, sigma = self.computeSizeAndSigma(fwhm)
145  if self.addWing:
146  wingsSigma = sigma * self.wingFwhmFactor
147  return DoubleGaussianPsf(kernelSize, kernelSize, sigma, wingsSigma, self.wingAmplitude)
148  else:
149  return SingleGaussianPsf(kernelSize, kernelSize, sigma)
150 
151  @classmethod
152  def makeField(cls, doc):
153  """Make an lsst.pex.config.ConfigurableField
154  """
155  def applyWrapper(config, **kwargs):
156  """Construct a Gaussian PSF
157 
158  @param[in] config: an instance of GaussianPsfFactory
159  """
160  return config.apply(**kwargs)
161  return ConfigurableField(
162  doc=doc,
163  target=applyWrapper,
164  ConfigClass=cls
165  )