def initData(shape, coords, amplitudes=None, convolve=True): """Initialize data for the tests """ B, Ny, Nx = shape K = len(coords) if amplitudes is None: amplitudes = np.ones((K, )) assert K == len(amplitudes) _seds = [ np.arange(B, dtype=float), np.arange(B, dtype=float)[::-1], np.ones((B, ), dtype=float) ] seds = np.array([_seds[n % 3] * amplitudes[n] for n in range(K)], dtype=np.float32) morphs = np.zeros((K, Ny, Nx), dtype=np.float32) for k, coord in enumerate(coords): morphs[k, coord[0], coord[1]] = 1 images = seds.T.dot(morphs.reshape(K, -1)).reshape(shape) if convolve: psfRadius = 20 psfShape = (2 * psfRadius + 1, 2 * psfRadius + 1) targetPsf = GaussianPsf(psfShape[1], psfShape[0], .9) targetPsfImage = targetPsf.computeImage().array psfs = [ GaussianPsf(psfShape[1], psfShape[0], 1 + .2 * b) for b in range(B) ] psfImages = np.array([psf.computeImage().array for psf in psfs]) psfImages /= psfImages.max(axis=(1, 2))[:, None, None] # Convolve the image with the psf in each channel # Use scipy.signal.convolve without using FFTs as a sanity check images = np.array([ scipy.signal.convolve(img, psf, method="direct", mode="same") for img, psf in zip(images, psfImages) ]) # Convolve the true morphology with the target PSF, # also using scipy.signal.convolve as a sanity check morphs = np.array([ scipy.signal.convolve(m, targetPsfImage, method="direct", mode="same") for m in morphs ]) morphs /= morphs.max() psfImages /= psfImages.sum(axis=(1, 2))[:, None, None] channels = range(len(images)) return targetPsfImage, psfImages, images, channels, seds, morphs, targetPsf, psfs
def testMultiPlaneFitsReaders(self): """Run tests for MaskedImageFitsReader and ExposureFitsReader. """ metadata = PropertyList() metadata.add("FIVE", 5) metadata.add("SIX", 6.0) wcs = makeSkyWcs(Point2D(2.5, 3.75), SpherePoint(40.0 * degrees, 50.0 * degrees), np.array([[1E-5, 0.0], [0.0, -1E-5]])) defineFilter("test_readers_filter", lambdaEff=470.0) calib = PhotoCalib(2.5E4) psf = GaussianPsf(21, 21, 8.0) polygon = Polygon(Box2D(self.bbox)) apCorrMap = ApCorrMap() visitInfo = VisitInfo(exposureTime=5.0) transmissionCurve = TransmissionCurve.makeIdentity() coaddInputs = CoaddInputs(ExposureTable.makeMinimalSchema(), ExposureTable.makeMinimalSchema()) detector = DetectorWrapper().detector record = coaddInputs.ccds.addNew() record.setWcs(wcs) record.setPhotoCalib(calib) record.setPsf(psf) record.setValidPolygon(polygon) record.setApCorrMap(apCorrMap) record.setVisitInfo(visitInfo) record.setTransmissionCurve(transmissionCurve) record.setDetector(detector) for n, dtypeIn in enumerate(self.dtypes): with self.subTest(dtypeIn=dtypeIn): exposureIn = Exposure(self.bbox, dtype=dtypeIn) shape = exposureIn.image.array.shape exposureIn.image.array[:, :] = np.random.randint(low=1, high=5, size=shape) exposureIn.mask.array[:, :] = np.random.randint(low=1, high=5, size=shape) exposureIn.variance.array[:, :] = np.random.randint(low=1, high=5, size=shape) exposureIn.setMetadata(metadata) exposureIn.setWcs(wcs) exposureIn.setFilter(Filter("test_readers_filter")) exposureIn.setFilterLabel( FilterLabel(physical="test_readers_filter")) exposureIn.setPhotoCalib(calib) exposureIn.setPsf(psf) exposureIn.getInfo().setValidPolygon(polygon) exposureIn.getInfo().setApCorrMap(apCorrMap) exposureIn.getInfo().setVisitInfo(visitInfo) exposureIn.getInfo().setTransmissionCurve(transmissionCurve) exposureIn.getInfo().setCoaddInputs(coaddInputs) exposureIn.setDetector(detector) with lsst.utils.tests.getTempFilePath(".fits") as fileName: exposureIn.writeFits(fileName) self.checkMaskedImageFitsReader(exposureIn, fileName, self.dtypes[n:]) self.checkExposureFitsReader(exposureIn, fileName, self.dtypes[n:])
def setUp(self): np.random.seed(1) self.bbox = Box2I(Point2I(1000, 2000), Extent2I(200, 100)) self.filters = ["G", "R", "I"] self.imgValue = 10 images = [ImageF(self.bbox, self.imgValue) for f in self.filters] mImage = MultibandImage.fromImages(self.filters, images) self.Mask = Mask[MaskPixel] # Store the default mask planes for later use maskPlaneDict = self.Mask().getMaskPlaneDict() self.defaultMaskPlanes = sorted(maskPlaneDict, key=maskPlaneDict.__getitem__) # reset so tests will be deterministic self.Mask.clearMaskPlaneDict() for p in ("BAD", "SAT", "INTRP", "CR", "EDGE"): self.Mask.addMaskPlane(p) self.maskValue = self.Mask.getPlaneBitMask("BAD") singles = [self.Mask(self.bbox) for f in range(len(self.filters))] for n in range(len(singles)): singles[n].set(self.maskValue) mMask = MultibandMask.fromMasks(self.filters, singles) self.varValue = 1e-2 images = [ImageF(self.bbox, self.varValue) for f in self.filters] mVariance = MultibandImage.fromImages(self.filters, images) self.kernelSize = 51 self.psfs = [GaussianPsf(self.kernelSize, self.kernelSize, 4.0) for f in self.filters] self.psfImage = np.array([p.computeKernelImage().array for p in self.psfs]) self.exposure = MultibandExposure(image=mImage, mask=mMask, variance=mVariance, psfs=self.psfs, filters=self.filters)
def makeExposure(self, universalId): """Make a tiny exposure with exposure info set, but no pixels In particular, exposure info is set as a record in a table, so it can be recorded in a coadd """ inputRecorder = self.coaddInputRecorder.makeCoaddTempExpRecorder(universalId, self.numExp) bbox = lsst.afw.geom.Box2I(lsst.afw.geom.Point2I(100, 100), lsst.afw.geom.Extent2I(10, 10)) detectorName = "detector {}".format(universalId) detector = lsst.afw.cameraGeom.testUtils.DetectorWrapper(name=detectorName, id=universalId).detector exp = lsst.afw.image.ExposureF(bbox) exp.setDetector(detector) expInfo = exp.getInfo() scale = 5.1e-5*lsst.afw.geom.degrees cdMatrix = lsst.afw.geom.makeCdMatrix(scale=scale) wcs = lsst.afw.geom.makeSkyWcs( crpix=lsst.afw.geom.Point2D(5, 5), crval=lsst.afw.geom.SpherePoint(10, 45, lsst.afw.geom.degrees), cdMatrix=cdMatrix, ) expInfo.setWcs(wcs) expInfo.setPsf(GaussianPsf(5, 5, 2.5)) expInfo.setPhotoCalib(lsst.afw.image.makePhotoCalibFromCalibZeroPoint(1.1e12, 2.2e10)) expInfo.setApCorrMap(self.makeApCorrMap()) expInfo.setValidPolygon(lsst.afw.geom.Polygon(lsst.afw.geom.Box2D(bbox).getCorners())) if self.version > 1: expInfo.setVisitInfo(self.makeVisitInfo()) inputRecorder.addCalExp(calExp=exp, ccdId=universalId, nGoodPix=100) inputRecorder.finish(coaddTempExp=exp, nGoodPix=100) return exp
def testPsf(self): psfImage = self.exposure.computePsfKernelImage().array self.assertFloatsAlmostEqual(psfImage, self.psfImage) newPsfs = [GaussianPsf(self.kernelSize, self.kernelSize, 1.0) for f in self.filters] newPsfImage = [p.computeImage().array for p in newPsfs] for psf, exposure in zip(newPsfs, self.exposure.singles): exposure.setPsf(psf) psfImage = self.exposure.computePsfKernelImage() self.assertFloatsAlmostEqual(psfImage.array, newPsfImage) psfImage = self.exposure.computePsfImage().array[0] self.assertFloatsAlmostEqual(psfImage, self.exposure["G"].getPsf().computeImage().array)
def loadData(psfSigma=1.5): """Prepare the data we need to run the example""" # Load sample input from disk mypath = lsst.utils.getPackageDir('afwdata') imFile = os.path.join(mypath, "CFHT", "D4", "cal-53535-i-797722_small_1.fits") exposure = afwImage.ExposureF(imFile) # add a filter afwImage.Filter.define(afwImage.FilterProperty(FilterName, 600, True)) exposure.setFilter(afwImage.Filter(FilterName)) # add a simple Gaussian PSF model psfModel = GaussianPsf(11, 11, psfSigma) exposure.setPsf(psfModel) return exposure
def makeExposure(self, universalId): """Make a tiny exposure with exposure info set, but no pixels In particular, exposure info is set as a record in a table, so it can be recorded in a coadd """ inputRecorder = self.coaddInputRecorder.makeCoaddTempExpRecorder( universalId, self.numExp) bbox = lsst.afw.geom.Box2I(lsst.afw.geom.Point2I(100, 100), lsst.afw.geom.Extent2I(10, 10)) detectorName = "detector {}".format(universalId) detector = lsst.afw.cameraGeom.testUtils.DetectorWrapper( name=detectorName, id=universalId).detector exp = lsst.afw.image.ExposureF(bbox) exp.setDetector(detector) expInfo = exp.getInfo() wcs = lsst.afw.image.makeWcs( IcrsCoord(10 * lsst.afw.geom.degrees, 45 * lsst.afw.geom.degrees), lsst.afw.geom.Point2D(5, 5), 5.1e-5, 0, 0, -5.1e-5, # CD: 11, 12, 21, 22 ) expInfo.setWcs(wcs) expInfo.setPsf(GaussianPsf(5, 5, 2.5)) calib = lsst.afw.image.Calib() calib.setFluxMag0(1.1e12, 2.2e10) expInfo.setCalib(calib) expInfo.setApCorrMap(self.makeApCorrMap()) expInfo.setValidPolygon(Polygon( lsst.afw.geom.Box2D(bbox).getCorners())) if self.version > 1: expInfo.setVisitInfo(self.makeVisitInfo()) inputRecorder.addCalExp(calExp=exp, ccdId=universalId, nGoodPix=100) inputRecorder.finish(coaddTempExp=exp, nGoodPix=100) return exp
def setUp(self): self.pgps = [] self.gps = [] for width, height, sigma in [(5, 5, 1.1), (5, 3, 1.2), (7, 7, 1.3)]: self.pgps.append(PyGaussianPsf(width, height, sigma)) self.gps.append(GaussianPsf(width, height, sigma))
def testComputeExposureSummary(self): """Make a fake exposure and background and compute summary. """ np.random.seed(12345) # Make an exposure with a noise image exposure = afwImage.ExposureF(100, 100) skySigma = 10.0 exposure.getImage().getArray()[:, :] = np.random.normal(0.0, skySigma, size=(100, 100)) exposure.getVariance().getArray()[:, :] = skySigma**2. # Set the visitInfo date = DateTime(date=59234.7083333334, system=DateTime.DateSystem.MJD) observatory = Observatory(-70.7366 * lsst.geom.degrees, -30.2407 * lsst.geom.degrees, 2650.) visitInfo = afwImage.VisitInfo(exposureTime=10.0, date=date, observatory=observatory) exposure.getInfo().setVisitInfo(visitInfo) # Install a Gaussian PSF psfSize = 2.0 psf = GaussianPsf(5, 5, psfSize) exposure.setPsf(psf) # Install a simple WCS scale = 0.2 * lsst.geom.arcseconds raCenter = 300.0 * lsst.geom.degrees decCenter = 0.0 * lsst.geom.degrees cdMatrix = makeCdMatrix(scale=scale) skyWcs = makeSkyWcs(crpix=exposure.getBBox().getCenter(), crval=lsst.geom.SpherePoint(raCenter, decCenter), cdMatrix=cdMatrix) exposure.setWcs(skyWcs) # Install a simple photoCalib photoCalib = afwImage.PhotoCalib(calibrationMean=0.3) zp = 2.5 * np.log10(photoCalib.getInstFluxAtZeroMagnitude()) exposure.setPhotoCalib(photoCalib) # Compute the background image bgGridSize = 10 bctrl = afwMath.BackgroundControl(afwMath.Interpolate.NATURAL_SPLINE) bctrl.setNxSample( int(exposure.getMaskedImage().getWidth() / bgGridSize) + 1) bctrl.setNySample( int(exposure.getMaskedImage().getHeight() / bgGridSize) + 1) backobj = afwMath.makeBackground(exposure.getMaskedImage().getImage(), bctrl) background = afwMath.BackgroundList() background.append(backobj) # Run the task expSummaryTask = ComputeExposureSummaryStatsTask() summary = expSummaryTask.run(exposure, None, background) # Test the outputs self.assertFloatsAlmostEqual(summary.psfSigma, psfSize) self.assertFloatsAlmostEqual(summary.psfIxx, psfSize**2.) self.assertFloatsAlmostEqual(summary.psfIyy, psfSize**2.) self.assertFloatsAlmostEqual(summary.psfIxy, 0.0) self.assertFloatsAlmostEqual(summary.psfArea, 23.088975164455444) delta = (scale * 50).asDegrees() for a, b in zip(summary.raCorners, [ raCenter.asDegrees() + delta, raCenter.asDegrees() - delta, raCenter.asDegrees() - delta, raCenter.asDegrees() + delta ]): self.assertFloatsAlmostEqual(a, b, atol=1e-10) for a, b in zip(summary.decCorners, [ decCenter.asDegrees() - delta, decCenter.asDegrees() - delta, decCenter.asDegrees() + delta, decCenter.asDegrees() + delta ]): self.assertFloatsAlmostEqual(a, b, atol=1e-10) self.assertFloatsAlmostEqual(summary.ra, raCenter.asDegrees(), atol=1e-10) self.assertFloatsAlmostEqual(summary.decl, decCenter.asDegrees(), atol=1e-10) self.assertFloatsAlmostEqual(summary.zeroPoint, zp) # Need to compare background level and noise # These are only approximately 0+/-10 because of the small image self.assertFloatsAlmostEqual(summary.skyBg, -0.079, atol=1e-3) self.assertFloatsAlmostEqual(summary.meanVar, skySigma**2.) self.assertFloatsAlmostEqual(summary.zenithDistance, 30.57112, atol=1e-5)
def makeMockVisitSummary(visit, ra_center=0.0, dec_center=-45.0, physical_filter='TEST-I', band='i', mjd=59234.7083333334, psf_sigma=3.0, zenith_distance=45.0, zero_point=30.0, sky_background=100.0, sky_noise=10.0, mean_var=100.0, exposure_time=100.0, detector_size=200, pixel_scale=0.2): """Make a mock visit summary catalog. This will contain two square detectors with the same metadata, with a small (20 pixel) gap between the detectors. There is no rotation, as each detector is simply offset in RA from the specified boresight. Parameters ---------- visit : `int` Visit number. ra_center : `float` Right ascension of the center of the "camera" boresight (degrees). dec_center : `float` Declination of the center of the "camera" boresight (degrees). physical_filter : `str` Arbitrary name for the physical filter. band : `str` Name of the associated band. mjd : `float` Modified Julian Date. psf_sigma : `float` Sigma width of Gaussian psf. zenith_distance : `float` Distance from zenith of the visit (degrees). zero_point : `float` Constant zero point for the visit (magnitudes). sky_background : `float` Background level for the visit (counts). sky_noise : `float` Noise level for the background of the visit (counts). mean_var : `float` Mean of the variance plane of the visit (counts). exposure_time : `float` Exposure time of the visit (seconds). detector_size : `int` Size of each square detector in the visit (pixels). pixel_scale : `float` Size of the pixel in arcseconds per pixel. Returns ------- visit_summary : `lsst.afw.table.ExposureCatalog` """ # We are making a 2 detector "camera" n_detector = 2 schema = ConsolidateVisitSummaryTask()._makeVisitSummarySchema() visit_summary = afwTable.ExposureCatalog(schema) visit_summary.resize(n_detector) bbox = geom.Box2I(x=geom.IntervalI(min=0, max=detector_size - 1), y=geom.IntervalI(min=0, max=detector_size - 1)) for detector_id in range(n_detector): row = visit_summary[detector_id] row['id'] = detector_id row.setBBox(bbox) row['visit'] = visit row['physical_filter'] = physical_filter row['band'] = band row['zenithDistance'] = zenith_distance row['zeroPoint'] = zero_point row['skyBg'] = sky_background row['skyNoise'] = sky_noise row['meanVar'] = mean_var # Generate a photocalib instFluxMag0 = 10.**(zero_point / 2.5) row.setPhotoCalib( afwImage.makePhotoCalibFromCalibZeroPoint(instFluxMag0)) # Generate a WCS and set values accordingly crpix = geom.Point2D(detector_size / 2., detector_size / 2.) # Create a 20 pixel gap between the two detectors (each offset 10 pixels). if detector_id == 0: delta_ra = -1.0 * ((detector_size + 10) * pixel_scale / 3600.) / np.cos(np.deg2rad(dec_center)) delta_dec = 0.0 elif detector_id == 1: delta_ra = ((detector_size + 10) * pixel_scale / 3600.) / np.cos( np.deg2rad(dec_center)) delta_dec = 0.0 crval = geom.SpherePoint(ra_center + delta_ra, dec_center + delta_dec, geom.degrees) cd_matrix = afwGeom.makeCdMatrix(scale=pixel_scale * geom.arcseconds, orientation=0.0 * geom.degrees) wcs = afwGeom.makeSkyWcs(crpix=crpix, crval=crval, cdMatrix=cd_matrix) row.setWcs(wcs) sph_pts = wcs.pixelToSky(geom.Box2D(bbox).getCorners()) row['raCorners'] = np.array( [float(sph.getRa().asDegrees()) for sph in sph_pts]) row['decCorners'] = np.array( [float(sph.getDec().asDegrees()) for sph in sph_pts]) sph_pt = wcs.pixelToSky(bbox.getCenter()) row['ra'] = sph_pt.getRa().asDegrees() row['decl'] = sph_pt.getDec().asDegrees() # Generate a visitInfo. # This does not need to be consistent with the zenith angle in the table, # it just needs to be valid and have sufficient information to compute # exposure time and parallactic angle. date = DateTime(date=mjd, system=DateTime.DateSystem.MJD) visit_info = afwImage.VisitInfo( exposureId=visit, exposureTime=exposure_time, date=date, darkTime=0.0, boresightRaDec=geom.SpherePoint(ra_center, dec_center, geom.degrees), era=45.1 * geom.degrees, observatory=Observatory(11.1 * geom.degrees, 0.0 * geom.degrees, 0.333), boresightRotAngle=0.0 * geom.degrees, rotType=afwImage.RotType.SKY) row.setVisitInfo(visit_info) # Generate a PSF and set values accordingly psf = GaussianPsf(15, 15, psf_sigma) row.setPsf(psf) psfAvgPos = psf.getAveragePosition() shape = psf.computeShape(psfAvgPos) row['psfSigma'] = psf.getSigma() row['psfIxx'] = shape.getIxx() row['psfIyy'] = shape.getIyy() row['psfIxy'] = shape.getIxy() row['psfArea'] = shape.getArea() return visit_summary