def makeTestImages(self, seed=5, nSrc=5, psfSize=2., noiseLevel=5., detectionSigma=5., sourceSigma=20., fluxRange=2.): """Make reproduceable PSF-convolved masked images for testing. Parameters ---------- seed : `int`, optional Seed value to initialize the random number generator. nSrc : `int`, optional Number of sources to simulate. psfSize : `float`, optional Width of the PSF of the simulated sources, in pixels. noiseLevel : `float`, optional Standard deviation of the noise to add to each pixel. detectionSigma : `float`, optional Threshold amplitude of the image to set the "DETECTED" mask. sourceSigma : `float`, optional Average amplitude of the simulated sources, relative to ``noiseLevel`` fluxRange : `float`, optional Range in flux amplitude of the simulated sources. Returns ------- modelImages : `list` of `lsst.afw.image.Image` A list of images, each containing the model for one subfilter """ rng = np.random.RandomState(seed) x0, y0 = self.bbox.getBegin() xSize, ySize = self.bbox.getDimensions() xLoc = rng.rand(nSrc)*(xSize - 2*self.bufferSize) + self.bufferSize + x0 yLoc = rng.rand(nSrc)*(ySize - 2*self.bufferSize) + self.bufferSize + y0 modelImages = [] imageSum = np.zeros((ySize, xSize)) for subfilter in range(self.dcrNumSubfilters): flux = (rng.rand(nSrc)*(fluxRange - 1.) + 1.)*sourceSigma*noiseLevel sigmas = [psfSize for src in range(nSrc)] coordList = list(zip(xLoc, yLoc, flux, sigmas)) model = plantSources(self.bbox, 10, 0, coordList, addPoissonNoise=False) model.image.array += rng.rand(ySize, xSize)*noiseLevel imageSum += model.image.array model.mask.addMaskPlane("CLIPPED") modelImages.append(model.image) maskVals = np.zeros_like(imageSum) maskVals[imageSum > detectionSigma*noiseLevel] = afwImage.Mask.getPlaneBitMask('DETECTED') model.mask.array[:] = maskVals self.mask = model.mask return modelImages
def testBasics(self): bbox = afwGeom.Box2I(afwGeom.Point2I(256, 100), afwGeom.Extent2I(128, 127)) minCounts = 5000 maxCounts = 50000 starSigma = 1.5 numX = 5 numY = 5 coordList = self.makeCoordList( bbox=bbox, numX=numX, numY=numY, minCounts=minCounts, maxCounts=maxCounts, sigma=starSigma, ) kwid = 11 sky = 2000 addPoissonNoise = True exposure = plantSources(bbox=bbox, kwid=kwid, sky=sky, coordList=coordList, addPoissonNoise=addPoissonNoise) schema = afwTable.SourceTable.makeMinimalSchema() config = SourceDetectionTask.ConfigClass() config.reEstimateBackground = False task = SourceDetectionTask(config=config, schema=schema) for doSmooth in (False, True): taskSigma = 2.2 res = task.detectFootprints(exposure, doSmooth=doSmooth, sigma=taskSigma) self.assertEqual(res.numPos, numX * numY) self.assertEqual(res.numNeg, 0) self.assertEqual(task.metadata.get("sigma"), taskSigma) self.assertEqual(task.metadata.get("doSmooth"), doSmooth) self.assertEqual(task.metadata.get("nGrow"), int(taskSigma * config.nSigmaToGrow + 0.5)) res = task.detectFootprints(exposure, doSmooth=doSmooth, sigma=None) taskSigma = task.metadata.get("sigma") self.assertTrue(abs(taskSigma - starSigma) < 0.1) self.assertEqual(res.numPos, numX * numY) self.assertEqual(res.numNeg, 0)
def setUp(self): xy0 = Point2I(12345, 67890) # xy0 for image dims = Extent2I(2345, 2345) # Dimensions of image box = Box2I(xy0, dims) # Bounding box of image sigma = 3.21 # PSF sigma buffer = 4.0 # Buffer for star centers around edge nSigmaForKernel = 5.0 # Number of PSF sigmas for kernel sky = 12345.6 # Sky level numStars = 100 # Number of stars noise = np.sqrt(sky)*np.pi*sigma**2 # Poisson noise per PSF faint = 1.0*noise # Faintest level for star fluxes bright = 100.0*noise # Brightest level for star fluxes starBox = Box2I(box) # Area on image in which we can put star centers starBox.grow(-int(buffer*sigma)) scale = 1.0e-5*degrees # Pixel scale np.random.seed(12345) stars = [(xx, yy, ff, sigma) for xx, yy, ff in zip(np.random.uniform(starBox.getMinX(), starBox.getMaxX(), numStars), np.random.uniform(starBox.getMinY(), starBox.getMaxY(), numStars), np.linspace(faint, bright, numStars))] self.exposure = plantSources(box, 2*int(nSigmaForKernel*sigma) + 1, sky, stars, True) self.exposure.setWcs(makeSkyWcs(crpix=Point2D(0, 0), crval=SpherePoint(0, 0, degrees), cdMatrix=makeCdMatrix(scale=scale))) # Make a large area of extra background; we should be robust against it # Unfortunately, some tuning is required here to get something challenging but not impossible: # * A very large box will cause failures because the "extra" and the "normal" are reversed. # * A small box will not be challenging because it's simple to clip out. # * A large value will cause failures because it produces large edges in background-subtrction that # broaden flux distributions. # * A small value will not be challenging because it has little effect. extraBox = Box2I(xy0 + Extent2I(345, 456), Extent2I(1234, 1234)) # Box for extra background extraValue = 0.5*noise # Extra background value to add in self.exposure.image[extraBox, PARENT] += extraValue self.config = DynamicDetectionTask.ConfigClass() self.config.skyObjects.nSources = 300 self.config.reEstimateBackground = False self.config.doTempWideBackground = True self.config.thresholdType = "pixel_stdev" # Relative tolerance for tweak factor # Not sure why this isn't smaller; maybe due to use of Poisson instead of Gaussian noise? self.rtol = 0.1
def testBasics(self): """Test detection and measurement on simple synthesized data """ bbox = Box2I(Point2I(256, 100), Extent2I(128, 127)) minCounts = 5000 maxCounts = 50000 starSigma = 1.5 numX = 5 numY = 5 coordList = self.makeCoordList( bbox=bbox, numX=numX, numY=numY, minCounts=minCounts, maxCounts=maxCounts, sigma=starSigma, ) kwid = 11 # kernel width sky = 2000 # create an exposure without a Wcs; add the Wcs later exposure = plantSources(bbox=bbox, kwid=kwid, sky=sky, coordList=coordList, addPoissonNoise=True) schema = SourceTable.makeMinimalSchema() config = DetectAndMeasureTask.ConfigClass() task = DetectAndMeasureTask(config=config, schema=schema) butler = Butler(root=InputDir) dataRef = butler.dataRef("calexp", dataId=dict(visit=1)) wcs = dataRef.get("raw").getWcs() exposure.setWcs(wcs) exposureIdInfo = dataRef.get("expIdInfo") taskRes = task.run(exposure=exposure, exposureIdInfo=exposureIdInfo) self.assertEqual(len(taskRes.sourceCat), numX * numY) schema = taskRes.sourceCat.schema centroidFlagKey = schema.find("slot_Centroid_flag").getKey() parentKey = schema.find("parent").getKey() psfFluxFlagKey = schema.find("slot_PsfFlux_flag").getKey() psfFluxKey = schema.find("slot_PsfFlux_flux").getKey() for src in taskRes.sourceCat: self.assertFalse(src.get(centroidFlagKey)) # centroid found self.assertEqual(src.get(parentKey), 0) # not debelended self.assertFalse(src.get(psfFluxFlagKey)) # flux measured self.assertGreater(src.get(psfFluxKey), 4000) # flux sane
def setUp(self): self.nx = 64 self.ny = 64 self.kwid = 15 self.sky = 100.0 self.val = 10000.0 self.sigma = 4.0 coordList = [[self.nx / 2, self.ny / 2, self.val, self.sigma]] # exposure with gaussian bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(self.nx, self.ny)) self.expGaussPsf = plantSources(bbox, self.kwid, self.sky, coordList, addPoissonNoise=False) # just plain sky (ie. a constant) self.mimg = afwImage.MaskedImageF(afwGeom.ExtentI(self.nx, self.ny)) self.mimg.set(self.sky, 0x0, self.sky) self.expSky = afwImage.makeExposure(self.mimg) if display > 1: ds9.mtv(self.expGaussPsf)
def testBasics(self): bbox = lsst.geom.Box2I(lsst.geom.Point2I(256, 100), lsst.geom.Extent2I(128, 127)) minCounts = 5000 maxCounts = 50000 starSigma = 1.5 numX = 5 numY = 5 coordList = self.makeCoordList( bbox=bbox, numX=numX, numY=numY, minCounts=minCounts, maxCounts=maxCounts, sigma=starSigma, ) kwid = 11 sky = 2000 addPoissonNoise = True exposure = plantSources(bbox=bbox, kwid=kwid, sky=sky, coordList=coordList, addPoissonNoise=addPoissonNoise) schema = afwTable.SourceTable.makeMinimalSchema() config = SourceDetectionTask.ConfigClass() config.reEstimateBackground = False task = SourceDetectionTask(config=config, schema=schema) for doSmooth in (False, True): taskSigma = 2.2 res = task.detectFootprints(exposure, doSmooth=doSmooth, sigma=taskSigma) self.assertEqual(res.numPos, numX * numY) self.assertEqual(res.numNeg, 0) self.assertEqual(task.metadata.getScalar("sigma"), taskSigma) self.assertEqual(task.metadata.getScalar("doSmooth"), doSmooth) self.assertEqual(task.metadata.getScalar("nGrow"), int(taskSigma * config.nSigmaToGrow + 0.5)) res = task.detectFootprints(exposure, doSmooth=doSmooth, sigma=None) taskSigma = task.metadata.getScalar("sigma") self.assertLess(abs(taskSigma - starSigma), 0.1) self.assertEqual(res.numPos, numX * numY) self.assertEqual(res.numNeg, 0)
def makeTestImages(self, seed=5, nSrc=5, psfSize=2., noiseLevel=5., detectionSigma=5., sourceSigma=20., fluxRange=2.): """Make reproduceable PSF-convolved masked images for testing. Parameters ---------- seed : `int`, optional Seed value to initialize the random number generator. nSrc : `int`, optional Number of sources to simulate. psfSize : `float`, optional Width of the PSF of the simulated sources, in pixels. noiseLevel : `float`, optional Standard deviation of the noise to add to each pixel. detectionSigma : `float`, optional Threshold amplitude of the image to set the "DETECTED" mask. sourceSigma : `float`, optional Average amplitude of the simulated sources, relative to ``noiseLevel`` fluxRange : `float`, optional Range in flux amplitude of the simulated sources. Returns ------- modelImages : `list` of `lsst.afw.image.Image` A list of images, each containing the model for one subfilter """ rng = np.random.RandomState(seed) x0, y0 = self.bbox.getBegin() xSize, ySize = self.bbox.getDimensions() xLoc = rng.rand(nSrc) * (xSize - 2 * self.bufferSize) + self.bufferSize + x0 yLoc = rng.rand(nSrc) * (ySize - 2 * self.bufferSize) + self.bufferSize + y0 modelImages = [] imageSum = np.zeros((ySize, xSize)) for subfilter in range(self.dcrNumSubfilters): flux = (rng.rand(nSrc) * (fluxRange - 1.) + 1.) * sourceSigma * noiseLevel sigmas = [psfSize for src in range(nSrc)] coordList = list(zip(xLoc, yLoc, flux, sigmas)) model = plantSources(self.bbox, 10, 0, coordList, addPoissonNoise=False) model.image.array += rng.rand(ySize, xSize) * noiseLevel imageSum += model.image.array model.mask.addMaskPlane("CLIPPED") modelImages.append(model.image) maskVals = np.zeros_like(imageSum) maskVals[imageSum > detectionSigma * noiseLevel] = afwImage.Mask.getPlaneBitMask('DETECTED') model.mask.array[:] = maskVals self.mask = model.mask return modelImages
def makeTestImage(self, expId, noiseLevel=None, psfSize=None, backgroundLevel=None, detectionSigma=5., badRegionBox=None): """Make a reproduceable PSF-convolved masked image for testing. Parameters ---------- expId : `int` A unique identifier to use to refer to the visit. noiseLevel : `float`, optional Standard deviation of the noise to add to each pixel. psfSize : `float`, optional Width of the PSF of the simulated sources, in pixels. backgroundLevel : `float`, optional Background value added to all pixels in the simulated images. detectionSigma : `float`, optional Threshold amplitude of the image to set the "DETECTED" mask. badRegionBox : `lsst.geom.Box2I`, optional Add a bad region bounding box (set to "BAD"). """ if backgroundLevel is None: backgroundLevel = self.backgroundLevel if noiseLevel is None: noiseLevel = 5. visitInfo = self.makeDummyVisitInfo(expId, randomizeTime=True) if psfSize is None: psfSize = self.rngMods.random() * ( self.maxPsfSize - self.minPsfSize) + self.minPsfSize nSrc = len(self.flux) sigmas = [psfSize for src in range(nSrc)] sigmasPsfMatched = [self.maxPsfSize for src in range(nSrc)] coordList = list(zip(self.xLoc, self.yLoc, self.flux, sigmas)) coordListPsfMatched = list( zip(self.xLoc, self.yLoc, self.flux, sigmasPsfMatched)) xSize, ySize = self.bbox.getDimensions() model = plantSources(self.bbox, self.kernelSize, self.backgroundLevel, coordList, addPoissonNoise=False) modelPsfMatched = plantSources(self.bbox, self.kernelSize, self.backgroundLevel, coordListPsfMatched, addPoissonNoise=False) model.variance.array = np.abs(model.image.array) + noiseLevel modelPsfMatched.variance.array = np.abs( modelPsfMatched.image.array) + noiseLevel noise = self.rngData.random((ySize, xSize)) * noiseLevel noise -= np.median(noise) model.image.array += noise modelPsfMatched.image.array += noise detectedMask = afwImage.Mask.getPlaneBitMask("DETECTED") detectionThreshold = self.backgroundLevel + detectionSigma * noiseLevel model.mask.array[ model.image.array > detectionThreshold] += detectedMask if badRegionBox is not None: model.mask[badRegionBox] = afwImage.Mask.getPlaneBitMask("BAD") exposure = self.makeCoaddTempExp(model, visitInfo, expId) matchedExposure = self.makeCoaddTempExp(modelPsfMatched, visitInfo, expId) return exposure, matchedExposure
def testBasics(self): bbox = afwGeom.Box2I(afwGeom.Point2I(256, 100), afwGeom.Extent2I(128, 127)) minCounts = 2000 maxCounts = 20000 starSigma = 1.5 numX = 4 numY = 4 coordList = self.makeCoordList( bbox=bbox, numX=numX, numY=numY, minCounts=minCounts, maxCounts=maxCounts, sigma=starSigma, ) kwid = 11 sky = 2000 addPoissonNoise = True exposure = plantSources(bbox=bbox, kwid=kwid, sky=sky, coordList=coordList, addPoissonNoise=addPoissonNoise) if display: ds9.mtv(exposure) schema = afwTable.SourceTable.makeMinimalSchema() config = SourceDetectionTask.ConfigClass() config.reEstimateBackground = False config.thresholdPolarity = 'both' detection = SourceDetectionTask(config=config, schema=schema) algMetadata = dafBase.PropertyList() measurement = SourceMeasurementTask(schema=schema, algMetadata=algMetadata) table = afwTable.SourceTable.make(schema) detections = detection.makeSourceCatalog(table, exposure) sources = detections.sources fpSets = detections.fpSets self.assertEqual(len(sources), numX * numY) self.assertEqual(fpSets.numPos, numX * numY / 2) self.assertEqual(fpSets.numNeg, numX * numY / 2) measurement.run(sources, exposure) nGoodCent = 0 nGoodShape = 0 for s in sources: cent = s.getCentroid() shape = s.getShape() if cent[0] == cent[0] and cent[1] == cent[1]: nGoodCent += 1 if (shape.getIxx() == shape.getIxx() and shape.getIyy() == shape.getIyy() and shape.getIxy() == shape.getIxy()): nGoodShape += 1 if display: xy = cent[0] - exposure.getX0(), cent[1] - exposure.getY0() ds9.dot('+', *xy) ds9.dot(shape, *xy, ctype=ds9.RED) self.assertEqual(nGoodCent, numX * numY) self.assertEqual(nGoodShape, numX * numY)
def testBasics(self): bbox = lsst.geom.Box2I(lsst.geom.Point2I(256, 100), lsst.geom.Extent2I(128, 127)) minCounts = 2000 maxCounts = 20000 starSigma = 1.5 numX = 4 numY = 4 coordList = self.makeCoordList( bbox=bbox, numX=numX, numY=numY, minCounts=minCounts, maxCounts=maxCounts, sigma=starSigma, ) kwid = 11 sky = 2000 addPoissonNoise = True exposure = plantSources(bbox=bbox, kwid=kwid, sky=sky, coordList=coordList, addPoissonNoise=addPoissonNoise) if display: disp = afwDisplay.Display(frame=1) disp.mtv(exposure, title=self._testMethodName + ": image with -ve sources") schema = afwTable.SourceTable.makeMinimalSchema() config = SourceDetectionTask.ConfigClass() config.reEstimateBackground = False config.thresholdPolarity = 'both' detection = SourceDetectionTask(config=config, schema=schema) algMetadata = dafBase.PropertyList() measurement = SourceMeasurementTask(schema=schema, algMetadata=algMetadata) table = afwTable.SourceTable.make(schema) detections = detection.makeSourceCatalog(table, exposure) sources = detections.sources fpSets = detections.fpSets self.assertEqual(len(sources), numX*numY) self.assertEqual(fpSets.numPos, numX*numY/2) self.assertEqual(fpSets.numNeg, numX*numY/2) measurement.run(sources, exposure) nGoodCent = 0 nGoodShape = 0 for s in sources: cent = s.getCentroid() shape = s.getShape() if cent[0] == cent[0] and cent[1] == cent[1]: nGoodCent += 1 if (shape.getIxx() == shape.getIxx() and shape.getIyy() == shape.getIyy() and shape.getIxy() == shape.getIxy()): nGoodShape += 1 if display: xy = cent[0], cent[1] disp.dot('+', *xy) disp.dot(shape, *xy, ctype=afwDisplay.RED) self.assertEqual(nGoodCent, numX*numY) self.assertEqual(nGoodShape, numX*numY)
def makeTestImage( seed=5, nSrc=20, psfSize=2., noiseLevel=5., noiseSeed=6, fluxLevel=500., fluxRange=2., kernelSize=32, templateBorderSize=0, background=None, xSize=256, ySize=256, x0=12345, y0=67890, calibration=1., doApplyCalibration=False, ): """Make a reproduceable PSF-convolved exposure for testing. Parameters ---------- seed : `int`, optional Seed value to initialize the random number generator for sources. nSrc : `int`, optional Number of sources to simulate. psfSize : `float`, optional Width of the PSF of the simulated sources, in pixels. noiseLevel : `float`, optional Standard deviation of the noise to add to each pixel. noiseSeed : `int`, optional Seed value to initialize the random number generator for noise. fluxLevel : `float`, optional Reference flux of the simulated sources. fluxRange : `float`, optional Range in flux amplitude of the simulated sources. kernelSize : `int`, optional Size in pixels of the kernel for simulating sources. templateBorderSize : `int`, optional Size in pixels of the image border used to pad the image. background : `lsst.afw.math.Chebyshev1Function2D`, optional Optional background to add to the output image. xSize, ySize : `int`, optional Size in pixels of the simulated image. x0, y0 : `int`, optional Origin of the image. calibration : `float`, optional Conversion factor between instFlux and nJy. doApplyCalibration : `bool`, optional Apply the photometric calibration and return the image in nJy? Returns ------- modelExposure : `lsst.afw.image.Exposure` The model image, with the mask and variance planes. sourceCat : `lsst.afw.table.SourceCatalog` Catalog of sources detected on the model image. """ # Distance from the inner edge of the bounding box to avoid placing test # sources in the model images. bufferSize = kernelSize / 2 + templateBorderSize + 1 bbox = lsst.geom.Box2I(lsst.geom.Point2I(x0, y0), lsst.geom.Extent2I(xSize, ySize)) if templateBorderSize > 0: bbox.grow(templateBorderSize) rng = np.random.RandomState(seed) rngNoise = np.random.RandomState(noiseSeed) x0, y0 = bbox.getBegin() xSize, ySize = bbox.getDimensions() xLoc = rng.rand(nSrc) * (xSize - 2 * bufferSize) + bufferSize + x0 yLoc = rng.rand(nSrc) * (ySize - 2 * bufferSize) + bufferSize + y0 flux = (rng.rand(nSrc) * (fluxRange - 1.) + 1.) * fluxLevel sigmas = [psfSize for src in range(nSrc)] coordList = list(zip(xLoc, yLoc, flux, sigmas)) skyLevel = 0 # Don't use the built in poisson noise: it modifies the global state of numpy random modelExposure = plantSources(bbox, kernelSize, skyLevel, coordList, addPoissonNoise=False) modelExposure.setWcs(makeFakeWcs()) noise = rngNoise.randn(ySize, xSize) * noiseLevel noise -= np.mean(noise) modelExposure.variance.array = np.sqrt(np.abs( modelExposure.image.array)) + noiseLevel**2 modelExposure.image.array += noise # Run source detection to set up the mask plane psfMatchTask = lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask() sourceCat = psfMatchTask.getSelectSources(modelExposure) modelExposure.setPhotoCalib(PhotoCalib(calibration, 0., bbox)) if background is not None: modelExposure.image += background modelExposure.maskedImage /= calibration if doApplyCalibration: modelExposure.maskedImage = modelExposure.photoCalib.calibrateImage( modelExposure.maskedImage) return modelExposure, sourceCat
def makeTestImages(self, seed=5, nSrc=5, psfSize=2., noiseLevel=5., fluxLevel=500., fluxRange=2.): """Make reproduceable PSF-convolved masked images for testing. Parameters ---------- seed : `int`, optional Seed value to initialize the random number generator. nSrc : `int`, optional Number of sources to simulate. psfSize : `float`, optional Width of the PSF of the simulated sources, in pixels. noiseLevel : `float`, optional Standard deviation of the noise to add to each pixel. fluxLevel : `float`, optional Reference flux of the simulated sources. fluxRange : `float`, optional Range in flux amplitude of the simulated sources. Returns ------- modelImages : `lsst.afw.image.ExposureF` The model image, with the mask and variance planes. sourceCat : `lsst.afw.table.SourceCatalog` Catalog of sources detected on the model image. """ rng = np.random.RandomState(seed) x0, y0 = self.bbox.getBegin() xSize, ySize = self.bbox.getDimensions() xLoc = rng.rand(nSrc) * (xSize - 2 * self.bufferSize) + self.bufferSize + x0 yLoc = rng.rand(nSrc) * (ySize - 2 * self.bufferSize) + self.bufferSize + y0 flux = (rng.rand(nSrc) * (fluxRange - 1.) + 1.) * fluxLevel sigmas = [psfSize for src in range(nSrc)] coordList = list(zip(xLoc, yLoc, flux, sigmas)) kernelSize = int( xSize / 2) # Need a careful explanation of this kernel size choice skyLevel = 0 # Don't use the built in poisson noise: it modifies the global state of numpy random model = plantSources(self.bbox, kernelSize, skyLevel, coordList, addPoissonNoise=False) noise = rng.rand(ySize, xSize) * noiseLevel model.image.array += noise model.variance.array = (np.sqrt(np.abs(model.image.array)) + noiseLevel - np.mean(np.sqrt(np.abs(noise)))) # Run source detection to set up the mask plane psfMatchTask = ImagePsfMatchTask(config=ImagePsfMatchConfig()) sourceCat = psfMatchTask.getSelectSources(model) model.setWcs(self._makeWcs()) return model, sourceCat