35 ObjectSizeStarSelectorTask
37 SourceFlagChecker, KernelCandidateF, cast_KernelCandidateF, makeKernelBasisList, \
38 KernelCandidateQa, DiaCatalogSourceSelectorTask, DiaCatalogSourceSelectorConfig, \
39 GetCoaddAsTemplateTask, GetCalexpAsTemplateTask, DipoleFitTask, DecorrelateALKernelTask
43 FwhmPerSigma = 2 * math.sqrt(2 * math.log(2))
47 """Config for ImageDifferenceTask
49 doAddCalexpBackground = pexConfig.Field(dtype=bool, default=
True,
50 doc=
"Add background to calexp before processing it. Useful as ipDiffim does background matching.")
51 doUseRegister = pexConfig.Field(dtype=bool, default=
True,
52 doc=
"Use image-to-image registration to align template with science image")
53 doDebugRegister = pexConfig.Field(dtype=bool, default=
False,
54 doc=
"Writing debugging data for doUseRegister")
55 doSelectSources = pexConfig.Field(dtype=bool, default=
True,
56 doc=
"Select stars to use for kernel fitting")
57 doSelectDcrCatalog = pexConfig.Field(dtype=bool, default=
False,
58 doc=
"Select stars of extreme color as part of the control sample")
59 doSelectVariableCatalog = pexConfig.Field(dtype=bool, default=
False,
60 doc=
"Select stars that are variable to be part of the control sample")
61 doSubtract = pexConfig.Field(dtype=bool, default=
True, doc=
"Compute subtracted exposure?")
62 doPreConvolve = pexConfig.Field(dtype=bool, default=
True,
63 doc=
"Convolve science image by its PSF before PSF-matching?")
64 useGaussianForPreConvolution = pexConfig.Field(dtype=bool, default=
True,
65 doc=
"Use a simple gaussian PSF model for pre-convolution (else use fit PSF)? "
66 "Ignored if doPreConvolve false.")
67 doDetection = pexConfig.Field(dtype=bool, default=
True, doc=
"Detect sources?")
68 doDecorrelation = pexConfig.Field(dtype=bool, default=
False,
69 doc=
"Perform diffim decorrelation to undo pixel correlation due to A&L kernel convolution? "
70 "If True, also update the diffim PSF.")
71 doMerge = pexConfig.Field(dtype=bool, default=
True,
72 doc=
"Merge positive and negative diaSources with grow radius set by growFootprint")
73 doMatchSources = pexConfig.Field(dtype=bool, default=
True,
74 doc=
"Match diaSources with input calexp sources and ref catalog sources")
75 doMeasurement = pexConfig.Field(dtype=bool, default=
True, doc=
"Measure diaSources?")
76 doDipoleFitting = pexConfig.Field(dtype=bool, default=
True, doc=
"Measure dipoles using new algorithm?")
77 doWriteSubtractedExp = pexConfig.Field(dtype=bool, default=
True, doc=
"Write difference exposure?")
78 doWriteMatchedExp = pexConfig.Field(dtype=bool, default=
False,
79 doc=
"Write warped and PSF-matched template coadd exposure?")
80 doWriteSources = pexConfig.Field(dtype=bool, default=
True, doc=
"Write sources?")
81 doAddMetrics = pexConfig.Field(dtype=bool, default=
True,
82 doc=
"Add columns to the source table to hold analysis metrics?")
84 coaddName = pexConfig.Field(
85 doc=
"coadd name: typically one of deep or goodSeeing",
89 convolveTemplate = pexConfig.Field(
90 doc=
"Which image gets convolved (default = template)",
94 refObjLoader = pexConfig.ConfigurableField(
95 target = measAstrom.LoadAstrometryNetObjectsTask,
96 doc =
"reference object loader",
98 astrometer = pexConfig.ConfigurableField(
99 target = measAstrom.AstrometryTask,
100 doc =
"astrometry task; used to match sources to reference objects, but not to fit a WCS",
102 sourceSelector = pexConfig.ConfigurableField(
103 target=ObjectSizeStarSelectorTask,
104 doc=
"Source selection algorithm",
106 subtract = pexConfig.ConfigurableField(
107 target=ImagePsfMatchTask,
108 doc=
"Warp and PSF match template to exposure, then subtract",
110 decorrelate = pexConfig.ConfigurableField(
111 target=DecorrelateALKernelTask,
112 doc=
"""Decorrelate effects of A&L kernel convolution on image difference, only if doSubtract is True.
113 If this option is enabled, then detection.thresholdValue should be set to 5.0 (rather than the
116 detection = pexConfig.ConfigurableField(
117 target=SourceDetectionTask,
118 doc=
"Low-threshold detection for final measurement",
124 measurement = pexConfig.ConfigurableField(
125 target=DipoleFitTask,
126 doc=
"Enable updated dipole fitting method.",
128 getTemplate = pexConfig.ConfigurableField(
129 target = GetCoaddAsTemplateTask,
130 doc =
"Subtask to retrieve template exposure and sources",
132 controlStepSize = pexConfig.Field(
133 doc=
"What step size (every Nth one) to select a control sample from the kernelSources",
137 controlRandomSeed = pexConfig.Field(
138 doc=
"Random seed for shuffing the control sample",
142 register = pexConfig.ConfigurableField(
144 doc=
"Task to enable image-to-image image registration (warping)",
146 kernelSourcesFromRef = pexConfig.Field(
147 doc=
"Select sources to measure kernel from reference catalog if True, template if false",
151 templateSipOrder = pexConfig.Field(dtype=int, default=2,
152 doc=
"Sip Order for fitting the Template Wcs (default is too high, overfitting)")
154 growFootprint = pexConfig.Field(dtype=int, default=2,
155 doc=
"Grow positive and negative footprints by this amount before merging")
157 diaSourceMatchRadius = pexConfig.Field(dtype=float, default=0.5,
158 doc=
"Match radius (in arcseconds) for DiaSource to Source association")
163 self.subtract.kernel.name =
"AL"
164 self.subtract.kernel.active.fitForBackground =
True
165 self.subtract.kernel.active.spatialKernelOrder = 1
166 self.subtract.kernel.active.spatialBgOrder = 0
173 self.detection.thresholdPolarity =
"both"
174 self.detection.thresholdValue = 5.5
175 self.detection.reEstimateBackground =
False
176 self.detection.thresholdType =
"pixel_stdev"
182 self.measurement.algorithms.names.add(
'base_PeakLikelihoodFlux')
188 pexConfig.Config.validate(self)
190 raise ValueError(
"Cannot run source measurement without source detection.")
192 raise ValueError(
"Cannot run source merging without source detection.")
194 raise ValueError(
"doUseRegister=True and doSelectSources=False. " +
195 "Cannot run RegisterTask without selecting sources.")
201 return pipeBase.TaskRunner.getTargetList(parsedCmd, templateIdList=parsedCmd.templateId.idList,
206 """Subtract an image from a template and measure the result
208 ConfigClass = ImageDifferenceConfig
209 RunnerClass = ImageDifferenceTaskRunner
210 _DefaultName =
"imageDifference"
213 """!Construct an ImageDifference Task
215 @param[in] butler Butler object to use in constructing reference object loaders
217 pipeBase.CmdLineTask.__init__(self, **kwargs)
218 self.makeSubtask(
"subtract")
219 self.makeSubtask(
"getTemplate")
220 self.makeSubtask(
"decorrelate")
222 if self.config.doUseRegister:
223 self.makeSubtask(
"register")
224 self.
schema = afwTable.SourceTable.makeMinimalSchema()
226 if self.config.doSelectSources:
227 self.makeSubtask(
"sourceSelector", schema=self.
schema)
228 self.makeSubtask(
'refObjLoader', butler=butler)
229 self.makeSubtask(
"astrometer", refObjLoader=self.refObjLoader)
232 if self.config.doDetection:
233 self.makeSubtask(
"detection", schema=self.
schema)
234 if self.config.doMeasurement:
235 if not self.config.doDipoleFitting:
236 self.makeSubtask(
"measurement", schema=self.
schema,
239 self.makeSubtask(
"measurement", schema=self.
schema)
240 if self.config.doMatchSources:
241 self.schema.addField(
"refMatchId",
"L",
"unique id of reference catalog match")
242 self.schema.addField(
"srcMatchId",
"L",
"unique id of source match")
245 def run(self, sensorRef, templateIdList=None):
246 """Subtract an image from a template coadd and measure the result
249 - warp template coadd to match WCS of image
250 - PSF match image to warped template
251 - subtract image from PSF-matched, warped template
252 - persist difference image
256 @param sensorRef: sensor-level butler data reference, used for the following data products:
262 - self.config.coaddName + "Coadd_skyMap"
263 - self.config.coaddName + "Coadd"
264 Input or output, depending on config:
265 - self.config.coaddName + "Diff_subtractedExp"
266 Output, depending on config:
267 - self.config.coaddName + "Diff_matchedExp"
268 - self.config.coaddName + "Diff_src"
270 @return pipe_base Struct containing these fields:
271 - subtractedExposure: exposure after subtracting template;
272 the unpersisted version if subtraction not run but detection run
273 None if neither subtraction nor detection run (i.e. nothing useful done)
274 - subtractRes: results of subtraction task; None if subtraction not run
275 - sources: detected and possibly measured sources; None if detection not run
277 self.log.info(
"Processing %s" % (sensorRef.dataId))
280 subtractedExposure =
None
284 controlSources =
None
290 expBits = sensorRef.get(
"ccdExposureId_bits")
291 expId = long(sensorRef.get(
"ccdExposureId"))
292 idFactory = afwTable.IdFactory.makeSource(expId, 64 - expBits)
295 exposure = sensorRef.get(
"calexp", immediate=
True)
296 if self.config.doAddCalexpBackground:
297 mi = exposure.getMaskedImage()
298 mi += sensorRef.get(
"calexpBackground").getImage()
299 if not exposure.hasPsf():
300 raise pipeBase.TaskError(
"Exposure has no psf")
301 sciencePsf = exposure.getPsf()
303 subtractedExposureName = self.config.coaddName +
"Diff_differenceExp"
304 templateExposure =
None
305 templateSources =
None
306 if self.config.doSubtract:
307 template = self.getTemplate.run(exposure, sensorRef, templateIdList=templateIdList)
308 templateExposure = template.exposure
309 templateSources = template.sources
314 scienceSigmaOrig = psfAttr.computeGaussianWidth(psfAttr.ADAPTIVE_MOMENT)
319 templateSigma = psfAttr.computeGaussianWidth(psfAttr.ADAPTIVE_MOMENT)
325 if self.config.doPreConvolve:
328 srcMI = exposure.getMaskedImage()
329 destMI = srcMI.Factory(srcMI.getDimensions())
331 if self.config.useGaussianForPreConvolution:
333 kWidth, kHeight = sciencePsf.getLocalKernel().getDimensions()
339 exposure.setMaskedImage(destMI)
340 scienceSigmaPost = scienceSigmaOrig * math.sqrt(2)
342 scienceSigmaPost = scienceSigmaOrig
345 if self.config.doSelectSources:
346 if not sensorRef.datasetExists(
"src"):
347 self.log.warn(
"Src product does not exist; running detection, measurement, selection")
349 selectSources = self.subtract.getSelectSources(
351 sigma = scienceSigmaPost,
352 doSmooth =
not self.doPreConvolve,
353 idFactory = idFactory,
356 self.log.info(
"Source selection via src product")
358 selectSources = sensorRef.get(
"src")
362 referenceFwhmPix=scienceSigmaPost * FwhmPerSigma,
363 targetFwhmPix=templateSigma * FwhmPerSigma))
365 if self.config.doAddMetrics:
367 kcQa = KernelCandidateQa(nparam)
368 selectSources = kcQa.addToSchema(selectSources)
370 if self.config.kernelSourcesFromRef:
372 astromRet = self.astrometer.loadAndMatch(exposure=exposure, sourceCat=selectSources)
373 matches = astromRet.matches
374 elif templateSources:
379 raise RuntimeError(
"doSelectSources=True and kernelSourcesFromRef=False," +
380 "but template sources not available. Cannot match science " +
381 "sources with template sources. Run process* on data from " +
382 "which templates are built.")
384 kernelSources = self.sourceSelector.selectStars(exposure, selectSources,
385 matches=matches).starCat
387 random.shuffle(kernelSources, random.random)
388 controlSources = kernelSources[::self.config.controlStepSize]
389 kernelSources = [k
for i,k
in enumerate(kernelSources)
if i % self.config.controlStepSize]
391 if self.config.doSelectDcrCatalog:
392 redSelector = DiaCatalogSourceSelectorTask(
393 DiaCatalogSourceSelectorConfig(grMin=self.sourceSelector.config.grMax, grMax=99.999))
394 redSources = redSelector.selectStars(exposure, selectSources, matches=matches).starCat
395 controlSources.extend(redSources)
397 blueSelector = DiaCatalogSourceSelectorTask(
398 DiaCatalogSourceSelectorConfig(grMin=-99.999, grMax=self.sourceSelector.config.grMin))
399 blueSources = blueSelector.selectStars(exposure, selectSources, matches=matches).starCat
400 controlSources.extend(blueSources)
402 if self.config.doSelectVariableCatalog:
403 varSelector = DiaCatalogSourceSelectorTask(
404 DiaCatalogSourceSelectorConfig(includeVariable=
True))
405 varSources = varSelector.selectStars(exposure, selectSources, matches=matches).starCat
406 controlSources.extend(varSources)
408 self.log.info(
"Selected %d / %d sources for Psf matching (%d for control sample)"
409 % (len(kernelSources), len(selectSources), len(controlSources)))
411 if self.config.doUseRegister:
412 self.log.info(
"Registering images")
414 if templateSources
is None:
417 templateSources = self.subtract.getSelectSources(
426 wcsResults = self.
fitAstrometry(templateSources, templateExposure, selectSources)
427 warpedExp = self.register.warpExposure(templateExposure, wcsResults.wcs,
428 exposure.getWcs(), exposure.getBBox())
429 templateExposure = warpedExp
434 if self.config.doDebugRegister:
436 srcToMatch = {x.second.getId() : x.first
for x
in matches}
438 refCoordKey = wcsResults.matches[0].first.getTable().getCoordKey()
439 inCentroidKey = wcsResults.matches[0].second.getTable().getCentroidKey()
440 sids = [m.first.getId()
for m
in wcsResults.matches]
441 positions = [m.first.get(refCoordKey)
for m
in wcsResults.matches]
442 residuals = [m.first.get(refCoordKey).getOffsetFrom(wcsResults.wcs.pixelToSky(
443 m.second.get(inCentroidKey)))
for m
in wcsResults.matches]
444 allresids = dict(zip(sids, zip(positions, residuals)))
446 cresiduals = [m.first.get(refCoordKey).getTangentPlaneOffset(
447 wcsResults.wcs.pixelToSky(
448 m.second.get(inCentroidKey)))
for m
in wcsResults.matches]
449 colors = numpy.array([-2.5*numpy.log10(srcToMatch[x].get(
"g"))
450 + 2.5*numpy.log10(srcToMatch[x].get(
"r"))
451 for x
in sids
if x
in srcToMatch.keys()])
452 dlong = numpy.array([r[0].asArcseconds()
for s,r
in zip(sids, cresiduals)
453 if s
in srcToMatch.keys()])
454 dlat = numpy.array([r[1].asArcseconds()
for s,r
in zip(sids, cresiduals)
455 if s
in srcToMatch.keys()])
456 idx1 = numpy.where(colors<self.sourceSelector.config.grMin)
457 idx2 = numpy.where((colors>=self.sourceSelector.config.grMin)&
458 (colors<=self.sourceSelector.config.grMax))
459 idx3 = numpy.where(colors>self.sourceSelector.config.grMax)
460 rms1Long = IqrToSigma*(numpy.percentile(dlong[idx1],75)-numpy.percentile(dlong[idx1],25))
461 rms1Lat = IqrToSigma*(numpy.percentile(dlat[idx1],75)-numpy.percentile(dlat[idx1],25))
462 rms2Long = IqrToSigma*(numpy.percentile(dlong[idx2],75)-numpy.percentile(dlong[idx2],25))
463 rms2Lat = IqrToSigma*(numpy.percentile(dlat[idx2],75)-numpy.percentile(dlat[idx2],25))
464 rms3Long = IqrToSigma*(numpy.percentile(dlong[idx3],75)-numpy.percentile(dlong[idx3],25))
465 rms3Lat = IqrToSigma*(numpy.percentile(dlat[idx3],75)-numpy.percentile(dlat[idx3],25))
466 self.log.info(
"Blue star offsets'': %.3f %.3f, %.3f %.3f" % (numpy.median(dlong[idx1]),
468 numpy.median(dlat[idx1]),
470 self.log.info(
"Green star offsets'': %.3f %.3f, %.3f %.3f" % (numpy.median(dlong[idx2]),
472 numpy.median(dlat[idx2]),
474 self.log.info(
"Red star offsets'': %.3f %.3f, %.3f %.3f" % (numpy.median(dlong[idx3]),
476 numpy.median(dlat[idx3]),
479 self.metadata.add(
"RegisterBlueLongOffsetMedian", numpy.median(dlong[idx1]))
480 self.metadata.add(
"RegisterGreenLongOffsetMedian", numpy.median(dlong[idx2]))
481 self.metadata.add(
"RegisterRedLongOffsetMedian", numpy.median(dlong[idx3]))
482 self.metadata.add(
"RegisterBlueLongOffsetStd", rms1Long)
483 self.metadata.add(
"RegisterGreenLongOffsetStd", rms2Long)
484 self.metadata.add(
"RegisterRedLongOffsetStd", rms3Long)
486 self.metadata.add(
"RegisterBlueLatOffsetMedian", numpy.median(dlat[idx1]))
487 self.metadata.add(
"RegisterGreenLatOffsetMedian", numpy.median(dlat[idx2]))
488 self.metadata.add(
"RegisterRedLatOffsetMedian", numpy.median(dlat[idx3]))
489 self.metadata.add(
"RegisterBlueLatOffsetStd", rms1Lat)
490 self.metadata.add(
"RegisterGreenLatOffsetStd", rms2Lat)
491 self.metadata.add(
"RegisterRedLatOffsetStd", rms3Lat)
498 self.log.info(
"Subtracting images")
499 subtractRes = self.subtract.subtractExposures(
500 templateExposure=templateExposure,
501 scienceExposure=exposure,
502 candidateList=kernelSources,
503 convolveTemplate=self.config.convolveTemplate,
504 doWarping=
not self.config.doUseRegister
506 subtractedExposure = subtractRes.subtractedExposure
508 if self.config.doWriteMatchedExp:
509 sensorRef.put(subtractRes.matchedExposure, self.config.coaddName +
"Diff_matchedExp")
511 if self.config.doDetection:
512 self.log.info(
"Computing diffim PSF")
513 if subtractedExposure
is None:
514 subtractedExposure = sensorRef.get(subtractedExposureName)
517 if not subtractedExposure.hasPsf():
518 if self.config.convolveTemplate:
519 subtractedExposure.setPsf(exposure.getPsf())
521 if templateExposure
is None:
522 template = self.getTemplate.run(exposure, sensorRef, templateIdList=templateIdList)
523 subtractedExposure.setPsf(template.exposure.getPsf())
527 if self.config.doDecorrelation
and self.config.doSubtract:
528 decorrResult = self.decorrelate.run(exposure, templateExposure,
530 subtractRes.psfMatchingKernel)
531 subtractedExposure = decorrResult.correctedExposure
533 if self.config.doDetection:
534 self.log.info(
"Running diaSource detection")
536 mask = subtractedExposure.getMaskedImage().getMask()
537 mask &= ~(mask.getPlaneBitMask(
"DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE"))
539 table = afwTable.SourceTable.make(self.
schema, idFactory)
541 results = self.detection.makeSourceCatalog(
543 exposure=subtractedExposure,
544 doSmooth=
not self.config.doPreConvolve
547 if self.config.doMerge:
548 fpSet = results.fpSets.positive
549 fpSet.merge(results.fpSets.negative, self.config.growFootprint,
550 self.config.growFootprint,
False)
552 fpSet.makeSources(diaSources)
553 self.log.info(
"Merging detections into %d sources" % (len(diaSources)))
555 diaSources = results.sources
557 if self.config.doMeasurement:
558 self.log.info(
"Running diaSource measurement")
559 if not self.config.doDipoleFitting:
560 self.measurement.run(diaSources, subtractedExposure)
562 if self.config.doSubtract:
563 self.measurement.run(diaSources, subtractedExposure, exposure,
564 subtractRes.matchedExposure)
566 self.measurement.run(diaSources, subtractedExposure, exposure)
569 if self.config.doMatchSources:
570 if sensorRef.datasetExists(
"src"):
572 matchRadAsec = self.config.diaSourceMatchRadius
573 matchRadPixel = matchRadAsec / exposure.getWcs().pixelScale().asArcseconds()
575 srcMatches =
afwTable.matchXy(sensorRef.get(
"src"), diaSources, matchRadPixel,
True)
576 srcMatchDict = dict([(srcMatch.second.getId(), srcMatch.first.getId())
for
577 srcMatch
in srcMatches])
578 self.log.info(
"Matched %d / %d diaSources to sources" % (len(srcMatchDict),
581 self.log.warn(
"Src product does not exist; cannot match with diaSources")
585 refAstromConfig = measAstrom.AstrometryConfig()
586 refAstromConfig.matcher.maxMatchDistArcSec = matchRadAsec
587 refAstrometer = measAstrom.AstrometryTask(refAstromConfig)
588 astromRet = refAstrometer.run(exposure=exposure, sourceCat=diaSources)
589 refMatches = astromRet.matches
590 if refMatches
is None:
591 self.log.warn(
"No diaSource matches with reference catalog")
594 self.log.info(
"Matched %d / %d diaSources to reference catalog" % (len(refMatches),
596 refMatchDict = dict([(refMatch.second.getId(), refMatch.first.getId())
for \
597 refMatch
in refMatches])
600 for diaSource
in diaSources:
601 sid = diaSource.getId()
602 if srcMatchDict.has_key(sid):
603 diaSource.set(
"srcMatchId", srcMatchDict[sid])
604 if refMatchDict.has_key(sid):
605 diaSource.set(
"refMatchId", refMatchDict[sid])
607 if diaSources
is not None and self.config.doWriteSources:
608 sensorRef.put(diaSources, self.config.coaddName +
"Diff_diaSrc")
610 if self.config.doAddMetrics
and self.config.doSelectSources:
611 self.log.info(
"Evaluating metrics and control sample")
614 for cell
in subtractRes.kernelCellSet.getCellList():
615 for cand
in cell.begin(
False):
616 kernelCandList.append(cast_KernelCandidateF(cand))
619 basisList = afwMath.cast_LinearCombinationKernel(
620 kernelCandList[0].getKernel(KernelCandidateF.ORIG)).getKernelList()
623 diffimTools.sourceTableToCandidateList(controlSources,
624 subtractRes.warpedExposure, exposure,
625 self.config.subtract.kernel.active,
626 self.config.subtract.kernel.active.detectionConfig,
627 self.log, doBuild=
True, basisList=basisList)
629 kcQa.apply(kernelCandList, subtractRes.psfMatchingKernel, subtractRes.backgroundModel,
631 kcQa.apply(controlCandList, subtractRes.psfMatchingKernel, subtractRes.backgroundModel)
633 if self.config.doDetection:
634 kcQa.aggregate(selectSources, self.metadata, allresids, diaSources)
636 kcQa.aggregate(selectSources, self.metadata, allresids)
638 sensorRef.put(selectSources, self.config.coaddName +
"Diff_kernelSrc")
640 if self.config.doWriteSubtractedExp:
641 sensorRef.put(subtractedExposure, subtractedExposureName)
643 self.
runDebug(exposure, subtractRes, selectSources, kernelSources, diaSources)
644 return pipeBase.Struct(
645 subtractedExposure=subtractedExposure,
646 subtractRes=subtractRes,
651 """Fit the relative astrometry between templateSources and selectSources
653 @todo remove this method. It originally fit a new WCS to the template before calling register.run
654 because our TAN-SIP fitter behaved badly for points far from CRPIX, but that's been fixed.
655 It remains because a subtask overrides it.
657 results = self.register.run(templateSources, templateExposure.getWcs(),
658 templateExposure.getBBox(), selectSources)
661 def runDebug(self, exposure, subtractRes, selectSources, kernelSources, diaSources):
662 """@todo Test and update for current debug display and slot names
673 if not maskTransparency:
675 ds9.setMaskTransparency(maskTransparency)
677 if display
and showSubtracted:
678 ds9.mtv(subtractRes.subtractedExposure, frame=lsstDebug.frame, title=
"Subtracted image")
679 mi = subtractRes.subtractedExposure.getMaskedImage()
680 x0, y0 = mi.getX0(), mi.getY0()
681 with ds9.Buffering():
683 x, y = s.getX() - x0, s.getY() - y0
684 ctype =
"red" if s.get(
"flags.negative")
else "yellow"
685 if (s.get(
"flags.pixel.interpolated.center")
or s.get(
"flags.pixel.saturated.center")
or
686 s.get(
"flags.pixel.cr.center")):
688 elif (s.get(
"flags.pixel.interpolated.any")
or s.get(
"flags.pixel.saturated.any")
or
689 s.get(
"flags.pixel.cr.any")):
693 ds9.dot(ptype, x, y, size=4, frame=lsstDebug.frame, ctype=ctype)
696 if display
and showPixelResiduals
and selectSources:
697 nonKernelSources = []
698 for source
in selectSources:
699 if not source
in kernelSources:
700 nonKernelSources.append(source)
702 diUtils.plotPixelResiduals(exposure,
703 subtractRes.warpedExposure,
704 subtractRes.subtractedExposure,
705 subtractRes.kernelCellSet,
706 subtractRes.psfMatchingKernel,
707 subtractRes.backgroundModel,
709 self.subtract.config.kernel.active.detectionConfig,
711 diUtils.plotPixelResiduals(exposure,
712 subtractRes.warpedExposure,
713 subtractRes.subtractedExposure,
714 subtractRes.kernelCellSet,
715 subtractRes.psfMatchingKernel,
716 subtractRes.backgroundModel,
718 self.subtract.config.kernel.active.detectionConfig,
720 if display
and showDiaSources:
721 flagChecker = SourceFlagChecker(diaSources)
722 isFlagged = [flagChecker(x)
for x
in diaSources]
723 isDipole = [x.get(
"classification.dipole")
for x
in diaSources]
724 diUtils.showDiaSources(diaSources, subtractRes.subtractedExposure, isFlagged, isDipole,
725 frame=lsstDebug.frame)
728 if display
and showDipoles:
729 DipoleAnalysis().displayDipoles(subtractRes.subtractedExposure, diaSources,
730 frame=lsstDebug.frame)
734 """Return the name of the config dataset
736 return "%sDiff_config" % (self.config.coaddName,)
739 """Return the name of the metadata dataset
741 return "%sDiff_metadata" % (self.config.coaddName,)
744 """Return a dict of empty catalogs for each catalog dataset produced by this task."""
747 return {self.config.coaddName +
"Diff_diaSrc": diaSrc}
751 """Create an argument parser
753 parser = pipeBase.ArgumentParser(name=cls._DefaultName)
754 parser.add_id_argument(
"--id",
"calexp", help=
"data ID, e.g. --id visit=12345 ccd=1,2")
755 parser.add_id_argument(
"--templateId",
"calexp", doMakeDataRefList=
True,
756 help=
"Optional template data ID (visit only), e.g. --templateId visit=6789")
760 winter2013WcsShift = pexConfig.Field(dtype=float, default=0.0,
761 doc=
"Shift stars going into RegisterTask by this amount")
762 winter2013WcsRms = pexConfig.Field(dtype=float, default=0.0,
763 doc=
"Perturb stars going into RegisterTask by this amount")
766 ImageDifferenceConfig.setDefaults(self)
767 self.getTemplate.retarget(GetCalexpAsTemplateTask)
771 """!Image difference Task used in the Winter 2013 data challege.
772 Enables testing the effects of registration shifts and scatter.
774 For use with winter 2013 simulated images:
775 Use --templateId visit=88868666 for sparse data
776 --templateId visit=22222200 for dense data (g)
777 --templateId visit=11111100 for dense data (i)
779 ConfigClass = Winter2013ImageDifferenceConfig
780 _DefaultName =
"winter2013ImageDifference"
783 ImageDifferenceTask.__init__(self, **kwargs)
786 """Fit the relative astrometry between templateSources and selectSources"""
787 if self.config.winter2013WcsShift > 0.0:
789 self.config.winter2013WcsShift)
790 cKey = templateSources[0].getTable().getCentroidKey()
791 for source
in templateSources:
792 centroid = source.get(cKey)
793 source.set(cKey, centroid+offset)
794 elif self.config.winter2013WcsRms > 0.0:
795 cKey = templateSources[0].getTable().getCentroidKey()
796 for source
in templateSources:
797 offset =
afwGeom.Extent2D(self.config.winter2013WcsRms*numpy.random.normal(),
798 self.config.winter2013WcsRms*numpy.random.normal())
799 centroid = source.get(cKey)
800 source.set(cKey, centroid+offset)
802 results = self.register.run(templateSources, templateExposure.getWcs(),
803 templateExposure.getBBox(), selectSources)
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)
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)
A floating-point coordinate rectangle geometry.