コード例 #1
0
def find_ghost_centers(fnlist, tolerance=3, thresh=4, guess=None):
    """This routine finds the most likely optical center for a series of images
    by attempting to match ghost reflections of stars with their stellar
    counterparts. It can be rather slow if the fields are very dense with
    stars, but is also much more likely to succeed in locating the correct
    center in dense fields.

    Images should have already been aperture masked at this point. If they
    haven't, run 'aperture_mask' on them first.

    It is useful if the images have had their seeing FWHMs measured. If they
    have, these values (in pixels) should be in the fits headers as "FPFWHM".
    If not, the FWHM is assumed to be 5 pixels (and this is not ideal).

    The routine creates a number of temporary files while running, which are
    deleted at the end of the routine. Interrupting the routine while running
    is probably not a great idea.

    The routine returns a list of image centers as well as appending these to
    the fits headers as "fpxcen" and "fpycen"

    Inputs:
    fnlist -> List of strings, each one containing the path to a fits image.
              The images should be roughly aligned to one another or the
              routine will not work.
    tolerance -> Optional. How close two pixels can be and be "close enough"
                 Default is 3 pixels.
    thresh -> Optional. Level above sky background variation to look for objs.
              Default is 4.5 (times SkySigma). Decrease if center positions
              aren't being found accurately. Increase for crowded fields to
              decrease computation time.
    guess -> Optional. If you already have an idea of where the center should
             be, you'd put it here. Should be a 2-long iterable, with guess[0]
             being the X center guess, and guess[1] being the y center guess.

    Outputs:
    xcenlist -> List of image center X coordinates
    ycenlist -> List of image center Y coordinates

    """

    # Get image FWHMs
    fwhm = np.empty(len(fnlist))
    firstimage = FPImage(fnlist[0])
    toggle = firstimage.fwhm
    axcen = firstimage.axcen
    aycen = firstimage.aycen
    arad = firstimage.arad
    firstimage.close()
    if axcen is None:
        exit("Error! Images have not yet been aperture-masked! Do this first!")
    if toggle is None:
        print "Warning: FWHMs have not been measured!"
        print "Assuming 5 pixel FWHM for all images."
        fwhm = 5.*np.ones(len(fnlist))
    else:
        for i in range(len(fnlist)):
            image = FPImage(fnlist[i])
            fwhm[i] = image.fwhm
            image.close()

    # Get sky background levels
    skyavg = np.empty(len(fnlist))
    skysig = np.empty(len(fnlist))
    for i in range(len(fnlist)):
        image = FPImage(fnlist[i])
        skyavg[i], skysig[i], _skyvar = image.skybackground()
        image.close()

    # Identify the stars in each image
    xlists = []
    ylists = []
    maglists = []
    print "Identifying stars and ghosts in each image..."
    for i in range(len(fnlist)):
        xlists.append([])
        ylists.append([])
        maglists.append([])
        image = FPImage(fnlist[i])
        axcen = image.axcen
        aycen = image.aycen
        arad = image.arad
        sources = daofind(image.inty-skyavg[i],
                          fwhm=fwhm[i],
                          threshold=thresh*skysig[i]).as_array()
        for j in range(len(sources)):
            # Masks for center and edge of image
            cenmask = ((sources[j][1]-axcen)**2 +
                       (sources[j][2]-aycen)**2 > (0.05*arad)**2)
            edgemask = ((sources[j][1]-axcen)**2 +
                        (sources[j][2]-aycen)**2 < (0.95*arad)**2)
            if np.logical_and(cenmask, edgemask):
                xlists[i].append(sources[j][1])
                ylists[i].append(sources[j][2])
                maglists[i].append(sources[j][-1])
        image.close()

    if guess is None:
        # Use the found stars to come up with a center guess for each image
        xcen = np.zeros(len(fnlist))
        ycen = np.zeros(len(fnlist))
        goodcen = np.zeros(len(fnlist))
        for i in range(len(fnlist)):
            N = len(xlists[i])
            xcenarray = np.zeros(((N*N-N)/2))
            ycenarray = np.zeros(((N*N-N)/2))
            dist2array = np.zeros(((N*N-N)/2))
            sub1array = np.zeros(((N*N-N)/2))
            index = 0
            # All "possible" centers for all possible pairs of stars
            for j in range(N):
                for k in range(j+1, N):
                    xcenarray[index] = xlists[i][j]+xlists[i][k]
                    ycenarray[index] = ylists[i][j]+ylists[i][k]
                    index = index+1
            xcenarray, ycenarray = 0.5*xcenarray, 0.5*ycenarray
            # Cross check the various possible centers against each other
            for j in range(len(xcenarray)):
                dist2array = ((xcenarray-xcenarray[j])**2 +
                              (ycenarray-ycenarray[j])**2)
                sub1array[j] = np.sum(dist2array < tolerance**2)
            # Determine the locations of the "best" centers.
            bestcenloc = np.where(sub1array == max(sub1array))[0]
            # Now cross check JUST the best ones against each other
            sub1array = np.zeros(len(bestcenloc))
            xcenarray = xcenarray[bestcenloc]
            ycenarray = ycenarray[bestcenloc]
            for j in range(len(bestcenloc)):
                dist2array = ((xcenarray-xcenarray[j])**2 +
                              (ycenarray-ycenarray[j])**2)
                sub1array[j] = np.sum(dist2array < tolerance**2)
            # Again, determine the locations of the "best" centers.
            bestcenloc = np.where(sub1array == max(sub1array))[0]
            xcen[i] = np.average(xcenarray[bestcenloc])
            ycen[i] = np.average(ycenarray[bestcenloc])

        # Cross-check the various image's centers against each other
        for i in range(len(fnlist)):
            dist2array = (xcen-xcen[i])**2 + (ycen-ycen[i])**2
            goodcen[i] = np.sum(dist2array < tolerance**2)

        # Determine where in the arrays the best fitting centers are
        bestcenloc = np.where(goodcen == max(goodcen))[0]
        bestxcen = np.average(xcen[bestcenloc])
        bestycen = np.average(ycen[bestcenloc])

    else:
        # Forced guess:
        bestxcen, bestycen = guess[0], guess[1]

    # Now we want to improve the center for each image using the best guesses
    xcenlist = np.zeros(len(fnlist))
    ycenlist = np.zeros(len(fnlist))
    objxs = []
    objys = []
    ghoxs = []
    ghoys = []
    for i in range(len(fnlist)):
        # Where would the reflected objects be if they exist
        refxlist = 2.*bestxcen - np.array(xlists[i])
        refylist = 2.*bestycen - np.array(ylists[i])
        # Populate lists of objects and ghosts based on this
        objxs.append([])
        objys.append([])
        ghoxs.append([])
        ghoys.append([])
        for j in range(len(xlists[i])):
            dist2list = ((xlists[i] - refxlist[j])**2 +
                         (ylists[i] - refylist[j])**2)
            matchlist = dist2list < tolerance**2
            if np.sum(matchlist) >= 1:
                # We found a match! Now we need to know where the match is
                matchloc = np.where(matchlist == 1)[0][0]
                # Cool, now, is this thing brighter than the ghost?
                if maglists[i][matchloc] < maglists[i][j]:
                    # It is! Record it:
                    objxs[i].append(xlists[i][matchloc])
                    objys[i].append(ylists[i][matchloc])
                    ghoxs[i].append(xlists[i][j])
                    ghoys[i].append(ylists[i][j])
        # Calculate the centers based on the object / ghost coords
        if len(objxs[i]) == 0:
            xcenlist[i] = 0
            ycenlist[i] = 0
        else:
            xcenlist[i] = 0.5*(np.average(objxs[i])+np.average(ghoxs[i]))
            ycenlist[i] = 0.5*(np.average(objys[i])+np.average(ghoys[i]))

    # Fill in the blanks with a "best guess"
    xcenlist[xcenlist == 0] = np.average(xcenlist[xcenlist != 0])
    ycenlist[ycenlist == 0] = np.average(ycenlist[ycenlist != 0])
    xcenlist[np.isnan(xcenlist)] = bestxcen
    ycenlist[np.isnan(ycenlist)] = bestycen

    # Append the values to the image headers
    for i in range(len(fnlist)):
        image = FPImage(fnlist[i], update=True)
        image.xcen = xcenlist[i]
        image.ycen = ycenlist[i]
        image.close()

    # Manually verify ghost centers
    while True:
        yn = raw_input("Manually verify ghost centers? (Recommended) (y/n) ")
        if "n" in yn or "N" in yn:
            break
        elif "y" in yn or "Y" in yn:
            goodtog = verify_center(fnlist,
                                    objxs, objys,
                                    ghoxs, ghoys,
                                    xcenlist, ycenlist)
            if goodtog:
                break
            else:
                exit("Centers not approved!")

    return xcenlist, ycenlist
コード例 #2
0
def make_final_image(input_image, output_image,
                     desired_fwhm, clobber=False):
    """This routine makes the 'final' images for a data cube. At least the
    paths to the input image, output image, and output wavelength image are
    necessary for this. Beyond that, the user may also have the routine create
    uncertainty images as well.

    Images are convolved to the resolution 'desired_fwhm'. If the current fwhm
    is already higher than that, the routine will throw an error.

    A number of fits header keywords are necessary for this program to function
    properly. Any of these missing will throw an error.

    The output images are intensity-weighted, i.e. the wavelength image will be
    created such that the wavelengths at each pixel are the 'most likely'
    wavelength for the intensity at that pixel, etc.

    Inputs:
    input_image -> Path to the input image.
    output_image -> Path to the output image.
    desired_fwhm -> Desired FWHM for the resultant image to have.

    Optional Inputs:
    clobber -> Overwrite output images if they already exist. Default is False.

    """

    print "Making final data cube images for image "+input_image

    # Open the image
    image = FPImage(input_image)

    # Measure the sky background level in the input image
    skyavg, _truesig, skysig = image.skybackground()

    # Get various header keywords, crash if necessary
    intygrid = image.inty
    fwhm = image.fwhm
    wave0 = image.wave0
    calf = image.calf
    xcen = image.xcen
    ycen = image.ycen
    if fwhm is None:
        exit("Error! FWHM not measured for image "+input_image+".")
    if wave0 is None or calf is None:
        exit("Error! Wavelength solution does " +
             "not exist for image "+input_image+".")
    if xcen is None or ycen is None:
        exit("Error! Center values not measured " +
             "image "+input_image+".")
    if fwhm > desired_fwhm:
        exit("Error! Desired FWHM too low for image " +
             input_image+".")

    # Calculate the necessary FWHM for convolution
    fwhm_conv = np.sqrt(desired_fwhm**2-fwhm**2)
    # Magic number converts sigma to fwhm
    sig = fwhm_conv/2.3548
    # Extremely conservative "safe" kernel size
    ksize = np.ceil(4*sig)
    # Generate the gaussian kernel, shifted to shift the image
    kxgrid, kygrid = np.meshgrid(np.linspace(-ksize, ksize, 2*ksize+1),
                                 np.linspace(-ksize, ksize, 2*ksize+1))
    xshift, yshift = image.xshift, image.yshift
    rad2grid = (kxgrid + xshift)**2 + (kygrid + yshift)**2
    kern = np.exp(-rad2grid/(2*sig**2))

    # Normalize the kernel
    kern = kern/np.sum(kern)

    # Extract relevant arrays
    vargrid = image.vari
    badpixgrid = image.badp

    # Convolve the variances appropriately
    new_vargrid = convolve_variance(vargrid, badpixgrid, kern)

    # Add the sky background uncertainty to the uncertainty grid
    vargrid = vargrid+skysig**2

    # Create and convolve the wavelength array
    if image.wave is None:
        rgrid = image.rarray(xcen, ycen)
        wavegrid = wave0 / np.sqrt(1+rgrid**2/calf**2)
    else:
        wavegrid = image.wave
    new_wavegrid = convolve_wave(wavegrid, intygrid, badpixgrid, kern)

    # Convolve the intensity image
    new_intygrid = convolve_inty(intygrid, badpixgrid, kern)
    new_intygrid[new_intygrid == 0] = intygrid[new_intygrid == 0]
    new_intygrid[np.isnan(new_intygrid)] = intygrid[np.isnan(new_intygrid)]
    image.fwhm = desired_fwhm  # Update header FWHM keyword

    # Subtract the sky background from the image
    new_intygrid[new_intygrid != 0] -= skyavg

    # Create a new fits extension for the wavelength array
    if image.wave is None:
        waveheader = image.openimage[2].header.copy()
        waveheader['EXTVER'] = 4
        wavehdu = ImageHDU(data=new_wavegrid,
                           header=waveheader,
                           name="WAVE")
        image.openimage[1].header.set('WAVEEXT', 4,
                                      comment='Extension for Wavelength Frame')
        image.openimage.append(wavehdu)

    # Put all of the convolved images in the right extensions
    image.inty = new_intygrid
    image.vari = new_vargrid
    image.wave = new_wavegrid

    # Adjust header keywords (even though they're kinda irrelevant now)
    image.xcen += image.xshift
    image.ycen += image.yshift
    image.axcen += image.xshift
    image.aycen += image.yshift

    # Write the output file
    image.writeto(output_image, clobber=clobber)

    # Close images
    image.close()

    return
コード例 #3
0
ファイル: align_norm.py プロジェクト: carlmitchell/saltfppipe
def align_norm(fnlist, tolerance=5, thresh=3.5):
    """Aligns a set of images to each other, as well as normalizing the images
    to the same average brightness.

    Both the alignment and normalization are accomplished through stellar
    photometry using the IRAF routine 'daophot'. The centroids of a handful
    of stars are found and used to run the IRAF routine 'imalign'. The
    instrumental magnitudes of the stars are used to determine by how much
    each image must be scaled for the photometry to match across images.

    The images are simply updated with their rescaled, shifted selves. This
    overwrites the previous images and adds the header keyword 'fpphot' to
    the images.

    A handful of temporary files are created during this process, which should
    all be deleted by the routine at the end. But if it is interrupted, they
    might not be.

    If the uncertainty images exist, this routine also shifts them by the same
    amounts as the intensity images, as well as updating the uncertainty values
    for both the new normalization and the uncertainties in normalizing the
    images.

    Inputs:
    fnlist -> List of strings, each the path to a fits image.
    tolerance -> How close two objects can be and still be considered the same
                 object. Default is 3 pixels.
    thresh -> Optional. Level above sky background variation to look for objs.
              Default is 3.5 (times SkySigma). Decrease if center positions
              aren't being found accurately. Increase for crowded fields to
              decrease computation time.

    """

    # Get image FWHMs
    fwhm = np.empty(len(fnlist))
    firstimage = FPImage(fnlist[0])
    toggle = firstimage.fwhm
    axcen = firstimage.axcen
    aycen = firstimage.aycen
    arad = firstimage.arad
    firstimage.close()
    if axcen is None:
        print "Error! Images have not yet been aperture-masked! Do this first!"
        crash()
    if toggle is None:
        print "Warning! FWHMs have not been measured!"
        print "Assuming 5 pixel FWHM for all images."
        for i in range(len(fnlist)):
            fwhm[i] = 5
    else:
        for i in range(len(fnlist)):
            image = FPImage(fnlist[i])
            fwhm[i] = image.fwhm
            image.close()

    # Get sky background levels
    skyavg = np.empty(len(fnlist))
    skysig = np.empty(len(fnlist))
    for i in range(len(fnlist)):
        image = FPImage(fnlist[i])
        skyavg[i], skysig[i], _skyvar = image.skybackground()
        image.close()

    # Identify the stars in each image
    xlists = []
    ylists = []
    print "Identifying stars in each image..."
    for i in range(len(fnlist)):
        xlists.append([])
        ylists.append([])
        image = FPImage(fnlist[i])
        axcen = image.axcen
        aycen = image.aycen
        arad = image.arad
        sources = daofind(image.inty-skyavg[i],
                          fwhm=fwhm[i],
                          threshold=thresh*skysig[i]).as_array()
        for j in range(len(sources)):
            # If the source is not near the center or edge
            centermask = ((sources[j][1]-axcen)**2 +
                          (sources[j][2]-aycen)**2 > (0.05*arad)**2)
            edgemask = ((sources[j][1]-axcen)**2 +
                        (sources[j][2]-aycen)**2 < (0.95*arad)**2)
            if np.logical_and(centermask, edgemask):
                xlists[i].append(sources[j][1])
                ylists[i].append(sources[j][2])
        image.close()

    # Match objects between fields
    print "Matching objects between images..."
    xcoo = []
    ycoo = []
    for i in range(len(xlists[0])):
        # For each object in the first image
        accept = True
        for j in range(1, len(fnlist)):
            # For each other image
            dist2 = ((np.array(xlists[j])-xlists[0][i])**2 +
                     (np.array(ylists[j])-ylists[0][i])**2)
            if (min(dist2) > tolerance**2):
                accept = False
                break
        if accept:
            # We found an object at that position in every image
            xcoo.append(xlists[0][i])
            ycoo.append(ylists[0][i])

    # Create coordinate arrays for the photometry and shifting
    x = np.zeros((len(fnlist), len(xcoo)))
    y = np.zeros_like(x)
    for i in range(len(xcoo)):
        # For every object found in the first image
        for j in range(len(fnlist)):
            # Find that object in every image
            dist2 = ((np.array(xlists[j])-xcoo[i])**2 +
                     (np.array(ylists[j])-ycoo[i])**2)
            index = np.argmin(dist2)
            x[j, i] = xlists[j][index]
            y[j, i] = ylists[j][index]

    # Do aperture photometry on the matched objects
    print "Performing photometry on matched stars..."
    counts = np.zeros_like(x)
    dcounts = np.zeros_like(x)
    for i in range(len(fnlist)):
        image = FPImage(fnlist[i])
        apertures = CircularAperture((x[i], y[i]), r=2*fwhm[i])
        annuli = CircularAnnulus((x[i], y[i]), r_in=3*fwhm[i], r_out=4*fwhm[i])
        phot_table = aperture_photometry(image.inty,
                                         apertures, error=np.sqrt(image.vari))
        sky_phot_table = aperture_photometry(image.inty, annuli,
                                             error=np.sqrt(image.vari))
        counts[i] = phot_table["aperture_sum"] / apertures.area()
        counts[i] -= sky_phot_table["aperture_sum"] / annuli.area()
        counts[i] *= apertures.area()
        dcounts[i] = phot_table["aperture_sum_err"] / apertures.area()
        image.close()

    # Calculate the shifts and normalizations
    norm, dnorm = calc_norm(counts, dcounts)
    for i in range(x.shape[1]):
        x[:, i] = -(x[:, i] - x[0, i])
        y[:, i] = -(y[:, i] - y[0, i])
    xshifts = np.average(x, axis=1)
    yshifts = np.average(y, axis=1)

    # Normalize the images and put shifts in the image headers
    for i in range(len(fnlist)):
        image = FPImage(fnlist[i], update=True)
        image.phottog = "True"
        image.dnorm = dnorm[i]
        image.inty /= norm[i]
        image.vari = image.vari/norm[i]**2
        image.xshift = xshifts[i]
        image.yshift = yshifts[i]
        image.close()

    return