lsst.ip.diffim  21.0.0-16-gec61338+16b039d1e0
makeKernelBasisList.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2016 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 __all__ = ["makeKernelBasisList", "generateAlardLuptonBasisList"]
24 
25 from . import diffimLib
26 from lsst.log import Log
27 import numpy as np
28 
29 sigma2fwhm = 2. * np.sqrt(2. * np.log(2.))
30 
31 
32 def makeKernelBasisList(config, targetFwhmPix=None, referenceFwhmPix=None,
33  basisDegGauss=None, metadata=None):
34  """Generate the delta function or Alard-Lupton kernel bases depending on the Config.
35  Wrapper to call either `lsst.ip.diffim.makeDeltaFunctionBasisList` or
36  `lsst.ip.diffim.generateAlardLuptonBasisList`.
37 
38  Parameters
39  ----------
40  config : `lsst.ip.diffim.PsfMatchConfigAL`
41  Configuration object.
42  targetFwhmPix : `float`, optional
43  Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
44  Not used for delta function basis sets.
45  referenceFwhmPix : `float`, optional
46  Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
47  Not used for delta function basis sets.
48  basisDegGauss : `list` of `int`, optional
49  Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
50  Not used for delta function basis sets.
51  metadata : `lsst.daf.base.PropertySet`, optional
52  Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
53  Not used for delta function basis sets.
54 
55  Returns
56  -------
57  basisList: `list` of `lsst.afw.math.kernel.FixedKernel`
58  List of basis kernels.
59 
60  Notes
61  -----
62  See `lsst.ip.diffim.generateAlardLuptonBasisList` and
63  `lsst.ip.diffim.makeDeltaFunctionBasisList` for more information.
64 
65  Raises
66  ------
67  ValueError
68  If ``config.kernelBasisSet`` has an invalid value (not "alard-lupton" or "delta-function").
69  """
70  if config.kernelBasisSet == "alard-lupton":
71  return generateAlardLuptonBasisList(config, targetFwhmPix=targetFwhmPix,
72  referenceFwhmPix=referenceFwhmPix,
73  basisDegGauss=basisDegGauss,
74  metadata=metadata)
75  elif config.kernelBasisSet == "delta-function":
76  kernelSize = config.kernelSize
77  return diffimLib.makeDeltaFunctionBasisList(kernelSize, kernelSize)
78  else:
79  raise ValueError("Cannot generate %s basis set" % (config.kernelBasisSet))
80 
81 
82 def generateAlardLuptonBasisList(config, targetFwhmPix=None, referenceFwhmPix=None,
83  basisDegGauss=None, metadata=None):
84  """Generate an Alard-Lupton kernel basis list based upon the Config and
85  the input FWHM of the science and template images.
86 
87  Parameters
88  ----------
89  config : `lsst.ip.diffim.PsfMatchConfigAL`
90  Configuration object for the Alard-Lupton algorithm.
91  targetFwhmPix : `float`, optional
92  Fwhm width (pixel) of the template exposure characteristic psf.
93  This is the _target_ that will be matched to the science exposure.
94  referenceFwhmPix : `float`, optional
95  Fwhm width (pixel) of the science exposure characteristic psf.
96  basisDegGauss : `list` of `int`, optional
97  Polynomial degree of each Gaussian (sigma) basis. If None, defaults to `config.alardDegGauss`.
98  metadata : `lsst.daf.base.PropertySet`, optional
99  If specified, object to collect metadata fields about the kernel basis list.
100 
101  Returns
102  -------
103  basisList : `list` of `lsst.afw.math.kernel.FixedKernel`
104  List of basis kernels. For each degree value ``n`` in ``config.basisDegGauss`` (n+2)(n+1)/2 kernels
105  are generated and appended to the list in the order of the polynomial parameter number.
106  See `lsst.afw.math.polynomialFunction2D` documentation for more details.
107 
108  Raises
109  ------
110  RuntimeError
111  - if ``config.kernelBasisSet`` is not equal to "alard-lupton"
112  ValueError
113  - if ``config.kernelSize`` is even
114  - if the number of Gaussians and the number of given
115  sigma values are not equal or
116  - if the number of Gaussians and the number of given
117  polynomial degree values are not equal
118 
119  Notes
120  -----
121  The polynomial functions (``f``) are always evaluated in the -1.0, +1.0 range in both x, y directions,
122  edge to edge, with ``f(0,0)`` evaluated at the kernel center pixel, ``f(-1.0,-1.0)`` at the kernel
123  ``(0,0)`` pixel. They are not scaled by the sigmas of the Gaussians.
124 
125  Base Gaussian widths (sigmas in pixels) of the kernels are determined as:
126  - If not all fwhm parameters are provided or ``config.scaleByFwhm==False``
127  then ``config.alardNGauss`` and ``config.alardSigGauss`` are used.
128  - If ``targetFwhmPix<referenceFwhmPix`` (normal convolution):
129  First sigma ``Sig_K`` is determined to satisfy: ``Sig_reference**2 = Sig_target**2 + Sig_K**2``.
130  If it's larger than ``config.alardMinSig * config.alardGaussBeta``, make it the
131  second kernel. Else make it the smallest kernel, unless only 1 kernel is asked for.
132  - If ``referenceFwhmPix < targetFwhmPix`` (deconvolution):
133  Define the progression of Gaussians using a
134  method to derive a deconvolution sum-of-Gaussians from it's
135  convolution counterpart. [1]_ Only use 3 since the algorithm
136  assumes 3 components.
137 
138  **Metadata fields**
139 
140  ALBasisNGauss : `int`
141  The number of base Gaussians in the AL basis functions.
142  ALBasisDegGauss : `list` of `int`
143  Polynomial order of spatial modification of the base Gaussian functions.
144  ALBasisSigGauss : `list` of `float`
145  Sigmas in pixels of the base Gaussians.
146  ALKernelSize : `int`
147  Kernel stamp size is (ALKernelSize pix, ALKernelSize pix).
148  ALBasisMode : `str`, either of ``config``, ``convolution``, ``deconvolution``
149  Indicates whether the config file values, the convolution or deconvolution algorithm
150  was used to determine the base Gaussian sigmas and the kernel stamp size.
151 
152  References
153  ----------
154  .. [1] Ulmer, W.: Inverse problem of linear combinations of Gaussian convolution kernels
155  (deconvolution) and some applications to proton/photon dosimetry and image
156  processing. http://iopscience.iop.org/0266-5611/26/8/085002 Equation 40
157  """
158 
159  if config.kernelBasisSet != "alard-lupton":
160  raise RuntimeError("Cannot generate %s basis within generateAlardLuptonBasisList" %
161  config.kernelBasisSet)
162 
163  kernelSize = config.kernelSize
164  fwhmScaling = config.kernelSizeFwhmScaling
165  basisNGauss = config.alardNGauss
166  basisSigmaGauss = config.alardSigGauss
167  basisGaussBeta = config.alardGaussBeta
168  basisMinSigma = config.alardMinSig
169  if basisDegGauss is None:
170  basisDegGauss = config.alardDegGauss
171 
172  if len(basisDegGauss) != basisNGauss:
173  raise ValueError("len(basisDegGauss) != basisNGauss : %d vs %d" % (len(basisDegGauss), basisNGauss))
174  if len(basisSigmaGauss) != basisNGauss:
175  raise ValueError("len(basisSigmaGauss) != basisNGauss : %d vs %d" %
176  (len(basisSigmaGauss), basisNGauss))
177  if (kernelSize % 2) != 1:
178  raise ValueError("Only odd-sized Alard-Lupton bases allowed")
179 
180  logger = Log.getLogger("ip.diffim.generateAlardLuptonBasisList")
181  if (targetFwhmPix is None) or (referenceFwhmPix is None) or (not config.scaleByFwhm):
182  logger.info("PSF sigmas are not available or scaling by fwhm disabled, "
183  "falling back to config values")
184  if metadata is not None:
185  metadata.add("ALBasisNGauss", basisNGauss)
186  metadata.add("ALBasisDegGauss", basisDegGauss)
187  metadata.add("ALBasisSigGauss", basisSigmaGauss)
188  metadata.add("ALKernelSize", kernelSize)
189  metadata.add("ALBasisMode", "config")
190 
191  return diffimLib.makeAlardLuptonBasisList(kernelSize//2, basisNGauss, basisSigmaGauss, basisDegGauss)
192 
193  targetSigma = targetFwhmPix / sigma2fwhm
194  referenceSigma = referenceFwhmPix / sigma2fwhm
195  logger.debug("Generating matching bases for sigma %.2f pix -> %.2f pix", targetSigma, referenceSigma)
196 
197  # Modify the size of Alard Lupton kernels based upon the images FWHM
198  #
199  # Note the operation is : template.x.kernel = science
200  #
201  # Assuming the template and science image Psfs are Gaussians with
202  # the Fwhm above, Fwhm_T **2 + Fwhm_K **2 = Fwhm_S **2
203  #
204  if targetSigma == referenceSigma:
205  # Leave defaults as-is
206  logger.info("Target and reference psf fwhms are equal, falling back to config values")
207  basisMode = "config"
208  elif referenceSigma > targetSigma:
209  # Normal convolution
210 
211  # First Gaussian has the sigma that comes from the convolution
212  # of two Gaussians : Sig_S**2 = Sig_T**2 + Sig_K**2
213  #
214  # If it's larger than basisMinSigma * basisGaussBeta, make it the
215  # second kernel. Else make it the smallest kernel. Unless
216  # only 1 kernel is asked for.
217  logger.info("Reference psf fwhm is the greater, normal convolution mode")
218  basisMode = "convolution"
219  kernelSigma = np.sqrt(referenceSigma**2 - targetSigma**2)
220  if kernelSigma < basisMinSigma:
221  kernelSigma = basisMinSigma
222 
223  basisSigmaGauss = []
224  if basisNGauss == 1:
225  basisSigmaGauss.append(kernelSigma)
226  nAppended = 1
227  else:
228  if (kernelSigma/basisGaussBeta) > basisMinSigma:
229  basisSigmaGauss.append(kernelSigma/basisGaussBeta)
230  basisSigmaGauss.append(kernelSigma)
231  nAppended = 2
232  else:
233  basisSigmaGauss.append(kernelSigma)
234  nAppended = 1
235 
236  # Any other Gaussians above basisNGauss=1 come from a scaling
237  # relationship: Sig_i+1 / Sig_i = basisGaussBeta
238  for i in range(nAppended, basisNGauss):
239  basisSigmaGauss.append(basisSigmaGauss[-1]*basisGaussBeta)
240 
241  kernelSize = int(fwhmScaling * basisSigmaGauss[-1])
242  kernelSize += 0 if kernelSize%2 else 1 # Make sure it's odd
243  kernelSize = min(config.kernelSizeMax, max(kernelSize, config.kernelSizeMin))
244 
245  else:
246  # Deconvolution; Define the progression of Gaussians using a
247  # method to derive a deconvolution sum-of-Gaussians from it's
248  # convolution counterpart. Only use 3 since the algorithm
249  # assumes 3 components.
250  #
251  # http://iopscience.iop.org/0266-5611/26/8/085002 Equation 40
252 
253  # Use specializations for deconvolution
254  logger.info("Target psf fwhm is the greater, deconvolution mode")
255  basisMode = "deconvolution"
256  basisNGauss = config.alardNGaussDeconv
257  basisMinSigma = config.alardMinSigDeconv
258 
259  kernelSigma = np.sqrt(targetSigma**2 - referenceSigma**2)
260  if kernelSigma < basisMinSigma:
261  kernelSigma = basisMinSigma
262 
263  basisSigmaGauss = []
264  if (kernelSigma/basisGaussBeta) > basisMinSigma:
265  basisSigmaGauss.append(kernelSigma/basisGaussBeta)
266  basisSigmaGauss.append(kernelSigma)
267  nAppended = 2
268  else:
269  basisSigmaGauss.append(kernelSigma)
270  nAppended = 1
271 
272  for i in range(nAppended, basisNGauss):
273  basisSigmaGauss.append(basisSigmaGauss[-1]*basisGaussBeta)
274 
275  kernelSize = int(fwhmScaling * basisSigmaGauss[-1])
276  kernelSize += 0 if kernelSize%2 else 1 # Make sure it's odd
277  kernelSize = min(config.kernelSizeMax, max(kernelSize, config.kernelSizeMin))
278 
279  # Now build a deconvolution set from these sigmas
280  sig0 = basisSigmaGauss[0]
281  sig1 = basisSigmaGauss[1]
282  sig2 = basisSigmaGauss[2]
283  basisSigmaGauss = []
284  for n in range(1, 3):
285  for j in range(n):
286  sigma2jn = (n - j)*sig1**2
287  sigma2jn += j * sig2**2
288  sigma2jn -= (n + 1)*sig0**2
289  sigmajn = np.sqrt(sigma2jn)
290  basisSigmaGauss.append(sigmajn)
291 
292  basisSigmaGauss.sort()
293  basisNGauss = len(basisSigmaGauss)
294  basisDegGauss = [config.alardDegGaussDeconv for x in basisSigmaGauss]
295 
296  if metadata is not None:
297  metadata.add("ALBasisNGauss", basisNGauss)
298  metadata.add("ALBasisDegGauss", basisDegGauss)
299  metadata.add("ALBasisSigGauss", basisSigmaGauss)
300  metadata.add("ALKernelSize", kernelSize)
301  metadata.add("ALBasisMode", basisMode)
302 
303  logger.debug("basisSigmaGauss: %s basisDegGauss: %s",
304  ','.join(['{:.1f}'.format(v) for v in basisSigmaGauss]),
305  ','.join(['{:d}'.format(v) for v in basisDegGauss]))
306 
307  return diffimLib.makeAlardLuptonBasisList(kernelSize//2, basisNGauss, basisSigmaGauss, basisDegGauss)
def generateAlardLuptonBasisList(config, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, metadata=None)
def makeKernelBasisList(config, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, metadata=None)