Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 

25from . import diffimLib 

26from lsst.log import Log 

27import numpy as np 

28 

29sigma2fwhm = 2. * np.sqrt(2. * np.log(2.)) 

30 

31 

32def 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 

82def 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 Notes 

109 ----- 

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. 

113 

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. 

126 

127 Metadata fields 

128 --------------- 

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. 

135 ALKernelSize : `int` 

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. 

140 

141 References 

142 ---------- 

143 

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 

147 

148 Raises 

149 ------ 

150 RuntimeError 

151 - if ``config.kernelBasisSet`` is not equal to "alard-lupton" 

152 ValueError 

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 

158 """ 

159 

160 if config.kernelBasisSet != "alard-lupton": 

161 raise RuntimeError("Cannot generate %s basis within generateAlardLuptonBasisList" % 

162 config.kernelBasisSet) 

163 

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 

172 

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") 

180 

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") 

191 

192 return diffimLib.makeAlardLuptonBasisList(kernelSize//2, basisNGauss, basisSigmaGauss, basisDegGauss) 

193 

194 targetSigma = targetFwhmPix / sigma2fwhm 

195 referenceSigma = referenceFwhmPix / sigma2fwhm 

196 logger.debug("Generating matching bases for sigma %.2f pix -> %.2f pix", targetSigma, referenceSigma) 

197 

198 # Modify the size of Alard Lupton kernels based upon the images FWHM 

199 # 

200 # Note the operation is : template.x.kernel = science 

201 # 

202 # Assuming the template and science image Psfs are Gaussians with 

203 # the Fwhm above, Fwhm_T **2 + Fwhm_K **2 = Fwhm_S **2 

204 # 

205 if targetSigma == referenceSigma: 

206 # Leave defaults as-is 

207 logger.info("Target and reference psf fwhms are equal, falling back to config values") 

208 basisMode = "config" 

209 elif referenceSigma > targetSigma: 

210 # Normal convolution 

211 

212 # First Gaussian has the sigma that comes from the convolution 

213 # of two Gaussians : Sig_S**2 = Sig_T**2 + Sig_K**2 

214 # 

215 # If it's larger than basisMinSigma * basisGaussBeta, make it the 

216 # second kernel. Else make it the smallest kernel. Unless 

217 # only 1 kernel is asked for. 

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 

223 

224 basisSigmaGauss = [] 

225 if basisNGauss == 1: 

226 basisSigmaGauss.append(kernelSigma) 

227 nAppended = 1 

228 else: 

229 if (kernelSigma/basisGaussBeta) > basisMinSigma: 

230 basisSigmaGauss.append(kernelSigma/basisGaussBeta) 

231 basisSigmaGauss.append(kernelSigma) 

232 nAppended = 2 

233 else: 

234 basisSigmaGauss.append(kernelSigma) 

235 nAppended = 1 

236 

237 # Any other Gaussians above basisNGauss=1 come from a scaling 

238 # relationship: Sig_i+1 / Sig_i = basisGaussBeta 

239 for i in range(nAppended, basisNGauss): 

240 basisSigmaGauss.append(basisSigmaGauss[-1]*basisGaussBeta) 

241 

242 kernelSize = int(fwhmScaling * basisSigmaGauss[-1]) 

243 kernelSize += 0 if kernelSize%2 else 1 # Make sure it's odd 

244 kernelSize = min(config.kernelSizeMax, max(kernelSize, config.kernelSizeMin)) 

245 

246 else: 

247 # Deconvolution; Define the progression of Gaussians using a 

248 # method to derive a deconvolution sum-of-Gaussians from it's 

249 # convolution counterpart. Only use 3 since the algorithm 

250 # assumes 3 components. 

251 # 

252 # http://iopscience.iop.org/0266-5611/26/8/085002 Equation 40 

253 

254 # Use specializations for deconvolution 

255 logger.info("Target psf fwhm is the greater, deconvolution mode") 

256 basisMode = "deconvolution" 

257 basisNGauss = config.alardNGaussDeconv 

258 basisMinSigma = config.alardMinSigDeconv 

259 

260 kernelSigma = np.sqrt(targetSigma**2 - referenceSigma**2) 

261 if kernelSigma < basisMinSigma: 

262 kernelSigma = basisMinSigma 

263 

264 basisSigmaGauss = [] 

265 if (kernelSigma/basisGaussBeta) > basisMinSigma: 

266 basisSigmaGauss.append(kernelSigma/basisGaussBeta) 

267 basisSigmaGauss.append(kernelSigma) 

268 nAppended = 2 

269 else: 

270 basisSigmaGauss.append(kernelSigma) 

271 nAppended = 1 

272 

273 for i in range(nAppended, basisNGauss): 

274 basisSigmaGauss.append(basisSigmaGauss[-1]*basisGaussBeta) 

275 

276 kernelSize = int(fwhmScaling * basisSigmaGauss[-1]) 

277 kernelSize += 0 if kernelSize%2 else 1 # Make sure it's odd 

278 kernelSize = min(config.kernelSizeMax, max(kernelSize, config.kernelSizeMin)) 

279 

280 # Now build a deconvolution set from these sigmas 

281 sig0 = basisSigmaGauss[0] 

282 sig1 = basisSigmaGauss[1] 

283 sig2 = basisSigmaGauss[2] 

284 basisSigmaGauss = [] 

285 for n in range(1, 3): 

286 for j in range(n): 

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) 

292 

293 basisSigmaGauss.sort() 

294 basisNGauss = len(basisSigmaGauss) 

295 basisDegGauss = [config.alardDegGaussDeconv for x in basisSigmaGauss] 

296 

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) 

303 

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])) 

307 

308 return diffimLib.makeAlardLuptonBasisList(kernelSize//2, basisNGauss, basisSigmaGauss, basisDegGauss)