34 from .makeKernelBasisList
import makeKernelBasisList
35 from .psfMatch
import PsfMatchTask, PsfMatchConfigDF, PsfMatchConfigAL
36 from .
import utils
as diffimUtils
37 from .
import diffimLib
38 from .
import diffimTools
41 __all__ = [
"ImagePsfMatchConfig",
"ImagePsfMatchTask",
"subtractAlgorithmRegistry"]
43 sigma2fwhm = 2.*np.sqrt(2.*np.log(2.))
47 """Configuration for image-to-image Psf matching. 49 kernel = pexConfig.ConfigChoiceField(
57 selectDetection = pexConfig.ConfigurableField(
58 target=SourceDetectionTask,
59 doc=
"Initial detections used to feed stars to kernel fitting",
61 selectMeasurement = pexConfig.ConfigurableField(
62 target=SingleFrameMeasurementTask,
63 doc=
"Initial measurements used to feed stars to kernel fitting",
73 self.
selectMeasurement.algorithms.names = (
'base_SdssCentroid',
'base_PsfFlux',
'base_PixelFlags',
74 'base_SdssShape',
'base_GaussianFlux',
'base_SkyCoord')
81 """Psf-match two MaskedImages or Exposures using the sources in the images. 86 Arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__ 88 Keyword arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__ 92 Upon initialization, the kernel configuration is defined by self.config.kernel.active. 93 The task creates an lsst.afw.math.Warper from the subConfig self.config.kernel.active.warpingConfig. 94 A schema for the selection and measurement of candidate lsst.ip.diffim.KernelCandidates is 95 defined, and used to initize subTasks selectDetection (for candidate detection) and selectMeasurement 96 (for candidate measurement). 100 Build a Psf-matching kernel using two input images, either as MaskedImages (in which case they need 101 to be astrometrically aligned) or Exposures (in which case astrometric alignment will happen by 102 default but may be turned off). This requires a list of input Sources which may be provided 103 by the calling Task; if not, the Task will perform a coarse source detection 104 and selection for this purpose. Sources are vetted for signal-to-noise and masked pixels 105 (in both the template and science image), and substamps around each acceptable 106 source are extracted and used to create an instance of KernelCandidate. 107 Each KernelCandidate is then placed within a lsst.afw.math.SpatialCellSet, which is used by an ensemble of 108 lsst.afw.math.CandidateVisitor instances to build the Psf-matching kernel. These visitors include, in 109 the order that they are called: BuildSingleKernelVisitor, KernelSumVisitor, BuildSpatialKernelVisitor, 110 and AssessSpatialKernelVisitor. 112 Sigma clipping of KernelCandidates is performed as follows: 114 - BuildSingleKernelVisitor, using the substamp diffim residuals from the per-source kernel fit, 115 if PsfMatchConfig.singleKernelClipping is True 116 - KernelSumVisitor, using the mean and standard deviation of the kernel sum from all candidates, 117 if PsfMatchConfig.kernelSumClipping is True 118 - AssessSpatialKernelVisitor, using the substamp diffim ressiduals from the spatial kernel fit, 119 if PsfMatchConfig.spatialKernelClipping is True 121 The actual solving for the kernel (and differential background model) happens in 122 lsst.ip.diffim.PsfMatchTask._solve. This involves a loop over the SpatialCellSet that first builds the 123 per-candidate matching kernel for the requested number of KernelCandidates per cell 124 (PsfMatchConfig.nStarPerCell). The quality of this initial per-candidate difference image is examined, 125 using moments of the pixel residuals in the difference image normalized by the square root of the variance 126 (i.e. sigma); ideally this should follow a normal (0, 1) distribution, 127 but the rejection thresholds are set 128 by the config (PsfMatchConfig.candidateResidualMeanMax and PsfMatchConfig.candidateResidualStdMax). 129 All candidates that pass this initial build are then examined en masse to find the 130 mean/stdev of the kernel sums across all candidates. 131 Objects that are significantly above or below the mean, 132 typically due to variability or sources that are saturated in one image but not the other, 133 are also rejected.This threshold is defined by PsfMatchConfig.maxKsumSigma. 134 Finally, a spatial model is built using all currently-acceptable candidates, 135 and the spatial model used to derive a second set of (spatial) residuals 136 which are again used to reject bad candidates, using the same thresholds as above. 140 There is no run() method for this Task. Instead there are 4 methods that 141 may be used to invoke the Psf-matching. These are 142 `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchMaskedImages`, 143 `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractMaskedImages`, 144 `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchExposures`, and 145 `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractExposures`. 147 The methods that operate on lsst.afw.image.MaskedImage require that the images already be astrometrically 148 aligned, and are the same shape. The methods that operate on lsst.afw.image.Exposure allow for the 149 input images to be misregistered and potentially be different sizes; by default a 150 lsst.afw.math.LanczosWarpingKernel is used to perform the astrometric alignment. The methods 151 that "match" images return a Psf-matched image, while the methods that "subtract" images 152 return a Psf-matched and template subtracted image. 154 See each method's returned lsst.pipe.base.Struct for more details. 158 The lsst.pipe.base.cmdLineTask.CmdLineTask command line task interface supports a 159 flag -d/--debug to import debug.py from your PYTHONPATH. The relevant contents of debug.py 160 for this Task include: 167 di = lsstDebug.getInfo(name) 168 if name == "lsst.ip.diffim.psfMatch": 169 di.display = True # enable debug output 170 di.maskTransparency = 80 # display mask transparency 171 di.displayCandidates = True # show all the candidates and residuals 172 di.displayKernelBasis = False # show kernel basis functions 173 di.displayKernelMosaic = True # show kernel realized across the image 174 di.plotKernelSpatialModel = False # show coefficients of spatial model 175 di.showBadCandidates = True # show the bad candidates (red) along with good (green) 176 elif name == "lsst.ip.diffim.imagePsfMatch": 177 di.display = True # enable debug output 178 di.maskTransparency = 30 # display mask transparency 179 di.displayTemplate = True # show full (remapped) template 180 di.displaySciIm = True # show science image to match to 181 di.displaySpatialCells = True # show spatial cells 182 di.displayDiffIm = True # show difference image 183 di.showBadCandidates = True # show the bad candidates (red) along with good (green) 184 elif name == "lsst.ip.diffim.diaCatalogSourceSelector": 185 di.display = False # enable debug output 186 di.maskTransparency = 30 # display mask transparency 187 di.displayExposure = True # show exposure with candidates indicated 188 di.pauseAtEnd = False # pause when done 190 lsstDebug.Info = DebugInfo 193 Note that if you want addional logging info, you may add to your scripts: 197 import lsst.log.utils as logUtils 198 logUtils.traceSetAt("ip.diffim", 4) 202 A complete example of using ImagePsfMatchTask 204 This code is imagePsfMatchTask.py in the examples directory, and can be run as e.g. 208 examples/imagePsfMatchTask.py --debug 209 examples/imagePsfMatchTask.py --debug --mode="matchExposures" 210 examples/imagePsfMatchTask.py --debug --template /path/to/templateExp.fits 211 --science /path/to/scienceExp.fits 213 Create a subclass of ImagePsfMatchTask that allows us to either match exposures, or subtract exposures: 217 class MyImagePsfMatchTask(ImagePsfMatchTask): 219 def __init__(self, args, kwargs): 220 ImagePsfMatchTask.__init__(self, args, kwargs) 222 def run(self, templateExp, scienceExp, mode): 223 if mode == "matchExposures": 224 return self.matchExposures(templateExp, scienceExp) 225 elif mode == "subtractExposures": 226 return self.subtractExposures(templateExp, scienceExp) 228 And allow the user the freedom to either run the script in default mode, 229 or point to their own images on disk. 230 Note that these images must be readable as an lsst.afw.image.Exposure. 232 We have enabled some minor display debugging in this script via the --debug option. However, if you 233 have an lsstDebug debug.py in your PYTHONPATH you will get additional debugging displays. The following 234 block checks for this script: 241 # Since I am displaying 2 images here, set the starting frame number for the LSST debug LSST 242 debug.lsstDebug.frame = 3 243 except ImportError as e: 244 print(e, file=sys.stderr) 246 Finally, we call a run method that we define below. 247 First set up a Config and modify some of the parameters. 248 E.g. use an "Alard-Lupton" sum-of-Gaussian basis, 249 fit for a differential background, and use low order spatial 250 variation in the kernel and background: 256 # Create the Config and use sum of gaussian basis 258 config = ImagePsfMatchTask.ConfigClass() 259 config.kernel.name = "AL" 260 config.kernel.active.fitForBackground = True 261 config.kernel.active.spatialKernelOrder = 1 262 config.kernel.active.spatialBgOrder = 0 264 Make sure the images (if any) that were sent to the script exist on disk and are readable. If no images 265 are sent, make some fake data up for the sake of this example script (have a look at the code if you want 266 more details on generateFakeImages): 270 # Run the requested method of the Task 271 if args.template is not None and args.science is not None: 272 if not os.path.isfile(args.template): 273 raise Exception("Template image %s does not exist" % (args.template)) 274 if not os.path.isfile(args.science): 275 raise Exception("Science image %s does not exist" % (args.science)) 277 templateExp = afwImage.ExposureF(args.template) 278 except Exception as e: 279 raise Exception("Cannot read template image %s" % (args.template)) 281 scienceExp = afwImage.ExposureF(args.science) 282 except Exception as e: 283 raise Exception("Cannot read science image %s" % (args.science)) 285 templateExp, scienceExp = generateFakeImages() 286 config.kernel.active.sizeCellX = 128 287 config.kernel.active.sizeCellY = 128 289 Create and run the Task: 294 psfMatchTask = MyImagePsfMatchTask(config=config) 296 result = psfMatchTask.run(templateExp, scienceExp, args.mode) 298 And finally provide some optional debugging displays: 303 # See if the LSST debug has incremented the frame number; if not start with frame 3 305 frame = debug.lsstDebug.frame + 1 308 afwDisplay.Display(frame=frame).mtv(result.matchedExposure, 309 title="Example script: Matched Template Image") 310 if "subtractedExposure" in result.getDict(): 311 afwDisplay.Display(frame=frame + 1).mtv(result.subtractedExposure, 312 title="Example script: Subtracted Image") 315 ConfigClass = ImagePsfMatchConfig
318 """Create the ImagePsfMatchTask. 320 PsfMatchTask.__init__(self, *args, **kwargs)
322 self.
_warper = afwMath.Warper.fromConfig(self.
kConfig.warpingConfig)
329 self.makeSubtask(
"selectDetection", schema=self.
selectSchema)
333 """Return the FWHM in pixels of a Psf. 335 sigPix = psf.computeShape().getDeterminantRadius()
336 return sigPix*sigma2fwhm
340 templateFwhmPix=None, scienceFwhmPix=None,
341 candidateList=None, doWarping=True, convolveTemplate=True):
342 """Warp and PSF-match an exposure to the reference. 344 Do the following, in order: 346 - Warp templateExposure to match scienceExposure, 347 if doWarping True and their WCSs do not already match 348 - Determine a PSF matching kernel and differential background model 349 that matches templateExposure to scienceExposure 350 - Convolve templateExposure by PSF matching kernel 354 templateExposure : `lsst.afw.image.Exposure` 355 Exposure to warp and PSF-match to the reference masked image 356 scienceExposure : `lsst.afw.image.Exposure` 357 Exposure whose WCS and PSF are to be matched to 358 templateFwhmPix :`float` 359 FWHM (in pixels) of the Psf in the template image (image to convolve) 360 scienceFwhmPix : `float` 361 FWHM (in pixels) of the Psf in the science image 362 candidateList : `list`, optional 363 a list of footprints/maskedImages for kernel candidates; 364 if `None` then source detection is run. 366 - Currently supported: list of Footprints or measAlg.PsfCandidateF 369 what to do if ``templateExposure`` and ``scienceExposure`` WCSs do not match: 371 - if `True` then warp ``templateExposure`` to match ``scienceExposure`` 372 - if `False` then raise an Exception 374 convolveTemplate : `bool` 375 Whether to convolve the template image or the science image: 377 - if `True`, ``templateExposure`` is warped if doWarping, 378 ``templateExposure`` is convolved 379 - if `False`, ``templateExposure`` is warped if doWarping, 380 ``scienceExposure`` is convolved 384 results : `lsst.pipe.base.Struct` 385 An `lsst.pipe.base.Struct` containing these fields: 387 - ``matchedImage`` : the PSF-matched exposure = 388 Warped ``templateExposure`` convolved by psfMatchingKernel. This has: 390 - the same parent bbox, Wcs and PhotoCalib as scienceExposure 391 - the same filter as templateExposure 392 - no Psf (because the PSF-matching process does not compute one) 394 - ``psfMatchingKernel`` : the PSF matching kernel 395 - ``backgroundModel`` : differential background model 396 - ``kernelCellSet`` : SpatialCellSet used to solve for the PSF matching kernel 401 Raised if doWarping is False and ``templateExposure`` and 402 ``scienceExposure`` WCSs do not match 404 if not self.
_validateWcs(templateExposure, scienceExposure):
406 self.log.
info(
"Astrometrically registering template to science image")
407 templatePsf = templateExposure.getPsf()
410 scienceExposure.getWcs())
411 psfWarped =
WarpedPsf(templatePsf, xyTransform)
414 destBBox=scienceExposure.getBBox())
415 templateExposure.setPsf(psfWarped)
417 self.log.
error(
"ERROR: Input images not registered")
418 raise RuntimeError(
"Input images not registered")
420 if templateFwhmPix
is None:
421 if not templateExposure.hasPsf():
422 self.log.
warn(
"No estimate of Psf FWHM for template image")
424 templateFwhmPix = self.
getFwhmPix(templateExposure.getPsf())
425 self.log.
info(
"templateFwhmPix: {}".
format(templateFwhmPix))
427 if scienceFwhmPix
is None:
428 if not scienceExposure.hasPsf():
429 self.log.
warn(
"No estimate of Psf FWHM for science image")
431 scienceFwhmPix = self.
getFwhmPix(scienceExposure.getPsf())
432 self.log.
info(
"scienceFwhmPix: {}".
format(scienceFwhmPix))
435 candidateList = self.
makeCandidateList(templateExposure, scienceExposure, kernelSize, candidateList)
439 templateExposure.getMaskedImage(), scienceExposure.getMaskedImage(), candidateList,
440 templateFwhmPix=templateFwhmPix, scienceFwhmPix=scienceFwhmPix)
443 scienceExposure.getMaskedImage(), templateExposure.getMaskedImage(), candidateList,
444 templateFwhmPix=scienceFwhmPix, scienceFwhmPix=templateFwhmPix)
447 psfMatchedExposure.setFilter(templateExposure.getFilter())
448 psfMatchedExposure.setPhotoCalib(scienceExposure.getPhotoCalib())
449 results.warpedExposure = templateExposure
450 results.matchedExposure = psfMatchedExposure
454 def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList,
455 templateFwhmPix=None, scienceFwhmPix=None):
456 """PSF-match a MaskedImage (templateMaskedImage) to a reference MaskedImage (scienceMaskedImage). 458 Do the following, in order: 460 - Determine a PSF matching kernel and differential background model 461 that matches templateMaskedImage to scienceMaskedImage 462 - Convolve templateMaskedImage by the PSF matching kernel 466 templateMaskedImage : `lsst.afw.image.MaskedImage` 467 masked image to PSF-match to the reference masked image; 468 must be warped to match the reference masked image 469 scienceMaskedImage : `lsst.afw.image.MaskedImage` 470 maskedImage whose PSF is to be matched to 471 templateFwhmPix : `float` 472 FWHM (in pixels) of the Psf in the template image (image to convolve) 473 scienceFwhmPix : `float` 474 FWHM (in pixels) of the Psf in the science image 475 candidateList : `list`, optional 476 A list of footprints/maskedImages for kernel candidates; 477 if `None` then source detection is run. 479 - Currently supported: list of Footprints or measAlg.PsfCandidateF 484 An `lsst.pipe.base.Struct` containing these fields: 486 - psfMatchedMaskedImage: the PSF-matched masked image = 487 ``templateMaskedImage`` convolved with psfMatchingKernel. 488 This has the same xy0, dimensions and wcs as ``scienceMaskedImage``. 489 - psfMatchingKernel: the PSF matching kernel 490 - backgroundModel: differential background model 491 - kernelCellSet: SpatialCellSet used to solve for the PSF matching kernel 496 Raised if input images have different dimensions 502 displaySpatialCells =
lsstDebug.Info(__name__).displaySpatialCells
504 if not maskTransparency:
507 afwDisplay.setDefaultMaskTransparency(maskTransparency)
509 if not candidateList:
510 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
512 if not self.
_validateSize(templateMaskedImage, scienceMaskedImage):
513 self.log.
error(
"ERROR: Input images different size")
514 raise RuntimeError(
"Input images different size")
516 if display
and displayTemplate:
517 disp = afwDisplay.Display(frame=lsstDebug.frame)
518 disp.mtv(templateMaskedImage, title=
"Image to convolve")
521 if display
and displaySciIm:
522 disp = afwDisplay.Display(frame=lsstDebug.frame)
523 disp.mtv(scienceMaskedImage, title=
"Image to not convolve")
530 if display
and displaySpatialCells:
531 diffimUtils.showKernelSpatialCells(scienceMaskedImage, kernelCellSet,
532 symb=
"o", ctype=afwDisplay.CYAN, ctypeUnused=afwDisplay.YELLOW,
533 ctypeBad=afwDisplay.RED, size=4, frame=lsstDebug.frame,
534 title=
"Image to not convolve")
537 if templateFwhmPix
and scienceFwhmPix:
538 self.log.
info(
"Matching Psf FWHM %.2f -> %.2f pix", templateFwhmPix, scienceFwhmPix)
540 if self.
kConfig.useBicForKernelBasis:
545 bicDegrees = nbe(tmpKernelCellSet, self.log)
547 alardDegGauss=bicDegrees[0], metadata=self.metadata)
551 metadata=self.metadata)
553 spatialSolution, psfMatchingKernel, backgroundModel = self.
_solve(kernelCellSet, basisList)
555 psfMatchedMaskedImage = afwImage.MaskedImageF(templateMaskedImage.getBBox())
557 afwMath.convolve(psfMatchedMaskedImage, templateMaskedImage, psfMatchingKernel, doNormalize)
558 return pipeBase.Struct(
559 matchedImage=psfMatchedMaskedImage,
560 psfMatchingKernel=psfMatchingKernel,
561 backgroundModel=backgroundModel,
562 kernelCellSet=kernelCellSet,
567 templateFwhmPix=None, scienceFwhmPix=None,
568 candidateList=None, doWarping=True, convolveTemplate=True):
569 """Register, Psf-match and subtract two Exposures. 571 Do the following, in order: 573 - Warp templateExposure to match scienceExposure, if their WCSs do not already match 574 - Determine a PSF matching kernel and differential background model 575 that matches templateExposure to scienceExposure 576 - PSF-match templateExposure to scienceExposure 577 - Compute subtracted exposure (see return values for equation). 581 templateExposure : `lsst.afw.image.Exposure` 582 Exposure to PSF-match to scienceExposure 583 scienceExposure : `lsst.afw.image.Exposure` 585 templateFwhmPix : `float` 586 FWHM (in pixels) of the Psf in the template image (image to convolve) 587 scienceFwhmPix : `float` 588 FWHM (in pixels) of the Psf in the science image 589 candidateList : `list`, optional 590 A list of footprints/maskedImages for kernel candidates; 591 if `None` then source detection is run. 593 - Currently supported: list of Footprints or measAlg.PsfCandidateF 596 What to do if ``templateExposure``` and ``scienceExposure`` WCSs do 599 - if `True` then warp ``templateExposure`` to match ``scienceExposure`` 600 - if `False` then raise an Exception 602 convolveTemplate : `bool` 603 Convolve the template image or the science image 605 - if `True`, ``templateExposure`` is warped if doWarping, 606 ``templateExposure`` is convolved 607 - if `False`, ``templateExposure`` is warped if doWarping, 608 ``scienceExposure is`` convolved 612 result : `lsst.pipe.base.Struct` 613 An `lsst.pipe.base.Struct` containing these fields: 615 - ``subtractedExposure`` : subtracted Exposure 616 scienceExposure - (matchedImage + backgroundModel) 617 - ``matchedImage`` : ``templateExposure`` after warping to match 618 ``templateExposure`` (if doWarping true), 619 and convolving with psfMatchingKernel 620 - ``psfMatchingKernel`` : PSF matching kernel 621 - ``backgroundModel`` : differential background model 622 - ``kernelCellSet`` : SpatialCellSet used to determine PSF matching kernel 625 templateExposure=templateExposure,
626 scienceExposure=scienceExposure,
627 templateFwhmPix=templateFwhmPix,
628 scienceFwhmPix=scienceFwhmPix,
629 candidateList=candidateList,
631 convolveTemplate=convolveTemplate
634 subtractedExposure = afwImage.ExposureF(scienceExposure,
True)
636 subtractedMaskedImage = subtractedExposure.getMaskedImage()
637 subtractedMaskedImage -= results.matchedExposure.getMaskedImage()
638 subtractedMaskedImage -= results.backgroundModel
640 subtractedExposure.setMaskedImage(results.warpedExposure.getMaskedImage())
641 subtractedMaskedImage = subtractedExposure.getMaskedImage()
642 subtractedMaskedImage -= results.matchedExposure.getMaskedImage()
643 subtractedMaskedImage -= results.backgroundModel
646 subtractedMaskedImage *= -1
649 subtractedMaskedImage /= results.psfMatchingKernel.computeImage(
650 afwImage.ImageD(results.psfMatchingKernel.getDimensions()),
False)
656 if not maskTransparency:
659 afwDisplay.setDefaultMaskTransparency(maskTransparency)
660 if display
and displayDiffIm:
661 disp = afwDisplay.Display(frame=lsstDebug.frame)
662 disp.mtv(templateExposure, title=
"Template")
664 disp = afwDisplay.Display(frame=lsstDebug.frame)
665 disp.mtv(results.matchedExposure, title=
"Matched template")
667 disp = afwDisplay.Display(frame=lsstDebug.frame)
668 disp.mtv(scienceExposure, title=
"Science Image")
670 disp = afwDisplay.Display(frame=lsstDebug.frame)
671 disp.mtv(subtractedExposure, title=
"Difference Image")
674 results.subtractedExposure = subtractedExposure
679 templateFwhmPix=None, scienceFwhmPix=None):
680 """Psf-match and subtract two MaskedImages. 682 Do the following, in order: 684 - PSF-match templateMaskedImage to scienceMaskedImage 685 - Determine the differential background 686 - Return the difference: scienceMaskedImage 687 ((warped templateMaskedImage convolved with psfMatchingKernel) + backgroundModel) 691 templateMaskedImage : `lsst.afw.image.MaskedImage` 692 MaskedImage to PSF-match to ``scienceMaskedImage`` 693 scienceMaskedImage : `lsst.afw.image.MaskedImage` 694 Reference MaskedImage 695 templateFwhmPix : `float` 696 FWHM (in pixels) of the Psf in the template image (image to convolve) 697 scienceFwhmPix : `float` 698 FWHM (in pixels) of the Psf in the science image 699 candidateList : `list`, optional 700 A list of footprints/maskedImages for kernel candidates; 701 if `None` then source detection is run. 703 - Currently supported: list of Footprints or measAlg.PsfCandidateF 707 results : `lsst.pipe.base.Struct` 708 An `lsst.pipe.base.Struct` containing these fields: 710 - ``subtractedMaskedImage`` : ``scienceMaskedImage`` - (matchedImage + backgroundModel) 711 - ``matchedImage`` : templateMaskedImage convolved with psfMatchingKernel 712 - `psfMatchingKernel`` : PSF matching kernel 713 - ``backgroundModel`` : differential background model 714 - ``kernelCellSet`` : SpatialCellSet used to determine PSF matching kernel 717 if not candidateList:
718 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
721 templateMaskedImage=templateMaskedImage,
722 scienceMaskedImage=scienceMaskedImage,
723 candidateList=candidateList,
724 templateFwhmPix=templateFwhmPix,
725 scienceFwhmPix=scienceFwhmPix,
728 subtractedMaskedImage = afwImage.MaskedImageF(scienceMaskedImage,
True)
729 subtractedMaskedImage -= results.matchedImage
730 subtractedMaskedImage -= results.backgroundModel
731 results.subtractedMaskedImage = subtractedMaskedImage
737 if not maskTransparency:
740 afwDisplay.setDefaultMaskTransparency(maskTransparency)
741 if display
and displayDiffIm:
742 disp = afwDisplay.Display(frame=lsstDebug.frame)
743 disp.mtv(subtractedMaskedImage, title=
"Subtracted masked image")
749 """Get sources to use for Psf-matching. 751 This method runs detection and measurement on an exposure. 752 The returned set of sources will be used as candidates for 757 exposure : `lsst.afw.image.Exposure` 758 Exposure on which to run detection/measurement 762 Whether or not to smooth the Exposure with Psf before detection 764 Factory for the generation of Source ids 769 source catalog containing candidates for the Psf-matching 772 table = afwTable.SourceTable.make(self.
selectSchema, idFactory)
775 mi = exposure.getMaskedImage()
777 imArr = mi.getImage().getArray()
778 maskArr = mi.getMask().getArray()
779 miArr = np.ma.masked_array(imArr, mask=maskArr)
781 bkgd = self.
background.fitBackground(mi).getImageF()
783 self.log.
warn(
"Failed to get background model. Falling back to median background estimation")
784 bkgd = np.ma.extras.median(miArr)
790 detRet = self.selectDetection.makeSourceCatalog(
796 selectSources = detRet.sources
797 self.selectMeasurement.
run(measCat=selectSources, exposure=exposure)
805 """Make a list of acceptable KernelCandidates. 807 Accept or generate a list of candidate sources for 808 Psf-matching, and examine the Mask planes in both of the 809 images for indications of bad pixels 813 templateExposure : `lsst.afw.image.Exposure` 814 Exposure that will be convolved 815 scienceExposure : `lsst.afw.image.Exposure` 816 Exposure that will be matched-to 818 Dimensions of the Psf-matching Kernel, used to grow detection footprints 819 candidateList : `list`, optional 820 List of Sources to examine. Elements must be of type afw.table.Source 821 or a type that wraps a Source and has a getSource() method, such as 822 meas.algorithms.PsfCandidateF. 826 candidateList : `list` of `dict` 827 A list of dicts having a "source" and "footprint" 828 field for the Sources deemed to be appropriate for Psf 831 if candidateList
is None:
834 if len(candidateList) < 1:
835 raise RuntimeError(
"No candidates in candidateList")
837 listTypes =
set(
type(x)
for x
in candidateList)
838 if len(listTypes) > 1:
839 raise RuntimeError(
"Candidate list contains mixed types: %s" % [l
for l
in listTypes])
843 candidateList[0].getSource()
844 except Exception
as e:
845 raise RuntimeError(
"Candidate List is of type: %s. " % (
type(candidateList[0])) +
846 "Can only make candidate list from list of afwTable.SourceRecords, " +
847 "measAlg.PsfCandidateF or other type with a getSource() method: %s" % (e))
848 candidateList = [c.getSource()
for c
in candidateList]
850 candidateList = diffimTools.sourceToFootprintList(candidateList,
851 templateExposure, scienceExposure,
855 if len(candidateList) == 0:
856 raise RuntimeError(
"Cannot find any objects suitable for KernelCandidacy")
860 def _adaptCellSize(self, candidateList):
861 """NOT IMPLEMENTED YET. 865 def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList):
866 """Build a SpatialCellSet for use with the solve method. 870 templateMaskedImage : `lsst.afw.image.MaskedImage` 871 MaskedImage to PSF-matched to scienceMaskedImage 872 scienceMaskedImage : `lsst.afw.image.MaskedImage` 873 Reference MaskedImage 874 candidateList : `list` 875 A list of footprints/maskedImages for kernel candidates; 877 - Currently supported: list of Footprints or measAlg.PsfCandidateF 881 kernelCellSet : `lsst.afw.math.SpatialCellSet` 882 a SpatialCellSet for use with self._solve 884 if not candidateList:
885 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
891 sizeCellX, sizeCellY)
893 policy = pexConfig.makePolicy(self.
kConfig)
895 for cand
in candidateList:
897 bbox = cand.getBBox()
899 bbox = cand[
'footprint'].getBBox()
900 tmi = afwImage.MaskedImageF(templateMaskedImage, bbox)
901 smi = afwImage.MaskedImageF(scienceMaskedImage, bbox)
905 cand = cand[
'source']
906 xPos = cand.getCentroid()[0]
907 yPos = cand.getCentroid()[1]
908 cand = diffimLib.makeKernelCandidate(xPos, yPos, tmi, smi, policy)
910 self.log.
debug(
"Candidate %d at %f, %f", cand.getId(), cand.getXCenter(), cand.getYCenter())
911 kernelCellSet.insertCandidate(cand)
915 def _validateSize(self, templateMaskedImage, scienceMaskedImage):
916 """Return True if two image-like objects are the same size. 918 return templateMaskedImage.getDimensions() == scienceMaskedImage.getDimensions()
920 def _validateWcs(self, templateExposure, scienceExposure):
921 """Return True if the WCS of the two Exposures have the same origin and extent. 923 templateWcs = templateExposure.getWcs()
924 scienceWcs = scienceExposure.getWcs()
925 templateBBox = templateExposure.getBBox()
926 scienceBBox = scienceExposure.getBBox()
929 templateOrigin = templateWcs.pixelToSky(
afwGeom.Point2D(templateBBox.getBegin()))
930 scienceOrigin = scienceWcs.pixelToSky(
afwGeom.Point2D(scienceBBox.getBegin()))
933 templateLimit = templateWcs.pixelToSky(
afwGeom.Point2D(templateBBox.getEnd()))
934 scienceLimit = scienceWcs.pixelToSky(
afwGeom.Point2D(scienceBBox.getEnd()))
936 self.log.
info(
"Template Wcs : %f,%f -> %f,%f",
937 templateOrigin[0], templateOrigin[1],
938 templateLimit[0], templateLimit[1])
939 self.log.
info(
"Science Wcs : %f,%f -> %f,%f",
940 scienceOrigin[0], scienceOrigin[1],
941 scienceLimit[0], scienceLimit[1])
943 templateBBox =
afwGeom.Box2D(templateOrigin.getPosition(afwGeom.degrees),
944 templateLimit.getPosition(afwGeom.degrees))
945 scienceBBox =
afwGeom.Box2D(scienceOrigin.getPosition(afwGeom.degrees),
946 scienceLimit.getPosition(afwGeom.degrees))
947 if not (templateBBox.overlaps(scienceBBox)):
948 raise RuntimeError(
"Input images do not overlap at all")
950 if ((templateOrigin != scienceOrigin)
or 951 (templateLimit != scienceLimit)
or 952 (templateExposure.getDimensions() != scienceExposure.getDimensions())):
957 subtractAlgorithmRegistry = pexConfig.makeRegistry(
958 doc=
"A registry of subtraction algorithms for use as a subtask in imageDifference",
961 subtractAlgorithmRegistry.register(
'al', ImagePsfMatchTask)
A floating-point coordinate rectangle geometry.
Class for storing ordered metadata with comments.
def makeKernelBasisList(config, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, metadata=None)
def getSelectSources(self, exposure, sigma=None, doSmooth=True, idFactory=None)
def _validateSize(self, templateMaskedImage, scienceMaskedImage)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
daf::base::PropertySet * set
def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList)
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.
def _adaptCellSize(self, candidateList)
A collection of SpatialCells covering an entire image.
def __init__(self, args, kwargs)
def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None)
def _solve(self, kernelCellSet, basisList, returnOnExcept=False)
def matchExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None, doWarping=True, convolveTemplate=True)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def makeCandidateList(self, templateExposure, scienceExposure, kernelSize, candidateList=None)
Subtract the background from an exposure.
std::shared_ptr< TransformPoint2ToPoint2 > makeWcsPairTransform(SkyWcs const &src, SkyWcs const &dst)
A Transform obtained by putting two SkyWcs objects "back to back".
def subtractMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None)
Record class that contains measurements made on a single exposure.
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)
Old, deprecated version of convolve.
def getFwhmPix(self, psf)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
def subtractExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None, doWarping=True, convolveTemplate=True)
def _validateWcs(self, templateExposure, scienceExposure)
A Psf class that maps an arbitrary Psf through a coordinate transformation.