コード例 #1
0
    def testLists(self):
        """Check updating lists of reference objects and sources"""
        # arbitrary but reasonable values that are intentionally different than testCatalogs
        maxPix = 1000
        numPoints = 10
        self.setCatalogs(maxPix=maxPix, numPoints=numPoints)

        # update the catalogs as lists
        afwTable.updateSourceCoords(self.wcs, [s for s in self.sourceCat])
        afwTable.updateRefCentroids(self.wcs, [r for r in self.refCat])

        self.checkCatalogs()
コード例 #2
0
    def testLists(self):
        """Check updating lists of reference objects and sources"""
        # arbitrary but reasonable values that are intentionally different than
        # testCatalogs
        maxPix = 1000
        numPoints = 10
        self.setCatalogs(maxPix=maxPix, numPoints=numPoints)

        # update the catalogs as lists
        afwTable.updateSourceCoords(self.wcs, [s for s in self.sourceCat])
        afwTable.updateRefCentroids(self.wcs, [r for r in self.refCat])

        self.checkCatalogs()
コード例 #3
0
    def testCatalogs(self):
        """Check updating catalogs of reference objects and sources"""
        # arbitrary but reasonable values that are intentionally different than testLists
        maxPix = 2000
        numPoints = 9
        self.setCatalogs(maxPix=maxPix, numPoints=numPoints)

        # update the catalogs
        afwTable.updateSourceCoords(self.wcs, self.sourceCat)
        afwTable.updateRefCentroids(self.wcs, self.refCat)

        # check that centroids and coords match
        self.checkCatalogs()
コード例 #4
0
    def testCatalogs(self):
        """Check updating catalogs of reference objects and sources"""
        # arbitrary but reasonable values that are intentionally different than
        # testLists
        maxPix = 2000
        numPoints = 9
        self.setCatalogs(maxPix=maxPix, numPoints=numPoints)

        # update the catalogs
        afwTable.updateSourceCoords(self.wcs, self.sourceCat)
        afwTable.updateRefCentroids(self.wcs, self.refCat)

        # check that centroids and coords match
        self.checkCatalogs()
コード例 #5
0
    def testRefCenter(self):
        """Check that a ref obj at the center is handled as expected"""
        refObj = self.refCat.addNew()
        refObj.set(self.refCoordKey, self.crval)

        # initial centroid should be nan
        nanRefCentroid = self.refCat[0].get(self.refCentroidKey)
        for val in nanRefCentroid:
            self.assertTrue(math.isnan(val))

        # computed centroid should be crpix
        afwTable.updateRefCentroids(self.wcs, self.refCat)
        refCentroid = self.refCat[0].get(self.refCentroidKey)
        self.assertPairsAlmostEqual(refCentroid, self.crpix)

        # coord should not be changed
        self.assertEqual(self.refCat[0].get(self.refCoordKey), self.crval)
コード例 #6
0
    def testRefCenter(self):
        """Check that a ref obj at the center is handled as expected"""
        refObj = self.refCat.addNew()
        refObj.set(self.refCoordKey, self.crval)

        # initial centroid should be nan and hasCentroid False
        nanRefCentroid = self.refCat[0].get(self.refCentroidKey)
        for val in nanRefCentroid:
            self.assertTrue(math.isnan(val))
        self.assertFalse(self.refCat[0].get(self.refHasCentroidKey))

        # computed centroid should be crpix and hasCentroid True
        afwTable.updateRefCentroids(self.wcs, self.refCat)
        refCentroid = self.refCat[0].get(self.refCentroidKey)
        self.assertPairsAlmostEqual(refCentroid, self.crpix)
        self.assertTrue(self.refCat[0].get(self.refHasCentroidKey))

        # coord should not be changed
        self.assertEqual(self.refCat[0].get(self.refCoordKey), self.crval)
コード例 #7
0
    def _trimToBBox(refCat, bbox, wcs):
        """!Remove objects outside a given pixel-based bbox and set centroid and hasCentroid fields

        @param[in,out] refCat  a catalog of objects (an lsst.afw.table.SimpleCatalog,
            or other table type that has fields "coord", "centroid" and "hasCentroid").
            The "coord" field is read.
            The "centroid" and "hasCentroid" fields are set.
        @param[in] bbox  pixel region (an afwImage.Box2D)
        @param[in] wcs  WCS used to convert sky position to pixel position (an lsst.afw.math.WCS)

        @return a catalog of reference objects in bbox, with centroid and hasCentroid fields set
        """
        afwTable.updateRefCentroids(wcs, refCat)
        centroidKey = afwTable.Point2DKey(refCat.schema["centroid"])
        retStarCat = type(refCat)(refCat.table)
        for star in refCat:
            point = star.get(centroidKey)
            if bbox.contains(point):
                retStarCat.append(star)
        return retStarCat
コード例 #8
0
    def fitWcs(self,
               matches,
               initWcs,
               bbox=None,
               refCat=None,
               sourceCat=None,
               exposure=None):
        """!Fit a TAN-SIP WCS from a list of reference object/source matches

        @param[in,out] matches  a list of lsst::afw::table::ReferenceMatch
            The following fields are read:
            - match.first (reference object) coord
            - match.second (source) centroid
            The following fields are written:
            - match.first (reference object) centroid,
            - match.second (source) centroid
            - match.distance (on sky separation, in radians)
        @param[in] initWcs  initial WCS
        @param[in] bbox  the region over which the WCS will be valid (an lsst:afw::geom::Box2I);
            if None or an empty box then computed from matches
        @param[in,out] refCat  reference object catalog, or None.
            If provided then all centroids are updated with the new WCS,
            otherwise only the centroids for ref objects in matches are updated.
            Required fields are "centroid_x", "centroid_y", "coord_ra", and "coord_dec".
        @param[in,out] sourceCat  source catalog, or None.
            If provided then coords are updated with the new WCS;
            otherwise only the coords for sources in matches are updated.
            Required fields are "slot_Centroid_x", "slot_Centroid_y", and "coord_ra", and "coord_dec".
        @param[in] exposure  Ignored; present for consistency with FitSipDistortionTask.

        @return an lsst.pipe.base.Struct with the following fields:
        - wcs  the fit WCS as an lsst.afw.geom.Wcs
        - scatterOnSky  median on-sky separation between reference objects and sources in "matches",
            as an lsst.afw.geom.Angle
        """
        if bbox is None:
            bbox = afwGeom.Box2I()

        import lsstDebug
        debug = lsstDebug.Info(__name__)

        wcs = self.initialWcs(matches, initWcs)
        rejected = np.zeros(len(matches), dtype=bool)
        for rej in range(self.config.numRejIter):
            sipObject = self._fitWcs(
                [mm for i, mm in enumerate(matches) if not rejected[i]], wcs)
            wcs = sipObject.getNewWcs()
            rejected = self.rejectMatches(matches, wcs, rejected)
            if rejected.sum() == len(rejected):
                raise RuntimeError("All matches rejected in iteration %d" %
                                   (rej + 1, ))
            self.log.debug(
                "Iteration {0} of astrometry fitting: rejected {1} outliers, "
                "out of {2} total matches.".format(rej, rejected.sum(),
                                                   len(rejected)))
            if debug.plot:
                print("Plotting fit after rejection iteration %d/%d" %
                      (rej + 1, self.config.numRejIter))
                self.plotFit(matches, wcs, rejected)
        # Final fit after rejection
        sipObject = self._fitWcs(
            [mm for i, mm in enumerate(matches) if not rejected[i]], wcs)
        wcs = sipObject.getNewWcs()
        if debug.plot:
            print("Plotting final fit")
            self.plotFit(matches, wcs, rejected)

        if refCat is not None:
            self.log.debug("Updating centroids in refCat")
            afwTable.updateRefCentroids(wcs, refList=refCat)
        else:
            self.log.warn(
                "Updating reference object centroids in match list; refCat is None"
            )
            afwTable.updateRefCentroids(
                wcs, refList=[match.first for match in matches])

        if sourceCat is not None:
            self.log.debug("Updating coords in sourceCat")
            afwTable.updateSourceCoords(wcs, sourceList=sourceCat)
        else:
            self.log.warn(
                "Updating source coords in match list; sourceCat is None")
            afwTable.updateSourceCoords(
                wcs, sourceList=[match.second for match in matches])

        self.log.debug("Updating distance in match list")
        setMatchDistance(matches)

        scatterOnSky = sipObject.getScatterOnSky()

        if scatterOnSky.asArcseconds() > self.config.maxScatterArcsec:
            raise pipeBase.TaskError(
                "Fit failed: median scatter on sky = %0.3f arcsec > %0.3f config.maxScatterArcsec"
                % (scatterOnSky.asArcseconds(), self.config.maxScatterArcsec))

        return pipeBase.Struct(
            wcs=wcs,
            scatterOnSky=scatterOnSky,
        )
コード例 #9
0
    def doTest(self,
               name,
               func,
               order=3,
               numIter=4,
               specifyBBox=False,
               doPlot=False,
               doPrint=False):
        """Apply func(x, y) to each source in self.sourceCat, then fit and check the resulting WCS
        """
        bbox = lsst.geom.Box2I()
        for refObj, src, d in self.matches:
            origPos = src.get(self.srcCentroidKey)
            x, y = func(*origPos)
            distortedPos = lsst.geom.Point2D(*func(*origPos))
            src.set(self.srcCentroidKey, distortedPos)
            bbox.include(lsst.geom.Point2I(lsst.geom.Point2I(distortedPos)))

        tanSipWcs = self.tanWcs
        for i in range(numIter):
            if specifyBBox:
                sipObject = makeCreateWcsWithSip(self.matches, tanSipWcs,
                                                 order, bbox)
            else:
                sipObject = makeCreateWcsWithSip(self.matches, tanSipWcs,
                                                 order)
            tanSipWcs = sipObject.getNewWcs()
        setMatchDistance(self.matches)
        fitRes = lsst.pipe.base.Struct(
            wcs=tanSipWcs,
            scatterOnSky=sipObject.getScatterOnSky(),
        )

        if doPrint:
            print("TAN-SIP metadata fit over bbox=", bbox)
            metadata = makeTanSipMetadata(
                crpix=tanSipWcs.getPixelOrigin(),
                crval=tanSipWcs.getSkyOrigin(),
                cdMatrix=tanSipWcs.getCdMatrix(),
                sipA=sipObject.getSipA(),
                sipB=sipObject.getSipB(),
                sipAp=sipObject.getSipAp(),
                sipBp=sipObject.getSipBp(),
            )
            print(metadata.toString())

        if doPlot:
            self.plotWcs(tanSipWcs, name=name)

        self.checkResults(fitRes, catsUpdated=False)

        if self.MatchClass == afwTable.ReferenceMatch:
            # reset source coord and reference centroid based on initial WCS
            afwTable.updateRefCentroids(wcs=self.tanWcs, refList=self.refCat)
            afwTable.updateSourceCoords(wcs=self.tanWcs,
                                        sourceList=self.sourceCat)

            fitterConfig = FitTanSipWcsTask.ConfigClass()
            fitterConfig.order = order
            fitterConfig.numIter = numIter
            fitter = FitTanSipWcsTask(config=fitterConfig)
            self.loadData()
            if specifyBBox:
                fitRes = fitter.fitWcs(
                    matches=self.matches,
                    initWcs=self.tanWcs,
                    bbox=bbox,
                    refCat=self.refCat,
                    sourceCat=self.sourceCat,
                )
            else:
                fitRes = fitter.fitWcs(
                    matches=self.matches,
                    initWcs=self.tanWcs,
                    bbox=bbox,
                    refCat=self.refCat,
                    sourceCat=self.sourceCat,
                )

            self.checkResults(fitRes, catsUpdated=True)
コード例 #10
0
 def testNull(self):
     """Check that an empty list causes no problems for either function"""
     afwTable.updateRefCentroids(self.wcs, [])
     afwTable.updateSourceCoords(self.wcs, [])
コード例 #11
0
 def testNull(self):
     """Check that an empty list causes no problems for either function"""
     afwTable.updateRefCentroids(self.wcs, [])
     afwTable.updateSourceCoords(self.wcs, [])
コード例 #12
0
    def doTest(self, name, func, order=3, numIter=4, specifyBBox=False, doPlot=False, doPrint=False):
        """Apply func(x, y) to each source in self.sourceCat, then fit and check the resulting WCS
        """
        bbox = lsst.geom.Box2I()
        for refObj, src, d in self.matches:
            origPos = src.get(self.srcCentroidKey)
            x, y = func(*origPos)
            distortedPos = lsst.geom.Point2D(*func(*origPos))
            src.set(self.srcCentroidKey, distortedPos)
            bbox.include(lsst.geom.Point2I(lsst.geom.Point2I(distortedPos)))

        tanSipWcs = self.tanWcs
        for i in range(numIter):
            if specifyBBox:
                sipObject = makeCreateWcsWithSip(self.matches, tanSipWcs, order, bbox)
            else:
                sipObject = makeCreateWcsWithSip(self.matches, tanSipWcs, order)
            tanSipWcs = sipObject.getNewWcs()
        setMatchDistance(self.matches)
        fitRes = lsst.pipe.base.Struct(
            wcs=tanSipWcs,
            scatterOnSky=sipObject.getScatterOnSky(),
        )

        if doPrint:
            print("TAN-SIP metadata fit over bbox=", bbox)
            metadata = makeTanSipMetadata(
                crpix=tanSipWcs.getPixelOrigin(),
                crval=tanSipWcs.getSkyOrigin(),
                cdMatrix=tanSipWcs.getCdMatrix(),
                sipA=sipObject.getSipA(),
                sipB=sipObject.getSipB(),
                sipAp=sipObject.getSipAp(),
                sipBp=sipObject.getSipBp(),
            )
            print(metadata.toString())

        if doPlot:
            self.plotWcs(tanSipWcs, name=name)

        self.checkResults(fitRes, catsUpdated=False)

        if self.MatchClass == afwTable.ReferenceMatch:
            # reset source coord and reference centroid based on initial WCS
            afwTable.updateRefCentroids(wcs=self.tanWcs, refList=self.refCat)
            afwTable.updateSourceCoords(wcs=self.tanWcs, sourceList=self.sourceCat)

            fitterConfig = FitTanSipWcsTask.ConfigClass()
            fitterConfig.order = order
            fitterConfig.numIter = numIter
            fitter = FitTanSipWcsTask(config=fitterConfig)
            self.loadData()
            if specifyBBox:
                fitRes = fitter.fitWcs(
                    matches=self.matches,
                    initWcs=self.tanWcs,
                    bbox=bbox,
                    refCat=self.refCat,
                    sourceCat=self.sourceCat,
                )
            else:
                fitRes = fitter.fitWcs(
                    matches=self.matches,
                    initWcs=self.tanWcs,
                    bbox=bbox,
                    refCat=self.refCat,
                    sourceCat=self.sourceCat,
                )

            self.checkResults(fitRes, catsUpdated=True)
コード例 #13
0
ファイル: fitTanSipWcs.py プロジェクト: lsst/meas_astrom
    def fitWcs(self, matches, initWcs, bbox=None, refCat=None, sourceCat=None, exposure=None):
        """Fit a TAN-SIP WCS from a list of reference object/source matches

        Parameters
        ----------
        matches : `list` of `lsst.afw.table.ReferenceMatch`
            The following fields are read:

            - match.first (reference object) coord
            - match.second (source) centroid

            The following fields are written:

            - match.first (reference object) centroid,
            - match.second (source) centroid
            - match.distance (on sky separation, in radians)

        initWcs : `lsst.afw.geom.SkyWcs`
            initial WCS
        bbox : `lsst.geom.Box2I`
            the region over which the WCS will be valid (an lsst:afw::geom::Box2I);
            if None or an empty box then computed from matches
        refCat : `lsst.afw.table.SimpleCatalog`
            reference object catalog, or None.
            If provided then all centroids are updated with the new WCS,
            otherwise only the centroids for ref objects in matches are updated.
            Required fields are "centroid_x", "centroid_y", "coord_ra", and "coord_dec".
        sourceCat : `lsst.afw.table.SourceCatalog`
            source catalog, or None.
            If provided then coords are updated with the new WCS;
            otherwise only the coords for sources in matches are updated.
            Required fields are "slot_Centroid_x", "slot_Centroid_y", and "coord_ra", and "coord_dec".
        exposure : `lsst.afw.image.Exposure`
            Ignored; present for consistency with FitSipDistortionTask.

        Returns
        -------
        result : `lsst.pipe.base.Struct`
            with the following fields:

            - ``wcs`` :  the fit WCS (`lsst.afw.geom.SkyWcs`)
            - ``scatterOnSky`` :  median on-sky separation between reference
              objects and sources in "matches" (`lsst.afw.geom.Angle`)
        """
        if bbox is None:
            bbox = lsst.geom.Box2I()

        import lsstDebug
        debug = lsstDebug.Info(__name__)

        wcs = self.initialWcs(matches, initWcs)
        rejected = np.zeros(len(matches), dtype=bool)
        for rej in range(self.config.numRejIter):
            sipObject = self._fitWcs([mm for i, mm in enumerate(matches) if not rejected[i]], wcs)
            wcs = sipObject.getNewWcs()
            rejected = self.rejectMatches(matches, wcs, rejected)
            if rejected.sum() == len(rejected):
                raise RuntimeError("All matches rejected in iteration %d" % (rej + 1,))
            self.log.debug(
                "Iteration {0} of astrometry fitting: rejected {1} outliers, "
                "out of {2} total matches.".format(
                    rej, rejected.sum(), len(rejected)
                )
            )
            if debug.plot:
                print("Plotting fit after rejection iteration %d/%d" % (rej + 1, self.config.numRejIter))
                self.plotFit(matches, wcs, rejected)
        # Final fit after rejection
        sipObject = self._fitWcs([mm for i, mm in enumerate(matches) if not rejected[i]], wcs)
        wcs = sipObject.getNewWcs()
        if debug.plot:
            print("Plotting final fit")
            self.plotFit(matches, wcs, rejected)

        if refCat is not None:
            self.log.debug("Updating centroids in refCat")
            afwTable.updateRefCentroids(wcs, refList=refCat)
        else:
            self.log.warn("Updating reference object centroids in match list; refCat is None")
            afwTable.updateRefCentroids(wcs, refList=[match.first for match in matches])

        if sourceCat is not None:
            self.log.debug("Updating coords in sourceCat")
            afwTable.updateSourceCoords(wcs, sourceList=sourceCat)
        else:
            self.log.warn("Updating source coords in match list; sourceCat is None")
            afwTable.updateSourceCoords(wcs, sourceList=[match.second for match in matches])

        self.log.debug("Updating distance in match list")
        setMatchDistance(matches)

        scatterOnSky = sipObject.getScatterOnSky()

        if scatterOnSky.asArcseconds() > self.config.maxScatterArcsec:
            raise pipeBase.TaskError(
                "Fit failed: median scatter on sky = %0.3f arcsec > %0.3f config.maxScatterArcsec" %
                (scatterOnSky.asArcseconds(), self.config.maxScatterArcsec))

        return pipeBase.Struct(
            wcs=wcs,
            scatterOnSky=scatterOnSky,
        )