def testCopy(self): bbox = lsst.geom.BoxI(lsst.geom.PointI(0, 2), lsst.geom.PointI(5, 6)) fp = afwDetect.Footprint(afwGeom.SpanSet(bbox), bbox) # test copy construct fp2 = afwDetect.Footprint(fp) self.assertEqual(fp2.getBBox(), bbox) self.assertEqual(fp2.getRegion(), bbox) self.assertEqual(fp2.getArea(), bbox.getArea()) y = bbox.getMinY() for s in fp2.getSpans(): self.assertEqual(s.getY(), y) self.assertEqual(s.getX0(), bbox.getMinX()) self.assertEqual(s.getX1(), bbox.getMaxX()) y += 1 # test assignment fp3 = afwDetect.Footprint() fp3.assign(fp) self.assertEqual(fp3.getBBox(), bbox) self.assertEqual(fp3.getRegion(), bbox) self.assertEqual(fp3.getArea(), bbox.getArea()) y = bbox.getMinY() for s in fp3.getSpans(): self.assertEqual(s.getY(), y) self.assertEqual(s.getX0(), bbox.getMinX()) self.assertEqual(s.getX1(), bbox.getMaxX()) y += 1
def testInclude(self): """Test that we can expand a Footprint to include the union of itself and all others provided (must be non-disjoint). """ region = afwGeom.Box2I(afwGeom.Point2I(-6, -6), afwGeom.Point2I(6, 6)) parent = afwDetect.Footprint(afwGeom.Box2I(afwGeom.Point2I(-2, -2), afwGeom.Point2I(2, 2)), region) parent.addPeak(0, 0, float("NaN")) child1 = afwDetect.Footprint(afwGeom.Box2I(afwGeom.Point2I(-3, 0), afwGeom.Point2I(0, 3)), region) child1.addPeak(-1, 1, float("NaN")) child2 = afwDetect.Footprint(afwGeom.Box2I(afwGeom.Point2I(-4, -3), afwGeom.Point2I(-1, 0)), region) child3 = afwDetect.Footprint(afwGeom.Box2I(afwGeom.Point2I(4, -1), afwGeom.Point2I(6, 1))) merge12 = afwDetect.Footprint(parent) merge12.include([child1, child2]) self.assertTrue(merge12.getBBox().contains(parent.getBBox())) self.assertTrue(merge12.getBBox().contains(child1.getBBox())) self.assertTrue(merge12.getBBox().contains(child2.getBBox())) mask12a = afwImage.MaskU(region) mask12b = afwImage.MaskU(region) afwDetect.setMaskFromFootprint(mask12a, parent, 1) afwDetect.setMaskFromFootprint(mask12a, child1, 1) afwDetect.setMaskFromFootprint(mask12a, child2, 1) afwDetect.setMaskFromFootprint(mask12b, merge12, 1) self.assertEqual(mask12a.getArray().sum(), merge12.getArea()) self.assertClose(mask12a.getArray(), mask12b.getArray(), rtol=0, atol=0) self.assertRaisesLsstCpp(pexExcept.RuntimeError, parent.include, [child1, child2, child3])
def testDot(self): """Test HeavyFootprint::dot""" size = 20, 20 for xOffset, yOffset in [(0, 0), (0, 3), (3, 0), (2, 2)]: mi1 = afwImage.MaskedImageF(*size) mi2 = afwImage.MaskedImageF(*size) mi1.set(0) mi2.set(0) fp1 = afwDetect.Footprint() fp2 = afwDetect.Footprint() for y, x0, x1 in [(5, 3, 7), (6, 3, 4), (6, 6, 7), (7, 3, 7),]: fp1.addSpan(y, x0, x1) fp2.addSpan(y + yOffset, x0 + xOffset, x1 + xOffset) for x in range(x0, x1 + 1): value = (x + y, 0, 1.0) mi1.set(x, y, value) mi2.set(x + xOffset, y + yOffset, value) hfp1 = afwDetect.makeHeavyFootprint(fp1, mi1) hfp2 = afwDetect.makeHeavyFootprint(fp2, mi2) hfp1.normalize() hfp2.normalize() dot = np.vdot(mi1.getImage().getArray(), mi2.getImage().getArray()) self.assertEqual(hfp1.dot(hfp2), dot) self.assertEqual(hfp2.dot(hfp1), dot)
def testDot(self): """Test HeavyFootprint::dot""" size = 20, 20 for xOffset, yOffset in [(0, 0), (0, 3), (3, 0), (2, 2)]: mi1 = afwImage.MaskedImageF(*size) mi2 = afwImage.MaskedImageF(*size) mi1.set(0) mi2.set(0) spanList1 = [] spanList2 = [] for y, x0, x1 in [ (5, 3, 7), (6, 3, 4), (6, 6, 7), (7, 3, 7), ]: spanList1.append(afwGeom.Span(y, x0, x1)) spanList2.append( afwGeom.Span(y + yOffset, x0 + xOffset, x1 + xOffset)) for x in range(x0, x1 + 1): value = (x + y, 0, 1.0) mi1[x, y, afwImage.LOCAL] = value mi2[x + xOffset, y + yOffset, afwImage.LOCAL] = value fp1 = afwDetect.Footprint(afwGeom.SpanSet(spanList1)) fp2 = afwDetect.Footprint(afwGeom.SpanSet(spanList2)) hfp1 = afwDetect.makeHeavyFootprint(fp1, mi1) hfp2 = afwDetect.makeHeavyFootprint(fp2, mi2) dot = np.vdot(mi1.getImage().getArray(), mi2.getImage().getArray()) self.assertEqual(hfp1.dot(hfp2), dot) self.assertEqual(hfp2.dot(hfp1), dot)
def testSpanShift(self): """Test our ability to shift spans""" span = afwDetect.Span(10, 100, 105) foot = afwDetect.Footprint() foot.addSpan(span, 1, 2) bbox = foot.getBBox() self.assertEqual(bbox.getWidth(), 6) self.assertEqual(bbox.getHeight(), 1) self.assertEqual(bbox.getMinX(), 101) self.assertEqual(bbox.getMinY(), 12) # # Shift that span using Span.shift # foot = afwDetect.Footprint() span.shift(-1, -2) foot.addSpan(span) bbox = foot.getBBox() self.assertEqual(bbox.getWidth(), 6) self.assertEqual(bbox.getHeight(), 1) self.assertEqual(bbox.getMinX(), 99) self.assertEqual(bbox.getMinY(), 8)
def testGrow(self): """Test growing a footprint""" x0, y0 = 20, 20 width, height = 20, 30 spanSet = afwGeom.SpanSet( lsst.geom.Box2I(lsst.geom.Point2I(x0, y0), lsst.geom.Extent2I(width, height))) foot1 = afwDetect.Footprint( spanSet, lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(100, 100))) # Add some peaks and check that they get copied into the new grown footprint foot1.addPeak(20, 20, 1) foot1.addPeak(30, 35, 2) foot1.addPeak(25, 45, 3) self.assertEqual(len(foot1.getPeaks()), 3) bbox1 = foot1.getBBox() self.assertEqual(bbox1.getMinX(), x0) self.assertEqual(bbox1.getMaxX(), x0 + width - 1) self.assertEqual(bbox1.getWidth(), width) self.assertEqual(bbox1.getMinY(), y0) self.assertEqual(bbox1.getMaxY(), y0 + height - 1) self.assertEqual(bbox1.getHeight(), height) ngrow = 5 for isotropic in (True, False): foot2 = afwDetect.Footprint().assign(foot1) stencil = afwGeom.Stencil.CIRCLE if isotropic else \ afwGeom.Stencil.MANHATTAN foot2.dilate(ngrow, stencil) # Check that the grown footprint is bigger than the original self.assertGreater(foot2.getArea(), foot1.getArea()) # Check that peaks got copied into grown footprint self.assertEqual(len(foot2.getPeaks()), 3) for peak in foot2.getPeaks(): self.assertIn((peak.getIx(), peak.getIy()), [(20, 20), (30, 35), (25, 45)]) bbox2 = foot2.getBBox() # check bbox2 self.assertEqual(bbox2.getMinX(), x0 - ngrow) self.assertEqual(bbox2.getWidth(), width + 2 * ngrow) self.assertEqual(bbox2.getMinY(), y0 - ngrow) self.assertEqual(bbox2.getHeight(), height + 2 * ngrow) # Check that region was preserved self.assertEqual(foot1.getRegion(), foot2.getRegion())
def testConstructors(self): ''' Test that each of the constructors constructs a valid Footprint, if any of these fails, an exception will be raised and the test will fail. ''' self.footprint = afwDet.Footprint(self.spans) self.footprintWithRegion = afwDet.Footprint(self.spans, self.region) self.footprintWithSchema = afwDet.Footprint(self.spans, self.schema) self.footprintWithSchemaRegion = afwDet.Footprint( self.spans, self.schema, self.region) self.emptyFootprint = afwDet.Footprint() self.assertEqual(len(self.emptyFootprint.spans), 0) self.assertEqual(len(self.emptyFootprint.peaks), 0)
def test1(self): task = measAlg.ReplaceWithNoiseTask() schema = afwTable.SourceTable.makeMinimalSchema() table = afwTable.SourceTable.make(schema) sources = afwTable.SourceCatalog(table) im = afwImage.ImageF(200, 50) seed = 42 rand = afwMath.Random(afwMath.Random.MT19937, seed) afwMath.randomGaussianImage(im, rand) s = sources.addNew() s.setId(1) fp = afwDet.Footprint() y, x0, x1 = (10, 10, 190) im.getArray()[y, x0:x1] = 10 fp.addSpan(y, x0, x1) s.setFootprint(fp) s = sources.addNew() s.setId(2) fp = afwDet.Footprint() y, x0, x1 = (40, 10, 190) im.getArray()[y, x0:x1] = 10 fp.addSpan(y, x0, x1) s.setFootprint(fp) mi = afwImage.MaskedImageF(im) exposure = afwImage.makeExposure(mi) self._save(mi, 'a') task.begin(exposure, sources) self._save(mi, 'b') sourcei = 0 task.insertSource(exposure, sourcei) self._save(mi, 'c') # do something task.removeSource(exposure, sources, sources[sourcei]) self._save(mi, 'd') sourcei = 1 task.insertSource(exposure, sourcei) self._save(mi, 'e') # do something task.removeSource(exposure, sources, sources[sourcei]) self._save(mi, 'f') task.end(exposure, sources) self._save(mi, 'g')
def morphToHeavy(source, peakSchema, xy0=Point2I()): """Convert the morphology to a `HeavyFootprint` Parameters ---------- source : `scarlet.Component` The scarlet source with a morphology to convert to a `HeavyFootprint`. peakSchema : `lsst.daf.butler.Schema` The schema for the `PeakCatalog` of the `HeavyFootprint`. xy0 : `tuple` `(x,y)` coordinates of the bounding box containing the `HeavyFootprint`. Returns ------- heavy : `lsst.afw.detection.HeavyFootprint` """ mask = afwImage.MaskX(np.array(source.morph > 0, dtype=np.int32), xy0=xy0) ss = SpanSet.fromMask(mask) if len(ss) == 0: return None tfoot = afwDet.Footprint(ss, peakSchema=peakSchema) cy, cx = source.pixel_center xmin, ymin = xy0 # HeavyFootprints are not defined for 64 bit floats morph = source.morph.astype(np.float32) peakFlux = morph[cy, cx] tfoot.addPeak(cx + xmin, cy + ymin, peakFlux) timg = afwImage.ImageF(morph, xy0=xy0) timg = timg[tfoot.getBBox()] heavy = afwDet.makeHeavyFootprint(tfoot, afwImage.MaskedImageF(timg)) return heavy
def testShrink(self): width, height = 5, 10 # Size of footprint x0, y0 = 50, 50 # Position of footprint imwidth, imheight = 100, 100 # Size of image foot = afwDetect.Footprint(afwGeom.Box2I(afwGeom.Point2I(x0, y0), afwGeom.Extent2I(width, height)), afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(imwidth, imheight))) self.assertEqual(foot.getNpix(), width*height) # Add some peaks to the original footprint and check that those lying outside # the shrunken footprint are omitted from the returned shrunken footprint. foot.addPeak(50, 50, 1) # should be omitted in shrunken footprint foot.addPeak(52, 52, 2) # should be kept in shrunken footprint foot.addPeak(50, 59, 3) # should be omitted in shrunken footprint self.assertEqual(len(foot.getPeaks()), 3) # check that all three peaks were added # Shrinking by one pixel makes each dimension *two* pixels shorter. shrunk = afwDetect.shrinkFootprint(foot, 1, True) self.assertEqual(3*8, shrunk.getNpix()) # Shrunken footprint should now only contain one peak at (52, 52) self.assertEqual(len(shrunk.getPeaks()), 1) peak = shrunk.getPeaks()[0] self.assertEqual((peak.getIx(), peak.getIy()), (52, 52)) # Without shifting the centroid self.assertEqual(shrunk.getCentroid(), foot.getCentroid()) # Get the same result from a Manhattan shrink shrunk = afwDetect.shrinkFootprint(foot, 1, False) self.assertEqual(3*8, shrunk.getNpix()) self.assertEqual(shrunk.getCentroid(), foot.getCentroid()) # Shrinking by a large amount leaves nothing. self.assertEqual(afwDetect.shrinkFootprint(foot, 100, True).getNpix(), 0)
def testFootprintFromEllipse(self): """Create an elliptical Footprint""" cen = afwGeom.Point2D(23, 25) a, b, theta = 25, 15, 30 ellipse = afwGeomEllipses.Ellipse(afwGeomEllipses.Axes(a, b, math.radians(theta)), cen) foot = afwDetect.Footprint(ellipse, afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(50, 60))) idImage = afwImage.ImageU(afwGeom.Extent2I(foot.getRegion().getWidth(), foot.getRegion().getHeight())) idImage.set(0) foot.insertIntoImage(idImage, foot.getId()) if display: ds9.mtv(idImage, frame=2) displayUtils.drawFootprint(foot, frame=2) shape = foot.getShape() shape.scale(2) # <r^2> = 1/2 for a disk ds9.dot(shape, *cen, frame=2, ctype=ds9.RED) shape = foot.getShape() shape.scale(2) # <r^2> = 1/2 for a disk ds9.dot(shape, *cen, frame=2, ctype=ds9.MAGENTA) axes = afwGeom.ellipses.Axes(foot.getShape()) axes.scale(2) # <r^2> = 1/2 for a disk self.assertEqual(foot.getCentroid(), cen) self.assertTrue(abs(a - axes.getA()) < 0.15, "a: %g v. %g" % (a, axes.getA())) self.assertTrue(abs(b - axes.getB()) < 0.02, "b: %g v. %g" % (b, axes.getB())) self.assertTrue(abs(theta - math.degrees(axes.getTheta())) < 0.2, "theta: %g v. %g" % (theta, math.degrees(axes.getTheta())))
def testRejectBlends(self): """Test the PcaPsfDeterminer blend removal We give it a single blended source, asking it to remove blends, and check that it barfs in the expected way. """ factory = measAlg.psfDeterminerRegistry["pca"] config = factory.ConfigClass() config.doRejectBlends = True psfDeterminer = factory(config) schema = afwTable.SourceTable.makeMinimalSchema() schema.addField("position", afwGeom.Point2D, doc="Position") schema.addField("flux.psf", float, doc="psf") schema.addField("flux.psf.flags", "Flag", doc="psf") catalog = afwTable.SourceCatalog(schema) catalog.defineCentroid("position") catalog.definePsfFlux("flux.psf") source = catalog.addNew() foot = afwDetection.Footprint(afwGeom.Point2I(123, 45), 6, self.exposure.getBBox()) foot.addPeak(123, 45, 6) foot.addPeak(126, 47, 5) source.setFootprint(foot) candidates = [measAlg.makePsfCandidate(source, self.exposure)] metadata = dafBase.PropertyList() with self.assertRaises(RuntimeError) as cm: psfDeterminer.determinePsf(self.exposure, candidates, metadata) self.assertEqual(cm.exception.message, "All PSF candidates removed as blends")
def testHsmPsfMoments(self): for width in (2.0, 3.0, 4.0): psf = afwDetection.GaussianPsf(35, 35, width) exposure = afwImage.ExposureF(45, 56) exposure.getMaskedImage().set(1.0, 0, 1.0) exposure.setPsf(psf) # perform the shape measurement msConfig = base.SingleFrameMeasurementConfig() msConfig.algorithms.names = ["ext_shapeHSM_HsmPsfMoments"] plugin, cat = makePluginAndCat(lsst.meas.extensions.shapeHSM.HsmPsfMomentsAlgorithm, "ext_shapeHSM_HsmPsfMoments", centroid="centroid", control=lsst.meas.extensions.shapeHSM.HsmPsfMomentsControl()) source = cat.addNew() source.set("centroid_x", 23) source.set("centroid_y", 34) offset = afwGeom.Point2I(23, 34) tmpSpans = afwGeom.SpanSet.fromShape(int(width), offset=offset) source.setFootprint(afwDetection.Footprint(tmpSpans)) plugin.measure(source, exposure) x = source.get("ext_shapeHSM_HsmPsfMoments_x") y = source.get("ext_shapeHSM_HsmPsfMoments_y") xx = source.get("ext_shapeHSM_HsmPsfMoments_xx") yy = source.get("ext_shapeHSM_HsmPsfMoments_yy") xy = source.get("ext_shapeHSM_HsmPsfMoments_xy") self.assertAlmostEqual(x, 0.0, 3) self.assertAlmostEqual(y, 0.0, 3) expected = afwEll.Quadrupole(afwEll.Axes(width, width, 0.0)) self.assertAlmostEqual(xx, expected.getIxx(), SHAPE_DECIMALS) self.assertAlmostEqual(xy, expected.getIxy(), SHAPE_DECIMALS) self.assertAlmostEqual(yy, expected.getIyy(), SHAPE_DECIMALS)
def run(self, exposure, catalog, flux_key): subtracted_exposure = afwImage.ExposureF(exposure, deep=True) model = self.modelImage.run(subtracted_exposure, catalog, flux_key) for source in catalog: with self.modelImage.replaced_source(subtracted_exposure, source, flux_key): # This parameter is the radius of the spanset # Changing the radius doesn't seem to affect SDSS Centroid? spanSet = afwGeom.SpanSet.fromShape(3) spanSet = spanSet.shiftedBy(Extent2I(source.getCentroid())) footprint = afwDetection.Footprint(spanSet) peak = footprint.peaks.addNew() peak.setFx(source.getCentroid().getX()) peak.setFy(source.getCentroid().getY()) # Check for NaNs if (source.getCentroid().getX() == source.getCentroid().getX() ): peak.setIx(int(source.getCentroid().getX())) peak.setIy(int(source.getCentroid().getY())) source.setFootprint(footprint) try: self.sdssCentroid.measure(source, subtracted_exposure) except (MeasurementError): pass
def testCopyWithinFootprintOutside(self): """Copy a footprint that is larger than the image""" target = afwImage.ImageF(100, 100) target.set(0) subTarget = afwImage.ImageF( target, lsst.geom.Box2I(lsst.geom.Point2I(40, 40), lsst.geom.Extent2I(20, 20))) source = afwImage.ImageF(10, 30) source.setXY0(45, 45) source.set(1.0) foot = afwDetect.Footprint() spanList = [ afwGeom.Span(*s) for s in ( ( 50, 50, 60 ), # Oversized on the source image, right; only some pixels overlap ( 60, 0, 100 ), # Oversized on the source, left and right; and on sub-target image, top ( 99, 0, 1000 ), # Oversized on the source image, top, left and right; aiming for segfault ) ] foot.spans = afwGeom.SpanSet(spanList) foot.spans.clippedTo(subTarget.getBBox()).clippedTo(source.getBBox()).\ copyImage(source, subTarget) expected = np.zeros((100, 100)) expected[50, 50:55] = 1.0 self.assertTrue(np.all(target.getArray() == expected))
def testSplit(self): spanList = [ afwGeom.Span(0, 2, 4), afwGeom.Span(1, 2, 4), afwGeom.Span(2, 2, 4), afwGeom.Span(10, 4, 7), afwGeom.Span(11, 4, 7), afwGeom.Span(12, 4, 7) ] spans = afwGeom.SpanSet(spanList) region = lsst.geom.Box2I(lsst.geom.PointI(-6, -6), lsst.geom.PointI(20, 20)) multiFoot = afwDet.Footprint(spans, region) records = [multiFoot.addPeak(3, 1, 100), multiFoot.addPeak(5, 11, 100)] # Verify that the footprint is multi-component self.assertFalse(multiFoot.isContiguous()) footprintList = multiFoot.split() self.assertEqual(len(footprintList), 2) for i, fp in enumerate(footprintList): # check that the correct Spans are populated for each tempSpan = afwGeom.SpanSet(spanList[i * 3:i * 3 + 3]) self.assertEqual(fp.spans, tempSpan) # check that the peaks are split properly self.assertEqual(len(fp.peaks), 1) self.assertEqual(fp.peaks[0], records[i])
def centroidsToCatalog(centroids, expWcs, transientsOnly=False): schema = afwTable.SourceTable.makeMinimalSchema() centroidKey = afwTable.Point2DKey.addFields(schema, 'centroid', 'centroid', 'pixel') schema.getAliasMap().set('slot_Centroid', 'centroid') #schema.addField('centroid_x', type=float, doc='x pixel coord') #schema.addField('centroid_y', type=float, doc='y pixel coord') schema.addField('inputFlux_template', type=float, doc='input flux in template') schema.addField('inputFlux_science', type=float, doc='input flux in science image') table = afwTable.SourceTable.make(schema) sources = afwTable.SourceCatalog(table) footprint_radius = 5 # pixels for row in centroids: if transientsOnly and row[2] != 0.: continue record = sources.addNew() coord = expWcs.pixelToSky(row[0], row[1]) record.setCoord(coord) record.set(centroidKey, afwGeom.Point2D(row[0], row[1])) record.set('inputFlux_template', row[2]) record.set('inputFlux_science', row[3]) fpCenter = afwGeom.Point2I(afwGeom.Point2D( row[0], row[1])) #expWcs.skyToPixel(coord)) footprint = afwDetection.Footprint(fpCenter, footprint_radius) record.setFootprint(footprint) sources = sources.copy(deep=True) # make it contiguous return sources
def getSkySourceFootprints(self, mergedList, skyInfo, seed): """! @brief Return a list of Footprints of sky objects which don't overlap with anything in mergedList @param mergedList The merged Footprints from all the input bands @param skyInfo A description of the patch @param seed Seed for the random number generator """ mask = afwImage.Mask(skyInfo.patchInfo.getOuterBBox()) detected = mask.getPlaneBitMask("DETECTED") for s in mergedList: s.getFootprint().spans.setMask(mask, detected) footprints = self.skyObjects.run(mask, seed) if not footprints: return footprints # Need to convert the peak catalog's schema so we can set the "merge_peak_<skyFilterName>" flags schema = self.merged.getPeakSchema() mergeKey = schema.find("merge_peak_%s" % self.config.skyFilterName).key converted = [] for oldFoot in footprints: assert len(oldFoot.getPeaks()) == 1, "Should be a single peak only" peak = oldFoot.getPeaks()[0] newFoot = afwDetect.Footprint(oldFoot.spans, schema) newFoot.addPeak(peak.getFx(), peak.getFy(), peak.getPeakValue()) newFoot.getPeaks()[0].set(mergeKey, True) converted.append(newFoot) return converted
def testCopyWithinFootprintOutside(self): """Copy a footprint that is larger than the image""" target = afwImage.ImageF(100, 100) target.set(0) subTarget = afwImage.ImageF( target, afwGeom.Box2I(afwGeom.Point2I(40, 40), afwGeom.Extent2I(20, 20))) source = afwImage.ImageF(10, 30) source.setXY0(45, 45) source.set(1.0) foot = afwDetect.Footprint() foot.addSpan( 50, 50, 60 ) # Oversized on the source image, right; only some pixels overlap foot.addSpan( 60, 0, 100 ) # Oversized on the source, left and right; and on sub-target image, top foot.addSpan( 99, 0, 1000 ) # Oversized on the source image, top, left and right; aiming for segfault afwDetect.copyWithinFootprintImage(foot, source, subTarget) expected = np.zeros((100, 100)) expected[50, 50:55] = 1.0 self.assertTrue(np.all(target.getArray() == expected))
def attachTransformedFootprints(self, sources, refCat, exposure, expWcs): """Default implementation for attaching Footprints to blank sources prior to measurement Footprints for forced photometry must be in the pixel coordinate system of the image being measured, while we want to use RA, Dec position from an external catalog. This implementation takes RA, Dec from an external catalog, a fixed radius, and creates and sets footprints in the exposure's pixel system. Note that ForcedPhotImageTask delegates to this method in its own attachFootprints method. attachFootprints can then be overridden by its subclasses to define how their Footprints should be generated. See the documentation for run() for information about the relationships between run(), generateMeasCat(), and attachTransformedFootprints(). """ footprint_radius = 5 # pixels for srcRecord, refRecord in zip(sources, refCat): # Add footprints # See https://community.lsst.org/t/how-do-i-do-forced-photometry-on-a-set-of-ra-dec/1074/9 # From TallJimbo (Jim Bosch) # "There's a Footprint constructor that takes an integer position and a radius, # so I think something like this should work:" # coord = lsst.afw.coord.IcrsCoord(lsst.afw.geom.Point2D(ra, dec), lsst.afw.geom.degrees) # fpCenter = lsst.afw.geom.Point2I(wcs.skyToPixel(coord)) # footprint = lsst.afw.detection.Footprint(fpCenter, radius) # coord = afwCoord.IcrsCoord(afwGeom.Point2D(row['RA'], row['Dec']), degrees) coord = refRecord.getCoord() fpCenter = afwGeom.Point2I(expWcs.skyToPixel(coord)) footprint = afwDetection.Footprint(fpCenter, footprint_radius) srcRecord.setFootprint(footprint)
def testTransform(self): dims = lsst.geom.Extent2I(512, 512) bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), dims) radius = 5 offset = lsst.geom.Extent2D(123, 456) crval = lsst.geom.SpherePoint(0, 0, lsst.geom.degrees) crpix = lsst.geom.Point2D(0, 0) cdMatrix = np.array([1.0e-5, 0.0, 0.0, 1.0e-5]) cdMatrix.shape = (2, 2) source = afwGeom.makeSkyWcs(crval=crval, crpix=crpix, cdMatrix=cdMatrix) target = afwGeom.makeSkyWcs(crval=crval, crpix=crpix + offset, cdMatrix=cdMatrix) sourceSpanSet = afwGeom.SpanSet.fromShape(radius, afwGeom.Stencil.CIRCLE) sourceSpanSet = sourceSpanSet.shiftedBy(12, 34) fpSource = afwDetect.Footprint(sourceSpanSet, bbox) fpTarget = fpSource.transform(source, target, bbox) self.assertEqual(len(fpSource.getSpans()), len(fpTarget.getSpans())) self.assertEqual(fpSource.getArea(), fpTarget.getArea()) imSource = afwImage.ImageU(dims) fpSource.spans.setImage(imSource, 1) imTarget = afwImage.ImageU(dims) fpTarget.spans.setImage(imTarget, 1) subSource = imSource.Factory(imSource, fpSource.getBBox()) subTarget = imTarget.Factory(imTarget, fpTarget.getBBox()) self.assertTrue(np.all(subSource.getArray() == subTarget.getArray())) # make a bbox smaller than the target footprint bbox2 = lsst.geom.Box2I(fpTarget.getBBox()) bbox2.grow(-1) fpTarget2 = fpSource.transform(source, target, bbox2) # this one clips fpTarget3 = fpSource.transform(source, target, bbox2, False) # this one doesn't self.assertTrue(bbox2.contains(fpTarget2.getBBox())) self.assertFalse(bbox2.contains(fpTarget3.getBBox())) self.assertNotEqual(fpTarget.getArea(), fpTarget2.getArea()) self.assertEqual(fpTarget.getArea(), fpTarget3.getArea()) # Test that peakCatalogs get Transformed correctly truthList = [(x, y, 10) for x, y in zip(range(-2, 2), range(-1, 3))] for value in truthList: fpSource.addPeak(*value) scaleFactor = 2 linTrans = lsst.geom.LinearTransform( np.array([[scaleFactor, 0], [0, scaleFactor]], dtype=float)) linTransFootprint = fpSource.transform(linTrans, fpSource.getBBox(), False) for peak, truth in zip(linTransFootprint.peaks, truthList): # Multiplied by two because that is the linear transform scaling # factor self.assertEqual(peak.getIx(), truth[0] * scaleFactor) self.assertEqual(peak.getIy(), truth[1] * scaleFactor)
def _fig8Test(self, x1, y1, x2, y2): # Construct a "figure of 8" consisting of two circles touching at the # centre of an image, then demonstrate that it shrinks correctly. # (Helper method for tests below.) radius = 3 imwidth, imheight = 100, 100 nshrink = 1 # These are the correct values for footprint sizes given the paramters # above. circle_npix = 29 initial_npix = circle_npix * 2 - 1 # touch at one pixel shrunk_npix = 26 box = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(imwidth, imheight)) e1 = afwGeom.Ellipse(afwGeomEllipses.Axes(radius, radius, 0), lsst.geom.Point2D(x1, y1)) spanSet1 = afwGeom.SpanSet.fromShape(e1) f1 = afwDetect.Footprint(spanSet1, box) self.assertEqual(f1.getArea(), circle_npix) e2 = afwGeom.Ellipse(afwGeomEllipses.Axes(radius, radius, 0), lsst.geom.Point2D(x2, y2)) spanSet2 = afwGeom.SpanSet.fromShape(e2) f2 = afwDetect.Footprint(spanSet2, box) self.assertEqual(f2.getArea(), circle_npix) initial = afwDetect.mergeFootprints(f1, f2) initial.setRegion( f2.getRegion()) # merge does not propagate the region self.assertEqual(initial_npix, initial.getArea()) shrunk = afwDetect.Footprint().assign(initial) shrunk.erode(nshrink) self.assertEqual(shrunk_npix, shrunk.getArea()) if display: idImage = afwImage.ImageU(imwidth, imheight) for i, foot in enumerate([initial, shrunk]): print(foot.getArea()) foot.spans.setImage(idImage, i + 1) afwDisplay.Display(frame=1).mtv(idImage, title=self._testMethodName + " image")
def testSetFromFootprint(self): """Test setting mask/image pixels from a Footprint list""" mi = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8)) im = mi.getImage() # # Objects that we should detect # self.objects = [] self.objects += [Object(10, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])] self.objects += [Object(20, [(5, 7, 8), (5, 10, 10), (6, 8, 9)])] self.objects += [Object(20, [(6, 3, 3)])] im.set(0) # clear image for obj in self.objects: obj.insert(im) if False and display: ds9.mtv(mi, frame=0) ds = afwDetect.FootprintSet(mi, afwDetect.Threshold(15)) objects = ds.getFootprints() afwDetect.setMaskFromFootprintList(mi.getMask(), objects, 0x1) self.assertEqual(mi.getMask()[4, 2, afwImage.LOCAL], 0x0) self.assertEqual(mi.getMask()[3, 6, afwImage.LOCAL], 0x1) self.assertEqual(mi.getImage()[3, 6, afwImage.LOCAL], 20) for ft in objects: ft.spans.setImage(mi.getImage(), 5.0) self.assertEqual(mi.getImage()[4, 2, afwImage.LOCAL], 10) self.assertEqual(mi.getImage()[3, 6, afwImage.LOCAL], 5) if display: ds9.mtv(mi, frame=1) # # Check Footprint.contains() while we are about it # self.assertTrue(objects[0].contains(lsst.geom.Point2I(7, 5))) self.assertFalse(objects[0].contains(lsst.geom.Point2I(10, 6))) self.assertFalse(objects[0].contains(lsst.geom.Point2I(7, 6))) self.assertFalse(objects[0].contains(lsst.geom.Point2I(4, 2))) self.assertTrue(objects[1].contains(lsst.geom.Point2I(3, 6))) # Verify the FootprintSet footprint list setter can accept inputs from # the footprint list getter # Create a copy of the ds' FootprintList dsFpList = ds.getFootprints() footprintListCopy = [afwDetect.Footprint().assign(f) for f in dsFpList] # Use the FootprintList setter with the output from the getter ds.setFootprints(ds.getFootprints()[:-1]) dsFpListNew = ds.getFootprints() self.assertTrue(len(dsFpListNew) == len(footprintListCopy)-1) for new, old in zip(dsFpListNew, footprintListCopy[:-1]): self.assertEqual(new, old)
def testPeakSort(self): footprint = afwDetect.Footprint( afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Point2I(10, 10))) footprint.addPeak(4, 5, 1) footprint.addPeak(3, 2, 5) footprint.addPeak(7, 8, -2) footprint.addPeak(5, 7, 4) footprint.sortPeaks() self.assertEqual([peak.getIx() for peak in footprint.getPeaks()], [3, 5, 4, 7])
def setUp(self): self.mi = afwImage.MaskedImageF(20, 10) self.objectPixelVal = (10, 0x1, 100) self.foot = afwDetect.Footprint() for y, x0, x1 in [(2, 10, 13), (3, 11, 14)]: self.foot.addSpan(y, x0, x1) for x in range(x0, x1 + 1): self.mi.set(x, y, self.objectPixelVal)
def runMeasurement(self, algorithmName, imageid, x, y, v): """Run the measurement algorithm on an image""" # load the test image imgFile = os.path.join(self.dataDir, "image.%d.fits" % imageid) img = afwImage.ImageF(imgFile) img -= self.bkgd nx, ny = img.getWidth(), img.getHeight() msk = afwImage.Mask(geom.Extent2I(nx, ny), 0x0) var = afwImage.ImageF(geom.Extent2I(nx, ny), v) mimg = afwImage.MaskedImageF(img, msk, var) msk.getArray()[:] = np.where(np.fabs(img.getArray()) < 1.0e-8, msk.getPlaneBitMask("BAD"), 0) # Put it in a bigger image, in case it matters big = afwImage.MaskedImageF(self.offset + mimg.getDimensions()) big.getImage().set(0) big.getMask().set(0) big.getVariance().set(v) subBig = afwImage.MaskedImageF(big, geom.Box2I(big.getXY0() + self.offset, mimg.getDimensions())) subBig <<= mimg mimg = big mimg.setXY0(self.xy0) exposure = afwImage.makeExposure(mimg) cdMatrix = np.array([1.0/(2.53*3600.0), 0.0, 0.0, 1.0/(2.53*3600.0)]) cdMatrix.shape = (2, 2) exposure.setWcs(afwGeom.makeSkyWcs(crpix=geom.Point2D(1.0, 1.0), crval=geom.SpherePoint(0, 0, geom.degrees), cdMatrix=cdMatrix)) # load the corresponding test psf psfFile = os.path.join(self.dataDir, "psf.%d.fits" % imageid) psfImg = afwImage.ImageD(psfFile) psfImg -= self.bkgd kernel = afwMath.FixedKernel(psfImg) kernelPsf = algorithms.KernelPsf(kernel) exposure.setPsf(kernelPsf) # perform the shape measurement msConfig = base.SingleFrameMeasurementConfig() alg = base.SingleFramePlugin.registry[algorithmName].PluginClass.AlgClass control = base.SingleFramePlugin.registry[algorithmName].PluginClass.ConfigClass().makeControl() msConfig.algorithms.names = [algorithmName] # Note: It is essential to remove the floating point part of the position for the # Algorithm._apply. Otherwise, when the PSF is realised it will have been warped # to account for the sub-pixel offset and we won't get *exactly* this PSF. plugin, table = makePluginAndCat(alg, algorithmName, control, centroid="centroid") center = geom.Point2D(int(x), int(y)) + geom.Extent2D(self.offset + geom.Extent2I(self.xy0)) source = table.makeRecord() source.set("centroid_x", center.getX()) source.set("centroid_y", center.getY()) source.setFootprint(afwDetection.Footprint(afwGeom.SpanSet(exposure.getBBox(afwImage.PARENT)))) plugin.measure(source, exposure) return source
def testGrow(self): """Test growing a footprint""" x0, y0 = 20, 20 width, height = 20, 30 foot1 = afwDetect.Footprint(afwGeom.Box2I(afwGeom.Point2I(x0, y0), afwGeom.Extent2I(width, height)), afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(100, 100))) # Add some peaks and check that they get copied into the new grown footprint foot1.addPeak(20, 20, 1) foot1.addPeak(30, 35, 2) foot1.addPeak(25, 45, 3) self.assertEqual(len(foot1.getPeaks()), 3) bbox1 = foot1.getBBox() self.assertEqual(bbox1.getMinX(), x0) self.assertEqual(bbox1.getMaxX(), x0 + width - 1) self.assertEqual(bbox1.getWidth(), width) self.assertEqual(bbox1.getMinY(), y0) self.assertEqual(bbox1.getMaxY(), y0 + height - 1) self.assertEqual(bbox1.getHeight(), height) ngrow = 5 for isotropic in (True, False): foot2 = afwDetect.growFootprint(foot1, ngrow, isotropic) # Check that peaks got copied into grown footprint self.assertEqual(len(foot2.getPeaks()), 3) for peak in foot2.getPeaks(): self.assertTrue((peak.getIx(), peak.getIy()) in [(20, 20), (30, 35), (25, 45)]) bbox2 = foot2.getBBox() if False and display: idImage = afwImage.ImageU(width, height) idImage.set(0) i = 1 for foot in [foot1, foot2]: foot.insertIntoImage(idImage, i) i += 1 metricImage = afwImage.ImageF("foo.fits") ds9.mtv(metricImage, frame=1) ds9.mtv(idImage) # check bbox2 self.assertEqual(bbox2.getMinX(), x0 - ngrow) self.assertEqual(bbox2.getWidth(), width + 2*ngrow) self.assertEqual(bbox2.getMinY(), y0 - ngrow) self.assertEqual(bbox2.getHeight(), height + 2*ngrow) # Check that region was preserved self.assertEqual(foot1.getRegion(), foot2.getRegion())
def setUp(self): self.mi = afwImage.MaskedImageF(20, 10) self.objectPixelVal = (10, 0x1, 100) spanList = [] for y, x0, x1 in [(2, 10, 13), (3, 11, 14)]: spanList.append(afwGeom.Span(y, x0, x1)) for x in range(x0, x1 + 1): self.mi[x, y, afwImage.LOCAL] = self.objectPixelVal self.foot = afwDetect.Footprint(afwGeom.SpanSet(spanList))
def testGetBBox(self): """Check that Footprint.getBBox() returns a copy""" x0, y0, w, h = 9, 10, 7, 4 foot = afwDetect.Footprint(afwGeom.Box2I(afwGeom.Point2I(x0, y0), afwGeom.Extent2I(w, h))) bbox = foot.getBBox() dx, dy = 10, 20 bbox.shift(afwGeom.Extent2I(dx, dy)) self.assertEqual(bbox.getMinX(), x0 + dx) self.assertEqual(foot.getBBox().getMinX(), x0)
def isMasked(self, footprint, mask): """Returns whether the footprint violates the mask limits""" size = float(footprint.getArea()) for maskName, limit in self.config.maskLimits.iteritems(): maskVal = mask.getPlaneBitMask(maskName) unmasked = afwDet.Footprint(footprint) unmasked.intersectMask(mask, maskVal) # footprint of unmasked pixels if (size - unmasked.getArea()) / size > limit: return True return False