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.
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.
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.
110 The polynomial functions (``f``) are always evaluated in the -1.0, +1.0 range in both x, y directions,
111 edge to edge, with ``f(0,0)`` evaluated at the kernel center pixel, ``f(-1.0,-1.0)`` at the kernel
112 ``(0,0)`` pixel. They are not scaled by the sigmas of the Gaussians.
114 Base Gaussian widths (sigmas in pixels) of the kernels are determined as:
115 - If not all fwhm parameters are provided or ``config.scaleByFwhm==False``
116 then ``config.alardNGauss`` and ``config.alardSigGauss`` are used.
117 - If ``targetFwhmPix<referenceFwhmPix`` (normal convolution):
118 First sigma ``Sig_K`` is determined to satisfy: ``Sig_reference**2 = Sig_target**2 + Sig_K**2``.
119 If it's larger than ``config.alardMinSig * config.alardGaussBeta``, make it the
120 second kernel. Else make it the smallest kernel, unless only 1 kernel is asked for.
121 - If ``referenceFwhmPix < targetFwhmPix`` (deconvolution):
122 Define the progression of Gaussians using a
123 method to derive a deconvolution sum-of-Gaussians from it's
124 convolution counterpart. [1]_ Only use 3 since the algorithm
125 assumes 3 components.
129 ALBasisNGauss : `int`
130 The number of base Gaussians in the AL basis functions.
131 ALBasisDegGauss : `list` of `int`
132 Polynomial order of spatial modification of the base Gaussian functions.
133 ALBasisSigGauss : `list` of `float`
134 Sigmas in pixels of the base Gaussians.
136 Kernel stamp size is (ALKernelSize pix, ALKernelSize pix).
137 ALBasisMode : `str`, either of ``config``, ``convolution``, ``deconvolution``
138 Indicates whether the config file values, the convolution or deconvolution algorithm
139 was used to determine the base Gaussian sigmas and the kernel stamp size.
144 .. [1] Ulmer, W.: Inverse problem of linear combinations of Gaussian convolution kernels
145 (deconvolution) and some applications to proton/photon dosimetry and image
146 processing. http://iopscience.iop.org/0266-5611/26/8/085002 Equation 40
151 - if ``config.kernelBasisSet`` is not equal to "alard-lupton"
153 - if ``config.kernelSize`` is even
154 - if the number of Gaussians and the number of given
155 sigma values are not equal or
156 - if the number of Gaussians and the number of given
157 polynomial degree values are not equal
160 if config.kernelBasisSet !=
"alard-lupton":
161 raise RuntimeError(
"Cannot generate %s basis within generateAlardLuptonBasisList" %
162 config.kernelBasisSet)
164 kernelSize = config.kernelSize
165 fwhmScaling = config.kernelSizeFwhmScaling
166 basisNGauss = config.alardNGauss
167 basisSigmaGauss = config.alardSigGauss
168 basisGaussBeta = config.alardGaussBeta
169 basisMinSigma = config.alardMinSig
170 if basisDegGauss
is None:
171 basisDegGauss = config.alardDegGauss
173 if len(basisDegGauss) != basisNGauss:
174 raise ValueError(
"len(basisDegGauss) != basisNGauss : %d vs %d" % (len(basisDegGauss), basisNGauss))
175 if len(basisSigmaGauss) != basisNGauss:
176 raise ValueError(
"len(basisSigmaGauss) != basisNGauss : %d vs %d" %
177 (len(basisSigmaGauss), basisNGauss))
178 if (kernelSize % 2) != 1:
179 raise ValueError(
"Only odd-sized Alard-Lupton bases allowed")
181 logger = Log.getLogger(
"ip.diffim.generateAlardLuptonBasisList")
182 if (targetFwhmPix
is None)
or (referenceFwhmPix
is None)
or (
not config.scaleByFwhm):
183 logger.info(
"PSF sigmas are not available or scaling by fwhm disabled, "
184 "falling back to config values")
185 if metadata
is not None:
186 metadata.add(
"ALBasisNGauss", basisNGauss)
187 metadata.add(
"ALBasisDegGauss", basisDegGauss)
188 metadata.add(
"ALBasisSigGauss", basisSigmaGauss)
189 metadata.add(
"ALKernelSize", kernelSize)
190 metadata.add(
"ALBasisMode",
"config")
192 return diffimLib.makeAlardLuptonBasisList(kernelSize//2, basisNGauss, basisSigmaGauss, basisDegGauss)
194 targetSigma = targetFwhmPix / sigma2fwhm
195 referenceSigma = referenceFwhmPix / sigma2fwhm
196 logger.debug(
"Generating matching bases for sigma %.2f pix -> %.2f pix", targetSigma, referenceSigma)
205 if targetSigma == referenceSigma:
207 logger.info(
"Target and reference psf fwhms are equal, falling back to config values")
209 elif referenceSigma > targetSigma:
218 logger.info(
"Reference psf fwhm is the greater, normal convolution mode")
219 basisMode =
"convolution"
220 kernelSigma = np.sqrt(referenceSigma**2 - targetSigma**2)
221 if kernelSigma < basisMinSigma:
222 kernelSigma = basisMinSigma
226 basisSigmaGauss.append(kernelSigma)
229 if (kernelSigma/basisGaussBeta) > basisMinSigma:
230 basisSigmaGauss.append(kernelSigma/basisGaussBeta)
231 basisSigmaGauss.append(kernelSigma)
234 basisSigmaGauss.append(kernelSigma)
239 for i
in range(nAppended, basisNGauss):
240 basisSigmaGauss.append(basisSigmaGauss[-1]*basisGaussBeta)
242 kernelSize = int(fwhmScaling * basisSigmaGauss[-1])
243 kernelSize += 0
if kernelSize%2
else 1
244 kernelSize =
min(config.kernelSizeMax,
max(kernelSize, config.kernelSizeMin))
255 logger.info(
"Target psf fwhm is the greater, deconvolution mode")
256 basisMode =
"deconvolution"
257 basisNGauss = config.alardNGaussDeconv
258 basisMinSigma = config.alardMinSigDeconv
260 kernelSigma = np.sqrt(targetSigma**2 - referenceSigma**2)
261 if kernelSigma < basisMinSigma:
262 kernelSigma = basisMinSigma
265 if (kernelSigma/basisGaussBeta) > basisMinSigma:
266 basisSigmaGauss.append(kernelSigma/basisGaussBeta)
267 basisSigmaGauss.append(kernelSigma)
270 basisSigmaGauss.append(kernelSigma)
273 for i
in range(nAppended, basisNGauss):
274 basisSigmaGauss.append(basisSigmaGauss[-1]*basisGaussBeta)
276 kernelSize = int(fwhmScaling * basisSigmaGauss[-1])
277 kernelSize += 0
if kernelSize%2
else 1
278 kernelSize =
min(config.kernelSizeMax,
max(kernelSize, config.kernelSizeMin))
281 sig0 = basisSigmaGauss[0]
282 sig1 = basisSigmaGauss[1]
283 sig2 = basisSigmaGauss[2]
285 for n
in range(1, 3):
287 sigma2jn = (n - j)*sig1**2
288 sigma2jn += j * sig2**2
289 sigma2jn -= (n + 1)*sig0**2
290 sigmajn = np.sqrt(sigma2jn)
291 basisSigmaGauss.append(sigmajn)
293 basisSigmaGauss.sort()
294 basisNGauss = len(basisSigmaGauss)
295 basisDegGauss = [config.alardDegGaussDeconv
for x
in basisSigmaGauss]
297 if metadata
is not None:
298 metadata.add(
"ALBasisNGauss", basisNGauss)
299 metadata.add(
"ALBasisDegGauss", basisDegGauss)
300 metadata.add(
"ALBasisSigGauss", basisSigmaGauss)
301 metadata.add(
"ALKernelSize", kernelSize)
302 metadata.add(
"ALBasisMode", basisMode)
304 logger.debug(
"basisSigmaGauss: %s basisDegGauss: %s",
305 ','.join([
'{:.1f}'.
format(v)
for v
in basisSigmaGauss]),
306 ','.join([
'{:d}'.
format(v)
for v
in basisDegGauss]))
308 return diffimLib.makeAlardLuptonBasisList(kernelSize//2, basisNGauss, basisSigmaGauss, basisDegGauss)