35from .makeKernelBasisList
import makeKernelBasisList
36from .psfMatch
import PsfMatchTask, PsfMatchConfigDF, PsfMatchConfigAL
37from .
import utils
as diffimUtils
38from .
import diffimLib
39from .
import diffimTools
41from lsst.utils.timer
import timeMethod
43__all__ = [
"ImagePsfMatchConfig",
"ImagePsfMatchTask",
"subtractAlgorithmRegistry"]
45sigma2fwhm = 2.*np.sqrt(2.*np.log(2.))
49 """Configuration for image-to-image Psf matching.
51 kernel = pexConfig.ConfigChoiceField(
59 selectDetection = pexConfig.ConfigurableField(
60 target=SourceDetectionTask,
61 doc=
"Initial detections used to feed stars to kernel fitting",
63 selectMeasurement = pexConfig.ConfigurableField(
64 target=SingleFrameMeasurementTask,
65 doc=
"Initial measurements used to feed stars to kernel fitting",
75 self.
selectMeasurement.algorithms.names = (
'base_SdssCentroid',
'base_PsfFlux',
'base_PixelFlags',
76 'base_SdssShape',
'base_GaussianFlux',
'base_SkyCoord')
83 """Psf-match two MaskedImages or Exposures using the sources in the images.
88 Arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__
90 Keyword arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__
94 Upon initialization, the kernel configuration is defined by self.config.kernel.active.
95 The task creates an
lsst.afw.math.Warper from the subConfig self.config.kernel.active.warpingConfig.
96 A schema
for the selection
and measurement of candidate lsst.ip.diffim.KernelCandidates
is
97 defined,
and used to initize subTasks selectDetection (
for candidate detection)
and selectMeasurement
98 (
for candidate measurement).
102 Build a Psf-matching kernel using two input images, either
as MaskedImages (
in which case they need
103 to be astrometrically aligned)
or Exposures (
in which case astrometric alignment will happen by
104 default but may be turned off). This requires a list of input Sources which may be provided
105 by the calling Task;
if not, the Task will perform a coarse source detection
106 and selection
for this purpose. Sources are vetted
for signal-to-noise
and masked pixels
107 (
in both the template
and science image),
and substamps around each acceptable
108 source are extracted
and used to create an instance of KernelCandidate.
111 the order that they are called: BuildSingleKernelVisitor, KernelSumVisitor, BuildSpatialKernelVisitor,
112 and AssessSpatialKernelVisitor.
114 Sigma clipping of KernelCandidates
is performed
as follows:
116 - BuildSingleKernelVisitor, using the substamp diffim residuals
from the per-source kernel fit,
117 if PsfMatchConfig.singleKernelClipping
is True
118 - KernelSumVisitor, using the mean
and standard deviation of the kernel sum
from all candidates,
119 if PsfMatchConfig.kernelSumClipping
is True
120 - AssessSpatialKernelVisitor, using the substamp diffim ressiduals
from the spatial kernel fit,
121 if PsfMatchConfig.spatialKernelClipping
is True
123 The actual solving
for the kernel (
and differential background model) happens
in
124 lsst.ip.diffim.PsfMatchTask._solve. This involves a loop over the SpatialCellSet that first builds the
125 per-candidate matching kernel
for the requested number of KernelCandidates per cell
126 (PsfMatchConfig.nStarPerCell). The quality of this initial per-candidate difference image
is examined,
127 using moments of the pixel residuals
in the difference image normalized by the square root of the variance
128 (i.e. sigma); ideally this should follow a normal (0, 1) distribution,
129 but the rejection thresholds are set
130 by the config (PsfMatchConfig.candidateResidualMeanMax
and PsfMatchConfig.candidateResidualStdMax).
131 All candidates that
pass this initial build are then examined en masse to find the
132 mean/stdev of the kernel sums across all candidates.
133 Objects that are significantly above
or below the mean,
134 typically due to variability
or sources that are saturated
in one image but
not the other,
135 are also rejected.This threshold
is defined by PsfMatchConfig.maxKsumSigma.
136 Finally, a spatial model
is built using all currently-acceptable candidates,
137 and the spatial model used to derive a second set of (spatial) residuals
138 which are again used to reject bad candidates, using the same thresholds
as above.
142 There
is no run() method
for this Task. Instead there are 4 methods that
143 may be used to invoke the Psf-matching. These are
144 `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchMaskedImages`,
145 `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractMaskedImages`,
146 `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchExposures`,
and
147 `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractExposures`.
151 input images to be misregistered
and potentially be different sizes; by default a
153 that
"match" images
return a Psf-matched image,
while the methods that
"subtract" images
154 return a Psf-matched
and template subtracted image.
156 See each method
's returned lsst.pipe.base.Struct for more details.
160 The ``pipetask`` command line interface supports a
161 flag --debug to import @b debug.py
from your PYTHONPATH. The relevant contents of debug.py
162 for this Task include:
170 if name ==
"lsst.ip.diffim.psfMatch":
172 di.maskTransparency = 80
173 di.displayCandidates =
True
174 di.displayKernelBasis =
False
175 di.displayKernelMosaic =
True
176 di.plotKernelSpatialModel =
False
177 di.showBadCandidates =
True
178 elif name ==
"lsst.ip.diffim.imagePsfMatch":
180 di.maskTransparency = 30
181 di.displayTemplate =
True
182 di.displaySciIm =
True
183 di.displaySpatialCells =
True
184 di.displayDiffIm =
True
185 di.showBadCandidates =
True
186 elif name ==
"lsst.ip.diffim.diaCatalogSourceSelector":
188 di.maskTransparency = 30
189 di.displayExposure =
True
190 di.pauseAtEnd =
False
195 Note that
if you want addional logging info, you may add to your scripts:
199 import lsst.utils.logging
as logUtils
200 logUtils.trace_set_at(
"lsst.ip.diffim", 4)
204 A complete example of using ImagePsfMatchTask
206 This code
is imagePsfMatchTask.py
in the examples directory,
and can be run
as e.g.
210 examples/imagePsfMatchTask.py --debug
211 examples/imagePsfMatchTask.py --debug --mode=
"matchExposures"
212 examples/imagePsfMatchTask.py --debug --template /path/to/templateExp.fits
213 --science /path/to/scienceExp.fits
215 Create a subclass of ImagePsfMatchTask that allows us to either match exposures,
or subtract exposures:
222 ImagePsfMatchTask.__init__(self, args, kwargs)
224 def run(self, templateExp, scienceExp, mode):
225 if mode ==
"matchExposures":
227 elif mode ==
"subtractExposures":
230 And allow the user the freedom to either run the script
in default mode,
231 or point to their own images on disk.
234 We have enabled some minor display debugging
in this script via the --debug option. However,
if you
235 have an lsstDebug debug.py
in your PYTHONPATH you will get additional debugging displays. The following
236 block checks
for this script:
244 debug.lsstDebug.frame = 3
245 except ImportError
as e:
246 print(e, file=sys.stderr)
248 Finally, we call a run method that we define below.
249 First set up a Config
and modify some of the parameters.
250 E.g. use an
"Alard-Lupton" sum-of-Gaussian basis,
251 fit
for a differential background,
and use low order spatial
252 variation
in the kernel
and background:
260 config = ImagePsfMatchTask.ConfigClass()
261 config.kernel.name =
"AL"
262 config.kernel.active.fitForBackground =
True
263 config.kernel.active.spatialKernelOrder = 1
264 config.kernel.active.spatialBgOrder = 0
266 Make sure the images (
if any) that were sent to the script exist on disk
and are readable. If no images
267 are sent, make some fake data up
for the sake of this example script (have a look at the code
if you want
268 more details on generateFakeImages):
273 if args.template
is not None and args.science
is not None:
274 if not os.path.isfile(args.template):
275 raise FileNotFoundError(
"Template image %s does not exist" % (args.template))
276 if not os.path.isfile(args.science):
277 raise FileNotFoundError(
"Science image %s does not exist" % (args.science))
279 templateExp = afwImage.ExposureF(args.template)
280 except Exception
as e:
281 raise RuntimeError(
"Cannot read template image %s" % (args.template))
283 scienceExp = afwImage.ExposureF(args.science)
284 except Exception
as e:
285 raise RuntimeError(
"Cannot read science image %s" % (args.science))
287 templateExp, scienceExp = generateFakeImages()
288 config.kernel.active.sizeCellX = 128
289 config.kernel.active.sizeCellY = 128
291 Create
and run the Task:
296 psfMatchTask = MyImagePsfMatchTask(config=config)
298 result = psfMatchTask.run(templateExp, scienceExp, args.mode)
300 And
finally provide some optional debugging displays:
307 frame = debug.lsstDebug.frame + 1
310 afwDisplay.Display(frame=frame).mtv(result.matchedExposure,
311 title=
"Example script: Matched Template Image")
312 if "subtractedExposure" in result.getDict():
313 afwDisplay.Display(frame=frame + 1).mtv(result.subtractedExposure,
314 title=
"Example script: Subtracted Image")
317 ConfigClass = ImagePsfMatchConfig
320 """Create the ImagePsfMatchTask.
322 PsfMatchTask.__init__(self, *args, **kwargs)
331 self.makeSubtask(
"selectDetection", schema=self.
selectSchema)
335 """Return the FWHM in pixels of a Psf.
338 position = psf.getAveragePosition()
339 sigPix = psf.computeShape(position).getDeterminantRadius()
340 return sigPix*sigma2fwhm
344 templateFwhmPix=None, scienceFwhmPix=None,
345 candidateList=None, doWarping=True, convolveTemplate=True):
346 """Warp and PSF-match an exposure to the reference.
348 Do the following, in order:
350 - Warp templateExposure to match scienceExposure,
351 if doWarping
True and their WCSs do
not already match
352 - Determine a PSF matching kernel
and differential background model
353 that matches templateExposure to scienceExposure
354 - Convolve templateExposure by PSF matching kernel
359 Exposure to warp
and PSF-match to the reference masked image
361 Exposure whose WCS
and PSF are to be matched to
362 templateFwhmPix :`float`
363 FWHM (
in pixels) of the Psf
in the template image (image to convolve)
364 scienceFwhmPix : `float`
365 FWHM (
in pixels) of the Psf
in the science image
366 candidateList : `list`, optional
367 a list of footprints/maskedImages
for kernel candidates;
368 if `
None` then source detection
is run.
370 - Currently supported: list of Footprints
or measAlg.PsfCandidateF
373 what to do
if ``templateExposure``
and ``scienceExposure`` WCSs do
not match:
375 -
if `
True` then warp ``templateExposure`` to match ``scienceExposure``
376 -
if `
False` then
raise an Exception
378 convolveTemplate : `bool`
379 Whether to convolve the template image
or the science image:
381 -
if `
True`, ``templateExposure``
is warped
if doWarping,
382 ``templateExposure``
is convolved
383 -
if `
False`, ``templateExposure``
is warped
if doWarping,
384 ``scienceExposure``
is convolved
388 results : `lsst.pipe.base.Struct`
389 An `lsst.pipe.base.Struct` containing these fields:
391 - ``matchedImage`` : the PSF-matched exposure =
392 Warped ``templateExposure`` convolved by psfMatchingKernel. This has:
394 - the same parent bbox, Wcs
and PhotoCalib
as scienceExposure
395 - the same filter
as templateExposure
396 - no Psf (because the PSF-matching process does
not compute one)
398 - ``psfMatchingKernel`` : the PSF matching kernel
399 - ``backgroundModel`` : differential background model
400 - ``kernelCellSet`` : SpatialCellSet used to solve
for the PSF matching kernel
405 Raised
if doWarping
is False and ``templateExposure``
and
406 ``scienceExposure`` WCSs do
not match
408 if not self.
_validateWcs(templateExposure, scienceExposure):
410 self.log.info(
"Astrometrically registering template to science image")
411 templatePsf = templateExposure.getPsf()
414 scienceExposure.getWcs())
415 psfWarped =
WarpedPsf(templatePsf, xyTransform)
416 templateExposure = self.
_warper.warpExposure(scienceExposure.getWcs(),
418 destBBox=scienceExposure.getBBox())
419 templateExposure.setPsf(psfWarped)
421 self.log.error(
"ERROR: Input images not registered")
422 raise RuntimeError(
"Input images not registered")
424 if templateFwhmPix
is None:
425 if not templateExposure.hasPsf():
426 self.log.warning(
"No estimate of Psf FWHM for template image")
428 templateFwhmPix = self.
getFwhmPix(templateExposure.getPsf())
429 self.log.info(
"templateFwhmPix: %s", templateFwhmPix)
431 if scienceFwhmPix
is None:
432 if not scienceExposure.hasPsf():
433 self.log.warning(
"No estimate of Psf FWHM for science image")
435 scienceFwhmPix = self.
getFwhmPix(scienceExposure.getPsf())
436 self.log.info(
"scienceFwhmPix: %s", scienceFwhmPix)
441 templateExposure, scienceExposure, kernelSize, candidateList)
443 templateExposure.getMaskedImage(), scienceExposure.getMaskedImage(), candidateList,
444 templateFwhmPix=templateFwhmPix, scienceFwhmPix=scienceFwhmPix)
448 templateExposure, scienceExposure, kernelSize, candidateList)
450 scienceExposure.getMaskedImage(), templateExposure.getMaskedImage(), candidateList,
451 templateFwhmPix=scienceFwhmPix, scienceFwhmPix=templateFwhmPix)
454 psfMatchedExposure.setFilter(templateExposure.getFilter())
455 psfMatchedExposure.setPhotoCalib(scienceExposure.getPhotoCalib())
456 results.warpedExposure = templateExposure
457 results.matchedExposure = psfMatchedExposure
462 templateFwhmPix=None, scienceFwhmPix=None):
463 """PSF-match a MaskedImage (templateMaskedImage) to a reference MaskedImage (scienceMaskedImage).
465 Do the following, in order:
467 - Determine a PSF matching kernel
and differential background model
468 that matches templateMaskedImage to scienceMaskedImage
469 - Convolve templateMaskedImage by the PSF matching kernel
474 masked image to PSF-match to the reference masked image;
475 must be warped to match the reference masked image
477 maskedImage whose PSF
is to be matched to
478 templateFwhmPix : `float`
479 FWHM (
in pixels) of the Psf
in the template image (image to convolve)
480 scienceFwhmPix : `float`
481 FWHM (
in pixels) of the Psf
in the science image
482 candidateList : `list`, optional
483 A list of footprints/maskedImages
for kernel candidates;
484 if `
None` then source detection
is run.
486 - Currently supported: list of Footprints
or measAlg.PsfCandidateF
491 An `lsst.pipe.base.Struct` containing these fields:
493 - psfMatchedMaskedImage: the PSF-matched masked image =
494 ``templateMaskedImage`` convolved
with psfMatchingKernel.
495 This has the same xy0, dimensions
and wcs
as ``scienceMaskedImage``.
496 - psfMatchingKernel: the PSF matching kernel
497 - backgroundModel: differential background model
498 - kernelCellSet: SpatialCellSet used to solve
for the PSF matching kernel
503 Raised
if input images have different dimensions
509 displaySpatialCells =
lsstDebug.Info(__name__).displaySpatialCells
511 if not maskTransparency:
514 afwDisplay.setDefaultMaskTransparency(maskTransparency)
516 if not candidateList:
517 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
519 if not self.
_validateSize(templateMaskedImage, scienceMaskedImage):
520 self.log.error(
"ERROR: Input images different size")
521 raise RuntimeError(
"Input images different size")
523 if display
and displayTemplate:
524 disp = afwDisplay.Display(frame=lsstDebug.frame)
525 disp.mtv(templateMaskedImage, title=
"Image to convolve")
528 if display
and displaySciIm:
529 disp = afwDisplay.Display(frame=lsstDebug.frame)
530 disp.mtv(scienceMaskedImage, title=
"Image to not convolve")
537 if display
and displaySpatialCells:
538 diffimUtils.showKernelSpatialCells(scienceMaskedImage, kernelCellSet,
539 symb=
"o", ctype=afwDisplay.CYAN, ctypeUnused=afwDisplay.YELLOW,
540 ctypeBad=afwDisplay.RED, size=4, frame=lsstDebug.frame,
541 title=
"Image to not convolve")
544 if templateFwhmPix
and scienceFwhmPix:
545 self.log.info(
"Matching Psf FWHM %.2f -> %.2f pix", templateFwhmPix, scienceFwhmPix)
552 bicDegrees = nbe(tmpKernelCellSet, self.log)
554 basisDegGauss=bicDegrees[0], metadata=self.metadata)
558 metadata=self.metadata)
560 spatialSolution, psfMatchingKernel, backgroundModel = self.
_solve(kernelCellSet, basisList)
562 psfMatchedMaskedImage = afwImage.MaskedImageF(templateMaskedImage.getBBox())
564 convolutionControl.setDoNormalize(
False)
565 afwMath.convolve(psfMatchedMaskedImage, templateMaskedImage, psfMatchingKernel, convolutionControl)
566 return pipeBase.Struct(
567 matchedImage=psfMatchedMaskedImage,
568 psfMatchingKernel=psfMatchingKernel,
569 backgroundModel=backgroundModel,
570 kernelCellSet=kernelCellSet,
575 templateFwhmPix=None, scienceFwhmPix=None,
576 candidateList=None, doWarping=True, convolveTemplate=True):
577 """Register, Psf-match and subtract two Exposures.
579 Do the following, in order:
581 - Warp templateExposure to match scienceExposure,
if their WCSs do
not already match
582 - Determine a PSF matching kernel
and differential background model
583 that matches templateExposure to scienceExposure
584 - PSF-match templateExposure to scienceExposure
585 - Compute subtracted exposure (see
return values
for equation).
589 templateExposure : `lsst.afw.image.ExposureF`
590 Exposure to PSF-match to scienceExposure
591 scienceExposure : `lsst.afw.image.ExposureF`
593 templateFwhmPix : `float`
594 FWHM (
in pixels) of the Psf
in the template image (image to convolve)
595 scienceFwhmPix : `float`
596 FWHM (
in pixels) of the Psf
in the science image
597 candidateList : `list`, optional
598 A list of footprints/maskedImages
for kernel candidates;
599 if `
None` then source detection
is run.
601 - Currently supported: list of Footprints
or measAlg.PsfCandidateF
604 What to do
if ``templateExposure```
and ``scienceExposure`` WCSs do
607 -
if `
True` then warp ``templateExposure`` to match ``scienceExposure``
608 -
if `
False` then
raise an Exception
610 convolveTemplate : `bool`
611 Convolve the template image
or the science image
613 -
if `
True`, ``templateExposure``
is warped
if doWarping,
614 ``templateExposure``
is convolved
615 -
if `
False`, ``templateExposure``
is warped
if doWarping,
616 ``scienceExposure
is`` convolved
620 result : `lsst.pipe.base.Struct`
621 An `lsst.pipe.base.Struct` containing these fields:
623 - ``subtractedExposure`` : subtracted Exposure
624 scienceExposure - (matchedImage + backgroundModel)
625 - ``matchedImage`` : ``templateExposure`` after warping to match
626 ``templateExposure`` (
if doWarping true),
627 and convolving
with psfMatchingKernel
628 - ``psfMatchingKernel`` : PSF matching kernel
629 - ``backgroundModel`` : differential background model
630 - ``kernelCellSet`` : SpatialCellSet used to determine PSF matching kernel
633 templateExposure=templateExposure,
634 scienceExposure=scienceExposure,
635 templateFwhmPix=templateFwhmPix,
636 scienceFwhmPix=scienceFwhmPix,
637 candidateList=candidateList,
639 convolveTemplate=convolveTemplate
642 subtractedExposure = afwImage.ExposureF(scienceExposure, deep=
True)
648 subtractedMaskedImage = subtractedExposure.maskedImage
649 subtractedMaskedImage -= results.matchedExposure.maskedImage
650 subtractedMaskedImage -= results.backgroundModel
652 subtractedMaskedImage = subtractedExposure.maskedImage
653 subtractedMaskedImage[:, :] = results.warpedExposure.maskedImage
654 subtractedMaskedImage -= results.matchedExposure.maskedImage
655 subtractedMaskedImage -= results.backgroundModel
658 subtractedMaskedImage *= -1
661 subtractedMaskedImage /= results.psfMatchingKernel.computeImage(
662 afwImage.ImageD(results.psfMatchingKernel.getDimensions()),
False)
664 subtractedExposure.setPsf(results.warpedExposure.getPsf())
670 if not maskTransparency:
673 afwDisplay.setDefaultMaskTransparency(maskTransparency)
674 if display
and displayDiffIm:
675 disp = afwDisplay.Display(frame=lsstDebug.frame)
676 disp.mtv(templateExposure, title=
"Template")
678 disp = afwDisplay.Display(frame=lsstDebug.frame)
679 disp.mtv(results.matchedExposure, title=
"Matched template")
681 disp = afwDisplay.Display(frame=lsstDebug.frame)
682 disp.mtv(scienceExposure, title=
"Science Image")
684 disp = afwDisplay.Display(frame=lsstDebug.frame)
685 disp.mtv(subtractedExposure, title=
"Difference Image")
688 results.subtractedExposure = subtractedExposure
693 templateFwhmPix=None, scienceFwhmPix=None):
694 """Psf-match and subtract two MaskedImages.
696 Do the following, in order:
698 - PSF-match templateMaskedImage to scienceMaskedImage
699 - Determine the differential background
700 - Return the difference: scienceMaskedImage
701 ((warped templateMaskedImage convolved
with psfMatchingKernel) + backgroundModel)
706 MaskedImage to PSF-match to ``scienceMaskedImage``
708 Reference MaskedImage
709 templateFwhmPix : `float`
710 FWHM (
in pixels) of the Psf
in the template image (image to convolve)
711 scienceFwhmPix : `float`
712 FWHM (
in pixels) of the Psf
in the science image
713 candidateList : `list`, optional
714 A list of footprints/maskedImages
for kernel candidates;
715 if `
None` then source detection
is run.
717 - Currently supported: list of Footprints
or measAlg.PsfCandidateF
721 results : `lsst.pipe.base.Struct`
722 An `lsst.pipe.base.Struct` containing these fields:
724 - ``subtractedMaskedImage`` : ``scienceMaskedImage`` - (matchedImage + backgroundModel)
725 - ``matchedImage`` : templateMaskedImage convolved
with psfMatchingKernel
726 - `psfMatchingKernel`` : PSF matching kernel
727 - ``backgroundModel`` : differential background model
728 - ``kernelCellSet`` : SpatialCellSet used to determine PSF matching kernel
731 if not candidateList:
732 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
735 templateMaskedImage=templateMaskedImage,
736 scienceMaskedImage=scienceMaskedImage,
737 candidateList=candidateList,
738 templateFwhmPix=templateFwhmPix,
739 scienceFwhmPix=scienceFwhmPix,
742 subtractedMaskedImage = afwImage.MaskedImageF(scienceMaskedImage,
True)
743 subtractedMaskedImage -= results.matchedImage
744 subtractedMaskedImage -= results.backgroundModel
745 results.subtractedMaskedImage = subtractedMaskedImage
751 if not maskTransparency:
754 afwDisplay.setDefaultMaskTransparency(maskTransparency)
755 if display
and displayDiffIm:
756 disp = afwDisplay.Display(frame=lsstDebug.frame)
757 disp.mtv(subtractedMaskedImage, title=
"Subtracted masked image")
763 """Get sources to use for Psf-matching.
765 This method runs detection and measurement on an exposure.
766 The returned set of sources will be used
as candidates
for
772 Exposure on which to run detection/measurement
776 Whether
or not to smooth the Exposure
with Psf before detection
778 Factory
for the generation of Source ids
783 source catalog containing candidates
for the Psf-matching
786 table = afwTable.SourceTable.make(self.
selectSchema, idFactory)
789 mi = exposure.getMaskedImage()
791 imArr = mi.getImage().getArray()
792 maskArr = mi.getMask().getArray()
793 miArr = np.ma.masked_array(imArr, mask=maskArr)
796 bkgd = fitBg.getImageF(self.
background.config.algorithm,
799 self.log.warning(
"Failed to get background model. Falling back to median background estimation")
800 bkgd = np.ma.median(miArr)
806 detRet = self.selectDetection.run(
812 selectSources = detRet.sources
813 self.selectMeasurement.run(measCat=selectSources, exposure=exposure)
821 """Make a list of acceptable KernelCandidates.
823 Accept or generate a list of candidate sources
for
824 Psf-matching,
and examine the Mask planes
in both of the
825 images
for indications of bad pixels
830 Exposure that will be convolved
832 Exposure that will be matched-to
834 Dimensions of the Psf-matching Kernel, used to grow detection footprints
835 candidateList : `list`, optional
836 List of Sources to examine. Elements must be of type afw.table.Source
837 or a type that wraps a Source
and has a getSource() method, such
as
842 candidateList : `list` of `dict`
843 A list of dicts having a
"source" and "footprint"
844 field
for the Sources deemed to be appropriate
for Psf
847 if candidateList
is None:
850 if len(candidateList) < 1:
851 raise RuntimeError(
"No candidates in candidateList")
853 listTypes =
set(
type(x)
for x
in candidateList)
854 if len(listTypes) > 1:
855 raise RuntimeError(
"Candidate list contains mixed types: %s" % [t
for t
in listTypes])
859 candidateList[0].getSource()
860 except Exception
as e:
861 raise RuntimeError(f
"Candidate List is of type: {type(candidateList[0])} "
862 "Can only make candidate list from list of afwTable.SourceRecords, "
863 f
"measAlg.PsfCandidateF or other type with a getSource() method: {e}")
864 candidateList = [c.getSource()
for c
in candidateList]
866 candidateList = diffimTools.sourceToFootprintList(candidateList,
867 templateExposure, scienceExposure,
871 if len(candidateList) == 0:
872 raise RuntimeError(
"Cannot find any objects suitable for KernelCandidacy")
876 def makeKernelBasisList(self, targetFwhmPix=None, referenceFwhmPix=None,
877 basisDegGauss=None, basisSigmaGauss=None, metadata=None):
878 """Wrapper to set log messages for
883 targetFwhmPix : `float`, optional
884 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
885 Not used for delta function basis sets.
886 referenceFwhmPix : `float`, optional
887 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
888 Not used
for delta function basis sets.
889 basisDegGauss : `list` of `int`, optional
890 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
891 Not used
for delta function basis sets.
892 basisSigmaGauss : `list` of `int`, optional
893 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
894 Not used
for delta function basis sets.
896 Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`.
897 Not used
for delta function basis sets.
901 basisList: `list` of `lsst.afw.math.kernel.FixedKernel`
902 List of basis kernels.
905 targetFwhmPix=targetFwhmPix,
906 referenceFwhmPix=referenceFwhmPix,
907 basisDegGauss=basisDegGauss,
908 basisSigmaGauss=basisSigmaGauss,
910 if targetFwhmPix == referenceFwhmPix:
911 self.log.info(
"Target and reference psf fwhms are equal, falling back to config values")
912 elif referenceFwhmPix > targetFwhmPix:
913 self.log.info(
"Reference psf fwhm is the greater, normal convolution mode")
915 self.log.info(
"Target psf fwhm is the greater, deconvolution mode")
919 def _adaptCellSize(self, candidateList):
920 """NOT IMPLEMENTED YET.
924 def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList):
925 """Build a SpatialCellSet for use with the solve method.
930 MaskedImage to PSF-matched to scienceMaskedImage
932 Reference MaskedImage
933 candidateList : `list`
934 A list of footprints/maskedImages for kernel candidates;
936 - Currently supported: list of Footprints
or measAlg.PsfCandidateF
941 a SpatialCellSet
for use
with self.
_solve
943 if not candidateList:
944 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
950 sizeCellX, sizeCellY)
954 for cand
in candidateList:
956 bbox = cand.getBBox()
958 bbox = cand[
'footprint'].getBBox()
959 tmi = afwImage.MaskedImageF(templateMaskedImage, bbox)
960 smi = afwImage.MaskedImageF(scienceMaskedImage, bbox)
964 cand = cand[
'source']
965 xPos = cand.getCentroid()[0]
966 yPos = cand.getCentroid()[1]
967 cand = diffimLib.makeKernelCandidate(xPos, yPos, tmi, smi, ps)
969 self.log.debug(
"Candidate %d at %f, %f", cand.getId(), cand.getXCenter(), cand.getYCenter())
970 kernelCellSet.insertCandidate(cand)
974 def _validateSize(self, templateMaskedImage, scienceMaskedImage):
975 """Return True if two image-like objects are the same size.
977 return templateMaskedImage.getDimensions() == scienceMaskedImage.getDimensions()
979 def _validateWcs(self, templateExposure, scienceExposure):
980 """Return True if the WCS of the two Exposures have the same origin and extent.
982 templateWcs = templateExposure.getWcs()
983 scienceWcs = scienceExposure.getWcs()
984 templateBBox = templateExposure.getBBox()
985 scienceBBox = scienceExposure.getBBox()
988 templateOrigin = templateWcs.pixelToSky(
geom.Point2D(templateBBox.getBegin()))
989 scienceOrigin = scienceWcs.pixelToSky(
geom.Point2D(scienceBBox.getBegin()))
992 templateLimit = templateWcs.pixelToSky(
geom.Point2D(templateBBox.getEnd()))
993 scienceLimit = scienceWcs.pixelToSky(
geom.Point2D(scienceBBox.getEnd()))
995 self.log.info(
"Template Wcs : %f,%f -> %f,%f",
996 templateOrigin[0], templateOrigin[1],
997 templateLimit[0], templateLimit[1])
998 self.log.info(
"Science Wcs : %f,%f -> %f,%f",
999 scienceOrigin[0], scienceOrigin[1],
1000 scienceLimit[0], scienceLimit[1])
1002 templateBBox =
geom.Box2D(templateOrigin.getPosition(geom.degrees),
1003 templateLimit.getPosition(geom.degrees))
1004 scienceBBox =
geom.Box2D(scienceOrigin.getPosition(geom.degrees),
1005 scienceLimit.getPosition(geom.degrees))
1006 if not (templateBBox.overlaps(scienceBBox)):
1007 raise RuntimeError(
"Input images do not overlap at all")
1009 if ((templateOrigin != scienceOrigin)
1010 or (templateLimit != scienceLimit)
1011 or (templateExposure.getDimensions() != scienceExposure.getDimensions())):
1016subtractAlgorithmRegistry = pexConfig.makeRegistry(
1017 doc=
"A registry of subtraction algorithms for use as a subtask in imageDifference",
1020subtractAlgorithmRegistry.register(
'al', ImagePsfMatchTask)
A class to contain the data, WCS, and other information needed to describe an image of the sky.
A class to manipulate images, masks, and variance as a single object.
Parameters to control convolution.
Lanczos warping: accurate but slow and can introduce ringing artifacts.
A collection of SpatialCells covering an entire image.
Record class that contains measurements made on a single exposure.
Class for storing ordered metadata with comments.
Class for storing generic metadata.
A floating-point coordinate rectangle geometry.
def _validateWcs(self, templateExposure, scienceExposure)
def _adaptCellSize(self, candidateList)
def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList)
def _validateSize(self, templateMaskedImage, scienceMaskedImage)
def subtractMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None)
def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None)
def __init__(self, *args, **kwargs)
def getSelectSources(self, exposure, sigma=None, doSmooth=True, idFactory=None)
def getFwhmPix(self, psf, position=None)
def subtractExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None, doWarping=True, convolveTemplate=True)
def matchExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None, doWarping=True, convolveTemplate=True)
def makeKernelBasisList(self, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, basisSigmaGauss=None, metadata=None)
def makeCandidateList(self, templateExposure, scienceExposure, kernelSize, candidateList=None)
def _buildCellSet(self, *args)
def _solve(self, kernelCellSet, basisList, returnOnExcept=False)
A Psf class that maps an arbitrary Psf through a coordinate transformation.
daf::base::PropertySet * set
std::shared_ptr< TransformPoint2ToPoint2 > makeWcsPairTransform(SkyWcs const &src, SkyWcs const &dst)
A Transform obtained by putting two SkyWcs objects "back to back".
std::shared_ptr< Exposure< ImagePixelT, MaskPixelT, VariancePixelT > > makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, std::shared_ptr< geom::SkyWcs const > wcs=std::shared_ptr< geom::SkyWcs const >())
A function to return an Exposure of the correct type (cf.
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, ConvolutionControl const &convolutionControl=ConvolutionControl())
Convolve an Image or MaskedImage with a Kernel, setting pixels of an existing output image.