-
-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
315 changed files
with
77,102 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Sphinx build info version 1 | ||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. | ||
config: 8b4d12baca50d6e4cfc56a5a419baf15 | ||
tags: 645f666f9bcd5a90fca523b33c5a78b7 |
236 changes: 236 additions & 0 deletions
236
v/pdev/_downloads/59793ee2752570cbec02c0d2fd15151e/convenience_functions.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
from astropy import visualization as aviz | ||
from astropy.nddata.blocks import block_reduce | ||
from astropy.nddata.utils import Cutout2D | ||
from matplotlib import pyplot as plt | ||
|
||
|
||
def show_image(image, | ||
percl=99, percu=None, is_mask=False, | ||
figsize=(10, 10), | ||
cmap='viridis', log=False, clip=True, | ||
show_colorbar=True, show_ticks=True, | ||
fig=None, ax=None, input_ratio=None): | ||
""" | ||
Show an image in matplotlib with some basic astronomically-appropriat stretching. | ||
Parameters | ||
---------- | ||
image | ||
The image to show | ||
percl : number | ||
The percentile for the lower edge of the stretch (or both edges if ``percu`` is None) | ||
percu : number or None | ||
The percentile for the upper edge of the stretch (or None to use ``percl`` for both) | ||
figsize : 2-tuple | ||
The size of the matplotlib figure in inches | ||
""" | ||
if percu is None: | ||
percu = percl | ||
percl = 100 - percl | ||
|
||
if (fig is None and ax is not None) or (fig is not None and ax is None): | ||
raise ValueError('Must provide both "fig" and "ax" ' | ||
'if you provide one of them') | ||
elif fig is None and ax is None: | ||
if figsize is not None: | ||
# Rescale the fig size to match the image dimensions, roughly | ||
image_aspect_ratio = image.shape[0] / image.shape[1] | ||
figsize = (max(figsize) * image_aspect_ratio, max(figsize)) | ||
|
||
fig, ax = plt.subplots(1, 1, figsize=figsize) | ||
|
||
|
||
# To preserve details we should *really* downsample correctly and | ||
# not rely on matplotlib to do it correctly for us (it won't). | ||
|
||
# So, calculate the size of the figure in pixels, block_reduce to | ||
# roughly that,and display the block reduced image. | ||
|
||
# Thanks, https://stackoverflow.com/questions/29702424/how-to-get-matplotlib-figure-size | ||
fig_size_pix = fig.get_size_inches() * fig.dpi | ||
|
||
ratio = (image.shape // fig_size_pix).max() | ||
|
||
if ratio < 1: | ||
ratio = 1 | ||
|
||
ratio = input_ratio or ratio | ||
|
||
reduced_data = block_reduce(image, ratio) | ||
|
||
if not is_mask: | ||
# Divide by the square of the ratio to keep the flux the same in the | ||
# reduced image. We do *not* want to do this for images which are | ||
# masks, since their values should be zero or one. | ||
reduced_data = reduced_data / ratio**2 | ||
|
||
# Of course, now that we have downsampled, the axis limits are changed to | ||
# match the smaller image size. Setting the extent will do the trick to | ||
# change the axis display back to showing the actual extent of the image. | ||
extent = [0, image.shape[1], 0, image.shape[0]] | ||
|
||
if log: | ||
stretch = aviz.LogStretch() | ||
else: | ||
stretch = aviz.LinearStretch() | ||
|
||
norm = aviz.ImageNormalize(reduced_data, | ||
interval=aviz.AsymmetricPercentileInterval(percl, percu), | ||
stretch=stretch, clip=clip) | ||
|
||
if is_mask: | ||
# The image is a mask in which pixels should be zero or one. | ||
# block_reduce may have changed some of the values, so reset here. | ||
reduced_data = reduced_data > 0 | ||
# Set the image scale limits appropriately. | ||
scale_args = dict(vmin=0, vmax=1) | ||
else: | ||
scale_args = dict(norm=norm) | ||
|
||
im = ax.imshow(reduced_data, origin='lower', | ||
cmap=cmap, extent=extent, aspect='equal', **scale_args) | ||
|
||
if show_colorbar: | ||
# I haven't a clue why the fraction and pad arguments below work to make | ||
# the colorbar the same height as the image, but they do....unless the image | ||
# is wider than it is tall. Sticking with this for now anyway... | ||
# Thanks: https://stackoverflow.com/a/26720422/3486425 | ||
fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04) | ||
# In case someone in the future wants to improve this: | ||
# https://joseph-long.com/writing/colorbars/ | ||
# https://stackoverflow.com/a/33505522/3486425 | ||
# https://matplotlib.org/mpl_toolkits/axes_grid/users/overview.html#colorbar-whose-height-or-width-in-sync-with-the-master-axes | ||
|
||
if not show_ticks: | ||
ax.tick_params(labelbottom=False, labelleft=False, labelright=False, labeltop=False) | ||
|
||
|
||
def image_snippet(image, center, width=50, axis=None, fig=None, | ||
is_mask=False, pad_black=False, **kwargs): | ||
""" | ||
Display a subsection of an image about a center. | ||
Parameters | ||
---------- | ||
image : numpy array | ||
The full image from which a section is to be taken. | ||
center : list-like | ||
The location of the center of the cutout. | ||
width : int, optional | ||
Width of the cutout, in pixels. | ||
axis : matplotlib.Axes instance, optional | ||
Axis on which the image should be displayed. | ||
fig : matplotlib.Figure, optional | ||
Figure on which the image should be displayed. | ||
is_mask : bool, optional | ||
Set to ``True`` if the image is a mask, i.e. all values are | ||
either zero or one. | ||
pad_black : bool, optional | ||
If ``True``, pad edges of the image with zeros to fill out width | ||
if the slice is near the edge. | ||
""" | ||
if pad_black: | ||
sub_image = Cutout2D(image, center, width, mode='partial', fill_value=0) | ||
else: | ||
# Return a smaller subimage if extent goes out side image | ||
sub_image = Cutout2D(image, center, width, mode='trim') | ||
show_image(sub_image.data, cmap='gray', ax=axis, fig=fig, | ||
show_colorbar=False, show_ticks=False, is_mask=is_mask, | ||
**kwargs) | ||
|
||
|
||
def _mid(sl): | ||
return (sl.start + sl.stop) // 2 | ||
|
||
|
||
def display_cosmic_rays(cosmic_rays, images, titles=None, | ||
only_display_rays=None): | ||
""" | ||
Display cutouts of the region around each cosmic ray and the other images | ||
passed in. | ||
Parameters | ||
---------- | ||
cosmic_rays : photutils.segmentation.SegmentationImage | ||
The segmented cosmic ray image returned by ``photuils.detect_source``. | ||
images : list of images | ||
The list of images to be displayed. Each image becomes a column in | ||
the generated plot. The first image must be the cosmic ray mask. | ||
titles : list of str | ||
Titles to be put above the first row of images. | ||
only_display_rays : list of int, optional | ||
The number of the cosmic ray(s) to display. The default value, | ||
``None``, means display them all. The number of the cosmic ray is | ||
its index in ``cosmic_rays``, which is also the number displayed | ||
on the mask. | ||
""" | ||
# Check whether the first image is actually a mask. | ||
|
||
if not ((images[0] == 0) | (images[0] == 1)).all(): | ||
raise ValueError('The first image must be a mask with ' | ||
'values of zero or one') | ||
|
||
if only_display_rays is None: | ||
n_rows = len(cosmic_rays.slices) | ||
else: | ||
n_rows = len(only_display_rays) | ||
|
||
n_columns = len(images) | ||
|
||
width = 12 | ||
|
||
# The height below is *CRITICAL*. If the aspect ratio of the figure as | ||
# a whole does not allow for square plots then one ends up with a bunch | ||
# of whitespace. The plots here are square by design. | ||
height = width / n_columns * n_rows | ||
fig, axes = plt.subplots(n_rows, n_columns, sharex=False, sharey='row', | ||
figsize=(width, height)) | ||
|
||
# Generate empty titles if none were provided. | ||
if titles is None: | ||
titles = [''] * n_columns | ||
|
||
display_row = 0 | ||
|
||
for row, s in enumerate(cosmic_rays.slices): | ||
if only_display_rays is not None: | ||
if row not in only_display_rays: | ||
# We are not supposed to display this one, so skip it. | ||
continue | ||
|
||
x = _mid(s[1]) | ||
y = _mid(s[0]) | ||
|
||
for column, plot_info in enumerate(zip(images, titles)): | ||
image = plot_info[0] | ||
title = plot_info[1] | ||
is_mask = column == 0 | ||
ax = axes[display_row, column] | ||
image_snippet(image, (x, y), width=80, axis=ax, fig=fig, | ||
is_mask=is_mask) | ||
if is_mask: | ||
ax.annotate('Cosmic ray {}'.format(row), (0.1, 0.9), | ||
xycoords='axes fraction', | ||
color='cyan', fontsize=20) | ||
|
||
if display_row == 0: | ||
# Only set the title if it isn't empty. | ||
if title: | ||
ax.set_title(title) | ||
|
||
display_row = display_row + 1 | ||
|
||
# This choice results in the images close to each other but with | ||
# a small gap. | ||
plt.subplots_adjust(wspace=0.1, hspace=0.05) |
Oops, something went wrong.