def aperture_mask(fnlist, axcen, aycen, arad): """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 """ for i in range(len(fnlist)): print ("Masking pixels outside aperture for image " + str(i+1)+" of "+str(len(fnlist))+": "+fnlist[i]) image = FPImage(fnlist[i], update=True) rgrid = image.rarray(axcen, aycen) image.inty[rgrid > arad] = 0 image.badp[rgrid > arad] = 1 image.axcen = axcen image.aycen = aycen image.arad = arad image.close()
def make_final_image(input_image, output_image, desired_fwhm, clobber=False): """This routine makes the 'final' images for a data cube. At least the paths to the input image, output image, and output wavelength image are necessary for this. Beyond that, the user may also have the routine create uncertainty images as well. Images are convolved to the resolution 'desired_fwhm'. If the current fwhm is already higher than that, the routine will throw an error. A number of fits header keywords are necessary for this program to function properly. Any of these missing will throw an error. The output images are intensity-weighted, i.e. the wavelength image will be created such that the wavelengths at each pixel are the 'most likely' wavelength for the intensity at that pixel, etc. Inputs: input_image -> Path to the input image. output_image -> Path to the output image. desired_fwhm -> Desired FWHM for the resultant image to have. Optional Inputs: clobber -> Overwrite output images if they already exist. Default is False. """ print "Making final data cube images for image "+input_image # Open the image image = FPImage(input_image) # Measure the sky background level in the input image skyavg, _truesig, skysig = image.skybackground() # Get various header keywords, crash if necessary intygrid = image.inty fwhm = image.fwhm wave0 = image.wave0 calf = image.calf xcen = image.xcen ycen = image.ycen if fwhm is None: exit("Error! FWHM not measured for image "+input_image+".") if wave0 is None or calf is None: exit("Error! Wavelength solution does " + "not exist for image "+input_image+".") if xcen is None or ycen is None: exit("Error! Center values not measured " + "image "+input_image+".") if fwhm > desired_fwhm: exit("Error! Desired FWHM too low for image " + input_image+".") # Calculate the necessary FWHM for convolution fwhm_conv = np.sqrt(desired_fwhm**2-fwhm**2) # Magic number converts sigma to fwhm sig = fwhm_conv/2.3548 # Extremely conservative "safe" kernel size ksize = np.ceil(4*sig) # Generate the gaussian kernel, shifted to shift the image kxgrid, kygrid = np.meshgrid(np.linspace(-ksize, ksize, 2*ksize+1), np.linspace(-ksize, ksize, 2*ksize+1)) xshift, yshift = image.xshift, image.yshift rad2grid = (kxgrid + xshift)**2 + (kygrid + yshift)**2 kern = np.exp(-rad2grid/(2*sig**2)) # Normalize the kernel kern = kern/np.sum(kern) # Extract relevant arrays vargrid = image.vari badpixgrid = image.badp # Convolve the variances appropriately new_vargrid = convolve_variance(vargrid, badpixgrid, kern) # Add the sky background uncertainty to the uncertainty grid vargrid = vargrid+skysig**2 # Create and convolve the wavelength array if image.wave is None: rgrid = image.rarray(xcen, ycen) wavegrid = wave0 / np.sqrt(1+rgrid**2/calf**2) else: wavegrid = image.wave new_wavegrid = convolve_wave(wavegrid, intygrid, badpixgrid, kern) # Convolve the intensity image new_intygrid = convolve_inty(intygrid, badpixgrid, kern) new_intygrid[new_intygrid == 0] = intygrid[new_intygrid == 0] new_intygrid[np.isnan(new_intygrid)] = intygrid[np.isnan(new_intygrid)] image.fwhm = desired_fwhm # Update header FWHM keyword # Subtract the sky background from the image new_intygrid[new_intygrid != 0] -= skyavg # Create a new fits extension for the wavelength array if image.wave is None: waveheader = image.openimage[2].header.copy() waveheader['EXTVER'] = 4 wavehdu = ImageHDU(data=new_wavegrid, header=waveheader, name="WAVE") image.openimage[1].header.set('WAVEEXT', 4, comment='Extension for Wavelength Frame') image.openimage.append(wavehdu) # Put all of the convolved images in the right extensions image.inty = new_intygrid image.vari = new_vargrid image.wave = new_wavegrid # Adjust header keywords (even though they're kinda irrelevant now) image.xcen += image.xshift image.ycen += image.yshift image.axcen += image.xshift image.aycen += image.yshift # Write the output file image.writeto(output_image, clobber=clobber) # Close images image.close() return
def deghost(fn): """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. """ # Open the image and check for center coordinates image = FPImage(fn, update=True) if image.xcen is None: exit("Error! Image "+fn+" doesn't have center coordinates in header!") xcen = image.xcen+1 ycen = image.ycen+1 bins = int(image.bins.split()[0]) reflection_xcen = image.xcen + reflection_center_x_shift/bins reflection_ycen = image.ycen + reflection_center_y_shift/bins r_array = image.rarray(reflection_xcen, reflection_ycen) g_array = reflection_intercept + reflection_slope*bins*r_array # Deghost the intensity image print "Deghosting image "+fn # Determine image size xsize, ysize = image.xsize, image.ysize # Make a mask for the chip gaps and outside-aperture stuff mask = image.inty == 0 # Make an array of the flipped data flipinty = image.inty[::-1, ::-1].copy()*1.0 flipvari = image.vari[::-1, ::-1].copy()*1.0 flip_g = g_array[::-1, ::-1].copy()*1.0 # 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. for i in range(4): # This branching is for the four overlapping pixel regions if i == 0: miny = max(np.floor(yshift), 0) maxy = min(ysize+np.floor(yshift), ysize) minx = max(np.floor(xshift), 0) maxx = min(xsize+np.floor(xshift), xsize) flipminy = max(-np.floor(yshift), 0) flipmaxy = min(ysize, ysize-np.floor(yshift)) flipminx = max(-np.floor(xshift), 0) flipmaxx = min(xsize, xsize-np.floor(xshift)) frac = abs((np.ceil(yshift)-yshift)*(np.ceil(xshift)-xshift)) if i == 1: miny = max(np.ceil(yshift), 0) maxy = min(ysize+np.ceil(yshift), ysize) minx = max(np.floor(xshift), 0) maxx = min(xsize+np.floor(xshift), xsize) flipminy = max(-np.ceil(yshift), 0) flipmaxy = min(ysize, ysize-np.ceil(yshift)) flipminx = max(-np.floor(xshift), 0) flipmaxx = min(xsize, xsize-np.floor(xshift)) frac = abs((np.floor(yshift)-yshift)*(np.ceil(xshift)-xshift)) if i == 2: miny = max(np.floor(yshift), 0) maxy = min(ysize+np.floor(yshift), ysize) minx = max(np.ceil(xshift), 0) maxx = min(xsize+np.ceil(xshift), xsize) flipminy = max(-np.floor(yshift), 0) flipmaxy = min(ysize, ysize-np.floor(yshift)) flipminx = max(-np.ceil(xshift), 0) flipmaxx = min(xsize, xsize-np.ceil(xshift)) frac = abs((np.ceil(yshift)-yshift)*(np.floor(xshift)-xshift)) if i == 3: miny = max(np.ceil(yshift), 0) maxy = min(ysize+np.ceil(yshift), ysize) minx = max(np.ceil(xshift), 0) maxx = min(xsize+np.ceil(xshift), xsize) flipminy = max(-np.ceil(yshift), 0) flipmaxy = min(ysize, ysize-np.ceil(yshift)) flipminx = max(-np.ceil(xshift), 0) flipmaxx = min(xsize, xsize-np.ceil(xshift)) frac = abs((np.floor(yshift)-yshift)*(np.floor(xshift)-xshift)) g = flip_g[flipminy:flipmaxy, flipminx:flipmaxx] # Rotate and subtract the intensity array image.inty[miny:maxy, minx:maxx] -= g*frac*flipinty[flipminy:flipmaxy, flipminx:flipmaxx] image.vari[miny:maxy, minx:maxx] += (g*frac)**2*flipvari[flipminy:flipmaxy, flipminx:flipmaxx] # Remask the intensity data using the mask we created image.inty[mask] = 0 # Update the header image.ghosttog = "True" # Close the image image.close() return