Пример #1
0
    def setUp(self):
        # Load sample input from disk
        testDir = os.path.dirname(__file__)

        self.srcSet = SourceCatalog.readFits(os.path.join(testDir, "v695833-e0-c000.xy.fits"))

        self.bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(2048, 4612))  # approximate
        # create an exposure with the right metadata; the closest thing we have is
        # apparently v695833-e0-c000-a00.sci.fits, which is much too small
        smallExposure = ExposureF(os.path.join(testDir, "v695833-e0-c000-a00.sci.fits"))
        self.exposure = ExposureF(self.bbox)
        self.exposure.setWcs(smallExposure.getWcs())
        self.exposure.setFilter(smallExposure.getFilter())
        # copy the pixels we can, in case the user wants a debug display
        mi = self.exposure.getMaskedImage()
        mi.assign(smallExposure.getMaskedImage(), smallExposure.getBBox())

        logLevel = Log.INFO
        refCatDir = os.path.join(testDir, "data", "sdssrefcat")
        butler = Butler(refCatDir)
        refObjLoader = LoadIndexedReferenceObjectsTask(butler=butler)
        astrometryConfig = AstrometryTask.ConfigClass()
        self.astrom = AstrometryTask(config=astrometryConfig, refObjLoader=refObjLoader)
        self.astrom.log.setLevel(logLevel)
        # Since our sourceSelector is a registry object we have to wait for it to be created
        # before setting default values.
        self.astrom.matcher.sourceSelector.config.minSnr = 0
Пример #2
0
    def testUsedFlag(self):
        """Test that the solver will record number of sources used to table
           if it is passed a schema on initialization.
        """
        self.exposure.setWcs(self.tanWcs)
        config = AstrometryTask.ConfigClass()
        config.wcsFitter.order = 2
        config.wcsFitter.numRejIter = 0

        sourceSchema = afwTable.SourceTable.makeMinimalSchema()
        measBase.SingleFrameMeasurementTask(
            schema=sourceSchema)  # expand the schema
        # schema must be passed to the solver task constructor
        solver = AstrometryTask(config=config,
                                refObjLoader=self.refObjLoader,
                                schema=sourceSchema)
        sourceCat = self.makeSourceCat(self.tanWcs, sourceSchema=sourceSchema)

        results = solver.run(
            sourceCat=sourceCat,
            exposure=self.exposure,
        )
        # check that the used flag is set the right number of times
        count = 0
        for source in sourceCat:
            if source.get('calib_astrometry_used'):
                count += 1
        self.assertEqual(count, len(results.matches))
    def doTest(self, pixelsToTanPixels, order=3):
        """Test using pixelsToTanPixels to distort the source positions
        """
        distortedWcs = afwGeom.makeModifiedWcs(pixelTransform=pixelsToTanPixels, wcs=self.tanWcs,
                                               modifyActualPixels=False)
        self.exposure.setWcs(distortedWcs)
        sourceCat = self.makeSourceCat(distortedWcs)
        config = AstrometryTask.ConfigClass()
        config.wcsFitter.order = order
        config.wcsFitter.numRejIter = 0
        solver = AstrometryTask(config=config, refObjLoader=self.refObjLoader)
        results = solver.run(
            sourceCat=sourceCat,
            exposure=self.exposure,
        )
        fitWcs = self.exposure.getWcs()
        self.assertRaises(Exception, self.assertWcsAlmostEqualOverBBox, fitWcs, distortedWcs)
        self.assertWcsAlmostEqualOverBBox(distortedWcs, fitWcs, self.bbox,
                                          maxDiffSky=0.01*afwGeom.arcseconds, maxDiffPix=0.02)

        srcCoordKey = afwTable.CoordKey(sourceCat.schema["coord"])
        refCoordKey = afwTable.CoordKey(results.refCat.schema["coord"])
        refCentroidKey = afwTable.Point2DKey(results.refCat.schema["centroid"])
        maxAngSep = afwGeom.Angle(0)
        maxPixSep = 0
        for refObj, src, d in results.matches:
            refCoord = refObj.get(refCoordKey)
            refPixPos = refObj.get(refCentroidKey)
            srcCoord = src.get(srcCoordKey)
            srcPixPos = src.getCentroid()

            angSep = refCoord.separation(srcCoord)
            maxAngSep = max(maxAngSep, angSep)

            pixSep = math.hypot(*(srcPixPos-refPixPos))
            maxPixSep = max(maxPixSep, pixSep)
        print("max angular separation = %0.4f arcsec" % (maxAngSep.asArcseconds(),))
        print("max pixel separation = %0.3f" % (maxPixSep,))
        self.assertLess(maxAngSep.asArcseconds(), 0.0026)
        self.assertLess(maxPixSep, 0.015)

        # try again, but without fitting the WCS
        config.forceKnownWcs = True
        solverNoFit = AstrometryTask(config=config, refObjLoader=self.refObjLoader)
        self.exposure.setWcs(distortedWcs)
        resultsNoFit = solverNoFit.run(
            sourceCat=sourceCat,
            exposure=self.exposure,
        )
        self.assertIsNone(resultsNoFit.scatterOnSky)

        # fitting should result in matches that are at least as good
        # (strictly speaking fitting might result in a larger match list with
        # some outliers, but in practice this test passes)
        meanFitDist = np.mean([match.distance for match in results.matches])
        meanNoFitDist = np.mean([match.distance for match in resultsNoFit.matches])
        self.assertLessEqual(meanFitDist, meanNoFitDist)
Пример #4
0
    def testMagnitudeOutlierRejection(self):
        """Test rejection of magnitude outliers.

        This test only tests the outlier rejection, and not any other
        part of the matching or astrometry fitter.
        """
        config = AstrometryTask.ConfigClass()
        config.doMagnitudeOutlierRejection = True
        config.magnitudeOutlierRejectionNSigma = 4.0
        solver = AstrometryTask(config=config, refObjLoader=None)

        nTest = 100

        refSchema = lsst.afw.table.SimpleTable.makeMinimalSchema()
        refSchema.addField('refFlux', 'F')
        refCat = lsst.afw.table.SimpleCatalog(refSchema)
        refCat.resize(nTest)

        srcSchema = lsst.afw.table.SourceTable.makeMinimalSchema()
        srcSchema.addField('srcFlux', 'F')
        srcCat = lsst.afw.table.SourceCatalog(srcSchema)
        srcCat.resize(nTest)

        np.random.seed(12345)
        refMag = np.full(nTest, 20.0)
        srcMag = np.random.normal(size=nTest, loc=0.0, scale=1.0)

        # Determine the sigma of the random sample
        zp = np.median(refMag[:-4] - srcMag[:-4])
        sigma = scipy.stats.median_abs_deviation(srcMag[:-4], scale='normal')

        # Deliberately alter some magnitudes to be outliers.
        srcMag[-3] = (config.magnitudeOutlierRejectionNSigma + 0.1) * sigma + (
            20.0 - zp)
        srcMag[-4] = -(config.magnitudeOutlierRejectionNSigma +
                       0.1) * sigma + (20.0 - zp)

        refCat['refFlux'] = (refMag * units.ABmag).to_value(units.nJy)
        srcCat['srcFlux'] = 10.0**(srcMag / (-2.5))

        # Deliberately poison some reference fluxes.
        refCat['refFlux'][-1] = np.inf
        refCat['refFlux'][-2] = np.nan

        matchesIn = []
        for ref, src in zip(refCat, srcCat):
            matchesIn.append(
                lsst.afw.table.ReferenceMatch(first=ref,
                                              second=src,
                                              distance=0.0))

        matchesOut = solver._removeMagnitudeOutliers('srcFlux', 'refFlux',
                                                     matchesIn)

        # We should lose the 4 outliers we created.
        self.assertEqual(len(matchesOut), len(matchesIn) - 4)
Пример #5
0
def run():
    exposure, srcCat = loadData()
    schema = srcCat.getSchema()
    #
    # Create the astrometry task
    #
    config = AstrometryTask.ConfigClass()
    config.refObjLoader.filterMap = {"_unknown_": "r"}
    config.matcher.sourceFluxType = "Psf"  # sample catalog does not contain aperture flux
    aTask = AstrometryTask(config=config)
    #
    # And the photometry Task
    #
    config = PhotoCalTask.ConfigClass()
    config.applyColorTerms = False  # we don't have any available, so this suppresses a warning
    pTask = PhotoCalTask(config=config, schema=schema)
    #
    # The tasks may have added extra elements to the schema (e.g. AstrometryTask's centroidKey to
    # handle distortion; photometryTask's config.outputField).  If this is so, we need to add
    # these columns to the Source table.
    #
    # We wouldn't need to do this if we created the schema prior to measuring the exposure,
    # but in this case we read the sources from disk
    #
    if schema != srcCat.getSchema():  # the tasks added fields
        print("Adding columns to the source catalogue")
        cat = afwTable.SourceCatalog(schema)
        cat.table.defineCentroid(srcCat.table.getCentroidDefinition())
        cat.table.definePsfFlux(srcCat.table.getPsfFluxDefinition())

        scm = afwTable.SchemaMapper(srcCat.getSchema(), schema)
        for schEl in srcCat.getSchema():
            scm.addMapping(schEl.getKey(), True)

        cat.extend(srcCat, True, scm)  # copy srcCat to cat, adding new columns

        srcCat = cat
        del cat
    #
    # Process the data
    #
    matches = aTask.run(exposure, srcCat).matches
    result = pTask.run(exposure, matches)

    calib = result.calib
    fm0, fm0Err = calib.getFluxMag0()

    print("Used %d calibration sources out of %d matches" %
          (len(result.matches), len(matches)))

    delta = result.arrays.refMag - result.arrays.srcMag
    q25, q75 = np.percentile(delta, [25, 75])
    print("RMS error is %.3fmmsg (robust %.3f, Calib says %.3f)" %
          (np.std(delta), 0.741 *
           (q75 - q25), 2.5 / np.log(10) * fm0Err / fm0))
Пример #6
0
    def testMaxMeanDistance(self):
        """If the astrometric fit does not satisfy the maxMeanDistanceArcsec
        threshold, ensure task raises an lsst.pipe.base.TaskError.
        """
        self.exposure.setWcs(self.tanWcs)
        config = AstrometryTask.ConfigClass()
        config.maxMeanDistanceArcsec = 0.0  # To ensure a "deemed" WCS failure
        solver = AstrometryTask(config=config, refObjLoader=self.refObjLoader)
        sourceCat = self.makeSourceCat(self.tanWcs, doScatterCentroids=True)

        with self.assertRaisesRegex(pipeBase.TaskError,
                                    "Fatal astrometry failure detected"):
            solver.run(sourceCat=sourceCat, exposure=self.exposure)
    def testUsedFlag(self):
        """Test that the solver will record number of sources used to table
           if it is passed a schema on initialization.
        """
        distortedWcs = afwImage.DistortedTanWcs(self.tanWcs,
                                                afwGeom.IdentityXYTransform())
        self.exposure.setWcs(distortedWcs)
        loadRes = self.refObjLoader.loadPixelBox(bbox=self.bbox,
                                                 wcs=distortedWcs,
                                                 filterName="r")
        refCat = loadRes.refCat
        refCentroidKey = afwTable.Point2DKey(refCat.schema["centroid"])
        refFluxRKey = refCat.schema["r_flux"].asKey()

        sourceSchema = afwTable.SourceTable.makeMinimalSchema()
        measBase.SingleFrameMeasurementTask(
            schema=sourceSchema)  # expand the schema
        config = AstrometryTask.ConfigClass()
        config.wcsFitter.order = 2
        config.wcsFitter.numRejIter = 0
        # schema must be passed to the solver task constructor
        solver = AstrometryTask(config=config,
                                refObjLoader=self.refObjLoader,
                                schema=sourceSchema)
        sourceCat = afwTable.SourceCatalog(sourceSchema)
        sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"])
        sourceFluxKey = sourceSchema["slot_ApFlux_flux"].asKey()
        sourceFluxSigmaKey = sourceSchema["slot_ApFlux_fluxSigma"].asKey()

        for refObj in refCat:
            src = sourceCat.addNew()
            src.set(sourceCentroidKey, refObj.get(refCentroidKey))
            src.set(sourceFluxKey, refObj.get(refFluxRKey))
            src.set(sourceFluxSigmaKey, refObj.get(refFluxRKey) / 100)

        results = solver.run(
            sourceCat=sourceCat,
            exposure=self.exposure,
        )
        # check that the used flag is set the right number of times
        count = 0
        for source in sourceCat:
            if source.get('calib_astrometryUsed'):
                count += 1
        self.assertEqual(count, len(results.matches))
Пример #8
0
    def runAstrometry(self, butler, exp, icSrc):
        refObjLoaderConfig = LoadIndexedReferenceObjectsTask.ConfigClass()
        refObjLoaderConfig.ref_dataset_name = 'gaia_dr2_20191105'
        refObjLoaderConfig.pixelMargin = 1000
        refObjLoader = LoadIndexedReferenceObjectsTask(
            butler=butler, config=refObjLoaderConfig)

        astromConfig = AstrometryTask.ConfigClass()
        astromConfig.wcsFitter.retarget(FitAffineWcsTask)
        astromConfig.referenceSelector.doMagLimit = True
        magLimit = MagnitudeLimit()
        magLimit.minimum = 1
        magLimit.maximum = 15
        astromConfig.referenceSelector.magLimit = magLimit
        astromConfig.referenceSelector.magLimit.fluxField = "phot_g_mean_flux"
        astromConfig.matcher.maxRotationDeg = 5.99
        astromConfig.matcher.maxOffsetPix = 3000
        astromConfig.sourceSelector['matcher'].minSnr = 10
        solver = AstrometryTask(config=astromConfig, refObjLoader=refObjLoader)

        # TODO: Change this to doing this the proper way
        referenceFilterName = self.config.referenceFilterOverride
        referenceFilterLabel = afwImage.FilterLabel(
            physical=referenceFilterName, band=referenceFilterName)
        originalFilterLabel = exp.getFilterLabel(
        )  # there's a better way of doing this with the task I think
        exp.setFilterLabel(referenceFilterLabel)

        try:
            astromResult = solver.run(sourceCat=icSrc, exposure=exp)
            exp.setFilterLabel(originalFilterLabel)
        except (RuntimeError, TaskError):
            self.log.warn("Solver failed to run completely")
            exp.setFilterLabel(originalFilterLabel)
            return None

        scatter = astromResult.scatterOnSky.asArcseconds()
        if scatter < 1:
            return astromResult
        else:
            self.log.warn("Failed to find an acceptable match")
        return None
Пример #9
0
def run():
    exposure, srcCat = loadData()
    schema = srcCat.getSchema()
    #
    # Create the reference catalog loader
    #
    refCatDir = os.path.join(lsst.utils.getPackageDir('meas_astrom'), 'tests',
                             'data', 'sdssrefcat')
    butler = Butler(refCatDir)
    refObjLoader = LoadIndexedReferenceObjectsTask(butler=butler)
    #
    # Create the astrometry task
    #
    config = AstrometryTask.ConfigClass()
    config.matcher.sourceFluxType = "Psf"  # sample catalog does not contain aperture flux
    config.matcher.minSnr = 0  # disable S/N test because sample catalog does not contain flux sigma
    aTask = AstrometryTask(config=config, refObjLoader=refObjLoader)
    #
    # And the photometry Task
    #
    config = PhotoCalTask.ConfigClass()
    config.applyColorTerms = False  # we don't have any available, so this suppresses a warning
    # The associated data has been prepared on the basis that we use PsfFlux to perform photometry.
    config.fluxField = "base_PsfFlux_flux"
    pTask = PhotoCalTask(config=config, schema=schema)
    #
    # The tasks may have added extra elements to the schema (e.g. AstrometryTask's centroidKey to
    # handle distortion; photometryTask's config.outputField).  If this is so, we need to add
    # these columns to the Source table.
    #
    # We wouldn't need to do this if we created the schema prior to measuring the exposure,
    # but in this case we read the sources from disk
    #
    if schema != srcCat.getSchema():  # the tasks added fields
        print("Adding columns to the source catalogue")
        cat = afwTable.SourceCatalog(schema)
        cat.table.defineCentroid(srcCat.table.getCentroidDefinition())
        cat.table.definePsfFlux(srcCat.table.getPsfFluxDefinition())

        scm = afwTable.SchemaMapper(srcCat.getSchema(), schema)
        for schEl in srcCat.getSchema():
            scm.addMapping(schEl.getKey(), True)

        cat.extend(srcCat, True, scm)  # copy srcCat to cat, adding new columns

        srcCat = cat
        del cat
    #
    # Process the data
    #
    matches = aTask.run(exposure, srcCat).matches
    result = pTask.run(exposure, matches)

    calib = result.calib
    fm0, fm0Err = calib.getFluxMag0()

    print("Used %d calibration sources out of %d matches" %
          (len(result.matches), len(matches)))

    delta = result.arrays.refMag - result.arrays.srcMag
    q25, q75 = np.percentile(delta, [25, 75])
    print("RMS error is %.3fmmsg (robust %.3f, Calib says %.3f)" %
          (np.std(delta), 0.741 *
           (q75 - q25), 2.5 / np.log(10) * fm0Err / fm0))