22__all__ = [
"MakeKernelConfig",
"MakeKernelTask"]
32from lsst.meas.algorithms
import SourceDetectionTask, SubtractBackgroundTask
38from .makeKernelBasisList
import makeKernelBasisList
39from .psfMatch
import PsfMatchConfig, PsfMatchTask, PsfMatchConfigAL, PsfMatchConfigDF
41from .
import diffimLib
42from .utils
import evaluateMeanPsfFwhm, getPsfFwhm
46 kernel = lsst.pex.config.ConfigChoiceField(
54 selectDetection = lsst.pex.config.ConfigurableField(
55 target=SourceDetectionTask,
56 doc=
"Initial detections used to feed stars to kernel fitting",
58 selectMeasurement = lsst.pex.config.ConfigurableField(
59 target=SingleFrameMeasurementTask,
60 doc=
"Initial measurements used to feed stars to kernel fitting",
62 fwhmExposureGrid = lsst.pex.config.Field(
63 doc=
"Grid size to compute the average PSF FWHM in an exposure",
67 fwhmExposureBuffer = lsst.pex.config.Field(
68 doc=
"Fractional buffer margin to be left out of all sides of the image during construction"
69 "of grid to compute average PSF FWHM in an exposure",
82 self.
selectMeasurement.algorithms.names = (
'base_SdssCentroid',
'base_PsfFlux',
'base_PixelFlags',
83 'base_SdssShape',
'base_GaussianFlux',
'base_SkyCoord',
84 'base_ClassificationSizeExtendedness')
91 """Construct a kernel for PSF matching two exposures.
94 ConfigClass = MakeKernelConfig
95 _DefaultName =
"makeALKernel"
101 self.
background = SubtractBackgroundTask(config=self.
kConfig.afwBackgroundConfig, name=
"background",
106 self.makeSubtask(
"selectDetection", schema=self.
selectSchema)
109 def run(self, template, science, kernelSources, preconvolved=False,
110 templateFwhmPix=None, scienceFwhmPix=None):
111 """Solve for the kernel and background model that best match two
112 Exposures evaluated at the given source locations.
116 template : `lsst.afw.image.Exposure`
117 Exposure that will be convolved.
118 science : `lsst.afw.image.Exposure`
119 The exposure that will be matched.
120 kernelSources : `lsst.afw.table.SourceCatalog`
121 Kernel candidate sources with appropriately sized footprints.
122 Typically the output of `MakeKernelTask.selectKernelSources`.
123 preconvolved : `bool`, optional
124 Was the science image convolved with its own PSF?
128 results : `lsst.pipe.base.Struct`
130 ``psfMatchingKernel`` : `lsst.afw.math.LinearCombinationKernel`
131 Spatially varying Psf-matching kernel.
132 ``backgroundModel`` : `lsst.afw.math.Function2D`
133 Spatially varying background-matching function.
135 kernelCellSet = self.
_buildCellSet(template.maskedImage, science.maskedImage, kernelSources)
136 if (scienceFwhmPix
is None)
or (templateFwhmPix
is None):
144 templateFwhmPix = getPsfFwhm(template.psf)
145 scienceFwhmPix = getPsfFwhm(science.psf)
146 except (InvalidParameterError, RangeError):
147 self.log.debug(
"Unable to evaluate PSF at the average position. "
148 "Evaluting PSF on a grid of points."
150 templateFwhmPix = evaluateMeanPsfFwhm(template,
151 fwhmExposureBuffer=self.config.fwhmExposureBuffer,
152 fwhmExposureGrid=self.config.fwhmExposureGrid
154 scienceFwhmPix = evaluateMeanPsfFwhm(science,
155 fwhmExposureBuffer=self.config.fwhmExposureBuffer,
156 fwhmExposureGrid=self.config.fwhmExposureGrid
160 scienceFwhmPix *= np.sqrt(2)
162 metadata=self.metadata)
163 spatialSolution, psfMatchingKernel, backgroundModel = self.
_solve(kernelCellSet, basisList)
164 return lsst.pipe.base.Struct(
165 psfMatchingKernel=psfMatchingKernel,
166 backgroundModel=backgroundModel,
170 templateFwhmPix=None, scienceFwhmPix=None):
171 """Select sources from a list of candidates, and extract footprints.
175 template : `lsst.afw.image.Exposure`
176 Exposure that will be convolved.
177 science : `lsst.afw.image.Exposure`
178 The exposure that will be matched.
179 candidateList : `lsst.afw.table.SourceCatalog`
180 Sources to check as possible kernel candidates.
181 preconvolved : `bool`, optional
182 Was the science image convolved with its own PSF?
186 kernelSources : `lsst.afw.table.SourceCatalog`
187 Kernel candidates with appropriate sized footprints.
189 if (scienceFwhmPix
is None)
or (templateFwhmPix
is None):
197 templateFwhmPix = getPsfFwhm(template.psf)
198 scienceFwhmPix = getPsfFwhm(science.psf)
199 except (InvalidParameterError, RangeError):
200 self.log.debug(
"Unable to evaluate PSF at the average position. "
201 "Evaluting PSF on a grid of points."
203 templateFwhmPix = evaluateMeanPsfFwhm(template,
204 fwhmExposureBuffer=self.config.fwhmExposureBuffer,
205 fwhmExposureGrid=self.config.fwhmExposureGrid
207 scienceFwhmPix = evaluateMeanPsfFwhm(science,
208 fwhmExposureBuffer=self.config.fwhmExposureBuffer,
209 fwhmExposureGrid=self.config.fwhmExposureGrid
212 scienceFwhmPix *= np.sqrt(2)
215 candidateList=candidateList,
216 preconvolved=preconvolved)
220 """Get sources to use for Psf-matching.
222 This method runs detection and measurement on an exposure.
223 The returned set of sources will be used as candidates for
228 exposure : `lsst.afw.image.Exposure`
229 Exposure on which to run detection/measurement
230 sigma : `float`, optional
231 PSF sigma, in pixels, used for smoothing the image for detection.
232 If `None`, the PSF width will be used.
234 Whether or not to smooth the Exposure with Psf before detection
235 idFactory : `lsst.afw.table.IdFactory`
236 Factory for the generation of Source ids
241 source catalog containing candidates for the Psf-matching
247 mi = exposure.getMaskedImage()
249 imArr = mi.image.array
250 maskArr = mi.mask.array
251 miArr = np.ma.masked_array(imArr, mask=maskArr)
254 bkgd = fitBg.getImageF(self.
background.config.algorithm,
257 self.log.warning(
"Failed to get background model. Falling back to median background estimation")
258 bkgd = np.ma.median(miArr)
264 detRet = self.selectDetection.
run(
270 selectSources = detRet.sources
271 self.selectMeasurement.
run(measCat=selectSources, exposure=exposure)
277 self.log.info(
"Selected %d sources via detection measurement.", len(selectSources))
281 candidateList, preconvolved=False, sigma=None):
282 """Make a list of acceptable KernelCandidates.
284 Generate a list of candidate sources for Psf-matching, remove sources
285 with bad pixel masks set or that extend off the image.
289 convolved : `lsst.afw.image.Exposure`
290 Exposure that will be convolved. This is typically the template
291 image, and may have a large bbox than the reference exposure.
292 reference : `lsst.afw.image.Exposure`
293 Exposure that will be matched-to. This is typically the science
296 Dimensions of the Psf-matching Kernel, used to set detection
298 candidateList : `lsst.afw.table.SourceCatalog`
299 List of Sources to examine for kernel candidacy.
300 preconvolved : `bool`, optional
301 Was the science exposure already convolved with its PSF?
305 candidates : `lsst.afw.table.SourceCatalog`
306 Candidates with footprints extended to a ``kernelSize`` box.
311 If ``candidateList`` is empty after sub-selection.
313 if candidateList
is None:
314 candidateList = self.
getSelectSources(reference, doSmooth=
not preconvolved, sigma=sigma)
315 if len(candidateList) < 1:
316 raise RuntimeError(
"No kernel candidates after detection and measurement.")
318 bitmask = reference.mask.getPlaneBitMask(self.config.badMaskPlanes)
319 good = np.ones(len(candidateList), dtype=bool)
321 for i, candidate
in enumerate(candidateList):
323 peak = candidate.getFootprint().getPeaks()[0]
324 size = 2*kernelSize + 1
328 boxFootprint.addPeak(peak.getFx(), peak.getFy(), peak.getPeakValue())
329 candidate.setFootprint(boxFootprint)
332 if not reference.getBBox().contains(bbox)
or not convolved.getBBox().contains(bbox):
336 if (reference.subset(bbox).mask.array & bitmask).any():
341 if (convolved.subset(bbox).mask.array & bitmask).any():
344 candidates = candidateList[good].copy(deep=
True)
346 self.log.info(
"Selected %d / %d sources as kernel candidates.", good.sum(), len(candidateList))
348 if len(candidates) < 1:
349 raise RuntimeError(
"No good kernel candidates available.")
353 def makeKernelBasisList(self, targetFwhmPix=None, referenceFwhmPix=None,
354 basisDegGauss=None, basisSigmaGauss=None, metadata=None):
355 """Wrapper to set log messages for
356 `lsst.ip.diffim.makeKernelBasisList`.
360 targetFwhmPix : `float`, optional
361 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
362 Not used for delta function basis sets.
363 referenceFwhmPix : `float`, optional
364 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
365 Not used for delta function basis sets.
366 basisDegGauss : `list` of `int`, optional
367 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
368 Not used for delta function basis sets.
369 basisSigmaGauss : `list` of `int`, optional
370 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
371 Not used for delta function basis sets.
372 metadata : `lsst.daf.base.PropertySet`, optional
373 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
374 Not used for delta function basis sets.
378 basisList: `list` of `lsst.afw.math.kernel.FixedKernel`
379 List of basis kernels.
381 basisList = makeKernelBasisList(self.
kConfig,
382 targetFwhmPix=targetFwhmPix,
383 referenceFwhmPix=referenceFwhmPix,
384 basisDegGauss=basisDegGauss,
385 basisSigmaGauss=basisSigmaGauss,
387 if targetFwhmPix == referenceFwhmPix:
388 self.log.info(
"Target and reference psf fwhms are equal, falling back to config values")
389 elif referenceFwhmPix > targetFwhmPix:
390 self.log.info(
"Reference psf fwhm is the greater, normal convolution mode")
392 self.log.info(
"Target psf fwhm is the greater, deconvolution mode")
397 """Build a SpatialCellSet for use with the solve method.
401 convolved : `lsst.afw.image.MaskedImage`
402 MaskedImage to PSF-matched to reference.
403 reference : `lsst.afw.image.MaskedImage`
404 Reference MaskedImage.
405 candidateList : `lsst.afw.table.SourceCatalog`
406 Kernel candidate sources with footprints.
410 kernelCellSet : `lsst.afw.math.SpatialCellSet`
411 A SpatialCellSet for use with self._solve.
415 imageBBox = convolved.getBBox()
416 imageBBox.clip(reference.getBBox())
420 candidateConfig = lsst.pex.config.makePropertySet(self.
kConfig)
422 for candidate
in candidateList:
423 bbox = candidate.getFootprint().getBBox()
424 templateCutout = lsst.afw.image.MaskedImageF(convolved, bbox)
425 scienceCutout = lsst.afw.image.MaskedImageF(reference, bbox)
427 kernelCandidate = diffimLib.makeKernelCandidate(candidate,
432 self.log.debug(
"Candidate %d at %.2f, %.2f rating=%f",
433 kernelCandidate.getId(),
434 kernelCandidate.getXCenter(),
435 kernelCandidate.getYCenter(),
436 kernelCandidate.getCandidateRating())
437 kernelCellSet.insertCandidate(kernelCandidate)
442 """NOT IMPLEMENTED YET.
446 candidateList : `list`
447 A list of footprints/maskedImages for kernel candidates;
451 sizeCellX, sizeCellY : `int`
452 New dimensions to use for the kernel.
static std::shared_ptr< SourceTable > make(Schema const &schema, std::shared_ptr< IdFactory > const &idFactory)
static Schema makeMinimalSchema()
static Box2I makeCenteredBox(Point2D const ¢er, Extent const &size)
makeCandidateList(self, convolved, reference, kernelSize, candidateList, preconvolved=False, sigma=None)
run(self, template, science, kernelSources, preconvolved=False, templateFwhmPix=None, scienceFwhmPix=None)
__init__(self, *args, **kwargs)
_buildCellSet(self, convolved, reference, candidateList)
_adaptCellSize(self, candidateList)
makeKernelBasisList(self, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, basisSigmaGauss=None, metadata=None)
selectKernelSources(self, template, science, candidateList=None, preconvolved=False, templateFwhmPix=None, scienceFwhmPix=None)
getSelectSources(self, exposure, sigma=None, doSmooth=True, idFactory=None)
_solve(self, kernelCellSet, basisList)