Example #1
0
    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)
Example #2
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))
Example #3
0
    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)
Example #4
0
    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,
        )
Example #6
0
    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)
Example #7
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)
Example #9
0
    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)
Example #10
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=[],
        )
Example #11
0
    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:])
Example #12
0
    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
        ))
Example #13
0
    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,
        )
Example #14
0
    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)
Example #15
0
    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:])
Example #16
0
    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),
        )
Example #17
0
    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)
Example #18
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
Example #19
0
    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)