示例#1
0
    def testCurvedFocalPlane(self):
        """Test a curved focal plane (with rectangular pixels)
        """
        bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0),
                             afwGeom.Extent2I(1000, 1000))
        pixelSizeMm = afwGeom.Extent2D(0.02, 0.03)
        plateScale = 25.0  # arcsec/mm
        yaw = afwGeom.Angle(20, afwGeom.degrees)
        # focal-plane position of ref position on detector (mm)
        fpPosition = afwGeom.Point2D(50, 25)
        # ref position on detector (pos of lower left corner)
        refPoint = afwGeom.Point2D(-0.5, -0.5)
        orientation = cameraGeom.Orientation(
            fpPosition,
            refPoint,
            yaw,
        )
        pixelToFocalPlane = orientation.makePixelFpTransform(pixelSizeMm)
        plateScaleRad = afwGeom.Angle(plateScale,
                                      afwGeom.arcseconds).asRadians()
        focalPlaneToField = afwGeom.makeRadialTransform(
            (0.0, plateScaleRad, 0.0, 0.001 * plateScaleRad))
        pixelToField = pixelToFocalPlane.then(focalPlaneToField)

        pixelToTanPixel = makePixelToTanPixel(
            bbox=bbox,
            orientation=orientation,
            focalPlaneToField=focalPlaneToField,
            pixelSizeMm=pixelSizeMm,
        )

        # the center point of the field angle frame should not move
        pixAtFieldCtr = pixelToField.applyInverse(afwGeom.Point2D(0, 0))
        tanPixAtFieldCr = pixelToTanPixel.applyForward(pixAtFieldCtr)
        self.assertPairsAlmostEqual(pixAtFieldCtr, tanPixAtFieldCr)

        # build same camera geometry transforms without optical distortion
        focalPlaneToFieldNoDistortion = afwGeom.makeRadialTransform(
            (0.0, plateScaleRad))
        pixelToFieldNoDistortion = pixelToFocalPlane.then(
            focalPlaneToFieldNoDistortion)

        for x in (100, 200, 1000):
            for y in (100, 500, 800):
                pixPos = afwGeom.Point2D(x, y)
                tanPixPos = pixelToTanPixel.applyForward(pixPos)

                # for a given field angle (which, together with a pointing, gives a position on the sky):
                # - field angle to pixels gives pixPos
                # - undistorted field angle to pixels gives tanPixPos
                fieldPos = pixelToField.applyForward(pixPos)
                desTanPixPos = pixelToFieldNoDistortion.applyInverse(fieldPos)
                # use a degraded accuracy because small Jacobian errors accumulate this far from the center
                self.assertPairsAlmostEqual(desTanPixPos,
                                            tanPixPos,
                                            maxDiff=1e-5)
 def testWarnings(self):
     """Test that approximateWcs raises a UserWarning when it cannot achieve desired tolerance"""
     radialTransform = afwGeom.makeRadialTransform([0, 2.0, 3.0])
     wcs = afwGeom.makeModifiedWcs(pixelTransform=radialTransform, wcs=self.tanWcs,
                                   modifyActualPixels=False)
     with self.assertRaises(UserWarning):
         approximateWcs(wcs=wcs, bbox=self.bbox, order=2)
示例#3
0
    def testDistortion(self):
        """Test computePixelToDistortedPixel with distortion

        pixelToDistortedPixel -> self.tanWcs should match a WCS
        created with makeDistortedTanWcs
        """
        focalPlaneToFieldAngle = afwGeom.makeRadialTransform([0.0, self.radPerMm, 0.0, self.radPerMm])
        pixelToDistortedPixel = computePixelToDistortedPixel(
            pixelToFocalPlane=self.pixelToFocalPlane,
            focalPlaneToFieldAngle=focalPlaneToFieldAngle,
        )
        # Do not try to make pixelToDistortedPixel -> self.tanWcs into a WCS
        # because the frame names will be wrong; use a TransformPoint2Tolsst.geom.SpherePoint instead
        tanWcsTransform = afwGeom.TransformPoint2ToSpherePoint(self.tanWcs.getFrameDict())
        pixelToDistortedSky = pixelToDistortedPixel.then(tanWcsTransform)

        wcs = makeDistortedTanWcs(
            tanWcs=self.tanWcs,
            pixelToFocalPlane=self.pixelToFocalPlane,
            focalPlaneToFieldAngle=focalPlaneToFieldAngle,
        )

        bboxD = lsst.geom.Box2D(self.bbox)
        pixelPoints = bboxD.getCorners()
        pixelPoints.append(bboxD.getCenter())

        skyPoints1 = pixelToDistortedSky.applyForward(pixelPoints)
        skyPoints2 = wcs.pixelToSky(pixelPoints)
        self.assertSpherePointListsAlmostEqual(skyPoints1, skyPoints2)

        pixelPoints1 = pixelToDistortedSky.applyInverse(skyPoints1)
        pixelPoints2 = wcs.skyToPixel(skyPoints1)
        assert_allclose(pixelPoints1, pixelPoints2)
示例#4
0
 def testRadial(self):
     """Add a radial transform"""
     for order in (4, 5, 6):
         self.doTest("testRadial",
                     afwGeom.makeRadialTransform([0, 1.001, 0.000003]),
                     order=order,
                     doPlot=False)
示例#5
0
    def testDistortion(self):
        """Test computePixelToDistortedPixel with distortion

        pixelToDistortedPixel -> self.tanWcs should match a WCS
        created with makeDistortedTanWcs
        """
        focalPlaneToFieldAngle = afwGeom.makeRadialTransform(
            [0.0, self.radPerMm, 0.0, self.radPerMm])
        pixelToDistortedPixel = computePixelToDistortedPixel(
            pixelToFocalPlane=self.pixelToFocalPlane,
            focalPlaneToFieldAngle=focalPlaneToFieldAngle,
        )
        # Do not try to make pixelToDistortedPixel -> self.tanWcs into a WCS
        # because the frame names will be wrong; use a TransformPoint2Tolsst.geom.SpherePoint instead
        tanWcsTransform = afwGeom.TransformPoint2ToSpherePoint(
            self.tanWcs.getFrameDict())
        pixelToDistortedSky = pixelToDistortedPixel.then(tanWcsTransform)

        wcs = makeDistortedTanWcs(
            tanWcs=self.tanWcs,
            pixelToFocalPlane=self.pixelToFocalPlane,
            focalPlaneToFieldAngle=focalPlaneToFieldAngle,
        )

        bboxD = lsst.geom.Box2D(self.bbox)
        pixelPoints = bboxD.getCorners()
        pixelPoints.append(bboxD.getCenter())

        skyPoints1 = pixelToDistortedSky.applyForward(pixelPoints)
        skyPoints2 = wcs.pixelToSky(pixelPoints)
        self.assertSpherePointListsAlmostEqual(skyPoints1, skyPoints2)

        pixelPoints1 = pixelToDistortedSky.applyInverse(skyPoints1)
        pixelPoints2 = wcs.skyToPixel(skyPoints1)
        assert_allclose(pixelPoints1, pixelPoints2)
示例#6
0
    def makeCameraGeom(self):
        """Make a camera geometry.

        Returns
        -------
        cameraGeom : `lsst.afw.cameraGeom.Camera`
            Camera geometry.

        Notes
        -----
        There is one field per entry in self.detectorFracPosList
        with specifications set by self.detectorWidthPix,
        self.detectorHeightPix, and self.pixelSizeMm.

        The plate scale is set by self.plateScale
        and the amount of optical distortion is fixed.

        All detectors have the same shape (unlike LSST) and orientation
        (unlike HSC). Varying these is not necessary for testing the CBP
        and having all detectors the same simplifies the code.
        """
        radialCoeff = np.array([0.0, 1.0, 0.0, 0.925
                                ]) / self.plateScale.asRadians()
        fieldAngleToFocalPlane = makeRadialTransform(radialCoeff)
        focalPlaneToFieldAngle = fieldAngleToFocalPlane.inverted()
        cameraTransformMap = cameraGeom.TransformMap(
            cameraGeom.FOCAL_PLANE,
            {cameraGeom.FIELD_ANGLE: focalPlaneToFieldAngle})
        detectorList = self._makeDetectorList(focalPlaneToFieldAngle)
        return cameraGeom.Camera("test", detectorList, cameraTransformMap)
 def testLinearize(self):
     for transform, invertible in (
         (afwGeom.TransformPoint2ToPoint2(makeForwardPolyMap(2, 2)), False),
         (afwGeom.makeIdentityTransform(), True),
         (afwGeom.makeTransform(lsst.geom.AffineTransform(np.array([[3.0, -2.0], [2.0, -1.0]]))), True),
         (afwGeom.makeRadialTransform([0.0, 8.0e-05, 0.0, -4.5e-12]), True),
     ):
         self.checkLinearize(transform, invertible)
    def testBadRadial(self):
        """Test radial with invalid coefficients
        """
        for badCoeffs in (
            (0.0,),     # len(coeffs) must be > 1
            (0.1, 1.0),  # coeffs[0] must be zero
            (0.0, 0.0),  # coeffs[1] must be nonzero
            (0.0, 0.0, 0.1),  # coeffs[1] must be nonzero
        ):
            with self.assertRaises(pexExcept.InvalidParameterError):
                afwGeom.makeRadialTransform(badCoeffs)

            radialFactory = afwGeom.transformRegistry["radial"]
            radialConfig = radialFactory.ConfigClass()
            radialConfig.coeffs = badCoeffs
            with self.assertRaises(Exception):
                radialConfig.validate()
示例#9
0
 def testWarnings(self):
     """Test that approximateWcs raises a UserWarning when it cannot achieve desired tolerance"""
     radialTransform = afwGeom.makeRadialTransform([0, 2.0, 3.0])
     wcs = afwGeom.makeModifiedWcs(pixelTransform=radialTransform,
                                   wcs=self.tanWcs,
                                   modifyActualPixels=False)
     with self.assertRaises(UserWarning):
         approximateWcs(wcs=wcs, bbox=self.bbox, order=2)
示例#10
0
 def testLinearize(self):
     for transform, invertible in (
         (afwGeom.TransformPoint2ToPoint2(makeForwardPolyMap(2, 2)), False),
         (afwGeom.makeIdentityTransform(), True),
         (afwGeom.makeTransform(lsst.geom.AffineTransform(np.array([[3.0, -2.0], [2.0, -1.0]]))), True),
         (afwGeom.makeRadialTransform([0.0, 8.0e-05, 0.0, -4.5e-12]), True),
     ):
         self.checkLinearize(transform, invertible)
    def testRadial(self):
        """Add radial distortion"""
        radialTransform = afwGeom.makeRadialTransform([0, 1.02, 1e-6])

        def radialDistortion(x, y):
            x, y = radialTransform.applyForward(lsst.geom.Point2D(x, y))
            return (x, y)
        self.doTest("testRadial", radialDistortion)
    def testBadRadial(self):
        """Test radial with invalid coefficients
        """
        for badCoeffs in (
            (0.0, ),  # len(coeffs) must be > 1
            (0.1, 1.0),  # coeffs[0] must be zero
            (0.0, 0.0),  # coeffs[1] must be nonzero
            (0.0, 0.0, 0.1),  # coeffs[1] must be nonzero
        ):
            with self.assertRaises(pexExcept.InvalidParameterError):
                afwGeom.makeRadialTransform(badCoeffs)

            radialFactory = afwGeom.transformRegistry["radial"]
            radialConfig = radialFactory.ConfigClass()
            radialConfig.coeffs = badCoeffs
            with self.assertRaises(Exception):
                radialConfig.validate()
示例#13
0
def makeTransformDict(nativeSys, transformDict, plateScale):
    """Make a dictionary of TransformPoint2ToPoint2s from yaml, mapping from
    nativeSys

    Parameters
    ----------
    nativeSys : `lsst.afw.cameraGeom.CameraSys`
    transformDict : `dict`
        A dict specifying parameters of transforms; keys are camera system
        names.
    plateScale : `lsst.geom.Angle`
        The size of a pixel in angular units/mm (e.g. 20 arcsec/mm for LSST)

    Returns
    -------
    transforms : `dict`
        A dict of `lsst.afw.cameraGeom.CameraSys` :
        `lsst.afw.geom.TransformPoint2ToPoint2`

    The resulting dict's keys are `~lsst.afw.cameraGeom.CameraSys`,
    and the values are Transforms *from* NativeSys *to* CameraSys
    """
    # As other comments note this is required, and this is one function where
    # it's assumed
    assert nativeSys == cameraGeom.FOCAL_PLANE, "Cameras with nativeSys != FOCAL_PLANE are not supported."

    resMap = dict()

    for key, transform in transformDict.items():
        transformType = transform["transformType"]
        knownTransformTypes = ["affine", "radial"]
        if transformType not in knownTransformTypes:
            raise RuntimeError("Saw unknown transform type for %s: %s (known types are: [%s])" % (
                key, transform["transformType"], ", ".join(knownTransformTypes)))

        if transformType == "affine":
            affine = geom.AffineTransform(np.array(transform["linear"]),
                                          np.array(transform["translation"]))

            transform = afwGeom.makeTransform(affine)
        elif transformType == "radial":
            # radial coefficients of the form [0, 1 (no units), C2 (rad),
            # usually 0, C3 (rad^2), ...]
            # Radial distortion is modeled as a radial polynomial that converts
            # from focal plane radius (in mm) to field angle (in radians).
            # The provided coefficients are divided by the plate
            # scale (in radians/mm) meaning that C1 is always 1.
            radialCoeffs = np.array(transform["coeffs"])

            radialCoeffs *= plateScale.asRadians()
            transform = afwGeom.makeRadialTransform(radialCoeffs)
        else:
            raise RuntimeError("Impossible condition \"%s\" is not in: [%s])" % (
                transform["transformType"], ", ".join(knownTransformTypes)))

        resMap[cameraGeom.CameraSys(key)] = transform

    return resMap
示例#14
0
    def testRadial(self):
        """Add radial distortion"""
        radialTransform = afwGeom.makeRadialTransform([0, 1.02, 1e-6])

        def radialDistortion(x, y):
            x, y = radialTransform.applyForward(afwGeom.Point2D(x, y))
            return (x, y)

        self.doTest("testRadial", radialDistortion)
示例#15
0
 def _makeRadialTransform(self, radialCoeffs):
     """Helper function to get the radial transform given the radial polynomial coefficients given in
        the constructor.
        @param[in]  radialCoeffs  List of coefficients describing a polynomial radial distortion in
                                  normalized units.
        @return     Transform object describing the radial distortion
     """
     pScaleRad = afwGeom.arcsecToRad(self.plateScale)
     return afwGeom.makeRadialTransform(
         [el / pScaleRad for el in radialCoeffs])
    def testRadial(self):
        """Add radial distortion"""
        radialTransform = afwGeom.makeRadialTransform([0, 1.01, 1e-8])

        def radialDistortion(x, y):
            x, y = radialTransform.applyForward(afwGeom.Point2D(x, y))
            return (x, y)
        for order in (4, 5, 6):
            doPrint = order == 5
            self.doTest("testRadial", radialDistortion, order=order, doPrint=doPrint)
    def testRadial(self):
        """Add radial distortion"""
        radialTransform = afwGeom.makeRadialTransform([0, 1.01, 1e-8])

        def radialDistortion(x, y):
            x, y = radialTransform.applyForward(lsst.geom.Point2D(x, y))
            return (x, y)
        for order in (4, 5, 6):
            doPrint = order == 5
            self.doTest("testRadial", radialDistortion, order=order, doPrint=doPrint)
示例#18
0
    def testDistortion(self):
        """Test makeDistortedTanWcs using a non-affine transform for pixelToFocalPlane
        """
        # Compute a distorted wcs that matches self.tanWcs at the center of the field;
        # the amount of distortion is 10s of pixels over the detector
        fieldAngleToFocalPlane = afwGeom.makeRadialTransform(
            [0.0, 1 / self.radPerMm, 0.0, 1000 / self.radPerMm])
        focalPlaneToFieldAngle = fieldAngleToFocalPlane.inverted()
        focalPlaneToTanFieldAngle = self.makeAffineTransform(
            scale=self.radPerMm)
        wcs = makeDistortedTanWcs(
            tanWcs=self.tanWcs,
            pixelToFocalPlane=self.pixelToFocalPlane,
            focalPlaneToFieldAngle=focalPlaneToFieldAngle,
        )

        # At the center of the focal plane both WCS should give the same sky position
        pixelAtCtr = self.pixelToFocalPlane.applyInverse(
            lsst.geom.Point2D(0, 0))
        tanSkyAtCtr = self.tanWcs.pixelToSky(pixelAtCtr)
        skyAtCtr = wcs.pixelToSky(pixelAtCtr)
        self.assertPairsAlmostEqual(tanSkyAtCtr, skyAtCtr)

        # At all reasonable sky points the following field angles should be almost equal:
        #   sky -> tanWcs.skyToPixel -> pixelToFocalPlane -> focalPlaneToTanFieldAngle
        #   sky -> wcs.skyToPixel -> pixelToFocalPlane -> focalPlaneToFieldAngle
        # where focalPlaneToTanFieldAngle is the linear approximation to
        # focalPlaneToFieldAngle at the center of the field (where tanWcs and wcs match),
        # since for a given pointing, field angle gives position on the sky
        skyPoints = self.tanWcs.pixelToSky(self.pixelPoints)

        tanFieldAnglePoints = focalPlaneToTanFieldAngle.applyForward(
            self.pixelToFocalPlane.applyForward(
                self.tanWcs.skyToPixel(skyPoints)))
        fieldAnglePoints = focalPlaneToFieldAngle.applyForward(
            self.pixelToFocalPlane.applyForward(wcs.skyToPixel(skyPoints)))
        assert_allclose(tanFieldAnglePoints, fieldAnglePoints)

        # The inverse should also be true: for a set of field angle points
        # the following sky positions should be almost equal:
        # fieldAngle -> fieldAngleToTanFocalPlane -> focalPlaneToPixel -> tanWcs.pixelToSky
        # fieldAngle -> fieldAngleToFocalPlane -> focalPlaneToPixel -> wcs.pixelToSky
        focalPlaneToPixel = self.pixelToFocalPlane.inverted()
        fieldAngleToTanFocalPlane = focalPlaneToTanFieldAngle.inverted()
        tanSkyPoints2 = self.tanWcs.pixelToSky(
            focalPlaneToPixel.applyForward(
                fieldAngleToTanFocalPlane.applyForward(fieldAnglePoints)))

        skyPoints2 = wcs.pixelToSky(
            focalPlaneToPixel.applyForward(
                fieldAngleToFocalPlane.applyForward(fieldAnglePoints)))

        self.assertSpherePointListsAlmostEqual(tanSkyPoints2, skyPoints2)
示例#19
0
    def testDistortion(self):
        """Test makeDistortedTanWcs using a non-affine transform for pixelToFocalPlane
        """
        # Compute a distorted wcs that matches self.tanWcs at the center of the field;
        # the amount of distortion is 10s of pixels over the detector
        fieldAngleToFocalPlane = afwGeom.makeRadialTransform([0.0, 1/self.radPerMm, 0.0, 1000/self.radPerMm])
        focalPlaneToFieldAngle = fieldAngleToFocalPlane.inverted()
        focalPlaneToTanFieldAngle = self.makeAffineTransform(scale=self.radPerMm)
        wcs = makeDistortedTanWcs(
            tanWcs=self.tanWcs,
            pixelToFocalPlane=self.pixelToFocalPlane,
            focalPlaneToFieldAngle=focalPlaneToFieldAngle,
        )

        # At the center of the focal plane both WCS should give the same sky position
        pixelAtCtr = self.pixelToFocalPlane.applyInverse(lsst.geom.Point2D(0, 0))
        tanSkyAtCtr = self.tanWcs.pixelToSky(pixelAtCtr)
        skyAtCtr = wcs.pixelToSky(pixelAtCtr)
        self.assertPairsAlmostEqual(tanSkyAtCtr, skyAtCtr)

        # At all reasonable sky points the following field angles should be almost equal:
        #   sky -> tanWcs.skyToPixel -> pixelToFocalPlane -> focalPlaneToTanFieldAngle
        #   sky -> wcs.skyToPixel -> pixelToFocalPlane -> focalPlaneToFieldAngle
        # where focalPlaneToTanFieldAngle is the linear approximation to
        # focalPlaneToFieldAngle at the center of the field (where tanWcs and wcs match),
        # since for a given pointing, field angle gives position on the sky
        skyPoints = self.tanWcs.pixelToSky(self.pixelPoints)

        tanFieldAnglePoints = focalPlaneToTanFieldAngle.applyForward(
            self.pixelToFocalPlane.applyForward(self.tanWcs.skyToPixel(skyPoints)))
        fieldAnglePoints = focalPlaneToFieldAngle.applyForward(
            self.pixelToFocalPlane.applyForward(wcs.skyToPixel(skyPoints)))
        assert_allclose(tanFieldAnglePoints, fieldAnglePoints)

        # The inverse should also be true: for a set of field angle points
        # the following sky positions should be almost equal:
        # fieldAngle -> fieldAngleToTanFocalPlane -> focalPlaneToPixel -> tanWcs.pixelToSky
        # fieldAngle -> fieldAngleToFocalPlane -> focalPlaneToPixel -> wcs.pixelToSky
        focalPlaneToPixel = self.pixelToFocalPlane.inverted()
        fieldAngleToTanFocalPlane = focalPlaneToTanFieldAngle.inverted()
        tanSkyPoints2 = self.tanWcs.pixelToSky(
            focalPlaneToPixel.applyForward(
                fieldAngleToTanFocalPlane.applyForward(fieldAnglePoints)))

        skyPoints2 = wcs.pixelToSky(
            focalPlaneToPixel.applyForward(
                fieldAngleToFocalPlane.applyForward(fieldAnglePoints)))

        self.assertSpherePointListsAlmostEqual(tanSkyPoints2, skyPoints2)
示例#20
0
 def __init__(self):
     plateScale = afwGeom.Angle(20, afwGeom.arcseconds)  # plate scale, in angle on sky/mm
     # Radial distortion is modeled as a radial polynomial that converts from focal plane (in mm)
     # to field angle (in radians). Thus the coefficients are:
     # C0: always 0, for continuity at the center of the focal plane; units are rad
     # C1: 1/plateScale; units are rad/mm
     # C2: usually 0; units are rad/mm^2
     # C3: radial distortion; units are rad/mm^3
     radialCoeff = np.array([0.0, 1.0, 0.0, 0.925]) / plateScale.asRadians()
     fieldAngleToFocalPlane = afwGeom.makeRadialTransform(radialCoeff)
     focalPlaneToFieldAngle = fieldAngleToFocalPlane.inverted()
     cameraTransformMap = cameraGeom.TransformMap(cameraGeom.FOCAL_PLANE,
                                                  {cameraGeom.FIELD_ANGLE: focalPlaneToFieldAngle})
     detectorList = self._makeDetectorList(focalPlaneToFieldAngle)
     cameraGeom.Camera.__init__(self, "test", detectorList, cameraTransformMap)
示例#21
0
    def testLargeDistortion(self):
        # This transform is about as extreme as I can get:
        # using 0.0005 in the last value appears to produce numerical issues.
        # It produces a maximum deviation of 459 pixels, which should be sufficient.
        pixelsToTanPixels = afwGeom.makeRadialTransform([0.0, 1.1, 0.0004])
        self.distortedWcs = afwGeom.makeModifiedWcs(pixelTransform=pixelsToTanPixels,
                                                    wcs=self.wcs,
                                                    modifyActualPixels=False)

        def applyDistortion(src):
            out = src.table.copyRecord(src)
            out.set(out.table.getCentroidKey(),
                    pixelsToTanPixels.applyInverse(src.getCentroid()))
            return out

        self.singleTestInstance(self.filename, applyDistortion)
示例#22
0
    def __new__(cls):
        plateScale = geom.Angle(20, geom.arcseconds)  # plate scale, in angle on sky/mm
        # Radial distortion is modeled as a radial polynomial that converts from focal plane (in mm)
        # to field angle (in radians). Thus the coefficients are:
        # C0: always 0, for continuity at the center of the focal plane; units are rad
        # C1: 1/plateScale; units are rad/mm
        # C2: usually 0; units are rad/mm^2
        # C3: radial distortion; units are rad/mm^3
        radialCoeff = np.array([0.0, 1.0, 0.0, 0.925]) / plateScale.asRadians()
        fieldAngleToFocalPlane = afwGeom.makeRadialTransform(radialCoeff)
        focalPlaneToFieldAngle = fieldAngleToFocalPlane.inverted()

        camera = cameraGeom.Camera.Builder("test")
        cls._makeDetectors(camera, focalPlaneToFieldAngle)
        camera.setTransformFromFocalPlaneTo(cameraGeom.FIELD_ANGLE, focalPlaneToFieldAngle)
        return camera.finish()
    def testLargeDistortion(self):
        # This transform is about as extreme as I can get:
        # using 0.0005 in the last value appears to produce numerical issues.
        # It produces a maximum deviation of 459 pixels, which should be sufficient.
        pixelsToTanPixels = afwGeom.makeRadialTransform([0.0, 1.1, 0.0004])
        self.distortedWcs = afwGeom.makeModifiedWcs(pixelTransform=pixelsToTanPixels,
                                                    wcs=self.wcs,
                                                    modifyActualPixels=False)

        def applyDistortion(src):
            out = src.table.copyRecord(src)
            out.set(out.table.getCentroidKey(),
                    pixelsToTanPixels.applyInverse(src.getCentroid()))
            return out

        self.singleTestInstance(self.filename, applyDistortion)
示例#24
0
 def __init__(self):
     """Construct a TestCamera
     """
     plateScale = afwGeom.Angle(
         13.55, afwGeom.arcseconds)  # plate scale, in angle on sky/mm
     radialDistortion = 0.  # radial distortion in mm/rad^2
     radialCoeff = numpy.array(
         (0.0, 1.0, 0.0, radialDistortion)) / plateScale.asRadians()
     focalPlaneToFieldAngle = afwGeom.makeRadialTransform(radialCoeff)
     fieldAngleToFocalPlane = focalPlaneToFieldAngle.inverted()
     cameraTransformMap = cameraGeom.TransformMap(
         cameraGeom.FOCAL_PLANE,
         {cameraGeom.FIELD_ANGLE: fieldAngleToFocalPlane})
     detectorList = self._makeDetectorList(fieldAngleToFocalPlane,
                                           plateScale)
     cameraGeom.Camera.__init__(self, "monocam", detectorList,
                                cameraTransformMap)
示例#25
0
    def testRadial(self):
        """Test radial = radial Transform
        """
        radialFactory = afwGeom.transformRegistry["radial"]
        radialConfig = radialFactory.ConfigClass()
        radialConfig.coeffs = (0.0, 8.5165e-05, 0.0, -4.5014e-12)

        def check(transform):
            self.checkRadial(transform, radialConfig.coeffs)
        self.checkGenericTransform(radialFactory, radialConfig, check)

        invertibleCoeffs = (0.0, 1.0, 0.05)
        inverseCoeffs = (0.0, 1.0, -0.05, 0.005, -0.000625, 0.0000875,
                         -1.3125e-5, 2.0625e-6, -3.3515625e-7, 5.5859375e-8,
                         -9.49609375e-9, 1.640234375e-9, -2.870410156e-10)
        transform = afwGeom.makeRadialTransform(invertibleCoeffs,
                                                inverseCoeffs)
        self.checkRadialInvertible(transform, invertibleCoeffs)
    def testRadial(self):
        """Test radial = radial Transform
        """
        radialFactory = afwGeom.transformRegistry["radial"]
        radialConfig = radialFactory.ConfigClass()
        radialConfig.coeffs = (0.0, 8.5165e-05, 0.0, -4.5014e-12)

        def check(transform):
            self.checkRadial(transform, radialConfig.coeffs)
        self.checkGenericTransform(radialFactory, radialConfig, check)

        invertibleCoeffs = (0.0, 1.0, 0.05)
        inverseCoeffs = (0.0, 1.0, -0.05, 0.005, -0.000625, 0.0000875,
                         -1.3125e-5, 2.0625e-6, -3.3515625e-7, 5.5859375e-8,
                         -9.49609375e-9, 1.640234375e-9, -2.870410156e-10)
        transform = afwGeom.makeRadialTransform(invertibleCoeffs,
                                                inverseCoeffs)
        self.checkRadialInvertible(transform, invertibleCoeffs)
示例#27
0
    def testMakeModifiedWcsNoActualPixels(self):
        """Test makeModifiedWcs on a SkyWcs that has no ACTUAL_PIXELS frame
        """
        cdMatrix = makeCdMatrix(scale=self.scale)
        originalWcs = makeSkyWcs(crpix=self.crpix,
                                 crval=self.crvalList[0],
                                 cdMatrix=cdMatrix)
        originalFrameDict = originalWcs.getFrameDict()

        # make an arbitrary but reasonable transform to insert using makeModifiedWcs
        pixelTransform = makeRadialTransform([0.0, 1.0, 0.0, 0.0011])
        # the result of the insertion should be as follows
        desiredPixelsToSky = pixelTransform.then(originalWcs.getTransform())

        pixPointList = (  # arbitrary but reasonable
            Point2D(0.0, 0.0),
            Point2D(1000.0, 0.0),
            Point2D(0.0, 2000.0),
            Point2D(-1111.0, -2222.0),
        )
        for modifyActualPixels in (False, True):
            modifiedWcs = makeModifiedWcs(
                pixelTransform=pixelTransform,
                wcs=originalWcs,
                modifyActualPixels=modifyActualPixels)
            modifiedFrameDict = modifiedWcs.getFrameDict()
            skyList = modifiedWcs.pixelToSky(pixPointList)

            # compare pixels to sky
            desiredSkyList = desiredPixelsToSky.applyForward(pixPointList)
            self.assertSpherePointListsAlmostEqual(skyList, desiredSkyList)

            # compare pixels to IWC
            pixelsToIwc = TransformPoint2ToPoint2(
                modifiedFrameDict.getMapping("PIXELS", "IWC"))
            desiredPixelsToIwc = TransformPoint2ToPoint2(
                pixelTransform.getMapping().then(
                    originalFrameDict.getMapping("PIXELS", "IWC")))
            self.assertPairListsAlmostEqual(
                pixelsToIwc.applyForward(pixPointList),
                desiredPixelsToIwc.applyForward(pixPointList))

            self.checkNonFitsWcs(modifiedWcs)
示例#28
0
 def __init__(self):
     """Construct a TestCamera
     """
     plateScale = afwGeom.Angle(
         20, afwGeom.arcseconds)  # plate scale, in angle on sky/mm
     # Radial distortion is modeled as a radial polynomial that converts from focal plane (in mm)
     # to field angle (in radians). Thus the coefficients are:
     # C0: always 0, for continuity at the center of the focal plane; units are rad
     # C1: 1/plateScale; units are rad/mm
     # C2: usually 0; units are rad/mm^2
     # C3: radial distortion; units are rad/mm^3
     radialCoeff = np.array([0.0, 1.0, 0.0, 0.925]) / plateScale.asRadians()
     fieldAngleToFocalPlane = afwGeom.makeRadialTransform(radialCoeff)
     focalPlaneToFieldAngle = fieldAngleToFocalPlane.getInverse()
     cameraTransformMap = cameraGeom.TransformMap(
         cameraGeom.FOCAL_PLANE,
         {cameraGeom.FIELD_ANGLE: focalPlaneToFieldAngle})
     detectorList = self._makeDetectorList(focalPlaneToFieldAngle)
     cameraGeom.Camera.__init__(self, "test", detectorList,
                                cameraTransformMap)
示例#29
0
    def __init__(self, cameraYamlFile):
        with open(cameraYamlFile) as fd:
            cameraParams = yaml.load(fd, Loader=yaml.Loader)

        plateScale = afwGeom.Angle(cameraParams["plateScale"],
                                   afwGeom.arcseconds)
        # radial coefficients of the form [0, no units, 1/rad but usually 0, 1/rad^2, ...]
        # Radial distortion is modeled as a radial polynomial that converts from focal plane radius (in mm)
        # to field angle (in radians). The coefficients are divided by the plate scale (in mm/radians)
        # meaning that C1 is always 1.
        radialCoeffs = np.array(
            cameraParams["radialCoeffs"]) / plateScale.asRadians()
        fieldAngleToFocalPlane = afwGeom.makeRadialTransform(radialCoeffs)
        focalPlaneToFieldAngle = fieldAngleToFocalPlane.getInverse()
        cameraTransformMap = cameraGeom.TransformMap(
            cameraGeom.FOCAL_PLANE,
            {cameraGeom.FIELD_ANGLE: focalPlaneToFieldAngle})
        detectorList = self._makeDetectorList(cameraParams["CCDs"],
                                              focalPlaneToFieldAngle)
        cameraGeom.Camera.__init__(self, cameraParams["name"], detectorList,
                                   cameraTransformMap)
示例#30
0
    def makeCameraGeom(self):
        """Make a camera geometry.

        Returns
        -------
        cameraGeom : `lsst.afw.cameraGeom.Camera`
            Camera geometry.

        Notes
        -----
        There is one field per entry in self.detectorFracPosList
        with specifications set by self.detectorWidthPix,
        self.detectorHeightPix, and self.pixelSizeMm.

        The plate scale is set by self.plateScale
        and the amount of optical distortion is fixed.

        All detectors have the same shape (unlike LSST) and orientation
        (unlike HSC). Varying these is not necessary for testing the CBP
        and having all detectors the same simplifies the code.
        """
        radialCoeff = np.array([0.0, 1.0, 0.0, 0.925
                                ]) / self.plateScale.asRadians()
        fieldAngleToFocalPlane = makeRadialTransform(radialCoeff)
        focalPlaneToFieldAngle = fieldAngleToFocalPlane.inverted()

        cameraBuilder = Camera.Builder("testCamera")
        cameraBuilder.setTransformFromFocalPlaneTo(FIELD_ANGLE,
                                                   focalPlaneToFieldAngle)
        ampBuilder = self._makeAmpBuilder()

        for i, fpPos in enumerate(self.detectorFracPosList):
            detectorConfig = self._makeDetectorConfig(id=i, fpPos=fpPos)
            addDetectorBuilderFromConfig(cameraBuilder, detectorConfig,
                                         [ampBuilder], focalPlaneToFieldAngle)

        return cameraBuilder.finish()
示例#31
0
    def testFlatFocalPlane(self):
        """Test an undistorted focal plane (with rectangular pixels)
        """
        bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0),
                             afwGeom.Extent2I(1000, 1000))
        pixelSizeMm = afwGeom.Extent2D(0.02, 0.03)
        plateScale = 25.0  # arcsec/mm
        yaw = afwGeom.Angle(20, afwGeom.degrees)
        # focal-plane position of ref position on detector (mm)
        fpPosition = afwGeom.Point2D(50, 25)
        # ref position on detector (pos of lower left corner)
        refPoint = afwGeom.Point2D(-0.5, -0.5)
        orientation = cameraGeom.Orientation(
            fpPosition,
            refPoint,
            yaw,
        )
        plateScaleRad = afwGeom.Angle(plateScale,
                                      afwGeom.arcseconds).asRadians()
        focalPlaneToField = afwGeom.makeRadialTransform((0.0, plateScaleRad))

        pixelToTanPixel = makePixelToTanPixel(
            bbox=bbox,
            orientation=orientation,
            focalPlaneToField=focalPlaneToField,
            pixelSizeMm=pixelSizeMm,
        )

        # with no distortion, this should be a unity transform
        for pointPix in (
                afwGeom.Point2D(0, 0),
                afwGeom.Point2D(1000, 2000),
                afwGeom.Point2D(-100.5, 27.23),
        ):
            pointTanPix = pixelToTanPixel.applyForward(pointPix)
            self.assertPairsAlmostEqual(pointTanPix, pointPix)
示例#32
0
    def testRadial(self):
        """Test fit with radial distortion

        The offset comes from the fact that the CCD is not centered
        """
        self.doTest(afwGeom.makeRadialTransform([0, 1.01, 1e-7]))
示例#33
0
 def setUp(self):
     self.nativeSys = cameraGeom.FOCAL_PLANE
     self.fieldTransform = afwGeom.makeRadialTransform([0, 0.5, 0.005])
     transforms = {cameraGeom.FIELD_ANGLE: self.fieldTransform}
     self.transformMap = cameraGeom.TransformMap(self.nativeSys, transforms)
示例#34
0
 def setUp(self):
     self.nativeSys = cameraGeom.FOCAL_PLANE
     self.fieldTransform = afwGeom.makeRadialTransform([0, 0.5, 0.005])
     transforms = {cameraGeom.FIELD_ANGLE: self.fieldTransform}
     self.transformMap = cameraGeom.TransformMap(
         self.nativeSys, transforms)
示例#35
0
    def __init__(
        self,
        name="detector 1",
        id=1,
        detType=SCIENCE,
        serial="xkcd722",
        bbox=None,  # do not use mutable objects as defaults
        numAmps=3,
        pixelSize=(0.02, 0.02),
        ampExtent=(5, 6),
        orientation=Orientation(),
        plateScale=20.0,
        radialDistortion=0.925,
        crosstalk=None,
        modFunc=None,
        physicalType="CCD",
    ):
        # note that (0., 0.) for the reference position is the center of the
        # first pixel
        self.name = name
        self.id = int(id)
        self.type = detType
        self.serial = serial
        if bbox is None:
            bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
                                   lsst.geom.Extent2I(1024, 1048))
        self.bbox = bbox
        self.pixelSize = lsst.geom.Extent2D(*pixelSize)
        self.ampExtent = lsst.geom.Extent2I(*ampExtent)
        self.plateScale = float(plateScale)
        self.radialDistortion = float(radialDistortion)
        schema = afwTable.AmpInfoTable.makeMinimalSchema()
        self.ampInfo = afwTable.AmpInfoCatalog(schema)
        for i in range(numAmps):
            record = self.ampInfo.addNew()
            ampName = "amp %d" % (i + 1, )
            record.setName(ampName)
            record.setBBox(
                lsst.geom.Box2I(lsst.geom.Point2I(-1, 1), self.ampExtent))
            record.setGain(1.71234e3)
            record.setReadNoise(0.521237e2)
            record.setReadoutCorner(afwTable.LL)
            record.setHasRawInfo(False)
        self.orientation = orientation

        # compute TAN_PIXELS transform
        pScaleRad = lsst.geom.arcsecToRad(self.plateScale)
        radialDistortCoeffs = [
            0.0, 1.0 / pScaleRad, 0.0, self.radialDistortion / pScaleRad
        ]
        focalPlaneToField = afwGeom.makeRadialTransform(radialDistortCoeffs)
        pixelToTanPixel = makePixelToTanPixel(
            bbox=self.bbox,
            orientation=self.orientation,
            focalPlaneToField=focalPlaneToField,
            pixelSizeMm=self.pixelSize,
        )

        self.transMap = {
            FOCAL_PLANE:
            self.orientation.makePixelFpTransform(self.pixelSize),
            CameraSys(TAN_PIXELS, self.name):
            pixelToTanPixel,
            CameraSys(ACTUAL_PIXELS, self.name):
            afwGeom.makeRadialTransform([0, 0.95, 0.01]),
        }
        if crosstalk is None:
            crosstalk = [[0.0 for _ in range(numAmps)] for _ in range(numAmps)]
        self.crosstalk = crosstalk
        self.physicalType = physicalType
        if modFunc:
            modFunc(self)
        self.detector = Detector(
            self.name,
            self.id,
            self.type,
            self.serial,
            self.bbox,
            self.ampInfo,
            self.orientation,
            self.pixelSize,
            self.transMap,
            np.array(self.crosstalk, dtype=np.float32),
            self.physicalType,
        )
示例#36
0
    def __init__(
        self,
        name="detector 1",
        id=1,
        detType=SCIENCE,
        serial="xkcd722",
        bbox=None,  # do not use mutable objects as defaults
        numAmps=3,
        pixelSize=(0.02, 0.02),
        ampExtent=(5, 6),
        orientation=Orientation(),
        plateScale=20.0,
        radialDistortion=0.925,
        crosstalk=None,
        modFunc=None,
    ):
        """!Construct a DetectorWrapper

        @param[in] name  detector name
        @param[in] id  detector ID (int)
        @param[in] detType  detector type (an lsst.afw.cameraGeom.DetectorType)
        @param[in] serial  serial "number" (a string)
        @param[in] bbox  bounding box; defaults to (0, 0), (1024x1024) (an lsst.geom.Box2I)
        @param[in] numAmps  number of amplifiers (int)
        @param[in] pixelSize  pixel size (mm) (an lsst.geom.Point2D)
        @param[in] ampExtent  dimensions of amplifier image bbox (an lsst.geom.Extent2I)
        @param[in] orientation  orientation of CCC in focal plane (lsst.afw.cameraGeom.Orientation)
        @param[in] plateScale  plate scale in arcsec/mm; 20.0 is for LSST
        @param[in] radialDistortion  radial distortion, in mm/rad^2
            (the r^3 coefficient of the radial distortion polynomial
            that converts FIELD_ANGLE in radians to FOCAL_PLANE in mm);
            0.925 is the value Dave Monet measured for lsstSim data
        @param[in] crosstalk  crosstalk coefficient matrix
        @param[in] modFunc  a function that can modify attributes just before constructing the detector;
            modFunc receives one argument: a DetectorWrapper with all attributes except detector set.
        """
        # note that (0., 0.) for the reference position is the center of the
        # first pixel
        self.name = name
        self.id = int(id)
        self.type = detType
        self.serial = serial
        if bbox is None:
            bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
                                   lsst.geom.Extent2I(1024, 1048))
        self.bbox = bbox
        self.pixelSize = lsst.geom.Extent2D(*pixelSize)
        self.ampExtent = lsst.geom.Extent2I(*ampExtent)
        self.plateScale = float(plateScale)
        self.radialDistortion = float(radialDistortion)
        schema = afwTable.AmpInfoTable.makeMinimalSchema()
        self.ampInfo = afwTable.AmpInfoCatalog(schema)
        for i in range(numAmps):
            record = self.ampInfo.addNew()
            ampName = "amp %d" % (i + 1, )
            record.setName(ampName)
            record.setBBox(
                lsst.geom.Box2I(lsst.geom.Point2I(-1, 1), self.ampExtent))
            record.setGain(1.71234e3)
            record.setReadNoise(0.521237e2)
            record.setReadoutCorner(afwTable.LL)
            record.setHasRawInfo(False)
        self.orientation = orientation

        # compute TAN_PIXELS transform
        pScaleRad = lsst.geom.arcsecToRad(self.plateScale)
        radialDistortCoeffs = [
            0.0, 1.0 / pScaleRad, 0.0, self.radialDistortion / pScaleRad
        ]
        focalPlaneToField = afwGeom.makeRadialTransform(radialDistortCoeffs)
        pixelToTanPixel = makePixelToTanPixel(
            bbox=self.bbox,
            orientation=self.orientation,
            focalPlaneToField=focalPlaneToField,
            pixelSizeMm=self.pixelSize,
        )

        self.transMap = {
            FOCAL_PLANE:
            self.orientation.makePixelFpTransform(self.pixelSize),
            CameraSys(TAN_PIXELS, self.name):
            pixelToTanPixel,
            CameraSys(ACTUAL_PIXELS, self.name):
            afwGeom.makeRadialTransform([0, 0.95, 0.01]),
        }
        if crosstalk is None:
            crosstalk = [[0.0 for _ in range(numAmps)] for _ in range(numAmps)]
        self.crosstalk = crosstalk
        if modFunc:
            modFunc(self)
        self.detector = Detector(
            self.name,
            self.id,
            self.type,
            self.serial,
            self.bbox,
            self.ampInfo,
            self.orientation,
            self.pixelSize,
            self.transMap,
            np.array(self.crosstalk, dtype=np.float32),
        )
示例#37
0
    def testMakeModifiedWcsWithActualPixels(self):
        """Test makeModifiedWcs on a SkyWcs that has an ACTUAL_PIXELS frame
        """
        cdMatrix = makeCdMatrix(scale=self.scale)
        baseWcs = makeSkyWcs(crpix=self.crpix,
                             crval=self.crvalList[0],
                             cdMatrix=cdMatrix)
        # model actual pixels to pixels as an arbitrary zoom factor;
        # this is not realistic, but is fine for a unit test
        actualPixelsToPixels = TransformPoint2ToPoint2(ast.ZoomMap(2, 0.72))
        originalWcs = addActualPixelsFrame(baseWcs, actualPixelsToPixels)
        originalFrameDict = originalWcs.getFrameDict()

        # make an arbitrary but reasonable transform to insert using makeModifiedWcs
        pixelTransform = makeRadialTransform([0.0, 1.0, 0.0, 0.0011
                                              ])  # arbitrary but reasonable

        pixPointList = (  # arbitrary but reasonable
            Point2D(0.0, 0.0),
            Point2D(1000.0, 0.0),
            Point2D(0.0, 2000.0),
            Point2D(-1111.0, -2222.0),
        )
        for modifyActualPixels in (True, False):
            modifiedWcs = makeModifiedWcs(
                pixelTransform=pixelTransform,
                wcs=originalWcs,
                modifyActualPixels=modifyActualPixels)
            modifiedFrameDict = modifiedWcs.getFrameDict()
            self.assertEqual(
                modifiedFrameDict.getFrame(modifiedFrameDict.BASE).domain,
                "ACTUAL_PIXELS")
            modifiedActualPixelsToPixels = \
                TransformPoint2ToPoint2(modifiedFrameDict.getMapping("ACTUAL_PIXELS", "PIXELS"))
            modifiedPixelsToIwc = TransformPoint2ToPoint2(
                modifiedFrameDict.getMapping("PIXELS", "IWC"))

            # compare pixels to sky
            skyList = modifiedWcs.pixelToSky(pixPointList)
            if modifyActualPixels:
                desiredPixelsToSky = pixelTransform.then(
                    originalWcs.getTransform())
            else:
                originalPixelsToSky = \
                    TransformPoint2ToSpherePoint(originalFrameDict.getMapping("PIXELS", "SKY"))
                desiredPixelsToSky = actualPixelsToPixels.then(
                    pixelTransform).then(originalPixelsToSky)
            desiredSkyList = desiredPixelsToSky.applyForward(pixPointList)
            self.assertSpherePointListsAlmostEqual(skyList, desiredSkyList)

            # compare ACTUAL_PIXELS to PIXELS and PIXELS to IWC
            if modifyActualPixels:
                # check that ACTUAL_PIXELS to PIXELS has been modified as expected
                desiredActualPixelsToPixels = pixelTransform.then(
                    actualPixelsToPixels)
                self.assertPairListsAlmostEqual(
                    modifiedActualPixelsToPixels.applyForward(pixPointList),
                    desiredActualPixelsToPixels.applyForward(pixPointList))

                # check that PIXELS to IWC is unchanged
                originalPixelsToIwc = TransformPoint2ToPoint2(
                    originalFrameDict.getMapping("PIXELS", "IWC"))
                self.assertPairListsAlmostEqual(
                    modifiedPixelsToIwc.applyForward(pixPointList),
                    originalPixelsToIwc.applyForward(pixPointList))

            else:
                # check that ACTUAL_PIXELS to PIXELS is unchanged
                self.assertPairListsAlmostEqual(
                    actualPixelsToPixels.applyForward(pixPointList),
                    actualPixelsToPixels.applyForward(pixPointList))

                # check that PIXELS to IWC has been modified as expected
                desiredPixelsToIwc = TransformPoint2ToPoint2(
                    pixelTransform.getMapping().then(
                        originalFrameDict.getMapping("PIXELS", "IWC")))
                self.assertPairListsAlmostEqual(
                    modifiedPixelsToIwc.applyForward(pixPointList),
                    desiredPixelsToIwc.applyForward(pixPointList))

            self.checkNonFitsWcs(modifiedWcs)
    def testRadial(self):
        """Test fit with radial distortion

        The offset comes from the fact that the CCD is not centered
        """
        self.doTest(afwGeom.makeRadialTransform([0, 1.01, 1e-7]))
示例#39
0
    def testSimpleCurvedFocalPlane(self):
        """Test a trivial curved focal plane with square pixels

        The CCD's lower left pixel is centered on the boresight
        pupil center = focal plane center
        CCD x is along focal plane x
        """
        bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0),
                             afwGeom.Extent2I(1000, 1000))
        pixelSizeMm = afwGeom.Extent2D(0.02, 0.02)
        plateScale = 25.0  # arcsec/mm
        yaw = 0 * afwGeom.degrees
        # focal-plane position of ref position on detector (mm)
        fpPosition = afwGeom.Point2D(0, 0)
        # ref position on detector (pos of lower left corner)
        refPoint = afwGeom.Point2D(0, 0)
        orientation = cameraGeom.Orientation(
            fpPosition,
            refPoint,
            yaw,
        )
        pixelToFocalPlane = orientation.makePixelFpTransform(pixelSizeMm)
        plateScaleRad = afwGeom.Angle(  # rad/mm
            plateScale, afwGeom.arcseconds).asRadians()
        focalPlaneToField = afwGeom.makeRadialTransform(
            (0.0, plateScaleRad, 0.0, 0.001 * plateScaleRad))
        pixelToField = pixelToFocalPlane.then(focalPlaneToField)

        pixelToTanPixel = makePixelToTanPixel(
            bbox=bbox,
            orientation=orientation,
            focalPlaneToField=focalPlaneToField,
            pixelSizeMm=pixelSizeMm,
        )

        # field center should be pixel position 0, 0 and tan pixel position 0,
        # 0
        pixAtFieldCtr = pixelToField.applyInverse(afwGeom.Point2D(0, 0))
        self.assertPairsAlmostEqual(pixAtFieldCtr, [0, 0])
        tanPixAtFieldCr = pixelToTanPixel.applyForward(pixAtFieldCtr)
        self.assertPairsAlmostEqual(tanPixAtFieldCr, [0, 0])

        # build same camera geometry transforms without optical distortion
        focalPlaneToFieldNoDistortion = afwGeom.makeRadialTransform(
            (0.0, plateScaleRad))
        pixelToFieldNoDistortion = pixelToFocalPlane.then(
            focalPlaneToFieldNoDistortion)

        for x in (100, 200, 1000):
            for y in (100, 500, 800):
                pixPos = afwGeom.Point2D(x, y)
                tanPixPos = pixelToTanPixel.applyForward(pixPos)
                # pix to tan pix should be radial
                self.assertAlmostEqual(
                    math.atan2(pixPos[1], pixPos[0]),
                    math.atan2(tanPixPos[1], tanPixPos[0]),
                )

                # for a given field angle (which, together with a pointing, gives a position on the sky):
                # - field angle to pixels gives pixPos
                # - undistorted field anle to pixels gives tanPixPos
                fieldPos = pixelToField.applyForward(pixPos)
                desTanPixPos = pixelToFieldNoDistortion.applyInverse(fieldPos)
                self.assertPairsAlmostEqual(desTanPixPos, tanPixPos)
 def testRadial(self):
     """Add a radial transform"""
     for order in (4, 5, 6):
         self.doTest("testRadial", afwGeom.makeRadialTransform([0, 1.001, 0.000003]), order=order,
                     doPlot=False)
示例#41
0
    def __init__(self,
                 name="detector 1",
                 id=1,
                 detType=SCIENCE,
                 serial="xkcd722",
                 bbox=None,    # do not use mutable objects as defaults
                 numAmps=3,
                 pixelSize=(0.02, 0.02),
                 ampExtent=(5, 6),
                 orientation=Orientation(),
                 plateScale=20.0,
                 radialDistortion=0.925,
                 crosstalk=None,
                 modFunc=None,
                 physicalType="CCD",
                 ):
        # note that (0., 0.) for the reference position is the center of the
        # first pixel
        self.name = name
        self.id = int(id)
        self.type = detType
        self.serial = serial
        if bbox is None:
            bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(1024, 1048))
        self.bbox = bbox
        self.pixelSize = lsst.geom.Extent2D(*pixelSize)
        self.ampExtent = lsst.geom.Extent2I(*ampExtent)
        self.plateScale = float(plateScale)
        self.radialDistortion = float(radialDistortion)
        schema = afwTable.AmpInfoTable.makeMinimalSchema()
        self.ampInfo = afwTable.AmpInfoCatalog(schema)
        for i in range(numAmps):
            record = self.ampInfo.addNew()
            ampName = "amp %d" % (i + 1,)
            record.setName(ampName)
            record.setBBox(lsst.geom.Box2I(lsst.geom.Point2I(-1, 1), self.ampExtent))
            record.setGain(1.71234e3)
            record.setReadNoise(0.521237e2)
            record.setReadoutCorner(afwTable.LL)
            record.setHasRawInfo(False)
        self.orientation = orientation

        # compute TAN_PIXELS transform
        pScaleRad = lsst.geom.arcsecToRad(self.plateScale)
        radialDistortCoeffs = [0.0, 1.0/pScaleRad,
                               0.0, self.radialDistortion/pScaleRad]
        focalPlaneToField = afwGeom.makeRadialTransform(radialDistortCoeffs)
        pixelToTanPixel = makePixelToTanPixel(
            bbox=self.bbox,
            orientation=self.orientation,
            focalPlaneToField=focalPlaneToField,
            pixelSizeMm=self.pixelSize,
        )

        self.transMap = {
            FOCAL_PLANE: self.orientation.makePixelFpTransform(self.pixelSize),
            CameraSys(TAN_PIXELS, self.name): pixelToTanPixel,
            CameraSys(ACTUAL_PIXELS, self.name): afwGeom.makeRadialTransform([0, 0.95, 0.01]),
        }
        if crosstalk is None:
            crosstalk = [[0.0 for _ in range(numAmps)] for _ in range(numAmps)]
        self.crosstalk = crosstalk
        self.physicalType = physicalType
        if modFunc:
            modFunc(self)
        self.detector = Detector(
            self.name,
            self.id,
            self.type,
            self.serial,
            self.bbox,
            self.ampInfo,
            self.orientation,
            self.pixelSize,
            self.transMap,
            np.array(self.crosstalk, dtype=np.float32),
            self.physicalType,
        )
示例#42
0
    def __init__(
            self,
            name="detector 1",
            id=1,
            detType=DetectorType.SCIENCE,
            serial="xkcd722",
            bbox=None,  # do not use mutable objects as defaults
            numAmps=3,
            pixelSize=(0.02, 0.02),
            ampExtent=(5, 6),
            orientation=Orientation(),
            plateScale=20.0,
            radialDistortion=0.925,
            crosstalk=None,
            modFunc=None,
            physicalType="CCD",
            cameraBuilder=None):
        # note that (0., 0.) for the reference position is the center of the
        # first pixel
        self.name = name
        self.id = int(id)
        self.type = detType
        self.serial = serial
        if bbox is None:
            bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
                                   lsst.geom.Extent2I(1024, 1048))
        self.bbox = bbox
        self.pixelSize = lsst.geom.Extent2D(*pixelSize)
        self.ampExtent = lsst.geom.Extent2I(*ampExtent)
        self.plateScale = float(plateScale)
        self.orientation = orientation
        self.radialDistortion = float(radialDistortion)

        # compute TAN_PIXELS transform
        pScaleRad = lsst.geom.arcsecToRad(self.plateScale)
        radialDistortCoeffs = [
            0.0, 1.0 / pScaleRad, 0.0, self.radialDistortion / pScaleRad
        ]
        focalPlaneToField = afwGeom.makeRadialTransform(radialDistortCoeffs)
        pixelToTanPixel = makePixelToTanPixel(
            bbox=self.bbox,
            orientation=self.orientation,
            focalPlaneToField=focalPlaneToField,
            pixelSizeMm=self.pixelSize,
        )
        tanPixelSys = CameraSys(TAN_PIXELS, self.name)
        actualPixelSys = CameraSys(ACTUAL_PIXELS, self.name)
        self.transMap = {
            FOCAL_PLANE: self.orientation.makePixelFpTransform(self.pixelSize),
            tanPixelSys: pixelToTanPixel,
            actualPixelSys: afwGeom.makeRadialTransform([0, 0.95, 0.01]),
        }
        if crosstalk is None:
            crosstalk = [[0.0 for _ in range(numAmps)] for _ in range(numAmps)]
        self.crosstalk = crosstalk
        self.physicalType = physicalType
        if cameraBuilder is None:
            cameraBuilder = Camera.Builder("CameraForDetectorWrapper")
        self.ampList = []
        for i in range(numAmps):
            ampBuilder = Amplifier.Builder()
            ampName = f"amp {i + 1}"
            ampBuilder.setName(ampName)
            ampBuilder.setBBox(
                lsst.geom.Box2I(lsst.geom.Point2I(-1, 1), self.ampExtent))
            ampBuilder.setGain(1.71234e3)
            ampBuilder.setReadNoise(0.521237e2)
            ampBuilder.setReadoutCorner(ReadoutCorner.LL)
            self.ampList.append(ampBuilder)
        if modFunc:
            modFunc(self)
        detectorBuilder = cameraBuilder.add(self.name, self.id)
        detectorBuilder.setType(self.type)
        detectorBuilder.setSerial(self.serial)
        detectorBuilder.setPhysicalType(self.physicalType)
        detectorBuilder.setBBox(self.bbox)
        detectorBuilder.setOrientation(self.orientation)
        detectorBuilder.setPixelSize(self.pixelSize)
        detectorBuilder.setTransformFromPixelsTo(tanPixelSys,
                                                 self.transMap[tanPixelSys])
        detectorBuilder.setTransformFromPixelsTo(actualPixelSys,
                                                 self.transMap[actualPixelSys])
        detectorBuilder.setCrosstalk(np.array(self.crosstalk,
                                              dtype=np.float32))
        for ampBuilder in self.ampList:
            detectorBuilder.append(ampBuilder)
        camera = cameraBuilder.finish()
        self.detector = camera[self.name]