1 from builtins
import zip
36 ObjectSizeStarSelectorTask
38 SourceFlagChecker, KernelCandidateF, makeKernelBasisList, \
39 KernelCandidateQa, DiaCatalogSourceSelectorTask, DiaCatalogSourceSelectorConfig, \
40 GetCoaddAsTemplateTask, GetCalexpAsTemplateTask, DipoleFitTask, DecorrelateALKernelTask
44 FwhmPerSigma = 2 * math.sqrt(2 * math.log(2))
49 """Config for ImageDifferenceTask
51 doAddCalexpBackground = pexConfig.Field(dtype=bool, default=
True,
52 doc=
"Add background to calexp before processing it. "
53 "Useful as ipDiffim does background matching.")
54 doUseRegister = pexConfig.Field(dtype=bool, default=
True,
55 doc=
"Use image-to-image registration to align template with "
57 doDebugRegister = pexConfig.Field(dtype=bool, default=
False,
58 doc=
"Writing debugging data for doUseRegister")
59 doSelectSources = pexConfig.Field(dtype=bool, default=
True,
60 doc=
"Select stars to use for kernel fitting")
61 doSelectDcrCatalog = pexConfig.Field(dtype=bool, default=
False,
62 doc=
"Select stars of extreme color as part of the control sample")
63 doSelectVariableCatalog = pexConfig.Field(dtype=bool, default=
False,
64 doc=
"Select stars that are variable to be part "
65 "of the control sample")
66 doSubtract = pexConfig.Field(dtype=bool, default=
True, doc=
"Compute subtracted exposure?")
67 doPreConvolve = pexConfig.Field(dtype=bool, default=
True,
68 doc=
"Convolve science image by its PSF before PSF-matching?")
69 useGaussianForPreConvolution = pexConfig.Field(dtype=bool, default=
True,
70 doc=
"Use a simple gaussian PSF model for pre-convolution "
71 "(else use fit PSF)? Ignored if doPreConvolve false.")
72 doDetection = pexConfig.Field(dtype=bool, default=
True, doc=
"Detect sources?")
73 doDecorrelation = pexConfig.Field(dtype=bool, default=
False,
74 doc=
"Perform diffim decorrelation to undo pixel correlation due to A&L "
75 "kernel convolution? If True, also update the diffim PSF.")
76 doMerge = pexConfig.Field(dtype=bool, default=
True,
77 doc=
"Merge positive and negative diaSources with grow radius "
78 "set by growFootprint")
79 doMatchSources = pexConfig.Field(dtype=bool, default=
True,
80 doc=
"Match diaSources with input calexp sources and ref catalog sources")
81 doMeasurement = pexConfig.Field(dtype=bool, default=
True, doc=
"Measure diaSources?")
82 doDipoleFitting = pexConfig.Field(dtype=bool, default=
True, doc=
"Measure dipoles using new algorithm?")
83 doWriteSubtractedExp = pexConfig.Field(dtype=bool, default=
True, doc=
"Write difference exposure?")
84 doWriteMatchedExp = pexConfig.Field(dtype=bool, default=
False,
85 doc=
"Write warped and PSF-matched template coadd exposure?")
86 doWriteSources = pexConfig.Field(dtype=bool, default=
True, doc=
"Write sources?")
87 doAddMetrics = pexConfig.Field(dtype=bool, default=
True,
88 doc=
"Add columns to the source table to hold analysis metrics?")
90 coaddName = pexConfig.Field(
91 doc=
"coadd name: typically one of deep or goodSeeing",
95 convolveTemplate = pexConfig.Field(
96 doc=
"Which image gets convolved (default = template)",
100 refObjLoader = pexConfig.ConfigurableField(
101 target=measAstrom.LoadAstrometryNetObjectsTask,
102 doc=
"reference object loader",
104 astrometer = pexConfig.ConfigurableField(
105 target=measAstrom.AstrometryTask,
106 doc=
"astrometry task; used to match sources to reference objects, but not to fit a WCS",
108 sourceSelector = pexConfig.ConfigurableField(
109 target=ObjectSizeStarSelectorTask,
110 doc=
"Source selection algorithm",
112 subtract = pexConfig.ConfigurableField(
113 target=ImagePsfMatchTask,
114 doc=
"Warp and PSF match template to exposure, then subtract",
116 decorrelate = pexConfig.ConfigurableField(
117 target=DecorrelateALKernelTask,
118 doc=
"""Decorrelate effects of A&L kernel convolution on image difference, only if doSubtract is True.
119 If this option is enabled, then detection.thresholdValue should be set to 5.0 (rather than the
122 detection = pexConfig.ConfigurableField(
123 target=SourceDetectionTask,
124 doc=
"Low-threshold detection for final measurement",
130 measurement = pexConfig.ConfigurableField(
131 target=DipoleFitTask,
132 doc=
"Enable updated dipole fitting method.",
134 getTemplate = pexConfig.ConfigurableField(
135 target=GetCoaddAsTemplateTask,
136 doc=
"Subtask to retrieve template exposure and sources",
138 controlStepSize = pexConfig.Field(
139 doc=
"What step size (every Nth one) to select a control sample from the kernelSources",
143 controlRandomSeed = pexConfig.Field(
144 doc =
"Random seed for shuffing the control sample",
148 register = pexConfig.ConfigurableField(
150 doc=
"Task to enable image-to-image image registration (warping)",
152 kernelSourcesFromRef = pexConfig.Field(
153 doc=
"Select sources to measure kernel from reference catalog if True, template if false",
157 templateSipOrder = pexConfig.Field(dtype=int, default=2,
158 doc=
"Sip Order for fitting the Template Wcs "
159 "(default is too high, overfitting)")
161 growFootprint = pexConfig.Field(dtype=int, default=2,
162 doc=
"Grow positive and negative footprints by this amount before merging")
164 diaSourceMatchRadius = pexConfig.Field(dtype=float, default=0.5,
165 doc=
"Match radius (in arcseconds) "
166 "for DiaSource to Source association")
171 self.subtract.kernel.name =
"AL"
172 self.subtract.kernel.active.fitForBackground =
True
173 self.subtract.kernel.active.spatialKernelOrder = 1
174 self.subtract.kernel.active.spatialBgOrder = 0
181 self.detection.thresholdPolarity =
"both"
182 self.detection.thresholdValue = 5.5
183 self.detection.reEstimateBackground =
False
184 self.detection.thresholdType =
"pixel_stdev"
190 self.measurement.algorithms.names.add(
'base_PeakLikelihoodFlux')
196 pexConfig.Config.validate(self)
198 raise ValueError(
"Cannot run source measurement without source detection.")
200 raise ValueError(
"Cannot run source merging without source detection.")
202 raise ValueError(
"doUseRegister=True and doSelectSources=False. " +
203 "Cannot run RegisterTask without selecting sources.")
210 return pipeBase.TaskRunner.getTargetList(parsedCmd, templateIdList=parsedCmd.templateId.idList,
215 """Subtract an image from a template and measure the result
217 ConfigClass = ImageDifferenceConfig
218 RunnerClass = ImageDifferenceTaskRunner
219 _DefaultName =
"imageDifference"
222 """!Construct an ImageDifference Task
224 @param[in] butler Butler object to use in constructing reference object loaders
226 pipeBase.CmdLineTask.__init__(self, **kwargs)
227 self.makeSubtask(
"subtract")
228 self.makeSubtask(
"getTemplate")
229 self.makeSubtask(
"decorrelate")
231 if self.config.doUseRegister:
232 self.makeSubtask(
"register")
233 self.
schema = afwTable.SourceTable.makeMinimalSchema()
235 if self.config.doSelectSources:
236 self.makeSubtask(
"sourceSelector", schema=self.
schema)
237 self.makeSubtask(
'refObjLoader', butler=butler)
238 self.makeSubtask(
"astrometer", refObjLoader=self.refObjLoader)
241 if self.config.doDetection:
242 self.makeSubtask(
"detection", schema=self.
schema)
243 if self.config.doMeasurement:
244 if not self.config.doDipoleFitting:
245 self.makeSubtask(
"measurement", schema=self.
schema,
248 self.makeSubtask(
"measurement", schema=self.
schema)
249 if self.config.doMatchSources:
250 self.schema.addField(
"refMatchId",
"L",
"unique id of reference catalog match")
251 self.schema.addField(
"srcMatchId",
"L",
"unique id of source match")
254 def run(self, sensorRef, templateIdList=None):
255 """Subtract an image from a template coadd and measure the result
258 - warp template coadd to match WCS of image
259 - PSF match image to warped template
260 - subtract image from PSF-matched, warped template
261 - persist difference image
265 @param sensorRef: sensor-level butler data reference, used for the following data products:
271 - self.config.coaddName + "Coadd_skyMap"
272 - self.config.coaddName + "Coadd"
273 Input or output, depending on config:
274 - self.config.coaddName + "Diff_subtractedExp"
275 Output, depending on config:
276 - self.config.coaddName + "Diff_matchedExp"
277 - self.config.coaddName + "Diff_src"
279 @return pipe_base Struct containing these fields:
280 - subtractedExposure: exposure after subtracting template;
281 the unpersisted version if subtraction not run but detection run
282 None if neither subtraction nor detection run (i.e. nothing useful done)
283 - subtractRes: results of subtraction task; None if subtraction not run
284 - sources: detected and possibly measured sources; None if detection not run
286 self.log.info(
"Processing %s" % (sensorRef.dataId))
289 subtractedExposure =
None
293 controlSources =
None
299 expBits = sensorRef.get(
"ccdExposureId_bits")
300 expId = int(sensorRef.get(
"ccdExposureId"))
301 idFactory = afwTable.IdFactory.makeSource(expId, 64 - expBits)
304 exposure = sensorRef.get(
"calexp", immediate=
True)
305 if self.config.doAddCalexpBackground:
306 mi = exposure.getMaskedImage()
307 mi += sensorRef.get(
"calexpBackground").getImage()
308 if not exposure.hasPsf():
309 raise pipeBase.TaskError(
"Exposure has no psf")
310 sciencePsf = exposure.getPsf()
312 subtractedExposureName = self.config.coaddName +
"Diff_differenceExp"
313 templateExposure =
None
314 templateSources =
None
315 if self.config.doSubtract:
316 template = self.getTemplate.run(exposure, sensorRef, templateIdList=templateIdList)
317 templateExposure = template.exposure
318 templateSources = template.sources
323 scienceSigmaOrig = psfAttr.computeGaussianWidth(psfAttr.ADAPTIVE_MOMENT)
328 templateSigma = psfAttr.computeGaussianWidth(psfAttr.ADAPTIVE_MOMENT)
334 if self.config.doPreConvolve:
337 srcMI = exposure.getMaskedImage()
338 destMI = srcMI.Factory(srcMI.getDimensions())
340 if self.config.useGaussianForPreConvolution:
342 kWidth, kHeight = sciencePsf.getLocalKernel().getDimensions()
348 exposure.setMaskedImage(destMI)
349 scienceSigmaPost = scienceSigmaOrig * math.sqrt(2)
351 scienceSigmaPost = scienceSigmaOrig
354 if self.config.doSelectSources:
355 if not sensorRef.datasetExists(
"src"):
356 self.log.warn(
"Src product does not exist; running detection, measurement, selection")
358 selectSources = self.subtract.getSelectSources(
360 sigma=scienceSigmaPost,
361 doSmooth=
not self.doPreConvolve,
365 self.log.info(
"Source selection via src product")
367 selectSources = sensorRef.get(
"src")
371 referenceFwhmPix=scienceSigmaPost * FwhmPerSigma,
372 targetFwhmPix=templateSigma * FwhmPerSigma))
374 if self.config.doAddMetrics:
376 kcQa = KernelCandidateQa(nparam)
377 selectSources = kcQa.addToSchema(selectSources)
379 if self.config.kernelSourcesFromRef:
381 astromRet = self.astrometer.loadAndMatch(exposure=exposure, sourceCat=selectSources)
382 matches = astromRet.matches
383 elif templateSources:
388 raise RuntimeError(
"doSelectSources=True and kernelSourcesFromRef=False," +
389 "but template sources not available. Cannot match science " +
390 "sources with template sources. Run process* on data from " +
391 "which templates are built.")
393 kernelSources = self.sourceSelector.selectStars(exposure, selectSources,
394 matches=matches).starCat
396 random.shuffle(kernelSources, random.random)
397 controlSources = kernelSources[::self.config.controlStepSize]
398 kernelSources = [k
for i, k
in enumerate(kernelSources)
if i % self.config.controlStepSize]
400 if self.config.doSelectDcrCatalog:
401 redSelector = DiaCatalogSourceSelectorTask(
402 DiaCatalogSourceSelectorConfig(grMin=self.sourceSelector.config.grMax, grMax=99.999))
403 redSources = redSelector.selectStars(exposure, selectSources, matches=matches).starCat
404 controlSources.extend(redSources)
406 blueSelector = DiaCatalogSourceSelectorTask(
407 DiaCatalogSourceSelectorConfig(grMin=-99.999, grMax=self.sourceSelector.config.grMin))
408 blueSources = blueSelector.selectStars(exposure, selectSources, matches=matches).starCat
409 controlSources.extend(blueSources)
411 if self.config.doSelectVariableCatalog:
412 varSelector = DiaCatalogSourceSelectorTask(
413 DiaCatalogSourceSelectorConfig(includeVariable=
True))
414 varSources = varSelector.selectStars(exposure, selectSources, matches=matches).starCat
415 controlSources.extend(varSources)
417 self.log.info(
"Selected %d / %d sources for Psf matching (%d for control sample)"
418 % (len(kernelSources), len(selectSources), len(controlSources)))
420 if self.config.doUseRegister:
421 self.log.info(
"Registering images")
423 if templateSources
is None:
426 templateSources = self.subtract.getSelectSources(
435 wcsResults = self.
fitAstrometry(templateSources, templateExposure, selectSources)
436 warpedExp = self.register.warpExposure(templateExposure, wcsResults.wcs,
437 exposure.getWcs(), exposure.getBBox())
438 templateExposure = warpedExp
443 if self.config.doDebugRegister:
445 srcToMatch = {x.second.getId(): x.first
for x
in matches}
447 refCoordKey = wcsResults.matches[0].first.getTable().getCoordKey()
448 inCentroidKey = wcsResults.matches[0].second.getTable().getCentroidKey()
449 sids = [m.first.getId()
for m
in wcsResults.matches]
450 positions = [m.first.get(refCoordKey)
for m
in wcsResults.matches]
451 residuals = [m.first.get(refCoordKey).getOffsetFrom(wcsResults.wcs.pixelToSky(
452 m.second.get(inCentroidKey)))
for m
in wcsResults.matches]
453 allresids = dict(zip(sids, zip(positions, residuals)))
455 cresiduals = [m.first.get(refCoordKey).getTangentPlaneOffset(
456 wcsResults.wcs.pixelToSky(
457 m.second.get(inCentroidKey)))
for m
in wcsResults.matches]
458 colors = numpy.array([-2.5*numpy.log10(srcToMatch[x].get(
"g"))
459 + 2.5*numpy.log10(srcToMatch[x].get(
"r"))
460 for x
in sids
if x
in srcToMatch.keys()])
461 dlong = numpy.array([r[0].asArcseconds()
for s, r
in zip(sids, cresiduals)
462 if s
in srcToMatch.keys()])
463 dlat = numpy.array([r[1].asArcseconds()
for s, r
in zip(sids, cresiduals)
464 if s
in srcToMatch.keys()])
465 idx1 = numpy.where(colors < self.sourceSelector.config.grMin)
466 idx2 = numpy.where((colors >= self.sourceSelector.config.grMin) &
467 (colors <= self.sourceSelector.config.grMax))
468 idx3 = numpy.where(colors > self.sourceSelector.config.grMax)
469 rms1Long = IqrToSigma * \
470 (numpy.percentile(dlong[idx1], 75)-numpy.percentile(dlong[idx1], 25))
471 rms1Lat = IqrToSigma*(numpy.percentile(dlat[idx1], 75)-numpy.percentile(dlat[idx1], 25))
472 rms2Long = IqrToSigma * \
473 (numpy.percentile(dlong[idx2], 75)-numpy.percentile(dlong[idx2], 25))
474 rms2Lat = IqrToSigma*(numpy.percentile(dlat[idx2], 75)-numpy.percentile(dlat[idx2], 25))
475 rms3Long = IqrToSigma * \
476 (numpy.percentile(dlong[idx3], 75)-numpy.percentile(dlong[idx3], 25))
477 rms3Lat = IqrToSigma*(numpy.percentile(dlat[idx3], 75)-numpy.percentile(dlat[idx3], 25))
478 self.log.info(
"Blue star offsets'': %.3f %.3f, %.3f %.3f" % (numpy.median(dlong[idx1]),
480 numpy.median(dlat[idx1]),
482 self.log.info(
"Green star offsets'': %.3f %.3f, %.3f %.3f" % (numpy.median(dlong[idx2]),
484 numpy.median(dlat[idx2]),
486 self.log.info(
"Red star offsets'': %.3f %.3f, %.3f %.3f" % (numpy.median(dlong[idx3]),
488 numpy.median(dlat[idx3]),
491 self.metadata.add(
"RegisterBlueLongOffsetMedian", numpy.median(dlong[idx1]))
492 self.metadata.add(
"RegisterGreenLongOffsetMedian", numpy.median(dlong[idx2]))
493 self.metadata.add(
"RegisterRedLongOffsetMedian", numpy.median(dlong[idx3]))
494 self.metadata.add(
"RegisterBlueLongOffsetStd", rms1Long)
495 self.metadata.add(
"RegisterGreenLongOffsetStd", rms2Long)
496 self.metadata.add(
"RegisterRedLongOffsetStd", rms3Long)
498 self.metadata.add(
"RegisterBlueLatOffsetMedian", numpy.median(dlat[idx1]))
499 self.metadata.add(
"RegisterGreenLatOffsetMedian", numpy.median(dlat[idx2]))
500 self.metadata.add(
"RegisterRedLatOffsetMedian", numpy.median(dlat[idx3]))
501 self.metadata.add(
"RegisterBlueLatOffsetStd", rms1Lat)
502 self.metadata.add(
"RegisterGreenLatOffsetStd", rms2Lat)
503 self.metadata.add(
"RegisterRedLatOffsetStd", rms3Lat)
510 self.log.info(
"Subtracting images")
511 subtractRes = self.subtract.subtractExposures(
512 templateExposure=templateExposure,
513 scienceExposure=exposure,
514 candidateList=kernelSources,
515 convolveTemplate=self.config.convolveTemplate,
516 doWarping=
not self.config.doUseRegister
518 subtractedExposure = subtractRes.subtractedExposure
520 if self.config.doWriteMatchedExp:
521 sensorRef.put(subtractRes.matchedExposure, self.config.coaddName +
"Diff_matchedExp")
523 if self.config.doDetection:
524 self.log.info(
"Computing diffim PSF")
525 if subtractedExposure
is None:
526 subtractedExposure = sensorRef.get(subtractedExposureName)
529 if not subtractedExposure.hasPsf():
530 if self.config.convolveTemplate:
531 subtractedExposure.setPsf(exposure.getPsf())
533 if templateExposure
is None:
534 template = self.getTemplate.run(exposure, sensorRef, templateIdList=templateIdList)
535 subtractedExposure.setPsf(template.exposure.getPsf())
539 if self.config.doDecorrelation
and self.config.doSubtract:
540 decorrResult = self.decorrelate.run(exposure, templateExposure,
542 subtractRes.psfMatchingKernel)
543 subtractedExposure = decorrResult.correctedExposure
545 if self.config.doDetection:
546 self.log.info(
"Running diaSource detection")
548 mask = subtractedExposure.getMaskedImage().getMask()
549 mask &= ~(mask.getPlaneBitMask(
"DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE"))
551 table = afwTable.SourceTable.make(self.
schema, idFactory)
553 results = self.detection.makeSourceCatalog(
555 exposure=subtractedExposure,
556 doSmooth=
not self.config.doPreConvolve
559 if self.config.doMerge:
560 fpSet = results.fpSets.positive
561 fpSet.merge(results.fpSets.negative, self.config.growFootprint,
562 self.config.growFootprint,
False)
564 fpSet.makeSources(diaSources)
565 self.log.info(
"Merging detections into %d sources" % (len(diaSources)))
567 diaSources = results.sources
569 if self.config.doMeasurement:
570 self.log.info(
"Running diaSource measurement")
571 if not self.config.doDipoleFitting:
572 self.measurement.run(diaSources, subtractedExposure)
574 if self.config.doSubtract:
575 self.measurement.run(diaSources, subtractedExposure, exposure,
576 subtractRes.matchedExposure)
578 self.measurement.run(diaSources, subtractedExposure, exposure)
581 if self.config.doMatchSources:
582 if sensorRef.datasetExists(
"src"):
584 matchRadAsec = self.config.diaSourceMatchRadius
585 matchRadPixel = matchRadAsec / exposure.getWcs().pixelScale().asArcseconds()
587 srcMatches =
afwTable.matchXy(sensorRef.get(
"src"), diaSources, matchRadPixel,
True)
588 srcMatchDict = dict([(srcMatch.second.getId(), srcMatch.first.getId())
for
589 srcMatch
in srcMatches])
590 self.log.info(
"Matched %d / %d diaSources to sources" % (len(srcMatchDict),
593 self.log.warn(
"Src product does not exist; cannot match with diaSources")
597 refAstromConfig = measAstrom.AstrometryConfig()
598 refAstromConfig.matcher.maxMatchDistArcSec = matchRadAsec
599 refAstrometer = measAstrom.AstrometryTask(refAstromConfig)
600 astromRet = refAstrometer.run(exposure=exposure, sourceCat=diaSources)
601 refMatches = astromRet.matches
602 if refMatches
is None:
603 self.log.warn(
"No diaSource matches with reference catalog")
606 self.log.info(
"Matched %d / %d diaSources to reference catalog" % (len(refMatches),
608 refMatchDict = dict([(refMatch.second.getId(), refMatch.first.getId())
for
609 refMatch
in refMatches])
612 for diaSource
in diaSources:
613 sid = diaSource.getId()
614 if sid
in srcMatchDict:
615 diaSource.set(
"srcMatchId", srcMatchDict[sid])
616 if sid
in refMatchDict:
617 diaSource.set(
"refMatchId", refMatchDict[sid])
619 if diaSources
is not None and self.config.doWriteSources:
620 sensorRef.put(diaSources, self.config.coaddName +
"Diff_diaSrc")
622 if self.config.doAddMetrics
and self.config.doSelectSources:
623 self.log.info(
"Evaluating metrics and control sample")
626 for cell
in subtractRes.kernelCellSet.getCellList():
627 for cand
in cell.begin(
False):
628 kernelCandList.append(KernelCandidateF.cast(cand))
631 basisList = afwMath.cast_LinearCombinationKernel(
632 kernelCandList[0].getKernel(KernelCandidateF.ORIG)).getKernelList()
635 diffimTools.sourceTableToCandidateList(controlSources,
636 subtractRes.warpedExposure, exposure,
637 self.config.subtract.kernel.active,
638 self.config.subtract.kernel.active.detectionConfig,
639 self.log, doBuild=
True, basisList=basisList)
641 kcQa.apply(kernelCandList, subtractRes.psfMatchingKernel, subtractRes.backgroundModel,
643 kcQa.apply(controlCandList, subtractRes.psfMatchingKernel, subtractRes.backgroundModel)
645 if self.config.doDetection:
646 kcQa.aggregate(selectSources, self.metadata, allresids, diaSources)
648 kcQa.aggregate(selectSources, self.metadata, allresids)
650 sensorRef.put(selectSources, self.config.coaddName +
"Diff_kernelSrc")
652 if self.config.doWriteSubtractedExp:
653 sensorRef.put(subtractedExposure, subtractedExposureName)
655 self.
runDebug(exposure, subtractRes, selectSources, kernelSources, diaSources)
656 return pipeBase.Struct(
657 subtractedExposure=subtractedExposure,
658 subtractRes=subtractRes,
663 """Fit the relative astrometry between templateSources and selectSources
665 @todo remove this method. It originally fit a new WCS to the template before calling register.run
666 because our TAN-SIP fitter behaved badly for points far from CRPIX, but that's been fixed.
667 It remains because a subtask overrides it.
669 results = self.register.run(templateSources, templateExposure.getWcs(),
670 templateExposure.getBBox(), selectSources)
673 def runDebug(self, exposure, subtractRes, selectSources, kernelSources, diaSources):
674 """@todo Test and update for current debug display and slot names
685 if not maskTransparency:
687 ds9.setMaskTransparency(maskTransparency)
689 if display
and showSubtracted:
690 ds9.mtv(subtractRes.subtractedExposure, frame=lsstDebug.frame, title=
"Subtracted image")
691 mi = subtractRes.subtractedExposure.getMaskedImage()
692 x0, y0 = mi.getX0(), mi.getY0()
693 with ds9.Buffering():
695 x, y = s.getX() - x0, s.getY() - y0
696 ctype =
"red" if s.get(
"flags.negative")
else "yellow"
697 if (s.get(
"flags.pixel.interpolated.center")
or s.get(
"flags.pixel.saturated.center")
or
698 s.get(
"flags.pixel.cr.center")):
700 elif (s.get(
"flags.pixel.interpolated.any")
or s.get(
"flags.pixel.saturated.any")
or
701 s.get(
"flags.pixel.cr.any")):
705 ds9.dot(ptype, x, y, size=4, frame=lsstDebug.frame, ctype=ctype)
708 if display
and showPixelResiduals
and selectSources:
709 nonKernelSources = []
710 for source
in selectSources:
711 if not source
in kernelSources:
712 nonKernelSources.append(source)
714 diUtils.plotPixelResiduals(exposure,
715 subtractRes.warpedExposure,
716 subtractRes.subtractedExposure,
717 subtractRes.kernelCellSet,
718 subtractRes.psfMatchingKernel,
719 subtractRes.backgroundModel,
721 self.subtract.config.kernel.active.detectionConfig,
723 diUtils.plotPixelResiduals(exposure,
724 subtractRes.warpedExposure,
725 subtractRes.subtractedExposure,
726 subtractRes.kernelCellSet,
727 subtractRes.psfMatchingKernel,
728 subtractRes.backgroundModel,
730 self.subtract.config.kernel.active.detectionConfig,
732 if display
and showDiaSources:
733 flagChecker = SourceFlagChecker(diaSources)
734 isFlagged = [flagChecker(x)
for x
in diaSources]
735 isDipole = [x.get(
"classification.dipole")
for x
in diaSources]
736 diUtils.showDiaSources(diaSources, subtractRes.subtractedExposure, isFlagged, isDipole,
737 frame=lsstDebug.frame)
740 if display
and showDipoles:
741 DipoleAnalysis().displayDipoles(subtractRes.subtractedExposure, diaSources,
742 frame=lsstDebug.frame)
746 """Return the name of the config dataset
748 return "%sDiff_config" % (self.config.coaddName,)
751 """Return the name of the metadata dataset
753 return "%sDiff_metadata" % (self.config.coaddName,)
756 """Return a dict of empty catalogs for each catalog dataset produced by this task."""
759 return {self.config.coaddName +
"Diff_diaSrc": diaSrc}
763 """Create an argument parser
765 parser = pipeBase.ArgumentParser(name=cls._DefaultName)
766 parser.add_id_argument(
"--id",
"calexp", help=
"data ID, e.g. --id visit=12345 ccd=1,2")
767 parser.add_id_argument(
"--templateId",
"calexp", doMakeDataRefList=
True,
768 help=
"Optional template data ID (visit only), e.g. --templateId visit=6789")
773 winter2013WcsShift = pexConfig.Field(dtype=float, default=0.0,
774 doc=
"Shift stars going into RegisterTask by this amount")
775 winter2013WcsRms = pexConfig.Field(dtype=float, default=0.0,
776 doc=
"Perturb stars going into RegisterTask by this amount")
779 ImageDifferenceConfig.setDefaults(self)
780 self.getTemplate.retarget(GetCalexpAsTemplateTask)
784 """!Image difference Task used in the Winter 2013 data challege.
785 Enables testing the effects of registration shifts and scatter.
787 For use with winter 2013 simulated images:
788 Use --templateId visit=88868666 for sparse data
789 --templateId visit=22222200 for dense data (g)
790 --templateId visit=11111100 for dense data (i)
792 ConfigClass = Winter2013ImageDifferenceConfig
793 _DefaultName =
"winter2013ImageDifference"
796 ImageDifferenceTask.__init__(self, **kwargs)
799 """Fit the relative astrometry between templateSources and selectSources"""
800 if self.config.winter2013WcsShift > 0.0:
802 self.config.winter2013WcsShift)
803 cKey = templateSources[0].getTable().getCentroidKey()
804 for source
in templateSources:
805 centroid = source.get(cKey)
806 source.set(cKey, centroid+offset)
807 elif self.config.winter2013WcsRms > 0.0:
808 cKey = templateSources[0].getTable().getCentroidKey()
809 for source
in templateSources:
810 offset =
afwGeom.Extent2D(self.config.winter2013WcsRms*numpy.random.normal(),
811 self.config.winter2013WcsRms*numpy.random.normal())
812 centroid = source.get(cKey)
813 source.set(cKey, centroid+offset)
815 results = self.register.run(templateSources, templateExposure.getWcs(),
816 templateExposure.getBBox(), selectSources)
A class to contain various attributes of the Psf.
Class for storing ordered metadata with comments.
Parameters to control convolution.
std::vector< Match< typename Cat::Record, typename Cat::Record > > matchRaDec(Cat const &cat, Angle radius, bool symmetric)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
Represent a PSF as a circularly symmetrical Gaussian.
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
Image difference Task used in the Winter 2013 data challege.
def __init__
Construct an ImageDifference Task.
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)
Old, deprecated version of convolve.
SourceMatchVector matchXy(SourceCatalog const &cat, double radius, bool symmetric)
Compute all tuples (s1,s2,d) where s1 != s2, s1 and s2 both belong to cat, and d, the distance betwee...
A floating-point coordinate rectangle geometry.