Example #1
0
def separate_lists(fnlist):
    """Separates a list of images into separate lists based on their header
    keywords 'OBJECT' and 'FILTER'
    
    Inputs:
    fnlist -> A list containing strings, each the location of a fits image
    
    Outputs:
    flatlist -> List of flatfield images
    list_of_objects -> List of distinct objects
    objectlists -> List of lists, each one all of the files for a particular object
    list_of_filters -> List of distinct filters
    filterlists -> List of lists, each one all the files for a given filter
    
    """
    
    #Open each file and look for flats and new filters/objects
    list_of_objects = []
    list_of_filters = []
    flatlist = []
    for i in range(len(fnlist)):
        image = openfits(fnlist[i])
        #Break if image was previously flagged as bad
        fpgood = image[0].header.get("FPGOOD")
        if not (fpgood is None):
            if "False" in fpgood:
                continue
        #Case for flats
        if image[0].header["OBJECT"]=="FLAT": flatlist.append(fnlist[i])
        #Case for ARCs
        elif image[0].header["OBJECT"]=="ARC":
            if not (image[0].header["FILTER"] in list_of_filters): list_of_filters.append(image[0].header["FILTER"])
        #Case for Objects
        else:
            if not (image[0].header["FILTER"] in list_of_filters): list_of_filters.append(image[0].header["FILTER"])
            if not (image[0].header["OBJECT"] in list_of_objects): list_of_objects.append(image[0].header["OBJECT"])
        image.close()
    list_of_filters = np.array(list_of_filters)
    list_of_objects = np.array(list_of_objects)
        
    #Reopen the files and populate the object lists
    objectlists = []
    filterlists = []
    for i in range(len(list_of_objects)): objectlists.append([])
    for i in range(len(list_of_filters)): filterlists.append([])
    for i in range(len(fnlist)):
        image = openfits(fnlist[i])
        #Break if image was previously flagged as bad
        fpgood = image[0].header.get("FPGOOD")
        if not (fpgood is None):
            if "False" in fpgood:
                continue
        if image[0].header["OBJECT"]!="FLAT":
            if image[0].header["OBJECT"]!="ARC": objectlists[np.where(image[0].header["OBJECT"]==list_of_objects)[0][0]].append(fnlist[i])
            filterlists[np.where(image[0].header["FILTER"]==list_of_filters)[0][0]].append(fnlist[i])
        image.close()
    return flatlist, list_of_objects, objectlists, list_of_filters, filterlists
Example #2
0
def get_aperture(path):
    """Opens a fits image and has the user click on points near the image
    aperture to fit a circle to those points. Once at least three points
    on the aperture boundary have been clicked, begins displaying a best-fit
    circle to those points overlayed on the image.
    
    Inputs:
    path -> Path to the fits file to open
    
    Outputs:
    axcen -> The aperture x center
    aycen -> The aperture y center
    arad -> The aperture radius
    
    """

    image = openfits(path)
    xs, ys = [], []
    axcen, aycen, arad = image[0].shape[1]/2, image[0].shape[0]/2, 0
    apguess = np.array([image[0].shape[1]/2,image[0].shape[0]/2,min(image[0].shape[0],image[0].shape[1])/2],dtype='float64')
    while True:
        click = Click_Near_Aperture_Plot(image[0].data,xs,ys,axcen,aycen,arad)
        if not (click.xcoo is None) and click.xcoo>26 and click.ycoo>26 and click.xcoo<image[0].data.shape[1]-26 and click.ycoo<image[0].data.shape[0]-26:
            newclick = Zoom_Aperture_Plot(image[0].data[click.ycoo-25:click.ycoo+25,click.xcoo-25:click.xcoo+25])
            if not (newclick.xcoo is None):
                xs.append(newclick.xcoo+click.xcoo-25)
                ys.append(newclick.ycoo+click.ycoo-25)
                if len(xs)>2:
                    apparams = minimize(lambda x: np.sum(((np.array(xs)-x[0])**2+(np.array(ys)-x[1])**2-x[2]**2)**2), apguess).x
                    axcen, aycen, arad = apparams[0], apparams[1], apparams[2]
        elif len(xs)>2 and click.xcoo is None: break
    
    image.close()
    
    return axcen, aycen, arad
Example #3
0
def make_median(fnlist,outfile):
    """Creates a median image from a series of images.
    
    Inputs:
    fnlist -> List of strings pointing to fits images
    outfile -> Location of resultant median image
    
    """
    
    imagelist = []
    datalist = []
    for i in range(len(fnlist)):
        imagelist.append(openfits(fnlist[i]))
        datalist.append(imagelist[i][0].data)
    datalist = np.array(datalist)
    
    meddata = np.median(datalist,axis=0)
    
    medhdu = PrimaryHDU(meddata)
    medhdu.writeto(outfile,clobber=True)
    
    for i in range(len(fnlist)):
        imagelist[i].close()
    
    return
Example #4
0
def combine_flat(flatlist,outfilepath="flat.fits"):
    """Combines a series of flatfield exposures to create a single flatfield
    image. Uses only the information contained inside the RSS aperture, which
    is the only significant difference between this routine and the familiar
    flat combination routines common to CCD astronomy.
    
    Inputs:
    flatlist -> A list of strings, each the path to a flatfield exposure
    outfilepath -> A string, the path of the output file (default 'flat.fits')
    
    """
    
    imagelist = []
    for i in range(len(flatlist)):
        imagelist.append(openfits(flatlist[i]))
        xgrid, ygrid = np.meshgrid(np.arange(imagelist[i][0].data.shape[1]),np.arange(imagelist[i][0].data.shape[0]))
        imagelist[i][0].data=imagelist[i][0].data/np.median(imagelist[i][0].data[np.power((xgrid-imagelist[i][0].header["fpaxcen"]),2)+np.power((ygrid-imagelist[i][0].header["fpaycen"]),2)<np.power(imagelist[i][0].header["fparad"],2)])
    imagestack = np.empty((len(flatlist),imagelist[0][0].data.shape[0],imagelist[0][0].data.shape[1]))
    for i in range(len(flatlist)):
        imagestack[i,:,:] = imagelist[i][0].data
    flatarray = np.median(imagestack,axis=0)
    flatarray[flatarray<0.000001]=0
    writefits(outfilepath,flatarray,clobber=True)
    
    #Close images
    for i in range(len(flatlist)): imagelist[i].close()
    
    return
Example #5
0
def fit_sky_level(fnlist):
    """Fits for the average sky background and sky background noise in a series
    of images.
    
    Inputs:
    fnlist -> List of strings, each containing the path to a fits image.
    
    Outputs:
    skyavg -> Array containing the average sky level in each image
    skysig -> Array containing the st. dev. of the sky level in each image
    
    """
    
    skyavg = np.empty(len(fnlist))
    skysig = np.empty(len(fnlist))
    for i in range(len(fnlist)):
        image = openfits(fnlist[i])
        xgrid, ygrid = np.meshgrid(np.arange(image[0].data.shape[1]),np.arange(image[0].data.shape[0]))
        data = image[0].data[np.power((xgrid-image[0].header["fpaxcen"]),2)+np.power((ygrid-image[0].header["fpaycen"]),2)<np.power(image[0].header["fparad"],2)]
        data = data[np.logical_and(data<np.percentile(data,90),data>np.percentile(data,10))]
        skyavg[i] = np.average(data)
        skysig[i] = np.std(data)
        image.close()
    
    return skyavg, skysig
Example #6
0
def flatten(fnlist,flatfile):
    """Flattens a series of images by dividing them by a flatfield image.
    This differs from traditional flatfielding only in that in maintains the
    RSS aperture mask.
    
    Inputs:
    fnlist -> A list of strings, each the path to an image to flatten
    flatfile -> The location of a master flat image
    
    """
    
    flatimage = openfits(flatfile)
    for i in range(len(fnlist)):
        print "Flattening image "+str(i+1)+" of "+str(len(fnlist))+": "+fnlist[i]
        image = openfits(fnlist[i],mode="update")
        image[0].header["fpflat"] = "True"
        xgrid, ygrid = np.meshgrid(np.arange(image[0].data.shape[1]),np.arange(image[0].data.shape[0]))
        image[0].data[np.power((xgrid-image[0].header["fpaxcen"]),2)+np.power((ygrid-image[0].header["fpaycen"]),2)<np.power(image[0].header["fparad"],2)] *= 1/flatimage[0].data[np.power((xgrid-image[0].header["fpaxcen"]),2)+np.power((ygrid-image[0].header["fpaycen"]),2)<np.power(image[0].header["fparad"],2)]
        image[0].data[np.isnan(image[0].data)] = 0
        image[0].data[np.isinf(image[0].data)] = 0
        image.close()
    flatimage.close()
def solar_velocity_shift(waveimagelist, restwave):
    """Opens the wavelength images of a data cube and shifts the wavelengths
    so they are shifted to the solar frame. This is accomplished using the
    IRAF task 'rvcorrect'.
    
    It does it in a really really stupid way, which is to temporarily redirect
    IRAF's output to a string, because IRAF doesn't actually save the output
    anywhere (that I know of), it only prints it to the terminal. Then this
    routine searches through that string for the necessary value. It's a bit
    backwards, but it works!
    
    Note that the boost here is a Lorentz boost, not a Galilean boost, though
    values for either will be similar (i.e. the relativistic formula is used)
    
    Inputs:
    waveimagelist -> List of strings, the paths to *wavelength* images to be
                     updated.
    restwave -> The rest wavelength of the line you're interested in.
    
    """
    
    c = 299792.458 #in km/s
    
    for i in range(len(waveimagelist)):
        sys.stdout = iraf_output = StringIO()
        image = openfits(waveimagelist[i],mode="update")
        iraf.rvcorrect(header="N",
                       input="N",
                       imupdate="N",
                       observatory="SAAO",
                       year=float(image[0].header["date-obs"].split("-")[0]),
                       month=float(image[0].header["date-obs"].split("-")[1]),
                       day=float(image[0].header["date-obs"].split("-")[2]),
                       ut=image[0].header["utc-obs"],
                       ra=image[0].header["ra"],
                       dec=image[0].header["dec"],
                       vobs=0,
                       mode="a")
        image[0].header["fpsolar"] = float(iraf_output.getvalue().split()[-6])
        iraf_output.close()
        beta_earth = ((image[0].data/restwave)**2-1) / ((image[0].data/restwave)**2+1)
        beta_shift = image[0].header["fpsolar"]/c
        beta_helio = (beta_earth+beta_shift)/(1+beta_earth*beta_shift)
        image[0].data = restwave*(1+beta_helio)/np.sqrt(1-beta_helio**2)
        image[0].data[np.isnan(image[0].data)] = 0
        image.close()
        
    sys.stdout = sys.__stdout__
    
    
Example #8
0
def cosmic_ray_remove(fnlist):
    """Runs the LACosmicx routine on a series of images in order to correct
    cosmic rays. Creates the header keyword 'fpcosmic' and sets its value to
    'True'
    
    Inputs:
    fnlist -> A list containing the paths to fits images.
    
    """
    
    for i in range(len(fnlist)):
        print "Cosmic-ray correcting image "+str(i+1)+" of "+str(len(fnlist))+": "+fnlist[i]
        image = openfits(fnlist[i],mode="update")
        image[0].header["fpcosmic"] = "True"
        _mask, image[0].data = lacosmicx(image[0].data,verbose=False,cleantype='idw')
        image[0].data[np.isnan(image[0].data)]=0
        image.close()
    
    return
Example #9
0
def aperture_mask(fnlist, axcen, aycen, arad, maskvalue = 0):
    """Opens a series of images and masks pixels outside a circular aperture.
    The images are overwritten with the new masked image. The values of axcen,
    aycen, and arad are added to the FITS headers as keywords 'fpaxcen',
    'fpaycen', and 'fparad' respectively.
    
    Inputs:
    fnlist -> A list containing strings, each the path to a fits image.
    axcen -> The aperture x center
    aycen -> The aperture y center
    arad -> The aperture radius
    maskvalue(optional) -> The value masked pixels will have
    
    """
    
    for i in range(len(fnlist)):
        print "Masking pixels outside aperture for image "+str(i+1)+" of "+str(len(fnlist))+": "+fnlist[i]
        image = openfits(fnlist[i],mode="update")
        xgrid, ygrid = np.meshgrid(np.arange(image[0].data.shape[1]),np.arange(image[0].data.shape[0]))
        image[0].data[np.power((xgrid-axcen),2) + np.power((ygrid-aycen),2) > np.power(arad,2)] = maskvalue
        image[0].header["fpaxcen"] = axcen
        image[0].header["fpaycen"] = aycen
        image[0].header["fparad"] = arad
        image.close()
Example #10
0
def align_norm(fnlist,uncertlist=None):
    """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.
    uncertlist (optional) -> List of paths to uncertainty images.
    
    """
    
    #Fit for the sky background level
    _skyavg, skysig = fit_sky_level(fnlist)
    
    #Get image FWHMs
    fwhm = np.empty(len(fnlist))
    firstimage = openfits(fnlist[0])
    toggle = firstimage[0].header.get("fpfwhm")
    axcen = firstimage[0].header.get("fpaxcen")
    aycen = firstimage[0].header.get("fpaycen")
    arad = firstimage[0].header.get("fparad")
    firstimage.close()
    if axcen == None:
        print "Error! Images have not yet been aperture-masked! Do this first!"
        crash()
    if toggle == None:
        print "Warning: FWHMs have not been measured! Assuming 5 pixel FWHM for all images."
        for i in range(len(fnlist)): fwhm[i] = 5
    else:
        for i in range(len(fnlist)):
            image = openfits(fnlist[i])
            fwhm[i] = image[0].header["fpfwhm"]
            image.close()
    
    #Identify objects in the fields
    coolist = identify_objects(fnlist,skysig,fwhm)
    
    #Match objects between fields
    coofile = match_objects(coolist)
    
    #Do aperture photometry on the matched objects
    photlist = do_phot(fnlist,coofile,fwhm,skysig)
    
    #Read the photometry files
    x, y, mag, dmag = read_phot(photlist)
    
    #Calculate the normalizations
    norm, dnorm = calc_norm(mag,dmag)
    
    #Normalize the images (and optionally, the uncertainty images)
    for i in range(len(fnlist)):
        print "Normalizing image "+fnlist[i]
        image = openfits(fnlist[i],mode="update")
        if not (uncertlist is None):
            uncimage = openfits(uncertlist[i],mode="update")
            uncimage[0].data = np.sqrt(norm[i]**2*uncimage[0].data**2 + dnorm[i]**2*image[0].data**2)
            uncimage.close()
        image[0].data *= norm[i]
        image.close()
    
    #Calculate the shifts
    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)
    
    #Shift the images (and optionally, the uncertainty images)
    iraf.images(_doprint=0)
    iraf.immatch(_doprint=0)
    for i in range(len(fnlist)):
        print "Shifting image "+fnlist[i]
        iraf.geotran(input=fnlist[i],
                     output=fnlist[i],
                     geometry="linear",
                     xshift=xshifts[i],
                     yshift=yshifts[i],
                     database="",
                     verbose="no")
        if not (uncertlist is None):
            iraf.geotran(input=uncertlist[i],
                         output=uncertlist[i],
                         geometry="linear",
                         xshift=xshifts[i],
                         yshift=yshifts[i],
                         database="",
                         verbose="no")
    
    #Update the image headers
    for i in range(len(fnlist)):
        image = openfits(fnlist[i],mode="update")
        image[0].header["fpphot"]="True"
        image[0].header["fpxcen"]+=xshifts[i]
        image[0].header["fpycen"]+=yshifts[i]
        image[0].header["fpaxcen"]+=xshifts[i]
        image[0].header["fpaycen"]+=yshifts[i]
        image.close()
    
    #Clean up the coordinate file list
    clean_files(fnlist)
    remove(coofile)
    for i in range(len(photlist)):
        remove(photlist[i])
    
    return
Example #11
0
def make_clean_map(cubedir, clobber=False):
    """Applies a series of user-specified masks to a velocity map file. The
    current masks are:

    - Minimum S/N cutoff
    - Maximum velocity cutoff
    - Minimum velocity cutoff
    - Maximum chi^2 cutoff
    - Maximum dsigma/sigma cutoff
    - Maximum dinty/inty cutoff

    The values of these cutoffs are stored in the image headers of the output
    image.
    """

    # Open images
    velimage = openfits(join(cubedir, "velocity.fits"))
    intyimage = openfits(join(cubedir, "intensity.fits"))
    dintyimage = openfits(join(cubedir, "dintensity.fits"))
    dcontimage = openfits(join(cubedir, "dcontinuum.fits"))
    sigmaimage = openfits(join(cubedir, "sigma.fits"))
    dsigmaimage = openfits(join(cubedir, "dsigma.fits"))
    chi2image = openfits(join(cubedir, "chi2.fits"))

    # Derived arrays
    normalized_dsig = dsigmaimage[0].data/sigmaimage[0].data
    normalized_dinty = dintyimage[0].data/intyimage[0].data
    snarray = intyimage[0].data / dcontimage[0].data

    # User input for cuts to make
    sncut = float(raw_input("Enter minimum signal-to-noise: "))
    minvel = float(raw_input("Enter minimum velocity: "))
    maxvel = float(raw_input("Enter maximum velocity: "))
    maxchi2 = float(raw_input("Enter maximum chi^2 (or 0 for no cut): "))
    maxsigmarat = float(raw_input("Enter maximum dsigma/sigma ratio " +
                                  "(or 0 for no cut): "))
    maxintyrat = float(raw_input("Enter maximum dinty/inty ratio " +
                                 "(or 0 for no cut): "))

    # Mask for the cuts
    mask = snarray > sncut
    mask = np.logical_and(mask, velimage[0].data > minvel)
    mask = np.logical_and(mask, velimage[0].data < maxvel)
    mask = np.logical_and(mask, np.logical_not(np.isinf(snarray)))
    if maxchi2 != 0:
        mask = np.logical_and(mask, chi2image[0].data < maxchi2)
    if maxsigmarat != 0:
        mask = np.logical_and(mask, normalized_dsig < maxsigmarat)
    if maxintyrat != 0:
        mask = np.logical_and(mask, normalized_dinty < maxintyrat)

    # Sum of mask pixels
    print repr(np.sum(mask))+" pixels kept."

    # Mask the velocity map
    velimage[0].data[np.logical_not(mask)] = np.nan
    velimage[0].header["CUTSN"] = sncut
    velimage[0].header["CUTVEL1"] = minvel
    velimage[0].header["CUTVEL2"] = maxvel
    velimage[0].header["CUTCHI2"] = maxchi2
    velimage[0].header["CUTDSIG"] = maxsigmarat
    velimage[0].header["CUTDINTY"] = maxintyrat
    velimage.writeto(join(cubedir, "cleanvelmap.fits"), clobber=clobber)

    # Close images
    velimage.close()
    intyimage.close()
    dintyimage.close()
    dcontimage.close()
    sigmaimage.close()
    dsigmaimage.close()
    chi2image.close()

    return
Example #12
0
def make_final_image(input_image, output_image, output_wave_image,
                     desired_fwhm,
                     input_uncert_image=None, output_uncert_image=None,
                     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.
    output_wave_image -> Path to the output wavelength image.
    desired_fwhm -> Desired FWHM for the resultant image to have.
    
    Optional Inputs:
    input_uncert_image -> Path to the input uncertainty image, if it exists.
    output_uncert_image -> Path to the output uncertainty image, if it exists.
    clobber -> Overwrite output images if they already exist. Default is False.
    
    """
    
    print "Making final data cube images for image "+input_image
    
    #Measure the sky background level in the input image
    skyavg, skysig = fit_sky_level([input_image])
    
    #Open the input image and get various header keywords, crash if necessary
    intyimage = openfits(input_image)
    intygrid = intyimage[0].data
    fwhm = intyimage[0].header.get("fpfwhm")
    wave0 = intyimage[0].header.get("fpwave0")
    calf = intyimage[0].header.get("fpcalf")
    xcen = intyimage[0].header.get("fpxcen")
    ycen = intyimage[0].header.get("fpycen")
    if fwhm == None: crash("Error! FWHM not measured for image "+input_image+".")
    if wave0 == None or calf == None: crash("Error! Wavelength solution does "+
                                            "not exist for image "+input_image+".")
    if xcen == None or ycen == None: crash("Error! Center values not measured "+
                                           "image "+input_image+".")
    if fwhm>desired_fwhm: crash("Error! Desired FWHM too low for image "+
                                input_image+".")
    
    #Subtract the sky background from the image
    intygrid[intygrid!=0] -= skyavg[0]
    
    #Calculate the necessary FWHM for convolution and make the gaussian kernel
    fwhm_conv = np.sqrt(desired_fwhm**2-fwhm**2)
    sig = fwhm_conv/2.3548+0.0001
    ksize = np.ceil(4*sig) #Generate the kernel to 4-sigma
    kxgrid, kygrid = np.meshgrid(np.linspace(-ksize,ksize,2*ksize+1),np.linspace(-ksize,ksize,2*ksize+1))
    kern = np.exp(-(kxgrid**2+kygrid**2)/(2*sig**2)) #Gaussian (unnormalized because sig can be 0)
    kern = kern/np.sum(kern) #Normalize the kernel
    
    #Open and convolve the uncertainty image if one exists. Save the output.
    if input_uncert_image != None:
        uncertimage = openfits(input_uncert_image)
        uncertgrid = uncertimage[0].data
        #Add the sky background uncertainty to the uncertainty grid
        uncertgrid[intygrid!=0] = np.sqrt(uncertgrid[intygrid!=0]**2+skysig[0]**2)
        #Convolve the uncertainties appropriately
        new_uncert_grid = convolve_uncert(uncertgrid, intygrid, kern)
        #Write to output file
        writefits(output_uncert_image,new_uncert_grid,header=uncertimage[0].header,clobber=clobber)
        uncertimage.close()
        
    #Create and convolve the wavelength image. Save the output.
    xgrid, ygrid = np.meshgrid(np.arange(intyimage[0].data.shape[1]),
                               np.arange(intyimage[0].data.shape[0]))
    r2grid = (xgrid-xcen)**2 + (ygrid-ycen)**2
    wavegrid = wave0 / np.sqrt(1+r2grid/calf**2)
    newwavegrid = convolve_wave(wavegrid, intygrid, kern)
    writefits(output_wave_image,newwavegrid,header=intyimage[0].header,clobber=clobber)
    
    #Convolve the intensity image. Save the output
    newintygrid = convolve_inty(intygrid, kern)
    intyimage[0].header["fpfwhm"] = desired_fwhm #Update header FWHM keyword
    writefits(output_image,newintygrid,header=intyimage[0].header,clobber=clobber)
    
    #Close images
    intyimage.close()
    
    return
Example #13
0
def fit_velmap_ha_n2_mode(wavelist, intylist, outdir, uncertlist=None, clobber=False):
    """
    """
    #Create blank lists for filling with images
    wavefilelist = []
    intyfilelist = []
    if not (uncertlist is None):
        uncertfilelist = []
    #Open images and populate the lists
    for i in range(len(wavelist)):
        wavefilelist.append(openfits(wavelist[i]))
        intyfilelist.append(openfits(intylist[i]))
        if not (uncertlist is None):
            uncertfilelist.append(openfits(uncertlist[i]))
    #Create blank arrays for filling with data
    wavecube = np.zeros((intyfilelist[0][0].data.shape[0],intyfilelist[0][0].data.shape[1],len(wavelist)))
    intycube = np.zeros((intyfilelist[0][0].data.shape[0],intyfilelist[0][0].data.shape[1],len(wavelist)))
    if not (uncertlist is None):
        uncertcube = np.zeros((intyfilelist[0][0].data.shape[0],intyfilelist[0][0].data.shape[1],len(wavelist)))
    #Fill the cube arrays with data
    for i in range(len(wavelist)):
        wavecube[:,:,i] = wavefilelist[i][0].data
        intycube[:,:,i] = intyfilelist[i][0].data
        if not (uncertlist is None):
            uncertcube[:,:,i] = uncertfilelist[i][0].data
    #Create blank arrays for the velocity+etc fits
    contarray = np.zeros_like(wavefilelist[0][0].data)
    intyarray = np.zeros_like(wavefilelist[0][0].data)
    intyratioarray = np.zeros_like(wavefilelist[0][0].data)
    wavearray = np.zeros_like(wavefilelist[0][0].data)
    two_line_success_array = np.zeros_like(wavefilelist[0][0].data,dtype="bool")
    one_line_success_array = np.zeros_like(wavefilelist[0][0].data,dtype="bool")
    #Create x and y arrays for the "progress bar"
    numb_updates=20 #Number of progress updates
    xgrid, ygrid = np.meshgrid(np.arange(contarray.shape[1]),np.arange(contarray.shape[0]))
    xgrid = xgrid.flatten()
    ygrid = ygrid.flatten()
    progX = []
    progY = []
    progPerc = []
    for i in range(1,numb_updates+1):
        progX.append(xgrid[np.int(len(xgrid)*i/numb_updates)-1])
        progY.append(ygrid[np.int(len(xgrid)*i/numb_updates)-1])
        progPerc.append(i*100/numb_updates)
    progX = np.array(progX)
    progY = np.array(progY)
    progPerc = np.array(progPerc)
    #Loop over pixels
    for y in range(wavearray.shape[0]):
        for x in range(wavearray.shape[1]):
            #Display the "progress bar" if appropriate
            if np.any(np.logical_and(x==progX,y==progY)): print "Velocity map approximately "+repr(progPerc[np.logical_and(progX==x,progY==y)][0])+"% complete..."
            #Get the spectrum to fit
            waves = wavecube[y,x,:]
            intys = intycube[y,x,:]
            if not (uncertlist is None): uncerts = uncertcube[y,x,:]
            else: uncerts = None
            
#             #Attempt the two-lined fit (halpha and NII)
#             if np.sum(waves!=0)>0.5*len(waves): fit, success = fit_ha_and_n2(waves[waves!=0], intys[intys!=0], uncerts[uncerts!=0])
#             else: fit, success = np.zeros(8), False
#             #Was the fit successful
#             if success:
#                 contarray[y,x] = fit[0]
#                 intyarray[y,x] = fit[1]
#                 intyratioarray[y,x] = fit[2]/fit[1]
#                 wavearray[y,x] = fit[3]
#                 two_line_success_array[y,x] = True
#             #If not, let's try only fitting the halpha line
#             else:
#                 if np.sum(waves!=0)>0.5*len(waves): fit, success = fit_only_ha(waves[waves!=0], intys[intys!=0], uncerts[uncerts!=0])
#                 else: fit, success = np.zeros(5), False
#                 if success:
#                     contarray[y,x] = fit[0]
#                     intyarray[y,x] = fit[1]
#                     wavearray[y,x] = fit[2]
#                     one_line_success_array[y,x] = True
            #Fit with voigtfit
            
            if np.sum(waves!=0)>0.5*len(waves):
                fit,_err,_chi2 = voigtfit.voigtfit(waves[waves!=0],intys[intys!=0],uncerts[uncerts!=0],voigtfit.voigtguess(waves[waves!=0],intys[intys!=0]),[True,True,True,True,True])
                contarray[y,x] = fit[0]
                intyarray[y,x] = fit[1]
                wavearray[y,x] = fit[2]
    #Convert wavelengths to velocities
    velarray = np.zeros_like(wavearray)
    velarray[wavearray!=0] = 299792.458*((wavearray[wavearray!=0]/6562.81)**2-1)/((wavearray[wavearray!=0]/6562.81)**2+1)
    #Save the output images
    writefits(join(outdir,"velocity.fits"), velarray, clobber=clobber)
    writefits(join(outdir,"intensity.fits"), intyarray, clobber=clobber)
    writefits(join(outdir,"intyratio.fits"), intyratioarray, clobber=clobber)
    writefits(join(outdir,"continuum.fits"), contarray, clobber=clobber)
    #writefits(join(outdir,"2linesuccess.fits"), two_line_success_array, clobber=clobber)
    #writefits(join(outdir,"1linesuccess.fits"), one_line_success_array, clobber=clobber)
    #Close input images
    for i in range(len(wavelist)):
        wavefilelist[i].close()
        intyfilelist[i].close()
        if not (uncertlist is None):
            uncertfilelist[i].close()
    
    return
Example #14
0
        if solnplot.key == "t": dotime = not dotime
        #Add time break
        if solnplot.key == "w":
            timeplot = TimePlot(tplot,resid,colorplot,time_dividers)
            if timeplot.xcoo != None: time_dividers.append(timeplot.xcoo)
        #Remove time breaks
        if solnplot.key == "q":
            time_dividers = []

    #Close all images
    for i in range(len(fnlist)):
        imagelist[i].close()
        
    #For each image, write the central wavelength and F to the image header
    for i in range(len(fnlist)):
        image = openfits(fnlist[i],mode="update")
        image_t = (image[0].header["JD"]-t0)*24*60
        #Figure out which time division it's in
        div_index = np.where(np.array(time_dividers)>image_t)[0]
        if len(div_index>0): div_index = div_index[0]
        else: div_index = len(time_dividers)
        image_fit = fit[div_index]
        if dotime:
            image_wave0 = image_fit[0]+image_fit[1]*image[0].header["ET1Z"]+image_fit[2]*image_t
            image_F = image_fit[3]
        else:
            image_wave0 = image_fit[0]+image_fit[1]*image[0].header["ET1Z"]
            image_F = image_fit[2]
        image[0].header["FPWAVE0"] = image_wave0
        image[0].header["FPCALF"] = image_F
        image.close()
Example #15
0
def pipeline(productdir = "product", mode = "halpha"):
    """Runs successive steps of the saltfp data reduction, checking along the
    way to see if each step was successful. This is the main driver program of
    the SALT Fabry-Perot pipeline.
    
    Inputs:
    productdir -> String, containing the path to the 'product' directory. By
                  default, this is 'product'
    mode -> Mode for velocity fitting. Currently the only option is H-Alpha
                line fitting.
                  
    """
    
    #Print the welcome message
    welcome_message()
    
    #Set rest wave based on the mode called
    if mode == "halpha": rest_wave = 6562.81
    
    #Acquire the list of filenames from the product directory
    fnlist = sorted(listdir(productdir))
    
    #Convert the images to single-extension fits files
    # This creates a new directory from scratch in order to preserve the
    # original 'product' directory.
    print "Converting images to single extension fits images..."
    productlist = []
    singextlist = []
    for i in range(len(fnlist)):
        if splitext(fnlist[i])[1] == ".fits":
            productlist.append(join(productdir,fnlist[i]))
            singextlist.append(join("singext","s"+fnlist[i]))
    toggle = create_directory("singext")
    if toggle:
        make_single_extension(productlist,singextlist)
    else: print "Skipping creation of single-extension fits images."
    
    #Manual verification of fits images and headers 
    firstimage = openfits(singextlist[0])
    verify = firstimage[0].header.get("fpverify")
    firstimage.close()
    if verify is None:
        while True:
            yn = raw_input("Manually verify image contents? (Recommended) (y/n) ")
            if "n" in yn or "N" in yn:
                print "Skipping manual verification of image contents (Not recommended)"
                break
            if "y" in yn or "Y" in yn:
                singextlist = verify_images(singextlist)
                break
    
    #Make separate lists of the different fits files
    flatlist, list_of_objs, objlists, list_of_filts, filtlists = separate_lists(singextlist)
    
    #Masking of pixels outside the aperture
    firstimage = openfits(objlists[0][0])
    axcen = firstimage[0].header.get("fpaxcen")
    firstimage.close()
    if axcen is None:
        print "Masking pixels outside the RSS aperture..."
        axcen, aycen, arad = get_aperture(objlists[0][0])
        aperture_mask(singextlist,axcen,aycen,arad,maskvalue=0)
    else: print "Images have already been aperture-masked."
    
    #Cosmic ray removal
    firstimage = openfits(objlists[0][0])
    crtoggle = firstimage[0].header.get("fpcosmic")
    firstimage.close()
    if crtoggle is None:
        print "Cosmic-ray correcting images..."
        cosmic_ray_remove(singextlist)
    else: print "Images have already been cosmic-ray corrected."
    
    #Create uncertainty images for the object files
    uncertlists = []
    for i in range(len(objlists)):
        uncertlists.append([])
        for j in range(len(objlists[i])):
            uncertlists[i].append(splitext(objlists[i][j])[0]+'_unc'+splitext(objlists[i][j])[1])
    if not isfile(uncertlists[0][0]):
        print "Generating uncertainty images..."
        for i in range(len(objlists)):
            for j in range(len(objlists[i])):
                print "Writing uncertainty image "+uncertlists[i][j]
                image = openfits(objlists[i][j])
                image[0].data = np.sqrt(image[0].data)
                image.writeto(uncertlists[i][j])
                image.close()
    else: print "Uncertainty images already exist."
    
    #Image Flattening
    firstimage = openfits(objlists[0][0])
    flattog = firstimage[0].header.get("fpflat")
    firstimage.close()
    if flattog is None:
        print "Flattening images..."
        if len(flatlist) == 0:
            while True:
                print "Uh oh! No flatfield exposure found!"
                flatpath = raw_input("Enter path to external flat image: (leave blank to skip flattening) ")
                if flatpath == "" or isfile(flatpath): break
        else:
            combine_flat(flatlist,"flat.fits")
            flatpath = "flat.fits"
        if flatpath != "":
            notflatlist = []
            for filtlist in filtlists: notflatlist+=filtlist
            for uncertlist in uncertlists: notflatlist+=uncertlist
            flatten(notflatlist,flatpath)
        else: print "Skipping image flattening. (Not recommended!)"
    else: print "Images have already been flattened."
    
    #Measure stellar FWHMs
    firstimage = openfits(objlists[0][0])
    fwhm = firstimage[0].header.get("fpfwhm")
    firstimage.close()
    if fwhm is None: dofwhm = True
    else:
        while True:
            yn = raw_input("Seeing FWHM has already been measured. Redo this? (y/n) ")
            if "n" in yn or "N" in yn:
                dofwhm = False
                break
            elif "y" in yn or "Y" in yn:
                dofwhm = True
                break
    if dofwhm:
        print "Measuring seeing FWHMs..."
        for objlist in objlists:
            measure_fwhm(objlist)
    
    #Find image centers using ghost pairs
    for i in range(len(objlists)):
        firstimage = openfits(objlists[i][0])
        xcen = firstimage[0].header.get("fpxcen")
        firstimage.close()
        if xcen is None:
            ghosttog = True
        else:
            while True:
                yn = raw_input("Optical centers already measured for object "+list_of_objs[i]+". Redo this? (y/n) ")
                if "n" in yn or "N" in yn:
                    ghosttog = False
                    break
                elif "y" in yn or "Y" in yn:
                    ghosttog = True
                    break
        if ghosttog:
            print "Identifying optical centers for object "+list_of_objs[i]+". This may take a while for crowded fields..."
            find_ghost_centers(objlists[i])
    
    #Deghost images
    firstimage = openfits(objlists[0][0])
    deghosted = firstimage[0].header.get("fpghost")
    firstimage.close()
    if deghosted is None:
        print "Deghosting images..."
        for i in range(len(objlists)):
            for j in range(len(objlists[i])):
                deghost(objlists[i][j],uncfn=uncertlists[i][j],g=0.04)
    else: print "Images have already been deghosted."
    
    #Make separate directories for each object.
    # This is the first bit since 'singext' to create a new directory, because
    # this is the first point where it's really necessary to start treating the
    # images from different tracks very differently.
    for i in range(len(objlists)):
        if isdir(list_of_objs[i].replace(" ", "")):
            while True:
                yn = raw_input("A directory for object "+list_of_objs[i]+" already exists. Recreate? (y/n) ")
                if "n" in yn or "N" in yn:
                    do_copy = False
                    break
                elif "y" in yn or "Y" in yn:
                    do_copy = True
                    rmtree(list_of_objs[i].replace(" ", ""))
                    break
        else: do_copy = True
        if do_copy:
            mkdir(list_of_objs[i].replace(" ", ""))
            for j in range(len(objlists[i])):
                copyfile(objlists[i][j],join(list_of_objs[i].replace(" ", ""),split(objlists[i][j])[1]))
                copyfile(uncertlists[i][j],join(list_of_objs[i].replace(" ", ""),split(uncertlists[i][j])[1]))
        for j in range(len(objlists[i])):
            objlists[i][j] = join(list_of_objs[i].replace(" ", ""),split(objlists[i][j])[1])
            uncertlists[i][j] = join(list_of_objs[i].replace(" ", ""),split(uncertlists[i][j])[1])
    #Update the filter lists (THIS IS TERRIBLE AND I SHOULD HAVE USED POINTERS AND I'M SO SORRY)
    for i in range(len(filtlists)):
        for j in range(len(filtlists[i])):
            for k in range(len(objlists)):
                for l in range(len(objlists[k])):
                    if split(filtlists[i][j])[1]==split(objlists[k][l])[1]: filtlists[i][j] = objlists[k][l]
    
    #Image alignment and normalization
    for i in range(len(objlists)):
        firstimage = openfits(objlists[i][0])
        aligned = firstimage[0].header.get("fpphot")
        firstimage.close()
        if aligned is None:
            print "Aligning and normalizing images for object "+list_of_objs[i]+"..."
            align_norm(objlists[i],uncertlist=uncertlists[i])
        else: print "Images for object "+list_of_objs[i]+" have already been aligned and normalized."
    
    #Make a median image for each object
    for i in range(len(objlists)):
        if isfile(join(list_of_objs[i].replace(" ", ""),"median.fits")):
            while True:
                yn = raw_input("Median image for object "+list_of_objs[i]+" already exists. Replace it? (y/n) ")
                if "n" in yn or "N" in yn:
                    break
                elif "y" in yn or "Y" in yn:
                    make_median(objlists[i],join(list_of_objs[i].replace(" ", ""),"median.fits"))
                    break
        else: make_median(objlists[i],join(list_of_objs[i].replace(" ", ""),"median.fits"))
    
    #Wavelength calibrations
    for i in range(len(list_of_filts)):
        #Do a separate calibration for each filter
        firstimage = openfits(filtlists[i][0])
        calf = firstimage[0].header.get("fpcalf")
        firstimage.close()
        if not(calf is None):
            while True:
                yn = raw_input("Wavelength solution already found for filter "+list_of_filts[i]+". Redo it? (y/n) ")
                if "n" in yn or "N" in yn:
                    break
                elif "y" in yn or "Y" in yn:
                    fit_wave_soln(filtlists[i])
                    break
        else: fit_wave_soln(filtlists[i])
    
    #Sky ring removal
    for i in range(len(objlists)):
        #Check to see if sky rings have already been removed
        firstimage = openfits(objlists[i][0])
        deringed = firstimage[0].header.get("fpdering")
        firstimage.close()
        if deringed is None:
            print "Subtracting sky rings for object "+list_of_objs[i]+"..."
            sub_sky_rings(objlists[i],[join(list_of_objs[i].replace(" ", ""),"median.fits")]*len(objlists[i]))
        else: print "Sky ring subtraction already done for object "+list_of_objs[i]
    
    #Creation of data cube and convolution to uniform PSF
    # Data cube is created in a separate directory, which is created if it
    # does not already exist, or overwritten if the user so chooses
    for i in range(len(objlists)):
        if isdir(list_of_objs[i].replace(" ", "")+"_cube"):
            while True:
                yn = raw_input("A data cube for object "+list_of_objs[i]+" already exists. Recreate? (y/n) ")
                if "n" in yn or "N" in yn:
                    do_create = False
                    break
                elif "y" in yn or "Y" in yn:
                    do_create = True
                    rmtree(list_of_objs[i].replace(" ", "")+"_cube")
                    break
        else: do_create = True
        if do_create:
            mkdir(list_of_objs[i].replace(" ", "")+"_cube")
            for j in range(len(objlists[i])):
                image = openfits(objlists[i][j])
                fwhm = image[0].header["fpfwhm"]
                if j==0: largestfwhm = fwhm
                if fwhm>largestfwhm: largestfwhm=fwhm
                image.close()
            while True:
                user_fwhm = raw_input("Enter desired final fwhm or leave blank to use default ("+str(largestfwhm)+" pix) ")
                if user_fwhm == "":
                    user_fwhm = largestfwhm
                    break
                else:
                    try: user_fwhm = float(user_fwhm)
                    except ValueError: print "That wasn't a valid number..."
                    else:
                        if user_fwhm<largestfwhm:
                            print "Final fwhm must exceed "+str(largestfwhm)+" pixels."
                        else: break
            desired_fwhm = user_fwhm
            for j in range(len(objlists[i])):
                make_final_image(objlists[i][j], #Input image
                                 join(list_of_objs[i].replace(" ", "")+"_cube",splitext(split(objlists[i][j])[1])[0]+"_inty"+splitext(split(objlists[i][j])[1])[1]), #Output image
                                 join(list_of_objs[i].replace(" ", "")+"_cube",splitext(split(objlists[i][j])[1])[0]+"_wave"+splitext(split(objlists[i][j])[1])[1]), #Output wave image
                                 desired_fwhm, #Desired final FWHM
                                 input_uncert_image = uncertlists[i][j], #Input uncertainty image
                                 output_uncert_image = join(list_of_objs[i].replace(" ", "")+"_cube",split(uncertlists[i][j])[1]), #Output uncertainty image
                                 clobber=True)
Example #16
0
def sub_sky_rings(fnlist,medfilelist):
    """Fits for night sky rings in a series of images, then subtracts them off.
    If uncertainty images exist, updates them with better uncertainties.
    
    The rings are fitted in an interactive way by the user.
    
    After the ring subtraction, the header keyword "fpdering" is created and
    set to "True."
    
    Note: A 'median.fits' image must exist for the object or this routine will
    not work.
    
    Inputs:
    fnlist -> List of strings, each the path to a fits image.
    medfilelist -> List of paths to median images for each image to subtract off
                   (probably all the same, but generally could be different)
    
    """
    
    #This bit takes care of the 's' to save shortcut in matplotlib.
    oldsavekey = plt.rcParams["keymap.save"]
    plt.rcParams["keymap.save"] = ""
    
    #Open all of the images, median-subtract them, make wavelength arrays
    # make skywavelibs for each, make spectra for each, trim images
    imagelist = []
    wavearraylist = []
    skywavelibs = []
    skywavelibs_extended = []
    spectrum_wave = []
    spectrum_inty = []
    minwavelist = []
    maxwavelist = []
    xcenlist = []
    ycenlist = []
    Flist = []
    wave0list = []
    has_been_saved = np.zeros(len(fnlist),dtype="bool")
    for i in range(len(fnlist)):
        #Open image
        imagelist.append(openfits(fnlist[i]))
        medimage = openfits(medfilelist[i])
        #Get a bunch of relevant header values
        Flist.append(imagelist[i][0].header["fpcalf"])
        wave0list.append(imagelist[i][0].header["fpwave0"])
        xcen = imagelist[i][0].header["fpxcen"]
        ycen = imagelist[i][0].header["fpycen"]
        arad = imagelist[i][0].header["fparad"]
        axcen = imagelist[i][0].header["fpaxcen"]
        aycen = imagelist[i][0].header["fpaycen"]
        #Median subtract it
        imagelist[i][0].data += -medimage[0].data
        #Make wavelength array
        xgrid, ygrid = np.meshgrid(np.arange(imagelist[i][0].data.shape[1]),np.arange(imagelist[i][0].data.shape[0]))
        r2grid = (xgrid-xcen)**2+(ygrid-ycen)**2
        wavearraylist.append(wave0list[i]/np.sqrt(1+r2grid/Flist[i]**2))
        #Get the sky wave library
        minwavelist.append(np.min(wavearraylist[i][r2grid<arad**2]))
        maxwavelist.append(np.max(wavearraylist[i][r2grid<arad**2]))
        skywaves = get_libraries(imagelist[i][0].header["FILTER"])[1]
        skywavelibs.append(skywaves[np.logical_and(skywaves>minwavelist[i],skywaves<maxwavelist[i])])
        #Make an "extended" sky wave library
        skywavelibs_extended.append(skywaves[np.logical_or(np.logical_and(skywaves<minwavelist[i],skywaves>minwavelist[i]-5),np.logical_and(skywaves>maxwavelist[i],skywaves<maxwavelist[i]+5))])
        #Make the spectrum
        spectrum_wave.append(np.linspace(np.min(wavearraylist[i][r2grid<arad**2]),np.max(wavearraylist[i][r2grid<arad**2]),np.int(((np.max(wavearraylist[i][r2grid<arad**2])-np.min(wavearraylist[i][r2grid<arad**2]))/0.25))))
        spectrum_inty.append(np.zeros_like(spectrum_wave[i][1:]))
        for j in range(len(spectrum_wave[i])-1):
            spectrum_inty[i][j] = np.median(imagelist[i][0].data[np.logical_and(np.logical_and(imagelist[i][0].data!=0,wavearraylist[i]>spectrum_wave[i][j]),wavearraylist[i]<spectrum_wave[i][j+1])])
        spectrum_wave[i] = 0.5*(spectrum_wave[i][:-1]+spectrum_wave[i][1:])
        #Trim the images and arrays to the aperture size
        imagelist[i][0].data = imagelist[i][0].data[aycen-arad:aycen+arad,axcen-arad:axcen+arad]
        wavearraylist[i] = wavearraylist[i][aycen-arad:aycen+arad,axcen-arad:axcen+arad]
        xcenlist.append(arad+xcen-axcen)
        ycenlist.append(arad+ycen-aycen)
        #Convert skywavelibs to lists
        skywavelibs[i] = list(skywavelibs[i])
        skywavelibs_extended[i] = list(skywavelibs_extended[i])
        #Close median image
        medimage.close()
    
    #Interactive plotting of ring profiles
    addedwaves = []
    final_fitted_waves = []
    final_fitted_intys = []
    final_fitted_sigs = []
    for i in range(len(fnlist)):
        addedwaves.append([])
        final_fitted_waves.append([])
        final_fitted_intys.append([])
        final_fitted_sigs.append([])
    i = 0
    while True:
        #Determine which wavelengths we want to fit
        waves_to_fit = skywavelibs[i]+addedwaves[i]
        
        #Generate a fitting function from those wavelengths
        func_to_fit = make_sum_gaussians(waves_to_fit)
        
        #Come up with reasonable guesses for the fitting function parameters
        contguess = [0]
        intyguesses = []
        sigguesses = []
        for j in range(len(waves_to_fit)):
            intyguesses.append(np.sum(spectrum_inty[i][np.logical_and(spectrum_wave[i]<waves_to_fit[j]+4,spectrum_wave[i]>waves_to_fit[j]-4)]*(spectrum_wave[i][1]-spectrum_wave[i][0])))
            sigguesses.append(2)
        guess = contguess+intyguesses+sigguesses
        
        #Fit the spectrum
        fitsuccess = True
        try:
            fit = curve_fit(func_to_fit,spectrum_wave[i],spectrum_inty[i],p0=guess)[0]
        except RuntimeError:
            print "Warning: Fit did not converge. Maybe remove some erroneous lines from the fit?"
            fit = guess
            fitsuccess = False
        fitcont = fit[0]
        fitintys = np.array(fit[1:len(waves_to_fit)+1])
        fitsigs = np.array(fit[len(waves_to_fit)+1:2*len(waves_to_fit)+1])
        
        #Make the subtracted plot array
        subarray = imagelist[i][0].data.copy()
        for j in range(len(waves_to_fit)): subarray += -fitintys[j]*nGauss(wavearraylist[i]-waves_to_fit[j],fitsigs[j])
        
        #Figure out which radius each wavelength is at
        radiilist = []
        for j in range(len(waves_to_fit)): radiilist.append(Flist[i]*np.sqrt((wave0list[i]/waves_to_fit[j])**2-1))
        
        #Create the subtracted spectrum
        sub_spec_inty = spectrum_inty[i] - fitcont
        for j in range(len(waves_to_fit)): sub_spec_inty += -fitintys[j]*nGauss(spectrum_wave[i]-waves_to_fit[j],fitsigs[j])
        
        #Create the spectrum fit plot
        fit_X = np.linspace(minwavelist[i],maxwavelist[i],500)
        fit_Y = np.ones_like(fit_X)*fitcont
        for j in range(len(waves_to_fit)): fit_Y += fitintys[j]*nGauss(fit_X-waves_to_fit[j], fitsigs[j])
        
        #Plot the image, spectrum, and fit
        profile_plot = SkyRingPlot(imagelist[i][0].data, subarray,
                                   xcenlist[i], ycenlist[i], radiilist,
                                   spectrum_wave[i], spectrum_inty[i], sub_spec_inty,
                                   fit_X, fit_Y,
                                   skywavelibs[i], addedwaves[i], skywavelibs_extended[i],
                                   repr(i+1)+"/"+repr(len(fnlist)), has_been_saved[i], fitsuccess)
        
        #Shifting images and breakout condition
        if profile_plot.key == "a": i+=-1
        if profile_plot.key == "d": i+=1
        quitloop = False
        if (i==-1 or i==len(fnlist) or profile_plot.key=="q"):
            if (np.sum(np.logical_not(has_been_saved))==0):
                while True:
                    yn = raw_input("Finished fitting ring profiles? (y/n) ")
                    if "n" in yn or "N" in yn:
                        break
                    if "y" in yn or "Y" in yn:
                        quitloop=True
                        break
            else:
                print "Error: Ring fits have not yet been saved for the following images:"
                print (np.arange(len(fnlist))+1)[np.logical_not(has_been_saved)]
        if quitloop: break
        if i == -1: i=0
        if i == len(fnlist): i=len(fnlist)-1
        
        #Delete ring option
        if profile_plot.key == "s":
            nearest_wave = None
            if profile_plot.axis in [1,3]:
                #The click was made in an image plot
                clicked_radius = np.sqrt((profile_plot.xcoo - xcenlist[i])**2 + (profile_plot.ycoo - ycenlist[i])**2)
                nearest_wave = waves_to_fit[np.argmin(np.abs((np.array(radiilist)-clicked_radius)))]
            elif profile_plot.axis in [2,4]:
                #The click was in a spectrum plot
                clicked_wave = profile_plot.xcoo
                nearest_wave = waves_to_fit[np.argmin(np.abs(np.array(waves_to_fit)-clicked_wave))]
            if nearest_wave != None:
                #Remove the nearest wavelength from the sky_wave_lib or added waves, add it to extra waves
                if nearest_wave in skywavelibs[i]:
                    skywavelibs[i].remove(nearest_wave)
                    skywavelibs_extended[i].append(nearest_wave)
                if nearest_wave in addedwaves[i]: addedwaves[i].remove(nearest_wave)
                
        #Add ring option
        if profile_plot.key == "w":
            clicked_wave = None
            if profile_plot.axis in [1,3]:
                #The click was made in an image plot
                clicked_radius = np.sqrt((profile_plot.xcoo - xcenlist[i])**2 + (profile_plot.ycoo - ycenlist[i])**2)
                #Convert radius to wavelength
                clicked_wave = wave0list[i] / (1+clicked_radius**2/Flist[i]**2)**0.5
            elif profile_plot.axis in [2,4]:
                #The click was in a spectrum plot
                clicked_wave = profile_plot.xcoo
            if clicked_wave != None:
                #Find the nearest wavelength in the extended list (if there are any)
                if len(np.abs(np.array(skywavelibs_extended[i])-clicked_wave))>0:
                    wave_to_add = skywavelibs_extended[i][np.argmin(np.abs(np.array(skywavelibs_extended[i])-clicked_wave))]
                    #Add that wave to skywavelibs, remove it from extended
                    skywavelibs[i].append(wave_to_add)
                    skywavelibs_extended[i].remove(wave_to_add)
        
        #Force add ring option
        if profile_plot.key == "e":
            clicked_wave = None
            if profile_plot.axis in [1,3]:
                #The click was made in an image plot
                clicked_radius = np.sqrt((profile_plot.xcoo - xcenlist[i])**2 + (profile_plot.ycoo - ycenlist[i])**2)
                #Convert radius to wavelength
                clicked_wave = wave0list[i] / (1+clicked_radius**2/Flist[i]**2)**0.5
            elif profile_plot.axis in [2,4]:
                #The click was in a spectrum plot
                clicked_wave = profile_plot.xcoo
            if clicked_wave != None:
                #Add this wavelength to the added array
                addedwaves[i].append(clicked_wave)
                
        #Save option
        if profile_plot.key == "r":
            final_fitted_waves[i] = waves_to_fit[:]
            final_fitted_intys[i] = fitintys[:]
            final_fitted_sigs[i] = fitsigs[:]
            has_been_saved[i] = True
        
    #Close all of the images
    medimage.close()
    for i in range(len(fnlist)):
        imagelist[i].close()
    
    #Subtract the ring profiles and update headers
    for i in range(len(fnlist)):
        image = openfits(fnlist[i], mode="update")
        arad = image[0].header["fparad"]
        axcen = image[0].header["fpaxcen"]
        aycen = image[0].header["fpaycen"]
        mask = image[0].data == 0 #For re-correcting chip gaps
        for j in range(len(final_fitted_waves[i])):
            image[0].data[aycen-arad:aycen+arad,axcen-arad:axcen+arad] += -final_fitted_intys[i][j]*nGauss(wavearraylist[i]-final_fitted_waves[i][j], final_fitted_sigs[i][j])
            image[0].header["fpdering"] = "True"
        image[0].data[mask]=0 #For re-correcting chip gaps
        image.close()
    
    #Restore the old keyword shortcut
    plt.rcParams["keymap.save"] = oldsavekey
    
    return
Example #17
0
def fit_wave_soln(fnlist):
    """Fits a wavelength solution to rings in a set of images. Appends this
    wavelength solution to the image headers as the keywords:
    'fpcala', 'fpcalb', ... 'fpcalf'
    
    Each object in fnlist must have a corresponding "median.fits" in its
    image directory, or this routine will not work.
    
    ARC ring images are fitted by adjusting the center, while the center is held
    fixed for night sky rings. A combination of fits to both sets of rings is
    used to determine a wavelength solution for the whole set of images.
    
    If the ARC rings disagree substantially with the night sky rings, it is
    recommended that users delete the ARC rings from the fit and use only the
    night sky rings.
    
    It is also known that the wavelength solution can sometimes be piecewise in
    time when a large jump in 'z' happens between two images; i.e. different
    wavelength solutions exist before and after the jump. The routine allows
    the user to make a piecewise solution for this reason, but this ability
    should be used sparingly.
    
    This routine contains one of the few hard-coded numbers in the pipeline,
    Fguess=5600. Currently F values are not written to the fits image headers,
    and this is a reasonable guess.
    
    Inputs:
    fnlist -> List of strings, each the path to a fits image. These images
    should all have been taken with the same order filter. If not, the routine
    will crash.
    
    """
    
    #This bit takes care of the 's' to save shortcut in matplotlib.
    oldsavekey = plt.rcParams["keymap.save"]
    plt.rcParams["keymap.save"] = ""
        
    #Open all of the images
    imagelist = []
    arclist = []
    objlist = []
    for i in range(len(fnlist)):
        imagelist.append(openfits(fnlist[i]))
        if i == 0: filt = imagelist[0][0].header["FILTER"]
        if imagelist[i][0].header["FILTER"] != filt:
            print "Error! Some of these images are in different filters!"
            crash()
        if imagelist[i][0].header["OBJECT"]=="ARC": arclist.append(imagelist[i])
        else:
            if not isfile(join(split(fnlist[i])[0],"median.fits")):
                print "Error! No 'median.fits' file found."
                crash()
            medimage = openfits(join(split(fnlist[i])[0],"median.fits"))
            imagelist[i][0].data += -medimage[0].data
            medimage.close()
            objlist.append(imagelist[i])
    
    #Load wavelength libraries
    arclib, nightlib = get_libraries(filt)
    if arclib is None:
        print "Error! Your filter isn't the wavelength library!"
        crash()

    



    #This next bit fits all of the rings that the user marks

    #Fit rings in the object images
    radlists = []
    for i in range(len(objlist)):
        radlists.append([])
    i=0
    while True:
        xgrid, ygrid = np.meshgrid(np.arange(objlist[i][0].data.shape[1]), np.arange(objlist[i][0].data.shape[0]))
        xcen = objlist[i][0].header["FPXCEN"]
        ycen = objlist[i][0].header["FPYCEN"]
        axcen = objlist[i][0].header["FPAXCEN"]
        aycen = objlist[i][0].header["FPAYCEN"]
        arad = objlist[i][0].header["FPARAD"]
        rgrid = np.sqrt((xgrid - xcen)**2 + (ygrid - ycen)**2)
        rbins = np.arange(arad-np.int(max(abs(axcen-xcen),abs(aycen-ycen))))+1
        intbins = np.empty_like(rbins)
        for j in range(len(rbins)):
            intbins[j] = np.median(objlist[i][0].data[np.logical_and(np.logical_and(objlist[i][0].data!=0,rgrid<rbins[j]),rgrid>rbins[j]-1)])
        ringplot = PlotRingProfile(objlist[i][0].data[aycen-arad:aycen+arad,axcen-arad:axcen+arad], #Data to be plotted. Only want stuff inside aperture
                                   rbins+(xcen-axcen)+arad, #Radii bins shifted to image center
                                   intbins*arad/np.percentile(np.abs(intbins),98)+(ycen-aycen)+arad, #Intensity bins, rescaled and shifted by image center
                                   xcen-axcen+arad, ycen-aycen+arad, #Shifted center
                                   radlists[i], #Previously fitted rings
                                   repr(i+1)+"/"+repr(len(objlist))) #numstring
        #Changing images and loop breakout conditions
        if ringplot.key == "d": i+=1
        if ringplot.key == "a": i+=-1
        if i == -1 or i == len(objlist):
            while True:
                yn = raw_input("Finished marking sky rings? (y/n) ")
                if "n" in yn or "N" in yn:
                    if i == -1: i=0
                    if i == len(objlist): i = len(objlist)-1
                    break
                elif "y" in yn or "Y" in yn:
                    break
        if i == -1 or i == len(objlist): break
        #Force-marking a ring
        if ringplot.key == "e" and ringplot.xcoo != None: radlists[i].append(ringplot.xcoo-arad-(xcen-axcen))
        #Deleting a ring
        if ringplot.key == "s" and ringplot.xcoo != None and len(radlists[i])>0:
            radlists[i].pop(np.argmin(np.array(radlists[i])-np.sqrt((ringplot.xcoo-arad-(xcen-axcen))**2 + (ringplot.ycoo-arad-(ycen-aycen))**2)))
        #Fitting a ring profile
        if ringplot.key == "w" and ringplot.xcoo != None:
            x = rbins[max(ringplot.xcoo-arad-(xcen-axcen)-50,0):min(ringplot.xcoo-arad-(xcen-axcen)+50,len(rbins))]**2
            y = intbins[max(ringplot.xcoo-arad-(xcen-axcen)-50,0):min(ringplot.xcoo-arad-(xcen-axcen)+50,len(rbins))]
            fit = GaussFit(x,y)
            fitplot = PlotRingFit(x,y,fit)
            if fitplot.key == "w": radlists[i].append(np.sqrt(fit[2]))
    zo = []
    to = []
    ro = []
    for i in range(len(objlist)):
        for j in range(len(radlists[i])):
            zo.append(objlist[i][0].header["ET1Z"])
            to.append(objlist[i][0].header["JD"])
            ro.append(radlists[i][j])
            
    #Fit rings in the ARC images
    xcen = objlist[0][0].header["FPXCEN"]
    ycen = objlist[0][0].header["FPYCEN"]
    radlists = []
    for i in range(len(arclist)):
        radlists.append([])
    i=0
    while True:
        xgrid, ygrid = np.meshgrid(np.arange(arclist[i][0].data.shape[1]), np.arange(arclist[i][0].data.shape[0]))
        axcen = arclist[i][0].header["FPAXCEN"]
        aycen = arclist[i][0].header["FPAYCEN"]
        arad = arclist[i][0].header["FPARAD"]
        rgrid = np.sqrt((xgrid - xcen)**2 + (ygrid - ycen)**2)
        rbins = np.arange(arad-np.int(max(abs(axcen-xcen),abs(aycen-ycen))))+1
        intbins = np.empty_like(rbins)
        for j in range(len(rbins)):
            intbins[j] = np.median(arclist[i][0].data[np.logical_and(np.logical_and(arclist[i][0].data!=0,rgrid<rbins[j]),rgrid>rbins[j]-1)])
        ringplot = PlotRingProfile(arclist[i][0].data[aycen-arad:aycen+arad,axcen-arad:axcen+arad], #Data to be plotted. Only want stuff inside aperture
                                   rbins+(xcen-axcen)+arad, #Radii bins shifted to image center
                                   intbins*arad/np.percentile(np.abs(intbins),98)+(ycen-aycen)+arad, #Intensity bins, rescaled and shifted by image center
                                   xcen-axcen+arad, ycen-aycen+arad, #Shifted center
                                   radlists[i], #Previously fitted rings
                                   repr(i+1)+"/"+repr(len(arclist))) #numstring
        #Changing images and loop breakout conditions
        if ringplot.key == "d": i+=1
        if ringplot.key == "a": i+=-1
        if i == -1 or i == len(arclist):
            while True:
                yn = raw_input("Finished marking ARC rings? (y/n) ")
                if "n" in yn or "N" in yn:
                    if i == -1: i=0
                    if i == len(arclist): i = len(arclist)-1
                    break
                elif "y" in yn or "Y" in yn:
                    break
        if i == -1 or i == len(arclist): break
        #Force-marking a ring
        if ringplot.key == "e" and ringplot.xcoo != None: radlists[i].append(ringplot.xcoo-arad-(xcen-axcen))
        #Deleting a ring
        if ringplot.key == "s" and ringplot.xcoo != None and len(radlists[i])>0:
            radlists[i].pop(np.argmin(np.array(radlists[i])-np.sqrt((ringplot.xcoo-arad-(xcen-axcen))**2 + (ringplot.ycoo-arad-(ycen-aycen))**2)))
        #Fitting a ring profile
        if ringplot.key == "w" and ringplot.xcoo != None:
            x = rbins[max(ringplot.xcoo-arad-(xcen-axcen)-50,0):min(ringplot.xcoo-arad-(xcen-axcen)+50,len(rbins))]**2
            y = intbins[max(ringplot.xcoo-arad-(xcen-axcen)-50,0):min(ringplot.xcoo-arad-(xcen-axcen)+50,len(rbins))]
            fit = GaussFit(x,y)
            fitplot = PlotRingFit(x,y,fit)
            if fitplot.key == "w": radlists[i].append(np.sqrt(fit[2]))
    za = []
    ta = []
    ra = []
    for i in range(len(arclist)):
        for j in range(len(radlists[i])):
            za.append(arclist[i][0].header["ET1Z"])
            ta.append(arclist[i][0].header["JD"])
            ra.append(radlists[i][j])
    
#     #Load previous ring fits from a text file - COMMENT THIS OUT LATER
#     rr,zz,tt = np.loadtxt("test.out",unpack=True)
#     za = list(zz[zz>0])
#     ta = list(tt[zz>0])
#     ra = list(rr[zz>0])
#     zo = list(zz[zz<0])
#     to = list(tt[zz<0])
#     ro = list(rr[zz<0])

    #Now we try to get a good guess at the wavelengths
    
    #Get a good guess at which wavelengths are which
    Bguess = objlist[0][0].header["ET1B"]
    Fguess = 5600
    
    #Figure out A by matching rings to the wavelength libraries
    master_r = np.array(ro+ra)
    master_z = np.array(zo+za)
    wavematch = np.zeros_like(master_r)
    isnight = np.array([True]*len(ro)+[False]*len(ra))
    oldrms = 10000 #Really high initial RMS for comparisons
    for i in range(len(master_r)):
        if isnight[i]: lib = nightlib
        else: lib = arclib
        for j in range(len(lib)):
            #Assume the i'th ring is the j'th line
            Aguess = lib[j]*np.sqrt(1+master_r[i]**2/Fguess**2)-Bguess*master_z[i]
            #What are all of the other rings, given this A?
            waveguess = (Aguess+Bguess*master_z)/np.sqrt(1+master_r**2/Fguess**2)
            for k in range(len(master_r)):
                if isnight[k]: wavematch[k] = nightlib[np.argmin(np.abs(nightlib-waveguess[k]))]
                else: wavematch[k] = arclib[np.argmin(np.abs(arclib-waveguess[k]))]
            rms = np.sqrt(np.average((waveguess-wavematch)**2))
            if rms < oldrms:
                #This is the new best solution. Keep it!
                oldrms = rms
                bestA = Aguess
                master_wave = wavematch.copy()
    
    #Make more master arrays for the plotting
    master_t = np.array(to+ta)
    t0 = np.min(master_t)
    master_t += -t0
    master_t *= 24*60 #Convert to minutes
    master_color = np.array(len(ro)*["blue"]+len(ra)*["red"]) #Colors for plotting
    toggle = np.ones(len(master_r),dtype="bool")
    dotime = False
    time_dividers = []
    
    #Do the interactive plotting
    while True:
        rplot = master_r[toggle]
        zplot = master_z[toggle]
        tplot = master_t[toggle]
        colorplot = master_color[toggle]
        waveplot = master_wave[toggle]
        fitplot = np.zeros(len(waveplot))
        xs = np.zeros((3,len(rplot)))
        xs[0] = rplot
        xs[1] = zplot
        xs[2] = tplot
        fit = [0]*(len(time_dividers)+1)
        time_dividers = sorted(time_dividers)
        if len(time_dividers)>1: print "Warning: Too many time divisions is likely unphysical. Be careful!"
        for i in range(len(time_dividers)+1):
            #Create a slice for all of the wavelengths before this time divider
            #but after the one before it
            if len(time_dividers)==0: tslice = tplot==tplot
            elif i == 0: tslice = tplot<time_dividers[i]
            elif i==len(time_dividers): tslice = tplot>time_dividers[i-1]
            else: tslice = np.logical_and(tplot<time_dividers[i],tplot>time_dividers[i-1])
            if dotime:
                fit[i] = curve_fit(fpfunc_for_curve_fit_with_t, xs[:,tslice], waveplot[tslice], p0=(bestA,Bguess,0,Fguess))[0]
                fitplot[tslice] = fpfunc_for_curve_fit_with_t(xs[:,tslice], fit[i][0], fit[i][1], fit[i][2], fit[i][3])
            else:
                fit[i] = curve_fit(fpfunc_for_curve_fit, xs[:,tslice], waveplot[tslice], p0=(bestA,Bguess,Fguess))[0]
                fitplot[tslice] = fpfunc_for_curve_fit(xs[:,tslice], fit[i][0], fit[i][1], fit[i][2])
        resid = waveplot - fitplot
        solnplot = WaveSolnPlot(rplot,zplot,tplot,waveplot,resid,colorplot,time_dividers)
        #Breakout case
        if solnplot.key == "a":
            while True:
                for i in range(len(time_dividers)+1):
                    if dotime: print "Solution 1: A = "+str(fit[i][0])+", B = "+str(fit[i][1])+", E = "+str(fit[i][2])+", F = "+str(fit[i][3])
                    else: print "Solution 1: A = "+str(fit[i][0])+", B = "+str(fit[i][1])+", F = "+str(fit[i][2])
                print "Residual rms="+str(np.sqrt(np.average(resid**2)))+" for "+repr(len(time_dividers)+1)+" independent "+repr(3+dotime)+"-parameter fits to "+repr(len(rplot))+" rings."
                yn = raw_input("Accept wavelength solution? (y/n) ")
                if "n" in yn or "N" in yn:
                    break
                elif "y" in yn or "Y" in yn:
                    solnplot.key = "QUIT"
                    break
        if solnplot.key == "QUIT": break
        #Restore all points case
        if solnplot.key == "r": toggle = np.ones(len(master_r),dtype="bool")
        #Delete nearest point case
        if solnplot.key == "d" and solnplot.axis != None:
            #Figure out which plot was clicked in
            if solnplot.axis == 1:
                #Resid vs. z plot
                z_loc = solnplot.xcoo
                resid_loc = solnplot.ycoo
                dist2 = ((zplot-z_loc)/(np.max(zplot)-np.min(zplot)))**2 + ((resid-resid_loc)/(np.max(resid)-np.min(resid)))**2
            elif solnplot.axis == 2:
                #Resid vs. R plot
                r_loc = solnplot.xcoo
                resid_loc = solnplot.ycoo
                dist2 = ((rplot-r_loc)/(np.max(rplot)-np.min(rplot)))**2 + ((resid-resid_loc)/(np.max(resid)-np.min(resid)))**2
            elif solnplot.axis == 3:
                #Resit vs. T plot
                t_loc = solnplot.xcoo
                resid_loc = solnplot.ycoo
                dist2 = ((tplot-t_loc)/(np.max(tplot)-np.min(tplot)))**2 + ((resid-resid_loc)/(np.max(resid)-np.min(resid)))**2
            elif solnplot.axis == 4:
                #Resid vs. Wave plot
                wave_loc = solnplot.xcoo
                resid_loc = solnplot.ycoo
                dist2 = ((waveplot-wave_loc)/(np.max(waveplot)-np.min(waveplot)))**2 + ((resid-resid_loc)/(np.max(resid)-np.min(resid)))**2
            #Get the radius and time of the worst ring
            r_mask = rplot[dist2 == np.min(dist2)][0]
            t_mask = tplot[dist2 == np.min(dist2)][0]
            toggle[np.logical_and(master_r == r_mask, master_t == t_mask)] = False
        #Fit for time case
        if solnplot.key == "t": dotime = not dotime
        #Add time break
        if solnplot.key == "w":
            timeplot = TimePlot(tplot,resid,colorplot,time_dividers)
            if timeplot.xcoo != None: time_dividers.append(timeplot.xcoo)
        #Remove time breaks
        if solnplot.key == "q":
            time_dividers = []
Example #18
0
def measure_fwhm(fnlist):
    """Gets the coordinates for a handful of stars from the user interactively,
    then attempts to measure the FWHM of those stars to obtain a value for the
    typical PSF of those images. The FWHMs are appended to the image headers
    as the keyword 'FPFWHM'. This routine assumes the PSFs are circularly
    symmetric Gaussians (which is not always the case for SALT, especially if
    the mirror alignment fell apart during a track!).
    
    Inputs:
    fnlist -> A list of strings, each the location of a fits image. The images
              should all have roughly the same coordinate system, because the
              same stellar coordinates are used for all images in the list.
    
    """

    #Open the images
    imagelist = []
    for i in range(len(fnlist)):
        imagelist.append(openfits(fnlist[i],mode="update"))
    
    xcen = imagelist[0][0].header["fpaxcen"]
    ycen = imagelist[0][0].header["fpaycen"]
    arad = imagelist[0][0].header["fparad"]
    data = imagelist[0][0].data[ycen-arad:ycen+arad,xcen-arad:xcen+arad]
    xgrid, ygrid = np.meshgrid( np.arange(50), np.arange(50) )
    starx, stary, starfwhm = [], [], []
    while True:
        plot = Mark_Stars_Plot(data,starx,stary)
        if plot.key == "q" and len(starx) != 0: break
        elif not (plot.xcoo is None) and plot.key == "d" and len(starx) != 0:
            #Find the star nearest to the coordinates
            distarray = (np.array(starx)-plot.xcoo)**2+(np.array(stary)-plot.ycoo)**2
            index = np.where(distarray == np.min(distarray))[0][0]
            starx.pop(index)
            stary.pop(index)
            starfwhm.pop(index)
        elif not (plot.xcoo is None) and plot.key == "z":
            zoom = Zoom_Mark_Stars_Plot(data[plot.ycoo-25:plot.ycoo+25,plot.xcoo-25:plot.xcoo+25])
            if not (zoom.xcoo is None):
                #Measure the FWHM of that star in every image
                fwhms = np.zeros(len(fnlist))
                for i in range(len(fnlist)):
                    zoomdata = imagelist[i][0].data[plot.ycoo+zoom.ycoo+ycen-arad-50:plot.ycoo+zoom.ycoo+ycen-arad,plot.xcoo+zoom.xcoo+xcen-arad-50:plot.xcoo+zoom.xcoo+xcen-arad]
                    xpeak = xgrid[zoomdata==np.max(zoomdata)][0]
                    ypeak = ygrid[zoomdata==np.max(zoomdata)][0]
                    xdata = zoomdata[ypeak,:]
                    ydata = zoomdata[:,xpeak]
                    xfit = GaussFit(np.arange(len(xdata)),xdata)
                    yfit = GaussFit(np.arange(len(ydata)),ydata)
                    if xfit[3]<50 and yfit[3]<50 and xfit[3]>0 and yfit[3]>0:
                        fwhms[i]=(xfit[3]/2+yfit[3]/2)*np.sqrt(8*np.log(2))
                        
                #Plot the FWHM across the track and ask user if it's okay
                if len(stary)>1:
                    oldfwhms = np.median(np.array(starfwhm), axis=0)
                    oldfwhmserr = np.std(np.array(starfwhm), axis=0)
                elif len(stary)==1:
                    oldfwhms = np.median(np.array(starfwhm), axis=0)
                    oldfwhmserr = None
                else:
                    oldfwhms = None
                    oldfwhmserr = None
                fwhmsplot = FWHMs_Plot(fwhms,oldfwhms,oldfwhmserr)
                if not(fwhmsplot.button is None):
                    #If it's okay, append the coordinates and FWHMs to the lists
                    starx.append(zoom.xcoo+plot.xcoo-25)
                    stary.append(zoom.ycoo+plot.ycoo-25)
                    starfwhm.append(fwhms)
    
    #Append the measured FWHMs to the image headers
    imagefwhm = np.median(np.array(starfwhm), axis=0)
    for i in range(len(fnlist)):
        imagelist[i][0].header["fpfwhm"] = imagefwhm[i]
    
    #Close the images
    for i in range(len(fnlist)):
        imagelist[i].close()
    
    return 
Example #19
0
def deghost(fn,uncfn=None,g=0.04):
    """Routine to deghost an image by rotating it about a central point and
    subtracting a constant multiple of this rotated image from the original.
    My experience has shown that about 0.04 is the right value, but your
    mileage may vary.
    
    The fits header must contain values in "fpxcen" and "fpycen" for the center
    about which to be rotated.
    
    Optionally also modifies an uncertainty image to account for the effects
    of deghosting on the error propagation.
    
    Creates the fits header keyword "fpghost" = "True".
    
    Inputs:
    fn -> String, the path to the fits image to be deghosted.
    uncfn (optional) -> String, the path to the uncertainty image.
    g -> The multiple to subtract from the original image, default = 4%
    
    """
    
    #Open the image and check for center coordinates
    image = openfits(fn,mode="update")
    xcen = image[0].header.get("fpxcen")
    ycen = image[0].header.get("fpycen")
    if xcen == None:
        print "Error! Image "+fn+" doesn't have center coordinates in header!"
        crash()
    
    #Deghost the image
    print "Deghosting image "+fn
    image[0].header["fpghost"] = "True"
    
    #Determine image size
    xsize=image[0].data.shape[1]
    ysize=image[0].data.shape[0]

    #Make a mask for the chip gaps and outside-aperture stuff
    mask = image[0].data==0

    #Make an array of the flipped data
    flip = image[0].data[::-1,::-1].copy()

    #Calculate the difference between the image's geometric center (midpoint) and the axis of rotation
    xshift = 2*xcen-xsize-1
    yshift = 2*ycen-ysize-1

    #A given pixel's position, when rotated, will overlap its four neighboring pixels.
    #All pixels will have the same four overlapping regions because the rotation is 180 degrees.
    #Here we take a weighted sum of these four pixels, where the weights are equal to the areas of overlap.
    #This weighted sum is subtracted from the original pixel in the data array.
    image[0].data[max(np.floor(yshift),0):min(ysize+np.floor(yshift),ysize),max(np.floor(xshift),0):min(xsize+np.floor(xshift),xsize)] += ( -g*abs((np.ceil(yshift)-yshift)*(np.ceil(xshift)-xshift))*flip[max(-np.floor(yshift),0):min(ysize,ysize-np.floor(yshift)),max(-np.floor(xshift),0):min(xsize,xsize-np.floor(xshift))])
    image[0].data[max(np.ceil(yshift),0):min(ysize+np.ceil(yshift),ysize),max(np.floor(xshift),0):min(xsize+np.floor(xshift),xsize)] += ( -g*abs((np.floor(yshift)-yshift)*(np.ceil(xshift)-xshift))*flip[max(-np.ceil(yshift),0):min(ysize,ysize-np.ceil(yshift)),max(-np.floor(xshift),0):min(xsize,xsize-np.floor(xshift))])
    image[0].data[max(np.floor(yshift),0):min(ysize+np.floor(yshift),ysize),max(np.ceil(xshift),0):min(xsize+np.ceil(xshift),xsize)] += ( -g*abs((np.ceil(yshift)-yshift)*(np.floor(xshift)-xshift))*flip[max(-np.floor(yshift),0):min(ysize,ysize-np.floor(yshift)),max(-np.ceil(xshift),0):min(xsize,xsize-np.ceil(xshift))])
    image[0].data[max(np.ceil(yshift),0):min(ysize+np.ceil(yshift),ysize),max(np.ceil(xshift),0):min(xsize+np.ceil(xshift),xsize)] += ( -g*abs((np.floor(yshift)-yshift)*(np.floor(xshift)-xshift))*flip[max(-np.ceil(yshift),0):min(ysize,ysize-np.ceil(yshift)),max(-np.ceil(xshift),0):min(xsize,xsize-np.ceil(xshift))])
    
    #Remask the data using the mask
    image[0].data[mask] = 0
    
    #Close the image
    image.close()
    
    #Modify the uncertainty image if it exists
    if not (uncfn is None):
        print "Updating uncertainty image "+uncfn
        uncimage = openfits(uncfn,mode="update")
        
        #Make an array of the flipped data
        flip = uncimage[0].data[::-1,::-1].copy()*1.0
        
        #Uncertainties add in quadrature
        uncimage[0].data = np.power(uncimage[0].data,2)
        
        #Add the uncertainty in the deghosted uncertainty image to the original
        uncimage[0].data[max(np.floor(yshift),0):min(ysize+np.floor(yshift),ysize),max(np.floor(xshift),0):min(xsize+np.floor(xshift),xsize)] += (g*abs((np.ceil(yshift)-yshift)*(np.ceil(xshift)-xshift))*flip[max(-np.floor(yshift),0):min(ysize,ysize-np.floor(yshift)),max(-np.floor(xshift),0):min(xsize,xsize-np.floor(xshift))])**2
        uncimage[0].data[max(np.ceil(yshift),0):min(ysize+np.ceil(yshift),ysize),max(np.floor(xshift),0):min(xsize+np.floor(xshift),xsize)] += ( g*abs((np.floor(yshift)-yshift)*(np.ceil(xshift)-xshift))*flip[max(-np.ceil(yshift),0):min(ysize,ysize-np.ceil(yshift)),max(-np.floor(xshift),0):min(xsize,xsize-np.floor(xshift))])**2
        uncimage[0].data[max(np.floor(yshift),0):min(ysize+np.floor(yshift),ysize),max(np.ceil(xshift),0):min(xsize+np.ceil(xshift),xsize)] += ( g*abs((np.ceil(yshift)-yshift)*(np.floor(xshift)-xshift))*flip[max(-np.floor(yshift),0):min(ysize,ysize-np.floor(yshift)),max(-np.ceil(xshift),0):min(xsize,xsize-np.ceil(xshift))])**2
        uncimage[0].data[max(np.ceil(yshift),0):min(ysize+np.ceil(yshift),ysize),max(np.ceil(xshift),0):min(xsize+np.ceil(xshift),xsize)] += ( g*abs((np.floor(yshift)-yshift)*(np.floor(xshift)-xshift))*flip[max(-np.ceil(yshift),0):min(ysize,ysize-np.ceil(yshift)),max(-np.ceil(xshift),0):min(xsize,xsize-np.ceil(xshift))])**2
        
        uncimage[0].data = np.sqrt(uncimage[0].data)
        
        #Close the uncertainty image
        uncimage.close()
        
    return
Example #20
0
 final_wave_lists = []
 final_unc_lists = []
 for i in range(len(list_of_objs)):
     final_inty_lists.append([])
     final_wave_lists.append([])
     final_unc_lists.append([])
     finalimagelist = sorted(listdir(list_of_objs[i].replace(" ", "")+"_cube"))
     for j in range(len(finalimagelist)):
         if "_wave.fits" in finalimagelist[j]: final_wave_lists[i].append(join(list_of_objs[i].replace(" ", "")+"_cube",finalimagelist[j]))
         elif "_unc.fits" in finalimagelist[j]: final_unc_lists[i].append(join(list_of_objs[i].replace(" ", "")+"_cube",finalimagelist[j]))
         elif "_inty.fits" in finalimagelist[j]: final_inty_lists[i].append(join(list_of_objs[i].replace(" ", "")+"_cube",finalimagelist[j]))
 if len(final_wave_lists) != len(final_unc_lists) or len(final_wave_lists) != len(final_inty_lists): crash("Error! Mismatched images in data cube directory!")
 
 #Shift to solar velocity frame
 for i in range(len(list_of_objs)):
     firstimage = openfits(final_wave_lists[i][0])
     velshift = firstimage[0].header.get("fpsolar")
     firstimage.close()
     if velshift is None:
         print "Performing solar velocity shift for object "+list_of_objs[i]+"..."
         solar_velocity_shift(final_wave_lists[i], rest_wave)
     else: print "Solar velocity shift for object "+list_of_objs[i]+" already done."
 
 #Velocity map fitting
 for i in range(len(list_of_objs)):
     if (isfile(join(list_of_objs[i].replace(" ", "")+"_cube","velocity.fits"))):
         while True:
             yn = raw_input("Velocity map already fitted for object "+list_of_objs[i]+". Redo this? (y/n) ")
             if "n" in yn or "N" in yn:
                 domap = False
                 break
Example #21
0
def verify_images(fnlist):
    """Interactively plots all of the images in 'fnlist' to verify their
    contents are correct. Allows user to mark bad images and change image
    header keywords to correct issues.
    
    Inputs:
    fnlist -> List of filenames to be plotted
    
    Outputs:
    newfnlist -> New list of filenames with bad files removed from the list
    
    """

    #How many total images are there?
    num_images = len(fnlist)
    flagged = np.zeros(len(fnlist),dtype="bool")
    
    #Interactively look at the images
    current_num = 0
    while True:
        image = openfits(fnlist[current_num],mode="update")
        image[0].header["fpverify"]="True"
        verify = Verify_Images_Plot(image[0].data,
                             image[0].header["OBJECT"],
                             flagged[current_num],
                             current_num+1,
                             num_images)
        image.close()
        if verify.key == "left": current_num = current_num-1
        if verify.key == "right": current_num = current_num+1
        if verify.key == "up": flagged[current_num] = True
        if verify.key == "down": flagged[current_num] = False
        
        #Breakout condition
        if current_num == -1:
            while True:
                yn = raw_input("Finished flagging images? (y/n) ")
                if "n" in yn or "N" in yn:
                    current_num = 0
                    break
                elif "y" in yn or "Y" in yn:
                    break
        elif current_num == num_images:
            while True:
                yn = raw_input("Finished flagging images? (y/n) ")
                if "n" in yn or "N" in yn:
                    current_num = current_num-1
                    break
                elif "y" in yn or "Y" in yn:
                    break
        if current_num == -1 or current_num == num_images or verify.key == "q": break
    
    #Review flagged images
    print "Reviewing "+str(np.sum(flagged))+" flagged images..."
    newfnlist = []
    for i in range(len(fnlist)):
        if flagged[i]:
            image = openfits(fnlist[i],mode="update")
            review = Review_Images_Plot(image[0].data,image[0].header["OBJECT"],fnlist[i])
            if review.key == "h":
                #Correct header
                while True:
                    newhead = raw_input("New object type for header (ARC, FLAT, etc.): ")
                    yn = raw_input("New object header: '"+newhead+"'. Is this okay? (y/n) ")
                    if ("y" in yn or "Y" in yn) and not ("n" in yn or "N" in yn):
                        image[0].header["OBJECT"] = newhead
                        break
                newfnlist.append(fnlist[i])
            if review.key == "a":
                newfnlist.append(fnlist[i])
            if review.key == "d":
                print fnlist[i]+" removed from file list."
                image[0].header["FPGOOD"] = "False"
            image.close()
        else: newfnlist.append(fnlist[i])
    
    return newfnlist