def subtractXTalk(mi, coeffs, minPixelToMask=45000, crosstalkStr="CROSSTALK"):
    """Subtract the crosstalk from MaskedImage mi given a set of coefficients

    The pixels affected by signal over minPixelToMask have the crosstalkStr bit set
    """
    sctrl = afwMath.StatisticsControl()
    sctrl.setAndMask(mi.getMask().getPlaneBitMask("BAD"))
    bkgd = afwMath.makeStatistics(mi, afwMath.MEDIAN, sctrl).getValue()
    #
    # These are the pixels that are bright enough to cause crosstalk (more precisely,
    # the ones that we label as causing crosstalk; in reality all pixels cause crosstalk)
    #
    tempStr = "TEMP"                    # mask plane used to record the bright pixels that we need to mask
    msk = mi.getMask()
    msk.addMaskPlane(tempStr)
    try:
        fs = afwDetect.FootprintSet(mi, afwDetect.Threshold(minPixelToMask), tempStr)

        mi.getMask().addMaskPlane(crosstalkStr)
        afwDisplay.getDisplay().setMaskPlaneColor(crosstalkStr, afwDisplay.MAGENTA)
        # the crosstalkStr bit will now be set whenever we subtract crosstalk
        fs.setMask(mi.getMask(), crosstalkStr)
        crosstalk = mi.getMask().getPlaneBitMask(crosstalkStr)

        width, height = mi.getDimensions()
        for i in range(nAmp):
            bbox = afwGeom.BoxI(afwGeom.PointI(i*(width//nAmp), 0), afwGeom.ExtentI(width//nAmp, height))
            ampI = mi.Factory(mi, bbox)
            for j in range(nAmp):
                if i == j:
                    continue

                bbox = afwGeom.BoxI(afwGeom.PointI(j*(width//nAmp), 0), afwGeom.ExtentI(width//nAmp, height))
                if (i + j)%2 == 1:
                    ampJ = afwMath.flipImage(mi.Factory(mi, bbox), True, False)  # no need for a deep copy
                else:
                    ampJ = mi.Factory(mi, bbox, afwImage.LOCAL, True)

                msk = ampJ.getMask()
                if np.all(msk.getArray() & msk.getPlaneBitMask("BAD")):
                    # Bad amplifier; ignore it completely --- its effect will come out in the bias
                    continue
                msk &= crosstalk

                ampJ -= bkgd
                ampJ *= coeffs[j][i]

                ampI -= ampJ
        #
        # Clear the crosstalkStr bit in the original bright pixels, where tempStr is set
        #
        msk = mi.getMask()
        temp = msk.getPlaneBitMask(tempStr)
        xtalk_temp = crosstalk | temp
        np_msk = msk.getArray()
        mask_indicies = np.where(np.bitwise_and(np_msk, xtalk_temp) == xtalk_temp)
        np_msk[mask_indicies] &= getattr(np, np_msk.dtype.name)(~crosstalk)

    finally:
        msk.removeAndClearMaskPlane(tempStr, True)  # added in afw #1853
Beispiel #2
0
def getAmplifier(image, amp, ampReference=None, offset=2):
    """Extract an image of the amplifier from the CCD, along with an offset version

    The amplifier image will be flipped (if required) to match the
    orientation of a nominated reference amplifier.

    An additional image, with the nominated offset applied, is also produced.

    @param image           Image of CCD
    @param amp             Index of amplifier
    @param ampReference    Index of reference amplifier
    @param offset          Offset to apply
    @return amplifier image, offset amplifier image
    """
    height = image.getHeight()
    ampBox = afwGeom.Box2I(afwGeom.Point2I(amp * 512, 0),
                           afwGeom.Extent2I(512, height))
    ampImage = image.Factory(image, ampBox, afwImage.LOCAL)
    if ampReference is not None and amp % 2 != ampReference % 2:
        ampImage = afwMath.flipImage(ampImage, True, False)
    offBox = afwGeom.Box2I(
        afwGeom.Point2I(offset if amp == ampReference else 0, 0),
        afwGeom.Extent2I(510, height))
    offsetImage = ampImage.Factory(ampImage, offBox, afwImage.LOCAL)
    return ampImage, offsetImage
def _assemble(filename, subtract_background, gains):
    if subtract_background:
        imageList = _read_with_bg_subtraction(filename)
    else:
        imageList = _read(filename)
        
    if gains != None:
        for i in range(len(imageList)):
            imageList[i].getImage().getArray()[:] *= gains[i]
 
    detsize = set([im.getMetadata().get("DETSIZE") for im in imageList])
    assert len(detsize) == 1, "Multiple DETSIZEs detected"
    detsize = detsize.pop()
    image = afwImage.ImageF(_regionToBox(detsize))
    for im in imageList:
        header = im.getMetadata()
        ltm11, ltm22 = map(int, [header.get("LTM1_1"), header.get("LTM2_2")])
        assert abs(ltm11) == 1 and abs(ltm22) == 1, "Binned data detected"
        assert "LTM1_2" not in header.names() and "LTM2_1" not in header.names(), "Rotated data detected"
        datasec = _regionToBox(header.get("DATASEC"))
        detsec = _regionToBox(header.get("DETSEC"))
        if subtract_background:
            data = afwImage.ImageF(im, datasec)
        else:
            data = afwImage.ImageF(im.getImage(), datasec)
        data = afwMath.flipImage(data, ltm11 < 0, ltm22 < 0)
        target = image.Factory(image, detsec)
        target <<= data
    return image
Beispiel #4
0
def subtractXTalk(mi, coeffs, minPixelToMask=45000, crosstalkStr="CROSSTALK"):
    """Subtract the crosstalk from MaskedImage mi given a set of coefficients

The pixels affected by signal over minPixelToMask have the crosstalkStr bit set
    """
    sctrl = afwMath.StatisticsControl()
    sctrl.setAndMask(mi.getMask().getPlaneBitMask("DETECTED"))
    bkgd = afwMath.makeStatistics(mi, afwMath.MEDIAN, sctrl).getValue()
    #
    # These are the pixels that are bright enough to cause crosstalk (more precisely,
    # the ones that we label as causing crosstalk; in reality all pixels cause crosstalk)
    #
    tempStr = "TEMP"                    # mask plane used to record the bright pixels that we need to mask
    mi.getMask().addMaskPlane(tempStr)
    try:
        fs = afwDetect.FootprintSet(mi, afwDetect.Threshold(minPixelToMask), tempStr)

        mi.getMask().addMaskPlane(crosstalkStr)
        afwDisplay.getDisplay().setMaskPlaneColor(crosstalkStr, afwDisplay.MAGENTA)
        fs.setMask(mi.getMask(), crosstalkStr) # the crosstalkStr bit will now be set whenever
                                               # we subtract crosstalk
        crosstalk = mi.getMask().getPlaneBitMask(crosstalkStr)

        width, height = mi.getDimensions()
        for i in range(nAmp):
            bbox = afwGeom.BoxI(afwGeom.PointI(i*(width//nAmp), 0), afwGeom.ExtentI(width//nAmp, height))
            ampI = mi.Factory(mi, bbox)
            for j in range(nAmp):
                if i == j:
                    continue

                bbox = afwGeom.BoxI(afwGeom.PointI(j*(width//nAmp), 0), afwGeom.ExtentI(width//nAmp, height))
                if (i + j)%2 == 1:
                    ampJ = afwMath.flipImage(mi.Factory(mi, bbox), True, False) # no need for a deep copy
                else:
                    ampJ = mi.Factory(mi, bbox, afwImage.LOCAL, True)

                msk = ampJ.getMask()
                if np.all(msk.getArray() & msk.getPlaneBitMask("SAT")):
                    # Bad amplifier; ignore it completely --- its effect will come out in the bias
                    continue
                msk &= crosstalk

                ampJ -= bkgd
                ampJ *= coeffs[j][i]

                ampI -= ampJ
        #
        # Clear the crosstalkStr bit in the original bright pixels, where tempStr is set
        #
        msk = mi.getMask()
        temp = msk.getPlaneBitMask(tempStr)
        xtalk_temp = crosstalk | temp
        np_msk = msk.getArray()
        mask_indicies = np.where(np.bitwise_and(np_msk, xtalk_temp) == xtalk_temp)
        np_msk[mask_indicies] &= getattr(np, np_msk.dtype.name)(~crosstalk)

    finally:
        msk.removeAndClearMaskPlane(tempStr, True) # added in afw #1853
Beispiel #5
0
 def readImage(self):
     if self.fileDescriptor.parameters:
         # It looks like the Gen2 std_raw code wouldn't have handled
         # flipping vs. subimages correctly, so we won't bother to either.
         # But we'll make sure no one tries to get a subimage, rather than
         # doing something confusing.
         raise NotImplementedError("Formatter does not support subimages.")
     image = ImageU(self.fileDescriptor.location.path)
     return flipImage(image, self.FLIP_LR, self.FLIP_TB)
 def readImage(self, fileDescriptor):
     if fileDescriptor.parameters:
         # It looks like the Gen2 std_raw code wouldn't have handled
         # flipping vs. subimages correctly, so we won't bother to either.
         # But we'll make sure no one tries to get a subimage, rather than
         # doing something confusing.
         raise NotImplementedError("Formatter does not support subimages.")
     image = ImageU(fileDescriptor.location.path)
     return flipImage(image, self.FLIP_LR, self.FLIP_TB)
def eimageCallback(im, ccd=None, imageSource=None):
    """A callback to handle eimages"""

    im = im.convertF()

    nQuarter = 1
    im = rotateImageBy90(im, nQuarter)
    im = flipImage(im, True, False)

    return im
Beispiel #8
0
    def _flipChipsLR(exp, wcs, dataId, dims=None):
        """Flip the chip left/right or top/bottom. Process either/and the pixels and wcs
           Most chips are flipped L/R, but the rotated ones (100..103) are flipped T/B
        """
        flipLR, flipTB = (False, True) if dataId['ccd'] in (100, 101, 102, 103) else (True, False)
        if exp:
            exp.setMaskedImage(afwMath.flipImage(exp.getMaskedImage(), flipLR, flipTB))
        if wcs:
            wcs.flipImage(flipLR, flipTB, exp.getDimensions() if dims is None else dims)

        return exp
Beispiel #9
0
    def apply_transform(self, transform: ImageSectionTransform, *,
                        allow_view: bool) -> AfwImageSection[_V]:
        # Docstring inherited.
        if not (transform.flip_x or transform.flip_y):
            image = type(self._image)(self._image, deep=not allow_view)
        else:
            from lsst.afw.math import flipImage  # type: ignore

            image: AfwImageLike = flipImage(self._image, transform.flip_x,
                                            transform.flip_y)
        image.setXY0(transform.output_bbox.getMin())
        return AfwImageSection(image)
Beispiel #10
0
    def transform_subimage(self, subimage):
        """Transform an already-extracted subimage to match the orientation
        and offset of the target amplifier.

        Parameters
        ----------
        subimage : image-like
            The subimage to transform; may be any of `lsst.afw.image.Image`,
            `lsst.afw.image.Mask`, `lsst.afw.image.MaskedImage`, and
            `lsst.afw.image.Exposure`.

        Returns
        -------
        transformed : image-like
            Transformed image of the same type as ``subimage``.
        """
        from lsst.afw.math import flipImage
        if hasattr(subimage, "getMaskedImage"):
            # flipImage doesn't support Exposure natively.
            # And sadly, there's no way to write to an existing MaskedImage,
            # so we need to make yet another copy.
            result = subimage.clone()
            result.setMaskedImage(
                flipImage(
                    subimage.getMaskedImage(),
                    self._amplifier_comparison & self._amplifier_comparison.FLIPPED_X,
                    self._amplifier_comparison & self._amplifier_comparison.FLIPPED_Y,
                )
            )
        else:
            result = flipImage(
                subimage,
                self._amplifier_comparison & self._amplifier_comparison.FLIPPED_X,
                self._amplifier_comparison & self._amplifier_comparison.FLIPPED_Y,
            )
        if self._is_parent_trimmed:
            result.setXY0(self._amplifier.getBBox().getMin())
        else:
            result.setXY0(self._amplifier.getRawBBox().getMin() + self._amplifier.getRawXYOffset())
        return result
Beispiel #11
0
    def testFlip(self):
        """Test that we end up with the correct image after flipping it"""

        frame = 2
        for flipLR, flipTB, x, y in [(True, False, 19, 0),
                                     (True, True,  19, 9),
                                     (False, True, 0,  9),
                                     (False, False, 0, 0)]:
            outImage = afwMath.flipImage(self.inImage, flipLR, flipTB)
            if display:
                ds9.mtv(outImage, frame=frame, title="%s %s" % (flipLR, flipTB))
                frame += 1
            self.assertEqual(self.inImage.get(0, 0), outImage.get(x, y))
Beispiel #12
0
    def testFlip(self):
        """Test that we end up with the correct image after flipping it"""

        frame = 2
        for flipLR, flipTB, x, y in [(True, False, 19, 0),
                                     (True, True, 19, 9),
                                     (False, True, 0, 9),
                                     (False, False, 0, 0)]:
            outImage = afwMath.flipImage(self.inImage, flipLR, flipTB)
            if display:
                ds9.mtv(outImage, frame=frame, title="%s %s" % (flipLR, flipTB))
                frame += 1
            self.assertEqual(self.inImage.get(0, 0), outImage.get(x, y))
 def testFlip(self):
     """Test that we end up with the correct image after flipping it.
     """
     frame = 2
     for flipLR, flipTB, x, y in [(True, False, 19, 0),
                                  (True, True, 19, 9),
                                  (False, True, 0, 9),
                                  (False, False, 0, 0)]:
         outImage = afwMath.flipImage(self.inImage, flipLR, flipTB)
         if display:
             afwDisplay.Display(frame=frame).mtv(outImage, title="%s %s" % (flipLR, flipTB))
             frame += 1
         self.assertEqual(self.inImage[0, 0, afwImage.LOCAL], outImage[x, y, afwImage.LOCAL])
Beispiel #14
0
 def testFlip(self):
     """Test that we end up with the correct image after flipping it.
     """
     frame = 2
     for flipLR, flipTB, x, y in [(True, False, 19, 0), (True, True, 19, 9),
                                  (False, True, 0, 9),
                                  (False, False, 0, 0)]:
         outImage = afwMath.flipImage(self.inImage, flipLR, flipTB)
         if display:
             afwDisplay.Display(frame=frame).mtv(outImage,
                                                 title=f"{flipLR} {flipTB}")
             frame += 1
         self.assertEqual(self.inImage[0, 0, afwImage.LOCAL],
                          outImage[x, y, afwImage.LOCAL])
Beispiel #15
0
    def run(self, expRef, butler):
        """Make summary plots of full focalplane images.
        """
        sbi = SimButlerImage(butler,
                             type=expRef.butlerSubset.datasetType,
                             visit=expRef.dataId['visit'])

        # Get the per ccd images
        def parse_name_to_dataId(name_str):
            raft, sensor = name_str.split()
            return {'raft': raft[-3:], 'sensor': sensor[-3:]}

        for ccd in butler.get('camera'):
            data_id = parse_name_to_dataId(ccd.getName())
            data_id.update(expRef.dataId)
            try:
                binned_im = sbi.getCcdImage(ccd,
                                            binSize=self.config.sensorBinSize,
                                            as_masked_image=True)[0]
                binned_im = rotateImageBy90(binned_im,
                                            ccd.getOrientation().getNQuarter())
                if self.config.putFullSensors:
                    butler.put(binned_im, 'binned_sensor_fits', **data_id)
            except (TypeError, RuntimeError):
                # butler couldn't put the image or there was no image to put
                continue
            (x, y) = binned_im.getDimensions()
            boxes = {
                'A':
                afwGeom.Box2I(afwGeom.PointI(0, y / 2),
                              afwGeom.ExtentI(x, y / 2)),
                'B':
                afwGeom.Box2I(afwGeom.PointI(0, 0), afwGeom.ExtentI(x, y / 2))
            }
            for half in ('A', 'B'):
                box = boxes[half]
                butler.put(afwImage.MaskedImageF(binned_im, box),
                           'binned_sensor_fits_halves',
                           half=half,
                           **data_id)

        im = cgu.showCamera(butler.get('camera'),
                            imageSource=sbi,
                            binSize=self.config.binSize)
        expRef.put(im, 'focalplane_summary_fits')
        im = flipImage(im, False, True)
        zmap = ZScaleMapping(im, contrast=self.config.contrast)
        rgb = zmap.makeRgbImage(im, im, im)
        file_name = expRef.get('focalplane_summary_png_filename')
        writeRGB(file_name[0], rgb)
Beispiel #16
0
    def _flipChipsLR(exp, wcs, detectorId, dims=None):
        """Flip the chip left/right or top/bottom. Process either/and the pixels and wcs

        Most chips are flipped L/R, but the rotated ones (100..103) are flipped T/B
        """
        flipLR, flipTB = (False, True) if detectorId in (100, 101, 102, 103) else (True, False)
        if exp:
            exp.setMaskedImage(afwMath.flipImage(exp.getMaskedImage(), flipLR, flipTB))
        if wcs:
            ampDimensions = exp.getDimensions() if dims is None else dims
            ampCenter = geom.Point2D(ampDimensions/2.0)
            wcs = afwGeom.makeFlippedWcs(wcs, flipLR, flipTB, ampCenter)

        return exp, wcs
Beispiel #17
0
    def _flipChipsLR(exp, wcs, dataId, dims=None):
        """Flip the chip left/right or top/bottom. Process either/and the pixels and wcs
Most chips are flipped L/R, but the rotated ones (100..103) are flipped T/B
        """
        flipLR, flipTB = (False, True) if dataId['ccd'] in (100, 101, 102,
                                                            103) else (True,
                                                                       False)
        if exp:
            exp.setMaskedImage(
                afwMath.flipImage(exp.getMaskedImage(), flipLR, flipTB))
        if wcs:
            wcs.flipImage(flipLR, flipTB,
                          exp.getDimensions() if dims is None else dims)

        return exp
def _assemble_alt_metadata(filename, metadata_filename, subtract_background, gains, ADC_Offsets = None):
    if subtract_background and (ADC_Offsets != None):
        print "**** - Error - both DM and bias background subtraction selected - ****"
        print "Defaulting to only using DM subtraction"
        ADC_Offsets = None
        
    if subtract_background:
        print "Using DM background subtraction"
        imageList = _read_with_bg_subtraction(filename)
    else:
        imageList = _read(filename)
        
    if ADC_Offsets == None:
        ADC_Offsets = numpy.zeros(16, dtype = 'f8')
        
    if gains != None: # if statement just deals with the fact that _read_with_bg_subtraction doesn't return decorated images...
        if not subtract_background: # for normal, decorated imaged
            for i in range(len(imageList)):
                imageList[i].getImage().getArray()[:] = (imageList[i].getImage().getArray()[:] - ADC_Offsets[i] ) * gains[i]
        else: #for un-decorated images, as returned by _read (i.e. without background subtraction
            for i in range(len(imageList)):
                imageList[i].getArray()[:] = (imageList[i].getArray()[:] - ADC_Offsets[i] ) * gains[i]
        
    metadata_imageList = _read(metadata_filename)

    detsize = set([md_im.getMetadata().get("DETSIZE") for md_im in metadata_imageList])
    assert len(detsize) == 1, "Multiple DETSIZEs detected"
    detsize = detsize.pop()
    image = afwImage.ImageF(_regionToBox(detsize))

    for i in range(len(imageList)):
        header = metadata_imageList[i].getMetadata()
        ltm11, ltm22 = map(int, [header.get("LTM1_1"), header.get("LTM2_2")])
        assert abs(ltm11) == 1 and abs(ltm22) == 1, "Binned data detected"
        assert "LTM1_2" not in header.names() and "LTM2_1" not in header.names(), "Rotated data detected"
        datasec = _regionToBox(header.get("DATASEC"))
        detsec = _regionToBox(header.get("DETSEC"))
        if subtract_background:
            data = afwImage.ImageF(imageList[i], datasec)
        else:
            data = afwImage.ImageF(imageList[i].getImage(), datasec)
        data = afwMath.flipImage(data, ltm11 < 0, ltm22 < 0)
        target = image.Factory(image, detsec)
        target <<= data
    return image
Beispiel #19
0
def getAmplifier(image, amp, ampReference=None, offset=2):
    """Extract an image of the amplifier from the CCD, along with an offset version

    The amplifier image will be flipped (if required) to match the
    orientation of a nominated reference amplifier.

    An additional image, with the nominated offset applied, is also produced.

    @param image           Image of CCD
    @param amp             Index of amplifier
    @param ampReference    Index of reference amplifier
    @param offset          Offset to apply
    @return amplifier image, offset amplifier image
    """
    height = image.getHeight()
    ampBox = afwGeom.Box2I(afwGeom.Point2I(amp*512, 0), afwGeom.Extent2I(512, height))
    ampImage = image.Factory(image, ampBox, afwImage.LOCAL)
    if ampReference is not None and amp % 2 != ampReference % 2:
        ampImage = afwMath.flipImage(ampImage, True, False)
    offBox = afwGeom.Box2I(afwGeom.Point2I(offset if amp == ampReference else 0, 0),
                           afwGeom.Extent2I(510, height))
    offsetImage = ampImage.Factory(ampImage, offBox, afwImage.LOCAL)
    return ampImage, offsetImage
Beispiel #20
0
    def readFull(self):
        # Docstring inherited.
        rawFile = self.fileDescriptor.location.path
        amplifier, detector, _ = standardizeAmplifierParameters(
            self.checked_parameters,
            self._instrument.getCamera()[self.observationInfo.detector_num],
        )
        if amplifier is not None:
            # LSST raws are already per-amplifier on disk, and in a different
            # assembly state than all of the other images we see in
            # DM-maintained formatters.  And we also need to deal with the
            # on-disk image having different overscans from our nominal
            # detector.  So we can't use afw.cameraGeom.AmplifierIsolator for
            # most of the implementation (as other formatters do), but we can
            # call most of the same underlying code to do the work.

            def findAmpHdu(name):
                """Find the HDU for the amplifier with the given name,
                according to cameraGeom.
                """
                for hdu, amp in enumerate(detector):
                    if amp.getName() == name:
                        return hdu + 1
                raise LookupError(f"Could not find HDU for amp with name {name}.")

            reader = lsst.afw.image.ImageFitsReader(rawFile, hdu=findAmpHdu(amplifier.getName()))
            image = reader.read(dtype=np.dtype(np.int32), allowUnsafe=True)
            with warn_once(rawFile) as logCmd:
                # Extract an amplifier from the on-disk detector and fix its
                # overscan bboxes as necessary to match the on-disk bbox.
                adjusted_amplifier_builder, _ = fixAmpGeometry(
                    detector[amplifier.getName()],
                    bbox=image.getBBox(),
                    metadata=reader.readMetadata(),
                    logCmd=logCmd,
                )
                on_disk_amplifier = adjusted_amplifier_builder.finish()
            # We've now got two Amplifier objects in play:
            # A) 'amplifier' is what the user wants
            # B) 'on_disk_amplifier' represents the subimage we have.
            # The one we want has the orientation/shift state of (A) with
            # the overscan regions of (B).
            comparison = amplifier.compareGeometry(on_disk_amplifier)
            # If the flips or origins differ, we need to modify the image
            # itself.
            if comparison & comparison.FLIPPED:
                from lsst.afw.math import flipImage
                image = flipImage(
                    image,
                    comparison & comparison.FLIPPED_X,
                    comparison & comparison.FLIPPED_Y,
                )
            if comparison & comparison.SHIFTED:
                image.setXY0(amplifier.getRawBBox().getMin())
            # Make a single-amplifier detector that reflects the image we're
            # returning.
            detector_builder = detector.rebuild()
            detector_builder.clear()
            detector_builder.unsetCrosstalk()
            if comparison & comparison.REGIONS_DIFFER:
                # We can't just install the amplifier the user gave us, because
                # that has the wrong overscan regions; instead we transform the
                # on-disk amplifier to have the same orientation and offsets as
                # the given one.
                adjusted_amplifier_builder.transform(
                    outOffset=on_disk_amplifier.getRawXYOffset(),
                    outFlipX=amplifier.getRawFlipX(),
                    outFlipY=amplifier.getRawFlipY(),
                )
                detector_builder.append(adjusted_amplifier_builder)
                detector_builder.setBBox(adjusted_amplifier_builder.getBBox())
            else:
                detector_builder.append(amplifier.rebuild())
                detector_builder.setBBox(amplifier.getBBox())
            exposure = lsst.afw.image.makeExposure(lsst.afw.image.makeMaskedImage(image))
            exposure.setDetector(detector_builder.finish())
        else:
            ampExps = readRawAmps(rawFile, detector)
            exposure = fixAmpsAndAssemble(ampExps, rawFile)
        self.attachComponentsFromMetadata(exposure)
        return exposure
    def run(self, exposure, crosstalkSources=None):
        """Perform crosstalk correction on a DECam exposure and its corresponding dataRef.

        Parameters
        ----------
        exposure : `lsst.afw.image.Exposure`
            Exposure to correct.
        dataRef : `lsst.daf.persistence.butlerSubset.ButlerDataRef`
            DataRef of exposure to correct which must include a dataId
            with at least one visit and ccdnum.
        crosstalkSources : `defaultdict`
            Must contain image data and corresponding crosstalk coefficients for
            each crosstalk source of the given dataRef victim. This is returned
            by prepCrosstalk.

        Returns
        -------
        `lsst.pipe.base.Struct`
        Struct with components:
        - ``exposure``: The exposure after crosstalk correction has been
                        applied (`lsst.afw.image.Exposure`).
        """
        self.log.info('Applying crosstalk correction')
        assert crosstalkSources is not None, "Sources are required for DECam crosstalk correction; \
                                              you must run CrosstalkTask via IsrTask which will \
                                              call prepCrosstalk to get the sources."

        # Load data from crosstalk 'victim' exposure we want to correct
        det = exposure.getDetector()
        for amp in det:
            ccdnum = det.getId()
            ccdnum_str = '%02d' % ccdnum
            victim = ccdnum_str + amp.getName()
            # If doCrosstalkBeforeAssemble=True, then use getRawBBox().  Otherwise, getBBox().
            dataBBox = amp.getRawBBox()
            # Check to see if victim overscan has been corrected, and if not, correct it first
            if not exposure.getMetadata().exists('OVERSCAN'):
                decamisr = IsrTask()
                decamisr.overscanCorrection(exposure, amp)
                self.log.warn(
                    'Overscan correction did not happen prior to crosstalk correction'
                )
                self.log.info(
                    'Correcting victim %s overscan before crosstalk' % victim)
            image = exposure.getMaskedImage().getImage()
            victim_data = image.Factory(image, dataBBox)
            # Load data from crosstalk 'source' exposures
            for source in crosstalkSources[victim]:
                source_data, source_coeff, source_idx = source
                victim_idx = 0
                if 'A' in victim:
                    victim_idx = 0
                elif 'B' in victim:
                    victim_idx = 1
                else:
                    self.log.fatal(
                        'DECam victim amp name does not contain A or B, cannot proceed'
                    )

                if source_idx != victim_idx:
                    # Occurs with amp A and B mismatch; need to flip horizontally
                    source_data = afwMath.flipImage(source_data,
                                                    flipLR=True,
                                                    flipTB=False)
                # Perform the linear crosstalk correction
                try:
                    source_data *= source_coeff
                    victim_data -= source_data
                except RuntimeError:
                    self.log.fatal(
                        'Crosstalk correction failed for victim %s from source %s'
                        % (victim, source))
        return pipeBase.Struct(exposure=exposure, )
Beispiel #22
0
    def makeImage(self):
        """Generate a simulated ISR image.

        Returns
        -------
        exposure : `lsst.afw.image.Exposure` or `dict`
            Simulated ISR image data.

        Notes
        -----
        This method currently constructs a "raw" data image by:
            * Generating a simulated sky with noise
            * Adding a single Gaussian "star"
            * Adding the fringe signal
            * Multiplying the frame by the simulated flat
            * Adding dark current (and noise)
            * Adding a bias offset (and noise)
            * Adding an overscan gradient parallel to the pixel y-axis
            * Simulating crosstalk by adding a scaled version of each
              amplifier to each other amplifier.

        The exposure with image data constructed this way is in one of
        three formats.
            * A single image, with overscan and prescan regions retained
            * A single image, with overscan and prescan regions trimmed
            * A `dict`, containing the amplifer data indexed by the
              amplifier name.

        The nonlinearity, CTE, and brighter fatter are currently not
        implemented.

        Note that this method generates an image in the reverse
        direction as the ISR processing, as the output image here has
        had a series of instrument effects added to an idealized
        exposure.
        """
        exposure = self.getExposure()

        for idx, amp in enumerate(exposure.getDetector()):
            bbox = None
            if self.config.isTrimmed is True:
                bbox = amp.getBBox()
            else:
                bbox = amp.getRawDataBBox()

            ampData = exposure.image[bbox]

            if self.config.doAddSky is True:
                self.amplifierAddNoise(ampData, self.config.skyLevel,
                                       np.sqrt(self.config.skyLevel))

            if self.config.doAddSource is True:
                for sourceAmp, sourceFlux, sourceX, sourceY in zip(
                        self.config.sourceAmp, self.config.sourceFlux,
                        self.config.sourceX, self.config.sourceY):
                    if idx == sourceAmp:
                        self.amplifierAddSource(ampData, sourceFlux, sourceX,
                                                sourceY)

            if self.config.doAddFringe is True:
                self.amplifierAddFringe(amp,
                                        ampData,
                                        np.array(self.config.fringeScale),
                                        x0=np.array(self.config.fringeX0),
                                        y0=np.array(self.config.fringeY0))

            if self.config.doAddFlat is True:
                if ampData.getArray().sum() == 0.0:
                    self.amplifierAddNoise(ampData, 1.0, 0.0)
                u0 = exposure.getDimensions().getX()
                v0 = exposure.getDimensions().getY()
                self.amplifierMultiplyFlat(amp,
                                           ampData,
                                           self.config.flatDrop,
                                           u0=u0,
                                           v0=v0)

            if self.config.doAddDark is True:
                self.amplifierAddNoise(
                    ampData, self.config.darkRate * self.config.darkTime /
                    self.config.gain,
                    np.sqrt(self.config.darkRate * self.config.darkTime /
                            self.config.gain))

        if self.config.doAddCrosstalk is True:

            for idxS, ampS in enumerate(exposure.getDetector()):
                for idxT, ampT in enumerate(exposure.getDetector()):
                    ampDataS = exposure.image[ampS.getBBox(
                    ) if self.config.isTrimmed else ampS.getRawDataBBox()]
                    ampDataT = exposure.image[ampT.getBBox(
                    ) if self.config.isTrimmed else ampT.getRawDataBBox()]
                    ampDataS = afwMath.flipImage(
                        ampDataS, (X_FLIP[ampS.getReadoutCorner()]
                                   ^ X_FLIP[ampT.getReadoutCorner()]),
                        (Y_FLIP[ampS.getReadoutCorner()]
                         ^ Y_FLIP[ampT.getReadoutCorner()]))
                    self.amplifierAddCT(ampDataS, ampDataT,
                                        self.crosstalkCoeffs[idxT][idxS])

        for amp in exposure.getDetector():
            bbox = None
            if self.config.isTrimmed is True:
                bbox = amp.getBBox()
            else:
                bbox = amp.getRawDataBBox()

            ampData = exposure.image[bbox]

            if self.config.doAddBias is True:
                self.amplifierAddNoise(
                    ampData, self.config.biasLevel,
                    self.config.readNoise / self.config.gain)

            if self.config.doAddOverscan is True:
                oscanBBox = amp.getRawHorizontalOverscanBBox()
                oscanData = exposure.image[oscanBBox]
                self.amplifierAddNoise(
                    oscanData, self.config.biasLevel,
                    self.config.readNoise / self.config.gain)

                self.amplifierAddYGradient(ampData,
                                           -1.0 * self.config.overscanScale,
                                           1.0 * self.config.overscanScale)
                self.amplifierAddYGradient(oscanData,
                                           -1.0 * self.config.overscanScale,
                                           1.0 * self.config.overscanScale)

        if self.config.doGenerateAmpDict is True:
            expDict = dict()
            for amp in exposure.getDetector():
                expDict[amp.getName()] = exposure
            return expDict
        else:
            return exposure
Beispiel #23
0
    def _flipChipsLR(exp, wcs, dataId, dims=None):
        flipLR, flipTB = (True, False)
        exp.setMaskedImage(
            afwMath.flipImage(exp.getMaskedImage(), flipLR, flipTB))

        return exp
Beispiel #24
0
 def testMask(self):
     """Test that we can flip a Mask"""
     mask = afwImage.MaskU(10, 20)
     afwMath.flipImage(
         mask, True,
         False)  # for a while, swig couldn't handle the resulting Mask::Ptr
    def runDataRef(self, expRefList, butler):
        """Make summary plots of full focalplane images.
        """
        if len(expRefList) == 0:
            return pipeBase.Struct(exitStatus=1)

        lsst.afw.fits.setAllowImageCompression(
            self.config.allowFitsCompression)

        dstype = expRefList[0].butlerSubset.datasetType

        if dstype == "raw":

            def callback(im, ccd, imageSource):
                return cgu.rawCallback(im,
                                       ccd,
                                       imageSource,
                                       correctGain=True,
                                       subtractBias=True)
        elif dstype == "eimage":
            callback = eimageCallback
        elif self.config.doApplySkyCorr:
            callback = skyCorrCallback
        else:
            callback = None

        for visit in set([er.dataId["visit"] for er in expRefList]):
            self.log.info("Processing visit %d", visit)
            expRefListForVisit = [
                er for er in expRefList if er.dataId["visit"] == visit
            ]

            dataId = expRefListForVisit[0].dataId
            bi = cgu.ButlerImage(butler,
                                 dstype,
                                 visit=visit,
                                 callback=callback,
                                 verbose=True)

            if self.config.doSensorImages:
                for dataId in (er.dataId for er in expRefListForVisit):
                    ccd = butler.get('calexp_detector', **dataId)
                    try:
                        md = butler.get('calexp_md', **dataId)
                    except RuntimeError:
                        md = None
                    if md:
                        afwGeom.makeSkyWcs(
                            md, strip=True
                        )  # strip WCS cards; they're invalidated by binning
                    try:
                        binned_im = bi.getCcdImage(
                            ccd,
                            binSize=self.config.sensorBinSize,
                            asMaskedImage=True)[0]
                        binned_im = rotateImageBy90(
                            binned_im,
                            ccd.getOrientation().getNQuarter())
                        if self.config.putFullSensors:
                            binned_exp = afwImage.ExposureF(binned_im)
                            binned_exp.setMetadata(md)
                            butler.put(binned_exp,
                                       'binned_sensor_fits',
                                       **dataId,
                                       dstype=dstype)
                    except (TypeError, RuntimeError) as e:
                        # butler couldn't put the image or there was no image to put
                        self.log.warn("Unable to make binned image: %s", e)
                        continue

                    (x, y) = binned_im.getDimensions()
                    boxes = {
                        'A':
                        afwGeom.Box2I(afwGeom.PointI(0, y / 2),
                                      afwGeom.ExtentI(x, y / 2)),
                        'B':
                        afwGeom.Box2I(afwGeom.PointI(0, 0),
                                      afwGeom.ExtentI(x, y / 2))
                    }
                    for half in ('A', 'B'):
                        box = boxes[half]
                        binned_exp = afwImage.ExposureF(binned_im[box])
                        binned_exp.setMetadata(md)
                        butler.put(binned_exp,
                                   'binned_sensor_fits_halves',
                                   half=half,
                                   **dataId,
                                   dstype=dstype)

            im = cgu.showCamera(butler.get('camera'),
                                imageSource=bi,
                                binSize=self.config.binSize)

            dstypeName = "%s-%s" % (
                dstype, self.config.fpId) if self.config.fpId else dstype

            butler.put(im, 'focal_plane_fits', visit=visit, dstype=dstypeName)

            # Compute the zscale stretch for just the CCDs that have data.
            detectorNameList = [
                "%s_%s" % (er.dataId["raftName"], er.dataId["detectorName"])
                for er in expRefListForVisit
            ]
            im_scaling = cgu.showCamera(butler.get('camera'),
                                        imageSource=bi,
                                        binSize=self.config.binSize,
                                        detectorNameList=detectorNameList)
            zmap = ZScaleMapping(im_scaling, contrast=self.config.contrast)

            im = flipImage(im, False, True)
            rgb = zmap.makeRgbImage(im, im, im)
            file_name = butler.get('focal_plane_png_filename',
                                   visit=visit,
                                   dstype=dstypeName)
            writeRGB(file_name[0], rgb)

        return pipeBase.Struct(exitStatus=0)
Beispiel #26
0
    def _flipChipsLR(exp, wcs, dataId, dims=None):
        flipLR, flipTB = (True, False)
        exp.setMaskedImage(afwMath.flipImage(exp.getMaskedImage(), flipLR, flipTB))

        return exp
 def testMask(self):
     """Test that we can flip a Mask.
     """
     mask = afwImage.Mask(10, 20)
     # for a while, swig couldn't handle the resulting std::shared_ptr<Mask>
     afwMath.flipImage(mask, True, False)
Beispiel #28
0
    def checkRepo(self, files=None):
        # Test amp parameter implementation for the LSST raw formatter.  This
        # is the same for all instruments, so repeating it in other test cases
        # is wasteful.
        butler = Butler(self.root, run=self.outputRun)
        ref = butler.registry.findDataset("raw", self.dataIds[0])
        full_assembled = butler.getDirect(ref)
        unassembled_detector = self.instrumentClass().getCamera()[
            ref.dataId["detector"]]
        assembled_detector = full_assembled.getDetector()
        for unassembled_amp, assembled_amp in zip(unassembled_detector,
                                                  assembled_detector):
            # Check that we're testing what we think we're testing: these
            # amps should differ in assembly state (offsets, flips), and they
            # _may_ differ in fundamental geometry if we had to patch the
            # overscan region sizes.
            comparison = unassembled_amp.compareGeometry(assembled_amp)
            self.assertTrue(comparison
                            & AmplifierGeometryComparison.ASSEMBLY_DIFFERS)
            assembled_subimage = butler.getDirect(
                ref, parameters={"amp": assembled_amp})
            unassembled_subimage = butler.getDirect(
                ref, parameters={"amp": unassembled_amp.getName()})
            self.assertEqual(len(assembled_subimage.getDetector()), 1)
            self.assertEqual(len(unassembled_subimage.getDetector()), 1)
            self.assertEqual(len(assembled_subimage.getDetector()), 1)
            self.assertEqual(len(unassembled_subimage.getDetector()), 1)
            self.assertImagesEqual(
                assembled_subimage.image,
                full_assembled.image[assembled_amp.getRawBBox()])
            self.assertImagesEqual(
                unassembled_subimage.image,
                flipImage(
                    full_assembled.image[assembled_amp.getRawBBox()],
                    flipLR=unassembled_amp.getRawFlipX(),
                    flipTB=unassembled_amp.getRawFlipY(),
                ),
            )
            self.assertAmplifiersEqual(assembled_subimage.getDetector()[0],
                                       assembled_amp)
            if comparison & comparison.REGIONS_DIFFER:
                # We needed to patch overscans, but unassembled_amp (which
                # comes straight from the camera) won't have those patches, so
                # we can't compare it to the amp attached to
                # unassembled_subimage (which does have those patches).
                comparison2 = unassembled_subimage.getDetector(
                )[0].compareGeometry(unassembled_amp)

                self.assertTrue(comparison2
                                & AmplifierGeometryComparison.REGIONS_DIFFER)
                # ...and that unassembled_subimage's amp has the same regions
                # (after accounting for assembly/orientation) as assembled_amp.
                comparison3 = unassembled_subimage.getDetector(
                )[0].compareGeometry(assembled_amp)
                self.assertTrue(comparison3
                                & AmplifierGeometryComparison.ASSEMBLY_DIFFERS)
                self.assertFalse(comparison3
                                 & AmplifierGeometryComparison.REGIONS_DIFFER)
            else:
                self.assertAmplifiersEqual(
                    unassembled_subimage.getDetector()[0], unassembled_amp)
Beispiel #29
0
 def readImage(self):
     index, metadata = self._determineHDU(self.dataId['detector'])
     image = lsst.afw.image.ImageF(self.fileDescriptor.location.path, index)
     return flipImage(image, self.FLIP_LR, self.FLIP_TB)
Beispiel #30
0
 def testMask(self):
     """Test that we can flip a Mask.
     """
     mask = afwImage.Mask(10, 20)
     # for a while, swig couldn't handle the resulting std::shared_ptr<Mask>
     afwMath.flipImage(mask, True, False)
Beispiel #31
0
 def testMask(self):
     """Test that we can flip a Mask"""
     mask = afwImage.MaskU(10, 20)
     afwMath.flipImage(mask, True, False) # for a while, swig couldn't handle the resulting Mask::Ptr