def __init__(self, butler=None, schema=None, initInputs=None, **kwargs): # Make PipelineTask-only wording less transitional after cmdlineTask is removed """! @brief Initialize the merge detections task. A @ref FootprintMergeList_ "FootprintMergeList" will be used to merge the source catalogs. @param[in] schema the schema of the detection catalogs used as input to this one @param[in] butler a butler used to read the input schema from disk, if schema is None @param[in] initInputs This a PipelineTask-only argument that holds all inputs passed in through the PipelineTask middleware @param[in] **kwargs keyword arguments to be passed to CmdLineTask.__init__ The task will set its own self.schema attribute to the schema of the output merged catalog. """ super().__init__(**kwargs) if initInputs is not None: schema = initInputs['schema'].schema self.makeSubtask("skyObjects") self.schema = self.getInputSchema(butler=butler, schema=schema) filterNames = [ getShortFilterName(name) for name in self.config.priorityList ] filterNames += [self.config.skyFilterName] self.merged = afwDetect.FootprintMergeList(self.schema, filterNames) self.outputSchema = afwTable.SourceCatalog(self.schema) self.outputPeakSchema = afwDetect.PeakCatalog( self.merged.getPeakSchema())
def getSchemaCatalogs(self): """! Return a dict of empty catalogs for each catalog dataset produced by this task. @param[out] dictionary of empty catalogs """ mergeDet = afwTable.SourceCatalog(self.schema) peak = afwDetect.PeakCatalog(self.merged.getPeakSchema()) return {self.config.coaddName + "Coadd_mergeDet": mergeDet, self.config.coaddName + "Coadd_peak": peak}
def setPeakSignificance(self, exposure, footprints, threshold, negative=False): """Set the significance of each detected peak to the pixel value divided by the appropriate standard-deviation for ``config.thresholdType``. Only sets significance for "stdev" and "pixel_stdev" thresholdTypes; we leave it undefined for "value" and "variance" as it does not have a well-defined meaning in those cases. Parameters ---------- exposure : `lsst.afw.image.Exposure` Exposure that footprints were detected on, likely the convolved, local background-subtracted image. footprints : `lsst.afw.detection.FootprintSet` Footprints detected on the image. threshold : `lsst.afw.detection.Threshold` Threshold used to find footprints. negative : `bool`, optional Are we calculating for negative sources? """ if footprints is None or footprints.getFootprints() == []: return footprints polarity = -1 if negative else 1 # All incoming footprints have the same schema. mapper = afwTable.SchemaMapper(footprints.getFootprints()[0].peaks.schema) mapper.addMinimalSchema(footprints.getFootprints()[0].peaks.schema) mapper.addOutputField("significance", type=float, doc="Ratio of peak value to configured standard deviation.") # Copy the old peaks to the new ones with a significance field. # Do this independent of the threshold type, so we always have a # significance field. newFootprints = afwDet.FootprintSet(footprints) for old, new in zip(footprints.getFootprints(), newFootprints.getFootprints()): newPeaks = afwDet.PeakCatalog(mapper.getOutputSchema()) newPeaks.extend(old.peaks, mapper=mapper) new.getPeaks().clear() new.setPeakCatalog(newPeaks) # Compute the significance values. if self.config.thresholdType == "pixel_stdev": for footprint in newFootprints.getFootprints(): footprint.updatePeakSignificance(exposure.variance, polarity) elif self.config.thresholdType == "stdev": sigma = threshold.getValue() / self.config.thresholdValue for footprint in newFootprints.getFootprints(): footprint.updatePeakSignificance(polarity*sigma) else: for footprint in newFootprints.getFootprints(): for peak in footprint.peaks: peak["significance"] = 0 return newFootprints
def updatePeaks(self, fpSet, image, threshold): """Update the Peaks in a FootprintSet by detecting new Footprints and Peaks in an image and using the new Peaks instead of the old ones. Parameters ---------- fpSet : `afw.detection.FootprintSet` Set of Footprints whose Peaks should be updated. image : `afw.image.MaskedImage` Image to detect new Footprints and Peak in. threshold : `afw.detection.Threshold` Threshold object for detection. Input Footprints with fewer Peaks than self.config.nPeaksMaxSimple are not modified, and if no new Peaks are detected in an input Footprint, the brightest original Peak in that Footprint is kept. """ for footprint in fpSet.getFootprints(): oldPeaks = footprint.getPeaks() if len(oldPeaks) <= self.config.nPeaksMaxSimple: continue # We detect a new FootprintSet within each non-simple Footprint's # bbox to avoid a big O(N^2) comparison between the two sets of # Footprints. sub = image.Factory(image, footprint.getBBox()) fpSetForPeaks = afwDet.FootprintSet( sub, threshold, "", # don't set a mask plane self.config.minPixels ) newPeaks = afwDet.PeakCatalog(oldPeaks.getTable()) for fpForPeaks in fpSetForPeaks.getFootprints(): for peak in fpForPeaks.getPeaks(): if footprint.contains(peak.getI()): newPeaks.append(peak) if len(newPeaks) > 0: del oldPeaks[:] oldPeaks.extend(newPeaks) else: del oldPeaks[1:]
def modelToHeavy(source, filters, xy0=Point2I(), observation=None, dtype=np.float32): """Convert the model to a `MultibandFootprint` Parameters ---------- source : `scarlet.Component` The source to convert to a `HeavyFootprint`. filters : `iterable` A "list" of names for each filter. xy0 : `lsst.geom.Point2I` `(x,y)` coordinates of the bounding box containing the `HeavyFootprint`. observation : `scarlet.Observation` The scarlet observation, used to convolve the image with the origin PSF. If `observation`` is `None` then the `HeavyFootprint` will exist in the model frame. dtype : `numpy.dtype` The data type for the returned `HeavyFootprint`. Returns ------- mHeavy : `lsst.detection.MultibandFootprint` The multi-band footprint containing the model for the source. """ if observation is not None: model = observation.render(source.get_model()).astype(dtype) else: model = source.get_model().astype(dtype) mHeavy = afwDet.MultibandFootprint.fromArrays(filters, model, xy0=xy0) peakCat = afwDet.PeakCatalog(source.detectedPeak.table) peakCat.append(source.detectedPeak) for footprint in mHeavy: footprint.setPeakCatalog(peakCat) return mHeavy
def addSignificance(footprints): """Return a new FootprintSet with a significance field added.""" mapper = afwTable.SchemaMapper( footprints.getFootprints()[0].peaks.schema) mapper.addMinimalSchema(footprints.getFootprints()[0].peaks.schema) mapper.addOutputField( "significance", type=float, doc="Ratio of peak value to configured standard deviation.") newFootprints = afwDetect.FootprintSet(footprints) for old, new in zip(footprints.getFootprints(), newFootprints.getFootprints()): newPeaks = afwDetect.PeakCatalog(mapper.getOutputSchema()) newPeaks.extend(old.peaks, mapper=mapper) new.getPeaks().clear() new.setPeakCatalog(newPeaks) for footprint in newFootprints.getFootprints(): for peak in footprint.peaks: peak['significance'] = 10 return newFootprints
def modelToHeavy(source, filters, xy0=Point2I(), observation=None, dtype=np.float32): """Convert the model to a `MultibandFootprint` Parameters ---------- source : `scarlet.Component` The source to convert to a `HeavyFootprint`. filters : `iterable` A "list" of names for each filter. xy0 : `lsst.geom.Point2I` `(x,y)` coordinates of the bounding box containing the `HeavyFootprint`. If `observation` is not `None` then this parameter is updated with the position of the new model observation : `scarlet.Observation` The scarlet observation, used to convolve the image with the origin PSF. If `observation`` is `None` then the `HeavyFootprint` will exist in the model frame. dtype : `numpy.dtype` The data type for the returned `HeavyFootprint`. Returns ------- mHeavy : `lsst.detection.MultibandFootprint` The multi-band footprint containing the model for the source. """ if observation is not None: # We want to convolve the model with the observed PSF, # which means we need to grow the model box by the PSF to # account for all of the flux after convolution. # FYI: The `scarlet.Box` class implements the `&` operator # to take the intersection of two boxes. # Get the PSF size and radii to grow the box py, px = observation.frame.psf.get_model().shape[1:] dh = py // 2 dw = px // 2 shape = (source.bbox.shape[0], source.bbox.shape[1] + py, source.bbox.shape[2] + px) origin = (source.bbox.origin[0], source.bbox.origin[1] - dh, source.bbox.origin[2] - dw) # Create the larger box to fit the model + PSf bbox = Box(shape, origin=origin) # Only use the portion of the convolved model that fits in the image overlap = Frame(bbox & source.frame.bbox, source.frame.channels, psfs=source.frame.psf) # Load the full multiband model in the larger box model = source.model_to_frame(overlap) # Convolve the model with the PSF in each band # Always use a real space convolution to limit artifacts model = observation.convolve(model, convolution_type="real").astype(dtype) # Update xy0 with the origin of the sources box xy0 = Point2I(overlap.origin[-1] + xy0.x, overlap.origin[-2] + xy0.y) else: model = source.get_model().astype(dtype) mHeavy = afwDet.MultibandFootprint.fromArrays(filters, model, xy0=xy0) peakCat = afwDet.PeakCatalog(source.detectedPeak.table) peakCat.append(source.detectedPeak) for footprint in mHeavy: footprint.setPeakCatalog(peakCat) return mHeavy
def test1(self): # circle spans = afwGeom.SpanSet.fromShape(45, offset=(50, 50)) fp = afwDet.Footprint(spans) # psfsig = 1.5 psffwhm = psfsig * 2.35 psf1 = measAlg.DoubleGaussianPsf(11, 11, psfsig) psf2 = measAlg.DoubleGaussianPsf(100, 100, psfsig) fbb = fp.getBBox() print('fbb', fbb.getMinX(), fbb.getMaxX(), fbb.getMinY(), fbb.getMaxY()) fmask = afwImage.Mask(fbb) fmask.setXY0(fbb.getMinX(), fbb.getMinY()) fp.spans.setMask(fmask, 1) sig1 = 10. img = afwImage.ImageF(fbb) A = img.getArray() A += np.random.normal(0, sig1, size=(fbb.getHeight(), fbb.getWidth())) print('img x0,y0', img.getX0(), img.getY0()) print('BBox', img.getBBox()) peaks = afwDet.PeakCatalog(afwDet.PeakTable.makeMinimalSchema()) def makePeak(x, y): p = peaks.addNew() p.setFx(x) p.setFy(y) p.setIx(int(x)) p.setIy(int(y)) return p pk1 = makePeak(20., 30.) pk2 = makePeak(23., 33.) pk3 = makePeak(92., 50.) ibb = img.getBBox() iext = [ibb.getMinX(), ibb.getMaxX(), ibb.getMinY(), ibb.getMaxY()] ix0, iy0 = iext[0], iext[2] pbbs = [] pxys = [] fluxes = [10000., 5000., 5000.] for pk, f in zip(peaks, fluxes): psfim = psf1.computeImage(afwGeom.Point2D(pk.getFx(), pk.getFy())) print('psfim x0,y0', psfim.getX0(), psfim.getY0()) pbb = psfim.getBBox() print('pbb', pbb.getMinX(), pbb.getMaxX(), pbb.getMinY(), pbb.getMaxY()) pbb.clip(ibb) print('clipped pbb', pbb.getMinX(), pbb.getMaxX(), pbb.getMinY(), pbb.getMaxY()) psfim = psfim.Factory(psfim, pbb) psfa = psfim.getArray() psfa /= psfa.sum() img.getArray()[pbb.getMinY() - iy0:pbb.getMaxY() + 1 - iy0, pbb.getMinX() - ix0:pbb.getMaxX() + 1 - ix0] += f * psfa pbbs.append( (pbb.getMinX(), pbb.getMaxX(), pbb.getMinY(), pbb.getMaxY())) pxys.append((pk.getFx(), pk.getFy())) if doPlot: plt.clf() plt.imshow(img.getArray(), extent=iext, interpolation='nearest', origin='lower') ax = plt.axis() x0, x1, y0, y1 = fbb.getMinX(), fbb.getMaxX(), fbb.getMinY( ), fbb.getMaxY() plt.plot([x0, x0, x1, x1, x0], [y0, y1, y1, y0, y0], 'k-') for x0, x1, y0, y1 in pbbs: plt.plot([x0, x0, x1, x1, x0], [y0, y1, y1, y0, y0], 'r-') for x, y in pxys: plt.plot(x, y, 'ro') plt.axis(ax) plt.savefig('img.png') varimg = afwImage.ImageF(fbb) varimg.set(sig1**2) psf_chisq_cut1 = psf_chisq_cut2 = psf_chisq_cut2b = 1.5 #pkres = PerPeak() pkres = DeblendedPeak(pk1, 0, None) loglvl = Log.INFO # if verbose: # loglvl = Log.DEBUG log = Log.getLogger('tests.fit_psf') log.setLevel(loglvl) cpsf = CachingPsf(psf1) peaksF = [pk.getF() for pk in peaks] pkF = pk1.getF() _fitPsf(fp, fmask, pk1, pkF, pkres, fbb, peaks, peaksF, log, cpsf, psffwhm, img, varimg, psf_chisq_cut1, psf_chisq_cut2, psf_chisq_cut2b) for k in dir(pkres): if k.startswith('__'): continue print(' ', k, getattr(pkres, k)) cpsf = CachingPsf(psf2) _fitPsf(fp, fmask, pk1, pkF, pkres, fbb, peaks, peaksF, log, cpsf, psffwhm, img, varimg, psf_chisq_cut1, psf_chisq_cut2, psf_chisq_cut2b) for k in dir(pkres): if k.startswith('__'): continue print(' ', k, getattr(pkres, k)) pkF = pk3.getF() _fitPsf(fp, fmask, pk3, pkF, pkres, fbb, peaks, peaksF, log, cpsf, psffwhm, img, varimg, psf_chisq_cut1, psf_chisq_cut2, psf_chisq_cut2b) for k in dir(pkres): if k.startswith('__'): continue print(' ', k, getattr(pkres, k))