def make_calring(hdu, method=None, thresh=5, niter=3, conv=0.05, minsize=10, axc=None, ayc=None): """Open each image and measure the position of the ring including its center and radius Return the information about the calibration ring """ # setup the data data = hdu[0].data # extract the time and convert to decimal hours utctime = saltkey.get("UTC-OBS", hdu[0]) utctime = salttime.time_obs2hr((utctime.split()[-1])) # determine the correct etalon and information to extract etstate = saltkey.get("ET-STATE", hdu[0]) if etstate.count("S2"): etz = saltkey.get("ET1Z", hdu[0]) elif etstate.count("S3"): etz = saltkey.get("ET2Z", hdu[0]) else: msg = "This etalon state is not currently supported" raise SaltError(msg) # extract the ring ring_list = findrings(data, thresh=thresh, niter=niter, minsize=minsize, axc=axc, ayc=ayc) # assumes only one ring in the data set ring = ring_list[0] # determine the center and radius of the ring if method is not None: ring = findcenter(data, ring, method, niter=niter, conv=conv) if axc: ring.xc = axc if ayc: ring.yc = ayc return ring.xc, ring.yc, ring.prad, ring.prad_err, etz, utctime
def fit_rings(file, trim_rad=470, disp=None): """ main routine to take a FITS file, read it in, azimuthally average it, find the rings, and then fit voigt profiles to them. Parameters ---------- file : string filename of FITS file to analyze trim_rad : int maximum radial extent of profile disp : boolean to control DS9 display of image Returns ------- list containing: boolean - success of finding peaks or not numpy array - wavelengths of profile numpy array - radial flux profile data numpy array - best-fit radial flux profile dict - parameters of best-fit """ hdu = pyfits.open(file) (data, header) = (hdu[0].data, hdu[0].header) etalon = int(header['ET-STATE'].split()[3]) etwave_key = "ET%dWAVE0" % etalon name_key = "ET%dMODE" % etalon etname = header[name_key] cenwave = float(header[etwave_key]) binning = int(header['CCDSUM'].split()[0]) if header['OBSMODE'] != 'FABRY-PEROT': return False, np.empty(1), np.empty(1), np.empty(1), np.empty(1) ysize, xsize = data.shape # cut FP image down to square fp_im = data[:, (xsize - ysize) / 2: (xsize + ysize) / 2] if disp: disp.set_np2arr(fp_im, dtype=np.int32) # mask those gaps fp_im = ma.masked_less_equal(data[:, (xsize - ysize) / 2: (xsize + ysize) / 2], 0.0) # define center based on FP ghost imaging with special mask xc = 2054 / binning yc = 2008 / binning # print "FP ring centre should be at x= %.1f y= %.1f" % (xc+280, yc) #Find the list of rings: ring_list=findrings(data, thresh=5, niter=5, minsize=10) print "%1d ring(s) found" % (len(ring_list)) sxc=syc=0 for i in range(len(ring_list)): ring_list[i]=findcenter(data, ring_list[i], method='center') print "Ring number %1d: centre at X= %.1f, Y=%.1f, Radius= %.1f" % (i+1, ring_list[i].xc, ring_list[i].yc,ring_list[i].prad) if ring_list[i].prad > 250 and ring_list[i].prad < 390: sxc=ring_list[i].xc syc=ring_list[i].yc # xcn,ycn=find_center(fp_im,xc,yc) if sxc >0 and syc > 0: print "Found ring centre at x= %.1f y= %.1f" % (sxc, syc) xc=sxc-280 yc=syc #Xc0 and Yc0 is the ring centre for a good ring # Ted's values: xc0=802 yc0=503 #From Tim's: # xc0=800 # yc0=500 deltaX=int(3.6*(syc-yc0)) deltaY=int(4.7*((sxc)-xc0)) # print "With this new centre, recommend dX= %1d, dY=%1d" % (deltaX, deltaY) else: print "Found ring centre at x= %.1f y= %.1f" % (sxc, syc) print "Using FP ghost ring centre at x= %.1f y= %.1f" % (xc+280, yc) # we use the 4x4 version of trim_rad since the vast majority of FP # data is taken with 4x4 binning trim_rad *= 4 / binning mask_val = 0.0 f = {} if cenwave < 5200: f['MR'] = 22149.6 f['LR'] = 22795.92 f['TF'] = 24360.32 if cenwave > 6500 and cenwave < 6600: f['MR'] = 22713.0 f['LR'] = 24191.40 f['TF'] = 24360.32 if cenwave >= 6600 and cenwave < 6700: f['MR'] = 22848.0 f['LR'] = 24169.32 f['TF'] = 23830.20 if cenwave >= 6700 and cenwave < 6900: f['MR'] = 22828.92 f['LR'] = 24087.68 f['TF'] = 24553.32 if cenwave >= 7300 and cenwave < 7700: f['MR'] = 22864.06 f['LR'] = 24087.68 f['TF'] = 24553.32 else: f['MR'] = 22828.92 f['LR'] = 24400.32 f['TF'] = 24553.32 # get the radial profile and flatten it with a default QTH flat profile prof, r = FP_profile(fp_im, xc, yc, trim_rad=trim_rad, mask=mask_val) # Not flatfielding the profile as ring already flat-fielded using saltflat. # prof = flatprof(prof, binning) wave = cenwave / np.sqrt(1.0 + (r * binning / f[etname]) ** 2) # find the peaks and bail out if none found npeaks, peak_list = find_peaks(prof, width=40) if npeaks < 1: print "No peaks found." return False, np.empty(1), np.empty(1), np.empty(1), np.empty(1) print "Found %d rings at:" % npeaks cenwidth = 20 for peak in peak_list: cen_peak = centroid(prof, peak, cenwidth) if np.isnan(cen_peak): cen_peak = peak print "\t R %f" % cen_peak if disp: disp.set("regions command {circle %f %f %f # color=red}" % (xc, yc, cen_peak)) # max_r = peak_list[0] # pmax = prof[max_r] back = 250.0 fwhm = 5.0 gam = 1.0 init = [back] bounds = [(0.0, 2.0e8)] # keep brightest peak = peak_list[0] # position init.append(cenwave / np.sqrt(1.0 + (peak * binning / f[etname]) ** 2)) bounds.append((cenwave - 30, cenwave + 30)) # amplitude init.append(prof[peak]) bounds.append((0.0, 1.0e8)) # FWHM init.append(fwhm) bounds.append((0.1, 20.0)) # gamma init.append(gam) bounds.append((0.0, 5.0)) ### keep these around in case we want to try again someday # #fit = opt.fmin_slsqp(fit_func, init, args=(prof, wave), # bounds=bounds) #fit, nfeval, rc = opt.fmin_tnc(fit_func, init, args=(prof, wave), # epsilon=0.0001, # bounds=bounds, # approx_grad=True, # disp=0, # maxfun=5000) # good ol' powell method FTW fit = opt.fmin_powell(fit_func, init, args=(prof, wave), ftol=0.00001, full_output=False, disp=False) #print "Return code: %s" % opt.tnc.RCSTRINGS[rc] pars = {} fit_v = fit[0] print "\nBackground = %f" % fit[0] pars['Background'] = fit[0] pars['R'] = [] pars['Amplitude'] = [] pars['Gauss FWHM'] = [] pars['Gamma'] = [] pars['FWHM'] = [] for i in range(1, len(fit), 4): fwhm = voigt_fwhm(fit[i + 2], fit[i + 3]) pars['R'].append(fit[i]) pars['Amplitude'].append(fit[i + 1]) pars['Gauss FWHM'].append(fit[i + 2]) pars['Gamma'].append(fit[i + 3]) pars['FWHM'].append(fwhm) fit_v = fit_v + qvoigt(wave, fit[i + 1], fit[i], fit[i + 2], fit[i + 3]) return True, wave, prof, fit_v, pars
def saltfpringfind(images, method=None, section=None, thresh=5, minsize=10, niter=5, conv=0.05, displayimage=True, clobber=False, logfile='salt.log',verbose=True): with logging(logfile,debug) as log: # Check the input images infiles = saltio.argunpack ('Input',images) #check the method method=saltio.checkfornone(method) # read in the section section=saltio.checkfornone(section) if section is None: pass else: section=saltio.getSection(section) # open each raw image file for img in infiles: #open the fits file struct=saltio.openfits(img) data=struct[0].data #determine the background value for the image if section is None: #if section is none, just use all pixels greater than zero bdata=data[data>0] else: y1,y2,x1,x2=section bdata=data[y1:y2,x1:x2] bmean, bmedian, bstd=iterstat(bdata, sig=thresh, niter=niter, verbose=False) message="Image Background Statistics\n%30s %6s %8s %8s\n%30s %5.4f %5.4f %5.4f\n" % \ ('Image', 'Mean', 'Median', 'Std',img, bmean, bmedian, bstd) log.message(message, with_stdout=verbose) mdata=data*(data-bmean>thresh*bstd) #prepare the first guess for the image ring_list=findrings(data, thresh=thresh, niter=niter, minsize=minsize) #if specified, find the center of the ring if method is not None: for i in range(len(ring_list)): ring_list[i]=findcenter(data, ring_list[i], method, niter=niter, conv=conv) #if one peak: no rings. If two peaks: one ring, if two peaks: four rings if len(ring_list)==1: msg="One ring dected in image" else: msg="%i rings found in image" % len(ring_list) log.message(message, with_stdout=verbose) if displayimage: regfile=img.replace('.fits', '.reg') if clobber and os.path.isfile(regfile): fout=saltio.delete(regfile) fout=open(regfile, 'w') fout.write("""# Region file format: DS9 version 4.1 # Filename: %s global color=green dashlist=8 3 width=1 font="helvetica 10 normal roman" select=1 highlite=1 dash=0 fixed=0 edit=1 move=1 delete=1 include=1 source=1 physical """ % img) for ring in ring_list: fout.write('circle(%f, %f, %f)\n' % (ring.xc,ring.yc,ring.prad)) fout.write('circle(%f, %f, %f)\n' % (ring.xc,ring.yc,ring.prad-3*ring.sigma)) fout.write('circle(%f, %f, %f)\n' % (ring.xc,ring.yc,ring.prad+3*ring.sigma)) fout.close() display(img, catname=regfile, rformat='reg') message = 'Ring Parameters\n%30s %6s %6s %6s\n' % ('Image', 'XC', 'YC', 'Radius') log.message(message, with_stdout=verbose) for ring in ring_list: msg='%30s %6.2f %6.2f %6.2f\n' % (img, ring.xc, ring.yc, ring.prad) log.message(msg, with_header=False, with_stdout=verbose)
def saltfpringfind(images, method=None, section=None, thresh=5, minsize=10, niter=5, conv=0.05, displayimage=True, clobber=False, logfile='salt.log', verbose=True): with logging(logfile, debug) as log: # Check the input images infiles = saltio.argunpack('Input', images) #check the method method = saltio.checkfornone(method) # read in the section section = saltio.checkfornone(section) if section is None: pass else: section = saltio.getSection(section) # open each raw image file for img in infiles: #open the fits file struct = saltio.openfits(img) data = struct[0].data #determine the background value for the image if section is None: #if section is none, just use all pixels greater than zero bdata = data[data > 0] else: y1, y2, x1, x2 = section bdata = data[y1:y2, x1:x2] bmean, bmedian, bstd = iterstat(bdata, sig=thresh, niter=niter, verbose=False) message="Image Background Statistics\n%30s %6s %8s %8s\n%30s %5.4f %5.4f %5.4f\n" % \ ('Image', 'Mean', 'Median', 'Std',img, bmean, bmedian, bstd) log.message(message, with_stdout=verbose) mdata = data * (data - bmean > thresh * bstd) #prepare the first guess for the image ring_list = findrings(data, thresh=thresh, niter=niter, minsize=minsize) #if specified, find the center of the ring if method is not None: for i in range(len(ring_list)): ring_list[i] = findcenter(data, ring_list[i], method, niter=niter, conv=conv) #if one peak: no rings. If two peaks: one ring, if two peaks: four rings if len(ring_list) == 1: msg = "One ring dected in image" else: msg = "%i rings found in image" % len(ring_list) log.message(message, with_stdout=verbose) if displayimage: regfile = img.replace('.fits', '.reg') if clobber and os.path.isfile(regfile): fout = saltio.delete(regfile) fout = open(regfile, 'w') fout.write("""# Region file format: DS9 version 4.1 # Filename: %s global color=green dashlist=8 3 width=1 font="helvetica 10 normal roman" select=1 highlite=1 dash=0 fixed=0 edit=1 move=1 delete=1 include=1 source=1 physical """ % img) for ring in ring_list: fout.write('circle(%f, %f, %f)\n' % (ring.xc, ring.yc, ring.prad)) fout.write('circle(%f, %f, %f)\n' % (ring.xc, ring.yc, ring.prad - 3 * ring.sigma)) fout.write('circle(%f, %f, %f)\n' % (ring.xc, ring.yc, ring.prad + 3 * ring.sigma)) fout.close() display(img, catname=regfile, rformat='reg') message = 'Ring Parameters\n%30s %6s %6s %6s\n' % ('Image', 'XC', 'YC', 'Radius') log.message(message, with_stdout=verbose) for ring in ring_list: msg = '%30s %6.2f %6.2f %6.2f\n' % (img, ring.xc, ring.yc, ring.prad) log.message(msg, with_header=False, with_stdout=verbose)
def fit_rings(file, trim_rad=470, disp=None): """ main routine to take a FITS file, read it in, azimuthally average it, find the rings, and then fit voigt profiles to them. Parameters ---------- file : string filename of FITS file to analyze trim_rad : int maximum radial extent of profile disp : boolean to control DS9 display of image Returns ------- list containing: boolean - success of finding peaks or not numpy array - wavelengths of profile numpy array - radial flux profile data numpy array - best-fit radial flux profile dict - parameters of best-fit """ hdu = pyfits.open(file) (data, header) = (hdu[0].data, hdu[0].header) etalon = int(header['ET-STATE'].split()[3]) etwave_key = "ET%dWAVE0" % etalon name_key = "ET%dMODE" % etalon etname = header[name_key] cenwave = float(header[etwave_key]) binning = int(header['CCDSUM'].split()[0]) if header['OBSMODE'] != 'FABRY-PEROT': return False, np.empty(1), np.empty(1), np.empty(1), np.empty(1) ysize, xsize = data.shape # cut FP image down to square fp_im = data[:, (xsize - ysize) / 2: (xsize + ysize) / 2] if disp: disp.set_np2arr(fp_im, dtype=np.int32) # mask those gaps fp_im = ma.masked_less_equal(data[:, (xsize - ysize) / 2: (xsize + ysize) / 2], 0.0) # define center based on FP ghost imaging with special mask xc = 2054 / binning yc = 2008 / binning print "FP ghost ring centre at x= %.1f y= %.1f" % (xc+280, yc) #Find the list of rings: ring_list=findrings(data, thresh=5, niter=5, minsize=10) print "%1d ring(s) found" % (len(ring_list)) sxc=syc=0 for i in range(len(ring_list)): ring_list[i]=findcenter(data, ring_list[i], method='center') print "Ring number %1d: centre at X= %.1f, Y=%.1f, Radius= %.1f" % (i+1, ring_list[i].xc, ring_list[i].yc,ring_list[i].prad) if ring_list[i].prad > 250 and ring_list[i].prad < 390: sxc=ring_list[i].xc syc=ring_list[i].yc # xcn,ycn=find_center(fp_im,xc,yc) if sxc >0 and syc > 0: print "Using centre at x= %.1f y= %.1f" % (sxc, syc) xc=sxc-280 yc=syc #Xc0 and Yc0 is the ring centre for a good ring # Ted's values: xc0=802 yc0=503 #From Tim's: # xc0=800 # yc0=500 deltaX=int(3.6*(syc-yc0)) deltaY=int(4.7*((sxc)-xc0)) print "With this new centre, recommend dX= %1d, dY=%1d" % (deltaX, deltaY) else: print "Using FP ghost ring centre at x= %.1f y= %.1f" % (xc+280, yc) # we use the 4x4 version of trim_rad since the vast majority of FP # data is taken with 4x4 binning trim_rad *= 4 / binning mask_val = 0.0 f = {} if cenwave < 5200: f['MR'] = 22149.6 f['LR'] = 22795.92 f['TF'] = 24360.32 if cenwave > 6500 and cenwave < 6600: f['MR'] = 22713.0 f['LR'] = 24191.40 f['TF'] = 24360.32 if cenwave >= 6600 and cenwave < 6700: f['MR'] = 22848.0 f['LR'] = 24169.32 f['TF'] = 23830.20 if cenwave >= 6700 and cenwave < 6900: f['MR'] = 22828.92 f['LR'] = 24087.68 f['TF'] = 24553.32 else: f['MR'] = 22828.92 f['LR'] = 24400.32 f['TF'] = 24553.32 # get the radial profile and flatten it with a default QTH flat profile prof, r = FP_profile(fp_im, xc, yc, trim_rad=trim_rad, mask=mask_val) # Not flatfielding the profile as ring already flat-fielded using saltflat. # prof = flatprof(prof, binning) wave = cenwave / np.sqrt(1.0 + (r * binning / f[etname]) ** 2) # find the peaks and bail out if none found npeaks, peak_list = find_peaks(prof, width=40) if npeaks < 1: print "No peaks found." return False, np.empty(1), np.empty(1), np.empty(1), np.empty(1) print "Found %d rings at:" % npeaks cenwidth = 20 for peak in peak_list: cen_peak = centroid(prof, peak, cenwidth) if np.isnan(cen_peak): cen_peak = peak print "\t R %f" % cen_peak if disp: disp.set("regions command {circle %f %f %f # color=red}" % (xc, yc, cen_peak)) # max_r = peak_list[0] # pmax = prof[max_r] back = 250.0 fwhm = 5.0 gam = 1.0 init = [back] bounds = [(0.0, 2.0e8)] # keep brightest peak = peak_list[0] # position init.append(cenwave / np.sqrt(1.0 + (peak * binning / f[etname]) ** 2)) bounds.append((cenwave - 30, cenwave + 30)) # amplitude init.append(prof[peak]) bounds.append((0.0, 1.0e8)) # FWHM init.append(fwhm) bounds.append((0.1, 20.0)) # gamma init.append(gam) bounds.append((0.0, 5.0)) ### keep these around in case we want to try again someday # #fit = opt.fmin_slsqp(fit_func, init, args=(prof, wave), # bounds=bounds) #fit, nfeval, rc = opt.fmin_tnc(fit_func, init, args=(prof, wave), # epsilon=0.0001, # bounds=bounds, # approx_grad=True, # disp=0, # maxfun=5000) # good ol' powell method FTW fit = opt.fmin_powell(fit_func, init, args=(prof, wave), ftol=0.00001, full_output=False, disp=False) #print "Return code: %s" % opt.tnc.RCSTRINGS[rc] pars = {} fit_v = fit[0] print "\nBackground = %f" % fit[0] pars['Background'] = fit[0] pars['R'] = [] pars['Amplitude'] = [] pars['Gauss FWHM'] = [] pars['Gamma'] = [] pars['FWHM'] = [] for i in range(1, len(fit), 4): fwhm = voigt_fwhm(fit[i + 2], fit[i + 3]) pars['R'].append(fit[i]) pars['Amplitude'].append(fit[i + 1]) pars['Gauss FWHM'].append(fit[i + 2]) pars['Gamma'].append(fit[i + 3]) pars['FWHM'].append(fwhm) fit_v = fit_v + qvoigt(wave, fit[i + 1], fit[i], fit[i + 2], fit[i + 3]) return True, wave, prof, fit_v, pars