def fromCamera(cls, config, camera): """Construct from a camera object Parameters ---------- config : `FocalPlaneBackgroundConfig` Configuration for measuring backgrounds. camera : `lsst.afw.cameraGeom.Camera` Camera for which to measure backgrounds. """ cameraBox = afwGeom.Box2D() for ccd in camera: for point in ccd.getCorners(afwCameraGeom.FOCAL_PLANE): cameraBox.include(point) width, height = cameraBox.getDimensions() # Offset so that we run from zero offset = afwGeom.Extent2D(cameraBox.getMin())*-1 # Add an extra pixel buffer on either side dims = afwGeom.Extent2I(int(numpy.ceil(width/config.xSize)) + 2, int(numpy.ceil(height/config.ySize)) + 2) # Transform takes us from focal plane coordinates --> sample coordinates transform = (afwGeom.AffineTransform.makeTranslation(afwGeom.Extent2D(1, 1))* afwGeom.AffineTransform.makeScaling(1.0/config.xSize, 1.0/config.ySize)* afwGeom.AffineTransform.makeTranslation(offset)) return cls(config, dims, afwGeom.makeTransform(transform))
def makePixelToTanPixel(bbox, orientation, focalPlaneToField, pixelSizeMm): """!Make a Transform whose forward direction converts PIXELS to TAN_PIXELS for one detector PIXELS and TAN_PIXELS are defined in @ref afwCameraGeomCoordSys in doc/cameraGeom.dox @param[in] bbox detector bounding box (an lsst.afw.geom.Box2I) @param[in] orientation orientation of detector in focal plane (an lsst.afw.cameraGeom.Orientation) @param[in] focalPlaneToField an lsst.afw.geom.Transform that converts from focal plane (mm) to field angle coordinates (radians) in the forward direction @param[in] pixelSizeMm size of the pixel in mm in X and Y (an lsst.afw.geom.Extent2D) @return a TransformPoint2ToPoint2 whose forward direction converts PIXELS to TAN_PIXELS """ pixelToFocalPlane = orientation.makePixelFpTransform(pixelSizeMm) pixelToField = pixelToFocalPlane.then(focalPlaneToField) # fieldToTanPix is affine and matches fieldToPix at field center # Note: focal plane to field angle is typically a radial transform, # and linearizing the inverse transform of that may fail, # so linearize the forward direction instead. (pixelToField is pixelToFocalPlane, # an affine transform, followed by focalPlaneToField, # so the same consideration applies to pixelToField) pixAtFieldCtr = pixelToField.applyInverse(afwGeom.Point2D(0, 0)) tanPixToFieldAffine = afwGeom.linearizeTransform(pixelToField, pixAtFieldCtr) fieldToTanPix = afwGeom.makeTransform(tanPixToFieldAffine.invert()) return pixelToField.then(fieldToTanPix)
def testTransform2(self): if display: import matplotlib.pyplot as plt scale = 2.0 shift = lsst.geom.Extent2D(3.0, 4.0) affineTransform = lsst.geom.AffineTransform.makeTranslation(shift) * \ lsst.geom.AffineTransform.makeScaling(scale) transform22 = afwGeom.makeTransform(affineTransform) for num in range(3, 30): small = self.polygon(num, 1.0, 0.0, 0.0) large1 = small.transform(affineTransform) large2 = small.transform(transform22) expect = self.polygon(num, scale, shift[0], shift[1]) self.assertEqual(large1, expect) self.assertEqual(large2, expect) if display: axes = small.plot(c='k') axes.set_aspect("equal") axes.set_title("AffineTransform: Polygon nside=%d" % num) large1.plot(axes, c='b') if not doPause: try: plt.pause(2) plt.close() except Exception: print( "%s: plt.pause() failed. Please close plots when done." % self.__str__()) plt.show() else: print("%s: Please close plots when done." % self.__str__()) plt.show()
def testTransform2(self): if display: import matplotlib.pyplot as plt scale = 2.0 shift = lsst.geom.Extent2D(3.0, 4.0) affineTransform = lsst.geom.AffineTransform.makeTranslation(shift) * \ lsst.geom.AffineTransform.makeScaling(scale) transform22 = afwGeom.makeTransform(affineTransform) for num in range(3, 30): small = self.polygon(num, 1.0, 0.0, 0.0) large1 = small.transform(affineTransform) large2 = small.transform(transform22) expect = self.polygon(num, scale, shift[0], shift[1]) self.assertEqual(large1, expect) self.assertEqual(large2, expect) if display: axes = small.plot(c='k') axes.set_aspect("equal") axes.set_title("AffineTransform: Polygon nside=%d" % num) large1.plot(axes, c='b') if not doPause: try: plt.pause(2) plt.close() except Exception: print("%s: plt.pause() failed. Please close plots when done." % self.__str__()) plt.show() else: print("%s: Please close plots when done." % self.__str__()) plt.show()
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 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 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
def makeAffineTransform(self, offset=(0, 0), rotation=0*lsst.geom.degrees, scale=1.0): """Make an affine TransformPoint2ToPoint2 that first adds the specified offset, then scales and rotates the result """ rotScale = lsst.geom.AffineTransform(lsst.geom.LinearTransform.makeScaling(scale) * lsst.geom.LinearTransform.makeRotation(rotation)) offset = lsst.geom.AffineTransform(lsst.geom.Extent2D(*offset)) # AffineTransform a*b = b.then(a) return afwGeom.makeTransform(rotScale*offset)
def makeAffineTransform(self, offset=(0, 0), rotation=0 * lsst.geom.degrees, scale=1.0): """Make an affine TransformPoint2ToPoint2 that first adds the specified offset, then scales and rotates the result """ rotScale = lsst.geom.AffineTransform( lsst.geom.LinearTransform.makeScaling(scale) * lsst.geom.LinearTransform.makeRotation(rotation)) offset = lsst.geom.AffineTransform(lsst.geom.Extent2D(*offset)) # AffineTransform a*b = b.then(a) return afwGeom.makeTransform(rotScale * offset)
def toCcdBackground(self, detector, bbox): """Produce a background model for a CCD The superpixel background model is warped back to the CCD frame, for application to the individual CCD. Parameters ---------- detector : `lsst.afw.cameraGeom.Detector` CCD for which to produce background model. bbox : `lsst.geom.Box2I` Bounding box of CCD exposure. Returns ------- bg : `lsst.afw.math.BackgroundList` Background model for CCD. """ transform = detector.getTransformMap().getTransform( detector.makeCameraSys(afwCameraGeom.PIXELS), detector.makeCameraSys(afwCameraGeom.FOCAL_PLANE)) binTransform = ( geom.AffineTransform.makeScaling(self.config.binning) * geom.AffineTransform.makeTranslation(geom.Extent2D(0.5, 0.5))) # Binned image on CCD --> unbinned image on CCD --> focal plane --> binned focal plane toSample = afwGeom.makeTransform(binTransform).then(transform).then( self.transform) focalPlane = self.getStatsImage() fpNorm = afwImage.ImageF(focalPlane.getBBox()) fpNorm.set(1.0) image = afwImage.ImageF(bbox.getDimensions() // self.config.binning) norm = afwImage.ImageF(image.getBBox()) ctrl = afwMath.WarpingControl("bilinear") afwMath.warpImage(image, focalPlane, toSample.inverted(), ctrl) afwMath.warpImage(norm, fpNorm, toSample.inverted(), ctrl) image /= norm mask = afwImage.Mask(image.getBBox()) isBad = numpy.isnan(image.getArray()) mask.getArray()[isBad] = mask.getPlaneBitMask("BAD") image.getArray()[isBad] = image.getArray()[~isBad].mean() return afwMath.BackgroundList( (afwMath.BackgroundMI(bbox, afwImage.makeMaskedImage(image, mask)), afwMath.stringToInterpStyle(self.config.interpolation), afwMath.stringToUndersampleStyle("REDUCE_INTERP_ORDER"), afwMath.ApproximateControl.UNKNOWN, 0, 0, False))
def testTransform(self): """Test constructor for Polygon involving transforms""" box = lsst.geom.Box2D(lsst.geom.Point2D(0.0, 0.0), lsst.geom.Point2D(123.4, 567.8)) poly1 = afwGeom.Polygon(box) scale = 1.5 shift = lsst.geom.Extent2D(3.0, 4.0) affineTransform = lsst.geom.AffineTransform.makeTranslation(shift) * \ lsst.geom.AffineTransform.makeScaling(scale) transform22 = afwGeom.makeTransform(affineTransform) transformedVertices = transform22.applyForward(box.getCorners()) expect = afwGeom.Polygon(transformedVertices) poly1 = afwGeom.Polygon(box, affineTransform) poly2 = afwGeom.Polygon(box, transform22) self.assertEqual(poly1, expect) self.assertEqual(poly2, expect)
def testTransform(self): """Test constructor for Polygon involving transforms""" box = lsst.geom.Box2D(lsst.geom.Point2D(0.0, 0.0), lsst.geom.Point2D(123.4, 567.8)) poly1 = afwGeom.Polygon(box) scale = 1.5 shift = lsst.geom.Extent2D(3.0, 4.0) affineTransform = lsst.geom.AffineTransform.makeTranslation(shift) * \ lsst.geom.AffineTransform.makeScaling(scale) transform22 = afwGeom.makeTransform(affineTransform) transformedVertices = transform22.applyForward(box.getCorners()) expect = afwGeom.Polygon(transformedVertices) poly1 = afwGeom.Polygon(box, affineTransform) poly2 = afwGeom.Polygon(box, transform22) self.assertEqual(poly1, expect) self.assertEqual(poly2, expect)
def rotateExposure(exp, nDegrees, kernelName='lanczos4', logger=None): """Rotate an exposure by nDegrees clockwise. Parameters ---------- exp : `lsst.afw.image.exposure.Exposure` The exposure to rotate nDegrees : `float` Number of degrees clockwise to rotate by kernelName : `str` Name of the warping kernel, used to instantiate the warper. logger : `lsst.log.Log` Logger for logging warnings Returns ------- rotatedExp : `lsst.afw.image.exposure.Exposure` A copy of the input exposure, rotated by nDegrees """ nDegrees = nDegrees % 360 if not logger: logger = lsstLog.getLogger('atmospec.utils') wcs = exp.getWcs() if not wcs: logger.warn( "Can't rotate exposure without a wcs - returning exp unrotated") return exp.clone( ) # return a clone so it's always returning a copy as this is what default does warper = afwMath.Warper(kernelName) if isinstance(exp, afwImage.ExposureU): # TODO: remove once this bug is fixed - DM-20258 logger.info('Converting ExposureU to ExposureF due to bug') logger.info('Remove this workaround after DM-20258') exp = afwImage.ExposureF(exp, deep=True) affineRotTransform = geom.AffineTransform.makeRotation(nDegrees * geom.degrees) transformP2toP2 = afwGeom.makeTransform(affineRotTransform) rotatedWcs = afwGeom.makeModifiedWcs(transformP2toP2, wcs, False) rotatedExp = warper.warpExposure(rotatedWcs, exp) # rotatedExp.setXY0(geom.Point2I(0, 0)) # TODO: check no longer required return rotatedExp
def testTransform2(self): scale = 2.0 shift = lsst.geom.Extent2D(3.0, 4.0) affineTransform = lsst.geom.AffineTransform.makeTranslation(shift) * \ lsst.geom.AffineTransform.makeScaling(scale) transform22 = afwGeom.makeTransform(affineTransform) for num in range(3, 30): small = self.polygon(num, 1.0, 0.0, 0.0) large1 = small.transform(affineTransform) large2 = small.transform(transform22) expect = self.polygon(num, scale, shift[0], shift[1]) self.assertEqual(large1, expect) self.assertEqual(large2, expect) if DEBUG: import matplotlib.pyplot as plt axes = small.plot(c='k') large1.plot(axes, c='b') plt.show()