Source code for gammapy.image.radial_profile
# Licensed under a 3-clause BSD style license - see LICENSE.rst
import numpy as np
from astropy.table import QTable
from .core import SkyImage
__all__ = [
'radial_profile',
'radial_profile_label_image',
]
[docs]def radial_profile(image, center, radius):
"""
Image radial profile.
TODO: show example and explain handling of "overflow"
and "underflow" bins (see ``radial_profile_label_image`` docstring).
Calls `numpy.digitize` to compute a label image and then
`scipy.ndimage.sum` to do measurements.
Parameters
----------
image : `~gammapy.image.SkyImage`
Image
center : `~astropy.coordinates.SkyCoord`
Center position
radius : `~astropy.coordinates.Angle`
Offset bin edge array.
Returns
-------
table : `~astropy.table.QTable`
Table with the following fields that define the binning:
* ``RADIUS_BIN_ID`` : Integer bin ID (starts at ``1``).
* ``RADIUS_MEAN`` : Radial bin center
* ``RADIUS_MIN`` : Radial bin minimum edge
* ``RADIUS_MAX`` : Radial bin maximum edge
And the following measurements from the pixels in each bin:
* ``N_PIX`` : Number of pixels
* ``SUM`` : Sum of pixel values
* ``MEAN`` : Mean of pixel values, computed as ``SUM / N_PIX``
Examples
--------
Make some example data::
from astropy.coordinates import Angle
from gammapy.image import SkyImage
image = SkyImage.empty()
image.fill(value=1)
center = image.center
radius = Angle([0.1, 0.2, 0.4, 0.5, 1.0], 'deg')
Compute and print a radial profile::
from gammapy.image import radial_profile
table = radial_profile(image, center, radius)
table.pprint()
If your measurement represents counts, you could e.g. use this
method to compute errors::
import numpy as np
table['SUM_ERR'] = np.sqrt(table['SUM'])
table['MEAN_ERR'] = table['SUM_ERR'] / table['N_PIX']
If you need to do special measurements or error computation
in each bin with access to the pixel values,
you could get the label image and then do the measurements yourself::
labels = radial_profile_label_image(image, center, radius)
labels.show()
"""
labels = radial_profile_label_image(image, center, radius)
# Note: here we could decide to also measure overflow and underflow bins.
index = np.arange(1, len(radius))
table = _radial_profile_measure(image, labels, index)
table['RADIUS_BIN_ID'] = index
table['RADIUS_MIN'] = radius[:-1]
table['RADIUS_MAX'] = radius[1:]
table['RADIUS_MEAN'] = 0.5 * (table['RADIUS_MAX'] + table['RADIUS_MIN'])
meta = dict(
type='radial profile',
center=center,
)
table.meta.update(meta)
return table
[docs]def radial_profile_label_image(image, center, radius):
"""
Image radial profile label image.
The ``radius`` array defines ``n_bins = len(radius) - 1`` bins.
The label image has the following values:
* Value ``1`` to ``n_bins`` for pixels in ``(radius[0], radius[-1])``
* Value ``0`` for pixels with ``r < radius[0]``
* Value ``n_bins`` for pixels with ``r >= radius[-1]``
Parameters
----------
image : `~gammapy.image.SkyImage`
Image
center : `~astropy.coordinates.SkyCoord`
Center position
radius : `~astropy.coordinates.Angle`
Offset bin edge array.
Returns
-------
labels : `~gammapy.image.SkyImage`
Label image (1 to max_label; outside pixels have value 0)
"""
radius_image = image.coordinates().separation(center)
labels = np.digitize(radius_image.deg, radius.deg)
return SkyImage(name='labels', data=labels, wcs=image.wcs.copy())
def _radial_profile_measure(image, labels, index):
"""
Measurements for radial profile.
This is a helper function to do measurements.
TODO: this should call the generic function,
nothing radial profile-specific here.
"""
from scipy import ndimage
# This function takes `SkyImage` objects as inputs
# but only operates on their `data`
image = image.data
labels = labels.data
table = QTable()
table['N_PIX'] = ndimage.sum(np.ones_like(image), labels, index=index)
table['SUM'] = ndimage.sum(image, labels, index=index)
table['MEAN'] = table['SUM'] / table['N_PIX']
return table