def trim(args): # Define region to be trimmed y0, y1, x0, x1 = args.region # args.output should be a list of output names. If they do not exist, the outputs should be the same as the # inputs with whatever suffix, if any, the user gave if not args.output: args.output = [utilities.add_suffix_prefix(im_name, suffix=args.suffix) for im_name in args.input] # Do the actual trimming. We will do it first into a temporary file, then copy it into args.output. This is just # in case the output and input filenames are the same, or if the output exists. IRAF will not overwrite! for im_name, new_name in zip(args.input, args.output): basename = os.path.splitext(os.path.basename(im_name))[0] _, temp_output = tempfile.mkstemp(prefix=basename, suffix=".fits") os.unlink(temp_output ) with utilities.tmp_mute(): imcopy(im_name + "[{0}:{1}, {2}:{3}]".format(x0, x1, y0, y1), temp_output) shutil.move(temp_output, new_name) # If a mask exists, trim exactly equally if args.mask_key: maskname = fits.getheader(im_name)[args.mask_key] with fits.open(maskname, 'readonly') as mask_im: mask_im[0].data = mask_im[0].data[y0:y1+1, x0:x1+1] mask_output = utilities.replace_extension(new_name, ".fits.msk") fits.writeto(mask_output, mask_im[0].data, mask_im[0].header, clobber=True) utilities.header_update_keyword(new_name, "MASK", os.path.abspath(mask_output), comment="Name of mask image. ") return args.output
def match(args): """ Match the PSF of a group of images with the PSF of a reference image. """ sort_by_seeing(args) # Get seeing from header and calculate psf of reference image, the first one since we sorted the input ref_seeing = utils.get_from_header(args.input[0], args.FWHM_key) ref_psf = psf.main(arguments=[args.input[0], "--stars", args.input_stars[0], "--sigma_key", args.sigma, "--gain_key", args.gain_key, "--ron_key", args.ron_key, "--expt_key", args.expt_key, "--airm_key", args.airm_key, "--FWHM_key", args.FWHM_key]) output_list = [] for image, stars in zip(args.input, args.input_stars): output = utils.add_suffix_prefix(image, suffix=args.suffix) # Too small differences of seeing are not worh doing any matching current_seeing = utils.get_from_header(image, args.FWHM_key) if abs(ref_seeing - current_seeing) < args.limit: # Not worth equating PSFs for small differences shutil.copy(image, output) # copy old file into new one else: # Calculate psf of the other images psf_object = psf.main(arguments=[image, "--stars", stars, "--sigma_key", args.sigma, "--gain_key", args.gain_key, "--ron_key", args.ron_key, "--expt_key", args.expt_key, "--airm_key", args.airm_key, "--FWHM_key", args.FWHM_key]) utils.if_exists_remove("kernel.fits", output) iraf.psfmatch(image, ref_psf, psf_object, "kernel.fits", convolution="psf", filter="cosbell") iraf.psfmatch(image, ref_psf, psf_object, convolution="kernel", kernel="kernel.fits", output=output, verbose="no") utils.if_exists_remove("kernel.fits") mssg = "Before equating PSFs: " + str(current_seeing) utils.header_update_keyword(output, args.FWHM_key, ref_seeing, comment=mssg) output_list.append(output) return output_list
def calculate_seeing(args): """ Program to estimate the seeing from an image and a list of estimates for the positions of stars. After calculating the seeing, some of the stars might get recalculated centers. The list will be updated with the """ for im, im_cat in zip(args.input, args.cat): output = "output.txt" ignore = "ignore.txt" # Victor Terron, esto es horroroso, sugerencias? utilities.if_exists_remove("q.txt", output, ignore) q = open("q.txt", "w") q.write("q") q.close() iraf.noao() iraf.obsutil() iraf.module.psfmeasure(im, coords = "mark1", size = "MFWHM", sbuffer = 10, swidth=10,radius=10, satura = 55000, ignore = "yes", imagecur = im_cat, display = "no", graphcur = "q.txt", Stdout=ignore, wcs = args.wcs, logfile=output) # Now we read the input im_cat and the output output.txt and compare # the location of stars. Those stars that have moved will not be # trusted. xout = np.array([]) yout = np.array([]) FWHM = np.array([]) for line in open(output, 'r'): # First line is a description of the file, second is a newline \n # and third the description of the columns. if line != "\n" and line.split()[0] in im: xout = np.append(xout, float(line.split()[1])) yout = np.append(yout, float(line.split()[2])) FWHM = np.append(FWHM, float(line.split()[4])) else: try: xout = np.append(xout, float(line.split()[0])) yout = np.append(yout, float(line.split()[1])) FWHM = np.append(FWHM, float(line.split()[3])) except: pass xin, yin = np.genfromtxt(im_cat, dtype="float", unpack=True) xin, yin = np.array(xin), np.array(yin) # case it is only one value # If args.wcs is "world" it means the input is in (RA, DEC), while # the output is in pixels (X,Y). We need to convert one to the other # and we choose to follow the (RA,DEC) which, after all, is meaningful if args.wcs == "world": coords_xy = np.array(zip(xout, yout)) hdr = fits.open(im)[0].header remove_keys = ["PC001001", "PC001002", "PC002001", "PC002002"] for keys in remove_keys: hdr.pop(keys,None) w = wcs.WCS(hdr) coords_RADEC = w.all_pix2world(coords_xy,1) xout = coords_RADEC[:,0] yout = coords_RADEC[:,1] # find common stars using KDtrees if xin.size > 1 : tree_in = spatial.KDTree(zip(xin, yin)) elif xin.size == 1: tree_in = spatial.KDTree([(float(xin), float(yin))]) if xout.size > 1: tree_out = spatial.KDTree(zip(xout, yout)) elif xout.size == 1: tree_out = spatial.KDTree([(float(xout), float(yout))]) # If WCS use 0.001 degree (~3.6 arcsec) as limit. If not, assume # pixels and say 4 pixels if args.wcs == "world": limit = 0.001 else: limit = 4 matches = tree_out.query_ball_tree(tree_in, limit) # Two close stars in the original .cat could resolve into one when # the FWHM is calculated. This will appear as several hits in the # matches with exactly the same numbers: [[0], [1,2], [1,2], [3]] # One solution is to erase one of them for index, value in enumerate(matches): if matches.count(value) > 1: matches[index] = [] # Now restrict to the common objects remove_indices = [ii for ii,jj in enumerate(matches) if jj == []] xout = np.delete(xout, remove_indices) yout = np.delete(yout, remove_indices) FWHM = np.delete(FWHM, remove_indices) # Finally, calculate the median FWHM of the image and rewrite valid # stars to the im_cat file. median_fwhm = np.median(FWHM) utilities.header_update_keyword(im, "seeing", median_fwhm, "FWHM of image") f = open(im_cat, 'w') # write the "good" stars in the catalogue for ii in range(len(xout)): f.write(str(xout[ii]) + " " + str(yout[ii]) + "\n") f.close() # And clean after yourself! utilities.if_exists_remove("q.txt", output, ignore)
os.chdir(lemon_dir) import lemon.seeing as seeing seeing.main(arguments=["--margin", "0", "--filename", '', "--suffix", "-s", image, os.path.split(image)[0] ]) newname = utilities.add_suffix_prefix(image, suffix = "-s") os.chdir(curdir) list_images["filename"][index] = newname # While running lemon.seeing a sextractor catalogue is produced. catalog = fits.getheader(newname)["SEX CATALOG"] catalog_newname = utilities.replace_extension(newname, ".cat") shutil.copy(catalog, catalog_newname) # Damn FITS format and its constraints! short_name = os.path.split(catalog_newname)[1] utilities.header_update_keyword(newname, "SEX CATALOG", short_name) print "Estimate sky for images of CIG(s), standard(s) and cluster(s) " for index, image in enumerate(list_images["filename"]): if list_images["type"][index] in ["cig","standards","clusters"]: find_sky.main(arguments=[list_images["filename"][index]]) print "Detecting objects for images of CIG(s), standard(s) and cluster(s)" # This part follows the example in the webpage: #http://www.lancesimms.com/programs/Python/pyraf/daofind.py for index, image in enumerate(list_images["filename"]): if list_images["type"][index] in ["cig","standards","clusters"]: outfile = utilities.replace_extension(image, ".cat") if os.path.isfile(outfile): os.remove(outfile) # Prepare and run daofind
def combine(args): # Create the folders that do not already exist for the output file outdir, outfile = os.path.split(os.path.abspath(args.output)) if outdir == "": outdir = "." utils.if_dir_not_exists_create(outdir) # Build a list of the filter of each image #filter_list, images_filters = build_filter_list(args) images_filters = utils.collect_from_images(args.input, args.filterk) # If user wants all images to be combined together, regardless of filter: if args.all_together: images_filters = ["AllFilters"] * len(images_filters) # Create a default dictionary for the resulting images result = collections.defaultdict(str) # For each of the filters present combine the images (and write) for filt in set(images_filters): # list of objects with current filter (exception: allfilters is true) list1 = [args.input[p] for p,f in enumerate(images_filters) if f == filt ] # Check that all images have same dimension. If not, exit program if not utils.check_dimensions(list1): sys.exit("Dimensions of images to combine are different!") # Calculate scale of images scales = compute_scales(list1, args.scale, args.mask_key) # Get the sizes of the images lx, ly = utils.get_from_header(list1[0], "NAXIS2", "NAXIS1") # Now, in order to avoid loading many images in memory, we need to slice the images in pieces and combine a slice # of all the images at a time n_slices = 32 # divide the slow axis in this many pieces # Define the whole image and set all elements of mask to False whole_image = numpy.ma.zeros([lx,ly]) whole_image.mask = numpy.zeros_like(whole_image.data) for xmin in range(0, lx, lx/n_slices): xmax = min(xmin + lx/n_slices, lx) # Now we can build and sort a section of the cube with all the images cube = cube_images(list1, args.mask_key, scales, limits=[xmin, 0, xmax, ly]) cube.sort(axis=0) # Finally, average! Remember that the cube is sorted so that # cube[0,ii,jj] < cube[1,ii,jj] and that the highest values of all # are the masked elements. We will take advantage of it if the median # is selected, because nowadays the masked median is absurdly slow: # https://github.com/numpy/numpy/issues/1811 map_cube = numpy.ma.count(cube, axis=0) # number non-masked values per pixel if args.average == "mean": image = numpy.ma.mean(cube, axis=0) non_masked_equivalent = numpy.mean(cube.data, axis=0) elif args.average == "median": image = home_made_median(map_cube, cube) non_masked_equivalent = numpy.median(cube.data, axis=0) # Image is a masked array, we need to fill in masked values with the # args.fill_val if user provided it. Also, values with less than # args.nmin valid values should be masked out. If user did not provide # a fill_val argument, we will substitute masked values with the # unmasked equivalent operation. image.mask[map_cube < args.nmin] = 1 mask = image.mask if args.fill_val != '': image = image.filled(args.fill_val) else: image.data[mask == True] = non_masked_equivalent[mask == True] image = image.data whole_image.data[xmin:xmax, 0:ly] = image[:,:] whole_image.mask[xmin:xmax, 0:ly] = mask[:,:] # And save images. If all_together is activated, use the file name given by user. If not, we need # to separate by filter, so compose a new name with the one given by the user adding the filter if args.all_together: newfile = args.output else: newfile = os.path.join(outdir, utils.add_suffix_prefix(outfile, suffix="_" + filt) ) if args.out_mask != "": name_mask = args.out_mask else: name_mask = newfile + ".msk" if os.path.isfile(newfile): os.remove(newfile) if os.path.isfile(name_mask): os.remove(name_mask) fits.writeto(newfile, whole_image.data) fits.writeto(name_mask, whole_image.mask.astype(numpy.int)) result[filt] = newfile # Add comments to the headers string1 = " - Image built from the combination of the images: "+\ ", ".join(list1) string2 = " combine = " + args.average + ", scale = " + args.scale utils.add_history_line(newfile, string1 + string2 ) utils.add_history_line(name_mask, " - Mask of image: " + newfile) if args.mask_key != "": utils.header_update_keyword(newfile, args.mask_key, name_mask, "Mask for this image") # To normalize calculate median and call arith_images to divide by it. if args.norm == True: median = compute_scales([newfile], args.scale, args.mask_key)[0] msg = "- NORMALIZED USING MEDIAN VALUE:" arith_images.main(arguments=["--message", msg, "--output", newfile, "--mask_key", args.mask_key, "--fill_val", args.fill_val, newfile, "/", str(median)]) return result
if os.path.exists( os.path.join(directory, 'cig') ): list_images = search_images(directory) else: print "Rename files" list_images = rename.main(arguments=["--copy", "--objectk", objectk,\ "--filterk", filterk, "--datek", datek,\ "--overwrite", "--exptime", exptimek,\ directory]) print "Include homogeneous filter names into the filter keyword" for im in list_images["filename"]: imfilt = utilities.get_from_header(im, filterk) imfilt2 = utilities.homogeneous_filter_name(imfilt) utilities.header_update_keyword(im, filterk+"_OLD", imfilt, "Original filter name") utilities.header_update_keyword(im, filterk, imfilt2, "Revised filter name") print "Ignore images as selected by user, if any." try: for im in remove_images: index = [i for i,v in enumerate(list_images["filename"]) if str(im) in v] for key in list_images.keys(): # remove that item from all the lists list_images[key] = np.delete(list_images[key], index) except NameError: # variable remove_images not defined pass print "Create masks for images" for ii,im in enumerate(list_images["filename"]):
"--expt_key", exptimek, "--airm_key", airmassk, "--FWHM_k", seeingk] + obj_images) list_images["filename"][np.where(list_images["objname"] == obj)] = output_images print "Normalize using exposure time. " for ii, im_name in enumerate(list_images["filename"]): if list_images["type"][ii] in ["cig", "clusters"]: tt = float(utils.get_from_header(im_name, exptimek)) newname = utils.add_suffix_prefix(im_name, suffix="-t") mssg = "Normalized to exptime (divided by " + str(tt) + ")" arith_images.main(arguments=["--output", newname, "--message", mssg, "--mask_key", "MASK", im_name, "/", str(tt)]) mssg = "Before normalizing: " + str(tt) # Update values for the exptime, the sky, the sky std... utils.header_update_keyword(newname, exptimek, 1, mssg) ss = float(utils.get_from_header(im_name, skyk)) utils.header_update_keyword(newname, skyk, ss/tt) ss_std = float(utils.get_from_header(im_name, sky_stdk)) utils.header_update_keyword(newname, sky_stdk, ss_std/tt) list_images["filename"][ii] = newname print "Correct for atmospheric extinction." for ii, im_name in enumerate(list_images["filename"]): if list_images["type"][ii] in ["cig", "clusters", "standards"]: airmass = float(utils.get_from_header(im_name, airmassk)) correcting_factor = 10**(ext_coeff * airmass / 2.5) newname = utils.add_suffix_prefix(im_name, suffix="-e") mssg = "Corrected from atmospheric extinction. Coefficient: " + str(ext_coeff) arith_images.main(arguments=["--output", newname,
def arith(args): output_list = [] for operand1, operand2 in zip(args.input1, args.input2): # Read inputs as masked arrays or numbers (input2 might be a scalar) im1 = utils.read_image_with_mask(operand1, args.mask_key) try: # if second operand is a number we still need it in a masked array value2 = numpy.ma.array([float(operand2)],mask=[0]) except (ValueError,TypeError): value2 = utils.read_image_with_mask(operand2, args.mask_key) # If --keyword was passed, read from the header of the image the value and use it, instead of the full image if args.keyword: value2 = numpy.ma.array( fits.getval(operand2, args.keyword) ) # Do the actual operation. Result is a masked array which masks # any pixel if it is masked either in im1 or in im2. operations = {"+":operator.add, "-":operator.sub, "*":operator.mul, "/":operator.div, "**":operator.pow} # Case of mean or median for Input2 if args.median == True: value2 = numpy.ma.median(value2) elif args.mean == True: value2 = numpy.ma.mean(value2) oper = operations[args.operation[0]] result = numpy.zeros_like(im1) result.data[:] = oper(im1.data, value2.data) # Actual operation of images result.mask[:] = im1.mask | value2.mask # If any is masked, result is masked as well # If args.fill_val is present, use it if args.fill_val: result.data[:] = result.filled(float(args.fill_val)) # If output exists use it, otherwise use the input. Prefix/suffix might # modify things in next step. if args.output != '': outpt = os.path.abspath(args.output) else: outpt = os.path.abspath(operand1) # Separate (path, file) and (file root, extensions). Then build new name. outdir, outfile = os.path.split(outpt) outfile_root, outfile_ext = re.match(r'(^.*?)(\..*)', outfile).groups() outpt = os.path.join(outdir, args.prefix + outfile_root + args.suffix + outfile_ext) # Now name for the mask, if the name exists, use it, otherwise build it. if args.mask_name != "": mask_name = args.mask_name else: mask_name = outpt + ".msk" # Prepare a header starting from input1 header hdr_im = fits.getheader(operand1) name2 = os.path.split(operand2)[1] if args.median: # if single number because median used name2 = "median(" + name2 + ") (" + str(value2) + ")" elif args.mean: # if single number because mean used name2 = "mean(" + name2 + ") (" + str(value) + ")" # Write a HISTORY element to the header #hdr_im.add_history(" - Operation performed: " + image1 + " " + # args.operation[0] + " " + name2) try: hdr_mask = fits.PrimaryHDU(result.mask.astype(int)).header except: pass hdr_mask.add_history("- Mask corresponding to image: " + outpt) # Update the keyword if asked by user if args.update and args.keyword: old_keyword = hdr_im[args.keyword] hdr_im[args.keyword] = (oper(old_keyword, value2.data), "Before: {0}".format(old_keyword) ) # Now save the resulting image and mask if os.path.isfile(outpt): os.remove(outpt) if os.path.isfile(mask_name): os.remove(mask_name) fits.writeto(outpt, result.data, header=hdr_im) fits.writeto(mask_name, result.mask.astype(numpy.int0), header=hdr_mask) utils.header_update_keyword(outpt, "MASK", os.path.abspath(mask_name), comment="Name of mask image. ") output_list.append(outpt) return output_list
"--expt_key", exptimek, "--airm_key", airmassk, "--FWHM_k", seeingk] + obj_images) list_images["filename"][np.where(list_images["objname"] == obj)] = output_images print "Normalize using exposure time. " for ii, im_name in enumerate(list_images["filename"]): if list_images["type"][ii] in ["cig", "clusters"]: tt = float(utils.get_from_header(im_name, exptimek)) newname = utils.add_suffix_prefix(im_name, suffix="-t") mssg = "Normalized to exptime (divided by " + str(tt) + ")" arith_images.main(arguments=["--output", newname, "--message", mssg, "--mask_key", "MASK", im_name, "/", str(tt)]) mssg = "Before normalizing: " + str(tt) # Update values for the exptime, the sky, the sky std... utils.header_update_keyword(newname, exptimek, 1, mssg) ss = float(utils.get_from_header(im_name, skyk)) utils.header_update_keyword(newname, skyk, ss/tt) ss_std = float(utils.get_from_header(im_name, sky_stdk)) utils.header_update_keyword(newname, skyk, ss_std/tt) list_images["filename"][ii] = newname print "Correct for atmospheric extinction." for ii, im_name in enumerate(list_images["filename"]): if list_images["type"][ii] in ["cig", "clusters", "standards"]: airmass = float(utils.get_from_header(im_name, airmassk)) correcting_factor = 10**(ext_coeff * airmass / 2.5) newname = utils.add_suffix_prefix(im_name, suffix="-e") mssg = "Corrected from atmospheric extinction. Coefficient: " + str(ext_coeff) arith_images.main(arguments=["--output", newname,