def measure_image_moments(image): """Compute 0th, 1st and 2nd moments of an image. NaN values are ignored in the computation. Parameters ---------- image :`astropy.io.fits.ImageHDU` Image to measure on. Returns ------- image moments : list List of image moments: [A, x_cms, y_cms, x_sigma, y_sigma, sqrt(x_sigma * y_sigma)] """ x, y = coordinates(image, lon_sym=True) A = image.data[np.isfinite(image.data)].sum() # Center of mass x_cms = (x * image.data)[np.isfinite(image.data)].sum() / A y_cms = (y * image.data)[np.isfinite(image.data)].sum() / A # Second moments x_var = ((x - x_cms) ** 2 * image.data)[np.isfinite(image.data)].sum() / A y_var = ((y - y_cms) ** 2 * image.data)[np.isfinite(image.data)].sum() / A x_sigma = np.sqrt(x_var) y_sigma = np.sqrt(y_var) return A, x_cms, y_cms, x_sigma, y_sigma, np.sqrt(x_sigma * y_sigma)
def _to_image_bbox(catalog, image): """@todo: make the usage of bbox an option of the function _to_image_simple, otherwise we duplicate a lot of code! Function takes catalog and an empty image. It returns the image of the catalog. catalog = atpy.Table image = kapteyn.FITSimage @todo: This function should have NO explicit references to morphology column names. Do something like: for p in morphology.morph_pars: exec('p = {0}'.format(p)) and then pars = [eval('[??? for x in eval('morphology.{0}_par'.format(??? """ logging.info('Building image out of catalog.') # Select sources in FOV # TODO: improve this check by reading the min, max from # the FITS header instead of finding the min of the array. l, b = utils.coordinates(image) #, glon_sym=True) # Get catalog columns common to all morph types morph_type = catalog.field('morph_type') glon = catalog.field('glon') glat = catalog.field('glat') S_PWN = catalog.field('S_PWN') S_SNR = catalog.field('S_SNR') ext_in_SNR = catalog.field('ext_in_SNR') ext_out_SNR = catalog.field('ext_out_SNR') ext_out_PWN = catalog.field('ext_out_PWN') contributing = np.all([ l.min() < glon + ext_out_SNR, glon - ext_out_SNR < l.max(), b.min() < glat + ext_out_SNR, glat - ext_out_SNR < b.max(), S_SNR > 0 ], axis=0) # Make image, adding one source at a time nsources = contributing.sum() logging.info('Adding {0} sources.'.format(nsources)) logging.info('Skipping {0} sources.'.format(catalog.size - nsources)) for i in range(catalog.size): # Skip sources that are not contributing if not contributing[i]: # logging.debug('Skipping source {0:6d}.'.format(i)) continue # Make a list of parameters appropriate for the morph_type pars = [glon[i], glat[i]] if morph_type[i] == 'sphere2d': pars += [S_PWN[i], ext_out_PWN[i]] elif morph_type[i] == 'shell2d': pars += [S_SNR[i], ext_out_SNR[i], ext_in_SNR[i]] logging.debug('Adding source {0:6d}.'.format(i)) _add_source(image, morph_type[i], pars, l, b)
def measure_image_moments(image): """Compute 0th, 1st and 2nd moments of an image. NaN values are ignored in the computation. Parameters ---------- image :`astropy.io.fits.ImageHDU` Image to measure on. Returns ------- image moments : list List of image moments: [A, x_cms, y_cms, x_sigma, y_sigma, sqrt(x_sigma * y_sigma)] """ x, y = coordinates(image, lon_sym=True) A = image.data[np.isfinite(image.data)].sum() # Center of mass x_cms = (x * image.data)[np.isfinite(image.data)].sum() / A y_cms = (y * image.data)[np.isfinite(image.data)].sum() / A # Second moments x_var = ((x - x_cms)**2 * image.data)[np.isfinite(image.data)].sum() / A y_var = ((y - y_cms)**2 * image.data)[np.isfinite(image.data)].sum() / A x_sigma = np.sqrt(x_var) y_sigma = np.sqrt(y_var) return A, x_cms, y_cms, x_sigma, y_sigma, np.sqrt(x_sigma * y_sigma)
def measure_containment_radius(image, glon, glat, containment_fraction=0.8): """Measure containment radius. Uses `scipy.optimize.brentq`. Parameters ---------- image : `astropy.io.fits.ImageHDU` Image to measure on. glon : float Source longitude in degree. glat : float Source latitude in degree. containment_fraction : float (default 0.8) Containment fraction Returns ------- containment_radius : float Containment radius (pix) """ from scipy.optimize import brentq GLON, GLAT = coordinates(image, lon_sym=True) rr = (GLON - glon)**2 + (GLAT - glat)**2 # Normalize image image.data = image.data / image.data[np.isfinite(image.data)].sum() def func(r): return measure_containment_fraction(r, rr, image.data) - containment_fraction containment_radius = brentq(func, a=0, b=np.sqrt(rr.max())) return containment_radius
def measure_containment_radius(image, glon, glat, containment_fraction=0.8): """Measure containment radius. Uses `scipy.optimize.brentq`. Parameters ---------- image : `astropy.io.fits.ImageHDU` Image to measure on. glon : float Source longitude in degree. glat : float Source latitude in degree. containment_fraction : float (default 0.8) Containment fraction Returns ------- containment_radius : float Containment radius (pix) """ from scipy.optimize import brentq GLON, GLAT = coordinates(image, lon_sym=True) rr = (GLON - glon) ** 2 + (GLAT - glat) ** 2 # Normalize image image.data = image.data / image.data[np.isfinite(image.data)].sum() def func(r): return measure_containment_fraction(r, rr, image.data) - containment_fraction containment_radius = brentq(func, a=0, b=np.sqrt(rr.max())) return containment_radius
def _to_image_bbox(catalog, image): """@todo: make the usage of bbox an option of the function _to_image_simple, otherwise we duplicate a lot of code! Function takes catalog and an empty image. It returns the image of the catalog. catalog = atpy.Table image = kapteyn.FITSimage @todo: This function should have NO explicit references to morphology column names. Do something like: for p in morphology.morph_pars: exec('p = {0}'.format(p)) and then pars = [eval('[??? for x in eval('morphology.{0}_par'.format(??? """ logging.info('Building image out of catalog.') # Select sources in FOV # TODO: improve this check by reading the min, max from # the FITS header instead of finding the min of the array. l, b = utils.coordinates(image)#, glon_sym=True) # Get catalog columns common to all morph types morph_type = catalog.field('morph_type') glon = catalog.field('glon') glat = catalog.field('glat') S_PWN = catalog.field('S_PWN') S_SNR = catalog.field('S_SNR') ext_in_SNR = catalog.field('ext_in_SNR') ext_out_SNR = catalog.field('ext_out_SNR') ext_out_PWN = catalog.field('ext_out_PWN') contributing = np.all([l.min() < glon + ext_out_SNR, glon - ext_out_SNR < l.max(), b.min() < glat + ext_out_SNR, glat - ext_out_SNR < b.max(), S_SNR > 0], axis=0) # Make image, adding one source at a time nsources = contributing.sum() logging.info('Adding {0} sources.'.format(nsources)) logging.info('Skipping {0} sources.'.format(catalog.size - nsources)) for i in range(catalog.size): # Skip sources that are not contributing if not contributing[i]: # logging.debug('Skipping source {0:6d}.'.format(i)) continue # Make a list of parameters appropriate for the morph_type pars = [glon[i], glat[i]] if morph_type[i] == 'sphere2d': pars += [S_PWN[i], ext_out_PWN[i]] elif morph_type[i] == 'shell2d': pars += [S_SNR[i], ext_out_SNR[i], ext_in_SNR[i]] logging.debug('Adding source {0:6d}.'.format(i)) _add_source(image, morph_type[i], pars, l, b)
def _to_image_gaussian(catalog, image): """Add catalog of asymmetric Gaussian sources to an image. @type catalog: atpy.Table @type image: maputils.FITSimage @return: maputils.FITSimage""" from kapteyn.maputils import FITSimage logging.info('Adding %d sources.' % len(catalog.data)) # # Read catalog # l_center = catalog.data['glon'] b_center = catalog.data['glat'] a = catalog.data['a'] b = catalog.data['b'] theta = catalog.data['theta'] flux = catalog.data['flux'] # Compute matrix entries that describe the ellipse, as defined in # the section "9.1.6. Ellipse parameters" in the SExtractor Manual. # Note that clb already contains the factor of 2! # # Another reference giving formulas is # http://en.wikipedia.org/wiki/Gaussian_function cll = np.cos(theta) ** 2 / a ** 2 + np.sin(theta) ** 2 / b ** 2 cbb = np.sin(theta) ** 2 / a ** 2 + np.cos(theta) ** 2 / b ** 2 clb = 2 * np.cos(theta) * np.sin(theta) * (a ** -2 - b ** -2) # # Add sources to image # data = image.dat header = image.hdr for i in range(len(catalog)): # Loop sources l, b = utils.coordinates(image) # convert l to the range -180 to +180 l = np.where(l > 180, l - 360, l) exponent = cll[i] * (l - l_center[i]) ** 2 + \ cbb[i] * (b - b_center[i]) ** 2 + \ clb[i] * (l - l_center[i]) * (b - b_center[i]) data += flux[i] * np.exp(-exponent) # Return image with sources image_out = FITSimage(externalheader=header, externaldata=data) return image_out
def _to_image_simple(catalog, image): """Add sources from a catalog to an image. catalog = atpy.Table image = kapteyn.maputils.FITSimage Note: This implementation doesn't use bounding boxes and thus is slow. You should use the faster _to_image_bbox()""" from kapteyn.maputils import FITSimage from morphology.shapes import morph_types nsources = len(catalog) data = image.dat logging.info('Adding {0} sources.'.format(nsources)) # Get coordinate maps l, b = utils.coordinates(image) # Add sources to image one at a time for i in range(nsources): logging.debug('Adding source {0:3d} of {1:3d}.' ''.format(i, nsources)) # nans = np.isnan(data).sum() # if nans: # logging.debug('Image contains {0} nan entries.'.format(nans)) # Get relevant columns morph_type = catalog[i]['morph_type'] xpos = catalog[i]['glon'] xpos = np.where(xpos > 180, xpos - 360, xpos) ypos = catalog[i]['glat'] ampl = catalog[i]['ampl'] sigma = catalog[i]['sigma'] epsilon = catalog[i]['epsilon'] theta = catalog[i]['theta'] r_in = catalog[i]['r_in'] r_out = catalog[i]['r_out'] pars = { 'delta2d': (xpos, ypos, ampl), 'shell2d': (xpos, ypos, ampl, r_in, r_out), 'gauss2d': (xpos, ypos, ampl, sigma, epsilon, theta) } par = pars[morph_type] data += morph_types[morph_type](par, l, b) return FITSimage(externalheader=image.hdr, externaldata=data)
def _to_image_gaussian(catalog, image): """Add catalog of asymmetric Gaussian sources to an image. @type catalog: atpy.Table @type image: maputils.FITSimage @return: maputils.FITSimage""" from kapteyn.maputils import FITSimage logging.info('Adding %d sources.' % len(catalog.data)) # # Read catalog # l_center = catalog.data['glon'] b_center = catalog.data['glat'] a = catalog.data['a'] b = catalog.data['b'] theta = catalog.data['theta'] flux = catalog.data['flux'] # Compute matrix entries that describe the ellipse, as defined in # the section "9.1.6. Ellipse parameters" in the SExtractor Manual. # Note that clb already contains the factor of 2! # # Another reference giving formulas is # http://en.wikipedia.org/wiki/Gaussian_function cll = np.cos(theta)**2 / a**2 + np.sin(theta)**2 / b**2 cbb = np.sin(theta)**2 / a**2 + np.cos(theta)**2 / b**2 clb = 2 * np.cos(theta) * np.sin(theta) * (a**-2 - b**-2) # # Add sources to image # data = image.dat header = image.hdr for i in range(len(catalog)): # Loop sources l, b = utils.coordinates(image) # convert l to the range -180 to +180 l = np.where(l > 180, l - 360, l) exponent = cll[i] * (l - l_center[i]) ** 2 + \ cbb[i] * (b - b_center[i]) ** 2 + \ clb[i] * (l - l_center[i]) * (b - b_center[i]) data += flux[i] * np.exp(-exponent) # Return image with sources image_out = FITSimage(externalheader=header, externaldata=data) return image_out
def _to_image_simple(catalog, image): """Add sources from a catalog to an image. catalog = atpy.Table image = kapteyn.maputils.FITSimage Note: This implementation doesn't use bounding boxes and thus is slow. You should use the faster _to_image_bbox()""" from kapteyn.maputils import FITSimage from morphology.shapes import morph_types nsources = len(catalog) data = image.dat logging.info('Adding {0} sources.'.format(nsources)) # Get coordinate maps l, b = utils.coordinates(image) # Add sources to image one at a time for i in range(nsources): logging.debug('Adding source {0:3d} of {1:3d}.' ''.format(i, nsources)) # nans = np.isnan(data).sum() # if nans: # logging.debug('Image contains {0} nan entries.'.format(nans)) # Get relevant columns morph_type = catalog[i]['morph_type'] xpos = catalog[i]['glon'] xpos = np.where(xpos > 180, xpos - 360, xpos) ypos = catalog[i]['glat'] ampl = catalog[i]['ampl'] sigma = catalog[i]['sigma'] epsilon = catalog[i]['epsilon'] theta = catalog[i]['theta'] r_in = catalog[i]['r_in'] r_out = catalog[i]['r_out'] pars = {'delta2d': (xpos, ypos, ampl), 'shell2d': (xpos, ypos, ampl, r_in, r_out), 'gauss2d': (xpos, ypos, ampl, sigma, epsilon, theta)} par = pars[morph_type] data += morph_types[morph_type](par, l, b) return FITSimage(externalheader=image.hdr, externaldata=data)
def measure_containment(image, glon, glat, radius): """ Measure containment in a given circle around the source position. Parameters ---------- image : `astropy.io.fits.ImageHDU` Image to measure on. glon : float Source longitude in degree. glat : float Source latitude in degree. radius : float Radius of the region to measure the containment in. """ GLON, GLAT = coordinates(image, lon_sym=True) rr = (GLON - glon) ** 2 + (GLAT - glat) ** 2 return measure_containment_fraction(radius, rr, image.data)
def measure_containment(image, glon, glat, radius): """ Measure containment in a given circle around the source position. Parameters ---------- image : `astropy.io.fits.ImageHDU` Image to measure on. glon : float Source longitude in degree. glat : float Source latitude in degree. radius : float Radius of the region to measure the containment in. """ GLON, GLAT = coordinates(image, lon_sym=True) rr = (GLON - glon)**2 + (GLAT - glat)**2 return measure_containment_fraction(radius, rr, image.data)
def lookup_max(image, GLON, GLAT, theta): """Look up the max image values within a circle of radius theta around lists of given positions (nan if outside)""" from .utils import coordinates GLON = np.asarray(GLON) GLON = np.where(GLON > 180, GLON - 360, GLON) GLAT = np.asarray(GLAT) n_pos = len(GLON) theta = np.asarray(theta) * np.ones(n_pos, dtype='float32') ll, bb = coordinates(image) val = np.nan * np.ones(n_pos, dtype='float32') for ii in range(n_pos): mask = ((GLON[ii] - ll)**2 + (GLAT[ii] - bb)**2 <= theta[ii]**2) try: val[ii] = image.data[mask].max() except ValueError: pass return val
def lookup_max(image, GLON, GLAT, theta): """Look up the max image values within a circle of radius theta around lists of given positions (nan if outside)""" from .utils import coordinates GLON = np.asarray(GLON) GLON = np.where(GLON > 180, GLON - 360, GLON) GLAT = np.asarray(GLAT) n_pos = len(GLON) theta = np.asarray(theta) * np.ones(n_pos, dtype='float32') ll, bb = coordinates(image) val = np.nan * np.ones(n_pos, dtype='float32') for ii in range(n_pos): mask = ((GLON[ii] - ll) ** 2 + (GLAT[ii] - bb) ** 2 <= theta[ii] ** 2) try: val[ii] = image.data[mask].max() except ValueError: pass return val
def _add_source(image, morph_type, pars, l, b): """Adds a specified source at a given position.""" from gammapy.morphology.shapes import morph_types # Get position and extension info # TODO: this won't work for delta2d, which doesn't # have a pars[3] entry! glon, glat, ext = pars[0], pars[1], pars[3] logging.debug('Adding source of type {0} at position ' '{1}, {2} with extension {3}' ''.format(morph_type, glon, glat, ext)) # Get indices of position #import IPython; IPython.embed() x_pix, y_pix = np.arange(image.header['NAXIS1']), np.arange( image.header['NAXIS2']) # Correct pixel numbering x_pix = np.floor(x_pix) - 1 y_pix = np.floor(y_pix) - 1 # Get data data = image.data pixsize = image.header['CDELT2'] # Compute boxsize with one pixel margin boxsize = 2 * np.floor(ext / pixsize + 2) # Determine corner pixels left_bottom = [y_pix, x_pix] - boxsize / 2 right_top = [y_pix, x_pix] + boxsize / 2 # Determine image boundaries y_bound, x_bound = data.shape # Correct Behaviour at the boundaries if left_bottom[1] < 0 or left_bottom[0] < 0 or right_top[ 1] > x_bound or right_top[0] > y_bound: logging.debug('Source is not completely in image.') # Get out of range coordinate map l, b = utils.coordinates(image, x_pix, y_pix, boxsize) # Get image of source box_image = morph_types[morph_type](pars, l, b) # Get slices s_x, s_y, b_x, b_y = _get_slices(left_bottom, right_top, x_bound, y_bound) # For debugging logging.debug('left_bottom: {0}'.format(left_bottom)) logging.debug('right_top: {0}'.format(right_top)) logging.debug('box_image.shape: {0}'.format(box_image.shape)) logging.debug('s_x: {0}'.format(s_x)) logging.debug('s_y: {0}'.format(s_y)) logging.debug('b_x: {0}'.format(b_x)) logging.debug('b_y: {0}'.format(b_y)) logging.debug('box_image.shape: {0}'.format(box_image[b_y, b_x].shape)) logging.debug('data.shape: {0}'.format(data[s_y, s_x].shape)) # Add box to image data[s_y, s_x] += box_image[b_y, b_x] else: logging.debug('Source is completely in image.') s_x = slice(left_bottom[1], right_top[1]) s_y = slice(left_bottom[0], right_top[0]) # Get image of source box_image = morph_types[morph_type](pars, l[s_y, s_x], b[s_y, s_x]) # Add box to image data[s_y, s_x] += box_image
""" Script to determine what fraction of source and diffuse flux is found in the large and small region""" from astropy.io import fits filename = raw_input('Image filename: ') source = fits.open(filename)[1] from gammapy.image.utils import coordinates, solid_angle lats, lons = coordinates(source) solid_angle = solid_angle(source) region_glat_high = 5 region_glat_low = -5 region_glon_high = 100 region_glon_low = -100 mask1 = (region_glat_low < lats) & (lats < region_glat_high) mask2 = (region_glon_low < lons) & (lons < region_glon_high) mask = mask1 & mask2 a = source.data #/ solid_angle.value source_flux_frac = a[mask].sum() / a.sum() print "Total Flux in Image" print a.sum() print "Galactic Flux" print a[mask].sum() print "Source Flux Fraction" print source_flux_frac
def _add_source(image, morph_type, pars, l, b): """Adds a specified source at a given position.""" from gammapy.morphology.shapes import morph_types # Get position and extension info # TODO: this won't work for delta2d, which doesn't # have a pars[3] entry! glon, glat, ext = pars[0], pars[1], pars[3] logging.debug('Adding source of type {0} at position ' '{1}, {2} with extension {3}' ''.format(morph_type, glon, glat, ext)) # Get indices of position #import IPython; IPython.embed() x_pix, y_pix = np.arange(image.header['NAXIS1']), np.arange(image.header['NAXIS2']) # Correct pixel numbering x_pix = np.floor(x_pix) - 1 y_pix = np.floor(y_pix) - 1 # Get data data = image.data pixsize = image.header['CDELT2'] # Compute boxsize with one pixel margin boxsize = 2 * np.floor(ext / pixsize + 2) # Determine corner pixels left_bottom = [y_pix, x_pix] - boxsize / 2 right_top = [y_pix, x_pix] + boxsize / 2 # Determine image boundaries y_bound, x_bound = data.shape # Correct Behaviour at the boundaries if left_bottom[1] < 0 or left_bottom[0] < 0 or right_top[1] > x_bound or right_top[0] > y_bound: logging.debug('Source is not completely in image.') # Get out of range coordinate map l, b = utils.coordinates(image, x_pix, y_pix, boxsize) # Get image of source box_image = morph_types[morph_type](pars, l, b) # Get slices s_x, s_y, b_x, b_y = _get_slices(left_bottom, right_top, x_bound, y_bound) # For debugging logging.debug('left_bottom: {0}'.format(left_bottom)) logging.debug('right_top: {0}'.format(right_top)) logging.debug('box_image.shape: {0}'.format(box_image.shape)) logging.debug('s_x: {0}'.format(s_x)) logging.debug('s_y: {0}'.format(s_y)) logging.debug('b_x: {0}'.format(b_x)) logging.debug('b_y: {0}'.format(b_y)) logging.debug('box_image.shape: {0}'.format(box_image[b_y, b_x].shape)) logging.debug('data.shape: {0}'.format(data[s_y, s_x].shape)) # Add box to image data[s_y, s_x] += box_image[b_y, b_x] else: logging.debug('Source is completely in image.') s_x = slice(left_bottom[1], right_top[1]) s_y = slice(left_bottom[0], right_top[0]) # Get image of source box_image = morph_types[morph_type](pars, l[s_y, s_x], b[s_y, s_x]) # Add box to image data[s_y, s_x] += box_image
""" Script to determine what fraction of source and diffuse flux is found in the large and small region""" from astropy.io import fits import numpy as np filename = raw_input('Image filename: ') source = fits.open(filename)[1] from gammapy.image.utils import coordinates, solid_angle lats, lons = coordinates(source) solid_angle = solid_angle(source) region_glat_high = 5 region_glat_low = -5 region_glon_high = 100 region_glon_low = -100 mask1 = (region_glat_low < lats) & (lats < region_glat_high) mask2 = (region_glon_low < lons) & (lons < region_glon_high) mask = mask1 & mask2 a = np.nan_to_num(source.data) source_flux_frac = a[mask].sum()/a.sum() print "Total Flux in Image" print a.sum() print "Galactic Flux" print a[mask].sum() print "Source Flux Fraction" print source_flux_frac