def convert_poly(poly, coords, wcs): '''convert the vertices of a polygon from one coordinate system to another''' newpoly=[] for vert in poly: if coords != 'world': #coords are in 1-indexed pixel coords x, y = vert[0]-1, vert[1]-1 # Convert to 0-indexed pixel coordinates else: # Convert the sky coordinates to 0-indexed pixel coordinates pixcrd = wcs.wcs_sky2pix(np.array([vert]), 0) x, y = pixcrd[0][0],pixcrd[0][1] newpoly.append([x,y]) return(newpoly)
def convert_poly(poly, coords, wcs): '''convert the vertices of a polygon from one coordinate system to another''' newpoly = [] for vert in poly: if coords != 'world': #coords are in 1-indexed pixel coords x, y = vert[0] - 1, vert[ 1] - 1 # Convert to 0-indexed pixel coordinates else: # Convert the sky coordinates to 0-indexed pixel coordinates pixcrd = wcs.wcs_sky2pix(np.array([vert]), 0) x, y = pixcrd[0][0], pixcrd[0][1] newpoly.append([x, y]) return (newpoly)
def radec2xy(hdr, ra, dec): """Transforms sky coordinates (RA and Dec) to pixel coordinates (x and y). Input: - hdr: FITS image header - ra <float> : Right ascension value in degrees - dec <float>: Declination value in degrees Output: - (x,y) <tuple>: pixel coordinates """ wcs = wcs.WCS(hdr) skycrd = np.array([[ra, dec]]) pixcrd = wcs.wcs_sky2pix(skycrd, 1) x = pixcrd[0][0] y = pixcrd[0][1] return (x, y)
def find_orientation(i, fitsim, ra, dec, Maj, Min, s=(3 / 60.), plot=False, head=None, hdulist=None): ''' Finds the orientation of multiple gaussian single sources To run this function for all sources, use setup_find_orientation_multiple_gaussians() instead. A big part of this function is a re-run of the postage function, since we need to make a new postage basically, but this time as an array. Arguments: i : the new_Index of the source fitsim: the postage created earlier ra, dec: the ra and dec of the source Maj: the major axis of the source s: the width of the image, default 3 arcmin, because it has to be a tiny bit lower than the postage created earlier (width 4 arcmin) or NaNs will appear in the image head and hdulist, the header and hdulist if a postage hasn't been created before. (This is so it doesn't open every time in the loop but is opened before the loop.) Output: i, max_angle, len(err_indices) Which are: the new_Index of the source, the best angle of the orientation, and the amount of orientations that have avg flux value > 80% of the peak avg flux value along the line If plot=True, produces the plots of the best orientation and the Flux vs Orientation as well ''' ################## BEGIN Postage Function ############# if not head: head = pf.getheader(fitsim) hdulist = pf.open(fitsim) # Parse the WCS keywords in the primary HDU wcs = pw.WCS(hdulist[0].header) # Some pixel coordinates of interest. skycrd = np.array([ra, dec]) skycrd = np.array([[ra, dec, 0, 0]], np.float_) # Convert pixel coordinates to world coordinates # The second argument is "origin" -- in this case we're declaring we # have 1-based (Fortran-like) coordinates. pixel = wcs.wcs_sky2pix(skycrd, 1) # Some pixel coordinates of interest. x = pixel[0][0] y = pixel[0][1] pixsize = abs(wcs.wcs.cdelt[0]) if pl.isnan(s): s = 25. N = (s / pixsize) # print 'x=%.5f, y=%.5f, N=%i' %(x,y,N) ximgsize = head.get('NAXIS1') yimgsize = head.get('NAXIS2') if x == 0: x = ximgsize / 2 if y == 0: y = yimgsize / 2 offcentre = False # subimage limits: check if runs over edges xlim1 = x - (N / 2) if (xlim1 < 1): xlim1 = 1 offcentre = True xlim2 = x + (N / 2) if (xlim2 > ximgsize): xlim2 = ximgsize offcentre = True ylim1 = y - (N / 2) if (ylim1 < 1): ylim1 = 1 offcentre = True ylim2 = y + (N / 2) if (ylim2 > yimgsize): offcentre = True ylim2 = yimgsize xl = int(xlim1) yl = int(ylim1) xu = int(xlim2) yu = int(ylim2) ################## END Postage Function ############# # extract the data array instead of making a postage stamp from astropy.nddata.utils import extract_array data_array = extract_array(hdulist[0].data, (yu - yl, xu - xl), (y, x)) # use a radius for the line that is the major axis, # but with 2 pixels more added to the radius # to make sure we do capture the whole source radius = Maj / 60 * 40 #arcsec -- > arcmin --> image units radius = int(radius) + 2 # is chosen arbitrarily # in the P173+55 1 arcmin = 40 image units, should check if this is true everywhere, for FieldNames it is. # check if there are no NaNs in the cutout image, if there are then make smaller cutout if True in (np.isnan(data_array)): if s < (2 / 60.): print "No hope left for this source " return i, 0.0, 100.5 elif s == (2 / 60.): print "Nan in the cutout image AGAIN ", head['OBJECT'], ' i = ', i try: return find_orientation(i, fitsim, ra, dec, Maj, Min, s=(Maj * 2. / 60. / 60.), plot=plot, head=head, hdulist=hdulist) except RuntimeError: print "No hope left for this source, " return i, 0.0, 100.5 else: print "NaN in the cutout image: ", head['OBJECT'], ' i = ', i try: return find_orientation(i, fitsim, ra, dec, Maj, Min, s=(2 / 60.), plot=plot, head=head, hdulist=hdulist) except RuntimeError: print "No hope left for this source, " return i, 0.0, 100.5 from scipy.ndimage import interpolation from scipy.ndimage import map_coordinates #the center of the image is at the halfway point -1 for using array-index xcenter = np.shape(data_array)[0] / 2 - 1 # pixel coordinates ycenter = np.shape(data_array)[1] / 2 - 1 # pixel coordinates # make a line with 'num' points and radius = radius x0, y0 = xcenter - radius, ycenter x1, y1 = xcenter + radius, ycenter num = 1000 x, y = np.linspace(x0, x1, num), np.linspace(y0, y1, num) # the final orientation will be max_angle max_angle = 0 max_value = 0 # flux values for 0 to 179 degrees of rotation (which is also convenietly their index) all_values = [] for angle in range(0, 180): # using spline order 3 interpolation to rotate the data by 0-180 deg data_array2 = interpolation.rotate(data_array, angle * 1., reshape=False) # extract the values along the line, zi = map_coordinates(data_array2, np.vstack((y, x)), prefilter=False) # calc the mean flux meanie = np.sum(zi) if meanie > max_value: max_value = meanie max_angle = angle all_values.append(meanie) # calculate all orientiations for which the average flux lies within # 80 per cent of the peak average flux err_orientations = np.where(all_values > (0.8 * max_value))[0] # find the cutoff value, dependent on the distance cutoff = 2 * np.arctan(Min / Maj) * 180 / np.pi # convert rad to deg if len(err_orientations) > cutoff: classification = 'Large err' else: classification = 'Small err' # then find the amount of maxima and the lobe_ratio from scipy.signal import argrelextrema data_array2 = interpolation.rotate(data_array, max_angle, reshape=False) zi = map_coordinates(data_array2, np.vstack((y, x)), prefilter=False) # find the local maxima in the flux along the line indx_extrema = argrelextrema(zi, np.greater) amount_of_maxima = len(indx_extrema[0]) # calculate the flux ratio of the lobes lobe_ratio_bool = False lobe_ratio = 0.0 # in case there is only 1 maximum if amount_of_maxima > 1: if amount_of_maxima == 2: lobe_ratio = (zi[indx_extrema][0] / zi[indx_extrema][1]) else: # more maxima, so the lobe_ratio is defined as the ratio between the brightest lobes indx_maximum_extrema = np.flip( np.argsort(zi[indx_extrema]), 0)[:2] #argsort sorts ascending so flip makes it descending indx_maximum_extrema = indx_extrema[0][indx_maximum_extrema] lobe_ratio = (zi[indx_maximum_extrema[0]] / zi[indx_maximum_extrema][1]) if plot: # the plot of the rotated source and the flux along the line fig, axes = plt.subplots(nrows=2, figsize=(12, 12)) axes[0].imshow(data_array2, origin='lower') axes[0].plot([x0, x1], [y0, y1], 'r-', alpha=0.3) axes[1].plot(zi) Fratio = 10. if amount_of_maxima > 1: if ((1. / Fratio) < lobe_ratio < (Fratio)): lobe_ratio_bool = True plt.suptitle('Field: ' + head['OBJECT'] + ' | Source ' + str(i) + '\n Best orientation = ' + str(max_angle) + ' degrees | classification: ' + classification + '\nlobe ratio ' + str(lobe_ratio_bool) + ' | extrema: ' + str(indx_extrema) + '\n lobe ratio: ' + str(lobe_ratio)) # saving the figures to seperate directories if amount_of_maxima == 2: if classification == 'Small err': plt.savefig( '/data1/osinga/figures/cutouts/P173+55/angle_distance/2_maxima/small_err/' + head['OBJECT'] + 'src_' + str(i) + '.png') # // EDITED TO P173+55 else: plt.savefig( '/data1/osinga/figures/cutouts/P173+55/angle_distance/2_maxima/large_err/' + head['OBJECT'] + 'src_' + str(i) + '.png') # // EDITED TO P173+55 elif amount_of_maxima > 2: if classification == 'Small err': plt.savefig( '/data1/osinga/figures/cutouts/P173+55/angle_distance/more_maxima/small_err/' + head['OBJECT'] + 'src_' + str(i) + '.png') # // EDITED TO P173+55 else: plt.savefig( '/data1/osinga/figures/cutouts/P173+55/angle_distance/more_maxima/large_err/' + head['OBJECT'] + 'src_' + str(i) + '.png') # // EDITED TO P173+55 else: plt.savefig( '/data1/osinga/figures/cutouts/P173+55/angle_distance/1_maximum/' + head['OBJECT'] + 'src_' + str(i) + '.png') # // EDITED TO P173+55 plt.clf() plt.close() # the plot of the flux vs orientation plt.plot(all_values, label='all orientations') plt.scatter(err_orientations, np.array(all_values)[err_orientations], color='y', label='0.8 fraction') plt.axvline(x=max_angle, ymin=0, ymax=1, color='r', label='best orientation') plt.title('Best orientation for Source ' + str(i) + '\nClassification: ' + classification + ' | Cutoff: ' + str(cutoff) + ' | Error: ' + str(len(err_orientations))) plt.ylabel('Average flux (arbitrary units)') plt.xlabel('orientation (degrees)') plt.legend() plt.xlim(0, 180) # saving the figures to seperate directories if amount_of_maxima == 2: if classification == 'Small err': plt.savefig( '/data1/osinga/figures/cutouts/P173+55/angle_distance/2_maxima/small_err/' + head['OBJECT'] + 'src_' + str(i) + '_orientation.png') # // EDITED TO P173+55 else: plt.savefig( '/data1/osinga/figures/cutouts/P173+55/angle_distance/2_maxima/large_err/' + head['OBJECT'] + 'src_' + str(i) + '_orientation.png') # // EDITED TO P173+55 elif amount_of_maxima > 2: if classification == 'Small err': plt.savefig( '/data1/osinga/figures/cutouts/P173+55/angle_distance/more_maxima/small_err/' + head['OBJECT'] + 'src_' + str(i) + '_orientation.png') # // EDITED TO P173+55 else: plt.savefig( '/data1/osinga/figures/cutouts/P173+55/angle_distance/more_maxima/large_err/' + head['OBJECT'] + 'src_' + str(i) + '_orientation.png') # // EDITED TO P173+55 else: plt.savefig( '/data1/osinga/figures/cutouts/P173+55/angle_distance/1_maximum/' + head['OBJECT'] + 'src_' + str(i) + '_orientation.png') # // EDITED TO P173+55 plt.clf() plt.close() return i, max_angle, len( err_orientations), amount_of_maxima, classification, lobe_ratio
def postage(fitsim, postfits, ra, dec, s, NN_RA=None, NN_DEC=None): ''' Makes a postage 'postfits' from the entire fitsim at RA=ra and DEC=dec with radius s (degrees) Creates both a 'postfits.fits' cutout as well as a 'postfits.png' image for quick viewing If NN_RA and NN_DEC are provided it shows an arrow from the source to the nearest neighbour. ''' import os head = pf.getheader(fitsim) hdulist = pf.open(fitsim) # Parse the WCS keywords in the primary HDU wcs = pw.WCS(hdulist[0].header) # Some pixel coordinates of interest. skycrd = np.array([ra, dec]) skycrd = np.array([[ra, dec, 0, 0]], np.float_) # Convert pixel coordinates to world coordinates # The second argument is "origin" -- in this case we're declaring we # have 1-based (Fortran-like) coordinates. pixel = wcs.wcs_sky2pix(skycrd, 1) # Some pixel coordinates of interest. x = pixel[0][0] y = pixel[0][1] pixsize = abs(wcs.wcs.cdelt[0]) # pixsize = 0.000416666666666666 # this is what it should give for P173+55 # but it returns 0 for pixsize, and pixel etc if pl.isnan(s): s = 25. N = (s / pixsize) print 'x=%.5f, y=%.5f, N=%i' % (x, y, N) ximgsize = head.get('NAXIS1') yimgsize = head.get('NAXIS2') if x == 0: x = ximgsize / 2 if y == 0: y = yimgsize / 2 offcentre = False # subimage limits: check if runs over edges xlim1 = x - (N / 2) if (xlim1 < 1): xlim1 = 1 offcentre = True xlim2 = x + (N / 2) if (xlim2 > ximgsize): xlim2 = ximgsize offcentre = True ylim1 = y - (N / 2) if (ylim1 < 1): ylim1 = 1 offcentre = True ylim2 = y + (N / 2) if (ylim2 > yimgsize): offcentre = True ylim2 = yimgsize xl = int(xlim1) yl = int(ylim1) xu = int(xlim2) yu = int(ylim2) print 'postage stamp is %i x %i pixels' % (xu - xl, yu - yl) from astropy.nddata.utils import extract_array # plt.imshow(extract_array(hdulist[0].data,(yu-yl,xu-xl),(y,x)),origin='lower') # plt.savefig(postfits+'.png') # plt.show() # make fits cutout inps = fitsim + '[%0.0f:%0.0f,%0.0f:%0.0f]' % (xl, xu, yl, yu) if os.path.isfile(postfits): os.system('rm ' + postfits) os.system('fitscopy %s %s' % (inps, postfits)) # print 'fitscopy %s %s' %(inps,postfits) # make a png cutout from the fits cutout with an arrow pointing to the NN if NN_RA: gc = aplpy.FITSFigure(postfits) gc.show_grayscale() gc.add_grid() gc.show_arrows(ra, dec, NN_RA - ra, NN_DEC - dec) # gc.show_regions(prefix+file+'cat.srl.reg') #very slow # pl.show() gc.save(postfits + '.png') gc.close() return postfits
def find_orientation(i, fitsim, ra, dec, Maj, s=(3 / 60.), plot=False, head=None, hdulist=None): ''' Finds the orientation of multiple gaussian single sources To run this function for all sources, use setup_find_orientation_multiple_gaussians() instead. A big part of this function is a re-run of the postage function, since we need to make a new postage basically, but this time as an array. Arguments: i : the new_Index of the source fitsim: the postage created earlier ra, dec: the ra and dec of the source Maj: the major axis of the source s: the width of the image, default 3 arcmin, because it has to be a tiny bit lower than the postage created earlier (width 4 arcmin) or NaNs will appear in the image head and hdulist, the header and hdulist if a postage hasn't been created before. (This is so it doesn't open every time in the loop but is opened before the loop.) Output: i, max_angle, len(err_indices) Which are: the new_Index of the source, the best angle of the orientation, and the amount of orientations that have avg flux value > 80% of the peak avg flux value along the line If plot=True, produces the plots of the best orientation and the Flux vs Orientation as well ''' ################## BEGIN Postage Function ############# if not head: head = pf.getheader(fitsim) hdulist = pf.open(fitsim) # Parse the WCS keywords in the primary HDU wcs = pw.WCS(hdulist[0].header) # Some pixel coordinates of interest. skycrd = np.array([ra, dec]) skycrd = np.array([[ra, dec, 0, 0]], np.float_) # Convert pixel coordinates to world coordinates # The second argument is "origin" -- in this case we're declaring we # have 1-based (Fortran-like) coordinates. pixel = wcs.wcs_sky2pix(skycrd, 1) # Some pixel coordinates of interest. x = pixel[0][0] y = pixel[0][1] pixsize = abs(wcs.wcs.cdelt[0]) if pl.isnan(s): s = 25. N = (s / pixsize) # print 'x=%.5f, y=%.5f, N=%i' %(x,y,N) ximgsize = head.get('NAXIS1') yimgsize = head.get('NAXIS2') if x == 0: x = ximgsize / 2 if y == 0: y = yimgsize / 2 offcentre = False # subimage limits: check if runs over edges xlim1 = x - (N / 2) if (xlim1 < 1): xlim1 = 1 offcentre = True xlim2 = x + (N / 2) if (xlim2 > ximgsize): xlim2 = ximgsize offcentre = True ylim1 = y - (N / 2) if (ylim1 < 1): ylim1 = 1 offcentre = True ylim2 = y + (N / 2) if (ylim2 > yimgsize): offcentre = True ylim2 = yimgsize xl = int(xlim1) yl = int(ylim1) xu = int(xlim2) yu = int(ylim2) ################## END Postage Function ############# # extract the data array instead of making a postage stamp from astropy.nddata.utils import extract_array data_array = extract_array(hdulist[0].data, (yu - yl, xu - xl), (y, x)) # use a radius for the line that is the major axis, # but with 2 pixels more added to the radius # to make sure we do capture the whole source radius = Maj / 60 * 40 #arcsec -- > arcmin --> image units radius = int(radius) + 2 # is chosen arbitrarily # in the P173+55 1 arcmin = 40 image units, should check if this is true everywhere if True in (np.isnan(data_array)): if s < (2 / 60.): print "No hope left for this source " return i, 0.0, 100.5 elif s == (2 / 60.): print "Nan in the cutout image AGAIN ", head['OBJECT'], ' i = ', i try: return find_orientation(i, fitsim, ra, dec, Maj, s=(Maj * 2 / 60 / 60), plot=plot, head=head, hdulist=hdulist) except RuntimeError: print "No hope left for this source, " return i, 0.0, 100.5 else: print "NaN in the cutout image: ", head['OBJECT'], ' i = ', i try: return find_orientation(i, fitsim, ra, dec, Maj, s=(2 / 60.), plot=plot, head=head, hdulist=hdulist) except RuntimeError: print "No hope left for this source, " return i, 0.0, 100.5 from scipy.ndimage import interpolation from scipy.ndimage import map_coordinates #the center of the image is at the halfway point -1 for using array-index xcenter = np.shape(data_array)[0] / 2 - 1 # pixel coordinates ycenter = np.shape(data_array)[0] / 2 - 1 # pixel coordinates # make a line with 'num' points and radius = radius x0, y0 = xcenter - radius, ycenter x1, y1 = xcenter + radius, ycenter num = 1000 x, y = np.linspace(x0, x1, num), np.linspace(y0, y1, num) # make a second and third line to have a linewidth of 3 # x0_2, y0_2 = xcenter-radius,ycenter-1 # x1_2, y1_2 = xcenter+radius,ycenter-1 # x0_3, y0_3 = xcenter-radius,ycenter+1 # x1_3, y1_3 = xcenter+radius,ycenter+1 # x2, y2 = np.linspace(x0_2,x1_2,num), np.linspace(y0_2,y1_2,num) # x3, y3 = np.linspace(x0_3,x1_3,num), np.linspace(y0_3,y1_3,num) # the final orientation will be max_angle max_angle = 0 max_value = 0 # flux values for 0 to 179 degrees of rotation (which is also convenietly their index) all_values = [] for angle in range(0, 180): # using spline order 3 interpolation to rotate the data by 0-180 deg data_array2 = interpolation.rotate(data_array, angle * 1., reshape=False) # extract the values along the line, zi = map_coordinates(data_array2, np.vstack((y, x)), prefilter=False) # zi2 = map_coordinates(data_array2, np.vstack((y2,x2)),prefilter=False) # zi3 = map_coordinates(data_array2, np.vstack((y3,x3)),prefilter=False) # calc the mean flux # zi = zi+zi2+zi3 meanie = np.sum(zi) if meanie > max_value: max_value = meanie max_angle = angle all_values.append(meanie) # calculate all orientiations for which the average flux lies within # 80 per cent of the peak average flux err_orientations = np.where(all_values > (0.8 * max_value))[0] # print 'winner at : ' # print max_angle, ' degrees, average flux (random units): ' , max_value # print 'amount of orientations within 80 per cent : ', len(err_orientations) if len(err_orientations) > 15: classification = 'Large err' else: classification = 'Small err' if plot: data_array2 = interpolation.rotate(data_array, max_angle, reshape=False) plt.imshow(data_array2, origin='lower') plt.plot([x0, x1], [y0, y1], 'r-', alpha=0.3) plt.plot([x0_2, x1_2], [y0_2, y1_2], 'r-', alpha=0.3) plt.plot([x0_3, x1_3], [y0_3, y1_3], 'r-', alpha=0.3) plt.title('Field: ' + head['OBJECT'] + ' | Source ' + str(i) + '\n Best orientation = ' + str(max_angle) + ' degrees | classification: ' + classification) # plt.savefig('/data1/osinga/figures/test2_src'+str(i)+'.png') plt.savefig( '/data1/osinga/figures/cutouts/all_multiple_gaussians2/elongated/' + head['OBJECT'] + 'src_' + str(i) + '.png') # // EDITED TO INCLUDE '2' plt.clf() plt.close() plt.plot(all_values, label='all orientations') plt.scatter(err_orientations, np.array(all_values)[err_orientations], color='y', label='0.8 fraction') plt.axvline(x=max_angle, ymin=0, ymax=1, color='r', label='best orientation') plt.title('Best orientation for Source ' + str(i) + '\nClassification: ' + classification + ' | Error: ' + str(len(err_orientations))) plt.ylabel('Average flux (arbitrary units)') plt.xlabel('orientation (degrees)') plt.legend() plt.xlim(0, 180) # plt.savefig('/data1/osinga/figures/test2_src'+str(i)+'_orientation.png') plt.savefig( '/data1/osinga/figures/cutouts/all_multiple_gaussians2/elongated/' + head['OBJECT'] + 'src_' + str(i) + '_orientation.png') # // EDITED TO INCLUDE '2' plt.clf() plt.close() return i, max_angle, len(err_orientations)