32 from .makeKernelBasisList
import makeKernelBasisList
33 from .psfMatch
import PsfMatchTask, PsfMatchConfigDF, PsfMatchConfigAL
34 from .
import utils
as diUtils
35 from .
import diffimLib
36 from .
import diffimTools
39 sigma2fwhm = 2. * np.sqrt(2. * np.log(2.))
42 """!Configuration for image-to-image Psf matching"""
43 kernel = pexConfig.ConfigChoiceField(
51 selectDetection = pexConfig.ConfigurableField(
52 target=SourceDetectionTask,
53 doc=
"Initial detections used to feed stars to kernel fitting",
55 selectMeasurement = pexConfig.ConfigurableField(
56 target=SingleFrameMeasurementTask,
57 doc=
"Initial measurements used to feed stars to kernel fitting",
62 self.selectDetection.reEstimateBackground =
False
63 self.selectDetection.thresholdValue = 10.0
66 self.selectMeasurement.algorithms.names.clear()
67 self.selectMeasurement.algorithms.names = (
'base_SdssCentroid',
'base_PsfFlux',
'base_PixelFlags',
68 'base_SdssShape',
'base_GaussianFlux',
'base_SkyCoord')
69 self.selectMeasurement.slots.modelFlux =
None
70 self.selectMeasurement.slots.apFlux =
None
71 self.selectMeasurement.slots.calibFlux =
None
82 \anchor ImagePsfMatchTask_
84 \brief Psf-match two MaskedImages or Exposures using the sources in the images
86 \section ip_diffim_imagepsfmatch_Contents Contents
88 - \ref ip_diffim_imagepsfmatch_Purpose
89 - \ref ip_diffim_imagepsfmatch_Initialize
90 - \ref ip_diffim_imagepsfmatch_IO
91 - \ref ip_diffim_imagepsfmatch_Config
92 - \ref ip_diffim_imagepsfmatch_Metadata
93 - \ref ip_diffim_imagepsfmatch_Debug
94 - \ref ip_diffim_imagepsfmatch_Example
96 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
98 \section ip_diffim_imagepsfmatch_Purpose Description
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 and selection for this purpose.
104 Sources are vetted for signal-to-noise and masked pixels (in both the template and science image), and
105 substamps around each acceptable source are extracted and used to create an instance of KernelCandidate.
106 Each KernelCandidate is then placed within a lsst.afw.math.SpatialCellSet, which is used by an ensemble of
107 lsst.afw.math.CandidateVisitor instances to build the Psf-matching kernel. These visitors include, in
108 the order that they are called: BuildSingleKernelVisitor, KernelSumVisitor, BuildSpatialKernelVisitor,
109 and AssessSpatialKernelVisitor.
111 Sigma clipping of KernelCandidates is performed as follows:
112 - BuildSingleKernelVisitor, using the substamp diffim residuals from the per-source kernel fit,
113 if PsfMatchConfig.singleKernelClipping is True
114 - KernelSumVisitor, using the mean and standard deviation of the kernel sum from all candidates,
115 if PsfMatchConfig.kernelSumClipping is True
116 - AssessSpatialKernelVisitor, using the substamp diffim ressiduals from the spatial kernel fit,
117 if PsfMatchConfig.spatialKernelClipping is True
119 The actual solving for the kernel (and differential background model) happens in
120 lsst.ip.diffim.PsfMatchTask._solve. This involves a loop over the SpatialCellSet that first builds the
121 per-candidate matching kernel for the requested number of KernelCandidates per cell
122 (PsfMatchConfig.nStarPerCell). The quality of this initial per-candidate difference image is examined,
123 using moments of the pixel residuals in the difference image normalized by the square root of the variance
124 (i.e. sigma); ideally this should follow a normal (0, 1) distribution, but the rejection thresholds are set
125 by the config (PsfMatchConfig.candidateResidualMeanMax and PsfMatchConfig.candidateResidualStdMax).
126 All candidates that pass this initial build are then examined en masse to find the
127 mean/stdev of the kernel sums across all candidates. Objects that are significantly above or below the mean,
128 typically due to variability or sources that are saturated in one image but not the other, are also rejected.
129 This threshold is defined by PsfMatchConfig.maxKsumSigma. Finally, a spatial model is built using all
130 currently-acceptable candidates, and the spatial model used to derive a second set of (spatial) residuals
131 which are again used to reject bad candidates, using the same thresholds as above.
133 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
135 \section ip_diffim_imagepsfmatch_Initialize Task initialization
137 \copydoc \_\_init\_\_
139 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
141 \section ip_diffim_imagepsfmatch_IO Invoking the Task
143 There is no run() method for this Task. Instead there are 4 methods that
144 may be used to invoke the Psf-matching. These are
145 \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchMaskedImages matchMaskedImages\endlink,
146 \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractMaskedImages subtractMaskedImages\endlink,
147 \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchExposures matchExposures\endlink, and
148 \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractExposures subtractExposures\endlink.
150 The methods that operate on lsst.afw.image.MaskedImage require that the images already be astrometrically
151 aligned, and are the same shape. The methods that operate on lsst.afw.image.Exposure allow for the
152 input images to be misregistered and potentially be different sizes; by default a
153 lsst.afw.math.LanczosWarpingKernel is used to perform the astrometric alignment. The methods
154 that "match" images return a Psf-matched image, while the methods that "subtract" images
155 return a Psf-matched and template subtracted image.
157 See each method's returned lsst.pipe.base.Struct for more details.
159 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
161 \section ip_diffim_imagepsfmatch_Config Configuration parameters
163 See \ref ImagePsfMatchConfig
165 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
167 \section ip_diffim_imagepsfmatch_Metadata Quantities set in Metadata
169 See \ref ip_diffim_psfmatch_Metadata "PsfMatchTask"
171 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
173 \section ip_diffim_imagepsfmatch_Debug Debug variables
175 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
176 flag \c -d/--debug to import \b debug.py from your \c PYTHONPATH. The relevant contents of debug.py
177 for this Task include:
183 di = lsstDebug.getInfo(name)
184 if name == "lsst.ip.diffim.psfMatch":
185 di.display = True # enable debug output
186 di.maskTransparency = 80 # ds9 mask transparency
187 di.displayCandidates = True # show all the candidates and residuals
188 di.displayKernelBasis = False # show kernel basis functions
189 di.displayKernelMosaic = True # show kernel realized across the image
190 di.plotKernelSpatialModel = False # show coefficients of spatial model
191 di.showBadCandidates = True # show the bad candidates (red) along with good (green)
192 elif name == "lsst.ip.diffim.imagePsfMatch":
193 di.display = True # enable debug output
194 di.maskTransparency = 30 # ds9 mask transparency
195 di.displayTemplate = True # show full (remapped) template
196 di.displaySciIm = True # show science image to match to
197 di.displaySpatialCells = True # show spatial cells
198 di.displayDiffIm = True # show difference image
199 di.showBadCandidates = True # show the bad candidates (red) along with good (green)
200 elif name == "lsst.ip.diffim.diaCatalogSourceSelector":
201 di.display = False # enable debug output
202 di.maskTransparency = 30 # ds9 mask transparency
203 di.displayExposure = True # show exposure with candidates indicated
204 di.pauseAtEnd = False # pause when done
206 lsstDebug.Info = DebugInfo
210 Note that if you want addional logging info, you may add to your scripts:
212 import lsst.pex.logging as pexLog
213 pexLog.Trace_setVerbosity('lsst.ip.diffim', 5)
216 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
218 \section ip_diffim_imagepsfmatch_Example A complete example of using ImagePsfMatchTask
220 This code is imagePsfMatchTask.py in the examples directory, and can be run as \em e.g.
222 examples/imagePsfMatchTask.py --debug
223 examples/imagePsfMatchTask.py --debug --mode="matchExposures"
224 examples/imagePsfMatchTask.py --debug --template /path/to/templateExp.fits --science /path/to/scienceExp.fits
227 \dontinclude imagePsfMatchTask.py
228 Create a subclass of ImagePsfMatchTask that allows us to either match exposures, or subtract exposures:
229 \skip MyImagePsfMatchTask
230 \until self.subtractExposures
232 And allow the user the freedom to either run the script in default mode, or point to their own images on disk.
233 Note that these images must be readable as an lsst.afw.image.Exposure:
237 We have enabled some minor display debugging in this script via the --debug option. However, if you
238 have an lsstDebug debug.py in your PYTHONPATH you will get additional debugging displays. The following
239 block checks for this script:
243 \dontinclude imagePsfMatchTask.py
244 Finally, we call a run method that we define below. First set up a Config and modify some of the parameters.
245 E.g. use an "Alard-Lupton" sum-of-Gaussian basis, fit for a differential background, and use low order spatial
246 variation in the kernel and background:
248 \until spatialBgOrder
250 Make sure the images (if any) that were sent to the script exist on disk and are readable. If no images
251 are sent, make some fake data up for the sake of this example script (have a look at the code if you want
252 more details on generateFakeImages):
256 Create and run the Task:
260 And finally provide some optional debugging displays:
262 \until result.subtractedExposure
263 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
266 ConfigClass = ImagePsfMatchConfig
269 """!Create the ImagePsfMatchTask
271 \param *args arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__
272 \param **kwargs keyword arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__
274 Upon initialization, the kernel configuration is defined by self.config.kernel.active.
275 The task creates an lsst.afw.math.Warper from the subConfig self.config.kernel.active.warpingConfig.
276 A schema for the selection and measurement of candidate lsst.ip.diffim.KernelCandidates is
277 defined, and used to initize subTasks selectDetection (for candidate detection) and selectMeasurement
278 (for candidate measurement).
280 PsfMatchTask.__init__(self, *args, **kwargs)
282 self.
_warper = afwMath.Warper.fromConfig(self.kConfig.warpingConfig)
285 self.
background = SubtractBackgroundTask(config=self.kConfig.afwBackgroundConfig, name=
"background",
289 self.makeSubtask(
"selectDetection", schema=self.
selectSchema)
293 """!Return the FWHM in pixels of a Psf"""
294 sigPix = psf.computeShape().getDeterminantRadius()
295 return sigPix * sigma2fwhm
299 templateFwhmPix=
None, scienceFwhmPix=
None,
300 candidateList=
None, doWarping=
True, convolveTemplate=
True):
301 """!Warp and PSF-match an exposure to the reference
303 Do the following, in order:
304 - Warp templateExposure to match scienceExposure,
305 if doWarping True and their WCSs do not already match
306 - Determine a PSF matching kernel and differential background model
307 that matches templateExposure to scienceExposure
308 - Convolve templateExposure by PSF matching kernel
310 @param templateExposure: Exposure to warp and PSF-match to the reference masked image
311 @param scienceExposure: Exposure whose WCS and PSF are to be matched to
312 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve)
313 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image
314 @param candidateList: a list of footprints/maskedImages for kernel candidates;
315 if None then source detection is run.
316 - Currently supported: list of Footprints or measAlg.PsfCandidateF
317 @param doWarping: what to do if templateExposure's and scienceExposure's WCSs do not match:
318 - if True then warp templateExposure to match scienceExposure
319 - if False then raise an Exception
320 @param convolveTemplate: convolve the template image or the science image
321 - if True, templateExposure is warped if doWarping, templateExposure is convolved
322 - if False, templateExposure is warped if doWarping, scienceExposure is convolved
324 @return a pipeBase.Struct containing these fields:
325 - matchedImage: the PSF-matched exposure =
326 warped templateExposure convolved by psfMatchingKernel. This has:
327 - the same parent bbox, Wcs and Calib as scienceExposure
328 - the same filter as templateExposure
329 - no Psf (because the PSF-matching process does not compute one)
330 - psfMatchingKernel: the PSF matching kernel
331 - backgroundModel: differential background model
332 - kernelCellSet: SpatialCellSet used to solve for the PSF matching kernel
334 Raise a RuntimeError if doWarping is False and templateExposure's and scienceExposure's
337 if not self.
_validateWcs(templateExposure, scienceExposure):
339 self.log.info(
"Astrometrically registering template to science image")
340 templatePsf = templateExposure.getPsf()
341 templateExposure = self._warper.warpExposure(scienceExposure.getWcs(),
342 templateExposure, destBBox=scienceExposure.getBBox())
343 templateExposure.setPsf(templatePsf)
345 pexLog.Trace(self.log.getName(), 1,
"ERROR: Input images not registered")
346 raise RuntimeError(
"Input images not registered")
348 if templateFwhmPix
is None:
349 if not templateExposure.hasPsf():
350 self.log.warn(
"No estimate of Psf FWHM for template image")
352 templateFwhmPix = self.
getFwhmPix(templateExposure.getPsf())
353 self.log.info(
"templateFwhmPix: {}".
format(templateFwhmPix))
355 if scienceFwhmPix
is None:
356 if not scienceExposure.hasPsf():
357 self.log.warn(
"No estimate of Psf FWHM for science image")
359 scienceFwhmPix = self.
getFwhmPix(scienceExposure.getPsf())
360 self.log.info(
"scienceFwhmPix: {}".
format(scienceFwhmPix))
363 candidateList = self.
makeCandidateList(templateExposure, scienceExposure, kernelSize, candidateList)
367 templateExposure.getMaskedImage(), scienceExposure.getMaskedImage(), candidateList,
368 templateFwhmPix=templateFwhmPix, scienceFwhmPix=scienceFwhmPix)
371 scienceExposure.getMaskedImage(), templateExposure.getMaskedImage(), candidateList,
372 templateFwhmPix=scienceFwhmPix, scienceFwhmPix=templateFwhmPix)
375 psfMatchedExposure.setFilter(templateExposure.getFilter())
376 psfMatchedExposure.setCalib(scienceExposure.getCalib())
377 results.warpedExposure = templateExposure
378 results.matchedExposure = psfMatchedExposure
382 def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList,
383 templateFwhmPix=
None, scienceFwhmPix=
None):
384 """!PSF-match a MaskedImage (templateMaskedImage) to a reference MaskedImage (scienceMaskedImage)
386 Do the following, in order:
387 - Determine a PSF matching kernel and differential background model
388 that matches templateMaskedImage to scienceMaskedImage
389 - Convolve templateMaskedImage by the PSF matching kernel
391 @param templateMaskedImage: masked image to PSF-match to the reference masked image;
392 must be warped to match the reference masked image
393 @param scienceMaskedImage: maskedImage whose PSF is to be matched to
394 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve)
395 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image
396 @param candidateList: a list of footprints/maskedImages for kernel candidates;
397 if None then source detection is run.
398 - Currently supported: list of Footprints or measAlg.PsfCandidateF
400 @return a pipeBase.Struct containing these fields:
401 - psfMatchedMaskedImage: the PSF-matched masked image =
402 templateMaskedImage convolved with psfMatchingKernel.
403 This has the same xy0, dimensions and wcs as scienceMaskedImage.
404 - psfMatchingKernel: the PSF matching kernel
405 - backgroundModel: differential background model
406 - kernelCellSet: SpatialCellSet used to solve for the PSF matching kernel
408 Raise a RuntimeError if input images have different dimensions
415 displaySpatialCells =
lsstDebug.Info(__name__).displaySpatialCells
417 if not maskTransparency:
420 ds9.setMaskTransparency(maskTransparency)
422 if not candidateList:
423 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
425 if not self.
_validateSize(templateMaskedImage, scienceMaskedImage):
426 pexLog.Trace(self.log.getName(), 1,
"ERROR: Input images different size")
427 raise RuntimeError(
"Input images different size")
429 if display
and displayTemplate:
430 ds9.mtv(templateMaskedImage, frame=lsstDebug.frame, title=
"Image to convolve")
433 if display
and displaySciIm:
434 ds9.mtv(scienceMaskedImage, frame=lsstDebug.frame, title=
"Image to not convolve")
441 if display
and displaySpatialCells:
442 diUtils.showKernelSpatialCells(scienceMaskedImage, kernelCellSet,
443 symb=
"o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW, ctypeBad=ds9.RED,
444 size=4, frame=lsstDebug.frame, title=
"Image to not convolve")
447 if templateFwhmPix
and scienceFwhmPix:
448 self.log.info(
"Matching Psf FWHM %.2f -> %.2f pix", templateFwhmPix, scienceFwhmPix)
450 if self.kConfig.useBicForKernelBasis:
455 bicDegrees = nbe(tmpKernelCellSet, self.log)
457 alardDegGauss=bicDegrees[0], metadata=self.metadata)
461 metadata=self.metadata)
463 spatialSolution, psfMatchingKernel, backgroundModel = self._solve(kernelCellSet, basisList)
468 psfMatchedMaskedImage = afwImage.MaskedImageF(templateMaskedImage.getBBox())
470 afwMath.convolve(psfMatchedMaskedImage, templateMaskedImage, psfMatchingKernel, doNormalize)
471 return pipeBase.Struct(
472 matchedImage=psfMatchedMaskedImage,
473 psfMatchingKernel=psfMatchingKernel,
474 backgroundModel=backgroundModel,
475 kernelCellSet=kernelCellSet,
480 templateFwhmPix=
None, scienceFwhmPix=
None,
481 candidateList=
None, doWarping=
True, convolveTemplate=
True):
482 """!Register, Psf-match and subtract two Exposures
484 Do the following, in order:
485 - Warp templateExposure to match scienceExposure, if their WCSs do not already match
486 - Determine a PSF matching kernel and differential background model
487 that matches templateExposure to scienceExposure
488 - PSF-match templateExposure to scienceExposure
489 - Compute subtracted exposure (see return values for equation).
491 @param templateExposure: exposure to PSF-match to scienceExposure
492 @param scienceExposure: reference Exposure
493 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve)
494 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image
495 @param candidateList: a list of footprints/maskedImages for kernel candidates;
496 if None then source detection is run.
497 - Currently supported: list of Footprints or measAlg.PsfCandidateF
498 @param doWarping: what to do if templateExposure's and scienceExposure's WCSs do not match:
499 - if True then warp templateExposure to match scienceExposure
500 - if False then raise an Exception
501 @param convolveTemplate: convolve the template image or the science image
502 - if True, templateExposure is warped if doWarping, templateExposure is convolved
503 - if False, templateExposure is warped if doWarping, scienceExposure is convolved
505 @return a pipeBase.Struct containing these fields:
506 - subtractedExposure: subtracted Exposure = scienceExposure - (matchedImage + backgroundModel)
507 - matchedImage: templateExposure after warping to match templateExposure (if doWarping true),
508 and convolving with psfMatchingKernel
509 - psfMatchingKernel: PSF matching kernel
510 - backgroundModel: differential background model
511 - kernelCellSet: SpatialCellSet used to determine PSF matching kernel
514 templateExposure=templateExposure,
515 scienceExposure=scienceExposure,
516 templateFwhmPix=templateFwhmPix,
517 scienceFwhmPix=scienceFwhmPix,
518 candidateList=candidateList,
520 convolveTemplate=convolveTemplate
523 subtractedExposure = afwImage.ExposureF(scienceExposure,
True)
525 subtractedMaskedImage = subtractedExposure.getMaskedImage()
526 subtractedMaskedImage -= results.matchedExposure.getMaskedImage()
527 subtractedMaskedImage -= results.backgroundModel
529 subtractedExposure.setMaskedImage(results.warpedExposure.getMaskedImage())
530 subtractedMaskedImage = subtractedExposure.getMaskedImage()
531 subtractedMaskedImage -= results.matchedExposure.getMaskedImage()
532 subtractedMaskedImage -= results.backgroundModel
535 subtractedMaskedImage *= -1
538 subtractedMaskedImage /= results.psfMatchingKernel.computeImage(
539 afwImage.ImageD(results.psfMatchingKernel.getDimensions()),
False)
545 if not maskTransparency:
548 ds9.setMaskTransparency(maskTransparency)
549 if display
and displayDiffIm:
550 ds9.mtv(templateExposure, frame=lsstDebug.frame, title=
"Template")
552 ds9.mtv(results.matchedExposure, frame=lsstDebug.frame, title=
"Matched template")
554 ds9.mtv(scienceExposure, frame=lsstDebug.frame, title=
"Science Image")
556 ds9.mtv(subtractedExposure, frame=lsstDebug.frame, title=
"Difference Image")
559 results.subtractedExposure = subtractedExposure
564 templateFwhmPix=
None, scienceFwhmPix=
None):
565 """!Psf-match and subtract two MaskedImages
567 Do the following, in order:
568 - PSF-match templateMaskedImage to scienceMaskedImage
569 - Determine the differential background
570 - Return the difference: scienceMaskedImage -
571 ((warped templateMaskedImage convolved with psfMatchingKernel) + backgroundModel)
573 @param templateMaskedImage: MaskedImage to PSF-match to scienceMaskedImage
574 @param scienceMaskedImage: reference MaskedImage
575 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve)
576 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image
577 @param candidateList: a list of footprints/maskedImages for kernel candidates;
578 if None then source detection is run.
579 - Currently supported: list of Footprints or measAlg.PsfCandidateF
581 @return a pipeBase.Struct containing these fields:
582 - subtractedMaskedImage = scienceMaskedImage - (matchedImage + backgroundModel)
583 - matchedImage: templateMaskedImage convolved with psfMatchingKernel
584 - psfMatchingKernel: PSF matching kernel
585 - backgroundModel: differential background model
586 - kernelCellSet: SpatialCellSet used to determine PSF matching kernel
588 if not candidateList:
589 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
592 templateMaskedImage=templateMaskedImage,
593 scienceMaskedImage=scienceMaskedImage,
594 candidateList=candidateList,
595 templateFwhmPix=templateFwhmPix,
596 scienceFwhmPix=scienceFwhmPix,
599 subtractedMaskedImage = afwImage.MaskedImageF(scienceMaskedImage,
True)
600 subtractedMaskedImage -= results.matchedImage
601 subtractedMaskedImage -= results.backgroundModel
602 results.subtractedMaskedImage = subtractedMaskedImage
608 if not maskTransparency:
611 ds9.setMaskTransparency(maskTransparency)
612 if display
and displayDiffIm:
613 ds9.mtv(subtractedMaskedImage, frame=lsstDebug.frame)
619 """!Get sources to use for Psf-matching
621 This method runs detection and measurement on an exposure.
622 The returned set of sources will be used as candidates for
625 @param exposure: Exposure on which to run detection/measurement
626 @param sigma: Detection threshold
627 @param doSmooth: Whether or not to smooth the Exposure with Psf before detection
628 @param idFactory: Factory for the generation of Source ids
630 @return source catalog containing candidates for the Psf-matching
634 table = afwTable.SourceTable.make(self.
selectSchema, idFactory)
637 mi = exposure.getMaskedImage()
639 imArr = mi.getImage().getArray()
640 maskArr = mi.getMask().getArray()
641 miArr = np.ma.masked_array(imArr, mask=maskArr)
643 bkgd = self.background.fitBackground(mi).getImageF()
645 self.log.warn(
"Failed to get background model. Falling back to median background estimation")
646 bkgd = np.ma.extras.median(miArr)
653 detRet = self.selectDetection.makeSourceCatalog(
659 selectSources = detRet.sources
660 self.selectMeasurement.run(measCat=selectSources, exposure=exposure)
668 """!Make a list of acceptable KernelCandidates
670 Accept or generate a list of candidate sources for
671 Psf-matching, and examine the Mask planes in both of the
672 images for indications of bad pixels
674 @param templateExposure: Exposure that will be convolved
675 @param scienceExposure: Exposure that will be matched-to
676 @param kernelSize: Dimensions of the Psf-matching Kernel, used to grow detection footprints
677 @param candidateList: List of Sources to examine. Elements must be of type afw.table.Source
678 or a type that wraps a Source and has a getSource() method, such as
679 meas.algorithms.PsfCandidateF.
681 @return a list of dicts having a "source" and "footprint"
682 field for the Sources deemed to be appropriate for Psf
685 if candidateList
is None:
688 if len(candidateList) < 1:
689 raise RuntimeError(
"No candidates in candidateList")
691 listTypes = set(type(x)
for x
in candidateList)
692 if len(listTypes) > 1:
693 raise RuntimeError(
"Candidate list contains mixed types: %s" % [l
for l
in listTypes])
697 candidateList[0].getSource()
698 except Exception
as e:
699 raise RuntimeError(
"Candidate List is of type: %s. " % (type(candidateList[0])) +
700 "Can only make candidate list from list of afwTable.SourceRecords, " +
701 "measAlg.PsfCandidateF or other type with a getSource() method: %s" % (e))
702 candidateList = [c.getSource()
for c
in candidateList]
704 candidateList = diffimTools.sourceToFootprintList(candidateList,
705 templateExposure, scienceExposure,
707 self.kConfig.detectionConfig,
709 if len(candidateList) == 0:
710 raise RuntimeError(
"Cannot find any objects suitable for KernelCandidacy")
715 """! NOT IMPLEMENTED YET"""
716 return self.kConfig.sizeCellX, self.kConfig.sizeCellY
718 def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList):
719 """!Build a SpatialCellSet for use with the solve method
721 @param templateMaskedImage: MaskedImage to PSF-matched to scienceMaskedImage
722 @param scienceMaskedImage: reference MaskedImage
723 @param candidateList: a list of footprints/maskedImages for kernel candidates;
724 if None then source detection is run.
725 - Currently supported: list of Footprints or measAlg.PsfCandidateF
727 @return kernelCellSet: a SpatialCellSet for use with self._solve
729 if not candidateList:
730 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
736 sizeCellX, sizeCellY)
738 policy = pexConfig.makePolicy(self.
kConfig)
740 for cand
in candidateList:
741 bbox = cand[
'footprint'].getBBox()
743 tmi = afwImage.MaskedImageF(templateMaskedImage, bbox)
744 smi = afwImage.MaskedImageF(scienceMaskedImage, bbox)
745 cand = diffimLib.makeKernelCandidate(cand[
'source'], tmi, smi, policy)
747 self.log.debug(
"Candidate %d at %f, %f", cand.getId(), cand.getXCenter(), cand.getYCenter())
748 kernelCellSet.insertCandidate(cand)
753 """!Return True if two image-like objects are the same size
755 return templateMaskedImage.getDimensions() == scienceMaskedImage.getDimensions()
758 """!Return True if the WCS of the two Exposures have the same origin and extent
760 templateWcs = templateExposure.getWcs()
761 scienceWcs = scienceExposure.getWcs()
762 templateBBox = templateExposure.getBBox()
763 scienceBBox = scienceExposure.getBBox()
766 templateOrigin = templateWcs.pixelToSky(
afwGeom.Point2D(templateBBox.getBegin()))
767 scienceOrigin = scienceWcs.pixelToSky(
afwGeom.Point2D(scienceBBox.getBegin()))
770 templateLimit = templateWcs.pixelToSky(
afwGeom.Point2D(templateBBox.getEnd()))
771 scienceLimit = scienceWcs.pixelToSky(
afwGeom.Point2D(scienceBBox.getEnd()))
773 self.log.info(
"Template Wcs : %f,%f -> %f,%f",
774 templateOrigin[0], templateOrigin[1],
775 templateLimit[0], templateLimit[1])
776 self.log.info(
"Science Wcs : %f,%f -> %f,%f",
777 scienceOrigin[0], scienceOrigin[1],
778 scienceLimit[0], scienceLimit[1])
780 templateBBox =
afwGeom.Box2D(templateOrigin.getPosition(), templateLimit.getPosition())
781 scienceBBox =
afwGeom.Box2D(scienceOrigin.getPosition(), scienceLimit.getPosition())
782 if not (templateBBox.overlaps(scienceBBox)):
783 raise RuntimeError(
"Input images do not overlap at all")
785 if ( (templateOrigin.getPosition() != scienceOrigin.getPosition())
or
786 (templateLimit.getPosition() != scienceLimit.getPosition())
or
787 (templateExposure.getDimensions() != scienceExposure.getDimensions())):
def getFwhmPix
Return the FWHM in pixels of a Psf.
def getSelectSources
Get sources to use for Psf-matching.
def matchMaskedImages
PSF-match a MaskedImage (templateMaskedImage) to a reference MaskedImage (scienceMaskedImage) ...
Configuration for image-to-image Psf matching.
Class for storing ordered metadata with comments.
limited backward compatibility to the DC2 run-time trace facilities
def subtractMaskedImages
Psf-match and subtract two MaskedImages.
def makeCandidateList
Make a list of acceptable KernelCandidates.
Psf-match two MaskedImages or Exposures using the sources in the images.
def _validateSize
Return True if two image-like objects are the same size.
def _buildCellSet
Build a SpatialCellSet for use with the solve method.
A collection of SpatialCells covering an entire image.
def _validateWcs
Return True if the WCS of the two Exposures have the same origin and extent.
def subtractExposures
Register, Psf-match and subtract two Exposures.
def _adaptCellSize
NOT IMPLEMENTED YET.
Exposure< ImagePixelT, MaskPixelT, VariancePixelT >::Ptr makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, boost::shared_ptr< Wcs const > wcs=boost::shared_ptr< Wcs const >())
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.
A floating-point coordinate rectangle geometry.
def matchExposures
Warp and PSF-match an exposure to the reference.
def __init__
Create the ImagePsfMatchTask.