Exemplo n.º 1
0
def addAmp(ampCatalog, i, eparams):
    """ Add an amplifier to an AmpInfoCatalog

    @param ampCatalog: An instance of an AmpInfoCatalog object to fill with amp properties
    @param i which amplifier? (i == 0 ? left : right)
    @param eparams: Electronic parameters.  This is a list of tuples with (i, params),
                    where params is a dictionary of electronic parameters.
    """
    #
    # Layout of active and overclock pixels in the as-readout data  The layout is:
    #     Amp0 || extended | overclock | data || data | overclock | extended || Amp1
    # for each row; all rows are identical in drift-scan data
    #
    height = 1361  # number of rows in a frame
    width = 1024  # number of data pixels read out through one amplifier
    nExtended = 8  # number of pixels in the extended register
    nOverclock = 32  # number of (horizontal) overclock pixels
    #
    # Construct the needed bounding boxes given that geometrical information.
    #
    # Note that all the offsets are relative to the origin of this amp, not to its eventual
    # position in the CCD
    #
    record = ampCatalog.addNew()
    xtot = width + nExtended + nOverclock
    allPixels = geom.BoxI(geom.PointI(0, 0), geom.ExtentI(xtot, height))
    biasSec = geom.BoxI(geom.PointI(nExtended if i == 0 else width, 0),
                        geom.ExtentI(nOverclock, height))
    dataSec = geom.BoxI(
        geom.PointI(nExtended + nOverclock if i == 0 else 0, 0),
        geom.ExtentI(width, height))
    emptyBox = geom.BoxI()
    bbox = geom.BoxI(geom.PointI(0, 0), geom.ExtentI(width, height))
    bbox.shift(geom.Extent2I(width * i, 0))

    shiftp = geom.Extent2I(xtot * i, 0)
    allPixels.shift(shiftp)
    biasSec.shift(shiftp)
    dataSec.shift(shiftp)

    record.setBBox(bbox)
    record.setRawXYOffset(geom.ExtentI(0, 0))
    record.setName('left' if i == 0 else 'right')
    record.setReadoutCorner(afwTable.LL if i == 0 else afwTable.LR)
    record.setGain(eparams['gain'])
    record.setReadNoise(eparams['readNoise'])
    record.setSaturation(eparams['fullWell'])
    record.setSuspectLevel(float("nan"))
    record.setLinearityType(NullLinearityType)
    record.setLinearityCoeffs([
        1.,
    ])
    record.setHasRawInfo(True)
    record.setRawFlipX(False)
    record.setRawFlipY(False)
    record.setRawBBox(allPixels)
    record.setRawDataBBox(dataSec)
    record.setRawHorizontalOverscanBBox(biasSec)
    record.setRawVerticalOverscanBBox(emptyBox)
    record.setRawPrescanBBox(emptyBox)
Exemplo n.º 2
0
 def testValid(self):
     test_data = {
         "[1:1084,1:1024]":
         geom.BoxI(geom.PointI(0, 0), geom.PointI(1083, 1023)),
         "[0:0,0:0]":
         geom.BoxI(geom.PointI(-1, -1), geom.PointI(-1, -1))
     }
     for val, result in test_data.items():
         self.assertEqual(obsBase.bboxFromIraf(val), result)
Exemplo n.º 3
0
def findBrightestStarRHL(exp, minArea=100, maxRadialOffset=1500):
    import lsst.afw.detection as afwDetect
    import lsst.afw.image as afwImage
    import lsst.afw.math as afwMath

    cexp = exp.clone()

    sigma = 4

    ksize = 1 + 4*int(sigma + 1)
    gauss1d = afwMath.GaussianFunction1D(sigma)
    kernel = afwMath.SeparableKernel(ksize, ksize, gauss1d, gauss1d)

    convolvedImage = cexp.maskedImage.Factory(cexp.maskedImage.getBBox())
    afwMath.convolve(convolvedImage, cexp.maskedImage, kernel)
    bbox = geom.BoxI(geom.PointI(ksize//2, ksize//2), geom.PointI(cexp.getWidth() - ksize//2 - 1, cexp.getHeight() - ksize//2 - 1))
    tmp = cexp.maskedImage[bbox]
    cexp.image *= 0
    tmp[bbox, afwImage.PARENT] = convolvedImage[bbox]

    del convolvedImage; del tmp

    if False:
        defectBBoxes = [
            geom.BoxI(geom.PointI(548, 3562), geom.PointI(642, 3999)),
            geom.BoxI(geom.PointI(474, 3959), geom.PointI(1056, 3999)),
            geom.BoxI(geom.PointI(500, 0),    geom.PointI(680, 40)),
        ]
        for bbox in defectBBoxes:
            cexp.maskedImage[bbox] = 0

    threshold = 1000
    feet = afwDetect.FootprintSet(cexp.maskedImage, afwDetect.Threshold(threshold), "DETECTED").getFootprints()

    maxVal = None
    centroid = None
    for foot in feet:
        peak = foot.peaks[0]
        
        if minArea > 0 and foot.getArea() < minArea:
            continue
            
        x, y = peak.getCentroid()
        if np.hypot(x - 2036, y - 2000) > maxRadialOffset:
            continue

        if maxVal is None or peak.getPeakValue() > maxVal:
            maxVal = peak.getPeakValue()
            centroid = peak.getCentroid()
            
    return centroid
    def testSubMap(self):
        bbox = geom.BoxI(geom.Point2I(200, 100), geom.Extent2I(300, 400))
        mapper = MinMapper2(root=ROOT)
        loc = mapper.map("raw_sub", {"ccd": 13, "bbox": bbox}, write=True)
        self.assertEqual(loc.getPythonType(), "lsst.afw.image.ExposureU")
        self.assertEqual(loc.getCppType(), "ImageU")
        self.assertEqual(loc.getStorageName(), "FitsStorage")
        self.assertEqual(loc.getLocations(), ["foo-13.fits"])
        self.assertEqual(loc.getStorage().root, ROOT)
        self.assertEqual(loc.getAdditionalData().getScalar("ccd"), 13)
        self.assertEqual(loc.getAdditionalData().getScalar("width"), 300)
        self.assertEqual(loc.getAdditionalData().getScalar("height"), 400)
        self.assertEqual(loc.getAdditionalData().getScalar("llcX"), 200)
        self.assertEqual(loc.getAdditionalData().getScalar("llcY"), 100)
        checkCompression(self, loc.getAdditionalData())

        loc = mapper.map("raw_sub", {
            "ccd": 13,
            "bbox": bbox,
            "imageOrigin": "PARENT"
        },
                         write=True)
        self.assertEqual(loc.getPythonType(), "lsst.afw.image.ExposureU")
        self.assertEqual(loc.getCppType(), "ImageU")
        self.assertEqual(loc.getStorageName(), "FitsStorage")
        self.assertEqual(loc.getLocations(), ["foo-13.fits"])
        self.assertEqual(loc.getStorage().root, ROOT)
        self.assertEqual(loc.getAdditionalData().getScalar("ccd"), 13)
        self.assertEqual(loc.getAdditionalData().getScalar("width"), 300)
        self.assertEqual(loc.getAdditionalData().getScalar("height"), 400)
        self.assertEqual(loc.getAdditionalData().getScalar("llcX"), 200)
        self.assertEqual(loc.getAdditionalData().getScalar("llcY"), 100)
        self.assertEqual(loc.getAdditionalData().getScalar("imageOrigin"),
                         "PARENT")
        checkCompression(self, loc.getAdditionalData())
Exemplo n.º 5
0
    def loadExposure(self, sensorRef):
        """Load SDSS data as a post-ISR exposure

        - Image is from fpC
        - Mask is from fpM
        - Wcs is from asTrans
        - PhotoCalib is from tsField
        - Psf is from psField
        """
        originalExp = sensorRef.get("fpC").convertF()
        image = originalExp.getMaskedImage().getImage()
        if self.config.removePedestal:
            image -= self.config.pedestalVal
        mask = sensorRef.get("fpM")
        wcs = sensorRef.get("asTrans")
        tsField = sensorRef.get("tsField")
        photoCalib = tsField.photoCalib
        gain = tsField.gain
        var = afwImage.ImageF(image, True)
        var /= gain

        mi = afwImage.MaskedImageF(image, mask, var)

        if self.config.removeOverlap:
            bbox = mi.getBBox()
            begin = bbox.getBegin()
            extent = bbox.getDimensions()
            extent -= geom.Extent2I(0, self.config.overlapSize)
            tbbox = geom.BoxI(begin, extent)
            mi = afwImage.MaskedImageF(mi, tbbox)

        exposure = afwImage.ExposureF(mi, wcs)
        expInfo = exposure.getInfo()
        expInfo.setPhotoCalib(photoCalib)

        camera = sensorRef.get('camera')
        detector = camera["%(filter)s%(camcol)d" % sensorRef.dataId]
        expInfo.setDetector(detector)
        expInfo.setFilter(afwImage.Filter(sensorRef.dataId['filter']))

        visitInfo = afwImage.VisitInfo(
            exposureTime=tsField.exptime,
            date=tsField.dateAvg,
            boresightAirmass=tsField.airmass,
        )
        expInfo.setVisitInfo(visitInfo)

        # Install the SDSS PSF here; if we want to overwrite it later, we can.
        psf = sensorRef.get('psField')
        exposure.setPsf(psf)

        return exposure
Exemplo n.º 6
0
def bboxFromIraf(irafBBoxStr):
    """Return a Box2I corresponding to an IRAF-style BBOX

    [x0:x1,y0:y1] where x0 and x1 are the one-indexed start and end columns, and correspondingly
    y0 and y1 are the start and end rows.
    """

    mat = re.search(r"^\[([-\d]+):([-\d]+),([-\d]+):([-\d]+)\]$", irafBBoxStr)
    if not mat:
        raise RuntimeError("Unable to parse IRAF-style bbox \"%s\"" %
                           irafBBoxStr)
    x0, x1, y0, y1 = [int(_) for _ in mat.groups()]

    return geom.BoxI(geom.PointI(x0 - 1, y0 - 1), geom.PointI(x1 - 1, y1 - 1))
Exemplo n.º 7
0
    def testParentTrailingSlash2527(self):
        """Just like testParentNormal, but put a trailing slash on the root
        paths.

        Test that an object can be found at root location and put into an
        output location. Then test that when the output locaiton is used as
        an input location, and with a new output location, that the object is
        found in the first output location."""
        # todo these shouldn't be commented out, I think the test wants the
        # trailing slash.
        testOutput = self.mkdtemp("testOutput") + '/'
        butler = dafPersist.Butler(inputs={
            'root': ROOT + '/',
            'mapper': MinMapper1
        },
                                   outputs=testOutput)
        mapper1 = butler._repos.inputs()[0].repo._mapper
        loc = mapper1.map("x", dict(sensor="1,1"), write=True)
        self.assertEqual(loc.getPythonType(), "lsst.afw.geom.BoxI")
        self.assertEqual(loc.getCppType(), "BoxI")
        self.assertEqual(loc.getStorageName(), "PickleStorage")
        self.assertEqual(loc.getLocations(), ["foo-1,1.pickle"])
        self.assertEqual(loc.getStorage().root, ROOT)
        self.assertEqual(loc.getAdditionalData().toString(),
                         "sensor = \"1,1\"\n")
        box = geom.BoxI(geom.PointI(0, 1), geom.PointI(2, 3))
        butler.put(box, "x", sensor="1,1")
        self.assertTrue(
            os.path.exists(os.path.join(testOutput,
                                        loc.getLocations()[0])))
        del butler
        del mapper1

        testOutput2 = self.mkdtemp("testOutput2") + '/'
        butler = dafPersist.Butler(inputs={
            'root': testOutput,
            'mapper': MinMapper1
        },
                                   outputs=testOutput2)
        mapper2 = butler._repos.inputs()[0].repo._mapper
        loc = mapper2.map("x", dict(sensor="1,1"))
        self.assertEqual(loc.getPythonType(), "lsst.afw.geom.BoxI")
        self.assertEqual(loc.getCppType(), "BoxI")
        self.assertEqual(loc.getStorageName(), "PickleStorage")
        self.assertEqual(loc.getLocations(), ["foo-1,1.pickle"])
        self.assertEqual(os.path.normpath(loc.getStorage().root),
                         os.path.normpath(testOutput))
        self.assertEqual(loc.getAdditionalData().toString(),
                         "sensor = \"1,1\"\n")
    def testGzImage(self):
        mapper = MinMapper2(root=ROOT)
        loc = mapper.map("someGz", dict(ccd=35))
        expectedLocations = [os.path.join("gz", "bar-35.fits.gz")]
        self.assertEqual(loc.getStorage().root, ROOT)
        self.assertEqual(loc.getLocations(), expectedLocations)

        butler = dafPersist.ButlerFactory(mapper=mapper).create()
        image = butler.get("someGz", ccd=35)
        self.assertEqual(image.getFilter().getName(), "r")

        bbox = geom.BoxI(geom.Point2I(200, 100), geom.Extent2I(300, 400))
        image = butler.get("someGz_sub",
                           ccd=35,
                           bbox=bbox,
                           imageOrigin="LOCAL",
                           immediate=True)
        self.assertEqual(image.getHeight(), 400)
        self.assertEqual(image.getWidth(), 300)
Exemplo n.º 9
0
    def testParentNormal(self):
        """Test that an object can be found at root location and put into an
        output location. Then test that when the output locaiton is used as an
        input location, and with a new output location, that the object is
        found in the first output location.
        """
        testOutput = self.mkdtemp("testOutput")
        butler = dafPersist.Butler(inputs={
            'root': ROOT,
            'mapper': MinMapper1
        },
                                   outputs=testOutput)
        mapper1 = butler._repos.inputs()[0].repo._mapper
        loc = mapper1.map("x", dict(sensor="1,1"), write=True)
        self.assertEqual(loc.getPythonType(), "lsst.afw.geom.BoxI")
        self.assertEqual(loc.getCppType(), "BoxI")
        self.assertEqual(loc.getStorageName(), "PickleStorage")
        self.assertEqual(loc.getLocations(), ["foo-1,1.pickle"])
        self.assertEqual(loc.getAdditionalData().toString(),
                         "sensor = \"1,1\"\n")
        box = geom.BoxI(geom.PointI(0, 1), geom.PointI(2, 3))
        butler.put(box, "x", sensor="1,1")
        self.assertTrue(
            os.path.exists(os.path.join(testOutput,
                                        loc.getLocations()[0])))
        del butler

        testOutput2 = self.mkdtemp("testOutput2")
        butler = dafPersist.Butler(inputs={
            'root': testOutput,
            'mapper': MinMapper1
        },
                                   outputs=testOutput2)
        mapper2 = butler._repos.inputs()[0].repo._mapper
        loc = mapper2.map("x", dict(sensor="1,1"))
        self.assertEqual(loc.getPythonType(), "lsst.afw.geom.BoxI")
        self.assertEqual(loc.getCppType(), "BoxI")
        self.assertEqual(loc.getStorageName(), "PickleStorage")
        self.assertEqual(loc.getLocations(), ["foo-1,1.pickle"])
        self.assertEqual(loc.getStorage().root, testOutput)
        self.assertEqual(loc.getAdditionalData().toString(),
                         "sensor = \"1,1\"\n")
Exemplo n.º 10
0
def plotDeblendFamilyRGB(parent, bands=['g', 'r', 'i'],
                         min=0.01, max=0.5, Q=8, rgbFileFmt=None):
    x, y = parent.getX(), parent.getY()

    fams = {}
    imBbox = geom.BoxI()
    for bandName in "GRI".upper():
        filterName = "HSC-%s" % bandName

        x0, y0 = coaddDict[filterName].getXY0()
        x0, y0 = 0, 0
        fams[filterName] = familiesDict[filterName].find((x + x0, y + y0), matchRadius=20)
        if not fams[filterName]:
            return
        parent, kids = fams[filterName]

        bbox = parent.getFootprint().getBBox()
        # Can children extend outside parent BBox?
        for kid in kids:
            kim = footprintToImage(kid.getFootprint(), coaddDict[filterName].getMaskedImage())
            bbox.include(kim.getBBox(afwImage.PARENT))

        imBbox.include(bbox)

    images = {} 
    for bandName in bands:
        filterName = "HSC-%s" % bandName.upper()

        images[bandName] = makeDeblendFamilyMosaic(coaddDict[filterName].getMaskedImage(),
                                                   *fams[filterName],
                                                    background=-0.1, imBbox=imBbox).makeMosaic(display=None)

    for bands in [bands]:
        B, G, R = bands
        rgb = afwRgb.makeRGB(images[R], images[G], images[B], min, max - min, Q)

        afwRgb.displayRGB(rgb, show=True)

        if rgbFileFmt:
            afwRgb.writeRGB(rgbFileFmt % "".join(bands), rgb)
Exemplo n.º 11
0
def makeBBoxFromList(ylist):
    """Given a list [(x0, y0), (xsize, ysize)], probably from a yaml file,
    return a BoxI
    """
    (x0, y0), (xsize, ysize) = ylist
    return geom.BoxI(geom.PointI(x0, y0), geom.ExtentI(xsize, ysize))
Exemplo n.º 12
0
 def _parallel_box(self, start, end):
     llc = lsstGeom.PointI(self.imaging.getMinX(), start)
     urc = lsstGeom.PointI(self.imaging.getMaxX(), end)
     return lsstGeom.BoxI(llc, urc)
Exemplo n.º 13
0
 def _serial_box(self, start, end):
     llc = lsstGeom.PointI(start, self.imaging.getMinY())
     urc = lsstGeom.PointI(end, self.imaging.getMaxY())
     return lsstGeom.BoxI(llc, urc)
Exemplo n.º 14
0
def assemble_raw(dataId, componentInfo, cls):
    """Called by the butler to construct the composite type "raw".

    Note that we still need to define "_raw" and copy various fields over.

    Parameters
    ----------
    dataId : `lsst.daf.persistence.dataId.DataId`
        The data ID.
    componentInfo : `dict`
        dict containing the components, as defined by the composite definition
        in the mapper policy.
    cls : 'object'
        Unused.

    Returns
    -------
    exposure : `lsst.afw.image.Exposure`
        The assembled exposure.
    """
    from lsst.ip.isr import AssembleCcdTask

    config = AssembleCcdTask.ConfigClass()
    config.doTrim = False

    assembleTask = AssembleCcdTask(config=config)

    ampExps = componentInfo['raw_amp'].obj
    if len(ampExps) == 0:
        raise RuntimeError("Unable to read raw_amps for %s" % dataId)

    ccd = ampExps[0].getDetector()      # the same (full, CCD-level) Detector is attached to all ampExps
    #
    # Check that the geometry in the metadata matches cameraGeom
    #
    logger = lsst.log.Log.getLogger("LsstCamMapper")
    warned = False
    for i, (amp, ampExp) in enumerate(zip(ccd, ampExps)):
        ampMd = ampExp.getMetadata().toDict()

        if amp.getRawBBox() != ampExp.getBBox():  # Oh dear. cameraGeom is wrong -- probably overscan
            if amp.getRawDataBBox().getDimensions() != amp.getBBox().getDimensions():
                raise RuntimeError("Active area is the wrong size: %s v. %s" %
                                   (amp.getRawDataBBox().getDimensions(), amp.getBBox().getDimensions()))
            if not warned:
                logger.warn("amp.getRawBBox() != data.getBBox(); patching. (%s v. %s)",
                            amp.getRawBBox(), ampExp.getBBox())
                warned = True

            w, h = ampExp.getBBox().getDimensions()
            ow, oh = amp.getRawBBox().getDimensions()  # "old" (cameraGeom) dimensions
            #
            # We could trust the BIASSEC keyword, or we can just assume that
            # they've changed the number of overscan pixels (serial and/or
            # parallel).  As Jim Chiang points out, the latter is safer
            #
            bbox = amp.getRawHorizontalOverscanBBox()
            hOverscanBBox = geom.BoxI(bbox.getBegin(),
                                      geom.ExtentI(w - bbox.getBeginX(), bbox.getHeight()))
            bbox = amp.getRawVerticalOverscanBBox()
            vOverscanBBox = geom.BoxI(bbox.getBegin(),
                                      geom.ExtentI(bbox.getWidth(), h - bbox.getBeginY()))

            amp.setRawBBox(ampExp.getBBox())
            amp.setRawHorizontalOverscanBBox(hOverscanBBox)
            amp.setRawVerticalOverscanBBox(vOverscanBBox)
            #
            # This gets all the geometry right for the amplifier, but the size
            # of the untrimmed image will be wrong and we'll put the amp
            # sections in the wrong places, i.e.
            #   amp.getRawXYOffset()
            # will be wrong.  So we need to recalculate the offsets.
            #
            xRawExtent, yRawExtent = amp.getRawBBox().getDimensions()

            x0, y0 = amp.getRawXYOffset()
            ix, iy = x0//ow, y0/oh
            x0, y0 = ix*xRawExtent, iy*yRawExtent
            amp.setRawXYOffset(geom.ExtentI(ix*xRawExtent, iy*yRawExtent))
        #
        # Check the "IRAF" keywords, but don't abort if they're wrong
        #
        # Only warn about the first amp, use debug for the others
        #
        detsec = bboxFromIraf(ampMd["DETSEC"]) if "DETSEC" in ampMd else None
        datasec = bboxFromIraf(ampMd["DATASEC"]) if "DATASEC" in ampMd else None
        biassec = bboxFromIraf(ampMd["BIASSEC"]) if "BIASSEC" in ampMd else None

        logCmd = logger.warn if i == 0 else logger.debug
        if detsec and amp.getBBox() != detsec:
            logCmd("DETSEC doesn't match for %s (%s != %s)", dataId, amp.getBBox(), detsec)
        if datasec and amp.getRawDataBBox() != datasec:
            logCmd("DATASEC doesn't match for %s (%s != %s)", dataId, amp.getRawDataBBox(), detsec)
        if biassec and amp.getRawHorizontalOverscanBBox() != biassec:
            logCmd("BIASSEC doesn't match for %s (%s != %s)",
                   dataId, amp.getRawHorizontalOverscanBBox(), detsec)

    ampDict = {}
    for amp, ampExp in zip(ccd, ampExps):
        ampDict[amp.getName()] = ampExp

    exposure = assembleTask.assembleCcd(ampDict)

    md = componentInfo['raw_hdu'].obj
    fix_header(md)  # No mapper so cannot specify the translator class
    exposure.setMetadata(md)
    visitInfo = LsstCamMakeRawVisitInfo(logger)(md)
    exposure.getInfo().setVisitInfo(visitInfo)

    boresight = visitInfo.getBoresightRaDec()
    rotangle = visitInfo.getBoresightRotAngle()

    if boresight.isFinite():
        exposure.setWcs(getWcsFromDetector(exposure.getDetector(), boresight,
                                           90*geom.degrees - rotangle))
    else:
        # Should only warn for science observations but VisitInfo does not know
        logger.warn("Unable to set WCS for %s from header as RA/Dec/Angle are unavailable" %
                    (dataId,))

    return exposure
Exemplo n.º 15
0
def generate_cutout(butler,
                    skymap,
                    ra,
                    dec,
                    band='i',
                    label='deepCoadd_skyMap',
                    radius=10.0 * u.arcsec,
                    psf=True,
                    verbose=False):
    """Generate a single cutout image.
    """
    if not isinstance(radius, u.Quantity):
        # Assume that this is in pixel
        radius = int(radius)
    else:
        radius = int(radius.to('arcsec').value / PIXEL_SCALE)

    # Width and height of the post-stamps
    stamp_shape = (radius * 2 + 1, radius * 2 + 1)

    # Coordinate of the image center
    coord = geom.SpherePoint(ra * geom.degrees, dec * geom.degrees)

    # Make a list of (RA, Dec) that covers the cutout region
    radec_list = np.array(sky_cone(ra, dec, radius * PIXEL_SCALE, steps=50)).T

    # Retrieve the Tracts and Patches that cover the cutout region
    patches, _ = tracts_n_patches(radec_list, skymap)

    # Collect the images
    images = []
    for t, p in patches:
        data_id = {
            'tract': t,
            'patch': p.decode(),
            'filter': 'HSC-' + band.upper()
        }
        if butler.datasetExists(label, data_id):
            img = butler.get(label, data_id, immediate=True)
            images.append(img)

    if len(images) == 0:
        if verbose:
            print('***** No data at {:.5f} {:.5f} *****'.format(ra, dec))
        return None

    cutouts = []
    idx, bbox_sizes, bbox_origins = [], [], []

    for img_patch in images:
        # Generate cutout
        cut, x0, y0 = make_single_cutout(img_patch, coord, radius)
        cutouts.append(cut)

        # Original lower corner pixel coordinate
        bbox_origins.append([x0, y0])

        # New lower corner pixel coordinate
        xnew, ynew = cut.getBBox().getBeginX() - x0, cut.getBBox().getBeginY(
        ) - y0
        idx.append([
            xnew, xnew + cut.getBBox().getWidth(), ynew,
            ynew + cut.getBBox().getHeight()
        ])

        # Pixel size of the cutout region
        bbox_sizes.append(cut.getBBox().getWidth() * cut.getBBox().getHeight())

    # Stitch cutouts together with the largest bboxes inserted last
    stamp_bbox = geom.BoxI(geom.Point2I(0, 0), geom.Extent2I(*stamp_shape))
    stamp = afwImage.MaskedImageF(stamp_bbox)
    bbox_sorted_ind = np.argsort(bbox_sizes)

    for i in bbox_sorted_ind:
        masked_img = cutouts[i].getMaskedImage()
        stamp[idx[i][0]:idx[i][1], idx[i][2]:idx[i][3]] = masked_img

    # Build the new WCS of the cutout
    stamp_wcs = build_cutout_wcs(coord, cutouts, bbox_sorted_ind[-1],
                                 bbox_origins)

    # The final product of the cutout
    if psf:
        return (afwImage.ExposureF(stamp, stamp_wcs),
                get_psf(cutouts[bbox_sorted_ind[-1]], coord))
    return afwImage.ExposureF(stamp, stamp_wcs)
Exemplo n.º 16
0
    def _fillVisitCatalog(self,
                          visitCat,
                          groupedDataRefs,
                          visitCatDataRef=None):
        """
        Fill the visit catalog with visit metadata

        Parameters
        ----------
        visitCat: `afw.table.BaseCatalog`
           Catalog with schema from _makeFgcmVisitSchema()
        groupedDataRefs: `dict`
           Dictionary with visit keys, and `list`s of
           `lsst.daf.persistence.ButlerDataRef
        visitCatDataRef: `lsst.daf.persistence.ButlerDataRef`, optional
           Dataref to write visitCat for checkpoints
        """

        bbox = geom.BoxI(geom.PointI(0, 0), geom.PointI(1, 1))

        for i, visit in enumerate(sorted(groupedDataRefs)):
            # We don't use the bypasses since we need the psf info which does
            # not have a bypass
            # TODO: When DM-15500 is implemented in the Gen3 Butler, this
            # can be fixed

            # Do not read those that have already been read
            if visitCat['used'][i]:
                continue

            if (i % self.config.nVisitsPerCheckpoint) == 0:
                self.log.info("Retrieving metadata for %s %d (%d/%d)" %
                              (self.config.visitDataRefName, visit, i,
                               len(groupedDataRefs)))
                # Save checkpoint if desired
                if visitCatDataRef is not None:
                    visitCatDataRef.put(visitCat)

            # Note that the reference ccd is first in the list (if available).

            # The first dataRef in the group will be the reference ccd (if available)
            dataRef = groupedDataRefs[visit][0]

            exp = dataRef.get(datasetType='calexp_sub',
                              bbox=bbox,
                              flags=afwTable.SOURCE_IO_NO_FOOTPRINTS)

            visitInfo = exp.getInfo().getVisitInfo()
            f = exp.getFilter()
            psf = exp.getPsf()

            rec = visitCat[i]
            rec['visit'] = visit
            rec['filtername'] = f.getName()
            # TODO DM-26991: when gen2 is removed, gen3 workflow will make it
            # much easier to get the wcs's necessary to recompute the pointing
            # ra/dec at the center of the camera.
            radec = visitInfo.getBoresightRaDec()
            rec['telra'] = radec.getRa().asDegrees()
            rec['teldec'] = radec.getDec().asDegrees()
            rec['telha'] = visitInfo.getBoresightHourAngle().asDegrees()
            rec['telrot'] = visitInfo.getBoresightRotAngle().asDegrees()
            rec['mjd'] = visitInfo.getDate().get(system=DateTime.MJD)
            rec['exptime'] = visitInfo.getExposureTime()
            # convert from Pa to millibar
            # Note that I don't know if this unit will need to be per-camera config
            rec['pmb'] = visitInfo.getWeather().getAirPressure() / 100
            # Flag to signify if this is a "deep" field.  Not currently used
            rec['deepFlag'] = 0
            # Relative flat scaling (1.0 means no relative scaling)
            rec['scaling'][:] = 1.0
            # Median delta aperture, to be measured from stars
            rec['deltaAper'] = 0.0

            rec['psfSigma'] = psf.computeShape().getDeterminantRadius()

            if dataRef.datasetExists(datasetType='calexpBackground'):
                # Get background for reference CCD
                # This approximation is good enough for now
                bgStats = (bg[0].getStatsImage().getImage().array
                           for bg in dataRef.get(
                               datasetType='calexpBackground'))
                rec['skyBackground'] = sum(
                    np.median(bg[np.isfinite(bg)]) for bg in bgStats)
            else:
                self.log.warn(
                    'Sky background not found for visit %d / ccd %d' %
                    (visit, dataRef.dataId[self.config.ccdDataRefName]))
                rec['skyBackground'] = -1.0

            rec['used'] = 1
Exemplo n.º 17
0
def makeAmplifierList(ccd):
    """Construct a list of AmplifierBuilder objects
    """
    # Much of this will need to be filled in when we know it.
    assert len(ccd) > 0
    amp = list(ccd['amplifiers'].values())[0]

    rawBBox = makeBBoxFromList(amp['rawBBox'])  # total in file
    xRawExtent, yRawExtent = rawBBox.getDimensions()

    readCorners = {"LL": ReadoutCorner.LL,
                   "LR": ReadoutCorner.LR,
                   "UL": ReadoutCorner.UL,
                   "UR": ReadoutCorner.UR}

    amplifierList = []
    for name, amp in sorted(ccd['amplifiers'].items(), key=lambda x: x[1]['hdu']):
        amplifier = Amplifier.Builder()
        amplifier.setName(name)

        ix, iy = amp['ixy']
        perAmpData = amp['perAmpData']
        if perAmpData:
            x0, y0 = 0, 0           # origin of data within each amp image
        else:
            x0, y0 = ix*xRawExtent, iy*yRawExtent

        rawDataBBox = makeBBoxFromList(amp['rawDataBBox'])  # Photosensitive area
        xDataExtent, yDataExtent = rawDataBBox.getDimensions()
        amplifier.setBBox(geom.BoxI(
            geom.PointI(ix*xDataExtent, iy*yDataExtent), rawDataBBox.getDimensions()))

        rawBBox = makeBBoxFromList(amp['rawBBox'])
        rawBBox.shift(geom.ExtentI(x0, y0))
        amplifier.setRawBBox(rawBBox)

        rawDataBBox = makeBBoxFromList(amp['rawDataBBox'])
        rawDataBBox.shift(geom.ExtentI(x0, y0))
        amplifier.setRawDataBBox(rawDataBBox)

        rawSerialOverscanBBox = makeBBoxFromList(amp['rawSerialOverscanBBox'])
        rawSerialOverscanBBox.shift(geom.ExtentI(x0, y0))
        amplifier.setRawHorizontalOverscanBBox(rawSerialOverscanBBox)

        rawParallelOverscanBBox = makeBBoxFromList(amp['rawParallelOverscanBBox'])
        rawParallelOverscanBBox.shift(geom.ExtentI(x0, y0))
        amplifier.setRawVerticalOverscanBBox(rawParallelOverscanBBox)

        rawSerialPrescanBBox = makeBBoxFromList(amp['rawSerialPrescanBBox'])
        rawSerialPrescanBBox.shift(geom.ExtentI(x0, y0))
        amplifier.setRawPrescanBBox(rawSerialPrescanBBox)

        if perAmpData:
            amplifier.setRawXYOffset(geom.Extent2I(ix*xRawExtent, iy*yRawExtent))
        else:
            amplifier.setRawXYOffset(geom.Extent2I(0, 0))

        amplifier.setReadoutCorner(readCorners[amp['readCorner']])
        amplifier.setGain(amp['gain'])
        amplifier.setReadNoise(amp['readNoise'])
        amplifier.setSaturation(amp['saturation'])
        amplifier.setSuspectLevel(amp.get('suspect', np.nan))

        # flip data when assembling if needs be (e.g. data from the serial at the top of a CCD)
        flipX, flipY = amp.get("flipXY")

        amplifier.setRawFlipX(flipX)
        amplifier.setRawFlipY(flipY)
        # linearity placeholder stuff
        amplifier.setLinearityCoeffs([float(val) for val in amp['linearityCoeffs']])
        amplifier.setLinearityType(amp['linearityType'])
        amplifier.setLinearityThreshold(float(amp['linearityThreshold']))
        amplifier.setLinearityMaximum(float(amp['linearityMax']))
        amplifier.setLinearityUnits("DN")
        amplifierList.append(amplifier)
    return amplifierList
Exemplo n.º 18
0
    def setUp(self):
        config = SingleFrameMeasurementTask.ConfigClass()
        config.slots.apFlux = 'base_CircularApertureFlux_12_0'
        self.schema = afwTable.SourceTable.makeMinimalSchema()

        self.measureSources = SingleFrameMeasurementTask(self.schema,
                                                         config=config)

        width, height = 110, 301

        self.mi = afwImage.MaskedImageF(geom.ExtentI(width, height))
        self.mi.set(0)
        sd = 3  # standard deviation of image
        self.mi.getVariance().set(sd * sd)
        self.mi.getMask().addMaskPlane("DETECTED")

        self.ksize = 31  # size of desired kernel

        sigma1 = 1.75
        sigma2 = 2 * sigma1

        self.exposure = afwImage.makeExposure(self.mi)
        self.exposure.setPsf(
            measAlg.DoubleGaussianPsf(self.ksize, self.ksize, 1.5 * sigma1, 1,
                                      0.1))
        cdMatrix = np.array([1.0, 0.0, 0.0, 1.0])
        cdMatrix.shape = (2, 2)
        wcs = afwGeom.makeSkyWcs(crpix=geom.PointD(0, 0),
                                 crval=geom.SpherePoint(
                                     0.0, 0.0, geom.degrees),
                                 cdMatrix=cdMatrix)
        self.exposure.setWcs(wcs)

        #
        # Make a kernel with the exactly correct basis functions.
        # Useful for debugging
        #
        basisKernelList = []
        for sigma in (sigma1, sigma2):
            basisKernel = afwMath.AnalyticKernel(
                self.ksize, self.ksize,
                afwMath.GaussianFunction2D(sigma, sigma))
            basisImage = afwImage.ImageD(basisKernel.getDimensions())
            basisKernel.computeImage(basisImage, True)
            basisImage /= np.sum(basisImage.getArray())

            if sigma == sigma1:
                basisImage0 = basisImage
            else:
                basisImage -= basisImage0

            basisKernelList.append(afwMath.FixedKernel(basisImage))

        order = 1  # 1 => up to linear
        spFunc = afwMath.PolynomialFunction2D(order)

        exactKernel = afwMath.LinearCombinationKernel(basisKernelList, spFunc)
        exactKernel.setSpatialParameters([[1.0, 0, 0],
                                          [0.0, 0.5 * 1e-2, 0.2e-2]])

        rand = afwMath.Random()  # make these tests repeatable by setting seed

        addNoise = True

        if addNoise:
            im = self.mi.getImage()
            afwMath.randomGaussianImage(im, rand)  # N(0, 1)
            im *= sd  # N(0, sd^2)
            del im

        xarr, yarr = [], []

        for x, y in [
            (20, 20),
            (60, 20),
            (30, 35),
            (50, 50),
            (20, 90),
            (70, 160),
            (25, 265),
            (75, 275),
            (85, 30),
            (50, 120),
            (70, 80),
            (60, 210),
            (20, 210),
        ]:
            xarr.append(x)
            yarr.append(y)

        for x, y in zip(xarr, yarr):
            dx = rand.uniform() - 0.5  # random (centered) offsets
            dy = rand.uniform() - 0.5

            k = exactKernel.getSpatialFunction(1)(
                x, y)  # functional variation of Kernel ...
            b = (k * sigma1**2 / ((1 - k) * sigma2**2)
                 )  # ... converted double Gaussian's "b"

            # flux = 80000 - 20*x - 10*(y/float(height))**2
            flux = 80000 * (1 + 0.1 * (rand.uniform() - 0.5))
            I0 = flux * (1 + b) / (2 * np.pi * (sigma1**2 + b * sigma2**2))
            for iy in range(y - self.ksize // 2, y + self.ksize // 2 + 1):
                if iy < 0 or iy >= self.mi.getHeight():
                    continue

                for ix in range(x - self.ksize // 2, x + self.ksize // 2 + 1):
                    if ix < 0 or ix >= self.mi.getWidth():
                        continue

                    II = I0 * psfVal(ix, iy, x + dx, y + dy, sigma1, sigma2, b)
                    Isample = rand.poisson(II) if addNoise else II
                    self.mi.image[ix, iy, afwImage.LOCAL] += Isample
                    self.mi.variance[ix, iy, afwImage.LOCAL] += II

        bbox = geom.BoxI(geom.PointI(0, 0), geom.ExtentI(width, height))
        self.cellSet = afwMath.SpatialCellSet(bbox, 100)

        self.footprintSet = afwDetection.FootprintSet(
            self.mi, afwDetection.Threshold(100), "DETECTED")

        self.catalog = self.measure(self.footprintSet, self.exposure)

        for source in self.catalog:
            try:
                cand = measAlg.makePsfCandidate(source, self.exposure)
                self.cellSet.insertCandidate(cand)

            except Exception as e:
                print(e)
                continue
Exemplo n.º 19
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("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 = geom.BoxI(geom.PointI(i*(width//nAmp), 0), geom.ExtentI(width//nAmp, height))
            ampI = mi.Factory(mi, bbox)
            for j in range(nAmp):
                if i == j:
                    continue

                bbox = geom.BoxI(geom.PointI(j*(width//nAmp), 0), geom.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
Exemplo n.º 20
0
 def bbox(self):
     """Return the detector bounding box from the separate box endpoint
     values.
     """
     return geom.BoxI(geom.PointI(self.bbox_x0, self.bbox_y0),
                      geom.PointI(self.bbox_x1, self.bbox_y1))
Exemplo n.º 21
0
def generate_cutout(butler,
                    skymap,
                    ra,
                    dec,
                    band='N708',
                    data_type='deepCoadd',
                    half_size=10.0 * u.arcsec,
                    psf=True,
                    verbose=False):
    """Generate a single cutout image.
    """
    if not isinstance(half_size, u.Quantity):
        # Assume that this is in pixel
        half_size_pix = int(half_size)
    else:
        half_size_pix = int(half_size.to('arcsec').value / PIXEL_SCALE)

    # Width and height of the post-stamps
    stamp_shape = (half_size_pix * 2 + 1, half_size_pix * 2 + 1)

    # Make a list of (RA, Dec) that covers the cutout region
    radec_list = np.array(
        sky_cone(ra,
                 dec,
                 half_size_pix * PIXEL_SCALE * u.Unit('arcsec'),
                 steps=50)).T

    # Retrieve the Patches that cover the cutout region
    img_patches = _get_patches(butler,
                               skymap,
                               radec_list,
                               band,
                               data_type=data_type)

    if img_patches is None:
        if verbose:
            print('***** No data at {:.5f} {:.5f} *****'.format(ra, dec))
        return None

    # Coordinate of the image center
    coord = geom.SpherePoint(ra * geom.degrees, dec * geom.degrees)

    # Making the stacked cutout
    cutouts = []
    idx, bbox_sizes, bbox_origins = [], [], []

    for img_p in img_patches:
        # Generate cutout
        cut, x0, y0 = _get_single_cutout(img_p, coord, half_size_pix)
        cutouts.append(cut)
        # Original lower corner pixel coordinate
        bbox_origins.append([x0, y0])
        # New lower corner pixel coordinate
        xnew, ynew = cut.getBBox().getBeginX() - x0, cut.getBBox().getBeginY(
        ) - y0
        idx.append([
            xnew, xnew + cut.getBBox().getWidth(), ynew,
            ynew + cut.getBBox().getHeight()
        ])
        # Area of the cutout region on this patch in unit of pixels
        # Will reverse rank all the overlapped images by this
        bbox_sizes.append(cut.getBBox().getWidth() * cut.getBBox().getHeight())

    # Stitch cutouts together with the largest bboxes inserted last
    stamp = afwImage.MaskedImageF(
        geom.BoxI(geom.Point2I(0, 0), geom.Extent2I(*stamp_shape)))
    bbox_sorted_ind = np.argsort(bbox_sizes)

    for i in bbox_sorted_ind:
        masked_img = cutouts[i].getMaskedImage()
        stamp[idx[i][0]:idx[i][1], idx[i][2]:idx[i][3]] = masked_img

    # Build the new WCS of the cutout
    stamp_wcs = _build_cutout_wcs(coord, cutouts, bbox_sorted_ind[-1],
                                  bbox_origins)

    cutout = afwImage.ExposureF(stamp, stamp_wcs)

    if bbox_sizes[bbox_sorted_ind[-1]] < (half_size_pix * 2 + 1)**2:
        flag = 1
    else:
        flag = 2

    # The final product of the cutout
    if psf:
        psf = _get_psf(cutouts[bbox_sorted_ind[-1]], coord)
        return cutout, psf, flag
    return cutout, flag