Skip to content

Commit

Permalink
deploy: 8da3bb8
Browse files Browse the repository at this point in the history
  • Loading branch information
mwcraig committed Mar 25, 2024
1 parent 0a95876 commit a78b270
Show file tree
Hide file tree
Showing 315 changed files with 77,102 additions and 0 deletions.
4 changes: 4 additions & 0 deletions v/pdev/.buildinfo
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
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)
Loading

0 comments on commit a78b270

Please sign in to comment.