def readFits(cls, path): """Read an external detrended image and create an LSST Exposure object from it. Any significant processing (e.g. pixel interpolation) should probably be done in a ExternalIsrTask instead of here so it can be done once and saved, instead of being done every time the image is loaded. THIS METHOD IS INCOMPLETE; IT MUST BE MODIFIED ACCORDING TO THE FORMAT OF THE DATA BEING LOADED. """ directory, filename = os.path.split(path) match = cls.EXTERNAL_REGEX.match(filename) camera = cls.getCameraFromVisit(match.group("visit")) # Customize the code below based on the camera determined above. # To support more than one camera it may be useful to delegate # to other methods that are specific to certain cameras. # Read the actual image in from the given path using e.g. astropy, # and use it to fill in various arrays below. bbox = Box2I(Point2I(0, 0), Extent2I(..., ...)) # width, height result = ExposureF(bbox) # main image, as a [y, x] numpy.float32 array result.image.array = ... # variance image, as a [y, x] numpy.float32 array result.variance.array = ... # This example includes masking NaN pixels as NO_DATA and pixels above # 1E5 counts as SAT. External information about where bad pixels # should be preferred when available, and obviously that saturation # threshold is just an example (saturation should actually be # determined before flat-fielding, of course). # Interpolating these bad pixels is handled by ExternalIsrTask. noDataBitMask = result.mask.getPlaneBitMask("NO_DATA") satBitMask = result.mask.getPlaneBitMask("SAT") result.mask.array |= noDataBitMask * np.isnan(result.image.array) result.mask.array |= satBitMask * (result.image.array > 1E5) # If you have a better guess at the PSF, we can find a way to use it. # But it'd be a good idea to at least put this in with a guess at the # seeing (RMS in pixels). result.setPsf(SingleGaussianPsf(seeingRMS)) # Add a guess for the WCS, in this case assuming it's in the FITS # header of the first HDU. Need to have something here, even if it # isn't very good (e.g. whatever comes from the telescope). metadata = readMetadata(filename) wcs = SkyWcs(metadata) result.setWcs(wcs) return result
class FilterFractionTest(lsst.utils.tests.TestCase): def setUp(self): # Set up a Coadd with CoaddInputs tables that have blank filter columns to be filled # in by later test code. self.coadd = ExposureF(30, 90) # WCS is arbitrary, since it'll be the same for all images wcs = makeSkyWcs(crpix=Point2D(0, 0), crval=SpherePoint(45.0, 45.0, degrees), cdMatrix=makeCdMatrix(scale=0.17 * degrees)) self.coadd.setWcs(wcs) schema = ExposureCatalog.Table.makeMinimalSchema() self.filterKey = schema.addField("filter", type=str, doc="", size=16) weightKey = schema.addField("weight", type=float, doc="") # First input image covers the first 2/3, second covers the last 2/3, so they # overlap in the middle 1/3. inputs = ExposureCatalog(schema) self.input1 = inputs.addNew() self.input1.setId(1) self.input1.setBBox(Box2I(Point2I(0, 0), Point2I(29, 59))) self.input1.setWcs(wcs) self.input1.set(weightKey, 2.0) self.input2 = inputs.addNew() self.input2.setId(2) self.input2.setBBox(Box2I(Point2I(0, 30), Point2I(29, 89))) self.input2.setWcs(wcs) self.input2.set(weightKey, 3.0) # Use the same catalog for visits and CCDs since the algorithm we're testing only cares # about CCDs. self.coadd.getInfo().setCoaddInputs(CoaddInputs(inputs, inputs)) # Set up a catalog with centroids and a FilterFraction plugin. # We have one record in each region (first input only, both inputs, second input only) schema = SourceCatalog.Table.makeMinimalSchema() centroidKey = Point2DKey.addFields(schema, "centroid", doc="position", unit="pixel") schema.getAliasMap().set("slot_Centroid", "centroid") self.plugin = FilterFractionPlugin( config=FilterFractionPlugin.ConfigClass(), schema=schema, name="subaru_FilterFraction", metadata=PropertyList()) catalog = SourceCatalog(schema) self.record1 = catalog.addNew() self.record1.set(centroidKey, Point2D(14.0, 14.0)) self.record12 = catalog.addNew() self.record12.set(centroidKey, Point2D(14.0, 44.0)) self.record2 = catalog.addNew() self.record2.set(centroidKey, Point2D(14.0, 74.0)) def tearDown(self): del self.coadd del self.input1 del self.input2 del self.record1 del self.record2 del self.record12 del self.plugin def testSingleFilter(self): """Test that we get FilterFraction=1 for filters with only one version.""" self.input1.set(self.filterKey, "g") self.input2.set(self.filterKey, "g") self.plugin.measure(self.record1, self.coadd) self.plugin.measure(self.record12, self.coadd) self.plugin.measure(self.record2, self.coadd) self.assertEqual(self.record1.get("subaru_FilterFraction_unweighted"), 1.0) self.assertEqual(self.record12.get("subaru_FilterFraction_unweighted"), 1.0) self.assertEqual(self.record2.get("subaru_FilterFraction_unweighted"), 1.0) self.assertEqual(self.record1.get("subaru_FilterFraction_weighted"), 1.0) self.assertEqual(self.record12.get("subaru_FilterFraction_weighted"), 1.0) self.assertEqual(self.record2.get("subaru_FilterFraction_weighted"), 1.0) def testTwoFiltersI(self): """Test that we get the right answers for a mix of i and i2.""" self.input1.set(self.filterKey, "i") self.input2.set(self.filterKey, "i2") self.plugin.measure(self.record1, self.coadd) self.plugin.measure(self.record12, self.coadd) self.plugin.measure(self.record2, self.coadd) self.assertEqual(self.record1.get("subaru_FilterFraction_unweighted"), 0.0) self.assertEqual(self.record12.get("subaru_FilterFraction_unweighted"), 0.5) self.assertEqual(self.record2.get("subaru_FilterFraction_unweighted"), 1.0) self.assertEqual(self.record1.get("subaru_FilterFraction_weighted"), 0.0) self.assertEqual(self.record12.get("subaru_FilterFraction_weighted"), 0.6) self.assertEqual(self.record2.get("subaru_FilterFraction_weighted"), 1.0) def testTwoFiltersR(self): """Test that we get the right answers for a mix of r and r2.""" self.input1.set(self.filterKey, "r") self.input2.set(self.filterKey, "r2") self.plugin.measure(self.record1, self.coadd) self.plugin.measure(self.record12, self.coadd) self.plugin.measure(self.record2, self.coadd) self.assertEqual(self.record1.get("subaru_FilterFraction_unweighted"), 0.0) self.assertEqual(self.record12.get("subaru_FilterFraction_unweighted"), 0.5) self.assertEqual(self.record2.get("subaru_FilterFraction_unweighted"), 1.0) self.assertEqual(self.record1.get("subaru_FilterFraction_weighted"), 0.0) self.assertEqual(self.record12.get("subaru_FilterFraction_weighted"), 0.6) self.assertEqual(self.record2.get("subaru_FilterFraction_weighted"), 1.0) def testInvalidCombination(self): """Test that we get a fatal exception for weird combinations of filters.""" self.input1.set(self.filterKey, "i") self.input2.set(self.filterKey, "r") with self.assertRaises(FatalAlgorithmError): self.plugin.measure(self.record12, self.coadd)
class joinMatchListWithCatalogTestCase(unittest.TestCase): def setUp(self): # Load sample input from disk testDir = os.path.dirname(__file__) self.srcSet = SourceCatalog.readFits(os.path.join(testDir, "v695833-e0-c000.xy.fits")) self.bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(2048, 4612)) # approximate # create an exposure with the right metadata; the closest thing we have is # apparently v695833-e0-c000-a00.sci.fits, which is much too small smallExposure = ExposureF(os.path.join(testDir, "v695833-e0-c000-a00.sci.fits")) self.exposure = ExposureF(self.bbox) self.exposure.setWcs(smallExposure.getWcs()) self.exposure.setFilter(smallExposure.getFilter()) # copy the pixels we can, in case the user wants a debug display mi = self.exposure.getMaskedImage() mi.assign(smallExposure.getMaskedImage(), smallExposure.getBBox()) logLevel = Log.INFO refCatDir = os.path.join(testDir, "data", "sdssrefcat") butler = Butler(refCatDir) refObjLoader = LoadIndexedReferenceObjectsTask(butler=butler) astrometryConfig = AstrometryTask.ConfigClass() self.astrom = AstrometryTask(config=astrometryConfig, refObjLoader=refObjLoader) self.astrom.log.setLevel(logLevel) # Since our sourceSelector is a registry object we have to wait for it to be created # before setting default values. self.astrom.matcher.sourceSelector.config.minSnr = 0 def tearDown(self): del self.srcSet del self.bbox del self.exposure del self.astrom def getAstrometrySolution(self): return self.astrom.solve(exposure=self.exposure, sourceCat=self.srcSet) def testJoin(self): res = self.getAstrometrySolution() matches = res.matches matchmeta = res.matchMeta normalized = packMatches(matches) normalized.table.setMetadata(matchmeta) matches2 = self.astrom.refObjLoader.joinMatchListWithCatalog(normalized, self.srcSet) self.assertEqual(len(matches2), len(matches)) for i in range(len(matches)): self.assertEqual(matches2[i].second.table, matches[i].second.table) self.assertEqual(matches2[i].second.getId(), matches[i].second.getId()) self.assertEqual(matches2[i].second, matches[i].second) # no deep copying, so we can compare ptrs self.assertEqual(matches2[i].first.getId(), matches[i].first.getId()) self.assertEqual(matches2[i].first.getRa().asDegrees(), matches[i].first.getRa().asDegrees()) self.assertEqual(matches2[i].first.getDec().asDegrees(), matches[i].first.getDec().asDegrees()) self.assertEqual(matches2[i].first.get("i_flux"), matches[i].first.get("i_flux")) def testJoinAllFluxes(self): """Test that we can read all the fluxes back from an a.n.d catalogue""" res = self.getAstrometrySolution() matches = res.matches matchmeta = res.matchMeta normalized = packMatches(matches) normalized.table.setMetadata(matchmeta) matches2 = self.astrom.refObjLoader.joinMatchListWithCatalog(normalized, self.srcSet) self.assertGreater(len(matches2), 0) ref = matches2[0][0] names = ref.getSchema().getNames() for b in ("u", "g", "r", "i", "z"): self.assertIn("%s_flux" % (b,), names) self.assertIn("%s_fluxSigma" % (b,), names)
class JoinMatchListWithCatalogTestCase(unittest.TestCase): def setUp(self): # Load sample input from disk testDir = os.path.dirname(__file__) self.srcSet = SourceCatalog.readFits(os.path.join(testDir, "v695833-e0-c000.xy.fits")) self.bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(2048, 4612)) # approximate # create an exposure with the right metadata; the closest thing we have is # apparently v695833-e0-c000-a00.sci.fits, which is much too small smallExposure = ExposureF(os.path.join(testDir, "v695833-e0-c000-a00.sci.fits")) self.exposure = ExposureF(self.bbox) self.exposure.setWcs(smallExposure.getWcs()) self.exposure.setFilter(smallExposure.getFilter()) # copy the pixels we can, in case the user wants a debug display mi = self.exposure.getMaskedImage() mi.assign(smallExposure.getMaskedImage(), smallExposure.getBBox()) logLevel = Log.INFO refCatDir = os.path.join(testDir, "data", "sdssrefcat") butler = Butler(refCatDir) refObjLoader = LoadIndexedReferenceObjectsTask(butler=butler) astrometryConfig = AstrometryTask.ConfigClass() self.astrom = AstrometryTask(config=astrometryConfig, refObjLoader=refObjLoader) self.astrom.log.setLevel(logLevel) # Since our sourceSelector is a registry object we have to wait for it to be created # before setting default values. self.astrom.sourceSelector.config.minSnr = 0 def tearDown(self): del self.srcSet del self.bbox del self.exposure del self.astrom def getAstrometrySolution(self): return self.astrom.solve(exposure=self.exposure, sourceCat=self.srcSet) def testJoin(self): res = self.getAstrometrySolution() matches = res.matches matchmeta = res.matchMeta normalized = packMatches(matches) normalized.table.setMetadata(matchmeta) matches2 = self.astrom.refObjLoader.joinMatchListWithCatalog(normalized, self.srcSet) self.assertEqual(len(matches2), len(matches)) for i in range(len(matches)): self.assertEqual(matches2[i].second.table, matches[i].second.table) self.assertEqual(matches2[i].second.getId(), matches[i].second.getId()) self.assertEqual(matches2[i].second, matches[i].second) # no deep copying, so we can compare ptrs self.assertEqual(matches2[i].first.getId(), matches[i].first.getId()) self.assertEqual(matches2[i].first.getRa().asDegrees(), matches[i].first.getRa().asDegrees()) self.assertEqual(matches2[i].first.getDec().asDegrees(), matches[i].first.getDec().asDegrees()) self.assertEqual(matches2[i].first.get("i_flux"), matches[i].first.get("i_flux")) def testJoinAllFluxes(self): """Test that we can read all the fluxes from a reference catalog""" res = self.getAstrometrySolution() matches = res.matches matchmeta = res.matchMeta normalized = packMatches(matches) normalized.table.setMetadata(matchmeta) matches2 = self.astrom.refObjLoader.joinMatchListWithCatalog(normalized, self.srcSet) self.assertGreater(len(matches2), 0) ref = matches2[0][0] refSchema = ref.getSchema() for b in ("u", "g", "r", "i", "z"): self.assertIn("%s_flux" % (b,), refSchema) self.assertIn("%s_fluxErr" % (b,), refSchema)