def half_mass(image): """ This method takes an image as imput and returns a mask of the pixels contained that account for half of the total light. """ XX, YY = np.meshgrid(np.arange(image.shape[0]), np.arange(image.shape[1])) max_elem = np.max(image.shape) mask = np.zeros_like(image, dtype=bool) com = np.array(centroid_com(image), dtype=int) in_pos = np.array([[com[0], com[1]]]) mask[com[0], com[1]] = True mass = np.sum(image[mask]) all_light = np.sum(image) while mass < all_light / 2: candidates_pos = [] candidates_mass = [] for ii, pos_i in enumerate(in_pos): # print(ii, pos_i) new_pos = pos_i + [0, 1] new_pos = new_pos.clip(min=0, max=max_elem) if (np.sum((in_pos - new_pos[np.newaxis, :])**2, axis=1).all() != 0) & (new_pos[0] < max_elem) & (new_pos[1] < max_elem): candidates_pos.append(new_pos) candidates_mass.append(image[new_pos[0], new_pos[1]]) new_pos = pos_i + [1, 0] new_pos = new_pos.clip(min=0, max=max_elem) if (np.sum((in_pos - new_pos[np.newaxis, :])**2, axis=1).all() != 0) & (new_pos[0] < max_elem) & (new_pos[1] < max_elem): candidates_pos.append(new_pos) candidates_mass.append(image[new_pos[0], new_pos[1]]) new_pos = pos_i + [0, -1] new_pos = new_pos.clip(min=0, max=max_elem) if (np.sum((in_pos - new_pos[np.newaxis, :])**2, axis=1).all() != 0) & (new_pos[0] < max_elem) & (new_pos[1] < max_elem): candidates_pos.append(new_pos) candidates_mass.append(image[new_pos[0], new_pos[1]]) new_pos = pos_i + [-1, 0] new_pos = new_pos.clip(min=0, max=max_elem) if (np.sum((in_pos - new_pos[np.newaxis, :])**2, axis=1).all() != 0) & (new_pos[0] < max_elem) & (new_pos[1] < max_elem): candidates_pos.append(new_pos) candidates_mass.append(image[new_pos[0], new_pos[1]]) try: best = np.argmax(candidates_mass) except: break in_pos = np.vstack((in_pos, candidates_pos[best])) mass += candidates_mass[best] mask[candidates_pos[best][0], candidates_pos[best][1]] = True # print(candidates_mass) print('% Completion... ', mass / (all_light / 2)) # if ii==3: # break return mask
def centroid_com(self): """ This function ... :return: """ return centroid_com(self._data)
def __init__(self, image): self.image = image self.image[np.isnan(self.image)] = 0 # self.image /= np.nanmax(self.image) self.XX, self.YY = np.meshgrid(np.arange(self.image.shape[0]), np.arange(self.image.shape[1])) x_o_g, y_o_g = centroid_com(self.image) self.com = np.array([x_o_g, y_o_g]) amplitude_g = np.median(self.image[int(x_o_g) - 10:int(x_o_g) + 10, int(y_o_g) - 10:int(y_o_g) + 10]) reff_g = (self.image.shape[0] + self.image.shape[1]) / 4 n_g = 2. ellip_g = 0.5 theta_g = 0. self.initial_guess = [ amplitude_g, reff_g, n_g, x_o_g, y_o_g, ellip_g, theta_g ] print('Initial guess:\n - Amplitude={:.2f}\n - Reff={:.2f}\n - n_sersic={:.1f}\n - COM=({:.1f},{:.1f})\n - ellip={:.2f}\n - theta={:.2}'.\ format(self.initial_guess[0], self.initial_guess[1], self.initial_guess[2], self.initial_guess[3], self.initial_guess[4], self.initial_guess[5], self.initial_guess[6]) )
def compute_com(self): try: self.median_flux except: self.get_normalization_flux() com = centroid_com(self.median_flux) self.center_of_mass = com return com
def centroids(self, aperture_mask=None): if aperture_mask is None: aperture_mask = self.aperture_mask() # Initialize return values xbar = np.zeros(self.n_cadences) ybar = np.zeros(self.n_cadences) flux = self.flux for i in range(self.n_cadences): xbar[i], ybar[i] = centroid_com(flux[i], mask=~aperture_mask) return xbar, ybar
def centroid_com(self, cutoff=None): """ This function ... :param cutoff: sigma level for cutting off the data :return: """ # Create cutoff mask if cutoff: mask = self.create_sigma_mask(cutoff, invert=True) else: mask = None # Calculate centroid return centroid_com(self._data, mask=mask)
def Center_OfMass(IMG, results, options): """ Compute the pixel location of the galaxy center using a light weighted center of mass. Looking at 50 seeing lengths around the center of the image (images should already be mostly centered), finds the average light weighted center of the image. """ current_center = {'x': IMG.shape[1]/2, 'y': IMG.shape[0]/2} if 'ap_guess_center' in options: current_center = deepcopy(options['ap_guess_center']) logging.info('%s: Center initialized by user: %s' % (options['ap_name'], str(current_center))) if 'ap_set_center' in options: logging.info('%s: Center set by user: %s' % (options['ap_name'], str(options['ap_set_center']))) return IMG, {'center': deepcopy(options['ap_set_center'])} # Create mask to focus centering algorithm on the center of the image ranges = [[max(0,int(current_center['x'] - 50*results['psf fwhm'])), min(IMG.shape[1],int(current_center['x'] + 50*results['psf fwhm']))], [max(0,int(current_center['y'] - 50*results['psf fwhm'])), min(IMG.shape[0],int(current_center['y'] + 50*results['psf fwhm']))]] centralize_mask = np.ones(IMG.shape, dtype = bool) centralize_mask[ranges[1][0]:ranges[1][1], ranges[0][0]:ranges[0][1]] = False try: x, y = centroid_com(IMG - results['background'], mask = centralize_mask) current_center = {'x': x, 'y': y} except: logging.warning('%s: 2D Gaussian center finding failed! using image center (or guess).' % options['ap_name']) # Plot center value for diagnostic purposes if 'ap_doplot' in options and options['ap_doplot']: plt.imshow(np.clip(IMG - results['background'],a_min = 0, a_max = None), origin = 'lower', cmap = 'Greys_r', norm = ImageNormalize(stretch=LogStretch())) plt.plot([y],[x], marker = 'x', markersize = 10, color = 'y') plt.savefig('%scenter_vis_%s.jpg' % (options['ap_plotpath'] if 'ap_plotpath' in options else '', options['ap_name'])) plt.close() logging.info('%s Center found: x %.1f, y %.1f' % (options['ap_name'], x, y)) return IMG, {'center': current_center, 'auxfile center': 'center x: %.2f pix, y: %.2f pix' % (current_center['x'], current_center['y'])}
def main(file, target_dir, plot_results=False, overwrite=False): fwhm = 1.70 nsigma = 1.0 size = 64 verbose = False psf_resamp = 2 debug = 0 if(re.search('psf.fits',file) != None): print("**** skipping PSF file:", file) return print("file is ", file," target_dir is ", target_dir, " plot_results is ", plot_results," overwrite is ", overwrite) (image, header, samp_rate, filter, pixel_scale) = read_image(file, debug) print("at line ", lineno(), " Filter is ", filter," pixel_scale ", pixel_scale) if(filter != "null" and pixel_scale == 0.0): pixel_scale = scale_from_filter(filter) if(filter == "null" and pixel_scale == 0.0): pixel_scale, filter = scale_from_filename(file) print("at line ", lineno(), " Filter is ", filter," pixel_scale ", pixel_scale) if(filter == "null"): print("at line ", lineno(), " no filter keyword for file ", file) return junk = re.split('/', file) if(target_dir != None): new_file = target_dir + junk[len(junk)-1] else: new_file = file print("Filter is ", filter) print("oversampling rate is ", samp_rate) print("pixel_scale is " , pixel_scale) # psf_output = re.sub('.fits','_'+filter+'_psf.fits',new_file) output_exists = exists(psf_output) if(output_exists and overwrite == False) : print ("file has already been reduced ", file, psf_output) return else: print("reducing ", file) print("psf_output is ", psf_output) # #-----------------------------------------------------------------------# # create a pixel mask for NaNs and noughts mask_file = re.sub('.fits','_mask.fits',new_file) zero_mask = numpy.where(image == 0,0,1) nan_mask = numpy.where(np.isnan(image),0,1) zero_mask = nan_mask * zero_mask fits.writeto(mask_file,zero_mask,header=header,overwrite=True) # photutils uses boolean masks nan_mask = numpy.where(zero_mask == 0,True,False) # detect sources using the photutils algorithms sources = find_objects(image, fwhm, nsigma, debug, mask=nan_mask) if(verbose == True) : for col in sources.colnames: sources[col].info.format = "%.8g" print(sources) # # Write regions file for detected stars # np.xc = sources['xcentroid'] np.yc = sources['ycentroid'] # output = open('test1.reg','w') for ii in range(0,len(np.xc)): line = 'point '+str(np.xc[ii]+1)+' '+str(np.yc[ii]+1) +' # point=diamond color=blue\n' output.write(line) # output.close() # # Fit average PSF, following: # https://photutils.readthedocs.io/en/stable/epsf.html # # Make cuttouts # xx = sources['xcentroid'] yy = sources['ycentroid'] epsf, fitted_stars = calculate_psf_template(image, xx, yy, size, psf_resamp, maxiters=3, mask=nan_mask,plot_candidates=False) comment = 'Filter' header.set('FILTER',filter, comment) comment = 'oversampling rate == 1 unless WEBBPSF template' header.set('OVERSAMP',samp_rate, comment) comment = 'arcsec/pixel, corrected for DET_SAMP ' header.set('PIXELSCL',pixel_scale/psf_resamp, comment) comment = 'oversampling rate used in EPSFBuilder' header.set('DET_SAMP',psf_resamp, comment) fits.writeto(psf_output,epsf.data,header=header,overwrite=True) # # PSF profile using the average PSF (may not be the best idea) # (xc, yc) = centroid_com(epsf.data) # print ("centroid_com ", (xc, yc)) print("ouput is ", psf_output) print("PSF resampling rate ", psf_resamp, "shape", epsf.data.shape, "centroid", xc, yc) radii = np.arange(1, 15, dtype=float) radii = radii * samp_rate print("array ", radii) radii = radii.tolist() if(plot_results == True or plot_results == 1) : norm = simple_norm(epsf.data, 'log', percent=99.) plt.imshow(epsf.data, norm=norm, origin='lower', cmap='viridis') plt.colorbar() png_plot = re.sub('.fits','.png',psf_output) plt.savefig(png_plot,bbox_inches='tight') plt.show() profile_png = re.sub('psf.png','profile.png',png_plot) aper_phot_old(epsf.data, xc, yc, radii, plot_results, profile_png) print("average PSF saved as ", output) print("PNG plot saved as ", png_plot) # print("file analysed ", file,"\n\n") return
def find_center(image, center_guess, cutout_size=30, max_iters=10): """ Find the centroid of a star from an initial guess of its position. Originally written to find star from a mouse click. Parameters ---------- image : numpy array or CCDData Image containing the star. center_guess : array or tuple The position, in pixels, of the initial guess for the position of the star. The coordinates should be horizontal first, then vertical, i.e. opposite the usual Python convention for a numpy array. cutout_size : int, optional The default width of the cutout to use for finding the star. max_iters : int, optional Maximum number of iterations to go through in finding the center. """ pad = cutout_size // 2 x, y = center_guess # Keep track of iterations cnt = 0 # Grab the cutout... sub_data = image[y - pad:y + pad, x - pad:x + pad] # - med # ...do stats on it... _, sub_med, _ = sigma_clipped_stats(sub_data) # sub_med = 0 # ...and centroid. x_cm, y_cm = centroid_com(sub_data - sub_med) # Translate centroid back to original image (maybe use Cutout2D instead) cen = np.array([x_cm + x - pad, y_cm + y - pad]) # ceno is the "original" center guess, set it to something nonsensical here ceno = np.array([-100, -100]) while (cnt <= max_iters and (np.abs(np.array([x_cm, y_cm]) - pad).max() > 3 or np.abs(cen - ceno).max() > 0.1)): # Update x, y positions for subsetting x = int(np.floor(x_cm)) + x - pad y = int(np.floor(y_cm)) + y - pad sub_data = image[y - pad:y + pad, x - pad:x + pad] # - med _, sub_med, _ = sigma_clipped_stats(sub_data) # sub_med = 0 mask = (sub_data - sub_med) < 0 x_cm, y_cm = centroid_com(sub_data - sub_med, mask=mask) ceno = cen cen = np.array([x_cm + x - pad, y_cm + y - pad]) if not np.all(~np.isnan(cen)): raise RuntimeError('Centroid finding failed, ' 'previous was {}, current is {}'.format( ceno, cen)) cnt += 1 return cen
if __name__ == '__main__': from astroquery.sdss import SDSS from astropy.coordinates import SkyCoord from astropy.wcs import WCS pos = SkyCoord(236.677624895, 43.3724791101, unit='deg', frame='icrs') xid = SDSS.query_region(pos, spectro=True) im = SDSS.get_images(matches=xid, band='r') wcs = WCS(im[0][0].header) pixel_pos = wcs.world_to_array_index(pos) data = np.copy(im[0][0].data) cutout = data[pixel_pos[0] - 50:pixel_pos[0] + 50, pixel_pos[1] - 50:pixel_pos[1] + 50] com = centroid_com(cutout) # a = half_mass(cutout) # %% SFit = SersicFit(cutout) SFit.fit() SFit.plot_fit() plt.figure() plt.imshow(np.log10(cutout), cmap='Greys', origin='lower') plt.contour(np.log10(SFit.model), colors='r', levels=[np.log10(SFit.amplitude)]) plt.plot(com[0], com[1], 'r+') # plt.contour(a, colors='b', levels=1)
def generate_regions(hdu, approx_location, centering_width=80, ap_rad=6.5, in_rad=7.0, out_rad=14.0): """ Generates source and background regions for aperture photometry. Given an image and the approximate RA/Dec of the source, finds the centroid within centering_width pixels and generates regions with the given parameters Parameters ---------- hdu : `~astropy.io.fits.hdu.image.PrimaryHDU` HDU object containing the FITS image from which regions are generated. Should be just the primary hdu (e.g., hdu[0]). approx_location : `~astropy.coordinates.SkyCoord` `astropy.coordinates.SkyCoord` with the RA and Dec of the object you want to generate a region for. centering_width : int, optional Size of box around source region to find the centroid of in pixels. ap_rad : float, optional Radius of source region in arcseconds. in_rad : float, optional Inner radius of background annulus in arcseconds out_rad : float, optional Outer radius of background annulus in arcseconds Returns ------- src : `~photutils.SkyCircularAperture` Aperture object for source bkg : `~photutils.SkyCircularAnnulus` Aperture object for background """ #Make data and wcs objects data = hdu.data wcs = WCS(hdu) #Make the right shape array of coordinates world_loc = np.array( [[approx_location.ra.value, approx_location.dec.value]]) #Convert to pixel coordinates from the FITS image, 0 indexed b.c. we're working with #a numpy array approx_pix = wcs.wcs_world2pix(world_loc, 0)[0] #Convert to pixel locations of the window. min_x = int(approx_pix[0] - centering_width / 2.0) min_y = int(approx_pix[1] - centering_width / 2.0) max_x = int(approx_pix[0] + centering_width / 2.0) max_y = int(approx_pix[1] + centering_width / 2.0) #Make a little cutout around the object #Numpy arrays are weird, so x->y, y->x stamp = data[min_y:max_y, min_x:max_x] #Calculate the centroid of the stamp x_stamp_centroid, y_stamp_centroid = centroid_com(stamp) #Add back in the boundaries of the box to get centroid in data coords x_centroid = x_stamp_centroid + min_x y_centroid = y_stamp_centroid + min_y #Convert back to RA/Dec. Remember, these are 0-indexed pixels. centroid = wcs.wcs_pix2world(np.array([[x_centroid, y_centroid]]), 0) #Convert centroid to SkyCoords object location = SkyCoord(ra=centroid[0, 0] * u.degree, dec=centroid[0, 1] * u.degree) #Generate regions based on coordinates and given radii. src = SkyCircularAperture(location, r=ap_rad * u.arcsecond) bkg = SkyCircularAnnulus(location, r_in=in_rad * u.arcsecond, r_out=out_rad * u.arcsecond) return src, bkg
def std1dspec(infile, startz=2000, nsigma=5, overwrite=False): print('\n#############################') print('Making 1D spectrum') hdl = fits.open(infile) hdr = hdl[0].header basename = hdr['FRAMEID'] outfile = basename + '.1dspec.fits' if os.path.isfile(outfile) and not overwrite: print('\t 1D data already exits. '+outfile) print('\t This procedure is skipped.') return outfile, True scidata = hdl[0].data binfac1 = hdr['BIN-FCT1'] # Showing the image aspect = 0.43/(0.104*binfac1) fig=plt.figure() plt.title('Click on the star. ') plt.imshow(scidata[startz,:,:], aspect=aspect, \ interpolation='nearest', origin='lower') global xc,yc xc = 0.0 yc = 0.0 def star_center(event): global xc,yc xc= event.xdata yc = event.ydata plt.close() return cid = fig.canvas.mpl_connect('button_press_event', star_center) print('\n\t Click near the star center.') plt.show() print('\t Initial star location: (%.2f, %.2f)'%(xc,yc)) initc = np.array((xc,yc)) cutdata, initp = cutout(scidata[startz,:,:], initc ,w=10) g_init = Gaussian2D(amplitude=np.max(cutdata), x_mean=initc[0]-initp[0], y_mean=initc[1]-initp[1], x_stddev=2.0, y_stddev=1.0, theta=3.1416/2) g_init.theta.fixed = True fitter = LevMarLSQFitter() y, x = np.indices(cutdata.shape) gfit = fitter(g_init, x, y, cutdata) print('\t Initial 2D Gaussian fitting result:') print(gfit) position0 = np.array([gfit.x_mean.value, gfit.y_mean.value]) position0 = position0 + initp position = position0 a = gfit.x_stddev.value * nsigma b = gfit.y_stddev.value * nsigma theta = gfit.theta.value plt.imshow(scidata[startz,:,:], aspect=aspect, \ interpolation='nearest', origin='lower') apertures = EllipticalAperture(position, a=a ,b=b,theta=theta) apertures.plot() print('\n\t Check the aperture, and close the plot window.') plt.title('Check the aperture') plt.show() global coords, ii, std1ddata, lam std1ddata = np.zeros(scidata.shape[0], dtype=np.float32) # Aperture photometry with incleasing wavelength pix from startz for i in range(startz,scidata.shape[0]): cutdata, initp = cutout(scidata[i,:,:],position) if np.min(cutdata) == np.max(cutdata): print('\t Cutdata is empty at '+str(i)+' pix.') break position_pre = position position = centroid_com(cutdata) position = position + initp if np.linalg.norm(position-position_pre) > 2.: print('\t Cetroid is not good at '+str(i)+' pix.') break apertures = EllipticalAperture(position, a=a ,b=b,theta=theta) phot_table = aperture_photometry(scidata[i,:,:], apertures) std1ddata[i] = phot_table['aperture_sum'].data[0] # Aperture photometry with decreasing wavelength pix from startz position = position0 for i in range(startz-1,0,-1): cutdata, initp = cutout(scidata[i,:,:],position) if np.min(cutdata) == np.max(cutdata): print('\t Cutdata is empty! at ' + str(i) + ' pix.') break position_pre = position position = centroid_com(cutdata) position = position + initp if np.linalg.norm(position-position_pre) > 2.: print('\t Cetroid is not good at ' + str(i) + ' pix.') break apertures = EllipticalAperture(position, a=a ,b=b,theta=theta) phot_table = aperture_photometry(scidata[i,:,:], apertures) std1ddata[i] = phot_table['aperture_sum'].data[0] # Plotting the 1D data & selecting the spectral range. crpix = hdr['CRPIX3'] crval = hdr['CRVAL3'] #cdelt = hdr['CDELT3'] cdelt = hdr['CD3_3'] object_name = hdr['OBJECT'] npix = len(std1ddata) start = crval - (crpix-1)*cdelt stop = crval + (npix - crpix + 0.5)*cdelt lam = np.arange(start ,stop, cdelt) coords = np.zeros((2,2)) ii=0 print('\n\t Press any key except \'q\' to specify a required range') def select_range(event): global coords, ii, std1ddata, lam if event.key == 'q': plt.close() elif ii == 0: coords[0,0] = event.xdata coords[0,1] = event.ydata ii = 1 print('\t Press any key again except \'q\'') elif ii == 1: coords[1,0] = event.xdata coords[1,1] = coords[0,1] plt.plot(coords[:,0], coords[:,1]) plt.draw() ii = 2 print('\t Press \'q\' to quit, or any other key to redo.') elif ii == 2: plt.cla() plt.plot(lam, std1ddata) plt.draw() coords[0,0] = event.xdata coords[0,1] = event.ydata ii = 1 print('\t Press any key except \'q\' to specify a required range') return fig=plt.figure() cid = fig.canvas.mpl_connect('key_press_event', select_range) plt.plot(lam,std1ddata) plt.title(object_name) plt.xlabel('Lambda (Angstrom)') plt.ylabel('Count') plt.show() num = 0 while coords[0,0] > lam[num]: num += 1 x1 = num crval = lam[num] while coords[1,0]> lam[num]: num += 1 x2 = num if x1 > x2: temp = x1 x1 = x2 x2 = temp # Saving the output fits file outhdu = fits.PrimaryHDU(data=std1ddata[x1:x2+1]) outhdl = fits.HDUList([outhdu]) outhdr = hdl[0].header outhdr['CTYPE1'] = hdl[0].header['CTYPE3'] outhdr['CRVAL1'] = crval outhdr['CRPIX1'] = 1 #outhdr['CDELT1'] = hdl[0].header['CDELT3'] outhdr['CD1_1'] = hdl[0].header['CD3_3'] outhdr['DISPAXIS'] = 1 outhdr['WCSDIM'] = 1 outhdr['XSTDDEV'] = (gfit.x_stddev.value, \ 'X stddev of the star radial profile') outhdr['YSTDDEV'] = (gfit.y_stddev.value, \ 'Y stddev of the star radial profile') outhdr['APNSIG'] = (nsigma, 'Number of sigmas for integration aperture') outhdr.remove('CTYPE2') outhdr.remove('CRVAL2') outhdr.remove('CRPIX2') #outhdr.remove('CDELT2') outhdr.remove('CD2_2') outhdr.remove('LTM2_2') #outhdr.remove('CD1_1') outhdr.remove('LTM1_1') outhdr.remove('CTYPE3') outhdr.remove('CRVAL3') outhdr.remove('CRPIX3') #outhdr.remove('CDELT3') outhdr.remove('CD3_3') outhdr.remove('LTM3_3') outhdl[0].header = outhdr outhdl.writeto(outfile, overwrite=overwrite) print('\t 1D data file: '+outfile) outhdl.close() hdl.close() return outfile, True
def centroider(target, sources, output_plots=False, gif=False, restore=False, box_w=8): matplotlib.use('TkAgg') plt.ioff() t1 = time.time() pines_path = pines_dir_check() short_name = short_name_creator(target) kernel = Gaussian2DKernel(x_stddev=1) #For fixing nans in cutouts. #If restore == True, read in existing output and return. if restore: centroid_df = pd.read_csv( pines_path / ('Objects/' + short_name + '/sources/target_and_references_centroids.csv'), converters={ 'X Centroids': eval, 'Y Centroids': eval }) print('Restoring centroider output from {}.'.format( pines_path / ('Objects/' + short_name + '/sources/target_and_references_centroids.csv'))) print('') return centroid_df #Create subdirectories in sources folder to contain output plots. if output_plots: subdirs = glob( str(pines_path / ('Objects/' + short_name + '/sources')) + '/*/') #Delete any source directories that are already there. for name in subdirs: shutil.rmtree(name) #Create new source directories. for name in sources['Name']: source_path = ( pines_path / ('Objects/' + short_name + '/sources/' + name + '/')) os.mkdir(source_path) #Read in extra shifts, in case the master image wasn't used for source detection. extra_shift_path = pines_path / ('Objects/' + short_name + '/sources/extra_shifts.txt') extra_shifts = pd.read_csv(extra_shift_path, delimiter=' ', names=['Extra X shift', 'Extra Y shift']) extra_x_shift = extra_shifts['Extra X shift'][0] extra_y_shift = extra_shifts['Extra Y shift'][0] np.seterr( divide='ignore', invalid='ignore' ) #Suppress some warnings we don't care about in median combining. #Get list of reduced files for target. reduced_path = pines_path / ('Objects/' + short_name + '/reduced') reduced_filenames = natsort.natsorted( [x.name for x in reduced_path.glob('*red.fits')]) reduced_files = np.array([reduced_path / i for i in reduced_filenames]) #Declare a new dataframe to hold the centroid information for all sources we want to track. columns = [] columns.append('Filename') columns.append('Seeing') columns.append('Time (JD UTC)') columns.append('Airmass') #Add x/y positions and cenroid flags for every tracked source for i in range(0, len(sources)): columns.append(sources['Name'][i] + ' Image X') columns.append(sources['Name'][i] + ' Image Y') columns.append(sources['Name'][i] + ' Cutout X') columns.append(sources['Name'][i] + ' Cutout Y') columns.append(sources['Name'][i] + ' Centroid Warning') centroid_df = pd.DataFrame(index=range(len(reduced_files)), columns=columns) log_path = pines_path / ('Logs/') log_dates = np.array( natsort.natsorted( [x.name.split('_')[0] for x in log_path.glob('*.txt')])) #Make sure we have logs for all the nights of these data. Need them to account for image shifts. nights = list(set([i.name.split('.')[0] for i in reduced_files])) for i in nights: if i not in log_dates: print('ERROR: {} not in {}. Download it from the PINES server.'. format(i + '_log.txt', log_path)) pdb.set_trace() shift_tolerance = 2.0 #Number of pixels that the measured centroid can be away from the expected position in either x or y before trying other centroiding algorithms. for i in range(len(sources)): #Get the initial source position. x_pos = sources['Source Detect X'][i] y_pos = sources['Source Detect Y'][i] print('') print( 'Getting centroids for {}, ({:3.1f}, {:3.1f}) in source detection image. Source {} of {}.' .format(sources['Name'][i], x_pos, y_pos, i + 1, len(sources))) if output_plots: print('Saving centroid plots to {}.'.format( pines_path / ('Objects/' + short_name + '/sources/' + sources['Name'][i] + '/'))) pbar = ProgressBar() for j in pbar(range(len(reduced_files))): centroid_df[sources['Name'][i] + ' Centroid Warning'][j] = 0 file = reduced_files[j] image = fits.open(file)[0].data #Get the measured image shift for this image. log = pines_log_reader(log_path / (file.name.split('.')[0] + '_log.txt')) log_ind = np.where(log['Filename'] == file.name.split('_')[0] + '.fits')[0][0] x_shift = float(log['X shift'][log_ind]) y_shift = float(log['Y shift'][log_ind]) #Save the filename for readability. Save the seeing for use in variable aperture photometry. Save the time for diagnostic plots. if i == 0: centroid_df['Filename'][j] = file.name.split('_')[0] + '.fits' centroid_df['Seeing'][j] = log['X seeing'][log_ind] time_str = fits.open(file)[0].header['DATE-OBS'] #Correct some formatting issues that can occur in Mimir time stamps. if time_str.split(':')[-1] == '60.00': time_str = time_str[0:14] + str( int(time_str.split(':')[-2]) + 1) + ':00.00' elif time_str.split(':')[-1] == '010.00': time_str = time_str[0:17] + time_str.split(':')[-1][1:] centroid_df['Time (JD UTC)'][j] = julian.to_jd( datetime.datetime.strptime(time_str, '%Y-%m-%dT%H:%M:%S.%f')) centroid_df['Airmass'][j] = log['Airmass'][log_ind] nan_flag = False #Flag indicating if you should not trust the log's shifts. Set to true if x_shift/y_shift are 'nan' or > 30 pixels. #If bad shifts were measured for this image, skip. if log['Shift quality flag'][log_ind] == 1: continue if np.isnan(x_shift) or np.isnan(y_shift): x_shift = 0 y_shift = 0 nan_flag = True #If there are clouds, shifts could have been erroneously high...just zero them? if abs(x_shift) > 200: #x_shift = 0 nan_flag = True if abs(y_shift) > 200: #y_shift = 0 nan_flag = True #Apply the shift. NOTE: This relies on having accurate x_shift and y_shift values from the log. #If they're incorrect, the cutout will not be in the right place. #x_pos = sources['Source Detect X'][i] - x_shift + extra_x_shift #y_pos = sources['Source Detect Y'][i] + y_shift - extra_y_shift x_pos = sources['Source Detect X'][i] - (x_shift - extra_x_shift) y_pos = sources['Source Detect Y'][i] + (y_shift - extra_y_shift) #TODO: Make all this its own function. #Cutout around the expected position and interpolate over any NaNs (which screw up source detection). cutout = interpolate_replace_nans( image[int(y_pos - box_w):int(y_pos + box_w) + 1, int(x_pos - box_w):int(x_pos + box_w) + 1], kernel=Gaussian2DKernel(x_stddev=0.5)) #interpolate_replace_nans struggles with edge pixels, so shave off edge_shave pixels in each direction of the cutout. edge_shave = 1 cutout = cutout[edge_shave:len(cutout) - edge_shave, edge_shave:len(cutout) - edge_shave] vals, lower, upper = sigmaclip( cutout, low=1.5, high=2.5) #Get sigma clipped stats on the cutout med = np.nanmedian(vals) std = np.nanstd(vals) try: centroid_x_cutout, centroid_y_cutout = centroid_2dg( cutout - med) #Perform centroid detection on the cutout. except: pdb.set_trace() centroid_x = centroid_x_cutout + int( x_pos ) - box_w + edge_shave #Translate the detected centroid from the cutout coordinates back to the full-frame coordinates. centroid_y = centroid_y_cutout + int(y_pos) - box_w + edge_shave # if i == 0: # qp(cutout) # plt.plot(centroid_x_cutout, centroid_y_cutout, 'rx') # # qp(image) # # plt.plot(centroid_x, centroid_y, 'rx') # pdb.set_trace() #If the shifts in the log are not 'nan' or > 200 pixels, check if the measured shifts are within shift_tolerance pixels of the expected position. # If they aren't, try alternate centroiding methods to try and find it. #Otherwise, use the shifts as measured with centroid_1dg. PINES_watchdog likely failed while observing, and we don't expect the centroids measured here to actually be at the expected position. if not nan_flag: #Try a 2D Gaussian detection. if (abs(centroid_x - x_pos) > shift_tolerance) or ( abs(centroid_y - y_pos) > shift_tolerance): centroid_x_cutout, centroid_y_cutout = centroid_2dg( cutout - med) centroid_x = centroid_x_cutout + int(x_pos) - box_w centroid_y = centroid_y_cutout + int(y_pos) - box_w #If that fails, try a COM detection. if (abs(centroid_x - x_pos) > shift_tolerance) or ( abs(centroid_y - y_pos) > shift_tolerance): centroid_x_cutout, centroid_y_cutout = centroid_com( cutout - med) centroid_x = centroid_x_cutout + int(x_pos) - box_w centroid_y = centroid_y_cutout + int(y_pos) - box_w #If that fails, try masking source and interpolate over any bad pixels that aren't in the bad pixel mask, then redo 1D gaussian detection. if (abs(centroid_x - x_pos) > shift_tolerance) or ( abs(centroid_y - y_pos) > shift_tolerance): mask = make_source_mask(cutout, nsigma=4, npixels=5, dilate_size=3) vals, lo, hi = sigmaclip(cutout[~mask]) bad_locs = np.where((mask == False) & ( (cutout > hi) | (cutout < lo))) cutout[bad_locs] = np.nan cutout = interpolate_replace_nans( cutout, kernel=Gaussian2DKernel(x_stddev=0.5)) centroid_x_cutout, centroid_y_cutout = centroid_1dg( cutout - med) centroid_x = centroid_x_cutout + int(x_pos) - box_w centroid_y = centroid_y_cutout + int(y_pos) - box_w #Try a 2D Gaussian detection on the interpolated cutout if (abs(centroid_x - x_pos) > shift_tolerance) or ( abs(centroid_y - y_pos) > shift_tolerance): centroid_x_cutout, centroid_y_cutout = centroid_2dg( cutout - med) centroid_x = centroid_x_cutout + int( x_pos) - box_w centroid_y = centroid_y_cutout + int( y_pos) - box_w #Try a COM on the interpolated cutout. if (abs(centroid_x - x_pos) > shift_tolerance ) or (abs(centroid_y - y_pos) > shift_tolerance): centroid_x_cutout, centroid_y_cutout = centroid_com( cutout) centroid_x = centroid_x_cutout + int( x_pos) - box_w centroid_y = centroid_y_cutout + int( y_pos) - box_w #Last resort: try cutting off the edge of the cutout. Edge pixels can experience poor interpolation, and this sometimes helps. if (abs(centroid_x - x_pos) > shift_tolerance) or ( abs(centroid_y - y_pos) > shift_tolerance): cutout = cutout[1:-1, 1:-1] centroid_x_cutout, centroid_y_cutout = centroid_1dg( cutout - med) centroid_x = centroid_x_cutout + int( x_pos) - box_w + 1 centroid_y = centroid_y_cutout + int( y_pos) - box_w + 1 #Try with a 2DG if (abs(centroid_x - x_pos) > shift_tolerance) or ( abs(centroid_y - y_pos) > shift_tolerance): centroid_x_cutout, centroid_y_cutout = centroid_2dg( cutout - med) centroid_x = centroid_x_cutout + int( x_pos) - box_w + 1 centroid_y = centroid_y_cutout + int( y_pos) - box_w + 1 #If ALL that fails, report the expected position as the centroid. if (abs(centroid_x - x_pos) > shift_tolerance) or ( abs(centroid_y - y_pos) > shift_tolerance): print( 'WARNING: large centroid deviation measured, returning predicted position' ) print('') centroid_df[ sources['Name'][i] + ' Centroid Warning'][j] = 1 centroid_x = x_pos centroid_y = y_pos #pdb.set_trace() #Check that your measured position is actually on the detector. if (centroid_x < 0) or (centroid_y < 0) or (centroid_x > 1023) or ( centroid_y > 1023): #Try a quick mask/interpolation of the cutout. mask = make_source_mask(cutout, nsigma=3, npixels=5, dilate_size=3) vals, lo, hi = sigmaclip(cutout[~mask]) bad_locs = np.where((mask == False) & ((cutout > hi) | (cutout < lo))) cutout[bad_locs] = np.nan cutout = interpolate_replace_nans( cutout, kernel=Gaussian2DKernel(x_stddev=0.5)) centroid_x, centroid_y = centroid_2dg(cutout - med) centroid_x += int(x_pos) - box_w centroid_y += int(y_pos) - box_w if (centroid_x < 0) or (centroid_y < 0) or ( centroid_x > 1023) or (centroid_y > 1023): print( 'WARNING: large centroid deviation measured, returning predicted position' ) print('') centroid_df[sources['Name'][i] + ' Centroid Warning'][j] = 1 centroid_x = x_pos centroid_y = y_pos #pdb.set_trace() #Check to make sure you didn't measure nan's. if np.isnan(centroid_x): centroid_x = x_pos print( 'NaN returned from centroid algorithm, defaulting to target position in source_detct_image.' ) if np.isnan(centroid_y): centroid_y = y_pos print( 'NaN returned from centroid algorithm, defaulting to target position in source_detct_image.' ) #Record the image and relative cutout positions. centroid_df[sources['Name'][i] + ' Image X'][j] = centroid_x centroid_df[sources['Name'][i] + ' Image Y'][j] = centroid_y centroid_df[sources['Name'][i] + ' Cutout X'][j] = centroid_x_cutout centroid_df[sources['Name'][i] + ' Cutout Y'][j] = centroid_y_cutout if output_plots: #Plot lock_x = int(centroid_df[sources['Name'][i] + ' Image X'][0]) lock_y = int(centroid_df[sources['Name'][i] + ' Image Y'][0]) norm = ImageNormalize(data=cutout, interval=ZScaleInterval()) plt.imshow(image, origin='lower', norm=norm) plt.plot(centroid_x, centroid_y, 'rx') ap = CircularAperture((centroid_x, centroid_y), r=5) ap.plot(lw=2, color='b') plt.ylim(lock_y - 30, lock_y + 30 - 1) plt.xlim(lock_x - 30, lock_x + 30 - 1) plt.title('CENTROID DIAGNOSTIC PLOT\n' + sources['Name'][i] + ', ' + reduced_files[j].name + ' (image ' + str(j + 1) + ' of ' + str(len(reduced_files)) + ')', fontsize=10) plt.text(centroid_x, centroid_y + 0.5, '(' + str(np.round(centroid_x, 1)) + ', ' + str(np.round(centroid_y, 1)) + ')', color='r', ha='center') plot_output_path = ( pines_path / ('Objects/' + short_name + '/sources/' + sources['Name'][i] + '/' + str(j).zfill(4) + '.jpg')) plt.gca().set_axis_off() plt.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0) plt.margins(0, 0) plt.gca().xaxis.set_major_locator(plt.NullLocator()) plt.gca().yaxis.set_major_locator(plt.NullLocator()) plt.savefig(plot_output_path, bbox_inches='tight', pad_inches=0, dpi=150) plt.close() if gif: gif_path = (pines_path / ('Objects/' + short_name + '/sources/' + sources['Name'][i] + '/')) gif_maker(path=gif_path, fps=10) output_filename = pines_path / ( 'Objects/' + short_name + '/sources/target_and_references_centroids.csv') #centroid_df.to_csv(pines_path/('Objects/'+short_name+'/sources/target_and_references_centroids.csv')) print('Saving centroiding output to {}.'.format(output_filename)) with open(output_filename, 'w') as f: for j in range(len(centroid_df)): #Write the header line. if j == 0: f.write('{:<17s}, '.format('Filename')) f.write('{:<15s}, '.format('Time (JD UTC)')) f.write('{:<6s}, '.format('Seeing')) f.write('{:<7s}, '.format('Airmass')) for i in range(len(sources['Name'])): n = sources['Name'][i] if i != len(sources['Name']) - 1: f.write( '{:<23s}, {:<23s}, {:<24s}, {:<24s}, {:<34s}, '. format(n + ' Image X', n + ' Image Y', n + ' Cutout X', n + ' Cutout Y', n + ' Centroid Warning')) else: f.write( '{:<23s}, {:<23s}, {:<24s}, {:<24s}, {:<34s}\n'. format(n + ' Image X', n + ' Image Y', n + ' Cutout X', n + ' Cutout Y', n + ' Centroid Warning')) #Write in the data lines. try: f.write('{:<17s}, '.format(centroid_df['Filename'][j])) f.write('{:<15.7f}, '.format(centroid_df['Time (JD UTC)'][j])) f.write('{:<6.1f}, '.format(float(centroid_df['Seeing'][j]))) f.write('{:<7.2f}, '.format(centroid_df['Airmass'][j])) except: pdb.set_trace() for i in range(len(sources['Name'])): n = sources['Name'][i] if i != len(sources['Name']) - 1: format_string = '{:<23.4f}, {:<23.4f}, {:<24.4f}, {:<24.4f}, {:<34d}, ' else: format_string = '{:<23.4f}, {:<23.4f}, {:<24.4f}, {:<24.4f}, {:<34d}\n' f.write( format_string.format( centroid_df[n + ' Image X'][j], centroid_df[n + ' Image Y'][j], centroid_df[n + ' Cutout X'][j], centroid_df[n + ' Cutout Y'][j], centroid_df[n + ' Centroid Warning'][j])) np.seterr(divide='warn', invalid='warn') print('') print('centroider runtime: {:.2f} minutes.'.format( (time.time() - t1) / 60)) print('') return centroid_df
def std1dspec(infile, startz=2000, nsigma=5, overwrite=False): print('\n#############################') print('Making 1D spectrum') hdl = fits.open(infile) hdr = hdl[0].header scidata = hdl[0].data binfac1 = hdr['BIN-FCT1'] # Showing the image aspect = 0.43/(0.104*binfac1) fig=plt.figure() plt.title('Click on the star. ') plt.imshow(scidata[startz,:,:], aspect=aspect, \ interpolation='nearest', origin='lower') global xc,yc xc = 0.0 yc = 0.0 def star_center(event): global xc,yc xc= event.xdata yc = event.ydata plt.close() return cid = fig.canvas.mpl_connect('button_press_event', star_center) print('\n\t Click near the star center.') plt.show() print('\t Initial star location: (%.2f, %.2f)'%(xc,yc)) initc = np.array((xc,yc)) cutdata, initp = cutout(scidata[startz,:,:], initc ,w=10) g_init = Gaussian2D(amplitude=np.max(cutdata), x_mean=initc[0]-initp[0], y_mean=initc[1]-initp[1], x_stddev=2.0, y_stddev=1.0, theta=3.1416/2) g_init.theta.fixed = True fitter = LevMarLSQFitter() y, x = np.indices(cutdata.shape) gfit = fitter(g_init, x, y, cutdata) print('\t Initial 2D Gaussian fitting result:') print(gfit) position0 = np.array([gfit.x_mean.value, gfit.y_mean.value]) position0 = position0 + initp position = position0 a = gfit.x_stddev.value * nsigma b = gfit.y_stddev.value * nsigma theta = gfit.theta.value plt.imshow(scidata[startz,:,:], aspect=aspect, \ interpolation='nearest', origin='lower') apertures = EllipticalAperture(position, a=a ,b=b,theta=theta) apertures.plot() print('\n\t Check the aperture, and close the plot window.') plt.title('Check the aperture') plt.show() global coords, ii, std1ddata, lam std1ddata = np.zeros(scidata.shape[0], dtype=np.float32) positions = np.zeros((scidata.shape[0], 2), dtype=np.float) # Aperture photometry with incleasing wavelength pix from startz for i in range(startz,scidata.shape[0]): cutdata, initp = cutout(scidata[i,:,:],position) if np.min(cutdata) == np.max(cutdata): print('\t Cutdata is empty at '+str(i)+' pix.') break position_pre = position position = centroid_com(cutdata) position = position + initp if np.linalg.norm(position-position_pre) > 2.: print('\t Cetroid is not good at '+str(i)+' pix.') break positions[i,:] = position #apertures = EllipticalAperture(position, a=a ,b=b,theta=theta) #phot_table = aperture_photometry(scidata[i,:,:], apertures) #std1ddata[i] = phot_table['aperture_sum'].data[0] # Aperture photometry with decreasing wavelength pix from startz position = position0 for i in range(startz-1,0,-1): cutdata, initp = cutout(scidata[i,:,:],position) if np.min(cutdata) == np.max(cutdata): print('\t Cutdata is empty! at ' + str(i) + ' pix.') break position_pre = position position = centroid_com(cutdata) position = position + initp if np.linalg.norm(position-position_pre) > 2.: print('\t Cetroid is not good at ' + str(i) + ' pix.') break positions[i,:] = position #apertures = EllipticalAperture(position, a=a ,b=b,theta=theta) #phot_table = aperture_photometry(scidata[i,:,:], apertures) #std1ddata[i] = phot_table['aperture_sum'].data[0] # Plotting the 1D data & selecting the spectral range. crpix = hdr['CRPIX3'] crval = hdr['CRVAL3'] #cdelt = hdr['CDELT3'] cdelt = hdr['CD3_3'] object_name = hdr['OBJECT'] npix = len(std1ddata) start = crval - (crpix-1)*cdelt stop = crval + (npix - crpix + 0.5)*cdelt lam = np.arange(start ,stop, cdelt) plt.plot(lam,positions[:,0]) plt.title(object_name) plt.xlabel('Lambda (Angstrom)') plt.ylabel('X (pix)') plt.grid() plt.show() plt.plot(lam,positions[:,1]) plt.title(object_name) plt.xlabel('Lambda (Angstrom)') plt.ylabel('Y (pix)') plt.grid() plt.show() return