LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
makeKernel.py
Go to the documentation of this file.
1# This file is part of ip_diffim.
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/>.
21
22__all__ = ["MakeKernelConfig", "MakeKernelTask"]
23
24import numpy as np
25
27import lsst.afw.image
28import lsst.afw.math
29import lsst.afw.table
30import lsst.daf.base
31from lsst.meas.algorithms import SourceDetectionTask, SubtractBackgroundTask
32from lsst.meas.base import SingleFrameMeasurementTask
33import lsst.pex.config
34import lsst.pipe.base
35
36from .makeKernelBasisList import makeKernelBasisList
37from .psfMatch import PsfMatchConfig, PsfMatchTask, PsfMatchConfigAL, PsfMatchConfigDF
38
39from . import diffimLib
40from . import diffimTools
41from .utils import getPsfFwhm
42
43
46 doc="kernel type",
47 typemap=dict(
48 AL=PsfMatchConfigAL,
49 DF=PsfMatchConfigDF
50 ),
51 default="AL",
52 )
54 target=SourceDetectionTask,
55 doc="Initial detections used to feed stars to kernel fitting",
56 )
58 target=SingleFrameMeasurementTask,
59 doc="Initial measurements used to feed stars to kernel fitting",
60 )
61
62 def setDefaults(self):
63 # High sigma detections only
64 self.selectDetectionselectDetection.reEstimateBackground = False
65 self.selectDetectionselectDetection.thresholdValue = 10.0
66
67 # Minimal set of measurments for star selection
68 self.selectMeasurementselectMeasurement.algorithms.names.clear()
69 self.selectMeasurementselectMeasurement.algorithms.names = ('base_SdssCentroid', 'base_PsfFlux', 'base_PixelFlags',
70 'base_SdssShape', 'base_GaussianFlux', 'base_SkyCoord')
71 self.selectMeasurementselectMeasurement.slots.modelFlux = None
72 self.selectMeasurementselectMeasurement.slots.apFlux = None
73 self.selectMeasurementselectMeasurement.slots.calibFlux = None
74
75
77 """Construct a kernel for PSF matching two exposures.
78 """
79
80 ConfigClass = MakeKernelConfig
81 _DefaultName = "makeALKernel"
82
83 def __init__(self, *args, **kwargs):
84 PsfMatchTask.__init__(self, *args, **kwargs)
85 self.kConfigkConfigkConfig = self.config.kernel.active
86 # the background subtraction task uses a config from an unusual location,
87 # so cannot easily be constructed with makeSubtask
88 self.backgroundbackground = SubtractBackgroundTask(config=self.kConfigkConfigkConfig.afwBackgroundConfig, name="background",
89 parentTask=self)
92 self.makeSubtask("selectDetection", schema=self.selectSchemaselectSchema)
93 self.makeSubtask("selectMeasurement", schema=self.selectSchemaselectSchema, algMetadata=self.selectAlgMetadataselectAlgMetadata)
94
95 def run(self, template, science, kernelSources, preconvolved=False):
96 """Solve for the kernel and background model that best match two
97 Exposures evaluated at the given source locations.
98
99 Parameters
100 ----------
101 template : `lsst.afw.image.Exposure`
102 Exposure that will be convolved.
103 science : `lsst.afw.image.Exposure`
104 The exposure that will be matched.
105 kernelSources : `list` of `dict`
106 A list of dicts having a "source" and "footprint"
107 field for the Sources deemed to be appropriate for Psf
108 matching. Can be the output from ``selectKernelSources``.
109 preconvolved : `bool`, optional
110 Was the science image convolved with its own PSF?
111
112 Returns
113 -------
114 results : `lsst.pipe.base.Struct`
115
116 ``psfMatchingKernel`` : `lsst.afw.math.LinearCombinationKernel`
117 Spatially varying Psf-matching kernel.
118 ``backgroundModel`` : `lsst.afw.math.Function2D`
119 Spatially varying background-matching function.
120 """
121 kernelCellSet = self._buildCellSet_buildCellSet_buildCellSet(template.maskedImage, science.maskedImage, kernelSources)
122 templateFwhmPix = getPsfFwhm(template.psf)
123 scienceFwhmPix = getPsfFwhm(science.psf)
124 if preconvolved:
125 scienceFwhmPix *= np.sqrt(2)
126 basisList = self.makeKernelBasisListmakeKernelBasisList(templateFwhmPix, scienceFwhmPix,
127 metadata=self.metadata)
128 spatialSolution, psfMatchingKernel, backgroundModel = self._solve_solve(kernelCellSet, basisList)
129 return lsst.pipe.base.Struct(
130 psfMatchingKernel=psfMatchingKernel,
131 backgroundModel=backgroundModel,
132 )
133
134 def selectKernelSources(self, template, science, candidateList=None, preconvolved=False):
135 """Select sources from a list of candidates, and extract footprints.
136
137 Parameters
138 ----------
139 template : `lsst.afw.image.Exposure`
140 Exposure that will be convolved.
141 science : `lsst.afw.image.Exposure`
142 The exposure that will be matched.
143 candidateList : `list`, optional
144 List of Sources to examine. Elements must be of type afw.table.Source
145 or a type that wraps a Source and has a getSource() method, such as
147 preconvolved : `bool`, optional
148 Was the science image convolved with its own PSF?
149
150 Returns
151 -------
152 kernelSources : `list` of `dict`
153 A list of dicts having a "source" and "footprint"
154 field for the Sources deemed to be appropriate for Psf
155 matching.
156 """
157 templateFwhmPix = getPsfFwhm(template.psf)
158 scienceFwhmPix = getPsfFwhm(science.psf)
159 if preconvolved:
160 scienceFwhmPix *= np.sqrt(2)
161 kernelSize = self.makeKernelBasisListmakeKernelBasisList(templateFwhmPix, scienceFwhmPix)[0].getWidth()
162 kernelSources = self.makeCandidateListmakeCandidateList(template, science, kernelSize,
163 candidateList=candidateList,
164 preconvolved=preconvolved)
165 return kernelSources
166
167 def getSelectSources(self, exposure, sigma=None, doSmooth=True, idFactory=None):
168 """Get sources to use for Psf-matching.
169
170 This method runs detection and measurement on an exposure.
171 The returned set of sources will be used as candidates for
172 Psf-matching.
173
174 Parameters
175 ----------
176 exposure : `lsst.afw.image.Exposure`
177 Exposure on which to run detection/measurement
178 sigma : `float`, optional
179 PSF sigma, in pixels, used for smoothing the image for detection.
180 If `None`, the PSF width will be used.
181 doSmooth : `bool`
182 Whether or not to smooth the Exposure with Psf before detection
183 idFactory : `lsst.afw.table.IdFactory`
184 Factory for the generation of Source ids
185
186 Returns
187 -------
188 selectSources :
189 source catalog containing candidates for the Psf-matching
190 """
191 if idFactory:
192 table = lsst.afw.table.SourceTable.make(self.selectSchemaselectSchema, idFactory)
193 else:
194 table = lsst.afw.table.SourceTable.make(self.selectSchemaselectSchema)
195 mi = exposure.getMaskedImage()
196
197 imArr = mi.getImage().getArray()
198 maskArr = mi.getMask().getArray()
199 miArr = np.ma.masked_array(imArr, mask=maskArr)
200 try:
201 fitBg = self.backgroundbackground.fitBackground(mi)
202 bkgd = fitBg.getImageF(self.backgroundbackground.config.algorithm,
203 self.backgroundbackground.config.undersampleStyle)
204 except Exception:
205 self.log.warning("Failed to get background model. Falling back to median background estimation")
206 bkgd = np.ma.median(miArr)
207
208 # Take off background for detection
209 mi -= bkgd
210 try:
211 table.setMetadata(self.selectAlgMetadataselectAlgMetadata)
212 detRet = self.selectDetection.run(
213 table=table,
214 exposure=exposure,
215 sigma=sigma,
216 doSmooth=doSmooth
217 )
218 selectSources = detRet.sources
219 self.selectMeasurement.run(measCat=selectSources, exposure=exposure)
220 finally:
221 # Put back on the background in case it is needed down stream
222 mi += bkgd
223 del bkgd
224 return selectSources
225
226 def makeCandidateList(self, templateExposure, scienceExposure, kernelSize,
227 candidateList=None, preconvolved=False):
228 """Make a list of acceptable KernelCandidates.
229
230 Accept or generate a list of candidate sources for
231 Psf-matching, and examine the Mask planes in both of the
232 images for indications of bad pixels
233
234 Parameters
235 ----------
236 templateExposure : `lsst.afw.image.Exposure`
237 Exposure that will be convolved
238 scienceExposure : `lsst.afw.image.Exposure`
239 Exposure that will be matched-to
240 kernelSize : `float`
241 Dimensions of the Psf-matching Kernel, used to grow detection footprints
242 candidateList : `list`, optional
243 List of Sources to examine. Elements must be of type afw.table.Source
244 or a type that wraps a Source and has a getSource() method, such as
246 preconvolved : `bool`, optional
247 Was the science exposure already convolved with its PSF?
248
249 Returns
250 -------
251 candidateList : `list` of `dict`
252 A list of dicts having a "source" and "footprint"
253 field for the Sources deemed to be appropriate for Psf
254 matching.
255
256 Raises
257 ------
258 RuntimeError
259 If ``candidateList`` is empty or contains incompatible types.
260 """
261 if candidateList is None:
262 candidateList = self.getSelectSourcesgetSelectSources(scienceExposure, doSmooth=not preconvolved)
263
264 if len(candidateList) < 1:
265 raise RuntimeError("No candidates in candidateList")
266
267 listTypes = set(type(x) for x in candidateList)
268 if len(listTypes) > 1:
269 raise RuntimeError("Candidate list contains mixed types: %s" % [t for t in listTypes])
270
271 if not isinstance(candidateList[0], lsst.afw.table.SourceRecord):
272 try:
273 candidateList[0].getSource()
274 except Exception as e:
275 raise RuntimeError(f"Candidate List is of type: {type(candidateList[0])} "
276 "Can only make candidate list from list of afwTable.SourceRecords, "
277 f"measAlg.PsfCandidateF or other type with a getSource() method: {e}")
278 candidateList = [c.getSource() for c in candidateList]
279
280 candidateList = diffimTools.sourceToFootprintList(candidateList,
281 templateExposure, scienceExposure,
282 kernelSize,
283 self.kConfigkConfigkConfig.detectionConfig,
284 self.log)
285 if len(candidateList) == 0:
286 raise RuntimeError("Cannot find any objects suitable for KernelCandidacy")
287
288 return candidateList
289
290 def makeKernelBasisList(self, targetFwhmPix=None, referenceFwhmPix=None,
291 basisDegGauss=None, basisSigmaGauss=None, metadata=None):
292 """Wrapper to set log messages for
294
295 Parameters
296 ----------
297 targetFwhmPix : `float`, optional
298 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
299 Not used for delta function basis sets.
300 referenceFwhmPix : `float`, optional
301 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
302 Not used for delta function basis sets.
303 basisDegGauss : `list` of `int`, optional
304 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
305 Not used for delta function basis sets.
306 basisSigmaGauss : `list` of `int`, optional
307 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
308 Not used for delta function basis sets.
309 metadata : `lsst.daf.base.PropertySet`, optional
310 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
311 Not used for delta function basis sets.
312
313 Returns
314 -------
315 basisList: `list` of `lsst.afw.math.kernel.FixedKernel`
316 List of basis kernels.
317 """
318 basisList = makeKernelBasisList(self.kConfigkConfigkConfig,
319 targetFwhmPix=targetFwhmPix,
320 referenceFwhmPix=referenceFwhmPix,
321 basisDegGauss=basisDegGauss,
322 basisSigmaGauss=basisSigmaGauss,
323 metadata=metadata)
324 if targetFwhmPix == referenceFwhmPix:
325 self.log.info("Target and reference psf fwhms are equal, falling back to config values")
326 elif referenceFwhmPix > targetFwhmPix:
327 self.log.info("Reference psf fwhm is the greater, normal convolution mode")
328 else:
329 self.log.info("Target psf fwhm is the greater, deconvolution mode")
330
331 return basisList
332
333 def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList):
334 """Build a SpatialCellSet for use with the solve method.
335
336 Parameters
337 ----------
338 templateMaskedImage : `lsst.afw.image.MaskedImage`
339 MaskedImage to PSF-matched to scienceMaskedImage
340 scienceMaskedImage : `lsst.afw.image.MaskedImage`
341 Reference MaskedImage
342 candidateList : `list`
343 A list of footprints/maskedImages for kernel candidates;
344
345 - Currently supported: list of Footprints or measAlg.PsfCandidateF
346
347 Returns
348 -------
349 kernelCellSet : `lsst.afw.math.SpatialCellSet`
350 a SpatialCellSet for use with self._solve_solve
351 """
352 if not candidateList:
353 raise RuntimeError("Candidate list must be populated by makeCandidateList")
354
355 sizeCellX, sizeCellY = self._adaptCellSize_adaptCellSize(candidateList)
356
357 # Object to store the KernelCandidates for spatial modeling
358 kernelCellSet = lsst.afw.math.SpatialCellSet(templateMaskedImage.getBBox(),
359 sizeCellX, sizeCellY)
360
361 ps = lsst.pex.config.makePropertySet(self.kConfigkConfigkConfig)
362 # Place candidates within the spatial grid
363 for cand in candidateList:
364 if isinstance(cand, lsst.afw.detection.Footprint):
365 bbox = cand.getBBox()
366 else:
367 bbox = cand['footprint'].getBBox()
368 tmi = lsst.afw.image.MaskedImageF(templateMaskedImage, bbox)
369 smi = lsst.afw.image.MaskedImageF(scienceMaskedImage, bbox)
370
371 if not isinstance(cand, lsst.afw.detection.Footprint):
372 if 'source' in cand:
373 cand = cand['source']
374 xPos = cand.getCentroid()[0]
375 yPos = cand.getCentroid()[1]
376 cand = diffimLib.makeKernelCandidate(xPos, yPos, tmi, smi, ps)
377
378 self.log.debug("Candidate %d at %f, %f", cand.getId(), cand.getXCenter(), cand.getYCenter())
379 kernelCellSet.insertCandidate(cand)
380
381 return kernelCellSet
382
383 def _adaptCellSize(self, candidateList):
384 """NOT IMPLEMENTED YET.
385
386 Parameters
387 ----------
388 candidateList : `list`
389 A list of footprints/maskedImages for kernel candidates;
390
391 Returns
392 -------
393 sizeCellX, sizeCellY : `int`
394 New dimensions to use for the kernel.
395 """
396 return self.kConfigkConfigkConfig.sizeCellX, self.kConfigkConfigkConfig.sizeCellY
table::Key< int > type
Definition: Detector.cc:163
Class to describe the properties of a detected object from an image.
Definition: Footprint.h:63
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Definition: Exposure.h:72
A class to manipulate images, masks, and variance as a single object.
Definition: MaskedImage.h:73
A kernel that is a linear combination of fixed basis kernels.
Definition: Kernel.h:704
A collection of SpatialCells covering an entire image.
Definition: SpatialCell.h:383
A polymorphic functor base class for generating record IDs for a table.
Definition: IdFactory.h:21
Record class that contains measurements made on a single exposure.
Definition: Source.h:78
static std::shared_ptr< SourceTable > make(Schema const &schema, std::shared_ptr< IdFactory > const &idFactory)
Construct a new table.
Definition: Source.cc:382
static Schema makeMinimalSchema()
Return a minimal schema for Source tables and records.
Definition: Source.h:258
Class for storing ordered metadata with comments.
Definition: PropertyList.h:68
Class for storing generic metadata.
Definition: PropertySet.h:66
def selectKernelSources(self, template, science, candidateList=None, preconvolved=False)
Definition: makeKernel.py:134
def run(self, template, science, kernelSources, preconvolved=False)
Definition: makeKernel.py:95
def makeKernelBasisList(self, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, basisSigmaGauss=None, metadata=None)
Definition: makeKernel.py:291
def getSelectSources(self, exposure, sigma=None, doSmooth=True, idFactory=None)
Definition: makeKernel.py:167
def makeCandidateList(self, templateExposure, scienceExposure, kernelSize, candidateList=None, preconvolved=False)
Definition: makeKernel.py:227
def _adaptCellSize(self, candidateList)
Definition: makeKernel.py:383
def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList)
Definition: makeKernel.py:333
def __init__(self, *args, **kwargs)
Definition: makeKernel.py:83
def _solve(self, kernelCellSet, basisList, returnOnExcept=False)
Definition: psfMatch.py:881
daf::base::PropertySet * set
Definition: fits.cc:912
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
def getPsfFwhm(psf)
Definition: utils.py:1083
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations.