Source code for astroimtools.filtering

# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
Image filtering utilities.
"""

import numpy as np
from astropy.modeling.models import Ellipse2D

__all__ = ['circular_footprint', 'circular_annulus_footprint',
           'elliptical_footprint', 'elliptical_annulus_footprint']


[docs] def circular_footprint(radius, dtype=int): """ Create a circular footprint. A pixel is considered to be entirely in or out of the footprint depending on whether its center is in or out of the footprint. The size of the output array is the minimal bounding box for the footprint. Parameters ---------- radius : int The radius of the circular footprint. dtype : data-type, optional The data type of the output `~numpy.ndarray`. Returns ------- footprint : `~numpy.ndarray` A footprint where array elements are 1 within the footprint and 0 otherwise. Examples -------- >>> from astroimtools import circular_footprint >>> circular_footprint(2) array([[0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [1, 1, 1, 1, 1], [0, 1, 1, 1, 0], [0, 0, 1, 0, 0]]) """ x = np.arange(-radius, radius + 1) xx, yy = np.meshgrid(x, x) return np.array((xx**2 + yy**2) <= radius**2, dtype=dtype)
[docs] def circular_annulus_footprint(radius_inner, radius_outer, dtype=int): """ Create a circular annulus footprint. A pixel is considered to be entirely in or out of the footprint depending on whether its center is in or out of the footprint. The size of the output array is the minimal bounding box for the footprint. Parameters ---------- radius_inner : int The inner radius of the circular annulus. radius_outer : int The outer radius of the circular annulus. dtype : data-type, optional The data type of the output `~numpy.ndarray`. Returns ------- footprint : `~numpy.ndarray` A footprint where array elements are 1 within the footprint and 0 otherwise. Examples -------- >>> from astroimtools import circular_footprint >>> circular_annulus_footprint(1, 2) array([[0, 0, 1, 0, 0], [0, 1, 0, 1, 0], [1, 0, 0, 0, 1], [0, 1, 0, 1, 0], [0, 0, 1, 0, 0]]) """ if radius_inner > radius_outer: raise ValueError('radius_outer must be >= radius_inner') size = (radius_outer * 2) + 1 y, x = np.mgrid[0:size, 0:size] circle_outer = Ellipse2D(1, radius_outer, radius_outer, radius_outer, radius_outer, theta=0)(x, y) circle_inner = Ellipse2D(1., radius_outer, radius_outer, radius_inner, radius_inner, theta=0)(x, y) return np.asarray(circle_outer - circle_inner, dtype=dtype)
[docs] def elliptical_footprint(a, b, theta=0, dtype=int): """ Create an elliptical footprint. A pixel is considered to be entirely in or out of the footprint depending on whether its center is in or out of the footprint. The size of the output array is the minimal bounding box for the footprint. Parameters ---------- a : int The semimajor axis. b : int The semiminor axis. theta : float, optional The rotation angle in radians of the semimajor axis. The angle is measured counterclockwise from the positive x axis. dtype : data-type, optional The data type of the output `~numpy.ndarray`. Returns ------- footprint : `~numpy.ndarray` A footprint where array elements are 1 within the footprint and 0 otherwise. Examples -------- >>> import numpy as np >>> from astroimtools import elliptical_footprint >>> elliptical_footprint(4, 2, theta=np.pi/2.) array([[0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0], [1, 1, 1, 1, 1], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0], [0, 0, 1, 0, 0]]) """ if b > a: raise ValueError('a must be >= b') size = (a * 2) + 1 y, x = np.mgrid[0:size, 0:size] ellipse = Ellipse2D(1.0, a, a, a, b, theta=theta)(x, y) # crop to minimal bounding box yi, xi = ellipse.nonzero() idx = (slice(min(yi), max(yi) + 1), slice(min(xi), max(xi) + 1)) return np.asarray(ellipse[idx], dtype=dtype)
[docs] def elliptical_annulus_footprint(a_inner, a_outer, b_inner, theta=0, dtype=int): """ Create an elliptical annulus footprint. A pixel is considered to be entirely in or out of the footprint depending on whether its center is in or out of the footprint. The size of the output array is the minimal bounding box for the footprint. Parameters ---------- a_inner : int The inner semimajor axis. a_outer : int The outer semimajor axis. b_inner : int The inner semiminor axis. The outer semiminor axis is calculated using the same axis ratio as the semimajor axis: .. math:: b_{outer} = b_{inner} \\left( \\frac{a_{outer}}{a_{inner}} \\right) theta : float, optional The rotation angle in radians of the semimajor axis. The angle is measured counterclockwise from the positive x axis. dtype : data-type, optional The data type of the output `~numpy.ndarray`. Returns ------- footprint : `~numpy.ndarray` A footprint where array elements are 1 within the footprint and 0 otherwise. Examples -------- >>> import numpy as np >>> from astroimtools import elliptical_annulus_footprint >>> elliptical_annulus_footprint(2, 4, 1, theta=np.pi/2.) array([[0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [0, 1, 0, 1, 0], [0, 1, 0, 1, 0], [1, 0, 0, 0, 1], [0, 1, 0, 1, 0], [0, 1, 0, 1, 0], [0, 1, 1, 1, 0], [0, 0, 1, 0, 0]]) """ if a_inner > a_outer: raise ValueError('a_outer must be >= a_inner') if b_inner > a_inner: raise ValueError('a_inner must be >= b_inner') size = (a_outer * 2) + 1 y, x = np.mgrid[0:size, 0:size] b_outer = b_inner * (a_outer / a_inner) ellipse_outer = Ellipse2D(1, a_outer, a_outer, a_outer, b_outer, theta=theta)(x, y) ellipse_inner = Ellipse2D(1, a_outer, a_outer, a_inner, b_inner, theta=theta)(x, y) annulus = ellipse_outer - ellipse_inner # crop to minimal bounding box yi, xi = annulus.nonzero() idx = (slice(min(yi), max(yi) + 1), slice(min(xi), max(xi) + 1)) return np.asarray(annulus[idx], dtype=dtype)