def testMeasureCentroid(self): """Test that we can use our silly centroid through the usual Tasks""" algorithms.AlgorithmRegistry.register("centroid.silly", testLib.SillyCentroidControl) x, y = 10, 20 im = afwImage.MaskedImageF(afwGeom.ExtentI(512, 512)) im.set(0) arr = im.getImage().getArray() arr[y, x] = 1 exp = afwImage.makeExposure(im) schema = afwTable.SourceTable.makeMinimalSchema() detConfig = algorithms.SourceDetectionConfig() detConfig.thresholdValue = 0.5 detConfig.thresholdType = "value" measConfig = algorithms.SourceMeasurementConfig() measConfig.algorithms.names.add("centroid.silly") measConfig.slots.centroid = "centroid.silly" measConfig.algorithms["centroid.silly"].param = 5 measConfig.doReplaceWithNoise = False det = algorithms.SourceDetectionTask(schema=schema, config=detConfig) meas = algorithms.SourceMeasurementTask(schema, config=measConfig) table = afwTable.SourceTable.make(schema) sources = det.makeSourceCatalog(table, exp, doSmooth=False, sigma=1.0).sources self.assertEqual(len(sources), 1) meas.run(exp, sources) self.assertEqual(len(sources), 1) self.assertEqual(sources[0].getY(), y + 5)
def measure(footprintSet, exposure): """Measure a set of Footprints, returning a SourceCatalog""" config = measAlg.SourceMeasurementConfig() config.prefix = "initial." config.algorithms.names = ["flags.pixel", "flux.psf", "flux.sinc", "flux.gaussian", "shape.sdss"] config.centroider.name = "centroid.sdss" config.algorithms["flux.naive"].radius = 3.0 config.slots.centroid = "initial.centroid.sdss" config.slots.psfFlux = "initial.flux.psf" config.slots.apFlux = "initial.flux.sinc" config.slots.modelFlux = None config.slots.instFlux = None config.slots.calibFlux = None config.slots.shape = "initial.shape.sdss" schema = afwTable.SourceTable.makeMinimalSchema() measureSources = config.makeMeasureSources(schema) catalog = afwTable.SourceCatalog(schema) config.slots.setupTable(catalog.table) if display: ds9.mtv(exposure, title="Original", frame=0) footprintSet.makeSources(catalog) for i, source in enumerate(catalog): measureSources.applyWithPeak(source, exposure) return catalog
def makeSourceMeasurementConfig(nsigma=6.0, nIterForRadius=1, kfac=2.5): """Construct a SourceMeasurementConfig with the requested parameters""" msConfig = measAlg.SourceMeasurementConfig() if False: # requires #2546 msConfig.centroider = None msConfig.slots.centroid = None msConfig.algorithms.names.add("flux.kron") msConfig.algorithms.names.remove("correctfluxes") msConfig.algorithms["flux.kron"].nSigmaForRadius = nsigma msConfig.algorithms["flux.kron"].nIterForRadius = nIterForRadius msConfig.algorithms["flux.kron"].nRadiusForFlux = kfac msConfig.algorithms["flux.kron"].enforceMinimumRadius = False return msConfig
def measureRotAngle(self, exposure, x, y): """Measure rotation angle quantities using the C++ code""" msConfig = measAlg.SourceMeasurementConfig() msConfig.algorithms.names.add("rotAngle") schema = afwTable.SourceTable.makeMinimalSchema() ms = msConfig.makeMeasureSources(schema) table = afwTable.SourceTable.make(schema) msConfig.slots.setupTable(table) source = table.makeRecord() fp = afwDetection.Footprint(exposure.getBBox()) source.setFootprint(fp) center = afwGeom.Point2D(x, y) ms.apply(source, exposure, center) return source.get("rotAngle.north"), source.get("rotAngle.east")
def mySetup(self, runCentroider=True): msConfig = algorithms.SourceMeasurementConfig() if not runCentroider: msConfig.centroider = None msConfig.slots.centroid = None schema = afwTable.SourceTable.makeMinimalSchema() ms = msConfig.makeMeasureSources(schema) table = afwTable.SourceTable.make(schema) msConfig.slots.setupTable(table) source = table.makeRecord() fp = afwDetection.Footprint(self.exp.getBBox()) source.setFootprint(fp) ms.apply(source, self.exp, afwGeom.Point2D(self.xcen, self.ycen)) return source
def setUp(self): self.config = measAlg.SourceMeasurementConfig() self.config.algorithms.names = ["flux.psf", "flux.aperture", "classification.extendedness"] self.config.algorithms["flux.aperture"].maxSincRadius = 10.0 self.config.algorithms["flux.aperture"].radii = [3.0, 6.0, 9.0, 12.0, 15.0, 40, 65.0] self.config.slots.psfFlux = "flux.psf" schema = afwTable.SourceTable.makeMinimalSchema() schema.addField(afwTable.Field[int]("deblend.nchild", "")) task = measAlg.SourceMeasurementTask(config=self.config, schema=schema) # adds to schema sources = afwTable.SourceCatalog(schema) sources.definePsfFlux("flux.psf") sources.setMetadata(dafBase.PropertyList()) sources.getMetadata().set("flux_aperture_radii", self.config.algorithms["flux.aperture"].radii) self.schema = schema self.sources = sources
def testEllipticalGaussian(self): """Test measuring the properties of an elliptical Gaussian""" width, height = 200, 200 xcen, ycen = 0.5 * width, 0.5 * height # # Make the object # gal = afwImage.ImageF(afwGeom.ExtentI(width, height)) a, b, theta = float(10), float(5), 20 flux = 1e4 I0 = flux / (2 * math.pi * a * b) c, s = math.cos(math.radians(theta)), math.sin(math.radians(theta)) for y in range(height): for x in range(width): dx, dy = x - xcen, y - ycen u = c * dx + s * dy v = -s * dx + c * dy val = I0 * math.exp(-0.5 * ((u / a)**2 + (v / b)**2)) if val < 0: val = 0 gal.set(x, y, val) objImg = afwImage.makeExposure(afwImage.makeMaskedImage(gal)) objImg.getMaskedImage().getVariance().set(1.0) del gal objImg.setXY0(afwGeom.Point2I(1234, 5678)) # # We need a PSF to be able to centroid well. Cf. #2540 # FWHM = 5 ksize = 25 # size of desired kernel objImg.setPsf( measAlg.DoubleGaussianPsf(ksize, ksize, FWHM / (2 * math.sqrt(2 * math.log(2))), 1, 0.1)) if display: frame = 0 ds9.mtv(objImg, frame=frame, title="Elliptical") self.assertAlmostEqual( 1.0, afwMath.makeStatistics(objImg.getMaskedImage().getImage(), afwMath.SUM).getValue() / flux) # # Test elliptical apertures # # msConfig = measAlg.SourceMeasurementConfig() msConfig.algorithms.names.add("flux.aperture.elliptical") radii = math.sqrt(a * b) * numpy.array([ 0.45, 1.0, 2.0, 3.0, 10.0, ]) msConfig.algorithms["flux.aperture.elliptical"].radii = radii schema = afwTable.SourceTable.makeMinimalSchema() ms = msConfig.makeMeasureSources(schema) table = afwTable.SourceTable.make(schema) msConfig.slots.setupTable(table) source = table.makeRecord() ss = afwDetection.FootprintSet(objImg.getMaskedImage(), afwDetection.Threshold(0.1)) fp = ss.getFootprints()[0] source.setFootprint(fp) center = fp.getPeaks()[0].getF() ms.apply(source, objImg, center) self.assertEqual(source.get("flux.aperture.elliptical.nProfile"), len(radii)) r0 = 0.0 if display: shape = source.getShape().clone() xy = afwGeom.ExtentD(source.getCentroid()) - afwGeom.ExtentD( objImg.getXY0()) ds9.dot("x", xcen, ycen, ctype=ds9.RED) ds9.dot("+", *xy, frame=frame) with ds9.Buffering(): for r, apFlux in zip(radii, source.get("flux.aperture.elliptical")): if display: # draw the inner and outer boundaries of the aperture shape.scale(r / shape.getDeterminantRadius()) ds9.dot(shape, *xy, frame=frame) trueFlux = flux * (math.exp(-r0**2 / (2 * a * b)) - math.exp(-r**2 / (2 * a * b))) if verbose: print "%5.2f %6.3f%%" % (r, 100 * ((trueFlux - apFlux) / flux)) self.assertAlmostEqual(trueFlux / flux, apFlux / flux, 5) r0 = r # # Now measure some annuli "by hand" (we'll repeat this will EllipticalAperture algorithm soon) # for r1, r2 in [ (0.0, 0.45 * a), (0.45 * a, 1.0 * a), (1.0 * a, 2.0 * a), (2.0 * a, 3.0 * a), (3.0 * a, 5.0 * a), (3.0 * a, 10.0 * a), ]: control = measAlg.SincFluxControl() control.radius1 = r1 control.radius2 = r2 control.angle = math.radians(theta) control.ellipticity = 1 - b / a schema = afwTable.SourceTable.makeMinimalSchema() mp = measAlg.MeasureSourcesBuilder().addAlgorithm(control).build( schema) table = afwTable.SourceTable.make(schema) source = table.makeRecord() if display: # draw the inner and outer boundaries of the aperture Mxx = 1 Myy = (b / a)**2 mxx, mxy, myy = c**2 * Mxx + s**2 * Myy, c * s * ( Mxx - Myy), s**2 * Mxx + c**2 * Myy for r in (r1, r2): ds9.dot("@:%g,%g,%g" % (r**2 * mxx, r**2 * mxy, r**2 * myy), xcen, ycen, frame=frame) mp.apply(source, objImg, center) self.assertAlmostEqual( math.exp(-0.5 * (r1 / a)**2) - math.exp(-0.5 * (r2 / a)**2), source.get(control.name) / flux, 5) control = measAlg.GaussianFluxControl() schema = afwTable.SourceTable.makeMinimalSchema() mp = measAlg.MeasureSourcesBuilder().addAlgorithm(control).build( schema) table = afwTable.SourceTable.make(schema) source = table.makeRecord() objImg.setPsf(None) # no Psf mp.apply(source, objImg, center) # we haven't provided a PSF, so the built-in aperture correction won't work...but we'll get # a result anyway # Note that flags.psffactor==True sets flags=True IFF we attempt aperture corrections self.assertEqual(source.get(control.name + ".flags"), False) self.assertEqual(source.get(control.name + ".flags.psffactor"), True) gflux = source.get(control.name) err = gflux / flux - 1 if abs(err) > 1.5e-5: self.assertEqual(gflux, flux, ("%g, %g: error is %g" % (gflux, flux, err)))
def setUp(self): self.x0, self.y0 = 0, 0 self.nx, self.ny = 512, 512 #2048, 4096 self.sky = 100.0 self.nObj = 100 # make a distorter # This is a lot of distortion ... from circle r=1, to ellipse with a=1.3 (ie. 30%) # For suprimecam, we expect only about 5% self.distCoeffs = [0.0, 1.0, 2.0e-04, 3.0e-8] lanczosOrder = 3 coefficientsDistort = True self.distorter = cameraGeom.RadialPolyDistortion( self.distCoeffs, coefficientsDistort, lanczosOrder) # make a detector self.detector = cameraUtils.makeDefaultCcd( afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(self.nx, self.ny))) self.detector.setDistortion(self.distorter) self.detector.setCenter(cameraGeom.FpPoint( 255.5, 255.5)) # move boresight from center to 0,0 if False: for x, y in [(0, 0), (0, 511), (511, 0), (511, 511)]: p = afwGeom.Point2D(x, y) iqq = self.distorter.distort(p, geomEllip.Quadrupole(), self.detector) print x, y, geomEllip.Axes(iqq) print self.detector.getPositionFromPixel(p).getMm() print "Max distortion on this detector: ", self.distorter.computeMaxShear( self.detector) # detection policies self.detConfig = measAlg.SourceDetectionConfig() # measurement policies self.measSrcConfig = measAlg.SourceMeasurementConfig() # psf star selector starSelectorFactory = measAlg.starSelectorRegistry["secondMoment"] starSelectorConfig = starSelectorFactory.ConfigClass() starSelectorConfig.fluxLim = 5000.0 starSelectorConfig.histSize = 32 starSelectorConfig.clumpNSigma = 1.0 starSelectorConfig.badFlags = [] self.starSelector = starSelectorFactory(starSelectorConfig) # psf determiner psfDeterminerFactory = measAlg.psfDeterminerRegistry["pca"] psfDeterminerConfig = psfDeterminerFactory.ConfigClass() width, height = self.nx, self.ny nEigenComponents = 3 psfDeterminerConfig.sizeCellX = width // 3 psfDeterminerConfig.sizeCellY = height // 3 psfDeterminerConfig.nEigenComponents = nEigenComponents psfDeterminerConfig.spatialOrder = 1 psfDeterminerConfig.kernelSizeMin = 31 psfDeterminerConfig.nStarPerCell = 0 psfDeterminerConfig.nStarPerCellSpatialFit = 0 # unlimited self.psfDeterminer = psfDeterminerFactory(psfDeterminerConfig)
def test2(self): # Check that doReplaceWithNoise works with deblended source # hierarchies. seed = 42 rand = afwMath.Random(afwMath.Random.MT19937, seed) psf = self.getpsf() im = afwImage.ImageF(200, 50) skystd = 100 afwMath.randomGaussianImage(im, rand) im *= skystd imorig = afwImage.ImageF(im, True) noiseim = imorig mi = afwImage.MaskedImageF(im) mi.getVariance().set(skystd**2) exposure = afwImage.makeExposure(mi) exposure.setPsf(psf) detconf = measAlg.SourceDetectionConfig() detconf.returnOriginalFootprints = True detconf.reEstimateBackground = False measconf = measAlg.SourceMeasurementConfig() measconf.doReplaceWithNoise = True measconf.replaceWithNoise.noiseSeed = 42 schema = afwTable.SourceTable.makeMinimalSchema() detect = measAlg.SourceDetectionTask(config=detconf, schema=schema) measure = MySourceMeasurementTask(config=measconf, schema=schema, doplot=plots) table = afwTable.SourceTable.make(schema) table.preallocate(10) # We're going to fake up a perfect deblend hierarchy here, by # creating individual images containing single sources and # measuring them, and then creating a deblend hierarchy where # the children have the correct HeavyFootprints. We want to # find that the measurements on the deblend hierarchy and the # blended image are equal to the individual images. # # Note that in the normal setup we don't expect the # measurements to be *identical* because of the faint wings of # the objects; when measuring a deblended child, we pick up # the wings of the other objects. # # In order to get exactly equal measurements, we'll fake some # sources that have no wings -- we'll copy just the source # pixels within the footprint. This means that all the # footprints are the same, and the pixels inside the footprint # are the same. fullim = None sources = None # "normal" measurements xx0, yy0, vx0, vy0 = [], [], [], [] # "no-wing" measurements xx1, yy1, vx1, vy1 = [], [], [], [] y = 25 for i in range(5): # no-noise source image sim = afwImage.ImageF(imorig.getWidth(), imorig.getHeight()) # Put all four sources in the parent (i==0), and one # source in each child (i=[1 to 4]) if i in [0, 1]: addPsf(sim, psf, 20, y, 1000) if i in [0, 2]: addGaussian(sim, 40, y, 10, 3, 2e5) if i in [0, 3]: addGaussian(sim, 75, y, 10, 3, 2e5) if i in [0, 4]: addPsf(sim, psf, 95, y, 1000) imcopy = afwImage.ImageF(imorig, True) imcopy += sim # copy the pixels into the exposure object im <<= imcopy if i == 0: detected = detect.makeSourceCatalog(table, exposure) sources = detected.sources print 'detected', len(sources), 'sources' self.assertEqual(len(sources), 1) else: fpSets = detect.detectFootprints(exposure) print 'detected', fpSets.numPos, 'sources' fpSets.positive.makeSources(sources) self.assertEqual(fpSets.numPos, 1) print len(sources), 'sources total' measure.plotpat = 'single-%i.png' % i measure.run(exposure, sources[-1:]) s = sources[-1] fp = s.getFootprint() if i == 0: # This is the blended image fullim = imcopy else: print 'Creating heavy footprint...' heavy = afwDet.makeHeavyFootprint(fp, mi) s.setFootprint(heavy) # Record the single-source measurements. xx0.append(s.getX()) yy0.append(s.getY()) vx0.append(s.getIxx()) vy0.append(s.getIyy()) # "no-wings": add just the source pixels within the footprint im <<= sim h = afwDet.makeHeavyFootprint(fp, mi) sim2 = afwImage.ImageF(imorig.getWidth(), imorig.getHeight()) h.insert(sim2) imcopy = afwImage.ImageF(imorig, True) imcopy += sim2 im <<= imcopy measure.plotpat = 'single2-%i.png' % i measure.run(exposure, sources[i:i + 1], noiseImage=noiseim) s = sources[i] xx1.append(s.getX()) yy1.append(s.getY()) vx1.append(s.getIxx()) vy1.append(s.getIyy()) if i == 0: fullim2 = imcopy # Now we'll build the fake deblended hierarchy. parent = sources[0] kids = sources[1:] # Ensure that the parent footprint contains all the child footprints pfp = parent.getFootprint() for s in kids: for span in s.getFootprint().getSpans(): pfp.addSpan(span) pfp.normalize() #parent.setFootprint(pfp) # The parent-child relationship is established through the IDs parentid = parent.getId() for s in kids: s.setParent(parentid) # Reset all the measurements shkey = sources.getTable().getShapeKey() ckey = sources.getTable().getCentroidKey() for s in sources: sh = s.get(shkey) sh.setIxx(np.nan) sh.setIyy(np.nan) sh.setIxy(np.nan) s.set(shkey, sh) c = s.get(ckey) c.setX(np.nan) c.setY(np.nan) s.set(ckey, c) # Measure the "deblended" normal sources im <<= fullim measure.plotpat = 'joint-%(sourcenum)i.png' measure.run(exposure, sources) xx2, yy2, vx2, vy2 = [], [], [], [] for s in sources: xx2.append(s.getX()) yy2.append(s.getY()) vx2.append(s.getIxx()) vy2.append(s.getIyy()) # Measure the "deblended" no-wings sources im <<= fullim2 measure.plotpat = 'joint2-%(sourcenum)i.png' measure.run(exposure, sources, noiseImage=noiseim) xx3, yy3, vx3, vy3 = [], [], [], [] for s in sources: xx3.append(s.getX()) yy3.append(s.getY()) vx3.append(s.getIxx()) vy3.append(s.getIyy()) print 'Normal:' print 'xx ', xx0 print ' vs', xx2 print 'yy ', yy0 print ' vs', yy2 print 'vx ', vx0 print ' vs', vx2 print 'vy ', vy0 print ' vs', vy2 print 'No wings:' print 'xx ', xx1 print ' vs', xx3 print 'yy ', yy1 print ' vs', yy3 print 'vx ', vx1 print ' vs', vx3 print 'vy ', vy1 print ' vs', vy3 # These "normal" tests are not very stringent. # 0.1-pixel centroids self.assertTrue(all([abs(v1 - v2) < 0.1 for v1, v2 in zip(xx0, xx2)])) self.assertTrue(all([abs(v1 - v2) < 0.1 for v1, v2 in zip(yy0, yy2)])) # 10% variances self.assertTrue( all([ abs(v1 - v2) / ((v1 + v2) / 2.) < 0.1 for v1, v2 in zip(vx0, vx2) ])) self.assertTrue( all([ abs(v1 - v2) / ((v1 + v2) / 2.) < 0.1 for v1, v2 in zip(vy0, vy2) ])) # The "no-wings" tests should be exact. self.assertTrue(xx1 == xx3) self.assertTrue(yy1 == yy3) self.assertTrue(vx1 == vx3) self.assertTrue(vy1 == vy3) # Reset sources for s in sources: sh = s.get(shkey) sh.setIxx(np.nan) sh.setIyy(np.nan) sh.setIxy(np.nan) s.set(shkey, sh) c = s.get(ckey) c.setX(np.nan) c.setY(np.nan) s.set(ckey, c) # Test that the parent/child order is unimportant. im <<= fullim2 measure.doplot = False sources2 = sources.copy() perm = [2, 1, 0, 3, 4] for i, j in enumerate(perm): sources2[i] = sources[j] # I'm not convinced that HeavyFootprints get copied correctly... sources2[i].setFootprint(sources[j].getFootprint()) measure.run(exposure, sources2, noiseImage=noiseim) # "measure.run" reorders the sources! xx3, yy3, vx3, vy3 = [], [], [], [] for s in sources: xx3.append(s.getX()) yy3.append(s.getY()) vx3.append(s.getIxx()) vy3.append(s.getIyy()) self.assertTrue(xx1 == xx3) self.assertTrue(yy1 == yy3) self.assertTrue(vx1 == vx3) self.assertTrue(vy1 == vy3) # Reset sources for s in sources: sh = s.get(shkey) sh.setIxx(np.nan) sh.setIyy(np.nan) sh.setIxy(np.nan) s.set(shkey, sh) c = s.get(ckey) c.setX(np.nan) c.setY(np.nan) s.set(ckey, c) # Test that it still works when the parent ID falls in the middle of # the child IDs. im <<= fullim2 measure.doplot = False sources2 = sources.copy() parentid = 3 ids = [parentid, 1, 2, 4, 5] for i, s in enumerate(sources2): s.setId(ids[i]) if i != 0: s.setParent(parentid) s.setFootprint(sources[i].getFootprint()) measure.run(exposure, sources2, noiseImage=noiseim) # The sources get reordered! xx3, yy3, vx3, vy3 = [], [], [], [] xx3, yy3, vx3, vy3 = [0] * 5, [0] * 5, [0] * 5, [0] * 5 for i, j in enumerate(ids): xx3[i] = sources2[j - 1].getX() yy3[i] = sources2[j - 1].getY() vx3[i] = sources2[j - 1].getIxx() vy3[i] = sources2[j - 1].getIyy() self.assertTrue(xx1 == xx3) self.assertTrue(yy1 == yy3) self.assertTrue(vx1 == vx3) self.assertTrue(vy1 == vy3)
def runone(self, kk, rand): psf = self.getpsf() im = afwImage.ImageF(120, 200) skystd = 100 afwMath.randomGaussianImage(im, rand) im *= skystd # The SDSS adaptive moments code seems sometimes to latch onto # an incorrect answer (maybe from a noise spike or something). # None of the flags seem to be set. The result are variance # measurements a bit bigger than the PSF. With different # noise draws the source values here will show this effect # (hence the loop in "test1" to try "runone" will different # noise draws). # The real point of this test case, though, is to show that # replacing other detections by noise results in better # measurements. We do this by constructing a fake image # containing six rows. In the top three rows, we have a # galaxy flanked by two stars that are far enough away that # they don't confuse the SDSS adaptive moments code. In the # bottom three rows, they're close enough that the detections # don't merge, but the stars cause the variance of the galaxy # to be mis-estimated. We want to show that with the # "doReplaceWithNoise" option, the measurements on the # bottom three improve. # If you love ASCII art (and who doesn't, really), the # synthetic image is going to look like this: # # * GGG * # * GGG * # * GGG * # * GGG * # * GGG * # * GGG * # We have three of each to work around the instability # mentioned above. x = 60 y0 = 16 ystep = 33 for i in range(6): dx = [28, 29, 30, 35, 36, 37][i] y = y0 + i * ystep # x y sx sy flux addGaussian(im, x, y, 10, 3, 2e5) addPsf(im, psf, x + dx, y, 1000) addPsf(im, psf, x - dx, y, 1000) #im.writeFits('im.fits') mi = afwImage.MaskedImageF(im) var = mi.getVariance() var.set(skystd**2) exposure = afwImage.makeExposure(mi) exposure.setPsf(psf) detconf = measAlg.SourceDetectionConfig() detconf.returnOriginalFootprints = True detconf.reEstimateBackground = False measconf = measAlg.SourceMeasurementConfig() measconf.doReplaceWithNoise = False #newalgs = [ 'shape.hsm.ksb', 'shape.hsm.bj', 'shape.hsm.linear' ] #measconf.algorithms = list(measconf.algorithms.names) + newalgs schema = afwTable.SourceTable.makeMinimalSchema() detect = measAlg.SourceDetectionTask(config=detconf, schema=schema) measure = measAlg.SourceMeasurementTask(config=measconf, schema=schema) print 'Running detection...' table = afwTable.SourceTable.make(schema) detected = detect.makeSourceCatalog(table, exposure) sources = detected.sources # We don't want the sources to be close enough that their # detection masks touch. self.assertEqual(len(sources), 18) # Run measurement with and without "doReplaceWithNoise"... for jj in range(2): print 'Running measurement...' measure.run(exposure, sources) #fields = schema.getNames() #print 'Fields:', fields fields = [ 'centroid.sdss', 'shape.sdss', #'shape.hsm.bj.moments', #'shape.hsm.ksb.moments', #'shape.hsm.linear.moments', #'shape.sdss.flags.maxiter', 'shape.sdss.flags.shift', #'shape.sdss.flags.unweighted', 'shape.sdss.flags.unweightedbad' ] keys = [schema.find(f).key for f in fields] xx, yy, vx, vy = [], [], [], [] for source in sources: #print ' ', source #for f,k in zip(fields, keys): # val = source.get(k) # print ' ', f, val xx.append(source.getX()) yy.append(source.getY()) vx.append(source.getIxx()) vy.append(source.getIyy()) if plots: plotSources(im, sources, schema) plt.savefig('%i%s.png' % (kk, chr(ord('a') + jj))) # Now we want to find the galaxy variance measurements... # Sort, first vertically then horizontally # iy ~ row number iy = [int(round((y - y0) / float(ystep))) for y in yy] iy = np.array(iy) xx = np.array(xx) vx = np.array(vx) vy = np.array(vy) I = np.argsort(iy * 1000 + xx) vx = vx[I] vy = vy[I] # The "left" stars will be indices 0, 3, 6, ... # The galaxies will be 1, 4, 7, ... vx = vx[slice(1, 18, 3)] # Bottom three galaxies may be contaminated by the stars bad = vx[:3] # Top three should be clean good = vx[3:] # When SdssShape fails, we get variance ~ 11 I = np.flatnonzero(bad > 50.) # Hope that we got at least one valid measurement self.assertTrue(len(I) > 0) bad = bad[I] I = np.flatnonzero(good > 50.) self.assertTrue(len(I) > 0) good = good[I] print 'bad:', bad print 'good:', good # Typical: # bad: [ 209.78476672 192.35271583 176.76274525] # good: [ 99.40557099 110.5701382 ] oklo, okhi = 80, 120 self.assertTrue(all((good > oklo) * (good < okhi))) if jj == 0: # Without "doReplaceWithNoise", we expect to find the variances # overestimated. self.assertTrue(all(bad > okhi)) else: # With "doReplaceWithNoise", no problem! self.assertTrue(all((bad > oklo) * (bad < okhi))) # Set "doReplaceWithNoise" for the second time through the loop... measconf.doReplaceWithNoise = True
def testVariance(self): size = 128 # size of image (pixels) center = afwGeom.Point2D(size // 2, size // 2) # object center width = 2.0 # PSF width flux = 10.0 # Flux of object variance = 1.0 # Variance value # Initial setup of an image exp = afwImage.ExposureF(size, size) image = exp.getMaskedImage().getImage() mask = exp.getMaskedImage().getMask() var = exp.getMaskedImage().getVariance() image.set(0.0) mask.set(0) var.set(variance) # Put down a PSF psf = measAlg.DoubleGaussianPsf(int(5 * width), int(5 * width), width) exp.setPsf(psf) psfImage = psf.computeImage(center).convertF() psfImage *= flux image.Factory(image, psfImage.getBBox(afwImage.PARENT)).__iadd__(psfImage) var.Factory(var, psfImage.getBBox(afwImage.PARENT)).__iadd__(psfImage) # Put in some bad pixels to ensure they're ignored for i in range(-5, 6): bad = size // 2 + i * width var.getArray()[bad, :] = float("nan") mask.getArray()[bad, :] = mask.getPlaneBitMask("BAD") var.getArray()[:, bad] = float("nan") mask.getArray()[:, bad] = mask.getPlaneBitMask("BAD") if display: import lsst.afw.display.ds9 as ds9 ds9.mtv(image, frame=1) ds9.mtv(mask, frame=2) ds9.mtv(var, frame=3) config = measAlg.SourceMeasurementConfig() config.algorithms.names = ["centroid.naive", "shape.sdss", "variance"] config.slots.centroid = "centroid.naive" config.slots.psfFlux = None config.slots.apFlux = None config.slots.modelFlux = None config.slots.instFlux = None config.slots.calibFlux = None config.slots.shape = "shape.sdss" config.algorithms["variance"].mask = ["BAD", "SAT"] config.validate() schema = afwTable.SourceTable.makeMinimalSchema() ms = config.makeMeasureSources(schema) catalog = afwTable.SourceCatalog(schema) config.slots.setupTable(catalog.getTable()) foot = afwDetection.Footprint(afwGeom.Point2I(center), width) peak = foot.getPeaks().addNew() peak.setIx(int(center.getX())) peak.setIy(int(center.getY())) peak.setFx(center.getX()) peak.setFy(center.getY()) peak.setPeakValue(flux) source = catalog.addNew() source.setFootprint(foot) ms.applyWithPeak(source, exp) self.assertEqual(source.get("variance"), variance)
def testAlgorithms(self): """Test that we can instantiate and use algorithms""" config = measAlg.SourceMeasurementConfig() config.algorithms.names = measAlg.AlgorithmRegistry.all.keys() config.algorithms.names.discard(config.centroider.name) config.doReplaceWithNoise = False config.algorithms.names.discard("flux.peakLikelihood") if False: log = pexLog.getDefaultLog() log.setThreshold(log.DEBUG) schema = afwTable.SourceTable.makeMinimalSchema() task = measAlg.SourceMeasurementTask(schema, config=config) catalog = afwTable.SourceCatalog(schema) source = catalog.addNew() source.set("id", 12345) size = 256 xStar, yStar = 65.432, 76.543 width = 3.21 x0, y0 = 12345, 54321 x, y = numpy.indices((size, size)) im = afwImage.MaskedImageF(afwGeom.ExtentI(size, size)) im.setXY0(afwGeom.Point2I(x0, y0)) im.getVariance().set(1.0) arr = im.getImage().getArray() arr[y, x] = numpy.exp(-0.5 * ((x - xStar)**2 + (y - yStar)**2) / width**2) psf = testLib.makeTestPsf(im) exp = afwImage.makeExposure(im) exp.setPsf(psf) exp.setXY0(afwGeom.Point2I(x0, y0)) scale = 1.0e-5 wcs = afwImage.makeWcs( afwCoord.Coord(0.0 * afwGeom.degrees, 0.0 * afwGeom.degrees), afwGeom.Point2D(0.0, 0.0), scale, 0.0, 0.0, scale) exp.setWcs(wcs) point = afwGeom.Point2I(int(xStar + x0), int(yStar + y0)) bbox = im.getBBox() bbox.shift(afwGeom.Extent2I(x0, y0)) foot = afwDetection.Footprint(point, width, bbox) foot.addPeak(point.getX(), point.getY(), 1.0) afwDetection.setMaskFromFootprint(exp.getMaskedImage().getMask(), foot, 1) source.setFootprint(foot) if display: ds9.mtv(exp, frame=1) task.run(exp, catalog) for alg in config.algorithms: flagName = alg + ".flags" if False: print(alg, source.get(flagName) if flagName in schema else None, source.get(alg) if alg in schema else None) elif flagName in schema: self.assertFalse(source.get(alg + ".flags"))
import lsst.afw.math as afwMath import lsst.afw.table as afwTable import lsst.afw.image as afwImg import lsst.afw.detection as afwDetect import lsst.meas.algorithms as measAlg statFlags = (afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV | afwMath.MAX | afwMath.MIN | afwMath.ERRORS) print "The statistics flags are set to %s." % bin(statFlags) print "Errors will be calculated.\n" # Configure the detection and measurement algorithms schema = afwTable.SourceTable.makeMinimalSchema() detectSourcesConfig = measAlg.SourceDetectionConfig(thresholdType='value') measureSourcesConfig = measAlg.SourceMeasurementConfig() # Setup the detection and measurement tasks detect = measAlg.SourceDetectionTask(config=detectSourcesConfig, schema=schema) measure = measAlg.SourceMeasurementTask(config=measureSourcesConfig, schema=schema) # Choose algorithms to look at the output of. fields = [ #'centroid.naive', #'centroid.naive.err', 'centroid.naive.flags', #'centroid.gaussian', #'centroid.gaussian.err', 'centroid.sdss', 'centroid.sdss.flags', 'shape.sdss', 'shape.sdss.err',
def setUp(self): width, height = 100, 300 self.mi = afwImage.MaskedImageF(afwGeom.ExtentI(width, height)) self.mi.set(0) self.mi.getVariance().set(10) self.mi.getMask().addMaskPlane("DETECTED") self.FWHM = 5 self.ksize = 25 # size of desired kernel self.exposure = afwImage.makeExposure(self.mi) psf = roundTripPsf( 2, algorithms.DoubleGaussianPsf(self.ksize, self.ksize, self.FWHM / (2 * sqrt(2 * log(2))), 1, 0.1)) self.exposure.setPsf(psf) for x, y in [ (20, 20), #(30, 35), (50, 50), (60, 20), (60, 210), (20, 210) ]: flux = 10000 - 0 * x - 10 * y sigma = 3 + 0.01 * (y - self.mi.getHeight() / 2) psf = roundTripPsf( 3, algorithms.DoubleGaussianPsf(self.ksize, self.ksize, sigma, 1, 0.1)) im = psf.computeImage().convertF() im *= flux smi = self.mi.getImage().Factory( self.mi.getImage(), afwGeom.BoxI( afwGeom.PointI(x - self.ksize / 2, y - self.ksize / 2), afwGeom.ExtentI(self.ksize)), afwImage.LOCAL) if False: # Test subtraction with non-centered psfs im = afwMath.offsetImage(im, 0.5, 0.5) smi += im del psf del im del smi psf = roundTripPsf( 4, algorithms.DoubleGaussianPsf(self.ksize, self.ksize, self.FWHM / (2 * sqrt(2 * log(2))), 1, 0.1)) self.cellSet = afwMath.SpatialCellSet( afwGeom.BoxI(afwGeom.PointI(0, 0), afwGeom.ExtentI(width, height)), 100) ds = afwDetection.FootprintSet(self.mi, afwDetection.Threshold(10), "DETECTED") # # Prepare to measure # msConfig = algorithms.SourceMeasurementConfig() msConfig.load("tests/config/MeasureSources.py") schema = afwTable.SourceTable.makeMinimalSchema() measureSources = msConfig.makeMeasureSources(schema) catalog = afwTable.SourceCatalog(schema) msConfig.slots.calibFlux = None msConfig.slots.setupTable(catalog.table) ds.makeSources(catalog) for i, source in enumerate(catalog): measureSources.applyWithPeak(source, self.exposure) self.cellSet.insertCandidate( algorithms.makePsfCandidate(source, self.exposure))
def testCountInputs(self): num = 3 # Number of images size = 10 # Size of images (pixels) shift = 4 # Shift to apply between images (pixels) value = 100.0 # Value to give objects offset = 12345 # x0,y0 cdMatrix = (1.0e-5, 0.0, 0.0, 1.0e-5) crval = afwCoord.Coord(0.0 * afwGeom.degrees, 0.0 * afwGeom.degrees) positions = [ afwGeom.Point2D(size // 2 + shift * (i - num // 2) + offset, size // 2 + shift * (i - num // 2) + offset) for i in range(num) ] wcsList = [ afwImage.makeWcs(crval, pos - afwGeom.Extent2D(offset, offset), *cdMatrix) for pos in positions ] imageBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(size, size)) wcsRef = afwImage.makeWcs(crval, afwGeom.Point2D(offset, offset), *cdMatrix) exp = afwImage.ExposureF(size, size) exp.setXY0(afwGeom.Point2I(offset, offset)) exp.setWcs(wcsRef) exp.setPsf(measAlg.DoubleGaussianPsf(5, 5, 1.0)) exp.getInfo().setCoaddInputs( afwImage.CoaddInputs(afwTable.ExposureTable.makeMinimalSchema(), afwTable.ExposureTable.makeMinimalSchema())) ccds = exp.getInfo().getCoaddInputs().ccds for wcs in wcsList: record = ccds.addNew() record.setWcs(wcs) record.setBBox(imageBox) record.setValidPolygon(Polygon(afwGeom.Box2D(imageBox))) exp.getMaskedImage().getImage().set(0) exp.getMaskedImage().getMask().set(0) exp.getMaskedImage().getVariance().set(1.0) for pp in positions: x, y = map(int, pp) exp.getMaskedImage().getImage().set0(x, y, value) exp.getMaskedImage().getMask().set(x - offset, y - offset, value) measureSourcesConfig = measAlg.SourceMeasurementConfig() measureSourcesConfig.algorithms.names = [ "centroid.naive", "countInputs" ] measureSourcesConfig.slots.centroid = "centroid.naive" measureSourcesConfig.slots.psfFlux = None measureSourcesConfig.slots.apFlux = None measureSourcesConfig.slots.modelFlux = None measureSourcesConfig.slots.instFlux = None measureSourcesConfig.slots.calibFlux = None measureSourcesConfig.slots.shape = None measureSourcesConfig.validate() schema = afwTable.SourceTable.makeMinimalSchema() ms = measureSourcesConfig.makeMeasureSources(schema) catalog = afwTable.SourceCatalog(schema) measureSourcesConfig.slots.setupTable(catalog.getTable()) for pp in positions: foot = afwDetection.Footprint(afwGeom.Point2I(pp), 1.0) peak = foot.getPeaks().addNew() peak.setIx(int(pp[0])) peak.setIy(int(pp[1])) peak.setFx(pp[0]) peak.setFy(pp[1]) peak.setPeakValue(value) source = catalog.addNew() source.setFootprint(foot) ms.applyWithPeak(source, exp) number = sum( afwGeom.Box2D(imageBox).contains( wcs.skyToPixel(wcsRef.pixelToSky(pp))) for wcs in wcsList) self.assertEqual(source.get("countInputs"), number)
def measureKronInPython(self, objImg, xcen, ycen, nsigma, kfac, nIterForRadius, makeImage=None): """Measure the Kron quantities of an elliptical Gaussian in python N.b. only works for XY0 == (0, 0) """ # # Measure moments using SDSS shape algorithm # msConfig = measAlg.SourceMeasurementConfig() if False: # requires #2546 msConfig.centroider = None msConfig.slots.centroid = None schema = afwTable.SourceTable.makeMinimalSchema() ms = msConfig.makeMeasureSources(schema) table = afwTable.SourceTable.make(schema) msConfig.slots.setupTable(table) source = table.makeRecord() fp = afwDetection.Footprint(objImg.getBBox()) source.setFootprint(fp) center = afwGeom.Point2D(xcen, ycen) ms.apply(source, objImg, center) Mxx = source.getIxx() Mxy = source.getIxy() Myy = source.getIyy() # # Calculate principal axes # Muu_p_Mvv = Mxx + Myy Muu_m_Mvv = math.sqrt((Mxx - Myy)**2 + 4*Mxy**2) Muu = 0.5*(Muu_p_Mvv + Muu_m_Mvv) Mvv = 0.5*(Muu_p_Mvv - Muu_m_Mvv) theta = 0.5*math.atan2(2*Mxy, Mxx - Myy) a = math.sqrt(Muu) b = math.sqrt(Mvv) ab = a/b # # Get footprint # ellipse = afwEllipses.Ellipse(afwEllipses.Axes(nsigma*a, nsigma*b, theta), afwGeom.Point2D(xcen - objImg.getX0(), ycen - objImg.getY0())) fpEllipse = afwDetection.Footprint(ellipse) sumI = 0.0 sumR = 0.38259771140356325/ab*(1 + math.sqrt(2)*math.hypot(math.fmod(xcen, 1), math.fmod(ycen, 1)))*\ objImg.getMaskedImage().getImage().get(int(xcen), int(ycen)) gal = objImg.getMaskedImage().getImage() c, s = math.cos(theta), math.sin(theta) for sp in fpEllipse.getSpans(): y, x0, x1 = sp.getY(), sp.getX0(), sp.getX1() for x in range(x0, x1 + 1): dx, dy = x - xcen, y - ycen u = c*dx + s*dy v = -s*dx + c*dy r = math.hypot(u, v*ab) try: val = gal.get(x, y) except: continue sumI += val sumR += val*r R_K = sumR/sumI sumI = 0.0 for y in range(self.height): for x in range(self.width): dx, dy = x - xcen, y - ycen u = c*dx + s*dy v = -s*dx + c*dy if math.hypot(u/a, v/b) < kfac: sumI += gal.get(x, y) return R_K, sumI, 0, False, False, False
def check(self, psfFwhm=0.5, flux=1000.0): """Check that we can measure convolved fluxes We create an image with a Gaussian PSF and a single point source. Measurements of the point source should match expectations for a Gaussian of the known sigma and known aperture radius. @param psfFwhm: PSF FWHM in arcsec @param flux: source flux in ADU """ bbox = afwGeom.Box2I(afwGeom.Point2I(12345, 6789), afwGeom.Extent2I(200, 300)) # We'll only achieve the target accuracy if the pixel scale is rather smaller than Gaussians # involved. Otherwise it's important to consider the convolution with the pixel grid, and we're # not doing that here. scale = 0.1 * afwGeom.arcseconds exposure, center = makeExposure(bbox, scale, psfFwhm, flux) msConfig = measAlg.SourceMeasurementConfig() msConfig.algorithms.names.add("flux.convolved") values = [ii / scale.asArcseconds() for ii in (0.6, 0.8, 1.0, 1.2)] msConfig.algorithms["flux.convolved"].seeing = values msConfig.algorithms["flux.convolved"].radius = values schema = afwTable.SourceTable.makeMinimalSchema() ms = msConfig.makeMeasureSources(schema) table = afwTable.SourceTable.make(schema) msConfig.slots.setupTable(table) source = table.makeRecord() ss = afwDetection.FootprintSet(exposure.getMaskedImage(), afwDetection.Threshold(0.1)) fp = ss.getFootprints()[0] source.setFootprint(fp) ms.apply(source, exposure, center) if display: ds9.mtv(exposure, frame=frame) ds9.dot("x", center.getX() - exposure.getX0(), center.getY() - exposure.getY0(), frame=frame) import pdb pdb.set_trace() self.assertFalse( source.get("flux.convolved.flag")) # algorithm succeeded for ii, seeing in enumerate( msConfig.algorithms["flux.convolved"].seeing): deconvolve = seeing < psfFwhm / scale.asArcseconds() self.assertTrue( source.get("flux.convolved.%d.deconv" % ii) == deconvolve) if deconvolve: # Not worth checking anything else continue for jj, radius in enumerate( msConfig.algorithms["flux.convolved"].radius): sigma = seeing / SIGMA_TO_FWHM expected = flux * (1.0 - math.exp(-0.5 * (radius / sigma)**2)) name = "flux.convolved.%d.%d" % (ii, jj) self.assertClose(expected, source.get(name), rtol=1.0e-3) self.assertFalse(source.get(name + ".flags")) self.assertGreater(source.get(name + ".err"), 0)