def addCalibColumns(self, catalog, dataRef): """Add columns with local calibration evaluated at each centroid for backwards compatibility with old repos. This exists for the purpose of converting old src catalogs (which don't have the expected local calib columns) to Source Tables. Parameters ---------- catalog: `afwTable.SourceCatalog` catalog to which calib columns will be added dataRef: `lsst.daf.persistence.ButlerDataRef for fetching the calibs from disk. Returns ------- newCat: `afwTable.SourceCatalog` Source Catalog with requested local calib columns """ mapper = afwTable.SchemaMapper(catalog.schema) measureConfig = SingleFrameMeasurementTask.ConfigClass() measureConfig.doReplaceWithNoise = False # Just need the WCS or the PhotoCalib attached to an exposue exposure = dataRef.get('calexp_sub', bbox=lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Point2I(0, 0))) mapper = afwTable.SchemaMapper(catalog.schema) mapper.addMinimalSchema(catalog.schema, True) schema = mapper.getOutputSchema() exposureIdInfo = dataRef.get("expIdInfo") measureConfig.plugins.names = [] if self.config.doApplyExternalSkyWcs: plugin = 'base_LocalWcs' if plugin in schema: raise RuntimeError( f"{plugin} already in src catalog. Set doApplyExternalSkyWcs=False" ) else: measureConfig.plugins.names.add(plugin) if self.config.doApplyExternalPhotoCalib: plugin = 'base_LocalPhotoCalib' if plugin in schema: raise RuntimeError( f"{plugin} already in src catalog. Set doApplyExternalPhotoCalib=False" ) else: measureConfig.plugins.names.add(plugin) measurement = SingleFrameMeasurementTask(config=measureConfig, schema=schema) newCat = afwTable.SourceCatalog(schema) newCat.extend(catalog, mapper=mapper) measurement.run(measCat=newCat, exposure=exposure, exposureId=exposureIdInfo.expId) return newCat
def run(self, sources, exposure, **kwds): """!Run dipole measurement and classification @param sources diaSources that will be measured using dipole measurement @param exposure Exposure on which the diaSources were detected @param **kwds Sent to SingleFrameMeasurementTask """ SingleFrameMeasurementTask.run(self, sources, exposure, **kwds) self.classify(sources)
def run(display=False): exposure = loadData() schema = afwTable.SourceTable.makeMinimalSchema() # # Create the detection task # config = SourceDetectionTask.ConfigClass() config.thresholdPolarity = "both" config.background.isNanSafe = True config.thresholdValue = 3 detectionTask = SourceDetectionTask(config=config, schema=schema) # # And the measurement Task # config = SingleFrameMeasurementTask.ConfigClass() config.algorithms.names = ["base_SdssCentroid", "base_SdssShape", "base_CircularApertureFlux"] config.algorithms["base_CircularApertureFlux"].radii = [1, 2, 4, 8, 12, 16] # pixels config.slots.gaussianFlux = None config.slots.modelFlux = None config.slots.psfFlux = None algMetadata = dafBase.PropertyList() measureTask = SingleFrameMeasurementTask(schema, algMetadata=algMetadata, config=config) radii = algMetadata.getArray("base_CircularApertureFlux_radii") # # Create the output table # tab = afwTable.SourceTable.make(schema) # # Process the data # result = detectionTask.run(tab, exposure) sources = result.sources print("Found %d sources (%d +ve, %d -ve)" % (len(sources), result.fpSets.numPos, result.fpSets.numNeg)) measureTask.run(sources, exposure) if display: # display image (see also --debug argparse option) afwDisplay.setDefaultMaskTransparency(75) frame = 1 disp = afwDisplay.Display(frame=frame) disp.mtv(exposure) with disp.Buffering(): for s in sources: xy = s.getCentroid() disp.dot('+', *xy, ctype=afwDisplay.CYAN if s.get("flags_negative") else afwDisplay.GREEN) disp.dot(s.getShape(), *xy, ctype=afwDisplay.RED) for radius in radii: disp.dot('o', *xy, size=radius, ctype=afwDisplay.YELLOW)
def run(display=False): exposure = loadData() schema = afwTable.SourceTable.makeMinimalSchema() # # Create the detection task # config = SourceDetectionTask.ConfigClass() config.thresholdPolarity = "both" config.background.isNanSafe = True config.thresholdValue = 3 detectionTask = SourceDetectionTask(config=config, schema=schema) # # And the measurement Task # config = SingleFrameMeasurementTask.ConfigClass() config.plugins.names.clear() for plugin in ["base_SdssCentroid", "base_SdssShape", "base_CircularApertureFlux", "base_GaussianFlux"]: config.plugins.names.add(plugin) config.slots.psfFlux = None config.slots.apFlux = "base_CircularApertureFlux_3_0" measureTask = SingleFrameMeasurementTask(schema, config=config) # # Print the schema the configuration produced # print(schema) # # Create the output table # tab = afwTable.SourceTable.make(schema) # # Process the data # result = detectionTask.run(tab, exposure) sources = result.sources print("Found %d sources (%d +ve, %d -ve)" % (len(sources), result.fpSets.numPos, result.fpSets.numNeg)) measureTask.run(sources, exposure) if display: # display image (see also --debug argparse option) frame = 1 disp = afwDisplay.Display(frame=frame) disp.mtv(exposure) with disp.Buffering(): for s in sources: xy = s.getCentroid() disp.dot('+', *xy, ctype=afwDisplay.CYAN if s.get("flags_negative") else afwDisplay.GREEN) disp.dot(s.getShape(), *xy, ctype=afwDisplay.RED) disp.dot('o', *xy, size=config.plugins["base_CircularApertureFlux"].radii[0], ctype=afwDisplay.YELLOW)
def test_detection_stdev(self): """Test detection and measurement on an exposure with negative sources for thresholdType="stdev". """ exposure, numX, numY = self._create_exposure() 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.run(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 run(display=False): exposure = loadData() schema = afwTable.SourceTable.makeMinimalSchema() # # Create the detection task # config = SourceDetectionTask.ConfigClass() config.thresholdPolarity = "both" config.background.isNanSafe = True config.thresholdValue = 3 detectionTask = SourceDetectionTask(config=config, schema=schema) # # And the measurement Task # config = SingleFrameMeasurementTask.ConfigClass() config.algorithms.names = ["base_SdssCentroid", "base_SdssShape", "base_CircularApertureFlux"] config.algorithms["base_CircularApertureFlux"].radii = [1, 2, 4, 8, 16] # pixels config.slots.instFlux = None config.slots.modelFlux = None config.slots.psfFlux = None algMetadata = dafBase.PropertyList() measureTask = SingleFrameMeasurementTask(schema, algMetadata=algMetadata, config=config) radii = algMetadata.get("base_CircularApertureFlux_radii") # # Create the output table # tab = afwTable.SourceTable.make(schema) # # Process the data # result = detectionTask.run(tab, exposure) sources = result.sources print("Found %d sources (%d +ve, %d -ve)" % (len(sources), result.fpSets.numPos, result.fpSets.numNeg)) measureTask.run(sources, exposure) if display: # display on ds9 (see also --debug argparse option) frame = 1 ds9.mtv(exposure, frame=frame) with ds9.Buffering(): for s in sources: xy = s.getCentroid() ds9.dot('+', *xy, ctype=ds9.CYAN if s.get("flags.negative") else ds9.GREEN, frame=frame) ds9.dot(s.getShape(), *xy, ctype=ds9.RED, frame=frame) for i in range(s.get("flux.aperture.nProfile")): ds9.dot('o', *xy, size=radii[i], ctype=ds9.YELLOW, frame=frame)
def measure(footprintSet, exposure): """Measure a set of Footprints, returning a SourceCatalog""" config = SingleFrameMeasurementTask.ConfigClass() config.slots.apFlux = 'base_CircularApertureFlux_12_0' schema = afwTable.SourceTable.makeMinimalSchema() measureSources = SingleFrameMeasurementTask(schema,config=config) tab = afwTable.SourceTable.make(schema) catalog = afwTable.SourceCatalog(schema) if display: ds9.mtv(exposure, title="Original", frame=0) footprintSet.makeSources(catalog) measureSources.run(catalog,exposure) return catalog
def testInclude(self): schema = afwTable.SourceTable.makeMinimalSchema() # Create the detection task config = SourceDetectionTask.ConfigClass() config.reEstimateBackground = False # Turn off so that background does not change from orig detectionTask = SourceDetectionTask(config=config, schema=schema) # Create the deblender Task debConfig = measDeb.SourceDeblendConfig() debTask = measDeb.SourceDeblendTask(schema, config=debConfig) # Create the measurement Task config = SingleFrameMeasurementTask.ConfigClass() measureTask = SingleFrameMeasurementTask(schema, config=config) # Create the output table tab = afwTable.SourceTable.make(schema) # Process the data result = detectionTask.run(tab, self.calexp) sources = result.sources # Run the deblender debTask.run(self.calexp, sources) # Run the measurement task: this where the replace-with-noise occurs measureTask.run(sources, self.calexp) plotOnFailure = False if display: plotOnFailure = True # The relative differences ranged from 0.02 to ~2. This rtol is somewhat # random, but will certainly catch the pathology if it occurs. self.assertFloatsAlmostEqual( self.calexpOrig.getMaskedImage().getImage().getArray(), self.calexp.getMaskedImage().getImage().getArray(), rtol=1E-3, printFailures=False, plotOnFailure=plotOnFailure)
def testInclude(self): schema = afwTable.SourceTable.makeMinimalSchema() # Create the detection task config = SourceDetectionTask.ConfigClass() config.reEstimateBackground = False # Turn off so that background does not change from orig detectionTask = SourceDetectionTask(config=config, schema=schema) # Create the deblender Task debConfig = measDeb.SourceDeblendConfig() debTask = measDeb.SourceDeblendTask(schema, config=debConfig) # Create the measurement Task config = SingleFrameMeasurementTask.ConfigClass() measureTask = SingleFrameMeasurementTask(schema, config=config) # Create the output table tab = afwTable.SourceTable.make(schema) # Process the data result = detectionTask.run(tab, self.calexp) sources = result.sources # Run the deblender debTask.run(self.calexp, sources) # Run the measurement task: this where the replace-with-noise occurs measureTask.run(sources, self.calexp) plotOnFailure = False if display: plotOnFailure = True # The relative differences ranged from 0.02 to ~2. This rtol is somewhat # random, but will certainly catch the pathology if it occurs. self.assertFloatsAlmostEqual(self.calexpOrig.getMaskedImage().getImage().getArray(), self.calexp.getMaskedImage().getImage().getArray(), rtol=1E-3, printFailures=False, plotOnFailure=plotOnFailure)
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 getClumps(self, sigma=1.0, display=False): if self._num <= 0: raise RuntimeError("No candidate PSF sources") psfImage = self.getImage() # # Embed psfImage into a larger image so we can smooth when measuring it # width, height = psfImage.getWidth(), psfImage.getHeight() largeImg = psfImage.Factory(afwGeom.ExtentI(2 * width, 2 * height)) largeImg.set(0) bbox = afwGeom.BoxI(afwGeom.PointI(width, height), afwGeom.ExtentI(width, height)) largeImg.assign(psfImage, bbox, afwImage.LOCAL) # # Now measure that image, looking for the highest peak. Start by building an Exposure # msk = afwImage.MaskU(largeImg.getDimensions()) msk.set(0) var = afwImage.ImageF(largeImg.getDimensions()) var.set(1) mpsfImage = afwImage.MaskedImageF(largeImg, msk, var) mpsfImage.setXY0(afwGeom.PointI(-width, -height)) del msk del var exposure = afwImage.makeExposure(mpsfImage) # # Next run an object detector # maxVal = afwMath.makeStatistics(psfImage, afwMath.MAX).getValue() threshold = maxVal - sigma * math.sqrt(maxVal) if threshold <= 0.0: threshold = maxVal threshold = afwDetection.Threshold(threshold) ds = afwDetection.FootprintSet(mpsfImage, threshold, "DETECTED") # # And measure it. This policy isn't the one we use to measure # Sources, it's only used to characterize this PSF histogram # schema = SourceTable.makeMinimalSchema() psfImageConfig = SingleFrameMeasurementConfig() psfImageConfig.slots.centroid = "base_SdssCentroid" psfImageConfig.plugins["base_SdssCentroid"].doFootprintCheck = False psfImageConfig.slots.psfFlux = None # "base_PsfFlux" psfImageConfig.slots.apFlux = "base_CircularApertureFlux_3_0" psfImageConfig.slots.modelFlux = None psfImageConfig.slots.instFlux = None psfImageConfig.slots.calibFlux = None psfImageConfig.slots.shape = "base_SdssShape" # Formerly, this code had centroid.sdss, flux.psf, flux.naive, # flags.pixel, and shape.sdss psfImageConfig.algorithms.names = [ "base_SdssCentroid", "base_CircularApertureFlux", "base_SdssShape" ] psfImageConfig.algorithms["base_CircularApertureFlux"].radii = [3.0] psfImageConfig.validate() task = SingleFrameMeasurementTask(schema, config=psfImageConfig) sourceCat = SourceCatalog(schema) gaussianWidth = 1.5 # Gaussian sigma for detection convolution exposure.setPsf(algorithmsLib.DoubleGaussianPsf(11, 11, gaussianWidth)) ds.makeSources(sourceCat) # # Show us the Histogram # if display: frame = 1 dispImage = mpsfImage.Factory( mpsfImage, afwGeom.BoxI(afwGeom.PointI(width, height), afwGeom.ExtentI(width, height)), afwImage.LOCAL) ds9.mtv(dispImage, title="PSF Selection Image", frame=frame) clumps = list() # List of clumps, to return e = None # thrown exception IzzMin = 1.0 # Minimum value for second moments IzzMax = ( self._xSize / 8.0)**2 # Max value ... clump radius should be < clumpImgSize/8 apFluxes = [] task.run( sourceCat, exposure) # notes that this is backwards for the new framework for i, source in enumerate(sourceCat): if source.getCentroidFlag(): continue x, y = source.getX(), source.getY() apFluxes.append(source.getApFlux()) val = mpsfImage.getImage().get(int(x) + width, int(y) + height) psfClumpIxx = source.getIxx() psfClumpIxy = source.getIxy() psfClumpIyy = source.getIyy() if display: if i == 0: ds9.pan(x, y, frame=frame) ds9.dot("+", x, y, ctype=ds9.YELLOW, frame=frame) ds9.dot("@:%g,%g,%g" % (psfClumpIxx, psfClumpIxy, psfClumpIyy), x, y, ctype=ds9.YELLOW, frame=frame) if psfClumpIxx < IzzMin or psfClumpIyy < IzzMin: psfClumpIxx = max(psfClumpIxx, IzzMin) psfClumpIyy = max(psfClumpIyy, IzzMin) if display: ds9.dot("@:%g,%g,%g" % (psfClumpIxx, psfClumpIxy, psfClumpIyy), x, y, ctype=ds9.RED, frame=frame) det = psfClumpIxx * psfClumpIyy - psfClumpIxy * psfClumpIxy try: a, b, c = psfClumpIyy / det, -psfClumpIxy / det, psfClumpIxx / det except ZeroDivisionError: a, b, c = 1e4, 0, 1e4 clumps.append( Clump(peak=val, x=x, y=y, a=a, b=b, c=c, ixx=psfClumpIxx, ixy=psfClumpIxy, iyy=psfClumpIyy)) if len(clumps) == 0: msg = "Failed to determine center of PSF clump" if e: msg += ": %s" % e raise RuntimeError(msg) # if it's all we got return it if len(clumps) == 1: return clumps # which clump is the best? # if we've undistorted the moments, stars should only have 1 clump # use the apFlux from the clump measurement, and take the highest # ... this clump has more psf star candidate neighbours than the others. # get rid of any that are huge, and thus poorly defined goodClumps = [] for clump in clumps: if clump.ixx < IzzMax and clump.iyy < IzzMax: goodClumps.append(clump) # if culling > IzzMax cost us all clumps, we'll have to take what we have if len(goodClumps) == 0: goodClumps = clumps # use the 'brightest' clump iBestClump = numpy.argsort(apFluxes)[0] clumps = [clumps[iBestClump]] return clumps
def getClumps(self, sigma=1.0, display=False): if self._num <= 0: raise RuntimeError("No candidate PSF sources") psfImage = self.getImage() # # Embed psfImage into a larger image so we can smooth when measuring it # width, height = psfImage.getWidth(), psfImage.getHeight() largeImg = psfImage.Factory(afwGeom.ExtentI(2*width, 2*height)) largeImg.set(0) bbox = afwGeom.BoxI(afwGeom.PointI(width, height), afwGeom.ExtentI(width, height)) largeImg.assign(psfImage, bbox, afwImage.LOCAL) # # Now measure that image, looking for the highest peak. Start by building an Exposure # msk = afwImage.MaskU(largeImg.getDimensions()) msk.set(0) var = afwImage.ImageF(largeImg.getDimensions()) var.set(1) mpsfImage = afwImage.MaskedImageF(largeImg, msk, var) mpsfImage.setXY0(afwGeom.PointI(-width, -height)) del msk del var exposure = afwImage.makeExposure(mpsfImage) # # Next run an object detector # maxVal = afwMath.makeStatistics(psfImage, afwMath.MAX).getValue() threshold = maxVal - sigma*math.sqrt(maxVal) if threshold <= 0.0: threshold = maxVal threshold = afwDetection.Threshold(threshold) ds = afwDetection.FootprintSet(mpsfImage, threshold, "DETECTED") # # And measure it. This policy isn't the one we use to measure # Sources, it's only used to characterize this PSF histogram # schema = SourceTable.makeMinimalSchema() psfImageConfig = SingleFrameMeasurementConfig() psfImageConfig.doApplyApCorr = "no" psfImageConfig.slots.centroid = "base_SdssCentroid" psfImageConfig.slots.psfFlux = None #"base_PsfFlux" psfImageConfig.slots.apFlux = "base_CircularApertureFlux_3_0" psfImageConfig.slots.modelFlux = None psfImageConfig.slots.instFlux = None psfImageConfig.slots.calibFlux = None psfImageConfig.slots.shape = "base_SdssShape" # Formerly, this code had centroid.sdss, flux.psf, flux.naive, # flags.pixel, and shape.sdss psfImageConfig.algorithms.names = ["base_SdssCentroid", "base_CircularApertureFlux", "base_SdssShape"] psfImageConfig.algorithms["base_CircularApertureFlux"].radii = [3.0] psfImageConfig.validate() task = SingleFrameMeasurementTask(schema, config=psfImageConfig) sourceCat = SourceCatalog(schema) gaussianWidth = 1.5 # Gaussian sigma for detection convolution exposure.setPsf(algorithmsLib.DoubleGaussianPsf(11, 11, gaussianWidth)) ds.makeSources(sourceCat) # # Show us the Histogram # if display: frame = 1 dispImage = mpsfImage.Factory(mpsfImage, afwGeom.BoxI(afwGeom.PointI(width, height), afwGeom.ExtentI(width, height)), afwImage.LOCAL) ds9.mtv(dispImage,title="PSF Selection Image", frame=frame) clumps = list() # List of clumps, to return e = None # thrown exception IzzMin = 1.0 # Minimum value for second moments IzzMax = (self._xSize/8.0)**2 # Max value ... clump r < clumpImgSize/8 # diameter should be < 1/4 clumpImgSize apFluxes = [] task.run(exposure, sourceCat) # notes that this is backwards for the new framework for i, source in enumerate(sourceCat): if source.getCentroidFlag(): continue x, y = source.getX(), source.getY() apFluxes.append(source.getApFlux()) val = mpsfImage.getImage().get(int(x) + width, int(y) + height) psfClumpIxx = source.getIxx() psfClumpIxy = source.getIxy() psfClumpIyy = source.getIyy() if display: if i == 0: ds9.pan(x, y, frame=frame) ds9.dot("+", x, y, ctype=ds9.YELLOW, frame=frame) ds9.dot("@:%g,%g,%g" % (psfClumpIxx, psfClumpIxy, psfClumpIyy), x, y, ctype=ds9.YELLOW, frame=frame) if psfClumpIxx < IzzMin or psfClumpIyy < IzzMin: psfClumpIxx = max(psfClumpIxx, IzzMin) psfClumpIyy = max(psfClumpIyy, IzzMin) if display: ds9.dot("@:%g,%g,%g" % (psfClumpIxx, psfClumpIxy, psfClumpIyy), x, y, ctype=ds9.RED, frame=frame) det = psfClumpIxx*psfClumpIyy - psfClumpIxy*psfClumpIxy try: a, b, c = psfClumpIyy/det, -psfClumpIxy/det, psfClumpIxx/det except ZeroDivisionError: a, b, c = 1e4, 0, 1e4 clumps.append(Clump(peak=val, x=x, y=y, a=a, b=b, c=c, ixx=psfClumpIxx, ixy=psfClumpIxy, iyy=psfClumpIyy)) if len(clumps) == 0: msg = "Failed to determine center of PSF clump" if e: msg += ": %s" % e raise RuntimeError(msg) # if it's all we got return it if len(clumps) == 1: return clumps # which clump is the best? # if we've undistorted the moments, stars should only have 1 clump # use the apFlux from the clump measurement, and take the highest # ... this clump has more psf star candidate neighbours than the others. # get rid of any that are huge, and thus poorly defined goodClumps = [] for clump in clumps: if clump.ixx < IzzMax and clump.iyy < IzzMax: goodClumps.append(clump) # if culling > IzzMax cost us all clumps, we'll have to take what we have if len(goodClumps) == 0: goodClumps = clumps # use the 'brightest' clump iBestClump = numpy.argsort(apFluxes)[0] clumps = [clumps[iBestClump]] return clumps
def testIsScarletPrimaryFlag(self): """Test detect_isPrimary column when scarlet is used as the deblender """ # We need a multiband coadd for scarlet, # even though there is only one band coadds = afwImage.MultibandExposure.fromExposures(["test"], [self.exposure]) # Create a SkyMap with a tract that contains a portion of the image, # subdivided into 3x3 patches wcs = self.exposure.getWcs() tractBBox = Box2I(Point2I(100, 100), Extent2I(900, 900)) skyMap = MockSkyMap([tractBBox], wcs, 3) tractInfo = skyMap[0] patchInfo = tractInfo[0, 0] patchBBox = patchInfo.getInnerBBox() schema = SourceCatalog.Table.makeMinimalSchema() # Initialize the detection task detectionTask = SourceDetectionTask(schema=schema) # Initialize the fake source injection task skyConfig = SkyObjectsTask.ConfigClass() skySourcesTask = SkyObjectsTask(name="skySources", config=skyConfig) schema.addField("merge_peak_sky", type="Flag") # Initialize the deblender task scarletConfig = ScarletDeblendTask.ConfigClass() scarletConfig.maxIter = 20 scarletConfig.columnInheritance["merge_peak_sky"] = "merge_peak_sky" deblendTask = ScarletDeblendTask(schema=schema, config=scarletConfig) # We'll customize the configuration of measurement to just run the # minimal number of plugins to make setPrimaryFlags work. measureConfig = SingleFrameMeasurementTask.ConfigClass() measureConfig.plugins.names = ["base_SdssCentroid", "base_SkyCoord"] measureConfig.slots.psfFlux = None measureConfig.slots.apFlux = None measureConfig.slots.shape = None measureConfig.slots.modelFlux = None measureConfig.slots.calibFlux = None measureConfig.slots.gaussianFlux = None measureTask = SingleFrameMeasurementTask(config=measureConfig, schema=schema) primaryConfig = SetPrimaryFlagsTask.ConfigClass() setPrimaryTask = SetPrimaryFlagsTask(config=primaryConfig, schema=schema, name="setPrimaryFlags", isSingleFrame=False) table = SourceCatalog.Table.make(schema) # detect sources detectionResult = detectionTask.run(table, coadds["test"]) catalog = detectionResult.sources # add fake sources skySources = skySourcesTask.run(mask=self.exposure.mask, seed=0) for foot in skySources[:5]: src = catalog.addNew() src.setFootprint(foot) src.set("merge_peak_sky", True) # deblend result = deblendTask.run(coadds, catalog) # measure measureTask.run(result["test"], self.exposure) outputCat = result["test"] # Set the primary flags setPrimaryTask.run(outputCat, skyMap=skyMap, tractInfo=tractInfo, patchInfo=patchInfo) # There should be the same number of deblenedPrimary and # deblendedModelPrimary sources, # since they both have the same blended sources and only differ # over which model to use for the isolated sources. isPseudo = getPseudoSources(outputCat, primaryConfig.pseudoFilterList, schema, setPrimaryTask.log) self.assertEqual( np.sum(outputCat["detect_isDeblendedSource"] & ~isPseudo), np.sum(outputCat["detect_isDeblendedModelSource"])) # Check that the sources contained in a tract are all marked appropriately x = outputCat["slot_Centroid_x"] y = outputCat["slot_Centroid_y"] tractInner = tractBBox.contains(x, y) np.testing.assert_array_equal(outputCat["detect_isTractInner"], tractInner) # Check that the sources contained in a patch are all marked appropriately patchInner = patchBBox.contains(x, y) np.testing.assert_array_equal(outputCat["detect_isPatchInner"], patchInner) # make sure all sky sources are flagged as not primary self.assertEqual( sum((outputCat["detect_isPrimary"]) & (outputCat["merge_peak_sky"])), 0) # Check that sky objects have not been deblended np.testing.assert_array_equal( isPseudo, isPseudo & (outputCat["deblend_nChild"] == 0))
def run(display=False): exposure = loadData() schema = afwTable.SourceTable.makeMinimalSchema() # # Create the detection task # config = SourceDetectionTask.ConfigClass() config.thresholdPolarity = "both" config.background.isNanSafe = True config.thresholdValue = 3 detectionTask = SourceDetectionTask(config=config, schema=schema) # # And the measurement Task # config = SingleFrameMeasurementTask.ConfigClass() config.algorithms.names = [ "base_SdssCentroid", "base_SdssShape", "base_CircularApertureFlux" ] config.algorithms["base_CircularApertureFlux"].radii = [ 1, 2, 4, 8, 12, 16 ] # pixels config.slots.gaussianFlux = None config.slots.modelFlux = None config.slots.psfFlux = None algMetadata = dafBase.PropertyList() measureTask = SingleFrameMeasurementTask(schema, algMetadata=algMetadata, config=config) radii = algMetadata.getArray("BASE_CIRCULARAPERTUREFLUX_RADII") # # Create the output table # tab = afwTable.SourceTable.make(schema) # # Process the data # result = detectionTask.run(tab, exposure) sources = result.sources print("Found %d sources (%d +ve, %d -ve)" % (len(sources), result.fpSets.numPos, result.fpSets.numNeg)) measureTask.run(sources, exposure) if display: # display image (see also --debug argparse option) afwDisplay.setDefaultMaskTransparency(75) frame = 1 disp = afwDisplay.Display(frame=frame) disp.mtv(exposure) with disp.Buffering(): for s in sources: xy = s.getCentroid() disp.dot('+', *xy, ctype=afwDisplay.CYAN if s.get("flags_negative") else afwDisplay.GREEN) disp.dot(s.getShape(), *xy, ctype=afwDisplay.RED) for radius in radii: disp.dot('o', *xy, size=radius, ctype=afwDisplay.YELLOW)
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 run(display=False): exposure = loadData() schema = afwTable.SourceTable.makeMinimalSchema() # # Create the detection task # config = SourceDetectionTask.ConfigClass() config.thresholdPolarity = "both" config.background.isNanSafe = True config.thresholdValue = 3 detectionTask = SourceDetectionTask(config=config, schema=schema) # # And the measurement Task # config = SingleFrameMeasurementTask.ConfigClass() config.plugins.names.clear() for plugin in [ "base_SdssCentroid", "base_SdssShape", "base_CircularApertureFlux", "base_GaussianFlux" ]: config.plugins.names.add(plugin) config.slots.psfFlux = None config.slots.apFlux = "base_CircularApertureFlux_3_0" measureTask = SingleFrameMeasurementTask(schema, config=config) # # Print the schema the configuration produced # print schema # # Create the output table # tab = afwTable.SourceTable.make(schema) # # Process the data # result = detectionTask.run(tab, exposure) sources = result.sources print "Found %d sources (%d +ve, %d -ve)" % ( len(sources), result.fpSets.numPos, result.fpSets.numNeg) measureTask.run(sources, exposure) if display: # display on ds9 (see also --debug argparse option) frame = 1 ds9.mtv(exposure, frame=frame) with ds9.Buffering(): for s in sources: xy = s.getCentroid() ds9.dot( '+', *xy, ctype=ds9.CYAN if s.get("flags_negative") else ds9.GREEN, frame=frame) ds9.dot(s.getShape(), *xy, ctype=ds9.RED, frame=frame) ds9.dot( 'o', *xy, size=config.plugins["base_CircularApertureFlux"].radii[0], ctype=ds9.YELLOW, frame=frame)