Esempio n. 1
0
def one_brick(X):
    (ibrick, brick) = X
    bands = ['g', 'r', 'z']

    print('Brick', brick.brickname)
    wcs = wcs_for_brick(brick, W=94, H=94, pixscale=10.)
    BH, BW = wcs.shape
    targetrd = np.array([
        wcs.pixelxy2radec(x, y)
        for x, y in [(1, 1), (BW, 1), (BW, BH), (1, BH), (1, 1)]
    ])
    survey = LegacySurveyData()
    C = survey.ccds_touching_wcs(wcs)
    if C is None:
        print('No CCDs touching brick')
        return None
    I = np.flatnonzero(C.ccd_cuts == 0)
    if len(I) == 0:
        print('No good CCDs touching brick')
        return None
    C.cut(I)
    print(len(C), 'CCDs touching brick')

    depths = {}
    for band in bands:
        d = np.zeros((BH, BW), np.float32)
        depths[band] = d

    npix = dict([(band, 0) for band in bands])
    nexps = dict([(band, 0) for band in bands])

    # survey.get_approx_wcs(ccd)
    for ccd in C:
        #im = survey.get_image_object(ccd)
        awcs = survey.get_approx_wcs(ccd)

        imh, imw = ccd.height, ccd.width
        x0, y0 = 0, 0
        x1 = x0 + imw
        y1 = y0 + imh
        imgpoly = [(1, 1), (1, imh), (imw, imh), (imw, 1)]
        ok, tx, ty = awcs.radec2pixelxy(targetrd[:-1, 0], targetrd[:-1, 1])
        tpoly = list(zip(tx, ty))
        clip = clip_polygon(imgpoly, tpoly)
        clip = np.array(clip)
        if len(clip) == 0:
            continue
        x0, y0 = np.floor(clip.min(axis=0)).astype(int)
        x1, y1 = np.ceil(clip.max(axis=0)).astype(int)
        #slc = slice(y0,y1+1), slice(x0,x1+1)
        awcs = awcs.get_subimage(x0, y0, x1 - x0, y1 - y0)
        ah, aw = awcs.shape

        #print('Image', ccd.expnum, ccd.ccdname, ccd.filter, 'overlap', x0,x1, y0,y1, '->', (1+x1-x0),'x',(1+y1-y0))

        # Find bbox in brick space
        r, d = awcs.pixelxy2radec([1, 1, aw, aw], [1, ah, ah, 1])
        ok, bx, by = wcs.radec2pixelxy(r, d)
        bx0 = np.clip(np.round(bx.min()).astype(int) - 1, 0, BW - 1)
        bx1 = np.clip(np.round(bx.max()).astype(int) - 1, 0, BW - 1)
        by0 = np.clip(np.round(by.min()).astype(int) - 1, 0, BH - 1)
        by1 = np.clip(np.round(by.max()).astype(int) - 1, 0, BH - 1)

        #print('Brick', bx0,bx1,by0,by1)

        band = ccd.filter[0]
        assert (band in bands)

        ccdzpt = ccd.ccdzpt + 2.5 * np.log10(ccd.exptime)

        psf_sigma = ccd.fwhm / 2.35
        psfnorm = 1. / (2. * np.sqrt(np.pi) * psf_sigma)
        orig_zpscale = zpscale = NanoMaggies.zeropointToScale(ccdzpt)
        sig1 = ccd.sig1 / orig_zpscale
        detsig1 = sig1 / psfnorm

        # print('Image', ccd.expnum, ccd.ccdname, ccd.filter,
        #       'PSF depth', -2.5 * (np.log10(5.*detsig1) - 9), 'exptime', ccd.exptime,
        #       'sig1', ccd.sig1, 'zpt', ccd.ccdzpt, 'fwhm', ccd.fwhm,
        #       'filename', ccd.image_filename.strip())

        depths[band][by0:by1 + 1, bx0:bx1 + 1] += (1. / detsig1**2)
        npix[band] += (y1 + 1 - y0) * (x1 + 1 - x0)
        nexps[band] += 1

    for band in bands:
        det = np.median(depths[band])
        # compute stats for 5-sigma detection
        with np.errstate(divide='ignore'):
            depth = 5. / np.sqrt(det)
        # that's flux in nanomaggies -- convert to mag
        depth = -2.5 * (np.log10(depth) - 9)
        if not np.isfinite(depth):
            depth = 0.

        depths[band] = depth
        #bricks.get('psfdepth_' + band)[ibrick] = depth
        print(brick.brickname, 'median PSF depth', band, ':', depth, 'npix',
              npix[band], 'nexp', nexps[band])
        #'npix', bricks.get('npix_'+band)[ibrick],
        #'nexp', bricks.get('nexp_'+band)[ibrick])

    return (npix, nexps, depths)
Esempio n. 2
0
def stage0(**kwargs):
    ps = PlotSequence('cfht')

    decals = CfhtDecals()
    B = decals.get_bricks()
    print('Bricks:')
    B.about()

    ra, dec = 190.0, 11.0

    #bands = 'ugri'
    bands = 'gri'

    B.cut(np.argsort(degrees_between(ra, dec, B.ra, B.dec)))
    print('Nearest bricks:', B.ra[:5], B.dec[:5], B.brickid[:5])

    brick = B[0]
    pixscale = 0.186
    #W,H = 1024,1024
    #W,H = 2048,2048
    #W,H = 3600,3600
    W, H = 4800, 4800

    targetwcs = wcs_for_brick(brick, pixscale=pixscale, W=W, H=H)
    ccdfn = 'cfht-ccds.fits'
    if os.path.exists(ccdfn):
        T = fits_table(ccdfn)
    else:
        T = get_ccd_list()
        T.writeto(ccdfn)
    print(len(T), 'CCDs')
    T.cut(ccds_touching_wcs(targetwcs, T))
    print(len(T), 'CCDs touching brick')

    T.cut(np.array([b in bands for b in T.filter]))
    print(len(T), 'in bands', bands)

    ims = []
    for t in T:
        im = CfhtImage(t)
        # magzp = hdr['PHOT_C'] + 2.5 * np.log10(hdr['EXPTIME'])
        # fwhm = t.seeing / (pixscale * 3600)
        # print '-> FWHM', fwhm, 'pix'
        im.seeing = t.seeing
        im.pixscale = t.pixscale
        print('seeing', t.seeing)
        print('pixscale', im.pixscale * 3600, 'arcsec/pix')
        im.run_calibs(t.ra, t.dec, im.pixscale, W=t.width, H=t.height)
        ims.append(im)

    # Read images, clip to ROI
    targetrd = np.array([
        targetwcs.pixelxy2radec(x, y)
        for x, y in [(1, 1), (W, 1), (W, H), (1, H), (1, 1)]
    ])
    keepims = []
    tims = []
    for im in ims:
        print()
        print('Reading expnum', im.expnum, 'name', im.extname, 'band', im.band,
              'exptime', im.exptime)
        band = im.band
        wcs = im.read_wcs()
        imh, imw = wcs.imageh, wcs.imagew
        imgpoly = [(1, 1), (1, imh), (imw, imh), (imw, 1)]
        ok, tx, ty = wcs.radec2pixelxy(targetrd[:-1, 0], targetrd[:-1, 1])
        tpoly = zip(tx, ty)
        clip = clip_polygon(imgpoly, tpoly)
        clip = np.array(clip)
        #print 'Clip', clip
        if len(clip) == 0:
            continue
        x0, y0 = np.floor(clip.min(axis=0)).astype(int)
        x1, y1 = np.ceil(clip.max(axis=0)).astype(int)
        slc = slice(y0, y1 + 1), slice(x0, x1 + 1)

        ## FIXME -- it seems I got lucky and the cross product is
        ## negative == clockwise, as required by clip_polygon. One
        ## could check this and reverse the polygon vertex order.
        # dx0,dy0 = tx[1]-tx[0], ty[1]-ty[0]
        # dx1,dy1 = tx[2]-tx[1], ty[2]-ty[1]
        # cross = dx0*dy1 - dx1*dy0
        # print 'Cross:', cross

        print('Image slice: x [%i,%i], y [%i,%i]' % (x0, x1, y0, y1))
        print('Reading image from', im.imgfn, 'HDU', im.hdu)
        img, imghdr = im.read_image(header=True, slice=slc)
        goodpix = (img != 0)
        print('Number of pixels == 0:', np.sum(img == 0))
        print('Number of pixels != 0:', np.sum(goodpix))
        if np.sum(goodpix) == 0:
            continue
        # print 'Image shape', img.shape
        print('Image range', img.min(), img.max())
        print('Goodpix image range:', (img[goodpix]).min(),
              (img[goodpix]).max())
        if img[goodpix].min() == img[goodpix].max():
            print('No dynamic range in image')
            continue
        # print 'Reading invvar from', im.wtfn, 'HDU', im.hdu
        # invvar = im.read_invvar(slice=slc)
        # # print 'Invvar shape', invvar.shape
        # # print 'Invvar range:', invvar.min(), invvar.max()
        # invvar[goodpix == 0] = 0.
        # if np.all(invvar == 0.):
        #     print 'Skipping zero-invvar image'
        #     continue
        # assert(np.all(np.isfinite(img)))
        # assert(np.all(np.isfinite(invvar)))
        # assert(not(np.all(invvar == 0.)))
        # # Estimate per-pixel noise via Blanton's 5-pixel MAD
        # slice1 = (slice(0,-5,10),slice(0,-5,10))
        # slice2 = (slice(5,None,10),slice(5,None,10))
        # # print 'sliced shapes:', img[slice1].shape, img[slice2].shape
        # # print 'good shape:', (goodpix[slice1] * goodpix[slice2]).shape
        # # print 'good values:', np.unique(goodpix[slice1] * goodpix[slice2])
        # # print 'sliced[good] shapes:', (img[slice1] -  img[slice2])[goodpix[slice1] * goodpix[slice2]].shape
        # mad = np.median(np.abs(img[slice1] - img[slice2])[goodpix[slice1] * goodpix[slice2]].ravel())
        # sig1 = 1.4826 * mad / np.sqrt(2.)
        # print 'MAD sig1:', sig1
        # # invvar was 1 or 0
        # invvar *= (1./(sig1**2))
        # medsky = np.median(img[goodpix])

        # Read full image for sig1 and sky estimate
        fullimg = im.read_image()
        fullgood = (fullimg != 0)
        # Estimate per-pixel noise via Blanton's 5-pixel MAD
        slice1 = (slice(0, -5, 10), slice(0, -5, 10))
        slice2 = (slice(5, None, 10), slice(5, None, 10))
        mad = np.median(
            np.abs(fullimg[slice1] -
                   fullimg[slice2])[fullgood[slice1] *
                                    fullgood[slice2]].ravel())
        sig1 = 1.4826 * mad / np.sqrt(2.)
        print('MAD sig1:', sig1)
        medsky = np.median(fullimg[fullgood])
        invvar = np.zeros_like(img)
        invvar[goodpix] = 1. / sig1**2

        # Median-smooth sky subtraction
        plt.clf()
        dimshow(np.round((img - medsky) / sig1), vmin=-3, vmax=5)
        plt.title('Scalar median: %s' % im.name)
        ps.savefig()

        # medsky = np.zeros_like(img)
        # # astrometry.util.util
        # median_smooth(img, np.logical_not(goodpix), 256, medsky)
        fullmed = np.zeros_like(fullimg)
        median_smooth(fullimg - medsky, np.logical_not(fullgood), 256, fullmed)
        fullmed += medsky
        medimg = fullmed[slc]

        plt.clf()
        dimshow(np.round((img - medimg) / sig1), vmin=-3, vmax=5)
        plt.title('Median filtered: %s' % im.name)
        ps.savefig()

        #print 'Subtracting median:', medsky
        #img -= medsky
        img -= medimg

        primhdr = im.read_image_primary_header()

        magzp = decals.get_zeropoint_for(im)
        print('magzp', magzp)
        zpscale = NanoMaggies.zeropointToScale(magzp)
        print('zpscale', zpscale)

        # Scale images to Nanomaggies
        img /= zpscale
        sig1 /= zpscale
        invvar *= zpscale**2
        orig_zpscale = zpscale

        zpscale = 1.
        assert (np.sum(invvar > 0) > 0)
        print('After scaling:')
        print('sig1', sig1)
        print('invvar range', invvar.min(), invvar.max())
        print('image range', img.min(), img.max())

        assert (np.all(np.isfinite(img)))
        assert (np.all(np.isfinite(invvar)))
        assert (np.isfinite(sig1))

        plt.clf()
        lo, hi = -5 * sig1, 10 * sig1
        n, b, p = plt.hist(img[goodpix].ravel(),
                           100,
                           range=(lo, hi),
                           histtype='step',
                           color='k')
        xx = np.linspace(lo, hi, 200)
        plt.plot(xx, max(n) * np.exp(-xx**2 / (2. * sig1**2)), 'r-')
        plt.xlim(lo, hi)
        plt.title('Pixel histogram: %s' % im.name)
        ps.savefig()

        twcs = ConstantFitsWcs(wcs)
        if x0 or y0:
            twcs.setX0Y0(x0, y0)

        info = im.get_image_info()
        fullh, fullw = info['dims']

        # read fit PsfEx model
        psfex = PsfEx.fromFits(im.psffitfn)
        print('Read', psfex)

        # HACK -- highly approximate PSF here!
        #psf_fwhm = imghdr['FWHM']
        #psf_fwhm = im.seeing

        psf_fwhm = im.seeing / (im.pixscale * 3600)
        print('PSF FWHM', psf_fwhm, 'pixels')
        psf_sigma = psf_fwhm / 2.35
        psf = NCircularGaussianPSF([psf_sigma], [1.])

        print('img type', img.dtype)

        tim = Image(img,
                    invvar=invvar,
                    wcs=twcs,
                    psf=psf,
                    photocal=LinearPhotoCal(zpscale, band=band),
                    sky=ConstantSky(0.),
                    name=im.name + ' ' + band)
        tim.zr = [-3. * sig1, 10. * sig1]
        tim.sig1 = sig1
        tim.band = band
        tim.psf_fwhm = psf_fwhm
        tim.psf_sigma = psf_sigma
        tim.sip_wcs = wcs
        tim.x0, tim.y0 = int(x0), int(y0)
        tim.psfex = psfex
        tim.imobj = im
        mn, mx = tim.zr
        tim.ima = dict(interpolation='nearest',
                       origin='lower',
                       cmap='gray',
                       vmin=mn,
                       vmax=mx)
        tims.append(tim)
        keepims.append(im)

    ims = keepims

    print('Computing resampling...')
    # save resampling params
    for tim in tims:
        wcs = tim.sip_wcs
        subh, subw = tim.shape
        subwcs = wcs.get_subimage(tim.x0, tim.y0, subw, subh)
        tim.subwcs = subwcs
        try:
            Yo, Xo, Yi, Xi, rims = resample_with_wcs(targetwcs, subwcs, [], 2)
        except OverlapError:
            print('No overlap')
            continue
        if len(Yo) == 0:
            continue
        tim.resamp = (Yo, Xo, Yi, Xi)

    print('Creating coadds...')
    # Produce per-band coadds, for plots
    coimgs = []
    cons = []
    for ib, band in enumerate(bands):
        coimg = np.zeros((H, W), np.float32)
        con = np.zeros((H, W), np.uint8)
        for tim in tims:
            if tim.band != band:
                continue
            (Yo, Xo, Yi, Xi) = tim.resamp
            if len(Yo) == 0:
                continue
            nn = (tim.getInvvar()[Yi, Xi] > 0)
            coimg[Yo, Xo] += tim.getImage()[Yi, Xi] * nn
            con[Yo, Xo] += nn

            # print
            # print 'tim', tim.name
            # print 'number of resampled pix:', len(Yo)
            # reim = np.zeros_like(coimg)
            # ren  = np.zeros_like(coimg)
            # reim[Yo,Xo] = tim.getImage()[Yi,Xi] * nn
            # ren[Yo,Xo] = nn
            # print 'number of resampled pix with positive invvar:', ren.sum()
            # plt.clf()
            # plt.subplot(2,2,1)
            # mn,mx = [np.percentile(reim[ren>0], p) for p in [25,95]]
            # print 'Percentiles:', mn,mx
            # dimshow(reim, vmin=mn, vmax=mx)
            # plt.colorbar()
            # plt.subplot(2,2,2)
            # dimshow(con)
            # plt.colorbar()
            # plt.subplot(2,2,3)
            # dimshow(reim, vmin=tim.zr[0], vmax=tim.zr[1])
            # plt.colorbar()
            # plt.subplot(2,2,4)
            # plt.hist(reim.ravel(), 100, histtype='step', color='b')
            # plt.hist(tim.getImage().ravel(), 100, histtype='step', color='r')
            # plt.suptitle('%s: %s' % (band, tim.name))
            # ps.savefig()

        coimg /= np.maximum(con, 1)
        coimgs.append(coimg)
        cons.append(con)

    plt.clf()
    dimshow(get_rgb(coimgs, bands))
    ps.savefig()

    plt.clf()
    for i, b in enumerate(bands):
        plt.subplot(2, 2, i + 1)
        dimshow(cons[i], ticks=False)
        plt.title('%s band' % b)
        plt.colorbar()
    plt.suptitle('Number of exposures')
    ps.savefig()

    print('Grabbing SDSS sources...')
    bandlist = [b for b in bands]
    cat, T = get_sdss_sources(bandlist, targetwcs)
    # record coordinates in target brick image
    ok, T.tx, T.ty = targetwcs.radec2pixelxy(T.ra, T.dec)
    T.tx -= 1
    T.ty -= 1
    T.itx = np.clip(np.round(T.tx).astype(int), 0, W - 1)
    T.ity = np.clip(np.round(T.ty).astype(int), 0, H - 1)

    plt.clf()
    dimshow(get_rgb(coimgs, bands))
    ax = plt.axis()
    plt.plot(T.tx, T.ty, 'o', mec=green, mfc='none', ms=10, mew=1.5)
    plt.axis(ax)
    plt.title('SDSS sources')
    ps.savefig()

    print('Detmaps...')
    # Render the detection maps
    detmaps = dict([(b, np.zeros((H, W), np.float32)) for b in bands])
    detivs = dict([(b, np.zeros((H, W), np.float32)) for b in bands])
    for tim in tims:
        iv = tim.getInvvar()
        psfnorm = 1. / (2. * np.sqrt(np.pi) * tim.psf_sigma)
        detim = tim.getImage().copy()
        detim[iv == 0] = 0.
        detim = gaussian_filter(detim, tim.psf_sigma) / psfnorm**2
        detsig1 = tim.sig1 / psfnorm
        subh, subw = tim.shape
        detiv = np.zeros((subh, subw), np.float32) + (1. / detsig1**2)
        detiv[iv == 0] = 0.
        (Yo, Xo, Yi, Xi) = tim.resamp
        detmaps[tim.band][Yo, Xo] += detiv[Yi, Xi] * detim[Yi, Xi]
        detivs[tim.band][Yo, Xo] += detiv[Yi, Xi]

    rtn = dict()
    for k in [
            'T', 'coimgs', 'cons', 'detmaps', 'detivs', 'targetrd', 'pixscale',
            'targetwcs', 'W', 'H', 'bands', 'tims', 'ps', 'brick', 'cat'
    ]:
        rtn[k] = locals()[k]
    return rtn
Esempio n. 3
0
    def get_tractor_image(self,
                          slc=None,
                          radecpoly=None,
                          gaussPsf=False,
                          pixPsf=False,
                          hybridPsf=False,
                          splinesky=False,
                          nanomaggies=True,
                          subsky=True,
                          tiny=10,
                          dq=True,
                          invvar=True,
                          pixels=True,
                          constant_invvar=False):
        '''
        Returns a tractor.Image ("tim") object for this image.
        
        Options describing a subimage to return:

        - *slc*: y,x slice objects
        - *radecpoly*: numpy array, shape (N,2), RA,Dec polygon describing
            bounding box to select.

        Options determining the PSF model to use:

        - *gaussPsf*: single circular Gaussian PSF based on header FWHM value.
        - *pixPsf*: pixelized PsfEx model.
        - *hybridPsf*: combo pixelized PsfEx + Gaussian approx.

        Options determining the sky model to use:
        
        - *splinesky*: median filter chunks of the image, then spline those.

        Options determining the units of the image:

        - *nanomaggies*: convert the image to be in units of NanoMaggies;
          *tim.zpscale* contains the scale value the image was divided by.

        - *subsky*: instantiate and subtract the initial sky model,
          leaving a constant zero sky model?

        '''
        get_dq = dq
        get_invvar = invvar

        band = self.band
        imh, imw = self.get_image_shape()
        wcs = self.get_wcs()
        x0, y0 = 0, 0
        x1 = x0 + imw
        y1 = y0 + imh

        # Clip to RA,Dec polygon?
        if slc is None and radecpoly is not None:
            from astrometry.util.miscutils import clip_polygon
            imgpoly = [(1, 1), (1, imh), (imw, imh), (imw, 1)]
            ok, tx, ty = wcs.radec2pixelxy(radecpoly[:-1, 0], radecpoly[:-1,
                                                                        1])
            tpoly = zip(tx, ty)
            clip = clip_polygon(imgpoly, tpoly)
            clip = np.array(clip)
            if len(clip) == 0:
                return None
            x0, y0 = np.floor(clip.min(axis=0)).astype(int)
            x1, y1 = np.ceil(clip.max(axis=0)).astype(int)
            slc = slice(y0, y1 + 1), slice(x0, x1 + 1)
            if y1 - y0 < tiny or x1 - x0 < tiny:
                print('Skipping tiny subimage')
                return None
        # Slice?
        if slc is not None:
            sy, sx = slc
            y0, y1 = sy.start, sy.stop
            x0, x1 = sx.start, sx.stop

        # Is part of this image bad?
        old_extent = (x0, x1, y0, y1)
        new_extent = self.get_good_image_slice((x0, x1, y0, y1),
                                               get_extent=True)
        if new_extent != old_extent:
            x0, x1, y0, y1 = new_extent
            print('Applying good subregion of CCD: slice is', x0, x1, y0, y1)
            if x0 >= x1 or y0 >= y1:
                return None
            slc = slice(y0, y1), slice(x0, x1)

        # Read image pixels
        if pixels:
            print('Reading image slice:', slc)
            img, imghdr = self.read_image(header=True, slice=slc)
            self.check_image_header(imghdr)
        else:
            img = np.zeros((imh, imw), np.float32)
            imghdr = self.read_image_header()
            if slc is not None:
                img = img[slc]
        assert (np.all(np.isfinite(img)))

        # Read inverse-variance (weight) map
        if get_invvar:
            invvar = self.read_invvar(slice=slc, clipThresh=0.)
        else:
            invvar = np.ones_like(img)
        assert (np.all(np.isfinite(invvar)))
        if np.all(invvar == 0.):
            print('Skipping zero-invvar image')
            return None
        # Negative invvars (from, eg, fpack decompression noise) cause havoc
        assert (np.all(invvar >= 0.))

        # Read data-quality (flags) map and zero out the invvars of masked pixels
        if get_dq:
            dq = self.read_dq(slice=slc)
            if dq is not None:
                invvar[dq != 0] = 0.
            if np.all(invvar == 0.):
                print('Skipping zero-invvar image (after DQ masking)')
                return None

        # header 'FWHM' is in pixels
        assert (self.fwhm > 0)
        psf_fwhm = self.fwhm
        psf_sigma = psf_fwhm / 2.35
        primhdr = self.read_image_primary_header()

        sky = self.read_sky_model(splinesky=splinesky,
                                  slc=slc,
                                  primhdr=primhdr,
                                  imghdr=imghdr)
        skysig1 = getattr(sky, 'sig1', None)

        midsky = 0.
        if subsky:
            print('Instantiating and subtracting sky model')
            from tractor.sky import ConstantSky
            skymod = np.zeros_like(img)
            sky.addTo(skymod)
            img -= skymod
            midsky = np.median(skymod)
            zsky = ConstantSky(0.)
            zsky.version = getattr(sky, 'version', '')
            zsky.plver = getattr(sky, 'plver', '')
            del skymod
            sky = zsky
            del zsky

        orig_zpscale = zpscale = NanoMaggies.zeropointToScale(self.ccdzpt)
        if nanomaggies:
            # Scale images to Nanomaggies
            img /= zpscale
            invvar = invvar * zpscale**2
            if not subsky:
                sky.scale(1. / zpscale)
            zpscale = 1.

        # Compute 'sig1', scalar typical per-pixel noise
        if get_invvar:
            sig1 = 1. / np.sqrt(np.median(invvar[invvar > 0]))
        elif skysig1 is not None:
            sig1 = skysig1
            if nanomaggies:
                # skysig1 is in the native units
                sig1 /= orig_zpscale
        else:
            # Estimate per-pixel noise via Blanton's 5-pixel MAD
            slice1 = (slice(0, -5, 10), slice(0, -5, 10))
            slice2 = (slice(5, None, 10), slice(5, None, 10))
            mad = np.median(np.abs(img[slice1] - img[slice2]).ravel())
            sig1 = 1.4826 * mad / np.sqrt(2.)
            print('sig1 estimate:', sig1)
            invvar *= (1. / sig1**2)
        assert (np.isfinite(sig1))

        if constant_invvar:
            print('Setting constant invvar', 1. / sig1**2)
            invvar[invvar > 0] = 1. / sig1**2

        if subsky:
            # Warn if the subtracted sky doesn't seem to work well
            # (can happen, eg, if sky calibration product is inconsistent with
            #  the data)
            imgmed = np.median(img[invvar > 0])
            if np.abs(imgmed) > sig1:
                print('WARNING: image median', imgmed, 'is more than 1 sigma',
                      'away from zero!')

        # tractor WCS object
        twcs = self.get_tractor_wcs(wcs,
                                    x0,
                                    y0,
                                    primhdr=primhdr,
                                    imghdr=imghdr)
        if hybridPsf:
            pixPsf = False
        psf = self.read_psf_model(x0,
                                  y0,
                                  gaussPsf=gaussPsf,
                                  pixPsf=pixPsf,
                                  hybridPsf=hybridPsf,
                                  psf_sigma=psf_sigma,
                                  cx=(x0 + x1) / 2.,
                                  cy=(y0 + y1) / 2.)

        tim = Image(img,
                    invvar=invvar,
                    wcs=twcs,
                    psf=psf,
                    photocal=LinearPhotoCal(zpscale, band=band),
                    sky=sky,
                    name=self.name + ' ' + band)
        assert (np.all(np.isfinite(tim.getInvError())))

        # PSF norm
        psfnorm = self.psf_norm(tim)

        # Galaxy-detection norm
        tim.band = band
        galnorm = self.galaxy_norm(tim)

        # CP (DECam) images include DATE-OBS and MJD-OBS, in UTC.
        import astropy.time
        mjd_tai = astropy.time.Time(self.mjdobs, format='mjd',
                                    scale='utc').tai.mjd
        tim.time = TAITime(None, mjd=mjd_tai)
        tim.slice = slc
        tim.zpscale = orig_zpscale
        tim.midsky = midsky
        tim.sig1 = sig1
        tim.psf_fwhm = psf_fwhm
        tim.psf_sigma = psf_sigma
        tim.propid = self.propid
        tim.psfnorm = psfnorm
        tim.galnorm = galnorm
        tim.sip_wcs = wcs
        tim.x0, tim.y0 = int(x0), int(y0)
        tim.imobj = self
        tim.primhdr = primhdr
        tim.hdr = imghdr
        tim.plver = primhdr.get('PLVER', '').strip()
        tim.skyver = (getattr(sky, 'version', ''), getattr(sky, 'plver', ''))
        tim.wcsver = (getattr(wcs, 'version', ''), getattr(wcs, 'plver', ''))
        tim.psfver = (getattr(psf, 'version', ''), getattr(psf, 'plver', ''))
        if get_dq:
            tim.dq = dq
        tim.dq_saturation_bits = self.dq_saturation_bits
        subh, subw = tim.shape
        tim.subwcs = tim.sip_wcs.get_subimage(tim.x0, tim.y0, subw, subh)
        return tim
Esempio n. 4
0
    def get_tractor_image(self, slc=None, radecpoly=None,
                          gaussPsf=False, const2psf=False, pixPsf=False,
                          splinesky=False,
                          nanomaggies=True, subsky=True, tiny=5,
                          dq=True, invvar=True, pixels=True):
        '''
        Returns a tractor.Image ("tim") object for this image.
        
        Options describing a subimage to return:

        - *slc*: y,x slice objects
        - *radecpoly*: numpy array, shape (N,2), RA,Dec polygon describing bounding box to select.

        Options determining the PSF model to use:

        - *gaussPsf*: single circular Gaussian PSF based on header FWHM value.
        - *const2Psf*: 2-component general Gaussian fit to PsfEx model at image center.
        - *pixPsf*: pixelized PsfEx model at image center.

        Options determining the sky model to use:
        
        - *splinesky*: median filter chunks of the image, then spline those.

        Options determining the units of the image:

        - *nanomaggies*: convert the image to be in units of NanoMaggies;
          *tim.zpscale* contains the scale value the image was divided by.

        - *subsky*: instantiate and subtract the initial sky model,
          leaving a constant zero sky model?

        '''
        from astrometry.util.miscutils import clip_polygon

        get_dq = dq
        get_invvar = invvar
        
        band = self.band
        imh,imw = self.get_image_shape()

        wcs = self.get_wcs()
        x0,y0 = 0,0
        x1 = x0 + imw
        y1 = y0 + imh
        if slc is None and radecpoly is not None:
            imgpoly = [(1,1),(1,imh),(imw,imh),(imw,1)]
            ok,tx,ty = wcs.radec2pixelxy(radecpoly[:-1,0], radecpoly[:-1,1])
            tpoly = zip(tx,ty)
            clip = clip_polygon(imgpoly, tpoly)
            clip = np.array(clip)
            if len(clip) == 0:
                return None
            x0,y0 = np.floor(clip.min(axis=0)).astype(int)
            x1,y1 = np.ceil (clip.max(axis=0)).astype(int)
            slc = slice(y0,y1+1), slice(x0,x1+1)
            if y1 - y0 < tiny or x1 - x0 < tiny:
                print('Skipping tiny subimage')
                return None
        if slc is not None:
            sy,sx = slc
            y0,y1 = sy.start, sy.stop
            x0,x1 = sx.start, sx.stop

        old_extent = (x0,x1,y0,y1)
        new_extent = self.get_good_image_slice((x0,x1,y0,y1), get_extent=True)
        if new_extent != old_extent:
            x0,x1,y0,y1 = new_extent
            print('Applying good subregion of CCD: slice is', x0,x1,y0,y1)
            if x0 >= x1 or y0 >= y1:
                return None
            slc = slice(y0,y1), slice(x0,x1)

        if pixels:
            print('Reading image slice:', slc)
            img,imghdr = self.read_image(header=True, slice=slc)
            #print('SATURATE is', imghdr.get('SATURATE', None))
            #print('Max value in image is', img.max())
            # check consistency... something of a DR1 hangover
            e = imghdr['EXTNAME']
            assert(e.strip() == self.ccdname.strip())
        else:
            img = np.zeros((imh, imw))
            imghdr = dict()
            if slc is not None:
                img = img[slc]
            
        if get_invvar:
            invvar = self.read_invvar(slice=slc, clipThresh=0.)
        else:
            invvar = np.ones_like(img)
            
        if get_dq:
            dq = self.read_dq(slice=slc)
            invvar[dq != 0] = 0.
        if np.all(invvar == 0.):
            print('Skipping zero-invvar image')
            return None
        assert(np.all(np.isfinite(img)))
        assert(np.all(np.isfinite(invvar)))
        assert(not(np.all(invvar == 0.)))

        # header 'FWHM' is in pixels
        # imghdr['FWHM']
        psf_fwhm = self.fwhm 
        psf_sigma = psf_fwhm / 2.35
        primhdr = self.read_image_primary_header()

        sky = self.read_sky_model(splinesky=splinesky, slc=slc)
        midsky = 0.
        if subsky:
            print('Instantiating and subtracting sky model...')
            from tractor.sky import ConstantSky
            skymod = np.zeros_like(img)
            sky.addTo(skymod)
            img -= skymod
            midsky = np.median(skymod)
            zsky = ConstantSky(0.)
            zsky.version = sky.version
            zsky.plver = sky.plver
            del skymod
            del sky
            sky = zsky
            del zsky

        magzp = self.decals.get_zeropoint_for(self)
        orig_zpscale = zpscale = NanoMaggies.zeropointToScale(magzp)
        if nanomaggies:
            # Scale images to Nanomaggies
            img /= zpscale
            invvar *= zpscale**2
            if not subsky:
                sky.scale(1./zpscale)
            zpscale = 1.

        assert(np.sum(invvar > 0) > 0)
        if get_invvar:
            sig1 = 1./np.sqrt(np.median(invvar[invvar > 0]))
        else:
            # Estimate from the image?
            # # Estimate per-pixel noise via Blanton's 5-pixel MAD
            slice1 = (slice(0,-5,10),slice(0,-5,10))
            slice2 = (slice(5,None,10),slice(5,None,10))
            mad = np.median(np.abs(img[slice1] - img[slice2]).ravel())
            sig1 = 1.4826 * mad / np.sqrt(2.)
            print('sig1 estimate:', sig1)
            invvar *= (1. / sig1**2)
            
        assert(np.all(np.isfinite(img)))
        assert(np.all(np.isfinite(invvar)))
        assert(np.isfinite(sig1))

        if subsky:
            ##
            imgmed = np.median(img[invvar>0])
            if np.abs(imgmed) > sig1:
                print('WARNING: image median', imgmed, 'is more than 1 sigma away from zero!')
                # Boom!
                #assert(False)

        twcs = ConstantFitsWcs(wcs)
        if x0 or y0:
            twcs.setX0Y0(x0,y0)

        psf = self.read_psf_model(x0, y0, gaussPsf=gaussPsf, pixPsf=pixPsf,
                                  const2psf=const2psf, psf_sigma=psf_sigma,
                                  cx=(x0+x1)/2., cy=(y0+y1)/2.)

        tim = Image(img, invvar=invvar, wcs=twcs, psf=psf,
                    photocal=LinearPhotoCal(zpscale, band=band),
                    sky=sky, name=self.name + ' ' + band)
        assert(np.all(np.isfinite(tim.getInvError())))

        # PSF norm
        psfnorm = self.psf_norm(tim)
        print('PSF norm', psfnorm, 'vs Gaussian',
              1./(2. * np.sqrt(np.pi) * psf_sigma))

        # Galaxy-detection norm
        tim.band = band
        galnorm = self.galaxy_norm(tim)
        print('Galaxy norm:', galnorm)
        
        # CP (DECam) images include DATE-OBS and MJD-OBS, in UTC.
        import astropy.time
        #mjd_utc = mjd=primhdr.get('MJD-OBS', 0)
        mjd_tai = astropy.time.Time(primhdr['DATE-OBS']).tai.mjd
        tim.slice = slc
        tim.time = TAITime(None, mjd=mjd_tai)
        tim.zr = [-3. * sig1, 10. * sig1]
        tim.zpscale = orig_zpscale
        tim.midsky = midsky
        tim.sig1 = sig1
        tim.psf_fwhm = psf_fwhm
        tim.psf_sigma = psf_sigma
        tim.propid = self.propid
        tim.psfnorm = psfnorm
        tim.galnorm = galnorm
        tim.sip_wcs = wcs
        tim.x0,tim.y0 = int(x0),int(y0)
        tim.imobj = self
        tim.primhdr = primhdr
        tim.hdr = imghdr
        tim.plver = primhdr['PLVER'].strip()
        tim.skyver = (sky.version, sky.plver)
        tim.wcsver = (wcs.version, wcs.plver)
        tim.psfver = (psf.version, psf.plver)
        if get_dq:
            tim.dq = dq
        tim.dq_bits = CP_DQ_BITS
        tim.saturation = imghdr.get('SATURATE', None)
        tim.satval = tim.saturation or 0.
        if subsky:
            tim.satval -= midsky
        if nanomaggies:
            tim.satval /= orig_zpscale
        subh,subw = tim.shape
        tim.subwcs = tim.sip_wcs.get_subimage(tim.x0, tim.y0, subw, subh)
        mn,mx = tim.zr
        tim.ima = dict(interpolation='nearest', origin='lower', cmap='gray',
                       vmin=mn, vmax=mx)
        return tim
Esempio n. 5
0
    def get_tractor_image(self, decals, slc=None, radecpoly=None, mock_psf=False):
        '''
        slc: y,x slices
        '''
        band = self.band
        imh,imw = self.get_image_shape()
        wcs = self.read_wcs()
        x0,y0 = 0,0
        if slc is None and radecpoly is not None:
            imgpoly = [(1,1),(1,imh),(imw,imh),(imw,1)]
            ok,tx,ty = wcs.radec2pixelxy(radecpoly[:-1,0], radecpoly[:-1,1])
            tpoly = zip(tx,ty)
            clip = clip_polygon(imgpoly, tpoly)
            clip = np.array(clip)
            if len(clip) == 0:
                return None
            x0,y0 = np.floor(clip.min(axis=0)).astype(int)
            x1,y1 = np.ceil (clip.max(axis=0)).astype(int)
            slc = slice(y0,y1+1), slice(x0,x1+1)

            if y1 - y0 < 5 or x1 - x0 < 5:
                print 'Skipping tiny subimage'
                return None
        if slc is not None:
            sy,sx = slc
            y0,y1 = sy.start, sy.stop
            x0,x1 = sx.start, sx.stop
        
        print 'Reading image from', self.imgfn, 'HDU', self.hdu
        img,imghdr = self.read_image(header=True, slice=slc)
        print 'Reading invvar from', self.wtfn, 'HDU', self.hdu
        invvar = self.read_invvar(slice=slc, clip=True)

        print 'Invvar range:', invvar.min(), invvar.max()
        if np.all(invvar == 0.):
            print 'Skipping zero-invvar image'
            return None
        assert(np.all(np.isfinite(img)))
        assert(np.all(np.isfinite(invvar)))
        assert(not(np.all(invvar == 0.)))

        # header 'FWHM' is in pixels
        psf_fwhm = imghdr['FWHM']
        psf_sigma = psf_fwhm / 2.35
        primhdr = self.read_image_primary_header()

        magzp = decals.get_zeropoint_for(self)
        print 'magzp', magzp
        zpscale = NanoMaggies.zeropointToScale(magzp)
        print 'zpscale', zpscale

        sky = self.read_sky_model()
        midsky = sky.getConstant()
        img -= midsky
        sky.subtract(midsky)

        # Scale images to Nanomaggies
        img /= zpscale
        invvar *= zpscale**2
        orig_zpscale = zpscale
        zpscale = 1.
        assert(np.sum(invvar > 0) > 0)
        sig1 = 1./np.sqrt(np.median(invvar[invvar > 0]))
        assert(np.all(np.isfinite(img)))
        assert(np.all(np.isfinite(invvar)))
        assert(np.isfinite(sig1))

        twcs = ConstantFitsWcs(wcs)
        if x0 or y0:
            twcs.setX0Y0(x0,y0)

        if mock_psf:
            from tractor.basics import NCircularGaussianPSF
            psfex = None
            psf = NCircularGaussianPSF([1.5], [1.0])
            print 'WARNING: using mock PSF:', psf
        else:
            # read fit PsfEx model -- with ellipse representation
            psfex = PsfEx.fromFits(self.psffitellfn)
            print 'Read', psfex
            psf = psfex

        tim = Image(img, invvar=invvar, wcs=twcs, psf=psf,
                    photocal=LinearPhotoCal(zpscale, band=band),
                    sky=sky, name=self.name + ' ' + band)
        assert(np.all(np.isfinite(tim.getInvError())))
        tim.zr = [-3. * sig1, 10. * sig1]
        tim.midsky = midsky
        tim.sig1 = sig1
        tim.band = band
        tim.psf_fwhm = psf_fwhm
        tim.psf_sigma = psf_sigma
        tim.sip_wcs = wcs
        tim.x0,tim.y0 = int(x0),int(y0)
        tim.psfex = psfex
        tim.imobj = self
        mn,mx = tim.zr
        subh,subw = tim.shape
        tim.subwcs = tim.sip_wcs.get_subimage(tim.x0, tim.y0, subw, subh)
        tim.ima = dict(interpolation='nearest', origin='lower', cmap='gray',
                       vmin=mn, vmax=mx)
        return tim
Esempio n. 6
0
def stage0(**kwargs):
    ps = PlotSequence('cfht')

    decals = CfhtDecals()
    B = decals.get_bricks()
    print 'Bricks:'
    B.about()

    ra,dec = 190.0, 11.0

    #bands = 'ugri'
    bands = 'gri'
    
    B.cut(np.argsort(degrees_between(ra, dec, B.ra, B.dec)))
    print 'Nearest bricks:', B.ra[:5], B.dec[:5], B.brickid[:5]

    brick = B[0]
    pixscale = 0.186
    #W,H = 1024,1024
    #W,H = 2048,2048
    #W,H = 3600,3600
    W,H = 4800,4800

    targetwcs = wcs_for_brick(brick, pixscale=pixscale, W=W, H=H)
    ccdfn = 'cfht-ccds.fits'
    if os.path.exists(ccdfn):
        T = fits_table(ccdfn)
    else:
        T = get_ccd_list()
        T.writeto(ccdfn)
    print len(T), 'CCDs'
    T.cut(ccds_touching_wcs(targetwcs, T))
    print len(T), 'CCDs touching brick'

    T.cut(np.array([b in bands for b in T.filter]))
    print len(T), 'in bands', bands

    ims = []
    for t in T:
        im = CfhtImage(t)
        # magzp = hdr['PHOT_C'] + 2.5 * np.log10(hdr['EXPTIME'])
        # fwhm = t.seeing / (pixscale * 3600)
        # print '-> FWHM', fwhm, 'pix'
        im.seeing = t.seeing
        im.pixscale = t.pixscale
        print 'seeing', t.seeing
        print 'pixscale', im.pixscale*3600, 'arcsec/pix'
        im.run_calibs(t.ra, t.dec, im.pixscale, W=t.width, H=t.height)
        ims.append(im)


    # Read images, clip to ROI
    targetrd = np.array([targetwcs.pixelxy2radec(x,y) for x,y in
                         [(1,1),(W,1),(W,H),(1,H),(1,1)]])
    keepims = []
    tims = []
    for im in ims:
        print
        print 'Reading expnum', im.expnum, 'name', im.extname, 'band', im.band, 'exptime', im.exptime
        band = im.band
        wcs = im.read_wcs()
        imh,imw = wcs.imageh,wcs.imagew
        imgpoly = [(1,1),(1,imh),(imw,imh),(imw,1)]
        ok,tx,ty = wcs.radec2pixelxy(targetrd[:-1,0], targetrd[:-1,1])
        tpoly = zip(tx,ty)
        clip = clip_polygon(imgpoly, tpoly)
        clip = np.array(clip)
        #print 'Clip', clip
        if len(clip) == 0:
            continue
        x0,y0 = np.floor(clip.min(axis=0)).astype(int)
        x1,y1 = np.ceil (clip.max(axis=0)).astype(int)
        slc = slice(y0,y1+1), slice(x0,x1+1)

        ## FIXME -- it seems I got lucky and the cross product is
        ## negative == clockwise, as required by clip_polygon. One
        ## could check this and reverse the polygon vertex order.
        # dx0,dy0 = tx[1]-tx[0], ty[1]-ty[0]
        # dx1,dy1 = tx[2]-tx[1], ty[2]-ty[1]
        # cross = dx0*dy1 - dx1*dy0
        # print 'Cross:', cross

        print 'Image slice: x [%i,%i], y [%i,%i]' % (x0,x1, y0,y1)
        print 'Reading image from', im.imgfn, 'HDU', im.hdu
        img,imghdr = im.read_image(header=True, slice=slc)
        goodpix = (img != 0)
        print 'Number of pixels == 0:', np.sum(img == 0)
        print 'Number of pixels != 0:', np.sum(goodpix)
        if np.sum(goodpix) == 0:
            continue
        # print 'Image shape', img.shape
        print 'Image range', img.min(), img.max()
        print 'Goodpix image range:', (img[goodpix]).min(), (img[goodpix]).max()
        if img[goodpix].min() == img[goodpix].max():
            print 'No dynamic range in image'
            continue
        # print 'Reading invvar from', im.wtfn, 'HDU', im.hdu
        # invvar = im.read_invvar(slice=slc)
        # # print 'Invvar shape', invvar.shape
        # # print 'Invvar range:', invvar.min(), invvar.max()
        # invvar[goodpix == 0] = 0.
        # if np.all(invvar == 0.):
        #     print 'Skipping zero-invvar image'
        #     continue
        # assert(np.all(np.isfinite(img)))
        # assert(np.all(np.isfinite(invvar)))
        # assert(not(np.all(invvar == 0.)))
        # # Estimate per-pixel noise via Blanton's 5-pixel MAD
        # slice1 = (slice(0,-5,10),slice(0,-5,10))
        # slice2 = (slice(5,None,10),slice(5,None,10))
        # # print 'sliced shapes:', img[slice1].shape, img[slice2].shape
        # # print 'good shape:', (goodpix[slice1] * goodpix[slice2]).shape
        # # print 'good values:', np.unique(goodpix[slice1] * goodpix[slice2])
        # # print 'sliced[good] shapes:', (img[slice1] -  img[slice2])[goodpix[slice1] * goodpix[slice2]].shape
        # mad = np.median(np.abs(img[slice1] - img[slice2])[goodpix[slice1] * goodpix[slice2]].ravel())
        # sig1 = 1.4826 * mad / np.sqrt(2.)
        # print 'MAD sig1:', sig1
        # # invvar was 1 or 0
        # invvar *= (1./(sig1**2))
        # medsky = np.median(img[goodpix])

        # Read full image for sig1 and sky estimate
        fullimg = im.read_image()
        fullgood = (fullimg != 0)
        # Estimate per-pixel noise via Blanton's 5-pixel MAD
        slice1 = (slice(0,-5,10),slice(0,-5,10))
        slice2 = (slice(5,None,10),slice(5,None,10))
        mad = np.median(np.abs(fullimg[slice1] - fullimg[slice2])[fullgood[slice1] * fullgood[slice2]].ravel())
        sig1 = 1.4826 * mad / np.sqrt(2.)
        print 'MAD sig1:', sig1
        medsky = np.median(fullimg[fullgood])
        invvar = np.zeros_like(img)
        invvar[goodpix] = 1./sig1**2

        # Median-smooth sky subtraction
        plt.clf()
        dimshow(np.round((img-medsky) / sig1), vmin=-3, vmax=5)
        plt.title('Scalar median: %s' % im.name)
        ps.savefig()

        # medsky = np.zeros_like(img)
        # # astrometry.util.util
        # median_smooth(img, np.logical_not(goodpix), 256, medsky)
        fullmed = np.zeros_like(fullimg)
        median_smooth(fullimg - medsky, np.logical_not(fullgood), 256, fullmed)
        fullmed += medsky
        medimg = fullmed[slc]
        
        plt.clf()
        dimshow(np.round((img - medimg) / sig1), vmin=-3, vmax=5)
        plt.title('Median filtered: %s' % im.name)
        ps.savefig()
        
        #print 'Subtracting median:', medsky
        #img -= medsky
        img -= medimg
        
        primhdr = im.read_image_primary_header()

        magzp = decals.get_zeropoint_for(im)
        print 'magzp', magzp
        zpscale = NanoMaggies.zeropointToScale(magzp)
        print 'zpscale', zpscale

        # Scale images to Nanomaggies
        img /= zpscale
        sig1 /= zpscale
        invvar *= zpscale**2
        orig_zpscale = zpscale

        zpscale = 1.
        assert(np.sum(invvar > 0) > 0)
        print 'After scaling:'
        print 'sig1', sig1
        print 'invvar range', invvar.min(), invvar.max()
        print 'image range', img.min(), img.max()

        assert(np.all(np.isfinite(img)))
        assert(np.all(np.isfinite(invvar)))
        assert(np.isfinite(sig1))

        plt.clf()
        lo,hi = -5*sig1, 10*sig1
        n,b,p = plt.hist(img[goodpix].ravel(), 100, range=(lo,hi), histtype='step', color='k')
        xx = np.linspace(lo, hi, 200)
        plt.plot(xx, max(n)*np.exp(-xx**2 / (2.*sig1**2)), 'r-')
        plt.xlim(lo,hi)
        plt.title('Pixel histogram: %s' % im.name)
        ps.savefig()

        twcs = ConstantFitsWcs(wcs)
        if x0 or y0:
            twcs.setX0Y0(x0,y0)

        info = im.get_image_info()
        fullh,fullw = info['dims']

        # read fit PsfEx model
        psfex = PsfEx.fromFits(im.psffitfn)
        print 'Read', psfex

        # HACK -- highly approximate PSF here!
        #psf_fwhm = imghdr['FWHM']
        #psf_fwhm = im.seeing

        psf_fwhm = im.seeing / (im.pixscale * 3600)
        print 'PSF FWHM', psf_fwhm, 'pixels'
        psf_sigma = psf_fwhm / 2.35
        psf = NCircularGaussianPSF([psf_sigma],[1.])

        print 'img type', img.dtype
        
        tim = Image(img, invvar=invvar, wcs=twcs, psf=psf,
                    photocal=LinearPhotoCal(zpscale, band=band),
                    sky=ConstantSky(0.), name=im.name + ' ' + band)
        tim.zr = [-3. * sig1, 10. * sig1]
        tim.sig1 = sig1
        tim.band = band
        tim.psf_fwhm = psf_fwhm
        tim.psf_sigma = psf_sigma
        tim.sip_wcs = wcs
        tim.x0,tim.y0 = int(x0),int(y0)
        tim.psfex = psfex
        tim.imobj = im
        mn,mx = tim.zr
        tim.ima = dict(interpolation='nearest', origin='lower', cmap='gray',
                       vmin=mn, vmax=mx)
        tims.append(tim)
        keepims.append(im)

    ims = keepims

    print 'Computing resampling...'
    # save resampling params
    for tim in tims:
        wcs = tim.sip_wcs
        subh,subw = tim.shape
        subwcs = wcs.get_subimage(tim.x0, tim.y0, subw, subh)
        tim.subwcs = subwcs
        try:
            Yo,Xo,Yi,Xi,rims = resample_with_wcs(targetwcs, subwcs, [], 2)
        except OverlapError:
            print 'No overlap'
            continue
        if len(Yo) == 0:
            continue
        tim.resamp = (Yo,Xo,Yi,Xi)

    print 'Creating coadds...'
    # Produce per-band coadds, for plots
    coimgs = []
    cons = []
    for ib,band in enumerate(bands):
        coimg = np.zeros((H,W), np.float32)
        con   = np.zeros((H,W), np.uint8)
        for tim in tims:
            if tim.band != band:
                continue
            (Yo,Xo,Yi,Xi) = tim.resamp
            if len(Yo) == 0:
                continue
            nn = (tim.getInvvar()[Yi,Xi] > 0)
            coimg[Yo,Xo] += tim.getImage ()[Yi,Xi] * nn
            con  [Yo,Xo] += nn

            # print
            # print 'tim', tim.name
            # print 'number of resampled pix:', len(Yo)
            # reim = np.zeros_like(coimg)
            # ren  = np.zeros_like(coimg)
            # reim[Yo,Xo] = tim.getImage()[Yi,Xi] * nn
            # ren[Yo,Xo] = nn
            # print 'number of resampled pix with positive invvar:', ren.sum()
            # plt.clf()
            # plt.subplot(2,2,1)
            # mn,mx = [np.percentile(reim[ren>0], p) for p in [25,95]]
            # print 'Percentiles:', mn,mx
            # dimshow(reim, vmin=mn, vmax=mx)
            # plt.colorbar()
            # plt.subplot(2,2,2)
            # dimshow(con)
            # plt.colorbar()
            # plt.subplot(2,2,3)
            # dimshow(reim, vmin=tim.zr[0], vmax=tim.zr[1])
            # plt.colorbar()
            # plt.subplot(2,2,4)
            # plt.hist(reim.ravel(), 100, histtype='step', color='b')
            # plt.hist(tim.getImage().ravel(), 100, histtype='step', color='r')
            # plt.suptitle('%s: %s' % (band, tim.name))
            # ps.savefig()

        coimg /= np.maximum(con,1)
        coimgs.append(coimg)
        cons  .append(con)

    plt.clf()
    dimshow(get_rgb(coimgs, bands))
    ps.savefig()

    plt.clf()
    for i,b in enumerate(bands):
        plt.subplot(2,2,i+1)
        dimshow(cons[i], ticks=False)
        plt.title('%s band' % b)
        plt.colorbar()
    plt.suptitle('Number of exposures')
    ps.savefig()

    print 'Grabbing SDSS sources...'
    bandlist = [b for b in bands]
    cat,T = get_sdss_sources(bandlist, targetwcs)
    # record coordinates in target brick image
    ok,T.tx,T.ty = targetwcs.radec2pixelxy(T.ra, T.dec)
    T.tx -= 1
    T.ty -= 1
    T.itx = np.clip(np.round(T.tx).astype(int), 0, W-1)
    T.ity = np.clip(np.round(T.ty).astype(int), 0, H-1)

    plt.clf()
    dimshow(get_rgb(coimgs, bands))
    ax = plt.axis()
    plt.plot(T.tx, T.ty, 'o', mec=green, mfc='none', ms=10, mew=1.5)
    plt.axis(ax)
    plt.title('SDSS sources')
    ps.savefig()

    print 'Detmaps...'
    # Render the detection maps
    detmaps = dict([(b, np.zeros((H,W), np.float32)) for b in bands])
    detivs  = dict([(b, np.zeros((H,W), np.float32)) for b in bands])
    for tim in tims:
        iv = tim.getInvvar()
        psfnorm = 1./(2. * np.sqrt(np.pi) * tim.psf_sigma)
        detim = tim.getImage().copy()
        detim[iv == 0] = 0.
        detim = gaussian_filter(detim, tim.psf_sigma) / psfnorm**2
        detsig1 = tim.sig1 / psfnorm
        subh,subw = tim.shape
        detiv = np.zeros((subh,subw), np.float32) + (1. / detsig1**2)
        detiv[iv == 0] = 0.
        (Yo,Xo,Yi,Xi) = tim.resamp
        detmaps[tim.band][Yo,Xo] += detiv[Yi,Xi] * detim[Yi,Xi]
        detivs [tim.band][Yo,Xo] += detiv[Yi,Xi]

    rtn = dict()
    for k in ['T', 'coimgs', 'cons', 'detmaps', 'detivs',
              'targetrd', 'pixscale', 'targetwcs', 'W','H',
              'bands', 'tims', 'ps', 'brick', 'cat']:
        rtn[k] = locals()[k]
    return rtn
Esempio n. 7
0
    def get_tractor_image(self,
                          slc=None,
                          radecpoly=None,
                          gaussPsf=False,
                          const2psf=False,
                          pixPsf=False,
                          splinesky=False,
                          nanomaggies=True,
                          subsky=True,
                          tiny=5,
                          dq=True,
                          invvar=True,
                          pixels=True):
        '''
        Returns a tractor.Image ("tim") object for this image.
        
        Options describing a subimage to return:

        - *slc*: y,x slice objects
        - *radecpoly*: numpy array, shape (N,2), RA,Dec polygon describing bounding box to select.

        Options determining the PSF model to use:

        - *gaussPsf*: single circular Gaussian PSF based on header FWHM value.
        - *const2Psf*: 2-component general Gaussian fit to PsfEx model at image center.
        - *pixPsf*: pixelized PsfEx model at image center.

        Options determining the sky model to use:
        
        - *splinesky*: median filter chunks of the image, then spline those.

        Options determining the units of the image:

        - *nanomaggies*: convert the image to be in units of NanoMaggies;
          *tim.zpscale* contains the scale value the image was divided by.

        - *subsky*: instantiate and subtract the initial sky model,
          leaving a constant zero sky model?

        '''
        from astrometry.util.miscutils import clip_polygon

        get_dq = dq
        get_invvar = invvar

        band = self.band
        imh, imw = self.get_image_shape()

        wcs = self.get_wcs()
        x0, y0 = 0, 0
        x1 = x0 + imw
        y1 = y0 + imh
        if slc is None and radecpoly is not None:
            imgpoly = [(1, 1), (1, imh), (imw, imh), (imw, 1)]
            ok, tx, ty = wcs.radec2pixelxy(radecpoly[:-1, 0], radecpoly[:-1,
                                                                        1])
            tpoly = zip(tx, ty)
            clip = clip_polygon(imgpoly, tpoly)
            clip = np.array(clip)
            if len(clip) == 0:
                return None
            x0, y0 = np.floor(clip.min(axis=0)).astype(int)
            x1, y1 = np.ceil(clip.max(axis=0)).astype(int)
            slc = slice(y0, y1 + 1), slice(x0, x1 + 1)
            if y1 - y0 < tiny or x1 - x0 < tiny:
                print('Skipping tiny subimage')
                return None
        if slc is not None:
            sy, sx = slc
            y0, y1 = sy.start, sy.stop
            x0, x1 = sx.start, sx.stop

        old_extent = (x0, x1, y0, y1)
        new_extent = self.get_good_image_slice((x0, x1, y0, y1),
                                               get_extent=True)
        if new_extent != old_extent:
            x0, x1, y0, y1 = new_extent
            print('Applying good subregion of CCD: slice is', x0, x1, y0, y1)
            if x0 >= x1 or y0 >= y1:
                return None
            slc = slice(y0, y1), slice(x0, x1)

        if pixels:
            print('Reading image slice:', slc)
            img, imghdr = self.read_image(header=True, slice=slc)
            #print('SATURATE is', imghdr.get('SATURATE', None))
            #print('Max value in image is', img.max())
            # check consistency... something of a DR1 hangover
            e = imghdr['EXTNAME']
            assert (e.strip() == self.ccdname.strip())
        else:
            img = np.zeros((imh, imw))
            imghdr = dict()
            if slc is not None:
                img = img[slc]

        if get_invvar:
            invvar = self.read_invvar(slice=slc, clipThresh=0.)
        else:
            invvar = np.ones_like(img)

        if get_dq:
            dq = self.read_dq(slice=slc)
            invvar[dq != 0] = 0.
        if np.all(invvar == 0.):
            print('Skipping zero-invvar image')
            return None
        assert (np.all(np.isfinite(img)))
        assert (np.all(np.isfinite(invvar)))
        assert (not (np.all(invvar == 0.)))

        # header 'FWHM' is in pixels
        # imghdr['FWHM']
        psf_fwhm = self.fwhm
        psf_sigma = psf_fwhm / 2.35
        primhdr = self.read_image_primary_header()

        sky = self.read_sky_model(splinesky=splinesky, slc=slc)
        midsky = 0.
        if subsky:
            print('Instantiating and subtracting sky model...')
            from tractor.sky import ConstantSky
            skymod = np.zeros_like(img)
            sky.addTo(skymod)
            img -= skymod
            midsky = np.median(skymod)
            zsky = ConstantSky(0.)
            zsky.version = sky.version
            zsky.plver = sky.plver
            del skymod
            del sky
            sky = zsky
            del zsky

        magzp = self.decals.get_zeropoint_for(self)
        orig_zpscale = zpscale = NanoMaggies.zeropointToScale(magzp)
        if nanomaggies:
            # Scale images to Nanomaggies
            img /= zpscale
            invvar *= zpscale**2
            if not subsky:
                sky.scale(1. / zpscale)
            zpscale = 1.

        assert (np.sum(invvar > 0) > 0)
        if get_invvar:
            sig1 = 1. / np.sqrt(np.median(invvar[invvar > 0]))
        else:
            # Estimate from the image?
            # # Estimate per-pixel noise via Blanton's 5-pixel MAD
            slice1 = (slice(0, -5, 10), slice(0, -5, 10))
            slice2 = (slice(5, None, 10), slice(5, None, 10))
            mad = np.median(np.abs(img[slice1] - img[slice2]).ravel())
            sig1 = 1.4826 * mad / np.sqrt(2.)
            print('sig1 estimate:', sig1)
            invvar *= (1. / sig1**2)

        assert (np.all(np.isfinite(img)))
        assert (np.all(np.isfinite(invvar)))
        assert (np.isfinite(sig1))

        if subsky:
            ##
            imgmed = np.median(img[invvar > 0])
            if np.abs(imgmed) > sig1:
                print('WARNING: image median', imgmed,
                      'is more than 1 sigma away from zero!')
                # Boom!
                #assert(False)

        twcs = ConstantFitsWcs(wcs)
        if x0 or y0:
            twcs.setX0Y0(x0, y0)

        psf = self.read_psf_model(x0,
                                  y0,
                                  gaussPsf=gaussPsf,
                                  pixPsf=pixPsf,
                                  const2psf=const2psf,
                                  psf_sigma=psf_sigma,
                                  cx=(x0 + x1) / 2.,
                                  cy=(y0 + y1) / 2.)

        tim = Image(img,
                    invvar=invvar,
                    wcs=twcs,
                    psf=psf,
                    photocal=LinearPhotoCal(zpscale, band=band),
                    sky=sky,
                    name=self.name + ' ' + band)
        assert (np.all(np.isfinite(tim.getInvError())))

        # PSF norm
        psfnorm = self.psf_norm(tim)
        print('PSF norm', psfnorm, 'vs Gaussian',
              1. / (2. * np.sqrt(np.pi) * psf_sigma))

        # Galaxy-detection norm
        tim.band = band
        galnorm = self.galaxy_norm(tim)
        print('Galaxy norm:', galnorm)

        # CP (DECam) images include DATE-OBS and MJD-OBS, in UTC.
        import astropy.time
        #mjd_utc = mjd=primhdr.get('MJD-OBS', 0)
        mjd_tai = astropy.time.Time(primhdr['DATE-OBS']).tai.mjd
        tim.slice = slc
        tim.time = TAITime(None, mjd=mjd_tai)
        tim.zr = [-3. * sig1, 10. * sig1]
        tim.zpscale = orig_zpscale
        tim.midsky = midsky
        tim.sig1 = sig1
        tim.psf_fwhm = psf_fwhm
        tim.psf_sigma = psf_sigma
        tim.propid = self.propid
        tim.psfnorm = psfnorm
        tim.galnorm = galnorm
        tim.sip_wcs = wcs
        tim.x0, tim.y0 = int(x0), int(y0)
        tim.imobj = self
        tim.primhdr = primhdr
        tim.hdr = imghdr
        tim.plver = primhdr['PLVER'].strip()
        tim.skyver = (sky.version, sky.plver)
        tim.wcsver = (wcs.version, wcs.plver)
        tim.psfver = (psf.version, psf.plver)
        if get_dq:
            tim.dq = dq
        tim.dq_bits = CP_DQ_BITS
        tim.saturation = imghdr.get('SATURATE', None)
        tim.satval = tim.saturation or 0.
        if subsky:
            tim.satval -= midsky
        if nanomaggies:
            tim.satval /= orig_zpscale
        subh, subw = tim.shape
        tim.subwcs = tim.sip_wcs.get_subimage(tim.x0, tim.y0, subw, subh)
        mn, mx = tim.zr
        tim.ima = dict(interpolation='nearest',
                       origin='lower',
                       cmap='gray',
                       vmin=mn,
                       vmax=mx)
        return tim