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
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
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
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
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
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__
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
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()
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
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
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
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
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()
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)
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
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 = []
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
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
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
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