def create_mosaic(file_list, inputimage, outputDir, outName, config, useweight=False, verbose='NORMAL', astrometry=False): """Create a mosaic of images""" np.savetxt('mosaic.list', file_list, fmt='%s') mosaic_name = outputDir + outName # Get pixel scale from input image header header = fits.getheader(inputimage) try: pixScale = abs(header['CDELT1']) except Exception: try: pixScale = abs(header['CD1_1']) except Exception: print( 'Pixel scale could not be found in fits header.\n Expected keyword: CDELT1 or CD1_1' ) pixScale = pixScale * 3600 #print (inputimage, pixScale) crval1 = header['CRVAL1'] crval2 = header['CRVAL2'] #print (crval1, crval2) #print (header['CRPIX1'], header['CRPIX2']) imagesize = [header['NAXIS1'], header['NAXIS2']] # Force reference pixel to be in the center # File name to store the common header that will be shared by all # images in filelist point = 'registration' # Delete if already exists rm_p(point + '.head') # First run swarp to create a .head file containing the shared header subprocess.call(['swarp', '-HEADER_ONLY', 'Y', '-IMAGEOUT_NAME', \ point + '.head' , '-VERBOSE_TYPE', verbose] + [inputimage]) # Some keywords manipulation using sed subprocess.call(['sed', '-i', \ 's/MJD-OBS/COMMENT/; s/EXPTIME/COMMENT/; s/GAIN /COMMENT/; s/SATURATE/COMMENT /', \ point + '.head']) imalists = ['@' + 'mosaic.list'] # Remove mosaic if already exists rm_p(mosaic_name + '.fits') if 'mask' in mosaic_name: subBackground = 'N' else: subBackground = 'Y' # Copy the common header in the .head file # So that it is read by sawrp for each image shutil.copy(point + '.head', mosaic_name + '.head') if useweight: subprocess.call(['swarp', '-IMAGEOUT_NAME', mosaic_name + '.fits', \ '-WEIGHTOUT_NAME', mosaic_name + '.weight.fits', \ '-VERBOSE_TYPE', verbose] + imalists) else: subprocess.call(['swarp', '-IMAGEOUT_NAME', mosaic_name + '.fits',\ '-SUBTRACT_BACK', subBackground, \ '-COMBINE', 'Y', \ '-BACK_SIZE', '128', \ '-BACK_FILTERSIZE', '3',\ #'-CENTER_TYPE', 'MANUAL', \ #'-CENTER', '%s, %s' % (crval1,crval2), \ '-RESAMPLE', 'Y',\ '-RESAMPLING_TYPE', 'LANCZOS3',\ #'-RESAMPLING_TYPE', 'BILINEAR',\ '-PIXELSCALE_TYPE', 'MANUAL', \ '-PIXEL_SCALE', str(pixScale), \ #'-IMAGE_SIZE', '%s, %s' % (imagesize[0], imagesize[1]), \ '-OVERSAMPLING', '0',\ '-COMBINE_TYPE', 'MEDIAN', \ '-COPY_KEYWORDS', ' PIXEL_SCALE', \ '-VERBOSE_TYPE', verbose] + imalists) rm_p(mosaic_name + '.head') # Perform astrometric calibration of the mosaic with scamp if astrometry: scamp(mosaic_name + '.fits', config, useweight=False, CheckPlot=False, verbose=verbose) rm_p('mosaic.list') rm_p('swarp.xml') rm_p(point + '.head') return True
def registration(filelist, config, resultDir="", reference=None, useweight=False, gain=1, normalise_exp=True, verbose="NORMAL"): """Register images""" # Initialise lists used for creating output astropy table inim_list = [] refim_list = [] mask_list = [] XY_lim = [] in_lo = [] in_up = [] ref_lo = [] ref_up = [] gain_in = [] gain_ref = [] for i in range(len(filelist)): inim = filelist[i][0] refim = filelist[i][1] refim_mask = filelist[i][2] # Create list of images to register files = [inim, refim] if refim_mask is not None: files = files + [refim_mask] # Save them in a file to give it as an argument to swarp np.savetxt("register.list", files, fmt="%s") # Get pixel scale from input image header header = fits.getheader(inim) pixScale = abs(float(header["CDELT1"])) * 3600 imalists = ["@" + "register.list"] # File name to store the common header that will be shared by all # images in filelist point = "registration" # Delete if already exists rm_p(point + ".head") # First run swarp to create a .head file containing the shared header subprocess.call([ "swarp", "-HEADER_ONLY", "Y", "-IMAGEOUT_NAME", point + ".head", "-GAIN_DEFAULT", str(gain), # '-VERBOSE_TYPE', verbose] + imalists) "-VERBOSE_TYPE", verbose, ] # + [inim] + [refim]) # Some keywords manipulation using sed subprocess.call([ "sed", "-i", "s/MJD-OBS/COMMENT/; s/EXPTIME/COMMENT/; s/GAIN /COMMENT/; s/SATURATE/COMMENT /", point + ".head", ]) outFiles = [] # Run swarp to perform the registration on each image in filelist for j, ima in enumerate(files): path, filename_ext = os.path.split(ima) epoch = resultDir + os.path.splitext(filename_ext)[0] + \ "_reg_%s" % i outFiles.append(epoch + ".fits") if "mask" in ima: subBackground = "N" else: subBackground = "Y" # use weight for PS1 image if reference == 'ps1' and 'rings_v3_skycell' in ima and \ 'mask' not in ima: # weight_type = "MAP_WEIGHT" # weight_type = "MAP_VARIANCE" weight_type = "MAP_RMS" # weight_type = "NONE" weight_name = path + '/' + \ os.path.splitext(filename_ext)[0] + \ ".weight.fits" else: weight_type = "NONE" weight_name = path + '/' + \ os.path.splitext(filename_ext)[0] + \ ".weight.fits" # Copy the common header in the .head file # So that it is read by sawrp for each image shutil.copy(point + ".head", epoch + ".head") # Use bilinear to avoid artefact, but worst for # noise so would need to check in more details. subprocess.call([ "swarp", "-IMAGEOUT_NAME", epoch + ".fits", "-WEIGHT_TYPE", weight_type, "-WEIGHT_IMAGE", weight_name, "-WEIGHTOUT_NAME", '.weight.fits', # Arbitrary threshold. # Pixels at the edsge after resampling are 0 so # it is enough here "-WEIGHT_THRESH", "0.1", "-RESCALE_WEIGHTS", "N", # '-GAIN_DEFAULT', str(gain), "-FSCALE_KEYWORD", "NONE", "-FSCALE_DEFAULT", "1, 1", "-SUBTRACT_BACK", subBackground, "-COMBINE", "Y", "-COMBINE_TYPE", "MEDIAN", "-BACK_SIZE", "128", "-BACK_FILTERSIZE", "3", "-RESAMPLE", "Y", "-RESAMPLE_DIR", resultDir, "-RESAMPLE_SUFFIX", '_test.fits', "-PIXELSCALE_TYPE", "MANUAL", "-PIXEL_SCALE", str(pixScale), # '-CENTER', '%s, %s' % (header['CRVAL1'], # header['CRVAL2']), # '-RESAMPLING_TYPE', 'LANCZOS3', "-RESAMPLING_TYPE", "BILINEAR", # '-RESAMPLING_TYPE', 'NEAREST', "-OVERSAMPLING", "0", "-VERBOSE_TYPE", verbose, "-COPY_KEYWORDS", "FILTER, DATE-OBS", ] + [ima]) # replace borders with NaNs in ref image if there are # any that are == 0, # hdulist=fits.open(epoch + '.fits') # hdulist[0].data[hdulist[0].data==0]=np.nan # hdulist.writeto(epoch + '.fits',overwrite=True) rm_p(epoch + ".head") rm_p(point + ".head") rm_p("register.list") rm_p("coadd.weight.fits") rm_p('swarp.xml') inim_regist = outFiles[0] refim_regist = outFiles[1] maskim_regist = outFiles[2] print('Rescale and homogeneise mask maps for bad pixels.') # Rescale flux to 1s # In the future can try to rescale flux of ref image # to match input image. if normalise_exp: rescale_flux(inim_regist) rescale_flux(refim_regist) # Set masked pixels to same value mask_pix = flag_bad_pixels(inim_regist, mask_ref=maskim_regist, value=1e-30) # Update mask map _ = flag_bad_pixels(maskim_regist, value=1e8, mask_map=mask_pix) # Apply mask on ref data _ = flag_bad_pixels(refim_regist, mask_ref=maskim_regist, value=1e-30) print('Remove bad pixels on the edge.') # Take only part of image with data # This will decrease image size and speed up the substraction # Might also avoid probelm with masked values, depending on how # good they are deal with in hotpants. limits = keep_useful_area(inim_regist, image_ref=refim_regist) _ = keep_useful_area(maskim_regist, limits_force=limits) # Perform a second time, as the edge are not straight, we can still # remove some pixels after the first cut. limits = keep_useful_area(inim_regist, image_ref=refim_regist) _ = keep_useful_area(maskim_regist, limits_force=limits) # Get info to tune hotpants parameters filelist_regist = [inim_regist, refim_regist, maskim_regist] hotpants_info = get_hotpants_info(filelist_regist, config, verbose) inim_list.append(outFiles[0]) refim_list.append(outFiles[1]) mask_list.append(outFiles[2]) XY_lim.append(hotpants_info[0]) in_lo.append(hotpants_info[1][0]) in_up.append(hotpants_info[1][1]) ref_lo.append(hotpants_info[1][2]) ref_up.append(hotpants_info[1][3]) gain_in.append(hotpants_info[2][0]) gain_ref.append(hotpants_info[2][1]) info = Table( [ inim_list, refim_list, mask_list, XY_lim, in_lo, in_up, ref_lo, ref_up, gain_in, gain_ref, ], names=[ "inim", "refim", "mask", "XY_lim", "in_lo", "in_up", "ref_lo", "ref_up", "gain_in", "gain_ref", ], ) return info
def create_ps1_mosaic(file_list, inputimage, outputDir, config, band, useweight=False, verbose="NORMAL"): """Create a single mosaic of PS1 image using swarp""" _, filenameInput = os.path.split(inputimage) # Create list of mask fits ima_list = [ima for ima in file_list if "_mask" not in ima] mask_list = [ima for ima in file_list if "_mask" in ima] np.savetxt("mosaic.list", ima_list, fmt="%s") np.savetxt("mask.list", mask_list, fmt="%s") imagefiles = [ outputDir + os.path.splitext(filenameInput)[0] + "_ps1_mosaic", outputDir + os.path.splitext(filenameInput)[0] + "_ps1_mosaic_mask", ] # Get pixel scale from input image header header = fits.getheader(inputimage) try: pixScale = abs(header["CDELT1"]) except Exception: try: pixScale = abs(header["CD1_1"]) except Exception: print("Pixel scale could not be found in fits header.\n" "Expected keyword: CDELT1 or CD1_1") pixScale = pixScale * 3600 # print (inputimage, pixScale) crval1 = header["CRVAL1"] crval2 = header["CRVAL2"] # print (crval1, crval2) # print (header['CRPIX1'], header['CRPIX2']) imagesize = [header["NAXIS1"], header["NAXIS2"]] # Force reference pixel to be in the center # File name to store the common header that will be shared by all # images in filelist point = "registration" # Delete if already exists rm_p(point + ".head") # First run swarp to create a .head file containing the shared header subprocess.call([ "swarp", "-HEADER_ONLY", "Y", "-IMAGEOUT_NAME", point + ".head", "-VERBOSE_TYPE", verbose, ] + [inputimage]) # Some keywords manipulation using sed subprocess.call([ "sed", "-i", "s/MJD-OBS/COMMENT/; s/EXPTIME/COMMENT/; s/GAIN /COMMENT/; s/SATURATE/COMMENT /", point + ".head", ]) imalists = [["@" + "mosaic.list"], ["@" + "mask.list"]] for i, imagefile in enumerate(imagefiles): # Remove mosaic if already exists rm_p(imagefile + ".fits") if "mask" in imagefile: subBackground = "N" else: subBackground = "Y" # Copy the common header in the .head file # So that it is read by sawrp for each image shutil.copy(point + ".head", imagefile + ".head") if useweight: subprocess.call([ "swarp", "-IMAGEOUT_NAME", imagefile + ".fits", "-WEIGHTOUT_NAME", imagefile + ".weight.fits", "-VERBOSE_TYPE", verbose, ] + imalists[i]) else: subprocess.call([ "swarp", "-IMAGEOUT_NAME", imagefile + ".fits", "-SUBTRACT_BACK", subBackground, "-COMBINE", "Y", "-BACK_SIZE", "128", "-BACK_FILTERSIZE", "3", # '-CENTER_TYPE', 'MANUAL', # '-CENTER', '%s, %s' % (crval1,crval2), "-RESAMPLE", "Y", #"-RESAMPLING_TYPE", "LANCZOS3", '-RESAMPLING_TYPE', 'BILINEAR', "-PIXELSCALE_TYPE", "MANUAL", "-PIXEL_SCALE", str(pixScale), # '-IMAGE_SIZE', '%s, %s' % (imagesize[0], imagesize[1]), "-OVERSAMPLING", "0", "-COMBINE_TYPE", "MEDIAN", "-COPY_KEYWORDS", " PIXEL_SCALE", "-VERBOSE_TYPE", verbose, ] + imalists[i]) rm_p(imagefile + ".head") # Perform astrometric calibration of the mosaic with scamp scamp( imagefiles[0] + ".fits", config, useweight=False, CheckPlot=False, verbose=verbose, ) # replace pixels == 0 with NaNs. Mostly the border, saturated pixels hdulist = fits.open(imagefiles[0] + ".fits") hdulist[0].data[hdulist[0].data == 0] = np.nan """ # Add header hdulist[0].header['FILTER'] = band hdulist[0].header['PHOT_C'] = 25 hdulist[0].header['PHOT_K'] = 0 hdulist[0].header['PHOTFLAG'] = 'T' hdulist[0].header['EXPTIME'] = 1 """ hdulist[0].header["GAIN"] = 1 hdulist[0].header["EXPTIME"] = 1 hdulist[0].header.remove("SATURATE") hdulist.writeto(imagefiles[0] + ".fits", overwrite=True) # Create a mask to propagate the nan pixels hdulist = fits.open(imagefiles[1] + ".fits") hdulist[0].data[hdulist[0].data > 0] = 1 hdulist[0].data[np.isnan(hdulist[0].data)] = 1 hdulist.writeto(imagefiles[1] + ".fits", overwrite=True) # for ima in file_list: # rm_p(ima) # for ima in mask_list: # rm_p(ima) rm_p("mosaic.list") rm_p("mask.list") rm_p("swarp.xml") rm_p(point + ".head") # rm_p('coadd.weight.fits') # Add extension to files imagefiles = [i + ".fits" for i in imagefiles] return imagefiles
def psfex(filename, config, useweight=False, verbose="NORMAL", outLevel=0, outDir=''): """Compute PSF in astronomical images""" FWHM_list = [] # imagelist=glob.glob(path+'/*.fits') imagelist = np.atleast_1d(filename) for ima in imagelist: print("\nRunning psfex to estimate FWHM in %s" % ima) root = os.path.splitext(ima)[0] if useweight: weight = root + ".weight.fits" subprocess.call([ "sex", ima, "-c", config["psfex"]["sextractor"], "-WEIGHT_IMAGE", weight, "-VERBOSE_TYPE", verbose, "-PARAMETERS_NAME", config["psfex"]["param"], "-FILTER_NAME", config['sextractor']['convFilter'], ]) else: subprocess.call([ "sex", ima, "-c", config["psfex"]["sextractor"], "-VERBOSE_TYPE", verbose, "-PARAMETERS_NAME", config["psfex"]["param"], "-FILTER_NAME", config['sextractor']['convFilter'], ]) cat = "preppsfex.cat" subprocess.call([ "psfex", cat, "-c", config["psfex"]["conf"], "-VERBOSE_TYPE", verbose ]) rm_p(cat) # Delete files depending on the required level of output files mv_p("snap_preppsfex.fits", root + "_psf.fits") # Get the mean PSF FWHM in pixels with open("psfex.xml") as fd: doc = xmltodict.parse(fd.read()) FWHM_stats = doc["VOTABLE"]["RESOURCE"]["RESOURCE"]["TABLE"][0][ "DATA"]["TABLEDATA"]["TR"]["TD"][20:23] FHWM_min = float(FWHM_stats[0]) FHWM_mean = float(FWHM_stats[1]) FHWM_max = float(FWHM_stats[2]) print("\nFWHM min: %.2f pixels" % FHWM_min) print("FWHM mean: %.2f pixels" % FHWM_mean) print("FWHM max: %.2f pixels\n" % FHWM_max) # Get number of psf snapshot per axis psf_snaps = os.popen("sed -n '/PSFVAR_NSNAP/p' %s" % config["psfex"]["conf"]).read() nb_snaps = psf_snaps.split()[1] # Add info to the header hdulist = fits.open(root + "_psf.fits") hdr = hdulist[0].header hdr["FWHMMIN"] = str(FHWM_min) hdr["FWHMMEA"] = str(FHWM_mean) hdr["FWHMMAX"] = str(FHWM_max) hdr["PSF_NB"] = str(nb_snaps) hdulist.writeto(root + "_psf.fits", overwrite=True) FWHM_list.append(FHWM_mean) mv_p("preppsfex.psf", root + ".psf") rm_p("preppsfex.psf") rm_p("psfex.xml") return FWHM_list
def resample_ps1(filename, inputDir, config, useweight=False, verbose="NORMAL"): """Resample PS1 image to the telescope resolution""" pixScale = config['pixScale'][0] basename = os.path.splitext(filename)[0] if useweight: subprocess.call( [ "swarp", inputDir+filename, "-IMAGEOUT_NAME", inputDIr + basename + \ '_%s' % config['telescope'] + ".fits", "-WEIGHTOUT_NAME", inputDIr + epoch + \ '_%s' % config['telescope'] + ".weight.fits", "-VERBOSE_TYPE", verbose, ] ) else: # Use bilinear to avoid artefact, but worst for # noise so would need to check in more details. subprocess.call( [ "swarp", inputDir+filename, "-IMAGEOUT_NAME", inputDir + basename + \ '_%s' % config['telescope'] + ".fits", # '-GAIN_DEFAULT', str(gain), "-FSCALE_KEYWORD", "NONE", "-FSCALE_DEFAULT", "1, 1", "-SUBTRACT_BACK", "N", "-COMBINE", "N", "-BACK_SIZE", "128", "-BACK_FILTERSIZE", "3", "-RESAMPLE", "Y", "-RESAMPLE_DIR", inputDir, "-RESAMPLE_SUFFIX", '_%s.fits' % config['telescope'], "-PIXELSCALE_TYPE", "MANUAL", "-PIXEL_SCALE", str(pixScale), # '-CENTER', '%s, %s' % (header['CRVAL1'], # header['CRVAL2']), # '-RESAMPLING_TYPE', 'LANCZOS3', "-RESAMPLING_TYPE", "BILINEAR", # '-RESAMPLING_TYPE', 'NEAREST', "-OVERSAMPLING", "0", "-COMBINE_TYPE", "MEDIAN", "-VERBOSE_TYPE", verbose, "-COPY_KEYWORDS", "FILTER, EXPTIME, SATURATE", ] ) rm_p('swarp.xml') rm_p("coadd.weight.fits") rm_p(inputDir + filename) if 'mask' in basename: # Add the new pixels created on the edge after resampling to # the mask hdulist1 = fits.open(inputDir + basename + \ '_%s' % config['telescope'] + ".weight.fits") zero_pix = hdulist1[0].data == 0 hdulist1.close() outName = inputDir + basename + \ '_%s' % config['telescope'] + ".fits" hdulist2 = fits.open(outName) # Put high value hdulist2[0].data[zero_pix] = 1e8 hdulist2.writeto(outName, overwrite=True) return True