def testApproximate(self): """Test I/O for BackgroundLists with Approximate""" # approx and interp should be very close, but not the same img = self.getParabolaImage(256, 256) # try regular interpolated image (the default) interpStyle = afwMath.Interpolate.AKIMA_SPLINE undersampleStyle = afwMath.REDUCE_INTERP_ORDER bgCtrl = afwMath.BackgroundControl(6, 6) bgCtrl.setInterpStyle(interpStyle) bgCtrl.setUndersampleStyle(undersampleStyle) bkgd = afwMath.makeBackground(img, bgCtrl) interpImage = bkgd.getImageF() with lsst.utils.tests.getTempFilePath("_bgi.fits") as bgiFile, \ lsst.utils.tests.getTempFilePath("_bga.fits") as bgaFile: bglInterp = afwMath.BackgroundList() bglInterp.append((bkgd, interpStyle, undersampleStyle, afwMath.ApproximateControl.UNKNOWN, 0, 0, True)) bglInterp.writeFits(bgiFile) # try an approx background approxStyle = afwMath.ApproximateControl.CHEBYSHEV approxOrder = 2 actrl = afwMath.ApproximateControl(approxStyle, approxOrder) bkgd.getBackgroundControl().setApproximateControl(actrl) approxImage = bkgd.getImageF() bglApprox = afwMath.BackgroundList() bglApprox.append((bkgd, interpStyle, undersampleStyle, approxStyle, approxOrder, approxOrder, True)) bglApprox.writeFits(bgaFile) # take a difference and make sure the two are very similar interpNp = interpImage.getArray() diff = np.abs(interpNp - approxImage.getArray()) / interpNp # the image and interp/approx parameters are chosen so these limits # will be greater than machine precision for float. The two methods # should be measurably different (so we know we're not just getting the # same thing from the getImage() method. But they should be very close # since they're both doing the same sort of thing. tolSame = 1.0e-3 # should be the same to this order tolDiff = 1.0e-4 # should be different here self.assertLess(diff.max(), tolSame) self.assertGreater(diff.max(), tolDiff) # now see if we can reload them from files and get the same images # we wrote interpImage2 = afwMath.BackgroundList().readFits( bgiFile).getImage() approxImage2 = afwMath.BackgroundList().readFits( bgaFile).getImage() idiff = interpImage.getArray() - interpImage2.getArray() adiff = approxImage.getArray() - approxImage2.getArray() self.assertEqual(idiff.max(), 0.0) self.assertEqual(adiff.max(), 0.0)
def exposureToBackground(bgExp): """Convert an exposure to background model Calibs need to be persisted as an Exposure, so we need to convert the persisted Exposure to a background model. Parameters ---------- bgExp : `lsst.afw.image.Exposure` Background model in Exposure format. Returns ------- bg : `lsst.afw.math.BackgroundList` Background model """ header = bgExp.getMetadata() xMin = header.getScalar("BOX.MINX") yMin = header.getScalar("BOX.MINY") xMax = header.getScalar("BOX.MAXX") yMax = header.getScalar("BOX.MAXY") algorithm = header.getScalar("ALGORITHM") bbox = afwGeom.Box2I(afwGeom.Point2I(xMin, yMin), afwGeom.Point2I(xMax, yMax)) return afwMath.BackgroundList( (afwMath.BackgroundMI(bbox, bgExp.getMaskedImage()), afwMath.stringToInterpStyle(algorithm), afwMath.stringToUndersampleStyle("REDUCE_INTERP_ORDER"), afwMath.ApproximateControl.UNKNOWN, 0, 0, False))
def loadImageRun(self, calExp, calExpBkg): """Serial implementation of self.loadImage() for Gen3. Load and restore background to calExp and calExpBkg. Parameters ---------- calExp : `lsst.afw.image.Exposure` Detector level calExp image to process. calExpBkg : `lsst.afw.math.BackgroundList` Detector level background list associated with the calExp. Returns ------- calExp : `lsst.afw.image.Exposure` Background restored calExp. bgList : `lsst.afw.math.BackgroundList` New background list containing the restoration background. """ image = calExp.getMaskedImage() for bgOld in calExpBkg: statsImage = bgOld[0].getStatsImage() statsImage *= -1 image -= calExpBkg.getImage() bgList = afwMath.BackgroundList() for bgData in calExpBkg: bgList.append(bgData) if self.config.doMaskObjects: self.maskObjects.findObjects(calExp) return (calExp, bgList)
def testMockCalibrateTask(self): task = MockCalibrateTask() pipelineTests.assertValidInitOutput(task) # Even the real CalibrateTask won't pass assertValidOutput, because for # some reason the outputs are injected in runQuantum rather than run. self.butler.put(afwImage.ExposureF(), "icExp", self.visitId) self.butler.put(afwMath.BackgroundList(), "icExpBackground", self.visitId) self.butler.put(afwTable.SourceCatalog(), "icSrc", self.visitId) self.butler.put(afwTable.SimpleCatalog(), "gaia_dr2_20200414", self.htmId) self.butler.put(afwTable.SimpleCatalog(), "ps1_pv3_3pi_20170110", self.htmId) quantum = pipelineTests.makeQuantum( task, self.butler, self.visitId, { "exposure": self.visitId, "background": self.visitId, "icSourceCat": self.visitId, "astromRefCat": [self.htmId], "photoRefCat": [self.htmId], "outputExposure": self.visitId, "outputCat": self.visitId, "outputBackground": self.visitId, "matches": self.visitId, "matchesDenormalized": self.visitId, }) pipelineTests.runTestQuantum(task, self.butler, quantum, mockRun=False)
def run(self, exposure, background=None, stats=True, statsKeys=None): """Fit and subtract the background of an exposure. Parameters ---------- exposure : `lsst.afw.image.Exposure` Exposure whose background is to be subtracted. background : `lsst.afw.math.BackgroundList` Initial background model already subtracted. May be None if no background has been subtracted. stats : `bool` If True then measure the mean and variance of the full background model and record the results in the exposure's metadata. statsKeys : `tuple` Key names used to store the mean and variance of the background in the exposure's metadata (another tuple); if None then use ("BGMEAN", "BGVAR"); ignored if stats is false. Returns ------- background : `lsst.afw.math.BackgroundLst` Full background model (initial model with changes), contained in an `lsst.pipe.base.Struct`. """ if background is None: background = afwMath.BackgroundList() maskedImage = exposure.getMaskedImage() fitBg = self.fitBackground(maskedImage) maskedImage -= fitBg.getImageF(self.config.algorithm, self.config.undersampleStyle) actrl = fitBg.getBackgroundControl().getApproximateControl() background.append((fitBg, getattr(afwMath.Interpolate, self.config.algorithm), fitBg.getAsUsedUndersampleStyle(), actrl.getStyle(), actrl.getOrderX(), actrl.getOrderY(), actrl.getWeighting())) if stats: self._addStats(exposure, background, statsKeys=statsKeys) subFrame = getDebugFrame(self._display, "subtracted") if subFrame: subDisp = afwDisplay.getDisplay(frame=subFrame) subDisp.mtv(exposure, title="subtracted") bgFrame = getDebugFrame(self._display, "background") if bgFrame: bgDisp = afwDisplay.getDisplay(frame=bgFrame) bgImage = background.getImage() bgDisp.mtv(bgImage, title="background") return pipeBase.Struct( background=background, )
def testBackgroundListIO(self): """Test I/O for BackgroundLists""" bgCtrl = afwMath.BackgroundControl(10, 10) interpStyle = afwMath.Interpolate.AKIMA_SPLINE undersampleStyle = afwMath.REDUCE_INTERP_ORDER approxOrderX = 6 approxOrderY = 6 im = self.image.Factory(self.image, self.image.getBBox(afwImage.PARENT)) arr = im.getArray() arr += numpy.random.normal(size=(im.getHeight(), im.getWidth())) for astyle in afwMath.ApproximateControl.UNKNOWN, afwMath.ApproximateControl.CHEBYSHEV: actrl = afwMath.ApproximateControl(astyle, approxOrderX) bgCtrl.setApproximateControl(actrl) backgroundList = afwMath.BackgroundList() backImage = afwImage.ImageF(im.getDimensions()) for i in range(2): bkgd = afwMath.makeBackground(im, bgCtrl) if i == 0: # no need to call getImage backgroundList.append((bkgd, interpStyle, undersampleStyle, astyle, approxOrderX, approxOrderY)) else: backgroundList.append( bkgd) # Relies on having called getImage; deprecated backImage += bkgd.getImageF(interpStyle, undersampleStyle) fileName = "backgroundList.fits" try: backgroundList.writeFits(fileName) backgrounds = afwMath.BackgroundList.readFits(fileName) finally: if os.path.exists(fileName): os.unlink(fileName) img = backgrounds.getImage() # # Check that the read-back image is identical to that generated from the backgroundList # round-tripped to disk # backImage -= img self.assertEqual(np.min(backImage.getArray()), 0.0) self.assertEqual(np.max(backImage.getArray()), 0.0)
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 loadImage(self, cache, dataId): """Load original image and restore the sky This method runs on the slave nodes. Parameters ---------- cache : `lsst.pipe.base.Struct` Process pool cache. dataId : `dict` Data identifier. Returns ------- exposure : `lsst.afw.image.Exposure` Resultant exposure. """ cache.dataId = dataId cache.exposure = cache.butler.get("calexp", dataId, immediate=True).clone() bgOld = cache.butler.get("calexpBackground", dataId, immediate=True) image = cache.exposure.getMaskedImage() if self.config.doDetection: # We deliberately use the specified 'detectSigma' instead of the PSF, in order to better pick up # the faint wings of objects. results = self.detection.detectFootprints( cache.exposure, doSmooth=True, sigma=self.config.detectSigma, clearMask=True) if hasattr(results, "background") and results.background: # Restore any background that was removed during detection maskedImage += results.background.getImage() # We're removing the old background, so change the sense of all its components for bgData in bgOld: statsImage = bgData[0].getStatsImage() statsImage *= -1 image -= bgOld.getImage() cache.bgList = afwMath.BackgroundList() for bgData in bgOld: cache.bgList.append(bgData) return self.collect(cache)
def testBackgroundListIO(self): """Test I/O for BackgroundLists""" bgCtrl = afwMath.BackgroundControl(10, 10) interpStyle = afwMath.Interpolate.AKIMA_SPLINE undersampleStyle = afwMath.REDUCE_INTERP_ORDER approxOrderX = 6 approxOrderY = 6 approxWeighting = True im = self.image.Factory(self.image, self.image.getBBox()) arr = im.getArray() arr += np.random.normal(size=(im.getHeight(), im.getWidth())) for astyle in afwMath.ApproximateControl.UNKNOWN, afwMath.ApproximateControl.CHEBYSHEV: actrl = afwMath.ApproximateControl(astyle, approxOrderX) bgCtrl.setApproximateControl(actrl) backgroundList = afwMath.BackgroundList() backImage = afwImage.ImageF(im.getDimensions()) for i in range(2): bkgd = afwMath.makeBackground(im, bgCtrl) if i == 0: # no need to call getImage backgroundList.append( (bkgd, interpStyle, undersampleStyle, astyle, approxOrderX, approxOrderY, approxWeighting)) else: # Relies on having called getImage; deprecated with self.assertWarns(FutureWarning): backgroundList.append(bkgd) backImage += bkgd.getImageF(interpStyle, undersampleStyle) with lsst.utils.tests.getTempFilePath(".fits") as fileName: backgroundList.writeFits(fileName) backgrounds = afwMath.BackgroundList.readFits(fileName) img = backgrounds.getImage() # Check that the read-back image is identical to that generated from the backgroundList # round-tripped to disk backImage -= img self.assertEqual(np.min(backImage.getArray()), 0.0) self.assertEqual(np.max(backImage.getArray()), 0.0)
def run(self, exposure, exposureIdInfo=None, background=None, icSourceCat=None): """Produce calibration outputs with no processing. Parameters ---------- exposure : `lsst.afw.image.Exposure` Exposure to calibrate. exposureIdInfo : `lsst.obs.base.ExposureIdInfo` ID info for exposure. background : `lsst.afw.math.BackgroundList` Background model already subtracted from exposure. icSourceCat : `lsst.afw.table.SourceCatalog` A SourceCatalog from CharacterizeImageTask from which we can copy some fields. Returns ------- result : `lsst.pipe.base.Struct` Struct containing these fields: ``outputExposure`` Calibrated science exposure with refined WCS and PhotoCalib (`lsst.afw.image.Exposure`). ``outputBackground`` Model of background subtracted from exposure (`lsst.afw.math.BackgroundList`). ``outputCat`` Catalog of measured sources (`lsst.afw.table.SourceCatalog`). ``astromMatches`` List of source/refObj matches from the astrometry solver (`list` [`lsst.afw.table.ReferenceMatch`]). """ # Can't persist empty BackgroundList; DM-33714 bg = afwMath.BackgroundMI( geom.Box2I(geom.Point2I(0, 0), geom.Point2I(16, 16)), afwImage.MaskedImageF(16, 16)) return Struct( outputExposure=exposure, outputBackground=afwMath.BackgroundList(bg), outputCat=afwTable.SourceCatalog(), astromMatches=[], )
def testBackgroundList(self): """Test that a BackgroundLists behaves like a list""" bgCtrl = afwMath.BackgroundControl(10, 10) interpStyle = afwMath.Interpolate.AKIMA_SPLINE undersampleStyle = afwMath.REDUCE_INTERP_ORDER approxStyle = afwMath.ApproximateControl.UNKNOWN approxOrderX = 0 approxOrderY = 0 approxWeighting = False backgroundList = afwMath.BackgroundList() for i in range(2): bkgd = afwMath.makeBackground(self.image, bgCtrl) if i == 0: # no need to call getImage backgroundList.append( (bkgd, interpStyle, undersampleStyle, approxStyle, approxOrderX, approxOrderY, approxWeighting)) else: # Relies on having called getImage; deprecated with self.assertWarns(FutureWarning): backgroundList.append(bkgd) def assertBackgroundList(bgl): self.assertEqual(len(bgl), 2) # check that len() works for a in bgl: # check that we can iterate pass self.assertEqual(len(bgl[0]), 7) # check that we can index # check that we always have a tuple (bkgd, interp, under, # approxStyle, orderX, orderY, weighting) self.assertEqual(len(bgl[1]), 7) assertBackgroundList(backgroundList) # Check pickling new = pickle.loads(pickle.dumps(backgroundList)) assertBackgroundList(new) self.assertEqual(len(new), len(backgroundList)) for i, j in zip(new, backgroundList): self.assertBackgroundEqual(i[0], j[0]) self.assertEqual(i[1:], j[1:])
def measureBackground(self, image): """Measure a background model for image This doesn't use a full-featured background model (e.g., no Chebyshev approximation) because we just want the binning behaviour. This will allow us to average the bins later (`averageBackgrounds`). The `BackgroundMI` is wrapped in a `BackgroundList` so it can be pickled and persisted. Parameters ---------- image : `lsst.afw.image.MaskedImage` Image for which to measure background. Returns ------- bgModel : `lsst.afw.math.BackgroundList` Background model. """ stats = afwMath.StatisticsControl() stats.setAndMask(image.getMask().getPlaneBitMask(self.config.background.mask)) stats.setNanSafe(True) ctrl = afwMath.BackgroundControl( self.config.background.algorithm, max(int(image.getWidth()/self.config.background.xBinSize + 0.5), 1), max(int(image.getHeight()/self.config.background.yBinSize + 0.5), 1), "REDUCE_INTERP_ORDER", stats, self.config.background.statistic ) bg = afwMath.makeBackground(image, ctrl) return afwMath.BackgroundList(( bg, afwMath.stringToInterpStyle(self.config.background.algorithm), afwMath.stringToUndersampleStyle("REDUCE_INTERP_ORDER"), afwMath.ApproximateControl.UNKNOWN, 0, 0, False ))
def run(self, exposure, background=None, stats=True, statsKeys=None): """!Fit and subtract the background of an exposure @param[in,out] exposure exposure whose background is to be subtracted @param[in,out] background initial background model already subtracted from exposure (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted. @param[in] stats if True then measure the mean and variance of the full background model and record the results in the exposure's metadata @param[in] statsKeys key names used to store the mean and variance of the background in the exposure's metadata (a pair of strings); if None then use ("BGMEAN", "BGVAR"); ignored if stats is false @return an lsst.pipe.base.Struct containing: - background full background model (initial model with changes), an lsst.afw.math.BackgroundList """ if background is None: background = afwMath.BackgroundList() maskedImage = exposure.getMaskedImage() fitBg = self.fitBackground(maskedImage) maskedImage -= fitBg.getImageF() background.append(fitBg) if stats: self._addStats(exposure, background, statsKeys=statsKeys) subFrame = getDebugFrame(self._display, "subtracted") if subFrame: subDisp = afwDisplay.getDisplay(frame=subFrame) subDisp.mtv(exposure, title="subtracted") bgFrame = getDebugFrame(self._display, "background") if bgFrame: bgDisp = afwDisplay.getDisplay(frame=bgFrame) bgImage = background.getImage() bgDisp.mtv(bgImage, title="background") return pipeBase.Struct( background=background, )
def loadImage(self, cache, dataId): """Load original image and restore the sky This method runs on the slave nodes. Parameters ---------- cache : `lsst.pipe.base.Struct` Process pool cache. dataId : `dict` Data identifier. Returns ------- exposure : `lsst.afw.image.Exposure` Resultant exposure. """ cache.dataId = dataId cache.exposure = cache.butler.get(self.config.calexpType, dataId, immediate=True).clone() bgOld = cache.butler.get("calexpBackground", dataId, immediate=True) image = cache.exposure.getMaskedImage() # We're removing the old background, so change the sense of all its components for bgData in bgOld: statsImage = bgData[0].getStatsImage() statsImage *= -1 image -= bgOld.getImage() cache.bgList = afwMath.BackgroundList() for bgData in bgOld: cache.bgList.append(bgData) if self.config.doMaskObjects: self.maskObjects.findObjects(cache.exposure) return self.collect(cache)
def testBackgroundList(self): """Test that a BackgroundLists behaves like a list""" bgCtrl = afwMath.BackgroundControl(10, 10) interpStyle = afwMath.Interpolate.AKIMA_SPLINE undersampleStyle = afwMath.REDUCE_INTERP_ORDER backgroundList = afwMath.BackgroundList() for i in range(2): bkgd = afwMath.makeBackground(self.image, bgCtrl) if i == 0: backgroundList.append(( bkgd, interpStyle, undersampleStyle, )) # no need to call getImage else: backgroundList.append( bkgd) # Relies on having called getImage; deprecated def assertBackgroundList(bgl): self.assertEqual(len(bgl), 2) # check that len() works for a in bgl: # check that we can iterate pass self.assertEqual(len(bgl[0]), 3) # check that we can index self.assertEqual( len(bgl[1]), 3) # check that we always have a tuple (bkgd, interp, under) assertBackgroundList(backgroundList) # Check pickling new = pickle.loads(pickle.dumps(backgroundList)) assertBackgroundList(new) self.assertEqual(len(new), len(backgroundList)) for i, j in zip(new, backgroundList): self.assertBackgroundEqual(i[0], j[0]) self.assertEqual(i[1:], j[1:])
def run(self, exposure, exposureIdInfo=None, background=None): """Produce characterization outputs with no processing. Parameters ---------- exposure : `lsst.afw.image.Exposure` Exposure to characterize. exposureIdInfo : `lsst.obs.base.ExposureIdInfo` ID info for exposure. background : `lsst.afw.math.BackgroundList` Initial model of background already subtracted from exposure. Returns ------- result : `lsst.pipe.base.Struct` Struct containing these fields: ``characterized`` Characterized exposure (`lsst.afw.image.Exposure`). ``sourceCat`` Detected sources (`lsst.afw.table.SourceCatalog`). ``backgroundModel`` Model of background subtracted from exposure (`lsst.afw.math.BackgroundList`) ``psfCellSet`` Spatial cells of PSF candidates (`lsst.afw.math.SpatialCellSet`) """ # Can't persist empty BackgroundList; DM-33714 bg = afwMath.BackgroundMI( geom.Box2I(geom.Point2I(0, 0), geom.Point2I(16, 16)), afwImage.MaskedImageF(16, 16)) return Struct( characterized=exposure, sourceCat=afwTable.SourceCatalog(), backgroundModel=afwMath.BackgroundList(bg), psfCellSet=afwMath.SpatialCellSet(exposure.getBBox(), 10), )
def testBackgroundListIO(self): """Test I/O for BackgroundLists""" bgCtrl = afwMath.BackgroundControl(10, 10) interpStyle = afwMath.Interpolate.AKIMA_SPLINE undersampleStyle = afwMath.REDUCE_INTERP_ORDER backgroundList = afwMath.BackgroundList() backImage = afwImage.ImageF(self.image.getDimensions()) for i in range(2): bkgd = afwMath.makeBackground(self.image, bgCtrl) if i == 0: backgroundList.append(( bkgd, interpStyle, undersampleStyle, )) # no need to call getImage else: backgroundList.append( bkgd) # Relies on having called getImage; deprecated backImage += bkgd.getImageF(interpStyle, undersampleStyle) with utilsTests.getTempFilePath(".fits") as fileName: backgroundList.writeFits(fileName) backgrounds = afwMath.BackgroundList.readFits(fileName) img = backgrounds.getImage() # # Check that the read-back image is identical to that generated from the backgroundList # round-tripped to disk # backImage -= img self.assertEqual(np.min(backImage.getArray()), 0.0) self.assertEqual(np.max(backImage.getArray()), 0.0)
def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None): """Detect footprints. Parameters ---------- exposure : `lsst.afw.image.Exposure` Exposure to process; DETECTED{,_NEGATIVE} mask plane will be set in-place. doSmooth : `bool`, optional If True, smooth the image before detection using a Gaussian of width ``sigma``. sigma : `float`, optional Gaussian Sigma of PSF (pixels); used for smoothing and to grow detections; if `None` then measure the sigma of the PSF of the ``exposure``. clearMask : `bool`, optional Clear both DETECTED and DETECTED_NEGATIVE planes before running detection. expId : `dict`, optional Exposure identifier; unused by this implementation, but used for RNG seed by subclasses. Return Struct contents ---------------------- positive : `lsst.afw.detection.FootprintSet` Positive polarity footprints (may be `None`) negative : `lsst.afw.detection.FootprintSet` Negative polarity footprints (may be `None`) numPos : `int` Number of footprints in positive or 0 if detection polarity was negative. numNeg : `int` Number of footprints in negative or 0 if detection polarity was positive. background : `lsst.afw.math.BackgroundList` Re-estimated background. `None` if ``reEstimateBackground==False``. factor : `float` Multiplication factor applied to the configured detection threshold. """ maskedImage = exposure.maskedImage if clearMask: self.clearMask(maskedImage.getMask()) psf = self.getPsf(exposure, sigma=sigma) with self.tempWideBackgroundContext(exposure): convolveResults = self.convolveImage(maskedImage, psf, doSmooth=doSmooth) middle = convolveResults.middle sigma = convolveResults.sigma results = self.applyThreshold(middle, maskedImage.getBBox()) results.background = afwMath.BackgroundList() if self.config.doTempLocalBackground: self.applyTempLocalBackground(exposure, middle, results) self.finalizeFootprints(maskedImage.mask, results, sigma) if self.config.reEstimateBackground: self.reEstimateBackground(maskedImage, results.background) self.clearUnwantedResults(maskedImage.getMask(), results) self.display(exposure, results, middle) return results
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)