Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add aperture masks #453

Merged
merged 39 commits into from Nov 5, 2016
Merged

Conversation

larrybradley
Copy link
Member

This PR is a significant refactoring of the aperture subpackage to add the ability to get aperture masks. It a continuation of the initial work in #446.

While there are many new features, there are only a few user-facing API changes:

  • the source_id keyword was renamed to indices in the aperture plot() method (because apertures are independent of sources or data).
  • the old aperture masking methods (e.g. get_fractions()) were removed.

Here are the highlights of the other changes:

  • Apertures no longer require a do_photometry() method. do_photometry() is now defined only once, in the PixelAperture class.
  • Apertures now require a to_mask() method. This method is provided by new Mixin classes for each of the PixelAperture shapes.
  • The to_mask() method returns a new ApertureMask object.
  • Added new `mask_area()`` method.
  • The do_photometry() method now has mask and unit keywords.
  • The aperture extents (bounding boxes) are now defined in _slices and _geom_slices properties.

Example usage:

from photutils import CircularAperture
aper = CircularAperture([(40, 40), (50, 50)], r=3)

masks = aper.to_mask(method='exact')    # list of ApertureMask objects (one for each position)
m0 = masks[0]    # an ApertureMask object
m0.data    # ndarray

# mask in an image of given shape
m0.to_image((300, 300))    # ndarray

# apply mask to data
data = np.ones((100, 100)
cutout_data = m0.apply(data)   # ndarray

@larrybradley larrybradley added this to the 0.3 milestone Nov 5, 2016
@larrybradley larrybradley self-assigned this Nov 5, 2016
@tnorth2
Copy link

tnorth2 commented Mar 13, 2017

@larrybradley hi, how exactly can I now use this method to perform the procedure described in #8 ? Despite many functions documented here, and an attempted implementation of the method described above I am unable to perform (what should be straightforward) Median background subtraction through an annulus.

It may be a lack of understanding of what exactly the ApertureMask object is returning on my half, as 'm0.shape' is returning a 2d array of mostly zeros (despite the fact the pixels inside the annulus certainly are not zero). Any direct answers to #8 ? Cheers

@larrybradley
Copy link
Member Author

larrybradley commented Mar 13, 2017

@tnorth2 Thanks for your question and reminding me that I need to expand the aperture mask documentation.

ApertureMask is an object that contains a 2D numpy array of the aperture mask pixel weights (stored in .data) and a bounding box object that defines the bounding box of the mask.

Here's a quick example:

from photutils.datasets import make_100gaussians_image
from photutils import CircularAnnulus

data = make_100gaussians_image()
xypos = (291, 113.5)
ann = CircularAnnulus(xypos, r_in=10, r_out=15)
method = 'exact'
ann_mask = ann.to_mask(method=method)[0]    # only one xypos
plt.imshow(ann_mask.data)

Here's what the annulus mask looks like:
download

Note that with method='exact' (the default), the aperture mask contains partial-pixel weights, thus we need to compute weighted statistics. numpy.average can do this for mean, but unfortunately there is not an analogous weighted-median function. It's on my to-do list to add a weighted median to astropy.stats, but in the meantime there are python packages that provide such functionality.

Weighted mean:

weighted_data = ann_mask.apply(data)
np.average(weighted_data, weights=ann_mask)
4.6969601309256248

If you want to simply use numpy.median, you can set the method='center' to turn-off partial-pixel weighting and then take the median of the non-zero pixels in the weighted data. Full example:

from photutils.datasets import make_100gaussians_image
from photutils import CircularAnnulus

data = make_100gaussians_image()
xypos = (291, 113.5)
ann = CircularAnnulus(xypos, r_in=10, r_out=15)
method = 'center'
ann_mask = ann.to_mask(method=method)[0]    # only one xypos
weighted_data = ann_mask.apply(data)
np.median(weighted_data[weighted_data != 0])
5.0622323212147089

EDIT: I should also note that the .apply() method has been renamed to .multiply() in the development version.

lgbouma added a commit to lgbouma/tr56reduc that referenced this pull request Jun 30, 2017
`photutils` defaults made this somewhat nontrivial, but it can be done
by following astropy/photutils#453
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants