def logSkyMapInfo(self, skyMap): """!Log information about a sky map @param[in] skyMap sky map (an lsst.skyMap.SkyMap) """ self.log.info("sky map has %s tracts" % (len(skyMap), )) for tractInfo in skyMap: wcs = tractInfo.getWcs() posBox = geom.Box2D(tractInfo.getBBox()) pixelPosList = ( posBox.getMin(), geom.Point2D(posBox.getMaxX(), posBox.getMinY()), posBox.getMax(), geom.Point2D(posBox.getMinX(), posBox.getMaxY()), ) skyPosList = [ wcs.pixelToSky(pos).getPosition(geom.degrees) for pos in pixelPosList ] posStrList = [ "(%0.3f, %0.3f)" % tuple(skyPos) for skyPos in skyPosList ] self.log.info( "tract %s has corners %s (RA, Dec deg) and %s x %s patches" % (tractInfo.getId(), ", ".join(posStrList), tractInfo.getNumPatches()[0], tractInfo.getNumPatches()[1]))
def testAccessors(self): xmin, xmax, ymin, ymax = np.random.uniform(low=-5, high=5, size=4) if xmin > xmax: xmin, xmax = xmax, xmin if ymin > ymax: ymin, ymax = ymax, ymin pmin = geom.Point2D(xmin, ymin) pmax = geom.Point2D(xmax, ymax) box = geom.Box2D(pmin, pmax, True) self.assertEqual(pmin, box.getMin()) self.assertEqual(pmax, box.getMax()) self.assertEqual(box.getMinX(), xmin) self.assertEqual(box.getMinY(), ymin) self.assertEqual(box.getMaxX(), xmax) self.assertEqual(box.getMaxY(), ymax) self.assertEqual(box.getDimensions(), (pmax - pmin)) self.assertEqual(box.getWidth(), (xmax - xmin)) self.assertEqual(box.getHeight(), (ymax - ymin)) self.assertEqual(box.getArea(), box.getWidth() * box.getHeight()) self.assertEqual(box.getCenterX(), 0.5 * (pmax.getX() + pmin.getX())) self.assertEqual(box.getCenterY(), 0.5 * (pmax.getY() + pmin.getY())) self.assertEqual(box.getCenter().getX(), box.getCenterX()) self.assertEqual(box.getCenter().getY(), box.getCenterY()) corners = box.getCorners() self.assertEqual(corners[0], box.getMin()) self.assertEqual(corners[1].getX(), box.getMaxX()) self.assertEqual(corners[1].getY(), box.getMinY()) self.assertEqual(corners[2], box.getMax()) self.assertEqual(corners[3].getX(), box.getMinX()) self.assertEqual(corners[3].getY(), box.getMaxY())
def _validateWcs(self, templateExposure, scienceExposure): """Return True if the WCS of the two Exposures have the same origin and extent. """ templateWcs = templateExposure.getWcs() scienceWcs = scienceExposure.getWcs() templateBBox = templateExposure.getBBox() scienceBBox = scienceExposure.getBBox() # LLC templateOrigin = templateWcs.pixelToSky(geom.Point2D(templateBBox.getBegin())) scienceOrigin = scienceWcs.pixelToSky(geom.Point2D(scienceBBox.getBegin())) # URC templateLimit = templateWcs.pixelToSky(geom.Point2D(templateBBox.getEnd())) scienceLimit = scienceWcs.pixelToSky(geom.Point2D(scienceBBox.getEnd())) self.log.info("Template Wcs : %f,%f -> %f,%f", templateOrigin[0], templateOrigin[1], templateLimit[0], templateLimit[1]) self.log.info("Science Wcs : %f,%f -> %f,%f", scienceOrigin[0], scienceOrigin[1], scienceLimit[0], scienceLimit[1]) templateBBox = geom.Box2D(templateOrigin.getPosition(geom.degrees), templateLimit.getPosition(geom.degrees)) scienceBBox = geom.Box2D(scienceOrigin.getPosition(geom.degrees), scienceLimit.getPosition(geom.degrees)) if not (templateBBox.overlaps(scienceBBox)): raise RuntimeError("Input images do not overlap at all") if ((templateOrigin != scienceOrigin) or (templateLimit != scienceLimit) or (templateExposure.getDimensions() != scienceExposure.getDimensions())): return False return True
def testSymmetry(self): """Verify that the projection is symmetrical about the equator """ for minDec in (-5.0, -1.0, 0.5): maxDec = minDec + 2.0 config = EquatSkyMap.ConfigClass() config.decRange = minDec, maxDec skyMap = EquatSkyMap(config) for tractInfo in skyMap[0:1]: numPatches = tractInfo.getNumPatches() midXIndex = numPatches[0]//2 minPixelPosList = [] maxPixelPosList = [] maxYInd = numPatches[1] - 1 for xInd in (0, midXIndex, numPatches[0] - 1): minDecPatchInfo = tractInfo.getPatchInfo((xInd, 0)) minDecPosBox = geom.Box2D(minDecPatchInfo.getOuterBBox()) minPixelPosList += [ minDecPosBox.getMin(), geom.Point2D(minDecPosBox.getMaxX(), minDecPosBox.getMinY()), ] maxDecPatchInfo = tractInfo.getPatchInfo((xInd, maxYInd)) maxDecPosBox = geom.Box2D(maxDecPatchInfo.getOuterBBox()) maxPixelPosList += [ maxDecPosBox.getMax(), geom.Point2D(maxDecPosBox.getMinX(), maxDecPosBox.getMaxY()), ] wcs = tractInfo.getWcs() minDecList = [wcs.pixelToSky(pos).getPosition(geom.degrees)[1] for pos in minPixelPosList] maxDecList = [wcs.pixelToSky(pos).getPosition(geom.degrees)[1] for pos in maxPixelPosList] self.assertTrue(numpy.allclose(minDecList, minDecList[0])) self.assertTrue(numpy.allclose(maxDecList, maxDecList[0])) self.assertTrue(minDecList[0] <= minDec) self.assertTrue(maxDecList[0] >= maxDec)
def logSkyMapInfo(self, log): """Write information about a sky map to supplied log Parameters ---------- log : `lsst.log.Log` Log object that information about skymap will be written """ log.info("sky map has %s tracts" % (len(self), )) for tractInfo in self: wcs = tractInfo.getWcs() posBox = geom.Box2D(tractInfo.getBBox()) pixelPosList = ( posBox.getMin(), geom.Point2D(posBox.getMaxX(), posBox.getMinY()), posBox.getMax(), geom.Point2D(posBox.getMinX(), posBox.getMaxY()), ) skyPosList = [ wcs.pixelToSky(pos).getPosition(geom.degrees) for pos in pixelPosList ] posStrList = [ "(%0.3f, %0.3f)" % tuple(skyPos) for skyPos in skyPosList ] log.info( "tract %s has corners %s (RA, Dec deg) and %s x %s patches" % (tractInfo.getId(), ", ".join(posStrList), tractInfo.getNumPatches()[0], tractInfo.getNumPatches()[1]))
def testConversion(self): for n in range(10): xmin, xmax, ymin, ymax = np.random.uniform(low=-10, high=10, size=4) if xmin > xmax: xmin, xmax = xmax, xmin if ymin > ymax: ymin, ymax = ymax, ymin fpMin = geom.Point2D(xmin, ymin) fpMax = geom.Point2D(xmax, ymax) if any((fpMax - fpMin).lt(3)): continue # avoid empty boxes with self.subTest(xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax): fpBox = geom.Box2D(fpMin, fpMax) intBoxBig = geom.Box2I(fpBox, geom.Box2I.EXPAND) fpBoxBig = geom.Box2D(intBoxBig) intBoxSmall = geom.Box2I(fpBox, geom.Box2I.SHRINK) fpBoxSmall = geom.Box2D(intBoxSmall) self.assertTrue(fpBoxBig.contains(fpBox)) self.assertTrue(fpBox.contains(fpBoxSmall)) self.assertTrue(intBoxBig.contains(intBoxSmall)) self.assertTrue(geom.Box2D(intBoxBig)) self.assertEqual(geom.Box2I(fpBoxBig, geom.Box2I.SHRINK), intBoxBig) self.assertEqual(geom.Box2I(fpBoxSmall, geom.Box2I.SHRINK), intBoxSmall) self.assertTrue(geom.Box2I(geom.Box2D()).isEmpty()) self.assertRaises( lsst.pex.exceptions.InvalidParameterError, geom.Box2I, geom.Box2D(geom.Point2D(), geom.Point2D(float("inf"), float("inf"))))
def get_amp_patches(det, amps=None): """ Return a list of Rectangle patches in focalplane coordinates corresponding to the amplifier segments in a detector object. Parameters ---------- det: `lsst.afw.cameraGeom.detector.detector.Detector` Detector object. amps: container-type object [None] Python container that can be queried like `'C01 in amps'` to see if a particular channel is included for plotting. If None, then use all channels in det. Returns ------- list of matplotlib.patches.Rectangle objects """ transform = det.getTransform(cameraGeom.PIXELS, cameraGeom.FOCAL_PLANE) bbox = list(det)[0].getBBox() dy, dx = bbox.getHeight(), bbox.getWidth() patches = [] for amp in det: if amps is not None and amp.getName() not in amps: continue j, i = tuple(int(_) for _ in amp.getName()[1:]) y, x = j * dy, i * dx x0, y0 = transform.applyForward(lsstGeom.Point2D(x, y)) x1, y1 = transform.applyForward(lsstGeom.Point2D(x + dx, y + dy)) patches.append(Rectangle((x0, y0), x1 - x0, y1 - y0)) return patches
def reportSkyMapInfo(skyMap): paramDict = skyMap.config.toDict() paramNameList = sorted(paramDict) print("Sky Map parameters:") for paramName in paramNameList: param = paramDict[paramName] print("skyMap.config.%s = %s" % (paramName, param)) print("\nSkyMap has %d tracts:" % (len(skyMap))) for tractInfo in skyMap: wcs = tractInfo.getWcs() posBox = geom.Box2D(tractInfo.getBBox()) pixelPosList = ( posBox.getMin(), geom.Point2D(posBox.getMaxX(), posBox.getMinY()), posBox.getMax(), geom.Point2D(posBox.getMinX(), posBox.getMaxY()), ) skyPosList = [ wcs.pixelToSky(pos).getPosition(geom.degrees) for pos in pixelPosList ] posStrList = [ "(%0.3f, %0.3f)" % tuple(skyPos) for skyPos in skyPosList ] print("tract %s has corners %s (RA, Dec deg) and %s x %s patches" % (tractInfo.getId(), ", ".join(posStrList), tractInfo.getNumPatches()[0], tractInfo.getNumPatches()[1]))
def testFindTract(self): """Test the SkyMap.findTract method """ for numTracts in (2, 4): config = EquatSkyMap.ConfigClass() config.numTracts = numTracts skyMap = EquatSkyMap(config) decRange = skyMap.config.decRange decList = ( (decRange[0] * 0.999) + (decRange[1] * 0.901), (decRange[0] * 0.500) + (decRange[1] * 0.500), (decRange[0] * 0.091) + (decRange[1] * 0.999), ) for tractInfo0 in skyMap: tractId0 = tractInfo0.getId() ctrCoord0 = tractInfo0.getCtrCoord() for tractInfo1 in self.getNeighborTracts(skyMap, tractId0): tractId1 = tractInfo1.getId() ctrCoord1 = tractInfo1.getCtrCoord() for deltaFrac in (-0.001, 0.001): v0 = ctrCoord0.getVector() * (0.5 + deltaFrac) v1 = ctrCoord1.getVector() * (0.5 - deltaFrac) testVec = v0 + v1 testRa = geom.SpherePoint(testVec).getRa() if deltaFrac > 0.0: expectedTractId = tractId0 else: expectedTractId = tractId1 for testDecDeg in decList: testDec = geom.Angle(testDecDeg, geom.degrees) testCoord = geom.SpherePoint(testRa, testDec) nearestTractInfo = skyMap.findTract(testCoord) nearestTractId = nearestTractInfo.getId() self.assertEqual(nearestTractId, expectedTractId) patchInfo = nearestTractInfo.findPatch(testCoord) pixelInd = geom.Point2I(nearestTractInfo.getWcs().skyToPixel(testCoord)) self.assertTrue(patchInfo.getInnerBBox().contains(pixelInd)) # find a point outside the tract and make sure it fails tractInfo = tractInfo0 wcs = tractInfo.getWcs() bbox = geom.Box2D(tractInfo.getBBox()) outerPixPosList = [ bbox.getMin() - geom.Extent2D(1, 1), geom.Point2D(bbox.getMaxX(), bbox.getMinY()) - geom.Extent2D(1, 1), bbox.getMax() + geom.Extent2D(1, 1), geom.Point2D(bbox.getMinX(), bbox.getMaxY()) + geom.Extent2D(1, 1), ] for outerPixPos in outerPixPosList: testCoord = wcs.pixelToSky(outerPixPos) self.assertRaises(LookupError, tractInfo.findPatch, testCoord)
def runMeasurement(self, algorithmName, imageid, x, y, v): """Run the measurement algorithm on an image""" # load the test image imgFile = os.path.join(self.dataDir, "image.%d.fits" % imageid) img = afwImage.ImageF(imgFile) img -= self.bkgd nx, ny = img.getWidth(), img.getHeight() msk = afwImage.Mask(geom.Extent2I(nx, ny), 0x0) var = afwImage.ImageF(geom.Extent2I(nx, ny), v) mimg = afwImage.MaskedImageF(img, msk, var) msk.getArray()[:] = np.where(np.fabs(img.getArray()) < 1.0e-8, msk.getPlaneBitMask("BAD"), 0) # Put it in a bigger image, in case it matters big = afwImage.MaskedImageF(self.offset + mimg.getDimensions()) big.getImage().set(0) big.getMask().set(0) big.getVariance().set(v) subBig = afwImage.MaskedImageF(big, geom.Box2I(big.getXY0() + self.offset, mimg.getDimensions())) subBig <<= mimg mimg = big mimg.setXY0(self.xy0) exposure = afwImage.makeExposure(mimg) cdMatrix = np.array([1.0/(2.53*3600.0), 0.0, 0.0, 1.0/(2.53*3600.0)]) cdMatrix.shape = (2, 2) exposure.setWcs(afwGeom.makeSkyWcs(crpix=geom.Point2D(1.0, 1.0), crval=geom.SpherePoint(0, 0, geom.degrees), cdMatrix=cdMatrix)) # load the corresponding test psf psfFile = os.path.join(self.dataDir, "psf.%d.fits" % imageid) psfImg = afwImage.ImageD(psfFile) psfImg -= self.bkgd kernel = afwMath.FixedKernel(psfImg) kernelPsf = algorithms.KernelPsf(kernel) exposure.setPsf(kernelPsf) # perform the shape measurement msConfig = base.SingleFrameMeasurementConfig() alg = base.SingleFramePlugin.registry[algorithmName].PluginClass.AlgClass control = base.SingleFramePlugin.registry[algorithmName].PluginClass.ConfigClass().makeControl() msConfig.algorithms.names = [algorithmName] # Note: It is essential to remove the floating point part of the position for the # Algorithm._apply. Otherwise, when the PSF is realised it will have been warped # to account for the sub-pixel offset and we won't get *exactly* this PSF. plugin, table = makePluginAndCat(alg, algorithmName, control, centroid="centroid") center = geom.Point2D(int(x), int(y)) + geom.Extent2D(self.offset + geom.Extent2I(self.xy0)) source = table.makeRecord() source.set("centroid_x", center.getX()) source.set("centroid_y", center.getY()) source.setFootprint(afwDetection.Footprint(afwGeom.SpanSet(exposure.getBBox(afwImage.PARENT)))) plugin.measure(source, exposure) return source
def addCcd(self, exposure): """Add CCD to model We measure the background on the CCD (clipped mean), and record the results in the model. For simplicity, measurements are made in a box on the CCD corresponding to the warped coordinates of the superpixel rather than accounting for little rotations, etc. We also record the number of pixels used in the measurement so we can have a measure of confidence in each bin's value. Parameters ---------- exposure : `lsst.afw.image.Exposure` CCD exposure to measure """ detector = exposure.getDetector() transform = detector.getTransformMap().getTransform( detector.makeCameraSys(afwCameraGeom.PIXELS), detector.makeCameraSys(afwCameraGeom.FOCAL_PLANE)) image = exposure.getMaskedImage() maskVal = image.getMask().getPlaneBitMask(self.config.mask) # Warp the binned image to the focal plane toSample = transform.then( self.transform) # CCD pixels --> focal plane --> sample warped = afwImage.ImageF(self._values.getBBox()) warpedCounts = afwImage.ImageF(self._numbers.getBBox()) width, height = warped.getDimensions() stats = afwMath.StatisticsControl() stats.setAndMask(maskVal) stats.setNanSafe(True) # Iterating over individual pixels in python is usually bad because it's slow, but there aren't many. pixels = itertools.product(range(width), range(height)) for xx, yy in pixels: llc = toSample.applyInverse(geom.Point2D(xx - 0.5, yy - 0.5)) urc = toSample.applyInverse(geom.Point2D(xx + 0.5, yy + 0.5)) bbox = geom.Box2I(geom.Point2I(llc), geom.Point2I(urc)) bbox.clip(image.getBBox()) if bbox.isEmpty(): continue subImage = image.Factory(image, bbox) result = afwMath.makeStatistics(subImage, afwMath.MEANCLIP | afwMath.NPOINT, stats) mean = result.getValue(afwMath.MEANCLIP) num = result.getValue(afwMath.NPOINT) if not numpy.isfinite(mean) or not numpy.isfinite(num): continue warped[xx, yy, afwImage.LOCAL] = mean * num warpedCounts[xx, yy, afwImage.LOCAL] = num self._values += warped self._numbers += warpedCounts
def maskVignetting(self, mask, detector): """Mask vignetted pixels in-place @param mask: Mask image to modify @param detector: Detector for transformation to focal plane coords """ mask.addMaskPlane(self.config.maskPlane) bitMask = mask.getPlaneBitMask(self.config.maskPlane) bbox = mask.getBBox(afwImage.LOCAL) w, h = bbox.getDimensions() numCorners = 0 # Number of corners outside radius pixelsToFocalPlane = detector.getTransform(afwcg.PIXELS, afwcg.FOCAL_PLANE) pixelCorners = [geom.Point2D(pos) for pos in bbox.getCorners()] fpCorners = pixelsToFocalPlane.applyForward(pixelCorners) fpCenter = geom.Point2D(self.config.vignette.xCenter, self.config.vignette.yCenter) numCorners = sum( math.hypot(*(fpPos - fpCenter)) > self.config.vignette.radius for fpPos in fpCorners) if numCorners == 0: # Nothing to be masked self.log.info("Detector %d is unvignetted", detector.getId()) return if numCorners == 4: # Everything to be masked # We ignore the question of how we're getting any data from a CCD # that's totally vignetted... self.log.warning("Detector %d is completely vignetted", detector.getId()) mask |= bitMask return # We have to go pixel by pixel numPixels = bbox.getArea() xx, yy = np.meshgrid(np.arange(0, w, dtype=int), np.arange(0, h, dtype=int)) xyDetector = [ geom.Point2D(x, y) for x, y in zip(xx.reshape(numPixels), yy.reshape(numPixels)) ] xyFocalPlane = pixelsToFocalPlane.applyForward(xyDetector) origin = geom.Point2D(self.config.vignette.xCenter, self.config.vignette.yCenter) r2 = np.array([pp.distanceSquared(origin) for pp in xyFocalPlane]) isBad = (r2 > self.config.vignette.radius**2).reshape((h, w)) self.log.info("Detector %d has %f%% pixels vignetted", detector.getId(), isBad.sum() / float(isBad.size) * 100.0) maskArray = mask.getArray() maskArray[yy[isBad], xx[isBad]] |= bitMask
def __init__(self, config=None): BaseSkyMap.__init__(self, config) decRange = tuple(geom.Angle(dr, geom.degrees) for dr in self.config.decRange) midDec = (decRange[0] + decRange[1]) / 2.0 tractWidthRA = geom.Angle(360.0 / self.config.numTracts, geom.degrees) tractOverlap = geom.Angle(self.config.tractOverlap, geom.degrees) for id in range(self.config.numTracts): begRA = tractWidthRA * id endRA = begRA + tractWidthRA vertexCoordList = ( geom.SpherePoint(begRA, decRange[0]), geom.SpherePoint(endRA, decRange[0]), geom.SpherePoint(endRA, decRange[1]), geom.SpherePoint(begRA, decRange[1]), ) midRA = begRA + tractWidthRA / 2.0 ctrCoord = geom.SpherePoint(midRA, midDec) # CRVal must have Dec=0 for symmetry about the equator crValCoord = geom.SpherePoint(midRA, geom.Angle(0.0)) # make initial WCS; don't worry about crPixPos because TractInfo will shift it as required wcs = self._wcsFactory.makeWcs(crPixPos=geom.Point2D(0, 0), crValCoord=crValCoord) self._tractInfoList.append(TractInfo( id=id, tractBuilder=self._tractBuilder, ctrCoord=ctrCoord, vertexCoordList=vertexCoordList, tractOverlap=tractOverlap, wcs=wcs, ))
def _makeStarImage(self, xc=[15.3], yc=[18.6], flux=[2500], schema=None, randomSeed=None): """Generate an exposure and catalog with the given stellar source(s). """ from lsst.meas.base.tests import TestDataset bbox = geom.Box2I(geom.Point2I(0, 0), geom.Point2I(self.w - 1, self.h - 1)) dataset = TestDataset(bbox, psfSigma=self.psfSigma, threshold=1.) for i in range(len(xc)): dataset.addSource(instFlux=flux[i], centroid=geom.Point2D(xc[i], yc[i])) if schema is None: schema = TestDataset.makeMinimalSchema() exposure, catalog = dataset.realize(noise=self.noise, schema=schema, randomSeed=randomSeed) if self.gradientParams is not None: y, x = np.mgrid[:self.w, :self.h] gp = self.gradientParams gradient = gp[0] + gp[1] * x + gp[2] * y if len(self.gradientParams ) > 3: # it includes a set of 2nd-order polynomial params gradient += gp[3] * x * y + gp[4] * x * x + gp[5] * y * y imgArr = exposure.getMaskedImage().getArrays()[0] imgArr += gradient return exposure, catalog
def _testCoaddPsf(self, newExposure): """Test that the new CoaddPsf of the `newExposure` returns PSF images ~identical to the input PSF of `self.exposure` across a grid covering the entire exposure bounding box. """ origPsf = self.exposure.getPsf() newPsf = newExposure.getPsf() self.assertTrue(isinstance(newPsf, measAlg.CoaddPsf)) extentX = int(self.exposure.getWidth()*0.05) extentY = int(self.exposure.getHeight()*0.05) for x in np.linspace(extentX, self.exposure.getWidth()-extentX, 10): for y in np.linspace(extentY, self.exposure.getHeight()-extentY, 10): point = geom.Point2D(np.rint(x), np.rint(y)) oPsf = origPsf.computeImage(point).getArray() nPsf = newPsf.computeImage(point).getArray() if oPsf.shape[0] < nPsf.shape[0]: # sometimes CoaddPsf does this. oPsf = np.pad(oPsf, ((1, 1), (0, 0)), mode='constant') elif oPsf.shape[0] > nPsf.shape[0]: nPsf = np.pad(nPsf, ((1, 1), (0, 0)), mode='constant') if oPsf.shape[1] < nPsf.shape[1]: # sometimes CoaddPsf does this. oPsf = np.pad(oPsf, ((0, 0), (1, 1)), mode='constant') elif oPsf.shape[1] > nPsf.shape[1]: nPsf = np.pad(nPsf, ((0, 0), (1, 1)), mode='constant') # pixel-wise comparison -- pretty stringent self.assertFloatsAlmostEqual(oPsf, nPsf, atol=1e-4, msg='Failed on Psf') origMmts = np.array(getPsfSecondMoments(oPsf)) newMmts = np.array(getPsfSecondMoments(nPsf)) self.assertFloatsAlmostEqual(origMmts, newMmts, atol=1e-4, msg='Failed on Psf')
def ampPixelToFocalMm(self, ampX, ampY, channel, detectorName=None): r"""Given position within an amplifier return the focal plane position ampX : `int` column on amp segment ampY : `int` row on amp segment channel: `int` Channel number of amplifier (1-indexed; identical to HDU) detectorName : `str` Name of detector (or default from setDetectorName() if None) N.b. all pixel coordinates have the centre of the bottom-left pixel at (0.0, 0.0), and CCD columns are taken to be vertical, with "R00" in the bottom left of the image Returns ------- focalPlaneX : `float` The x-focal plane position (mm) relative to the centre of the camera focalPlaneY : `float` The y-focal plane position (mm) relative to the centre of the camera Raises ------ RuntimeError If the requested pixel doesn't lie on the detector """ detector = self.getDetector(detectorName) ccdX, ccdY = ampPixelToCcdPixel(ampX, ampY, detector, channel) return detector.transform(geom.Point2D(ccdX, ccdY), cameraGeom.PIXELS, cameraGeom.FOCAL_PLANE)
def ccdPixelToFocalMm(self, ccdX, ccdY, detectorName=None): r"""Given position within a detector return the focal plane position Parameters ---------- ccdX : `int` column pixel position within detector ccdY : `int` row pixel position within detector detectorName : `str` Name of detector (or default from setDetectorName() if None) N.b. all pixel coordinates have the centre of the bottom-left pixel at (0.0, 0.0), and CCD columns are taken to be vertical, with "R00" in the bottom left of the image Returns ------- focalPlaneX : `float` The x-focal plane position (mm) relative to the centre of the camera focalPlaneY : `float` The y-focal plane position (mm) relative to the centre of the camera """ detector = self.getDetector(detectorName) return detector.transform(geom.Point2D(ccdX, ccdY), cameraGeom.PIXELS, cameraGeom.FOCAL_PLANE)
def getReferences(self, butler, exposure): """! Get reference catalogs from all patch,tract combinations that overlaps this exposure This method will filter out any rferences that are not in the inner tract,patch region so that there will be no duplicates across boundaries. @param[in] butler An lsst.daf.persistence.Butler. This is used to get the references @param[in] exposure A deepDiff_exposure on which to run the measurements @return Dictionary of tract, reference catalogs """ skyMap = butler.get(f"{self.config.coaddName}Coadd_skyMap") wcs = exposure.getWcs() imagePixelCorners = exposure.getBBox().getCorners() imageSkyCorners = [wcs.pixelToSky(geom.Point2D(a)) for a in imagePixelCorners] # get initial list of tracts and patches tractList = skyMap.findTractPatchList(imageSkyCorners) tractRefCat = dict() for tract, patchList in tractList: usePatchList = [] for patch in patchList: patchPoly = patch.getInnerSkyPolygon(tract.getWcs()) imagePoly = sphgeom.ConvexPolygon.convexHull([coord.getVector() for coord in imageSkyCorners]) if patchPoly.intersects(imagePoly): usePatchList.append(patch) refCat = self.fetchInPatches(butler, exposure, tract, usePatchList) tractRefCat[tract] = refCat return tractRefCat
def generateTract(self, index): """Generate TractInfo for the specified tract index.""" center = geom.SpherePoint(self.config.raList[index], self.config.decList[index], geom.degrees) radius = self.config.radiusList[index] wcs = self._wcsFactory.makeWcs(crPixPos=geom.Point2D(0, 0), crValCoord=center) return ExplicitTractInfo(index, self.config.patchInnerDimensions, self.config.patchBorder, center, radius*geom.degrees, self.config.tractOverlap*geom.degrees, wcs)
def setUp(self): nSources = 10 # CFHT Filters from the camera mapper. afwImageUtils.resetFilters() afwImageUtils.defineFilter('u', lambdaEff=374, alias="u.MP9301") afwImageUtils.defineFilter('g', lambdaEff=487, alias="g.MP9401") afwImageUtils.defineFilter('r', lambdaEff=628, alias="r.MP9601") afwImageUtils.defineFilter('i', lambdaEff=778, alias="i.MP9701") afwImageUtils.defineFilter('z', lambdaEff=1170, alias="z.MP9801") self.bbox = geom.Box2I(geom.Point2I(0, 0), geom.Extent2I(1024, 1153)) dataset = measTests.TestDataset(self.bbox) for srcIdx in range(nSources): dataset.addSource(100000.0, geom.Point2D(100, 100)) self.inputCatalogNoFlags, _ = make_input_source_catalog(dataset, False) self.inputCatalog, self.exposure = \ make_input_source_catalog(dataset, True) detector = DetectorWrapper(id=23, bbox=self.exposure.getBBox()).detector visit = afwImage.VisitInfo(exposureId=4321, exposureTime=200., date=dafBase.DateTime(nsecs=1400000000 * 10**9)) self.exposure.setDetector(detector) self.exposure.getInfo().setVisitInfo(visit) self.exposure.setFilter(afwImage.Filter('g.MP9401')) scale = 2 scaleErr = 1 self.photoCalib = afwImage.PhotoCalib(scale, scaleErr) self.exposure.setPhotoCalib(self.photoCalib)
def test_ps_dmpsf_smoke(): rng = np.random.RandomState(7812) dim = 20 masked_image = afw_image.MaskedImageF(dim, dim) exp = afw_image.ExposureF(masked_image) psf_dim = 15 wcs = make_sim_wcs(dim) pspsf = make_ps_psf(rng=rng, dim=dim) fpsf = make_dm_psf(psf=pspsf, psf_dim=psf_dim, wcs=wcs) exp.setPsf(fpsf) psf = exp.getPsf() x = 8.5 y = 10.1 pos = geom.Point2D(x=x, y=y) gs_pos = galsim.PositionD(x=x, y=y) # this one is always centered msim = psf.computeKernelImage(pos) assert msim.array.shape == (psf_dim, psf_dim) gspsf = pspsf.getPSF(gs_pos) gsim = gspsf.drawImage( nx=psf_dim, ny=psf_dim, wcs=wcs.local(image_pos=gs_pos), ) assert np.allclose(msim.array, gsim.array)
def subtractStars(self, exposure, catalog, chi_lim=-1): """Subtract the exposure's PSF from all the sources in catalog""" mi, psf = exposure.getMaskedImage(), exposure.getPsf() subtracted = mi.Factory(mi, True) for s in catalog: xc, yc = s.getX(), s.getY() bbox = subtracted.getBBox(afwImage.PARENT) if bbox.contains(geom.PointI(int(xc), int(yc))): try: measAlg.subtractPsf(psf, subtracted, xc, yc) except Exception: pass chi = subtracted.Factory(subtracted, True) var = subtracted.getVariance() np.sqrt(var.getArray(), var.getArray()) # inplace sqrt chi /= var if display: afwDisplay.Display(frame=1).mtv(subtracted, title="Subtracted") afwDisplay.Display(frame=2).mtv(chi, title="Chi") xc, yc = exposure.getWidth() // 2, exposure.getHeight() // 2 afwDisplay.Display(frame=3).mtv(psf.computeImage( geom.Point2D(xc, yc)), title="Psf %.1f,%.1f" % (xc, yc)) chi_min, chi_max = np.min(chi.getImage().getArray()), np.max( chi.getImage().getArray()) if False: print(chi_min, chi_max) if chi_lim > 0: self.assertGreater(chi_min, -chi_lim) self.assertLess(chi_max, chi_lim)
def _getCenterOfMass(exp, nominalCentroid, boxSize): """Get the centre of mass around a point in the image. Parameters ---------- exp : `lsst.afw.image.Exposure` The exposure in question. nominalCentroid : `tuple` of `float` Nominal location of the centroid in pixel coordinates. boxSize : `int` The size of the box around the nominalCentroid in which to measure the centre of mass. Returns ------- com : `tuple` of `float` The locaiton of the centre of mass of the brightest source in pixel coordinates. """ centroidPoint = geom.Point2I(nominalCentroid) extent = geom.Extent2I(1, 1) bbox = geom.Box2I(centroidPoint, extent) bbox = bbox.dilatedBy(int(boxSize // 2)) bbox = bbox.clippedTo(exp.getBBox()) data = exp[bbox].image.array xy0 = exp[bbox].getXY0() peak = ndImage.center_of_mass(data) peak = (peak[1], peak[0]) # numpy coords returned com = geom.Point2D(xy0) com.shift(geom.Extent2D(*peak)) return (com[0], com[1])
def __init__(self, config=None): BaseSkyMap.__init__(self, config) self._dodecahedron = detail.Dodecahedron( withFacesOnPoles=self.config.withTractsOnPoles) tractOverlap = geom.Angle(self.config.tractOverlap, geom.degrees) for id in range(12): tractVec = self._dodecahedron.getFaceCtr(id) tractCoord = detail.coordFromVec(tractVec, defRA=geom.Angle(0)) tractRA = tractCoord.getLongitude() vertexVecList = self._dodecahedron.getVertices(id) # make initial WCS; don't worry about crPixPos because TractInfo will shift it as required wcs = self._wcsFactory.makeWcs(crPixPos=geom.Point2D(0, 0), crValCoord=tractCoord) self._tractInfoList.append( TractInfo( id=id, tractBuilder=self._tractBuilder, ctrCoord=tractCoord, vertexCoordList=[ detail.coordFromVec(vec, defRA=tractRA) for vec in vertexVecList ], tractOverlap=tractOverlap, wcs=wcs, ))
def testPeakRemoval(self): ''' A simple example: three overlapping blobs (detected as 1 footprint with three peaks). Additional peaks are added near the blob peaks that should be identified as degenerate. ''' H, W = 100, 100 fpbb = geom.Box2I(geom.Point2I(0, 0), geom.Point2I(W - 1, H - 1)) afwimg = afwImage.MaskedImageF(fpbb) imgbb = afwimg.getBBox() img = afwimg.getImage().getArray() var = afwimg.getVariance().getArray() var[:, :] = 1. blob_fwhm = 10. blob_psf = doubleGaussianPsf(99, 99, blob_fwhm, 2.*blob_fwhm, 0.03) fakepsf_fwhm = 3. fakepsf = gaussianPsf(11, 11, fakepsf_fwhm) blobimgs = [] x = 75. XY = [(x, 35.), (x, 65.), (50., 50.)] flux = 1e6 for x, y in XY: bim = blob_psf.computeImage(geom.Point2D(x, y)) bbb = bim.getBBox() bbb.clip(imgbb) bim = bim.Factory(bim, bbb) bim2 = bim.getArray() blobimg = np.zeros_like(img) blobimg[bbb.getMinY():bbb.getMaxY()+1, bbb.getMinX():bbb.getMaxX()+1] += flux*bim2 blobimgs.append(blobimg) img[bbb.getMinY():bbb.getMaxY()+1, bbb.getMinX():bbb.getMaxX()+1] += flux * bim2 # Run the detection code to get a ~ realistic footprint thresh = afwDet.createThreshold(5., 'value', True) fpSet = afwDet.FootprintSet(afwimg, thresh, 'DETECTED', 1) fps = fpSet.getFootprints() self.assertTrue(len(fps) == 1) # Add new peaks near to the first peaks that will be degenerate fp0 = fps[0] for x, y in XY: fp0.addPeak(x - 10, y + 6, 10) deb = deblend(fp0, afwimg, fakepsf, fakepsf_fwhm, verbose=True, removeDegenerateTemplates=True) self.assertTrue(deb.deblendedParents[0].peaks[3].degenerate) self.assertTrue(deb.deblendedParents[0].peaks[4].degenerate) self.assertTrue(deb.deblendedParents[0].peaks[5].degenerate)
def pupilCoordsFromFocalPlaneCoords(xFocal, yFocal, camera=None): """ Get the pupil coordinates in radians from the focal plane coordinates in millimeters @param [in] xFocal the x focal plane coordinates in millimeters. Can be a float or a numpy array. @param [in] yFocal the y focal plane coordinates in millimeters. Can be a float or a numpy array. @param [in] camera is an afw.cameraGeom camera object @param [out] a 2-D numpy array in which the first row is the x pupil coordinate and the second row is the y pupil coordinate (both in radians) """ are_arrays = _validate_inputs([xFocal, yFocal], ['xFocal', 'yFocal'], 'pupilCoordsFromFocalPlaneCoords') if camera is None: raise RuntimeError( "You cannot calculate pupil coordinates without specifying a camera" ) focal_to_field = camera.getTransformMap().getTransform( FOCAL_PLANE, FIELD_ANGLE) if are_arrays: focal_point_list = [geom.Point2D(x, y) for x, y in zip(xFocal, yFocal)] pupil_point_list = focal_to_field.applyForward(focal_point_list) pupil_arr = np.array([[pp.getX(), pp.getY()] for pp in pupil_point_list]).transpose() is_nan = np.where(np.logical_or(np.isnan(xFocal), np.isnan(yFocal))) pupil_arr[0][is_nan] = np.NaN pupil_arr[1][is_nan] = np.NaN return pupil_arr # if not are_arrays if np.isfinite(xFocal) and np.isfinite(yFocal): pupPoint = focal_to_field.applyForward(geom.Point2D(xFocal, yFocal)) return np.array([pupPoint.getX(), pupPoint.getY()]) return np.array([np.NaN, np.NaN])
def testMakeCenteredBox(self): dimensionsI = [ geom.Extent2I(100, 50), geom.Extent2I(15, 15), geom.Extent2I(0, 10), geom.Extent2I(25, 30), geom.Extent2I(15, -5) ] dimensionsD = [geom.Extent2D(d) for d in dimensionsI] \ + [geom.Extent2D(1.5, 2.1), geom.Extent2D(4, 3.7), geom.Extent2D(-0.1, -0.1), geom.Extent2D(5.5, 5.5), geom.Extent2D(-np.nan, 5.5), geom.Extent2D(4, np.inf)] locations = [ geom.Point2D(0, 0), geom.Point2D(0.2, 0.7), geom.Point2D(1, 1.5), geom.Point2D(-0.5 + 1e-4, -0.5 + 1e-4), geom.Point2D(-0.5 - 1e-4, -0.5 - 1e-4), geom.Point2D(-np.nan, 0), geom.Point2D(1.0, np.inf), ] for center in locations: for size in dimensionsI: self._checkBoxConstruction(geom.Box2I, size, center, np.sqrt(0.5)) for size in dimensionsD: self._checkBoxConstruction(geom.Box2D, size, center, 1e-10)
def bboxToRaDec(bbox, wcs): """Get the corners of a BBox and convert them to lists of RA and Dec.""" corners = [] for corner in bbox.getCorners(): p = geom.Point2D(corner.getX(), corner.getY()) coord = wcs.pixelToSky(p) corners.append([coord.getRa().asDegrees(), coord.getDec().asDegrees()]) ra, dec = zip(*corners) return ra, dec
def generateTract(self, index): """Generate TractInfo for the specified tract index.""" center = angToCoord( healpy.pix2ang(self._nside, index, nest=self.config.nest)) wcs = self._wcsFactory.makeWcs(crPixPos=geom.Point2D(0, 0), crValCoord=center) return HealpixTractInfo(self._nside, index, self.config.nest, self._tractBuilder, center, self.config.tractOverlap * geom.degrees, wcs)
def mkFakeStars(self, fakeCat, band, photoCalib, psf, image): """Make fake stars based off the properties in the fakeCat. Parameters ---------- band : `str` psf : `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf` The PSF information to use to make the PSF images fakeCat : `pandas.core.frame.DataFrame` The catalog of fake sources to be input image : `lsst.afw.image.exposure.exposure.ExposureF` The image into which the fake sources should be added photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` Photometric calibration to be used to calibrate the fake sources Yields ------- starImages : `generator` A generator of tuples of `lsst.afw.image.ImageF` of fake stars and `lsst.geom.Point2D` of their locations. Notes ----- To take a given magnitude and translate to the number of counts in the image we use photoCalib.magnitudeToInstFlux, which returns the instrumental flux for the given calibration radius used in the photometric calibration step. Thus `calibFluxRadius` should be set to this same radius so that we can normalize the PSF model to the correct instrumental flux within calibFluxRadius. """ self.log.info("Making %d fake star images" % len(fakeCat)) for (index, row) in fakeCat.iterrows(): xy = geom.Point2D(row["x"], row["y"]) # We put these two PSF calculations within this same try block so that we catch cases # where the object's position is outside of the image. try: correctedFlux = psf.computeApertureFlux( self.config.calibFluxRadius, xy) starIm = psf.computeImage(xy) starIm /= correctedFlux except InvalidParameterError: self.log.info("Star at %0.4f, %0.4f outside of image" % (row["x"], row["y"])) continue try: flux = photoCalib.magnitudeToInstFlux( row[self.config.magVar % band], xy) except LogicError: flux = 0 starIm *= flux yield ((starIm.convertF(), xy))