21 from __future__
import absolute_import, division, print_function
23 __all__ = [
"ImagePsfMatchConfig",
"ImagePsfMatchTask"]
33 import lsst.pipe.base
as pipeBase
34 from lsst.meas.algorithms
import SourceDetectionTask, SubtractBackgroundTask
36 from .makeKernelBasisList
import makeKernelBasisList
37 from .psfMatch
import PsfMatchTask, PsfMatchConfigDF, PsfMatchConfigAL
38 from .
import utils
as diUtils
39 from .
import diffimLib
40 from .
import diffimTools
43 sigma2fwhm = 2. * np.sqrt(2. * np.log(2.))
47 """!Configuration for image-to-image Psf matching""" 48 kernel = pexConfig.ConfigChoiceField(
56 selectDetection = pexConfig.ConfigurableField(
57 target=SourceDetectionTask,
58 doc=
"Initial detections used to feed stars to kernel fitting",
60 selectMeasurement = pexConfig.ConfigurableField(
61 target=SingleFrameMeasurementTask,
62 doc=
"Initial measurements used to feed stars to kernel fitting",
72 self.
selectMeasurement.algorithms.names = (
'base_SdssCentroid',
'base_PsfFlux',
'base_PixelFlags',
73 'base_SdssShape',
'base_GaussianFlux',
'base_SkyCoord')
88 \anchor ImagePsfMatchTask_ 90 \brief Psf-match two MaskedImages or Exposures using the sources in the images 92 \section ip_diffim_imagepsfmatch_Contents Contents 94 - \ref ip_diffim_imagepsfmatch_Purpose 95 - \ref ip_diffim_imagepsfmatch_Initialize 96 - \ref ip_diffim_imagepsfmatch_IO 97 - \ref ip_diffim_imagepsfmatch_Config 98 - \ref ip_diffim_imagepsfmatch_Metadata 99 - \ref ip_diffim_imagepsfmatch_Debug 100 - \ref ip_diffim_imagepsfmatch_Example 102 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 104 \section ip_diffim_imagepsfmatch_Purpose Description 106 Build a Psf-matching kernel using two input images, either as MaskedImages (in which case they need 107 to be astrometrically aligned) or Exposures (in which case astrometric alignment will happen by 108 default but may be turned off). This requires a list of input Sources which may be provided 109 by the calling Task; if not, the Task will perform a coarse source detection and selection for this purpose. 110 Sources are vetted for signal-to-noise and masked pixels (in both the template and science image), and 111 substamps around each acceptable source are extracted and used to create an instance of KernelCandidate. 112 Each KernelCandidate is then placed within a lsst.afw.math.SpatialCellSet, which is used by an ensemble of 113 lsst.afw.math.CandidateVisitor instances to build the Psf-matching kernel. These visitors include, in 114 the order that they are called: BuildSingleKernelVisitor, KernelSumVisitor, BuildSpatialKernelVisitor, 115 and AssessSpatialKernelVisitor. 117 Sigma clipping of KernelCandidates is performed as follows: 118 - BuildSingleKernelVisitor, using the substamp diffim residuals from the per-source kernel fit, 119 if PsfMatchConfig.singleKernelClipping is True 120 - KernelSumVisitor, using the mean and standard deviation of the kernel sum from all candidates, 121 if PsfMatchConfig.kernelSumClipping is True 122 - AssessSpatialKernelVisitor, using the substamp diffim ressiduals from the spatial kernel fit, 123 if PsfMatchConfig.spatialKernelClipping is True 125 The actual solving for the kernel (and differential background model) happens in 126 lsst.ip.diffim.PsfMatchTask._solve. This involves a loop over the SpatialCellSet that first builds the 127 per-candidate matching kernel for the requested number of KernelCandidates per cell 128 (PsfMatchConfig.nStarPerCell). The quality of this initial per-candidate difference image is examined, 129 using moments of the pixel residuals in the difference image normalized by the square root of the variance 130 (i.e. sigma); ideally this should follow a normal (0, 1) distribution, but the rejection thresholds are set 131 by the config (PsfMatchConfig.candidateResidualMeanMax and PsfMatchConfig.candidateResidualStdMax). 132 All candidates that pass this initial build are then examined en masse to find the 133 mean/stdev of the kernel sums across all candidates. 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, are also rejected. 135 This threshold is defined by PsfMatchConfig.maxKsumSigma. Finally, a spatial model is built using all 136 currently-acceptable candidates, and the spatial model used to derive a second set of (spatial) residuals 137 which are again used to reject bad candidates, using the same thresholds as above. 139 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 141 \section ip_diffim_imagepsfmatch_Initialize Task initialization 143 \copydoc \_\_init\_\_ 145 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 147 \section ip_diffim_imagepsfmatch_IO Invoking the Task 149 There is no run() method for this Task. Instead there are 4 methods that 150 may be used to invoke the Psf-matching. These are 151 \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchMaskedImages matchMaskedImages\endlink, 152 \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractMaskedImages subtractMaskedImages\endlink, 153 \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchExposures matchExposures\endlink, and 154 \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractExposures subtractExposures\endlink. 156 The methods that operate on lsst.afw.image.MaskedImage require that the images already be astrometrically 157 aligned, and are the same shape. The methods that operate on lsst.afw.image.Exposure allow for the 158 input images to be misregistered and potentially be different sizes; by default a 159 lsst.afw.math.LanczosWarpingKernel is used to perform the astrometric alignment. The methods 160 that "match" images return a Psf-matched image, while the methods that "subtract" images 161 return a Psf-matched and template subtracted image. 163 See each method's returned lsst.pipe.base.Struct for more details. 165 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 167 \section ip_diffim_imagepsfmatch_Config Configuration parameters 169 See \ref ImagePsfMatchConfig 171 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 173 \section ip_diffim_imagepsfmatch_Metadata Quantities set in Metadata 175 See \ref ip_diffim_psfmatch_Metadata "PsfMatchTask" 177 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 179 \section ip_diffim_imagepsfmatch_Debug Debug variables 181 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a 182 flag \c -d/--debug to import \b debug.py from your \c PYTHONPATH. The relevant contents of debug.py 183 for this Task include: 189 di = lsstDebug.getInfo(name) 190 if name == "lsst.ip.diffim.psfMatch": 191 di.display = True # enable debug output 192 di.maskTransparency = 80 # ds9 mask transparency 193 di.displayCandidates = True # show all the candidates and residuals 194 di.displayKernelBasis = False # show kernel basis functions 195 di.displayKernelMosaic = True # show kernel realized across the image 196 di.plotKernelSpatialModel = False # show coefficients of spatial model 197 di.showBadCandidates = True # show the bad candidates (red) along with good (green) 198 elif name == "lsst.ip.diffim.imagePsfMatch": 199 di.display = True # enable debug output 200 di.maskTransparency = 30 # ds9 mask transparency 201 di.displayTemplate = True # show full (remapped) template 202 di.displaySciIm = True # show science image to match to 203 di.displaySpatialCells = True # show spatial cells 204 di.displayDiffIm = True # show difference image 205 di.showBadCandidates = True # show the bad candidates (red) along with good (green) 206 elif name == "lsst.ip.diffim.diaCatalogSourceSelector": 207 di.display = False # enable debug output 208 di.maskTransparency = 30 # ds9 mask transparency 209 di.displayExposure = True # show exposure with candidates indicated 210 di.pauseAtEnd = False # pause when done 212 lsstDebug.Info = DebugInfo 216 Note that if you want addional logging info, you may add to your scripts: 218 import lsst.log.utils as logUtils 219 logUtils.traceSetAt("ip.diffim", 4) 222 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 224 \section ip_diffim_imagepsfmatch_Example A complete example of using ImagePsfMatchTask 226 This code is imagePsfMatchTask.py in the examples directory, and can be run as \em e.g. 228 examples/imagePsfMatchTask.py --debug 229 examples/imagePsfMatchTask.py --debug --mode="matchExposures" 230 examples/imagePsfMatchTask.py --debug --template /path/to/templateExp.fits --science /path/to/scienceExp.fits 233 \dontinclude imagePsfMatchTask.py 234 Create a subclass of ImagePsfMatchTask that allows us to either match exposures, or subtract exposures: 235 \skip MyImagePsfMatchTask 236 @until self.subtractExposures 238 And allow the user the freedom to either run the script in default mode, or point to their own images on disk. 239 Note that these images must be readable as an lsst.afw.image.Exposure: 243 We have enabled some minor display debugging in this script via the --debug option. However, if you 244 have an lsstDebug debug.py in your PYTHONPATH you will get additional debugging displays. The following 245 block checks for this script: 249 \dontinclude imagePsfMatchTask.py 250 Finally, we call a run method that we define below. First set up a Config and modify some of the parameters. 251 E.g. use an "Alard-Lupton" sum-of-Gaussian basis, fit for a differential background, and use low order spatial 252 variation in the kernel and background: 254 @until spatialBgOrder 256 Make sure the images (if any) that were sent to the script exist on disk and are readable. If no images 257 are sent, make some fake data up for the sake of this example script (have a look at the code if you want 258 more details on generateFakeImages): 262 Create and run the Task: 266 And finally provide some optional debugging displays: 268 @until result.subtractedExposure 269 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 272 ConfigClass = ImagePsfMatchConfig
275 """!Create the ImagePsfMatchTask 277 \param *args arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__ 278 \param **kwargs keyword arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__ 280 Upon initialization, the kernel configuration is defined by self.config.kernel.active. 281 The task creates an lsst.afw.math.Warper from the subConfig self.config.kernel.active.warpingConfig. 282 A schema for the selection and measurement of candidate lsst.ip.diffim.KernelCandidates is 283 defined, and used to initize subTasks selectDetection (for candidate detection) and selectMeasurement 284 (for candidate measurement). 286 PsfMatchTask.__init__(self, *args, **kwargs)
288 self.
_warper = afwMath.Warper.fromConfig(self.
kConfig.warpingConfig)
291 self.
background = SubtractBackgroundTask(config=self.
kConfig.afwBackgroundConfig, name=
"background",
295 self.makeSubtask(
"selectDetection", schema=self.
selectSchema)
299 """!Return the FWHM in pixels of a Psf""" 300 sigPix = psf.computeShape().getDeterminantRadius()
301 return sigPix * sigma2fwhm
305 templateFwhmPix=None, scienceFwhmPix=None,
306 candidateList=None, doWarping=True, convolveTemplate=True):
307 """!Warp and PSF-match an exposure to the reference 309 Do the following, in order: 310 - Warp templateExposure to match scienceExposure, 311 if doWarping True and their WCSs do not already match 312 - Determine a PSF matching kernel and differential background model 313 that matches templateExposure to scienceExposure 314 - Convolve templateExposure by PSF matching kernel 316 @param templateExposure: Exposure to warp and PSF-match to the reference masked image 317 @param scienceExposure: Exposure whose WCS and PSF are to be matched to 318 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve) 319 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image 320 @param candidateList: a list of footprints/maskedImages for kernel candidates; 321 if None then source detection is run. 322 - Currently supported: list of Footprints or measAlg.PsfCandidateF 323 @param doWarping: what to do if templateExposure's and scienceExposure's WCSs do not match: 324 - if True then warp templateExposure to match scienceExposure 325 - if False then raise an Exception 326 @param convolveTemplate: convolve the template image or the science image 327 - if True, templateExposure is warped if doWarping, templateExposure is convolved 328 - if False, templateExposure is warped if doWarping, scienceExposure is convolved 330 @return a pipeBase.Struct containing these fields: 331 - matchedImage: the PSF-matched exposure = 332 warped templateExposure convolved by psfMatchingKernel. This has: 333 - the same parent bbox, Wcs and Calib as scienceExposure 334 - the same filter as templateExposure 335 - no Psf (because the PSF-matching process does not compute one) 336 - psfMatchingKernel: the PSF matching kernel 337 - backgroundModel: differential background model 338 - kernelCellSet: SpatialCellSet used to solve for the PSF matching kernel 340 Raise a RuntimeError if doWarping is False and templateExposure's and scienceExposure's 343 if not self.
_validateWcs(templateExposure, scienceExposure):
345 self.log.info(
"Astrometrically registering template to science image")
346 templatePsf = templateExposure.getPsf()
347 templateExposure = self.
_warper.warpExposure(scienceExposure.getWcs(),
349 destBBox=scienceExposure.getBBox())
350 templateExposure.setPsf(templatePsf)
352 self.log.error(
"ERROR: Input images not registered")
353 raise RuntimeError(
"Input images not registered")
355 if templateFwhmPix
is None:
356 if not templateExposure.hasPsf():
357 self.log.warn(
"No estimate of Psf FWHM for template image")
359 templateFwhmPix = self.
getFwhmPix(templateExposure.getPsf())
360 self.log.info(
"templateFwhmPix: {}".format(templateFwhmPix))
362 if scienceFwhmPix
is None:
363 if not scienceExposure.hasPsf():
364 self.log.warn(
"No estimate of Psf FWHM for science image")
366 scienceFwhmPix = self.
getFwhmPix(scienceExposure.getPsf())
367 self.log.info(
"scienceFwhmPix: {}".format(scienceFwhmPix))
370 candidateList = self.
makeCandidateList(templateExposure, scienceExposure, kernelSize, candidateList)
374 templateExposure.getMaskedImage(), scienceExposure.getMaskedImage(), candidateList,
375 templateFwhmPix=templateFwhmPix, scienceFwhmPix=scienceFwhmPix)
378 scienceExposure.getMaskedImage(), templateExposure.getMaskedImage(), candidateList,
379 templateFwhmPix=scienceFwhmPix, scienceFwhmPix=templateFwhmPix)
382 psfMatchedExposure.setFilter(templateExposure.getFilter())
383 psfMatchedExposure.setCalib(scienceExposure.getCalib())
384 results.warpedExposure = templateExposure
385 results.matchedExposure = psfMatchedExposure
389 def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList,
390 templateFwhmPix=None, scienceFwhmPix=None):
391 """!PSF-match a MaskedImage (templateMaskedImage) to a reference MaskedImage (scienceMaskedImage) 393 Do the following, in order: 394 - Determine a PSF matching kernel and differential background model 395 that matches templateMaskedImage to scienceMaskedImage 396 - Convolve templateMaskedImage by the PSF matching kernel 398 @param templateMaskedImage: masked image to PSF-match to the reference masked image; 399 must be warped to match the reference masked image 400 @param scienceMaskedImage: maskedImage whose PSF is to be matched to 401 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve) 402 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image 403 @param candidateList: a list of footprints/maskedImages for kernel candidates; 404 if None then source detection is run. 405 - Currently supported: list of Footprints or measAlg.PsfCandidateF 407 @return a pipeBase.Struct containing these fields: 408 - psfMatchedMaskedImage: the PSF-matched masked image = 409 templateMaskedImage convolved with psfMatchingKernel. 410 This has the same xy0, dimensions and wcs as scienceMaskedImage. 411 - psfMatchingKernel: the PSF matching kernel 412 - backgroundModel: differential background model 413 - kernelCellSet: SpatialCellSet used to solve for the PSF matching kernel 415 Raise a RuntimeError if input images have different dimensions 422 displaySpatialCells =
lsstDebug.Info(__name__).displaySpatialCells
424 if not maskTransparency:
427 ds9.setMaskTransparency(maskTransparency)
429 if not candidateList:
430 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
432 if not self.
_validateSize(templateMaskedImage, scienceMaskedImage):
433 self.log.error(
"ERROR: Input images different size")
434 raise RuntimeError(
"Input images different size")
436 if display
and displayTemplate:
437 ds9.mtv(templateMaskedImage, frame=lsstDebug.frame, title=
"Image to convolve")
440 if display
and displaySciIm:
441 ds9.mtv(scienceMaskedImage, frame=lsstDebug.frame, title=
"Image to not convolve")
448 if display
and displaySpatialCells:
449 diUtils.showKernelSpatialCells(scienceMaskedImage, kernelCellSet,
450 symb=
"o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW, ctypeBad=ds9.RED,
451 size=4, frame=lsstDebug.frame, title=
"Image to not convolve")
454 if templateFwhmPix
and scienceFwhmPix:
455 self.log.info(
"Matching Psf FWHM %.2f -> %.2f pix", templateFwhmPix, scienceFwhmPix)
457 if self.
kConfig.useBicForKernelBasis:
462 bicDegrees = nbe(tmpKernelCellSet, self.log)
464 alardDegGauss=bicDegrees[0], metadata=self.metadata)
468 metadata=self.metadata)
470 spatialSolution, psfMatchingKernel, backgroundModel = self.
_solve(kernelCellSet, basisList)
472 psfMatchedMaskedImage = afwImage.MaskedImageF(templateMaskedImage.getBBox())
474 afwMath.convolve(psfMatchedMaskedImage, templateMaskedImage, psfMatchingKernel, doNormalize)
475 return pipeBase.Struct(
476 matchedImage=psfMatchedMaskedImage,
477 psfMatchingKernel=psfMatchingKernel,
478 backgroundModel=backgroundModel,
479 kernelCellSet=kernelCellSet,
484 templateFwhmPix=None, scienceFwhmPix=None,
485 candidateList=None, doWarping=True, convolveTemplate=True):
486 """!Register, Psf-match and subtract two Exposures 488 Do the following, in order: 489 - Warp templateExposure to match scienceExposure, if their WCSs do not already match 490 - Determine a PSF matching kernel and differential background model 491 that matches templateExposure to scienceExposure 492 - PSF-match templateExposure to scienceExposure 493 - Compute subtracted exposure (see return values for equation). 495 @param templateExposure: exposure to PSF-match to scienceExposure 496 @param scienceExposure: reference Exposure 497 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve) 498 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image 499 @param candidateList: a list of footprints/maskedImages for kernel candidates; 500 if None then source detection is run. 501 - Currently supported: list of Footprints or measAlg.PsfCandidateF 502 @param doWarping: what to do if templateExposure's and scienceExposure's WCSs do not match: 503 - if True then warp templateExposure to match scienceExposure 504 - if False then raise an Exception 505 @param convolveTemplate: convolve the template image or the science image 506 - if True, templateExposure is warped if doWarping, templateExposure is convolved 507 - if False, templateExposure is warped if doWarping, scienceExposure is convolved 509 @return a pipeBase.Struct containing these fields: 510 - subtractedExposure: subtracted Exposure = scienceExposure - (matchedImage + backgroundModel) 511 - matchedImage: templateExposure after warping to match templateExposure (if doWarping true), 512 and convolving with psfMatchingKernel 513 - psfMatchingKernel: PSF matching kernel 514 - backgroundModel: differential background model 515 - kernelCellSet: SpatialCellSet used to determine PSF matching kernel 518 templateExposure=templateExposure,
519 scienceExposure=scienceExposure,
520 templateFwhmPix=templateFwhmPix,
521 scienceFwhmPix=scienceFwhmPix,
522 candidateList=candidateList,
524 convolveTemplate=convolveTemplate
527 subtractedExposure = afwImage.ExposureF(scienceExposure,
True)
529 subtractedMaskedImage = subtractedExposure.getMaskedImage()
530 subtractedMaskedImage -= results.matchedExposure.getMaskedImage()
531 subtractedMaskedImage -= results.backgroundModel
533 subtractedExposure.setMaskedImage(results.warpedExposure.getMaskedImage())
534 subtractedMaskedImage = subtractedExposure.getMaskedImage()
535 subtractedMaskedImage -= results.matchedExposure.getMaskedImage()
536 subtractedMaskedImage -= results.backgroundModel
539 subtractedMaskedImage *= -1
542 subtractedMaskedImage /= results.psfMatchingKernel.computeImage(
543 afwImage.ImageD(results.psfMatchingKernel.getDimensions()),
False)
549 if not maskTransparency:
552 ds9.setMaskTransparency(maskTransparency)
553 if display
and displayDiffIm:
554 ds9.mtv(templateExposure, frame=lsstDebug.frame, title=
"Template")
556 ds9.mtv(results.matchedExposure, frame=lsstDebug.frame, title=
"Matched template")
558 ds9.mtv(scienceExposure, frame=lsstDebug.frame, title=
"Science Image")
560 ds9.mtv(subtractedExposure, frame=lsstDebug.frame, title=
"Difference Image")
563 results.subtractedExposure = subtractedExposure
568 templateFwhmPix=None, scienceFwhmPix=None):
569 """!Psf-match and subtract two MaskedImages 571 Do the following, in order: 572 - PSF-match templateMaskedImage to scienceMaskedImage 573 - Determine the differential background 574 - Return the difference: scienceMaskedImage - 575 ((warped templateMaskedImage convolved with psfMatchingKernel) + backgroundModel) 577 @param templateMaskedImage: MaskedImage to PSF-match to scienceMaskedImage 578 @param scienceMaskedImage: reference MaskedImage 579 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve) 580 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image 581 @param candidateList: a list of footprints/maskedImages for kernel candidates; 582 if None then source detection is run. 583 - Currently supported: list of Footprints or measAlg.PsfCandidateF 585 @return a pipeBase.Struct containing these fields: 586 - subtractedMaskedImage = scienceMaskedImage - (matchedImage + backgroundModel) 587 - matchedImage: templateMaskedImage convolved with psfMatchingKernel 588 - psfMatchingKernel: PSF matching kernel 589 - backgroundModel: differential background model 590 - kernelCellSet: SpatialCellSet used to determine PSF matching kernel 592 if not candidateList:
593 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
596 templateMaskedImage=templateMaskedImage,
597 scienceMaskedImage=scienceMaskedImage,
598 candidateList=candidateList,
599 templateFwhmPix=templateFwhmPix,
600 scienceFwhmPix=scienceFwhmPix,
603 subtractedMaskedImage = afwImage.MaskedImageF(scienceMaskedImage,
True)
604 subtractedMaskedImage -= results.matchedImage
605 subtractedMaskedImage -= results.backgroundModel
606 results.subtractedMaskedImage = subtractedMaskedImage
612 if not maskTransparency:
615 ds9.setMaskTransparency(maskTransparency)
616 if display
and displayDiffIm:
617 ds9.mtv(subtractedMaskedImage, frame=lsstDebug.frame)
623 """!Get sources to use for Psf-matching 625 This method runs detection and measurement on an exposure. 626 The returned set of sources will be used as candidates for 629 @param exposure: Exposure on which to run detection/measurement 630 @param sigma: Detection threshold 631 @param doSmooth: Whether or not to smooth the Exposure with Psf before detection 632 @param idFactory: Factory for the generation of Source ids 634 @return source catalog containing candidates for the Psf-matching 638 table = afwTable.SourceTable.make(self.
selectSchema, idFactory)
641 mi = exposure.getMaskedImage()
643 imArr = mi.getImage().getArray()
644 maskArr = mi.getMask().getArray()
645 miArr = np.ma.masked_array(imArr, mask=maskArr)
647 bkgd = self.
background.fitBackground(mi).getImageF()
649 self.log.warn(
"Failed to get background model. Falling back to median background estimation")
650 bkgd = np.ma.extras.median(miArr)
656 detRet = self.selectDetection.makeSourceCatalog(
662 selectSources = detRet.sources
663 self.selectMeasurement.run(measCat=selectSources, exposure=exposure)
671 """!Make a list of acceptable KernelCandidates 673 Accept or generate a list of candidate sources for 674 Psf-matching, and examine the Mask planes in both of the 675 images for indications of bad pixels 677 @param templateExposure: Exposure that will be convolved 678 @param scienceExposure: Exposure that will be matched-to 679 @param kernelSize: Dimensions of the Psf-matching Kernel, used to grow detection footprints 680 @param candidateList: List of Sources to examine. Elements must be of type afw.table.Source 681 or a type that wraps a Source and has a getSource() method, such as 682 meas.algorithms.PsfCandidateF. 684 @return a list of dicts having a "source" and "footprint" 685 field for the Sources deemed to be appropriate for Psf 688 if candidateList
is None:
691 if len(candidateList) < 1:
692 raise RuntimeError(
"No candidates in candidateList")
694 listTypes = set(type(x)
for x
in candidateList)
695 if len(listTypes) > 1:
696 raise RuntimeError(
"Candidate list contains mixed types: %s" % [l
for l
in listTypes])
698 if not isinstance(candidateList[0], afwTable.SourceRecord):
700 candidateList[0].getSource()
701 except Exception
as e:
702 raise RuntimeError(
"Candidate List is of type: %s. " % (type(candidateList[0])) +
703 "Can only make candidate list from list of afwTable.SourceRecords, " +
704 "measAlg.PsfCandidateF or other type with a getSource() method: %s" % (e))
705 candidateList = [c.getSource()
for c
in candidateList]
707 candidateList = diffimTools.sourceToFootprintList(candidateList,
708 templateExposure, scienceExposure,
712 if len(candidateList) == 0:
713 raise RuntimeError(
"Cannot find any objects suitable for KernelCandidacy")
717 def _adaptCellSize(self, candidateList):
718 """! NOT IMPLEMENTED YET""" 721 def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList):
722 """!Build a SpatialCellSet for use with the solve method 724 @param templateMaskedImage: MaskedImage to PSF-matched to scienceMaskedImage 725 @param scienceMaskedImage: reference MaskedImage 726 @param candidateList: a list of footprints/maskedImages for kernel candidates; 727 if None then source detection is run. 728 - Currently supported: list of Footprints or measAlg.PsfCandidateF 730 @return kernelCellSet: a SpatialCellSet for use with self._solve 732 if not candidateList:
733 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
739 sizeCellX, sizeCellY)
741 policy = pexConfig.makePolicy(self.
kConfig)
743 for cand
in candidateList:
744 bbox = cand[
'footprint'].getBBox()
746 tmi = afwImage.MaskedImageF(templateMaskedImage, bbox)
747 smi = afwImage.MaskedImageF(scienceMaskedImage, bbox)
748 cand = diffimLib.makeKernelCandidate(cand[
'source'], tmi, smi, policy)
750 self.log.debug(
"Candidate %d at %f, %f", cand.getId(), cand.getXCenter(), cand.getYCenter())
751 kernelCellSet.insertCandidate(cand)
755 def _validateSize(self, templateMaskedImage, scienceMaskedImage):
756 """!Return True if two image-like objects are the same size 758 return templateMaskedImage.getDimensions() == scienceMaskedImage.getDimensions()
760 def _validateWcs(self, templateExposure, scienceExposure):
761 """!Return True if the WCS of the two Exposures have the same origin and extent 763 templateWcs = templateExposure.getWcs()
764 scienceWcs = scienceExposure.getWcs()
765 templateBBox = templateExposure.getBBox()
766 scienceBBox = scienceExposure.getBBox()
769 templateOrigin = templateWcs.pixelToSky(
afwGeom.Point2D(templateBBox.getBegin()))
770 scienceOrigin = scienceWcs.pixelToSky(
afwGeom.Point2D(scienceBBox.getBegin()))
773 templateLimit = templateWcs.pixelToSky(
afwGeom.Point2D(templateBBox.getEnd()))
774 scienceLimit = scienceWcs.pixelToSky(
afwGeom.Point2D(scienceBBox.getEnd()))
776 self.log.info(
"Template Wcs : %f,%f -> %f,%f",
777 templateOrigin[0], templateOrigin[1],
778 templateLimit[0], templateLimit[1])
779 self.log.info(
"Science Wcs : %f,%f -> %f,%f",
780 scienceOrigin[0], scienceOrigin[1],
781 scienceLimit[0], scienceLimit[1])
783 templateBBox =
afwGeom.Box2D(templateOrigin.getPosition(afwGeom.degrees),
784 templateLimit.getPosition(afwGeom.degrees))
785 scienceBBox =
afwGeom.Box2D(scienceOrigin.getPosition(afwGeom.degrees),
786 scienceLimit.getPosition(afwGeom.degrees))
787 if not (templateBBox.overlaps(scienceBBox)):
788 raise RuntimeError(
"Input images do not overlap at all")
790 if ((templateOrigin != scienceOrigin)
or 791 (templateLimit != scienceLimit)
or 792 (templateExposure.getDimensions() != scienceExposure.getDimensions())):
797 subtractAlgorithmRegistry = pexConfig.makeRegistry(
798 doc=
"A registry of subtraction algorithms for use as a subtask in imageDifference",
801 subtractAlgorithmRegistry.register(
'al', ImagePsfMatchTask)
Base class for Psf Matching; should not be called directly.
Configuration for image-to-image Psf matching.
def makeKernelBasisList(config, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, metadata=None)
def getSelectSources(self, exposure, sigma=None, doSmooth=True, idFactory=None)
Get sources to use for Psf-matching.
def _validateSize(self, templateMaskedImage, scienceMaskedImage)
Return True if two image-like objects are the same size.
Psf-match two MaskedImages or Exposures using the sources in the images.
def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList)
Build a SpatialCellSet for use with the solve method.
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 >())
def _adaptCellSize(self, candidateList)
NOT IMPLEMENTED YET.
def __init__(self, args, kwargs)
Create the ImagePsfMatchTask.
def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None)
PSF-match a MaskedImage (templateMaskedImage) to a reference MaskedImage (scienceMaskedImage) ...
def _solve(self, kernelCellSet, basisList, returnOnExcept=False)
Solve for the PSF matching kernel.
def matchExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None, doWarping=True, convolveTemplate=True)
Warp and PSF-match an exposure to the reference.
def makeCandidateList(self, templateExposure, scienceExposure, kernelSize, candidateList=None)
Make a list of acceptable KernelCandidates.
def subtractMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None)
Psf-match and subtract two MaskedImages.
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)
def getFwhmPix(self, psf)
Return the FWHM in pixels of a Psf.
def subtractExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None, doWarping=True, convolveTemplate=True)
Register, Psf-match and subtract two Exposures.
def _validateWcs(self, templateExposure, scienceExposure)
Return True if the WCS of the two Exposures have the same origin and extent.