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 setUp(self): config = SingleFrameMeasurementTask.ConfigClass() config.slots.apFlux = 'base_CircularApertureFlux_12_0' self.schema = afwTable.SourceTable.makeMinimalSchema() self.measureSources = SingleFrameMeasurementTask(self.schema, config=config) bbox = afwGeom.BoxI(afwGeom.PointI(0, 0), afwGeom.ExtentI(self.width, self.height)) self.cellSet = afwMath.SpatialCellSet(bbox, 100) self.footprintSet = afwDetection.FootprintSet( self.mi, afwDetection.Threshold(self.detectThresh), "DETECTED") self.catalog = self.measure(self.footprintSet, self.exposure) for source in self.catalog: try: cand = measAlg.makePsfCandidate(source, self.exposure) self.cellSet.insertCandidate(cand) except Exception as e: print(e) continue
def run(display=False): exposure = loadData() schema = afwTable.SourceTable.makeMinimalSchema() # # Create the detection and measurement Tasks # config = SourceDetectionTask.ConfigClass() config.reEstimateBackground = False detectionTask = SourceDetectionTask(config=config, schema=schema) config = SingleFrameMeasurementTask.ConfigClass() # Use the minimum set of plugins required. config.plugins.names.clear() for plugin in [ "base_SdssCentroid", "base_SdssShape", "base_CircularApertureFlux", "base_PixelFlags" ]: config.plugins.names.add(plugin) config.plugins["base_CircularApertureFlux"].radii = [7.0] # Use of the PSF flux is hardcoded in secondMomentStarSelector config.slots.psfFlux = "base_CircularApertureFlux_7_0" measureTask = SingleFrameMeasurementTask(schema, config=config) # # Create the measurePsf task # config = MeasurePsfTask.ConfigClass() psfDeterminer = config.psfDeterminer.apply() psfDeterminer.config.sizeCellX = 128 psfDeterminer.config.sizeCellY = 128 psfDeterminer.config.spatialOrder = 1 psfDeterminer.config.nEigenComponents = 3 measurePsfTask = MeasurePsfTask(config=config, schema=schema) # # Create the output table # tab = afwTable.SourceTable.make(schema) # # Process the data # sources = detectionTask.run(tab, exposure, sigma=2).sources measureTask.measure(exposure, sources) result = measurePsfTask.run(exposure, sources) psf = result.psf cellSet = result.cellSet 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, frame=frame) if s.get("calib.psf.candidate"): ds9.dot('x', *xy, ctype=ds9.YELLOW, frame=frame) if s.get("calib.psf.used"): ds9.dot('o', *xy, size=4, ctype=ds9.RED, frame=frame)
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 and measurement Tasks # config = SourceDetectionTask.ConfigClass() config.reEstimateBackground = False detectionTask = SourceDetectionTask(config=config, schema=schema) config = SingleFrameMeasurementTask.ConfigClass() # Use the minimum set of plugins required. config.plugins.names.clear() for plugin in ["base_SdssCentroid", "base_SdssShape", "base_CircularApertureFlux", "base_PixelFlags"]: config.plugins.names.add(plugin) config.plugins["base_CircularApertureFlux"].radii = [7.0] config.slots.psfFlux = "base_CircularApertureFlux_7_0" # Use of the PSF flux is hardcoded in secondMomentStarSelector measureTask = SingleFrameMeasurementTask(schema, config=config) # # Create the measurePsf task # config = MeasurePsfTask.ConfigClass() psfDeterminer = config.psfDeterminer.apply() psfDeterminer.config.sizeCellX = 128 psfDeterminer.config.sizeCellY = 128 psfDeterminer.config.spatialOrder = 1 psfDeterminer.config.nEigenComponents = 3 measurePsfTask = MeasurePsfTask(config=config, schema=schema) # # Create the output table # tab = afwTable.SourceTable.make(schema) # # Process the data # sources = detectionTask.run(tab, exposure, sigma=2).sources measureTask.measure(exposure, sources) result = measurePsfTask.run(exposure, sources) psf = result.psf cellSet = result.cellSet 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, frame=frame) if s.get("calib.psf.candidate"): ds9.dot('x', *xy, ctype=ds9.YELLOW, frame=frame) if s.get("calib.psf.used"): ds9.dot('o', *xy, size=4, ctype=ds9.RED, frame=frame)
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 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 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 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 setUp(self): # make a nominal match list where the distances are 0; test can then modify # source centroid, reference coord or distance field for each match, as desired self.wcs = afwGeom.makeSkyWcs(crpix=lsst.geom.Point2D(1500, 1500), crval=lsst.geom.SpherePoint(215.5, 53.0, lsst.geom.degrees), cdMatrix=afwGeom.makeCdMatrix(scale=5.1e-5*lsst.geom.degrees)) self.bboxD = lsst.geom.Box2D(lsst.geom.Point2D(10, 100), lsst.geom.Extent2D(1000, 1500)) self.numMatches = 25 sourceSchema = afwTable.SourceTable.makeMinimalSchema() # add centroid (and many other unwanted fields) to sourceSchema SingleFrameMeasurementTask(schema=sourceSchema) self.sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"]) self.sourceCat = afwTable.SourceCatalog(sourceSchema) refSchema = afwTable.SourceTable.makeMinimalSchema() self.refCoordKey = afwTable.CoordKey(refSchema["coord"]) self.refCat = afwTable.SourceCatalog(refSchema) self.matchList = [] np.random.seed(5) pixPointList = [lsst.geom.Point2D(pos) for pos in np.random.random_sample([self.numMatches, 2])*self.bboxD.getDimensions() + self.bboxD.getMin()] for pixPoint in pixPointList: src = self.sourceCat.addNew() src.set(self.sourceCentroidKey, pixPoint) ref = self.refCat.addNew() ref.set(self.refCoordKey, self.wcs.pixelToSky(pixPoint)) match = afwTable.ReferenceMatch(ref, src, 0) self.matchList.append(match)
def __init__(self, schema, algMetadata=None, **kwds): """!Create the Task, and add Task-specific fields to the provided measurement table schema. @param[in,out] schema Schema object for measurement fields; modified in-place. @param[in,out] algMetadata Passed to MeasureSources object to be filled with metadata by algorithms (e.g. radii for aperture photometry). @param **kwds Passed to Task.__init__. """ # NaiveDipoleCentroid_x/y and classification fields are not added by the algorithms # In the interim, better to add here than to require in client code # DM-3515 will provide for a more permanent solution if self._ClassificationFlag not in schema.getNames(): schema.addField(self._ClassificationFlag, "F", "probability of being a dipole") SingleFrameMeasurementTask.__init__(self, schema, algMetadata, **kwds) self.dipoleAnalysis = DipoleAnalysis()
def loadData(self, rangePix=3000, numPoints=25): """Load catalogs and make the match list This is a separate function so data can be reloaded if fitting more than once (each time a WCS is fit it may update the source catalog, reference catalog and match list) """ refSchema = LoadReferenceObjectsTask.makeMinimalSchema( filterNameList=["r"], addIsPhotometric=True, addCentroid=True) self.refCat = afwTable.SimpleCatalog(refSchema) srcSchema = afwTable.SourceTable.makeMinimalSchema() SingleFrameMeasurementTask(schema=srcSchema) self.srcCoordKey = afwTable.CoordKey(srcSchema["coord"]) self.srcCentroidKey = afwTable.Point2DKey(srcSchema["slot_Centroid"]) self.srcCentroidKey_xErr = srcSchema["slot_Centroid_xErr"].asKey() self.srcCentroidKey_yErr = srcSchema["slot_Centroid_yErr"].asKey() self.sourceCat = afwTable.SourceCatalog(srcSchema) self.matches = [] for i in np.linspace(0., rangePix, numPoints): for j in np.linspace(0., rangePix, numPoints): src = self.sourceCat.addNew() refObj = self.refCat.addNew() src.set(self.srcCentroidKey, lsst.geom.Point2D(i, j)) src.set(self.srcCentroidKey_xErr, 0.1) src.set(self.srcCentroidKey_yErr, 0.1) c = self.tanWcs.pixelToSky(i, j) refObj.setCoord(c) self.matches.append(self.MatchClass(refObj, src, 0.0))
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 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 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 setUp(self): # make a nominal match list where the distances are 0; test can then modify # source centroid, reference coord or distance field for each match, as desired ctrPix = afwGeom.Point2I(1500, 1500) metadata = PropertySet() metadata.set("RADECSYS", "FK5") metadata.set("EQUINOX", 2000.0) metadata.set("CTYPE1", "RA---TAN") metadata.set("CTYPE2", "DEC--TAN") metadata.set("CUNIT1", "deg") metadata.set("CUNIT2", "deg") metadata.set("CRVAL1", 215.5) metadata.set("CRVAL2", 53.0) metadata.set("CRPIX1", ctrPix[0] + 1) metadata.set("CRPIX2", ctrPix[1] + 1) metadata.set("CD1_1", 5.1e-05) metadata.set("CD1_2", 0.0) metadata.set("CD2_2", -5.1e-05) metadata.set("CD2_1", 0.0) self.wcs = afwImage.makeWcs(metadata) self.bboxD = afwGeom.Box2D(afwGeom.Point2D(10, 100), afwGeom.Extent2D(1000, 1500)) self.numMatches = 25 sourceSchema = afwTable.SourceTable.makeMinimalSchema() # add centroid (and many other unwanted fields) to sourceSchema SingleFrameMeasurementTask(schema=sourceSchema) self.sourceCentroidKey = afwTable.Point2DKey( sourceSchema["slot_Centroid"]) self.sourceCat = afwTable.SourceCatalog(sourceSchema) refSchema = afwTable.SourceTable.makeMinimalSchema() self.refCoordKey = afwTable.CoordKey(refSchema["coord"]) self.refCat = afwTable.SourceCatalog(refSchema) self.matchList = [] np.random.seed(5) pixPointList = [ afwGeom.Point2D(pos) for pos in np.random.random_sample([self.numMatches, 2]) * self.bboxD.getDimensions() + self.bboxD.getMin() ] for pixPoint in pixPointList: src = self.sourceCat.addNew() src.set(self.sourceCentroidKey, pixPoint) ref = self.refCat.addNew() ref.set(self.refCoordKey, self.wcs.pixelToSky(pixPoint)) match = afwTable.ReferenceMatch(ref, src, 0) self.matchList.append(match)
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 setUp(self): crval = IcrsCoord(afwGeom.PointD(44., 45.)) crpix = afwGeom.PointD(0, 0) arcsecPerPixel = 1 / 3600.0 CD11 = arcsecPerPixel CD12 = 0 CD21 = 0 CD22 = arcsecPerPixel self.tanWcs = makeWcs(crval, crpix, CD11, CD12, CD21, CD22) S = 300 N = 5 if self.MatchClass == afwTable.ReferenceMatch: refSchema = LoadReferenceObjectsTask.makeMinimalSchema( filterNameList=["r"], addFluxSigma=True, addIsPhotometric=True) self.refCat = afwTable.SimpleCatalog(refSchema) elif self.MatchClass == afwTable.SourceMatch: refSchema = afwTable.SourceTable.makeMinimalSchema() self.refCat = afwTable.SourceCatalog(refSchema) else: raise RuntimeError("Unsupported MatchClass=%r" % (self.MatchClass, )) srcSchema = afwTable.SourceTable.makeMinimalSchema() SingleFrameMeasurementTask(schema=srcSchema) self.refCoordKey = afwTable.CoordKey(refSchema["coord"]) self.srcCoordKey = afwTable.CoordKey(srcSchema["coord"]) self.srcCentroidKey = afwTable.Point2DKey(srcSchema["slot_Centroid"]) self.sourceCat = afwTable.SourceCatalog(srcSchema) self.origSourceCat = afwTable.SourceCatalog( srcSchema) # undistorted copy self.matches = [] for i in np.linspace(0., S, N): for j in np.linspace(0., S, N): src = self.sourceCat.addNew() refObj = self.refCat.addNew() src.set(self.srcCentroidKey, afwGeom.Point2D(i, j)) c = self.tanWcs.pixelToSky(afwGeom.Point2D(i, j)) refObj.setCoord(c) self.matches.append(self.MatchClass(refObj, src, 0.0))
def loadData(self, rangePix=3000, numPoints=25): """Load catalogs and make the match list This is a separate function so data can be reloaded if fitting more than once (each time a WCS is fit it may update the source catalog, reference catalog and match list) """ if self.MatchClass == afwTable.ReferenceMatch: refSchema = LoadReferenceObjectsTask.makeMinimalSchema( filterNameList=["r"], addIsPhotometric=True, addCentroid=True) self.refCat = afwTable.SimpleCatalog(refSchema) elif self.MatchClass == afwTable.SourceMatch: refSchema = afwTable.SourceTable.makeMinimalSchema() self.refCat = afwTable.SourceCatalog(refSchema) else: raise RuntimeError("Unsupported MatchClass=%r" % (self.MatchClass, )) srcSchema = afwTable.SourceTable.makeMinimalSchema() SingleFrameMeasurementTask(schema=srcSchema) self.srcCoordKey = afwTable.CoordKey(srcSchema["coord"]) self.srcCentroidKey = afwTable.Point2DKey(srcSchema["slot_Centroid"]) self.srcCentroidKey_xErr = srcSchema["slot_Centroid_xErr"].asKey() self.srcCentroidKey_yErr = srcSchema["slot_Centroid_yErr"].asKey() self.sourceCat = afwTable.SourceCatalog(srcSchema) self.matches = [] for i in np.linspace(0., rangePix, numPoints): for j in np.linspace(0., rangePix, numPoints): src = self.sourceCat.addNew() refObj = self.refCat.addNew() src.set(self.srcCentroidKey, lsst.geom.Point2D(i, j)) src.set(self.srcCentroidKey_xErr, 0.1) src.set(self.srcCentroidKey_yErr, 0.1) c = self.tanWcs.pixelToSky(i, j) refObj.setCoord(c) if False: print( "x,y = (%.1f, %.1f) pixels -- RA,Dec = (%.3f, %.3f) deg" % (i, j, c.toFk5().getRa().asDegrees(), c.toFk5().getDec().asDegrees())) self.matches.append(self.MatchClass(refObj, src, 0.0))
def setUp(self): crval = afwGeom.SpherePoint(44, 45, afwGeom.degrees) crpix = afwGeom.PointD(0, 0) scale = 1 * afwGeom.arcseconds self.tanWcs = afwGeom.makeSkyWcs( crpix=crpix, crval=crval, cdMatrix=afwGeom.makeCdMatrix(scale=scale)) S = 300 N = 5 if self.MatchClass == afwTable.ReferenceMatch: refSchema = LoadReferenceObjectsTask.makeMinimalSchema( filterNameList=["r"], addFluxSigma=True, addIsPhotometric=True) self.refCat = afwTable.SimpleCatalog(refSchema) elif self.MatchClass == afwTable.SourceMatch: refSchema = afwTable.SourceTable.makeMinimalSchema() self.refCat = afwTable.SourceCatalog(refSchema) else: raise RuntimeError("Unsupported MatchClass=%r" % (self.MatchClass, )) srcSchema = afwTable.SourceTable.makeMinimalSchema() SingleFrameMeasurementTask(schema=srcSchema) self.refCoordKey = afwTable.CoordKey(refSchema["coord"]) self.srcCoordKey = afwTable.CoordKey(srcSchema["coord"]) self.srcCentroidKey = afwTable.Point2DKey(srcSchema["slot_Centroid"]) self.sourceCat = afwTable.SourceCatalog(srcSchema) self.origSourceCat = afwTable.SourceCatalog( srcSchema) # undistorted copy self.matches = [] for i in np.linspace(0., S, N): for j in np.linspace(0., S, N): src = self.sourceCat.addNew() refObj = self.refCat.addNew() src.set(self.srcCentroidKey, afwGeom.Point2D(i, j)) c = self.tanWcs.pixelToSky(afwGeom.Point2D(i, j)) refObj.setCoord(c) self.matches.append(self.MatchClass(refObj, src, 0.0))
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 approximateWcs(wcs, bbox, camera=None, detector=None, obs_metadata=None, order=3, nx=20, ny=20, iterations=3, skyTolerance=0.001 * afwGeom.arcseconds, pixelTolerance=0.02, useTanWcs=False): """Approximate an existing WCS as a TAN-SIP WCS The fit is performed by evaluating the WCS at a uniform grid of points within a bounding box. @param[in] wcs wcs to approximate @param[in] bbox the region over which the WCS will be fit @param[in] camera is an instantiation of afw.cameraGeom.camera @param[in] detector is a detector from camera @param[in] obs_metadata is an ObservationMetaData characterizing the telescope pointing @param[in] order order of SIP fit @param[in] nx number of grid points along x @param[in] ny number of grid points along y @param[in] iterations number of times to iterate over fitting @param[in] skyTolerance maximum allowed difference in world coordinates between input wcs and approximate wcs (default is 0.001 arcsec) @param[in] pixelTolerance maximum allowed difference in pixel coordinates between input wcs and approximate wcs (default is 0.02 pixels) @param[in] useTanWcs send a TAN version of wcs to the fitter? It is documented to require that, but I don't think the fitter actually cares @return the fit TAN-SIP WCS """ if useTanWcs: crCoord = wcs.getSkyOrigin() crPix = wcs.getPixelOrigin() cdMat = wcs.getCDMatrix() tanWcs = afwImage.makeWcs(crCoord, crPix, cdMat[0, 0], cdMat[0, 1], cdMat[1, 0], cdMat[1, 1]) else: tanWcs = wcs # create a matchList consisting of a grid of points covering the bbox refSchema = afwTable.SimpleTable.makeMinimalSchema() refCoordKey = afwTable.CoordKey(refSchema["coord"]) refCat = afwTable.SimpleCatalog(refSchema) sourceSchema = afwTable.SourceTable.makeMinimalSchema() SingleFrameMeasurementTask(schema=sourceSchema) # expand the schema sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"]) sourceCat = afwTable.SourceCatalog(sourceSchema) # 20 March 2017 # the 'try' block is how it works in swig; # the 'except' block is how it works in pybind11 try: matchList = afwTable.ReferenceMatchVector() except AttributeError: matchList = [] bboxd = afwGeom.Box2D(bbox) for x in np.linspace(bboxd.getMinX(), bboxd.getMaxX(), nx): for y in np.linspace(bboxd.getMinY(), bboxd.getMaxY(), ny): pixelPos = afwGeom.Point2D(x, y) ra, dec = raDecFromPixelCoords(np.array([x]), np.array([y]), [detector.getName()], camera=camera, obs_metadata=obs_metadata, epoch=2000.0, includeDistortion=True) skyCoord = afwCoord.Coord(afwGeom.Point2D(ra[0], dec[0])) refObj = refCat.addNew() refObj.set(refCoordKey, skyCoord) source = sourceCat.addNew() source.set(sourceCentroidKey, pixelPos) matchList.append(afwTable.ReferenceMatch(refObj, source, 0.0)) # The TAN-SIP fitter is fitting x and y separately, so we have to iterate to make it converge for indx in range(iterations): sipObject = makeCreateWcsWithSip(matchList, tanWcs, order, bbox) tanWcs = sipObject.getNewWcs() fitWcs = sipObject.getNewWcs() return fitWcs
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(config, inputFiles, weightFiles=None, varianceFiles=None, returnCalibSources=False, displayResults=[], verbose=False): # # Create the tasks # schema = afwTable.SourceTable.makeMinimalSchema() algMetadata = dafBase.PropertyList() isrTask = IsrTask(config=config.isr) calibrateTask = CalibrateTask(config=config.calibrate) sourceDetectionTask = SourceDetectionTask(config=config.detection, schema=schema) if config.doDeblend: if SourceDeblendTask: sourceDeblendTask = SourceDeblendTask(config=config.deblend, schema=schema) else: print >> sys.stderr, "Failed to import lsst.meas.deblender; setting doDeblend = False" config.doDeblend = False sourceMeasurementTask = SingleFrameMeasurementTask(config=config.measurement, schema=schema, algMetadata=algMetadata) sourceMeasurementTask.config.doApplyApCorr = 'yes' # # Add fields needed to identify stars while calibrating # keysToCopy = [(schema.addField(afwTable.Field["Flag"]("calib_detected", "Source was detected by calibrate")), None)] for key in calibrateTask.getCalibKeys(): keysToCopy.append((schema.addField(calibrateTask.schema.find(key).field), key)) exposureDict = {}; calibSourcesDict = {}; sourcesDict = {} for inputFile, weightFile, varianceFile in zip(inputFiles, weightFiles, varianceFiles): # # Create the output table # tab = afwTable.SourceTable.make(schema) # # read the data # if verbose: print "Reading %s" % inputFile exposure = makeExposure(inputFile, weightFile, varianceFile, config.badPixelValue, config.variance) # if config.interpPlanes: import lsst.ip.isr as ipIsr defects = ipIsr.getDefectListFromMask(exposure.getMaskedImage(), config.interpPlanes, growFootprints=0) isrTask.run(exposure, defects=defects) # # process the data # if config.doCalibrate: result = calibrateTask.run(exposure) exposure, calibSources = result.exposure, result.sources else: calibSources = None if not exposure.getPsf(): calibrateTask.installInitialPsf(exposure) exposureDict[inputFile] = exposure calibSourcesDict[inputFile] = calibSources if returnCalibSources else None result = sourceDetectionTask.run(tab, exposure) sources = result.sources sourcesDict[inputFile] = sources if config.doDeblend: sourceDeblendTask.run(exposure, sources, exposure.getPsf()) sourceMeasurementTask.measure(exposure, sources) if verbose: print "Detected %d objects" % len(sources) propagateCalibFlags(keysToCopy, calibSources, sources) if displayResults: # display results of processing (see also --debug argparse option) showApertures = "showApertures".upper() in displayResults showShapes = "showShapes".upper() in displayResults display = afwDisplay.getDisplay(frame=1) if algMetadata.exists("base_CircularApertureFlux_radii"): radii = algMetadata.get("base_CircularApertureFlux_radii") else: radii = [] display.mtv(exposure, title=os.path.split(inputFile)[1]) with display.Buffering(): for s in sources: xy = s.getCentroid() display.dot('+', *xy, ctype=afwDisplay.CYAN if s.get("flags_negative") else afwDisplay.GREEN) if showShapes: display.dot(s.getShape(), *xy, ctype=afwDisplay.RED) if showApertures: for radius in radii: display.dot('o', *xy, size=radius, ctype=afwDisplay.YELLOW) return exposureDict, calibSourcesDict, sourcesDict
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)
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(config, inputFiles, weightFiles=None, varianceFiles=None, returnCalibSources=False, displayResults=[], verbose=False): # # Create the tasks # schema = afwTable.SourceTable.makeMinimalSchema() algMetadata = dafBase.PropertyList() isrTask = IsrTask(config=config.isr) charImageTask = CharacterizeImageTask(None, config=config.charImage) sourceDetectionTask = SourceDetectionTask(config=config.detection, schema=schema) if config.doDeblend: if SourceDeblendTask: sourceDeblendTask = SourceDeblendTask(config=config.deblend, schema=schema) else: print >> sys.stderr, "Failed to import lsst.meas.deblender; setting doDeblend = False" config.doDeblend = False sourceMeasurementTask = SingleFrameMeasurementTask( schema=schema, config=config.measurement, algMetadata=algMetadata) keysToCopy = [] for key in [ charImageTask.measurePsf.reservedKey, charImageTask.measurePsf.usedKey, ]: keysToCopy.append( (schema.addField(charImageTask.schema.find(key).field), key)) exposureDict = {} calibSourcesDict = {} sourcesDict = {} for inputFile, weightFile, varianceFile in zip(inputFiles, weightFiles, varianceFiles): # # Create the output table # tab = afwTable.SourceTable.make(schema) # # read the data # if verbose: print "Reading %s" % inputFile exposure = makeExposure(inputFile, weightFile, varianceFile, config.badPixelValue, config.variance) # if config.interpPlanes: import lsst.ip.isr as ipIsr defects = ipIsr.getDefectListFromMask(exposure.getMaskedImage(), config.interpPlanes, growFootprints=0) isrTask.run(exposure, defects=defects) # # process the data # if config.doCalibrate: result = charImageTask.characterize(exposure) exposure, calibSources = result.exposure, result.sourceCat else: calibSources = None if not exposure.getPsf(): charImageTask.installSimplePsf.run(exposure) exposureDict[inputFile] = exposure calibSourcesDict[ inputFile] = calibSources if returnCalibSources else None result = sourceDetectionTask.run(tab, exposure) sources = result.sources sourcesDict[inputFile] = sources if config.doDeblend: sourceDeblendTask.run(exposure, sources) sourceMeasurementTask.measure(exposure, sources) if verbose: print "Detected %d objects" % len(sources) propagatePsfFlags(keysToCopy, calibSources, sources) if displayResults: # display results of processing (see also --debug argparse option) showApertures = "showApertures".upper() in displayResults showPSFs = "showPSFs".upper() in displayResults showShapes = "showShapes".upper() in displayResults display = afwDisplay.getDisplay(frame=1) if algMetadata.exists("base_CircularApertureFlux_radii"): radii = algMetadata.get("base_CircularApertureFlux_radii") else: radii = [] display.mtv(exposure, title=os.path.split(inputFile)[1]) with display.Buffering(): for s in sources: xy = s.getCentroid() display.dot('+', *xy, ctype=afwDisplay.CYAN if s.get("flags_negative") else afwDisplay.GREEN) if showPSFs and (s.get("calib_psfUsed") or s.get("calib_psfReserved")): display.dot( 'o', *xy, size=10, ctype=afwDisplay.GREEN if s.get("calib_psfUsed") else afwDisplay.YELLOW) if showShapes: display.dot(s.getShape(), *xy, ctype=afwDisplay.RED) if showApertures: for radius in radii: display.dot('o', *xy, size=radius, ctype=afwDisplay.YELLOW) return exposureDict, calibSourcesDict, sourcesDict
def setUp(self): config = SingleFrameMeasurementTask.ConfigClass() config.slots.apFlux = 'base_CircularApertureFlux_12_0' self.schema = afwTable.SourceTable.makeMinimalSchema() self.measureSources = SingleFrameMeasurementTask(self.schema, config=config) width, height = 110, 301 self.mi = afwImage.MaskedImageF(afwGeom.ExtentI(width, height)) self.mi.set(0) sd = 3 # standard deviation of image self.mi.getVariance().set(sd * sd) self.mi.getMask().addMaskPlane("DETECTED") self.ksize = 31 # size of desired kernel sigma1 = 1.75 sigma2 = 2 * sigma1 self.exposure = afwImage.makeExposure(self.mi) self.exposure.setPsf( measAlg.DoubleGaussianPsf(self.ksize, self.ksize, 1.5 * sigma1, 1, 0.1)) cdMatrix = np.array([1.0, 0.0, 0.0, 1.0]) cdMatrix.shape = (2, 2) wcs = afwGeom.makeSkyWcs(crpix=afwGeom.PointD(0, 0), crval=afwGeom.SpherePoint( 0.0, 0.0, afwGeom.degrees), cdMatrix=cdMatrix) self.exposure.setWcs(wcs) # # Make a kernel with the exactly correct basis functions. Useful for debugging # basisKernelList = [] for sigma in (sigma1, sigma2): basisKernel = afwMath.AnalyticKernel( self.ksize, self.ksize, afwMath.GaussianFunction2D(sigma, sigma)) basisImage = afwImage.ImageD(basisKernel.getDimensions()) basisKernel.computeImage(basisImage, True) basisImage /= np.sum(basisImage.getArray()) if sigma == sigma1: basisImage0 = basisImage else: basisImage -= basisImage0 basisKernelList.append(afwMath.FixedKernel(basisImage)) order = 1 # 1 => up to linear spFunc = afwMath.PolynomialFunction2D(order) exactKernel = afwMath.LinearCombinationKernel(basisKernelList, spFunc) exactKernel.setSpatialParameters([[1.0, 0, 0], [0.0, 0.5 * 1e-2, 0.2e-2]]) rand = afwMath.Random() # make these tests repeatable by setting seed addNoise = True if addNoise: im = self.mi.getImage() afwMath.randomGaussianImage(im, rand) # N(0, 1) im *= sd # N(0, sd^2) del im xarr, yarr = [], [] for x, y in [ (20, 20), (60, 20), (30, 35), (50, 50), (20, 90), (70, 160), (25, 265), (75, 275), (85, 30), (50, 120), (70, 80), (60, 210), (20, 210), ]: xarr.append(x) yarr.append(y) for x, y in zip(xarr, yarr): dx = rand.uniform() - 0.5 # random (centered) offsets dy = rand.uniform() - 0.5 k = exactKernel.getSpatialFunction(1)( x, y) # functional variation of Kernel ... b = (k * sigma1**2 / ((1 - k) * sigma2**2) ) # ... converted double Gaussian's "b" #flux = 80000 - 20*x - 10*(y/float(height))**2 flux = 80000 * (1 + 0.1 * (rand.uniform() - 0.5)) I0 = flux * (1 + b) / (2 * np.pi * (sigma1**2 + b * sigma2**2)) for iy in range(y - self.ksize // 2, y + self.ksize // 2 + 1): if iy < 0 or iy >= self.mi.getHeight(): continue for ix in range(x - self.ksize // 2, x + self.ksize // 2 + 1): if ix < 0 or ix >= self.mi.getWidth(): continue I = I0 * psfVal(ix, iy, x + dx, y + dy, sigma1, sigma2, b) Isample = rand.poisson(I) if addNoise else I self.mi.getImage().set( ix, iy, self.mi.getImage().get(ix, iy) + Isample) self.mi.getVariance().set( ix, iy, self.mi.getVariance().get(ix, iy) + I) bbox = afwGeom.BoxI(afwGeom.PointI(0, 0), afwGeom.ExtentI(width, height)) self.cellSet = afwMath.SpatialCellSet(bbox, 100) self.footprintSet = afwDetection.FootprintSet( self.mi, afwDetection.Threshold(100), "DETECTED") self.catalog = self.measure(self.footprintSet, self.exposure) for source in self.catalog: try: cand = measAlg.makePsfCandidate(source, self.exposure) self.cellSet.insertCandidate(cand) except Exception as e: print(e) continue
def approximateWcs(wcs, camera_wrapper=None, detector_name=None, obs_metadata=None, order=3, nx=20, ny=20, iterations=3, skyTolerance=0.001 * afwGeom.arcseconds, pixelTolerance=0.02): """Approximate an existing WCS as a TAN-SIP WCS The fit is performed by evaluating the WCS at a uniform grid of points within a bounding box. @param[in] wcs wcs to approximate @param[in] camera_wrapper is an instantiation of GalSimCameraWrapper @param[in] detector_name is the name of the detector @param[in] obs_metadata is an ObservationMetaData characterizing the telescope pointing @param[in] order order of SIP fit @param[in] nx number of grid points along x @param[in] ny number of grid points along y @param[in] iterations number of times to iterate over fitting @param[in] skyTolerance maximum allowed difference in world coordinates between input wcs and approximate wcs (default is 0.001 arcsec) @param[in] pixelTolerance maximum allowed difference in pixel coordinates between input wcs and approximate wcs (default is 0.02 pixels) @return the fit TAN-SIP WCS """ tanWcs = wcs # create a matchList consisting of a grid of points covering the bbox refSchema = afwTable.SimpleTable.makeMinimalSchema() refCoordKey = afwTable.CoordKey(refSchema["coord"]) refCat = afwTable.SimpleCatalog(refSchema) sourceSchema = afwTable.SourceTable.makeMinimalSchema() SingleFrameMeasurementTask(schema=sourceSchema) # expand the schema sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"]) sourceCat = afwTable.SourceCatalog(sourceSchema) # 20 March 2017 # the 'try' block is how it works in swig; # the 'except' block is how it works in pybind11 try: matchList = afwTable.ReferenceMatchVector() except AttributeError: matchList = [] bbox = camera_wrapper.getBBox(detector_name) bboxd = afwGeom.Box2D(bbox) for x in np.linspace(bboxd.getMinX(), bboxd.getMaxX(), nx): for y in np.linspace(bboxd.getMinY(), bboxd.getMaxY(), ny): pixelPos = afwGeom.Point2D(x, y) ra, dec = camera_wrapper.raDecFromPixelCoords( np.array([x]), np.array([y]), detector_name, obs_metadata=obs_metadata, epoch=2000.0, includeDistortion=True) skyCoord = afwGeom.SpherePoint(ra[0], dec[0], LsstGeom.degrees) refObj = refCat.addNew() refObj.set(refCoordKey, skyCoord) source = sourceCat.addNew() source.set(sourceCentroidKey, pixelPos) matchList.append(afwTable.ReferenceMatch(refObj, source, 0.0)) # The TAN-SIP fitter is fitting x and y separately, so we have to iterate to make it converge for indx in range(iterations): sipObject = makeCreateWcsWithSip(matchList, tanWcs, order, bbox) tanWcs = sipObject.getNewWcs() fitWcs = sipObject.getNewWcs() return fitWcs
def run(config, inputFiles, weightFiles=None, varianceFiles=None, returnCalibSources=False, display=False, verbose=False): # # Create the tasks # schema = afwTable.SourceTable.makeMinimalSchema() algMetadata = dafBase.PropertyList() calibrateTask = CalibrateTask(config=config.calibrate) sourceDetectionTask = SourceDetectionTask(config=config.detection, schema=schema) if config.doDeblend: if SourceDeblendTask: sourceDeblendTask = SourceDeblendTask(config=config.deblend, schema=schema) else: print >> sys.stderr, "Failed to import lsst.meas.deblender; setting doDeblend = False" config.doDeblend = False sourceMeasurementTask = SingleFrameMeasurementTask(config=config.measurement, schema=schema, algMetadata=algMetadata) exposureDict = {}; calibSourcesDict = {}; sourcesDict = {} for inputFile, weightFile, varianceFile in zip(inputFiles, weightFiles, varianceFiles): # # Create the output table # tab = afwTable.SourceTable.make(schema) # # read the data # if verbose: print "Reading %s" % inputFile exposure = makeExposure(inputFile, weightFile, varianceFile, config.badPixelValue, config.variance, verbose) # # process the data # calibSources = None # sources used to calibrate the frame (photom, astrom, psf) if config.doCalibrate: result = calibrateTask.run(exposure) exposure, sources = result.exposure, result.sources if returnCalibSources: calibSources = sources else: if not exposure.getPsf(): calibrateTask.installInitialPsf(exposure) if config.edgeRolloff.applyModel: if verbose: print "Adding edge rolloff distortion to the exposure WCS" addEdgeRolloffDistortion(exposure, config.edgeRolloff) exposureDict[inputFile] = exposure calibSourcesDict[inputFile] = calibSources result = sourceDetectionTask.run(tab, exposure) sources = result.sources sourcesDict[inputFile] = sources if config.doDeblend: sourceDeblendTask.run(exposure, sources, exposure.getPsf()) sourceMeasurementTask.measure(exposure, sources) if verbose: print "Detected %d objects" % len(sources) if display: # display on ds9 (see also --debug argparse option) if algMetadata.exists("base_CircularApertureFlux_radii"): radii = algMetadata.get("base_CircularApertureFlux_radii") else: radii = None frame = 1 ds9.mtv(exposure, title=os.path.split(inputFile)[1], 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) if radii: for radius in radii: ds9.dot('o', *xy, size=radius, ctype=ds9.YELLOW, frame=frame) return exposureDict, calibSourcesDict, sourcesDict
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 approximateWcs(wcs, bbox, order=3, nx=20, ny=20, iterations=3, skyTolerance=0.001 * afwGeom.arcseconds, pixelTolerance=0.02, useTanWcs=False): """Approximate an existing WCS as a TAN-SIP WCS The fit is performed by evaluating the WCS at a uniform grid of points within a bounding box. @param[in] wcs wcs to approximate @param[in] bbox the region over which the WCS will be fit @param[in] order order of SIP fit @param[in] nx number of grid points along x @param[in] ny number of grid points along y @param[in] iterations number of times to iterate over fitting @param[in] skyTolerance maximum allowed difference in world coordinates between input wcs and approximate wcs (default is 0.001 arcsec) @param[in] pixelTolerance maximum allowed difference in pixel coordinates between input wcs and approximate wcs (default is 0.02 pixels) @param[in] useTanWcs send a TAN version of wcs to the fitter? It is documented to require that, but I don't think the fitter actually cares @return the fit TAN-SIP WCS """ if useTanWcs: crCoord = wcs.getSkyOrigin() crPix = wcs.getPixelOrigin() cdMat = wcs.getCDMatrix() tanWcs = afwImage.makeWcs(crCoord, crPix, cdMat[0, 0], cdMat[0, 1], cdMat[1, 0], cdMat[1, 1]) else: tanWcs = wcs # create a matchList consisting of a grid of points covering the bbox refSchema = afwTable.SimpleTable.makeMinimalSchema() refCoordKey = afwTable.CoordKey(refSchema["coord"]) refCat = afwTable.SimpleCatalog(refSchema) sourceSchema = afwTable.SourceTable.makeMinimalSchema() SingleFrameMeasurementTask(schema=sourceSchema) # expand the schema sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"]) sourceCat = afwTable.SourceCatalog(sourceSchema) matchList = [] bboxd = afwGeom.Box2D(bbox) for x in np.linspace(bboxd.getMinX(), bboxd.getMaxX(), nx): for y in np.linspace(bboxd.getMinY(), bboxd.getMaxY(), ny): pixelPos = afwGeom.Point2D(x, y) skyCoord = wcs.pixelToSky(pixelPos) refObj = refCat.addNew() refObj.set(refCoordKey, skyCoord) source = sourceCat.addNew() source.set(sourceCentroidKey, pixelPos) matchList.append(afwTable.ReferenceMatch(refObj, source, 0.0)) # The TAN-SIP fitter is fitting x and y separately, so we have to iterate to make it converge for indx in range(iterations): sipObject = makeCreateWcsWithSip(matchList, tanWcs, order, bbox) tanWcs = sipObject.getNewWcs() fitWcs = sipObject.getNewWcs() mockTest = _MockTestCase() assertWcsAlmostEqualOverBBox(mockTest, wcs, fitWcs, bbox, maxDiffSky=skyTolerance, maxDiffPix=pixelTolerance) return fitWcs
def detect_and_deblend(*, exp, log): log = lsst.log.Log.getLogger("LSSTMEDSifier") thresh = 5.0 loglevel = 'INFO' # This schema holds all the measurements that will be run within the # stack It needs to be constructed before running anything and passed # to algorithms that make additional measurents. schema = afw_table.SourceTable.makeMinimalSchema() # Setup algorithms to run meas_config = SingleFrameMeasurementConfig() meas_config.plugins.names = [ "base_SdssCentroid", "base_PsfFlux", "base_SkyCoord", # "modelfit_ShapeletPsfApprox", "modelfit_DoubleShapeletPsfApprox", "modelfit_CModel", # "base_SdssShape", # "base_LocalBackground", ] # set these slots to none because we aren't running these algorithms meas_config.slots.apFlux = None meas_config.slots.gaussianFlux = None meas_config.slots.calibFlux = None meas_config.slots.modelFlux = None # goes with SdssShape above meas_config.slots.shape = None # fix odd issue where it things things are near the edge meas_config.plugins['base_SdssCentroid'].binmax = 1 meas_task = SingleFrameMeasurementTask( config=meas_config, schema=schema, ) # setup detection config detection_config = SourceDetectionConfig() detection_config.reEstimateBackground = False detection_config.thresholdValue = thresh detection_task = SourceDetectionTask(config=detection_config) detection_task.log.setLevel(getattr(lsst.log, loglevel)) deblend_config = SourceDeblendConfig() deblend_task = SourceDeblendTask(config=deblend_config, schema=schema) deblend_task.log.setLevel(getattr(lsst.log, loglevel)) # Detect objects table = afw_table.SourceTable.make(schema) result = detection_task.run(table, exp) sources = result.sources # run the deblender deblend_task.run(exp, sources) # Run on deblended images noise_replacer_config = NoiseReplacerConfig() footprints = { record.getId(): (record.getParent(), record.getFootprint()) for record in result.sources } # This constructor will replace all detected pixels with noise in the # image replacer = NoiseReplacer( noise_replacer_config, exposure=exp, footprints=footprints, ) nbad = 0 ntry = 0 kept_sources = [] for record in result.sources: # Skip parent objects where all children are inserted if record.get('deblend_nChild') != 0: continue ntry += 1 # This will insert a single source into the image replacer.insertSource(record.getId()) # Get the peak as before # peak = record.getFootprint().getPeaks()[0] # The bounding box will be for the parent object # bbox = record.getFootprint().getBBox() meas_task.callMeasure(record, exp) # Remove object replacer.removeSource(record.getId()) if record.getCentroidFlag(): nbad += 1 kept_sources.append(record) # Insert all objects back into image replacer.end() if ntry > 0: log.debug('nbad center: %d frac: %d' % (nbad, nbad / ntry)) nkeep = len(kept_sources) ntot = len(result.sources) log.debug('kept %d/%d non parents' % (nkeep, ntot)) return kept_sources