예제 #1
0
 def setUp(self):
     self.exposure = lsst.afw.image.ExposureF(201, 201)
     # for convenience, we'll put the source at the origin
     self.exposure.setXY0(lsst.afw.geom.Point2I(-100, -100))
     self.exposure.getMaskedImage().getVariance()[:] = 1.0
     self.psf = lsst.meas.algorithms.DoubleGaussianPsf(
         71, 71, 8.0, 15.0, 1.0)
     self.exposure.setPsf(self.psf)
     self.flux = 50.0
     psfImage = self.psf.computeImage()
     box = psfImage.getBBox(lsst.afw.image.PARENT)
     image = self.exposure.getMaskedImage().getImage()
     subImage = image.Factory(image, box, lsst.afw.image.PARENT, False)
     subImage.scaledPlus(self.flux, psfImage.convertF())
     self.footprint = lsst.afw.detection.Footprint(box)
     self.footprint.getPeaks().append(lsst.afw.detection.Peak(0, 0))
     self.config = lsst.meas.algorithms.SourceMeasurementConfig()
     self.config.algorithms.names = [
         "flux.psf", "flux.gaussian", "flux.sinc", "correctfluxes"
     ]
     self.config.algorithms.names.add("shape.sdss")
     self.config.centroider.name = None
     self.config.slots.centroid = None
     self.config.slots.shape = None
     self.config.doReplaceWithNoise = False
예제 #2
0
 def setUp(self):
     self.exposure = lsst.afw.image.ExposureF(201, 201)
     # for convenience, we'll put the source at the origin
     self.exposure.setXY0(lsst.afw.geom.Point2I(-100, -100))
     self.psf = lsst.meas.algorithms.DoubleGaussianPsf(
         71, 71, 8.0, 15.0, 1.0)
     self.exposure.setPsf(self.psf)
     self.flux = 50.0
     self.radii = [3.0, 6.0, 9.0, 12.0, 15.0, 98.0, 200.0]
     psfImage = self.psf.computeImage()
     box = psfImage.getBBox(lsst.afw.image.PARENT)
     image = self.exposure.getMaskedImage().getImage()
     subImage = image.Factory(image, box, lsst.afw.image.PARENT, False)
     subImage.scaledPlus(self.flux, psfImage.convertF())
     self.footprint = lsst.afw.detection.Footprint(box)
     self.footprint.addPeak(0, 0, float("NaN"))
     self.config = lsst.meas.algorithms.SourceMeasurementConfig()
     self.config.algorithms.names = [
         "flux.sinc", "flux.naive", "flux.aperture"
     ]
     self.config.algorithms["flux.sinc"].radius = 3.0
     self.config.algorithms["flux.naive"].radius = 15.0
     self.config.algorithms["flux.aperture"].maxSincRadius = 10.0
     self.config.algorithms["flux.aperture"].radii = self.radii
     self.config.centroider.name = None
     self.config.slots.centroid = None
     self.config.slots.shape = None
     self.config.slots.apFlux = None
     self.config.slots.modelFlux = None
     self.config.slots.psfFlux = None
     self.config.slots.instFlux = None
     self.config.slots.calibFlux = None
     self.config.doReplaceWithNoise = False
예제 #3
0
    def drawSource(self, record, exposure, buffer=0):
        """Draw the given truth catalog record on the given exposure, makings use of its Psf, Wcs,
        PhotoCalib, and possibly filter to transform it appropriately.

        The mask and variance planes of the Exposure are ignored.

        The 'buffer' parameter is used to expand the source's bounding box when testing whether it
        is considered fully part of the image.

        Returns 0 if the object does not appear on the given image at all, 1 if it appears partially,
        and 2 if it appears fully (including the given buffer).
        """
        wcs = exposure.getWcs()
        center = wcs.skyToPixel(record.getCoord())
        try:
            psfImage = exposure.getPsf().computeImage(center).convertF()
        except Exception:
            return 0
        psfBBox = psfImage.getBBox()
        overlap = exposure.getBBox()
        overlap.clip(psfBBox)
        if overlap.isEmpty():
            return 0
        flux = exposure.getPhotoCalib().magnitudeToInstFlux(
            record.getD(self.magKey))
        normalization = flux / psfImage.getArray().sum()
        if psfBBox != overlap:
            psfImage = psfImage.Factory(psfImage, overlap)
            result = 1
        else:
            result = 2
            if buffer != 0:
                bufferedBBox = lsst.geom.Box2I(psfBBox)
                bufferedBBox.grow(buffer)
                bufferedOverlap = exposure.getBBox()
                bufferedOverlap.clip(bufferedBBox)
                if bufferedOverlap != bufferedBBox:
                    result = 1
        image = exposure.getMaskedImage().getImage()
        subImage = image.Factory(image, overlap)
        subImage.scaledPlus(normalization, psfImage)
        return result
예제 #4
0
    def setUp(self):
        width, height = 250, 500
        self.numAmps = 4
        numPixelsPerAmp = 1000
        # crosstalk[i][j] is the fraction of the j-th amp present on the i-th
        # amp.
        self.crosstalk = [[0.0, 1e-4, 2e-4, 3e-4], [3e-4, 0.0, 2e-4, 1e-4],
                          [4e-4, 5e-4, 0.0, 6e-4], [7e-4, 8e-4, 9e-4, 0.0]]
        self.value = 12345
        self.crosstalkStr = "XTLK"

        # A bit of noise is important, because otherwise the pixel
        # distributions are razor-thin and then rejection doesn't work.
        rng = np.random.RandomState(12345)
        self.noise = rng.normal(0.0, 0.1, (2 * height, 2 * width))

        # Create amp images
        withoutCrosstalk = [
            lsst.afw.image.ImageF(width, height) for _ in range(self.numAmps)
        ]
        for image in withoutCrosstalk:
            image.set(0)
            xx = rng.randint(0, width, numPixelsPerAmp)
            yy = rng.randint(0, height, numPixelsPerAmp)
            image.getArray()[yy, xx] = self.value

        # Add in crosstalk
        withCrosstalk = [
            image.Factory(image, True) for image in withoutCrosstalk
        ]
        for ii, iImage in enumerate(withCrosstalk):
            for jj, jImage in enumerate(withoutCrosstalk):
                value = self.crosstalk[ii][jj]
                iImage.scaledPlus(value, jImage)

        # Put amp images together
        def construct(imageList):
            image = lsst.afw.image.ImageF(2 * width, 2 * height)
            image.getArray()[:height, :width] = imageList[0].getArray()
            image.getArray()[:height,
                             width:] = imageList[1].getArray()[:, ::
                                                               -1]  # flip in x
            image.getArray()[height:, :width] = imageList[2].getArray(
            )[::-1, :]  # flip in y
            image.getArray()[height:, width:] = imageList[3].getArray(
            )[::-1, ::-1]  # flip in x and y
            image.getArray()[:] += self.noise
            return image

        # Construct detector
        detName = 'detector 1'
        detId = 1
        detSerial = 'serial 1'
        orientation = cameraGeom.Orientation()
        pixelSize = lsst.geom.Extent2D(1, 1)
        bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
                               lsst.geom.Extent2I(2 * width, 2 * height))
        crosstalk = np.array(self.crosstalk, dtype=np.float32)

        camBuilder = cameraGeom.Camera.Builder("fakeCam")
        detBuilder = camBuilder.add(detName, detId)
        detBuilder.setSerial(detSerial)
        detBuilder.setBBox(bbox)
        detBuilder.setOrientation(orientation)
        detBuilder.setPixelSize(pixelSize)
        detBuilder.setCrosstalk(crosstalk)

        # Construct second detector in this fake camera
        detName = 'detector 2'
        detId = 2
        detSerial = 'serial 2'
        orientation = cameraGeom.Orientation()
        pixelSize = lsst.geom.Extent2D(1, 1)
        bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
                               lsst.geom.Extent2I(2 * width, 2 * height))
        crosstalk = np.array(self.crosstalk, dtype=np.float32)

        detBuilder2 = camBuilder.add(detName, detId)
        detBuilder2.setSerial(detSerial)
        detBuilder2.setBBox(bbox)
        detBuilder2.setOrientation(orientation)
        detBuilder2.setPixelSize(pixelSize)
        detBuilder2.setCrosstalk(crosstalk)

        # Create amp info
        for ii, (xx, yy, corner) in enumerate([
            (0, 0, lsst.afw.cameraGeom.ReadoutCorner.LL),
            (width, 0, lsst.afw.cameraGeom.ReadoutCorner.LR),
            (0, height, lsst.afw.cameraGeom.ReadoutCorner.UL),
            (width, height, lsst.afw.cameraGeom.ReadoutCorner.UR)
        ]):

            amp = cameraGeom.Amplifier.Builder()
            amp.setName("amp %d" % ii)
            amp.setBBox(
                lsst.geom.Box2I(lsst.geom.Point2I(xx, yy),
                                lsst.geom.Extent2I(width, height)))
            amp.setRawDataBBox(
                lsst.geom.Box2I(lsst.geom.Point2I(xx, yy),
                                lsst.geom.Extent2I(width, height)))
            amp.setReadoutCorner(corner)
            detBuilder.append(amp)
            detBuilder2.append(amp)

        cam = camBuilder.finish()
        ccd1 = cam.get('detector 1')
        ccd2 = cam.get('detector 2')

        self.exposure = lsst.afw.image.makeExposure(
            lsst.afw.image.makeMaskedImage(construct(withCrosstalk)))
        self.exposure.setDetector(ccd1)

        # Create a single ctSource that will be used for interChip CT
        # correction.
        self.ctSource = lsst.afw.image.makeExposure(
            lsst.afw.image.makeMaskedImage(construct(withCrosstalk)))
        self.ctSource.setDetector(ccd2)

        self.corrected = construct(withoutCrosstalk)

        if display:
            disp = lsst.afw.display.Display(frame=1)
            disp.mtv(self.exposure, title="exposure")
            disp = lsst.afw.display.Display(frame=0)
            disp.mtv(self.corrected, title="corrected exposure")
    def convolve(self, exposure, seeing, target, footprint, maxRadius):
        """Convolve image around source to target seeing

        We also record the original seeing at the source position.

        Because we don't want to convolve the entire image just for this source,
        we cut out an area corresponding to the source's footprint, grown by the
        radius provided by `maxRadius`.

        We assume a Gaussian PSF to simplify and speed the convolution.
        The `seeing` and `target` may be either Gaussian sigma or FWHM, so long
        as they are the same.

        Parameters
        ----------
        exposure : `lsst.afw.image.Exposure`
            Image to convolve.
        seeing : `float`
            Current seeing, pixels.
        target : `float`
            Desired target seeing, pixels.
        footprint : `lsst.afw.detection.Footprint`
            Footprint for source.
        maxRadius : `int`
            Maximum radius required by measurement algorithms.

        Returns
        -------
        convExp : `lsst.afw.image.Exposure`
            Sub-image containing the source, convolved to the target seeing.

        Raises
        ------
        DeconvolutionError
            If the target seeing requires deconvolution.
        RuntimeError
            If the bounding box is too small after clipping.
        """

        if target < seeing:
            raise DeconvolutionError("Target seeing requires deconvolution")
        kernelSigma = math.sqrt(target*target - seeing*seeing)
        kernelRadius = int(self.config.kernelScale*kernelSigma + 0.5)
        kernelWidth = 2*kernelRadius + 1
        gauss = lsst.afw.math.GaussianFunction1D(kernelSigma)
        kernel = lsst.afw.math.SeparableKernel(kernelWidth, kernelWidth, gauss, gauss)

        bbox = footprint.getBBox()
        bbox.grow(kernelRadius + maxRadius)  # add an extra buffer?
        bbox.clip(exposure.getBBox())
        if bbox.getWidth() < kernelWidth or bbox.getHeight() < kernelWidth:
            raise RuntimeError("Bounding box is too small following clipping")

        image = exposure.getMaskedImage()
        subImage = image.Factory(image, bbox)
        convolved = image.Factory(bbox)
        lsst.afw.math.convolve(convolved, subImage, kernel, lsst.afw.math.ConvolutionControl(True, True))

        convExp = lsst.afw.image.makeExposure(convolved)
        convExp.setInfo(lsst.afw.image.ExposureInfo(exposure.getInfo()))

        return convExp
예제 #6
0
    def setUp(self):
        width, height = 250, 500
        self.numAmps = 4
        numPixelsPerAmp = 1000
        # crosstalk[i][j] is the fraction of the j-th amp present on the i-th amp.
        self.crosstalk = [[0.0, 1e-4, 2e-4, 3e-4], [3e-4, 0.0, 2e-4, 1e-4],
                          [4e-4, 5e-4, 0.0, 6e-4], [7e-4, 8e-4, 9e-4, 0.0]]
        self.value = 12345
        self.crosstalkStr = "XTLK"

        # A bit of noise is important, because otherwise the pixel distributions are razor-thin
        # and then rejection doesn't work
        rng = np.random.RandomState(12345)
        self.noise = rng.normal(0.0, 0.1, (2 * height, 2 * width))

        # Create amp images
        withoutCrosstalk = [
            lsst.afw.image.ImageF(width, height) for _ in range(self.numAmps)
        ]
        for image in withoutCrosstalk:
            image.set(0)
            xx = rng.randint(0, width, numPixelsPerAmp)
            yy = rng.randint(0, height, numPixelsPerAmp)
            image.getArray()[yy, xx] = self.value

        # Add in crosstalk
        withCrosstalk = [
            image.Factory(image, True) for image in withoutCrosstalk
        ]
        for ii, iImage in enumerate(withCrosstalk):
            for jj, jImage in enumerate(withoutCrosstalk):
                value = self.crosstalk[ii][jj]
                iImage.scaledPlus(value, jImage)

        # Put amp images together
        def construct(imageList):
            image = lsst.afw.image.ImageF(2 * width, 2 * height)
            image.getArray()[:height, :width] = imageList[0].getArray()
            image.getArray()[:height,
                             width:] = imageList[1].getArray()[:, ::
                                                               -1]  # flip in x
            image.getArray()[height:, :width] = imageList[2].getArray(
            )[::-1, :]  # flip in y
            image.getArray()[height:, width:] = imageList[3].getArray(
            )[::-1, ::-1]  # flip in x and y
            image.getArray()[:] += self.noise
            return image

        # Create amp info
        schema = lsst.afw.table.AmpInfoTable.makeMinimalSchema()
        amplifiers = lsst.afw.table.AmpInfoCatalog(schema)
        for ii, (xx, yy, corner) in enumerate([(0, 0, LL), (width, 0, LR),
                                               (0, height, UL),
                                               (width, height, UR)]):
            amp = amplifiers.addNew()
            amp.setName("amp %d" % ii)
            amp.setBBox(
                lsst.afw.geom.Box2I(lsst.afw.geom.Point2I(xx, yy),
                                    lsst.afw.geom.Extent2I(width, height)))
            amp.setRawDataBBox(
                lsst.afw.geom.Box2I(lsst.afw.geom.Point2I(xx, yy),
                                    lsst.afw.geom.Extent2I(width, height)))
            amp.setReadoutCorner(corner)

        # Put everything together
        ccd = lsst.afw.cameraGeom.Detector(
            "detector", 123, lsst.afw.cameraGeom.SCIENCE, "serial",
            lsst.afw.geom.Box2I(lsst.afw.geom.Point2I(0, 0),
                                lsst.afw.geom.Extent2I(2 * width, 2 * height)),
            amplifiers, lsst.afw.cameraGeom.Orientation(),
            lsst.afw.geom.Extent2D(1, 1), {},
            np.array(self.crosstalk, dtype=np.float32))

        self.exposure = lsst.afw.image.makeExposure(
            lsst.afw.image.makeMaskedImage(construct(withCrosstalk)))
        self.exposure.setDetector(ccd)

        self.corrected = construct(withoutCrosstalk)

        if display:
            disp = lsst.afw.display.Display(frame=1)
            disp.mtv(self.exposure, title="exposure")
            disp = lsst.afw.display.Display(frame=0)
            disp.mtv(self.corrected, title="corrected exposure")