def setUp(self): """Build up three different sets of objects that are to be merged""" pos1 = [(40, 40), (220, 35), (40, 48), (220, 50), (67, 67), (150, 50), (40, 90), (70, 160), (35, 255), (70, 180), (250, 200), (120, 120), (170, 180), (100, 210), (20, 210), ] pos2 = [(43, 45), (215, 31), (171, 258), (211, 117), (48, 99), (70, 160), (125, 45), (251, 33), (37, 170), (134, 191), (79, 223), (258, 182) ] pos3 = [(70, 170), (219, 41), (253, 173), (253, 192)] box = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Point2I(300, 300)) psfsig = 1. kernelSize = 41 flux = 1000 # Create a different sized psf for each image and insert them at the # desired positions im1 = afwImage.MaskedImageD(box) psf1 = afwDetect.GaussianPsf(kernelSize, kernelSize, psfsig) im2 = afwImage.MaskedImageD(box) psf2 = afwDetect.GaussianPsf(kernelSize, kernelSize, 2*psfsig) im3 = afwImage.MaskedImageD(box) psf3 = afwDetect.GaussianPsf(kernelSize, kernelSize, 1.3*psfsig) insertPsf(pos1, im1, psf1, kernelSize, flux) insertPsf(pos2, im2, psf2, kernelSize, flux) insertPsf(pos3, im3, psf3, kernelSize, flux) schema = afwTable.SourceTable.makeMinimalSchema() self.idFactory = afwTable.IdFactory.makeSimple() self.table = afwTable.SourceTable.make(schema, self.idFactory) # Create SourceCatalogs from these objects fp1 = afwDetect.FootprintSet( im1, afwDetect.Threshold(0.001), "DETECTED") self.catalog1 = afwTable.SourceCatalog(self.table) fp1.makeSources(self.catalog1) fp2 = afwDetect.FootprintSet( im2, afwDetect.Threshold(0.001), "DETECTED") self.catalog2 = afwTable.SourceCatalog(self.table) fp2.makeSources(self.catalog2) fp3 = afwDetect.FootprintSet( im3, afwDetect.Threshold(0.001), "DETECTED") self.catalog3 = afwTable.SourceCatalog(self.table) fp3.makeSources(self.catalog3)
def plantSources(bbox, kwid, sky, coordList, addPoissonNoise=True): """Make an exposure with stars (modelled as Gaussians).""" """ @param bbox: parent bbox of exposure @param kwid: kernel width (and height; kernel is square) @param sky: amount of sky background (counts) @param coordList: a list of [x, y, counts, sigma], where: * x,y are relative to exposure origin * counts is the integrated counts for the star * sigma is the Gaussian sigma in pixels @param addPoissonNoise: add Poisson noise to the exposure? """ # make an image with sources img = afwImage.ImageD(bbox) meanSigma = 0.0 for coord in coordList: x, y, counts, sigma = coord meanSigma += sigma # make a single gaussian psf psf = afwDetection.GaussianPsf(kwid, kwid, sigma) # make an image of it and scale to the desired number of counts thisPsfImg = psf.computeImage(lsst.geom.PointD(int(x), int(y))) thisPsfImg *= counts # bbox a window in our image and add the fake star image imgSeg = img.Factory(img, thisPsfImg.getBBox()) imgSeg += thisPsfImg meanSigma /= len(coordList) img += sky # add Poisson noise if (addPoissonNoise): np.random.seed(seed=1) # make results reproducible imgArr = img.getArray() imgArr[:] = np.random.poisson(imgArr) # bundle into a maskedimage and an exposure mask = afwImage.Mask(bbox) var = img.convertFloat() img -= sky mimg = afwImage.MaskedImageF(img.convertFloat(), mask, var) exposure = afwImage.makeExposure(mimg) # insert an approximate psf psf = afwDetection.GaussianPsf(kwid, kwid, meanSigma) exposure.setPsf(psf) return exposure
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 makeExposure(bbox, scale, psfFwhm, flux): """Make a fake exposure @param bbox: Bounding box for image (Box2I) @param scale: Pixel scale (Angle) @param psfFwhm: PSF FWHM (arcseconds) @param flux: PSF flux (ADU) @return Exposure, source center """ image = afwImage.ImageF(bbox) image.set(0) center = afwGeom.Box2D(bbox).getCenter() psfSigma = psfFwhm / SIGMA_TO_FWHM / scale.asArcseconds() psfWidth = 2 * int(4.0 * psfSigma) + 1 psf = afwDetection.GaussianPsf(psfWidth, psfWidth, psfSigma) psfImage = psf.computeImage(center).convertF() psfFlux = psfImage.getArray().sum() psfImage *= flux / psfFlux subImage = afwImage.ImageF(image, psfImage.getBBox(afwImage.PARENT), afwImage.PARENT) subImage += psfImage exp = afwImage.makeExposure(afwImage.makeMaskedImage(image)) exp.setPsf(psf) exp.getMaskedImage().getVariance().set(1.0) exp.getMaskedImage().getMask().set(0) exp.setWcs( afwImage.makeWcs( afwCoord.Coord(0.0 * afwGeom.degrees, 0.0 * afwGeom.degrees), center, scale.asDegrees(), 0.0, 0.0, scale.asDegrees())) return exp, center
def getPsf(self, exposure, sigma=None): """Retrieve the PSF for an exposure If ``sigma`` is provided, we make a ``GaussianPsf`` with that, otherwise use the one from the ``exposure``. Parameters ---------- exposure : `lsst.afw.image.Exposure` Exposure from which to retrieve the PSF. sigma : `float`, optional Gaussian sigma to use if provided. Returns ------- psf : `lsst.afw.detection.Psf` PSF to use for detection. """ if sigma is None: psf = exposure.getPsf() if psf is None: raise RuntimeError("Unable to determine PSF to use for detection: no sigma provided") sigma = psf.computeShape().getDeterminantRadius() size = self.calculateKernelSize(sigma) psf = afwDet.GaussianPsf(size, size, sigma) return psf
def testMeasurePsf(self): """Test that we measure the Psf only when we want to and fail when we can't.""" def doMeasure(exposure, config): schema = afwTable.SourceTable.makeMinimalSchema() shapeFinder = algorithms.MeasureSourcesBuilder().addAlgorithm( config.makeControl()).build(schema) table = afwTable.SourceTable.make(schema) record = table.makeRecord() shapeFinder.apply(record, exposure, afwGeom.Point2D(0.0, 0.0)) return record psf = afwDetection.GaussianPsf(101, 101, 3.0) psfImage = psf.computeKernelImage() exposure1 = afwImage.ExposureF(psfImage.getBBox(afwImage.PARENT)) exposure1.getMaskedImage().getImage().getArray( )[:, :] = psfImage.getArray() exposure1.getMaskedImage().getVariance().set(0.1) # We try to measure the Psf shape, but we don't have one: flag should be set record1 = doMeasure(exposure1, algorithms.SdssShapeConfig()) self.assertTrue(record1.get("shape.sdss.flags.psf")) # We don't try to measure the Psf shape: extra fields should not be present record2 = doMeasure(exposure1, algorithms.SdssShapeConfig(doMeasurePsf=False)) self.assertFalse("shape.sdss.flags.psf" in record2.schema) self.assertFalse("shape.sdss.psf" in record2.schema) # We try to measure the Psf shape, and do have one: shape of image should match shape of Psf exposure1.setPsf(psf) record3 = doMeasure(exposure1, algorithms.SdssShapeConfig()) self.assertFalse(record3.get("shape.sdss.flags.psf")) self.assertFalse(record3.get("shape.sdss.flags")) self.assertClose(record3.get("shape.sdss.psf").getParameterVector(), record3.get("shape.sdss").getParameterVector(), rtol=1E-4)
def setUp(self): """Make the image we'll measure. """ self.exp = afwImage.ExposureF(100, 100) psf = afwDetection.GaussianPsf(15, 15, 3.0) self.exp.setPsf(psf) self.I0, self.xcen, self.ycen = 1000.0, 50.5, 50.0 im = self.exp.getMaskedImage().getImage() for i in (-1, 0, 1): for j in (-1, 0, 1): im[int(self.xcen) + i, int(self.ycen) + j, afwImage.LOCAL] = \ self.I0*(1 - 0.5*math.hypot(i - 0.5, j))
def do_testAstrometry(self, alg, bkgd, control): """Test that we can instantiate and play with a centroiding algorithm. """ schema = afwTable.SourceTable.makeMinimalSchema() schema.getAliasMap().set("slot_Centroid", "test") centroider = alg(control, "test", schema) table = afwTable.SourceCatalog(schema) x0, y0 = 12345, 54321 for imageFactory in (afwImage.MaskedImageF,): im = imageFactory(lsst.geom.ExtentI(100, 100)) im.setXY0(lsst.geom.Point2I(x0, y0)) # This fixed DoubleGaussianPsf replaces a computer generated one. # The values are not anything in particular, just a reasonable size. psf = afwDetection.GaussianPsf(15, 15, 3.0) exp = afwImage.makeExposure(im) exp.setPsf(psf) im.set(bkgd) x, y = 30, 20 im.image[x, y, afwImage.LOCAL] = 1010 source = table.addNew() spans = afwGeom.SpanSet(exp.getBBox(afwImage.LOCAL)) foot = afwDetection.Footprint(spans) foot.addPeak(x + x0, y + y0, 1010) source.setFootprint(foot) centroider.measure(source, exp) self.assertFloatsAlmostEqual(x + x0, source.getX(), rtol=.00001) self.assertFloatsAlmostEqual(y + y0, source.getY(), rtol=.00001) self.assertFalse(source.get("test_flag")) im.set(bkgd) im.image[10, 20, afwImage.LOCAL] = 1010 im.image[10, 21, afwImage.LOCAL] = 1010 im.image[11, 20, afwImage.LOCAL] = 1010 im.image[11, 21, afwImage.LOCAL] = 1010 x, y = 10.5 + x0, 20.5 + y0 source = table.addNew() source.set('test_x', x) source.set('test_y', y) centroider.measure(source, exp) self.assertFloatsAlmostEqual(x, source.getX(), rtol=.00001) self.assertFloatsAlmostEqual(y, source.getY(), rtol=.00001) self.assertFalse(source.get("test_flag"))
def test1(self): #exposure = afwImage.ExposureF('mini-v85408556-fr-R23-S11.fits') #exposure = afwImage.ExposureF('../afwdata/ImSim/calexp/v85408556-fr/R23/S11.fits') #bb = afwGeom.Box2I(afwGeom.Point2I(0,0), afwGeom.Point2I(511,511)) #exposure = afwImage.ExposureF('data/goodSeeingCoadd/r/3/113,0/coadd-r-3-113,0.fits', 0, bb) #exposure.writeFits('mini-r-3-113,0.fits') fn = os.path.join(os.path.dirname(__file__), 'data', 'mini-r-3-113,0.fits.gz') print 'Reading image', fn exposure = afwImage.ExposureF(fn) exposure.setPsf(afwDetection.GaussianPsf(15, 15, 3)) schema = afwTable.SourceTable.makeMinimalSchema() idFactory = afwTable.IdFactory.makeSimple() dconf = measAlg.SourceDetectionConfig() dconf.reEstimateBackground = False dconf.includeThresholdMultiplier = 5. mconf = SingleFrameMeasurementConfig() aconf = ANetAstrometryConfig() aconf.forceKnownWcs = True det = measAlg.SourceDetectionTask(schema=schema, config=dconf) meas = SingleFrameMeasurementTask(schema, config=mconf) astrom = ANetAstrometryTask(schema, config=aconf, name='astrom') astrom.log.setThreshold(pexLog.Log.DEBUG) inwcs = exposure.getWcs() print 'inwcs:', inwcs instr = inwcs.getFitsMetadata().toString() print 'inwcs:', instr table = afwTable.SourceTable.make(schema, idFactory) sources = det.makeSourceCatalog(table, exposure, sigma=1).sources meas.measure(exposure, sources) for dosip in [False, True]: aconf.solver.calculateSip = dosip ast = astrom.run(sourceCat=sources, exposure=exposure) outwcs = exposure.getWcs() outstr = outwcs.getFitsMetadata().toString() if dosip is False: self.assertEqual(inwcs, outwcs) self.assertEqual(instr, outstr) print 'inwcs:', instr print 'outwcs:', outstr print len(ast.matches), 'matches' self.assertTrue(len(ast.matches) > 10)
def makeExposure(bbox, scale, psfFwhm, flux): """Make a fake exposure Parameters ---------- bbox : `lsst.afw.geom.Box2I` Bounding box for image. scale : `lsst.afw.geom.Angle` Pixel scale. psfFwhm : `float` PSF FWHM (arcseconds) flux : `float` PSF flux (ADU) Returns ------- exposure : `lsst.afw.image.ExposureF` Fake exposure. center : `lsst.afw.geom.Point2D` Position of fake source. """ image = afwImage.ImageF(bbox) image.set(0) center = afwGeom.Box2D(bbox).getCenter() psfSigma = psfFwhm / SIGMA_TO_FWHM / scale.asArcseconds() psfWidth = 2 * int(4.0 * psfSigma) + 1 psf = afwDetection.GaussianPsf(psfWidth, psfWidth, psfSigma) psfImage = psf.computeImage(center).convertF() psfFlux = psfImage.getArray().sum() psfImage *= flux / psfFlux subImage = afwImage.ImageF(image, psfImage.getBBox(afwImage.PARENT), afwImage.PARENT) subImage += psfImage exp = afwImage.makeExposure(afwImage.makeMaskedImage(image)) exp.setPsf(psf) exp.getMaskedImage().getVariance().set(1.0) exp.getMaskedImage().getMask().set(0) cdMatrix = afwGeom.makeCdMatrix(scale=scale) exp.setWcs( afwGeom.makeSkyWcs(crpix=center, crval=afwGeom.SpherePoint(0.0, 0.0, afwGeom.degrees), cdMatrix=cdMatrix)) return exp, center
def testDM17431(self): """Test that priority order is respected specifically when lower priority footprints overlap two previously disconnected higher priority footprints. """ box = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Point2I(100, 100)) psfsig = 1. kernelSize = 41 flux = 1000 cat1 = {} cat2 = {} peakDist = 10 samePeakDist = 3 # cat2 connects cat1's 2 separated footprints. # 65 and 70 are too close to be new or same peaks. # 70 is higher priority and should be in the catalog instead of 65. cat1['pos'] = [(50, 50), (70, 50)] cat2['pos'] = [(55, 50), (65, 50)] schema = afwTable.SourceTable.makeMinimalSchema() idFactory = afwTable.IdFactory.makeSimple() table = afwTable.SourceTable.make(schema, idFactory) for (cat, psfFactor) in zip([cat1, cat2], [1, 2]): cat['mi'] = afwImage.MaskedImageD(box) cat['psf'] = afwDetect.GaussianPsf(kernelSize, kernelSize, psfFactor * psfsig) insertPsf(cat['pos'], cat['mi'], cat['psf'], kernelSize, flux) fp = afwDetect.FootprintSet(cat['mi'], afwDetect.Threshold(0.001), "DETECTED") cat['catalog'] = afwTable.SourceCatalog(table) fp.makeSources(cat['catalog']) merge, nob, npeak = mergeCatalogs([cat1['catalog'], cat2['catalog']], ["1", "2"], peakDist, idFactory, samePeakDist=samePeakDist) # Check that both higher-priority cat1 records survive peak merging for record in cat1['catalog']: for peak in record.getFootprint().getPeaks(): self.assertTrue(isPeakInCatalog(peak, merge))
def test1(self): fn = os.path.join(os.path.dirname(__file__), 'data', 'mini-r-3-113,0.fits.gz') print('Reading image', fn) exposure = afwImage.ExposureF(fn) exposure.setPsf(afwDetection.GaussianPsf(15, 15, 3)) schema = afwTable.SourceTable.makeMinimalSchema() idFactory = afwTable.IdFactory.makeSimple() dconf = measAlg.SourceDetectionConfig() dconf.reEstimateBackground = False dconf.includeThresholdMultiplier = 5. mconf = SingleFrameMeasurementConfig() aconf = ANetAstrometryConfig() aconf.forceKnownWcs = True det = measAlg.SourceDetectionTask(schema=schema, config=dconf) meas = SingleFrameMeasurementTask(schema, config=mconf) astrom = ANetAstrometryTask(schema, config=aconf, name='astrom') astrom.log.setLevel(astrom.log.TRACE) inwcs = exposure.getWcs() print('inwcs:', inwcs) instr = inwcs.getFitsMetadata().toString() print('inwcs:', instr) table = afwTable.SourceTable.make(schema, idFactory) sources = det.makeSourceCatalog(table, exposure, sigma=1).sources meas.measure(sources, exposure) for dosip in [False, True]: aconf.solver.calculateSip = dosip ast = astrom.run(sourceCat=sources, exposure=exposure) outwcs = exposure.getWcs() outstr = outwcs.getFitsMetadata().toString() if not dosip: self.assertEqual(inwcs, outwcs) self.assertEqual(instr, outstr) print('inwcs:', instr) print('outwcs:', outstr) print(len(ast.matches), 'matches') self.assertGreater(len(ast.matches), 10)
def makeGalaxy(width, height, flux, a, b, theta, dx=0.0, dy=0.0, xy0=None, xcen=None, ycen=None): """Make a fake galaxy image. """ gal = afwImage.ImageF(width, height) if xcen is None: xcen = 0.5 * width + dx if ycen is None: ycen = 0.5 * height + dy I0 = flux / (2 * math.pi * a * b) if xy0 is not None: gal.setXY0(xy0) c, s = math.cos(math.radians(theta)), math.sin(math.radians(theta)) ii, iuu, ivv = 0.0, 0.0, 0.0 for y in range(height): for x in range(width): dx, dy = x + gal.getX0() - xcen, y + gal.getY0() - ycen if math.hypot(dx, dy) < 10.5: nsample = 5 subZ = np.linspace(-0.5 * (1 - 1 / nsample), 0.5 * (1 - 1 / nsample), nsample) else: nsample = 1 subZ = [0.0] val = 0 for sx in subZ: for sy in subZ: u = c * (dx + sx) + s * (dy + sy) v = -s * (dx + sx) + c * (dy + sy) val += I0 * math.exp(-0.5 * ((u / a)**2 + (v / b)**2)) if val < 0: val = 0 gal[afwGeom.Point2I(x, y), afwImage.LOCAL] = val / nsample**2 ii += val iuu += val * u**2 ivv += val * v**2 iuu /= ii ivv /= ii exp = afwImage.makeExposure(afwImage.makeMaskedImage(gal)) exp.getMaskedImage().getVariance().set(1.0) scale = 1.0e-4 * afwGeom.degrees cdMatrix = afwGeom.makeCdMatrix(scale=scale, flipX=True) exp.setWcs( afwGeom.makeSkyWcs(crpix=afwGeom.Point2D(0.0, 0.0), crval=afwGeom.SpherePoint(0.0, 0.0, afwGeom.degrees), cdMatrix=cdMatrix)) # add a dummy Psf. The new SdssCentroid needs one exp.setPsf(afwDetection.GaussianPsf(11, 11, 0.01)) return exp
def plantSources(bbox, kwid, sky, coordList, addPoissonNoise=True): """Make an exposure with stars (modelled as Gaussians). Parameters ---------- bbox : `lsst.afw.geom.Box2I` Parent bounding box of output exposure. kwid : `float` Kernel width (and height; kernel is square). sky : `float` Amount of sky background (counts) coordList : iterable of `list` Where: - ``coordList[0]`` is the X coord of the star relative to the origin - ``coordList[1]`` is the Y coord of the star relative to the origin - ``coordList[2]`` is the integrated counts in the star - ``coordlist[3]`` is the Gaussian sigma in pixels. addPoissonNoise : `bool`, optional Add Poisson noise to the output exposure? Returns ------- exposure : `lsst.afw.image.ExposureF` Resulting exposure with simulated stars. """ # make an image with sources img = afwImage.ImageD(bbox) meanSigma = 0.0 for coord in coordList: x, y, counts, sigma = coord meanSigma += sigma # make a single gaussian psf psf = afwDetection.GaussianPsf(kwid, kwid, sigma) # make an image of it and scale to the desired number of counts thisPsfImg = psf.computeImage(lsst.geom.PointD(int(x), int(y))) thisPsfImg *= counts # bbox a window in our image and add the fake star image imgSeg = img.Factory(img, thisPsfImg.getBBox()) imgSeg += thisPsfImg meanSigma /= len(coordList) img += sky # add Poisson noise if (addPoissonNoise): np.random.seed(seed=1) # make results reproducible imgArr = img.getArray() imgArr[:] = np.random.poisson(imgArr) # bundle into a maskedimage and an exposure mask = afwImage.Mask(bbox) var = img.convertFloat() img -= sky mimg = afwImage.MaskedImageF(img.convertFloat(), mask, var) exposure = afwImage.makeExposure(mimg) # insert an approximate psf psf = afwDetection.GaussianPsf(kwid, kwid, meanSigma) exposure.setPsf(psf) return exposure
rand.uniformInt(im.getWidth() - 2 * buffer_xy) + buffer_xy for i in xrange(n_objects) ] y_positions = [ rand.uniformInt(im.getHeight() - 2 * buffer_xy) + buffer_xy for i in xrange(n_objects) ] print("x_positions , y_positions = ", x_positions, y_positions) display = afwDisplay.getDisplay() display.setMaskTransparency(50, None) psf_size = 121 # This has to be odd sigma = 0.7 / 0.2 # seeing in arcsec/pixel size in arcsec peak_val = 6000 psf = afwDetect.GaussianPsf(psf_size, psf_size, sigma) psf_im = psf.computeImage() print("psf = ", psf) print("psf_im = ", psf_im) for x, y in zip(x_positions, y_positions): x0 = x - (psf_size - 1) / 2 y0 = y - (psf_size - 1) / 2 box = afwGeom.BoxI(afwGeom.PointI(x0, y0), afwGeom.ExtentI(psf_size, psf_size)) subim = afwImage.ImageF(im, box, afwImage.LOCAL) try: subim += psf_im
def main(): date = datetime.datetime.now().strftime("%a %Y-%m-%d %H:%M:%S") ######################################################################## # command line arguments and options ######################################################################## parser = optparse.OptionParser(usage = __doc__) #parser.add_option("-a", "--aa", dest="aa", type=float, # default=1.0, help="default=%default") opts, args = parser.parse_args() if len(args) == 0: r1, r2, dr = 3.0, 3.0, 0.5 elif len(args) == 3: r1, r2, dr = map(float, args) else: parser.print_help() sys.exit(1) # make a list of radii to compute the growthcurve points radius = [] nR = int( (r2 - r1)/dr + 1 ) for iR in range(nR): radius.append(r1 + iR*dr) # make an image big enough to hold the largest requested aperture xwidth = 2*(0 + 128) ywidth = xwidth # initializations sigmas = [1.5, 2.5] # the Gaussian widths of the psfs we'll use nS = len(sigmas) a = 100.0 aptaper = 2.0 xcen = xwidth/2 ycen = ywidth/2 alg = measBase.PsfFluxAlgorithm schema = afwTable.SourceTable.makeMinimalSchema() schema.addField("centroid_x", type=float) schema.addField("centroid_y", type=float) plugin = alg(measBase.PsfFluxControl(), "test", schema) cat = afwTable.SourceCatalog(schema) cat.defineCentroid("centroid") print "# sig rad Naive Sinc Psf" for iS in range(nS): sigma = sigmas[iS]; # make a Gaussian star to measure gauss = afwMath.GaussianFunction2D(sigma, sigma) kernel = afwMath.AnalyticKernel(xwidth, ywidth, gauss) kimg = afwImage.ImageD(kernel.getDimensions()) kernel.computeImage(kimg, False) kimg *= 100.0 mimg = afwImage.MaskedImageF(kimg.convertFloat(), afwImage.MaskU(kimg.getDimensions(), 0x0), afwImage.ImageF(kimg.getDimensions(), 0.0)) exposure = afwImage.ExposureF(mimg) # loop over all the radii in the growthcurve for iR in range(nR): psfH = int(2.0*(r2 + 2.0)) + 1 psfW = int(2.0*(r2 + 2.0)) + 1 # this test uses a single Gaussian instead of the original double gaussian psf = afwDet.GaussianPsf(psfW, psfH, sigma) # get the aperture fluxes for Naive and Sinc methods axes = afwGeom.ellipses.Axes(radius[iR], radius[iR], math.radians(0)); center = afwGeom.Point2D(0,0) ellipse = afwGeom.ellipses.Ellipse(axes, center) resultSinc = measBase.ApertureFluxAlgorithm.computeSincFlux(mimg.getImage(), ellipse) resultNaive = measBase.ApertureFluxAlgorithm.computeNaiveFlux(mimg.getImage(), ellipse) source = cat.addNew() source["centroid_x"] = 0 source["centroid_y"] = 0 exposure.setPsf(psf) plugin.measure(source, exposure) fluxNaive = resultNaive.flux fluxSinc = resultSinc.flux fluxPsf = source["test_flux"] # get the exact flux for the theoretical smooth PSF # rpsf = RGaussian(sigma, a, radius[iR], aptaper) # *** not sure how to integrate a python functor *** print "%.2f %.2f %.3f %.3f %.3f" % (sigma, radius[iR], fluxNaive, fluxSinc, fluxPsf)
def testPeakLikelihoodFlux(self): """Test measurement with PeakLikelihoodFlux """ # make and measure a series of exposures containing just one star, approximately centered bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(100, 101)) kernelWidth = 35 var = 100 fwhm = 3.0 sigma = fwhm / FwhmPerSigma convolutionControl = afwMath.ConvolutionControl() psf = afwDetection.GaussianPsf(kernelWidth, kernelWidth, sigma) psfKernel = psf.getLocalKernel() psfImage = psf.computeKernelImage() sumPsfSq = numpy.sum(psfImage.getArray()**2) psfSqArr = psfImage.getArray()**2 for flux in (1000, 10000): ctrInd = afwGeom.Point2I(50, 51) ctrPos = afwGeom.Point2D(ctrInd) kernelBBox = psfImage.getBBox() kernelBBox.shift(afwGeom.Extent2I(ctrInd)) # compute predicted flux error unshMImage = makeFakeImage(bbox, [ctrPos], [flux], fwhm, var) # filter image by PSF unshFiltMImage = afwImage.MaskedImageF(unshMImage.getBBox()) afwMath.convolve(unshFiltMImage, unshMImage, psfKernel, convolutionControl) # compute predicted flux = value of image at peak / sum(PSF^2) # this is a sanity check of the algorithm, as much as anything predFlux = unshFiltMImage.getImage().get(ctrInd[0], ctrInd[1]) / sumPsfSq self.assertLess(abs(flux - predFlux), flux * 0.01) # compute predicted flux error based on filtered pixels # = sqrt(value of filtered variance at peak / sum(PSF^2)^2) predFluxErr = math.sqrt(unshFiltMImage.getVariance().get( ctrInd[0], ctrInd[1])) / sumPsfSq # compute predicted flux error based on unfiltered pixels # = sqrt(sum(unfiltered variance * PSF^2)) / sum(PSF^2) # and compare to that derived from filtered pixels; # again, this is a test of the algorithm varView = afwImage.ImageF(unshMImage.getVariance(), kernelBBox) varArr = varView.getArray() unfiltPredFluxErr = math.sqrt(numpy.sum( varArr * psfSqArr)) / sumPsfSq self.assertLess(abs(unfiltPredFluxErr - predFluxErr), predFluxErr * 0.01) for fracOffset in (afwGeom.Extent2D(0, 0), afwGeom.Extent2D(0.2, -0.3)): adjCenter = ctrPos + fracOffset if fracOffset == (0, 0): maskedImage = unshMImage filteredImage = unshFiltMImage else: maskedImage = makeFakeImage(bbox, [adjCenter], [flux], fwhm, var) # filter image by PSF filteredImage = afwImage.MaskedImageF( maskedImage.getBBox()) afwMath.convolve(filteredImage, maskedImage, psfKernel, convolutionControl) exp = afwImage.makeExposure(filteredImage) exp.setPsf(psf) control = measBase.PeakLikelihoodFluxControl() plugin, cat = makePluginAndCat( measBase.PeakLikelihoodFluxAlgorithm, "test", control, centroid="centroid") source = cat.makeRecord() source.set("centroid_x", adjCenter.getX()) source.set("centroid_y", adjCenter.getY()) plugin.measure(source, exp) measFlux = source.get("test_flux") measFluxErr = source.get("test_fluxSigma") self.assertLess(abs(measFlux - flux), flux * 0.003) self.assertLess(abs(measFluxErr - predFluxErr), predFluxErr * 0.2) # try nearby points and verify that the flux is smaller; # this checks that the sub-pixel shift is performed in the correct direction for dx in (-0.2, 0, 0.2): for dy in (-0.2, 0, 0.2): if dx == dy == 0: continue offsetCtr = afwGeom.Point2D(adjCenter[0] + dx, adjCenter[1] + dy) source = cat.makeRecord() source.set("centroid_x", offsetCtr.getX()) source.set("centroid_y", offsetCtr.getY()) plugin.measure(source, exp) self.assertLess(source.get("test_flux"), measFlux) # source so near edge of image that PSF does not overlap exposure should result in failure for edgePos in ( (1, 50), (50, 1), (50, bbox.getHeight() - 1), (bbox.getWidth() - 1, 50), ): source = cat.makeRecord() source.set("centroid_x", edgePos[0]) source.set("centroid_y", edgePos[1]) self.assertRaises( lsst.pex.exceptions.RangeError, plugin.measure, source, exp, ) # no PSF should result in failure: flags set noPsfExposure = afwImage.ExposureF(filteredImage) source = cat.makeRecord() source.set("centroid_x", edgePos[0]) source.set("centroid_y", edgePos[1]) self.assertRaises( lsst.pex.exceptions.InvalidParameterError, plugin.measure, source, noPsfExposure, )
def setUp(self): size = 128 # size of image (pixels) center = afwGeom.Point2D(size // 2, size // 2) # object center width = 2 # PSF width flux = 10.0 # Flux of object variance = 1.0 # Mean variance value varianceStd = 0.1 # Standard deviation of the variance value # Set a seed for predictable randomness np.random.seed(300) # Create a random image to be used as variance plane variancePlane = np.random.normal(variance, varianceStd, size * size).reshape(size, size) # 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.getArray()[:, :] = variancePlane # Put down a PSF psfSize = int(6 * width + 1) # Size of PSF image; must be odd psf = afwDetection.GaussianPsf(psfSize, psfSize, 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") # Put in some unmasked bad pixels outside the expected aperture, to ensure the aperture is working var.getArray()[0, 0] = float("nan") var.getArray()[0, -1] = float("nan") var.getArray()[-1, 0] = float("nan") var.getArray()[-1, -1] = float("nan") if display: import lsst.afw.display as afwDisplay afwDisplay.getDisplay(1).mtv(image) afwDisplay.getDisplay(2).mtv(mask) afwDisplay.getDisplay(3).mtv(var) config = measBase.SingleFrameMeasurementConfig() config.plugins.names = [ "base_NaiveCentroid", "base_SdssShape", "base_Variance" ] config.slots.centroid = "base_NaiveCentroid" config.slots.psfFlux = None config.slots.apFlux = None config.slots.modelFlux = None config.slots.instFlux = None config.slots.calibFlux = None config.slots.shape = "base_SdssShape" config.slots.psfShape = None config.plugins["base_Variance"].mask = ["BAD", "SAT"] config.validate() schema = afwTable.SourceTable.makeMinimalSchema() task = measBase.SingleFrameMeasurementTask(schema, config=config) catalog = afwTable.SourceCatalog(schema) spans = afwGeom.SpanSet.fromShape(int(width)) spans = spans.shiftedBy(int(center.getX()), int(center.getY())) foot = afwDetection.Footprint(spans) 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) self.variance = variance self.varianceStd = varianceStd self.mask = mask self.catalog = catalog self.exp = exp self.task = task self.source = source
def testForced(self): """Check that forced photometry works in the presence of rotations and translations. """ kfac = 2.5 warper = afwMath.Warper("lanczos4") a = 13 for axisRatio in (0.25, 1.0): b = a * axisRatio for theta in (0, 30, 45): width, height = 256, 256 center = afwGeom.Point2D(0.5 * width, 0.5 * height) original = makeGalaxy(width, height, 1000.0, a, b, theta) msConfig = makeMeasurementConfig(forced=False, kfac=kfac) source = measureFree(original, center, msConfig) algMeta = source.getTable().getMetadata() self.assertTrue( algMeta.exists( 'ext_photometryKron_KronFlux_nRadiusForFlux')) if source.get("ext_photometryKron_KronFlux_flag"): continue angleList = [val * afwGeom.degrees for val in (45, 90)] scaleList = [1.0, 0.5] offsetList = [(1.23, 4.56), (12.3, 45.6)] for angle, scale, offset in itertools.product( angleList, scaleList, offsetList): dx, dy = offset pixelScale = original.getWcs().getPixelScale() * scale cdMatrix = afwGeom.makeCdMatrix(scale=pixelScale, orientation=angle, flipX=True) wcs = afwGeom.makeSkyWcs(crpix=afwGeom.Point2D(dx, dy), crval=afwGeom.SpherePoint( 0.0, 0.0, afwGeom.degrees), cdMatrix=cdMatrix) warped = warper.warpExposure(wcs, original) # add a Psf if there is none. The new SdssCentroid needs a Psf. if warped.getPsf() is None: warped.setPsf(afwDetection.GaussianPsf(11, 11, 0.01)) msConfig = makeMeasurementConfig(kfac=kfac, forced=True) forced = measureForced(warped, source, original.getWcs(), msConfig) algMeta = source.getTable().getMetadata() self.assertTrue( algMeta.exists( 'ext_photometryKron_KronFlux_nRadiusForFlux')) if display: disp1 = afwDisplay.Display(frame=1) disp1.mtv(original, title=self._testMethodName + ": original image") shape = source.getShape().clone() xc, yc = source.getCentroid() radius = source.get( "ext_photometryKron_KronFlux_radius") for r, ct in [ (radius, afwDisplay.BLUE), (radius * kfac, afwDisplay.CYAN), ]: shape.scale(r / shape.getDeterminantRadius()) disp1.dot(shape, xc, yc, ctype=ct) disp2 = afwDisplay.Display(frame=2) disp2.mtv(warped, title=self._testMethodName + ": warped image") transform = (wcs.linearizeSkyToPixel( source.getCoord(), lsst.geom.degrees) * original.getWcs().linearizePixelToSky( source.getCoord(), lsst.geom.degrees)) shape = shape.transform(transform.getLinear()) radius = source.get( "ext_photometryKron_KronFlux_radius") xc, yc = wcs.skyToPixel(source.getCoord()) for r, ct in [ (radius, afwDisplay.BLUE), (radius * kfac, afwDisplay.CYAN), ]: shape.scale(r / shape.getDeterminantRadius()) disp2.dot(shape, xc, yc, ctype=ct) try: self.assertFloatsAlmostEqual( source.get("ext_photometryKron_KronFlux_instFlux"), forced.get("ext_photometryKron_KronFlux_instFlux"), rtol=1.0e-3) self.assertFloatsAlmostEqual( source.get("ext_photometryKron_KronFlux_radius"), scale * forced.get("ext_photometryKron_KronFlux_radius"), rtol=1.0e-3) self.assertEqual( source.get("ext_photometryKron_KronFlux_flag"), forced.get("ext_photometryKron_KronFlux_flag")) except Exception: print(("Failed:", angle, scale, offset, [ (source.get(f), forced.get(f)) for f in ("ext_photometryKron_KronFlux_instFlux", "ext_photometryKron_KronFlux_radius", "ext_photometryKron_KronFlux_flag") ])) raise
def test(self): """Check that we can measure an aperture correction""" width, height = 1234, 2345 edge = 25 # buffer around edge numStars = 10 # in each dimension size = 15 # Size of PSF sigma = 3.0 # PSF sigma measureConfig = SourceMeasurementConfig() measureConfig.algorithms["flux.sinc"].radius = size apCorrConfig = MeasureApCorrTask.ConfigClass() apCorrConfig.inputFilterFlag = "use.this" schema = afwTable.SourceTable.makeMinimalSchema() measurer = measureConfig.makeMeasureSources(schema) schema.addField("use.this", type="Flag", doc="use this for aperture correction") apCorr = MeasureApCorrTask(config=apCorrConfig, schema=schema) # Generate an image with a bunch of sources exposure = afwImage.ExposureF(width, height) psf = afwDetection.GaussianPsf(size, size, sigma) exposure.setPsf(psf) image = exposure.getMaskedImage().getImage() mask = exposure.getMaskedImage().getMask() mask.addMaskPlane("DETECTED") detected = mask.getPlaneBitMask("DETECTED") xList = numpy.linspace(edge, width - edge, numStars) yList = numpy.linspace(edge, height - edge, numStars) for x in xList: for y in yList: psfImage = psf.computeImage(afwGeom.Point2D(x, y)).convertF() psfImage *= 10000 bbox = psfImage.getBBox(afwImage.PARENT) subImage = image.Factory(image, bbox) subImage += psfImage # Detect and measure those sources catalog = afwTable.SourceCatalog(schema) measureConfig.slots.setupTable(catalog.table) feet = afwDetection.FootprintSet(exposure.getMaskedImage(), afwDetection.Threshold(0.1), "DETECTED") feet.makeSources(catalog) self.assertGreater(len(catalog), 0) for source in catalog: measurer.applyWithPeak(source, exposure) source.set("use.this", True) if display: ds9.mtv(exposure, frame=1) # Cheat in order to trigger clipping in aperture correction measurement good = numpy.ones(len(catalog), dtype=bool) good[len(catalog) // 3] = False catalog[apCorrConfig.reference][numpy.where(good == 0)] *= 2 apCorrMap = apCorr.run(exposure.getBBox(), catalog) # Validate answers self.assertGreater(len(apCorrMap), 0) # Or we're not really testing anything for name in apCorrMap: expected = (catalog[apCorrConfig.reference][good] / catalog[name][good]).mean() corr = apCorrMap[name] for x in xList: for y in yList: value = corr.evaluate(x, y) if name.endswith(".err"): self.assertLess(value, 5.0e-4) else: self.assertClose(corr.evaluate(x, y), expected, atol=5.0e-4) if not name.endswith(".err"): self.assertTrue( numpy.all(catalog["apcorr." + name + ".used"] == good)) # Used only good srcs