#!/usr/bin/env python
# -*- coding: utf-8 -*-
# #########################################################################
# Copyright (c) 2018, UChicago Argonne, LLC. All rights reserved. #
# #
# Copyright 2018. UChicago Argonne, LLC. This software was produced #
# under U.S. Government contract DE-AC02-06CH11357 for Argonne National #
# Laboratory (ANL), which is operated by UChicago Argonne, LLC for the #
# U.S. Department of Energy. The U.S. Government has rights to use, #
# reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR #
# UChicago Argonne, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR #
# ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is #
# modified to produce derivative works, such modified software should #
# be clearly marked, so as not to confuse it with the version available #
# from ANL. #
# #
# Additionally, redistribution and use in source and binary forms, with #
# or without modification, are permitted provided that the following #
# conditions are met: #
# #
# * Redistributions of source code must retain the above copyright #
# notice, this list of conditions and the following disclaimer. #
# #
# * Redistributions in binary form must reproduce the above copyright #
# notice, this list of conditions and the following disclaimer in #
# the documentation and/or other materials provided with the #
# distribution. #
# #
# * Neither the name of UChicago Argonne, LLC, Argonne National #
# Laboratory, ANL, the U.S. Government, nor the names of its #
# contributors may be used to endorse or promote products derived #
# from this software without specific prior written permission. #
# #
# THIS SOFTWARE IS PROVIDED BY UChicago Argonne, LLC AND CONTRIBUTORS #
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT #
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS #
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UChicago #
# Argonne, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, #
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, #
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; #
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT #
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN #
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE #
# POSSIBILITY OF SUCH DAMAGE. #
# #########################################################################
"""Define functions for plotting and viewing data of various types."""
__author__ = "Doga Gursoy"
__copyright__ = "Copyright (c) 2018, UChicago Argonne, LLC."
__docformat__ = 'restructuredtext en'
__all__ = [
'plot_complex',
'plot_phase',
'trajectory',
'plot_footprint',
'plot_trajectories',
'plot_sino_coverage',
]
import logging
import warnings
from matplotlib import ticker
import matplotlib.pyplot as plt
import numpy as np
logger = logging.getLogger(__name__)
[docs]def plot_complex(Z, rmin=None, rmax=None, imin=None, imax=None):
"""Plot real and imaginary parts of a 2D image size by side.
Takes parameters rmin, rmax, imin, imax to scale the ranges of the real
and imaginary plots.
"""
plt.subplot(1, 2, 1)
plt.imshow(Z.real, vmin=rmin, vmax=rmax)
cb0 = plt.colorbar(orientation='horizontal')
plt.subplot(1, 2, 2)
plt.imshow(Z.imag, vmin=imin, vmax=imax)
cb1 = plt.colorbar(orientation='horizontal')
[docs]def plot_phase(Z, amin=None, amax=None):
"""Plot the amplitude and phase of a 2D image side by side.
Takes parameters amin, amax to scale the range of the amplitude. The phase
is scaled to the range -pi to pi.
"""
amplitude, phase = np.abs(Z), np.angle(Z)
if np.any(amplitude == 0):
warnings.warn(
"This phase plot will be incorrect because "
"the phase of a zero-amplitude complex number is undefined. "
"Adding a small constant to the amplitude may help.")
plt.subplot(1, 2, 1)
plt.imshow(amplitude, vmin=amin, vmax=amax)
cb0 = plt.colorbar(orientation='horizontal')
plt.subplot(1, 2, 2)
plt.imshow(phase, vmin=-np.pi, vmax=np.pi, cmap=plt.cm.twilight)
cb1 = plt.colorbar(orientation='horizontal')
print(np.min(Z), np.max(Z))
[docs]def trajectory(x, y, connect=True, frame=None, pause=True, dt=1e-12):
"""Plot a 2D trajectory."""
if frame is None:
frame = [np.min(x), np.max(x), np.min(y), np.max(y)]
ax = fig.add_subplot(111)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.grid('on')
ax.axis('image')
ax.set_xlim(frame[0], frame[1])
ax.set_ylim(frame[2], frame[3])
if connect is True:
line, = ax.plot([], [], '-or')
else:
line, = ax.plot([], [], 'or')
for m in range(x.size):
line.set_xdata(np.append(line.get_xdata(), x[m]))
line.set_ydata(np.append(line.get_ydata(), y[m]))
plt.draw()
if pause is True:
plt.pause(dt)
[docs]def plot_sino_coverage(
theta, v, h, dwell=None, bins=[16, 8, 4],
probe_grid=[[1]], probe_shape=(0, 0)
): # yapf: disable
"""Plot projections of minimum coverage in the sinogram space."""
# Wrap theta into [0, pi)
theta = theta % (np.pi)
# Set default dwell value
if dwell is None:
dwell = np.ones(theta.shape)
# Make sure probe_grid is array
probe_grid = np.asarray(probe_grid)
# Create one ray for each pixel in the probe grid
dv, dh = np.meshgrid(
np.linspace(0, probe_shape[0], probe_grid.shape[0],
endpoint=False) + probe_shape[0] / probe_grid.shape[0] / 2,
np.linspace(0, probe_shape[1], probe_grid.shape[1], endpoint=False) +
probe_shape[1] / probe_grid.shape[1] / 2,
)
dv = dv.flatten()
dh = dh.flatten()
probe_grid = probe_grid.flatten()
H = np.zeros(bins)
for i in range(probe_grid.size):
if probe_grid[i] > 0:
# Compute histogram
sample = np.stack([theta, v + dv[i]], h + dh[i], axis=1)
dH, edges = np.histogramdd(
sample,
bins=bins,
range=[[0, np.pi], [-.5, .5], [-.5, .5]],
weights=dwell * probe_grid[i])
H += dH
ideal_bin_count = np.sum(dwell) * np.sum(probe_grid) / np.prod(bins)
H /= ideal_bin_count
# Plot
ax1a = plt.subplot(1, 3, 2)
plt.imshow(
np.min(H, axis=0).T, vmin=0, vmax=2, origin="lower", cmap=plt.cm.RdBu)
ax1a.axis('equal')
plt.xticks(np.array([0, bins[1] / 2, bins[1]]) - 0.5, [-.5, 0, .5])
plt.yticks(np.array([0, bins[2] / 2, bins[2]]) - 0.5, [-.5, 0, .5])
plt.xlabel("h")
plt.ylabel("v")
ax1b = plt.subplot(1, 3, 3)
plt.imshow(
np.min(H, axis=1).T, vmin=0, vmax=2, origin="lower", cmap=plt.cm.RdBu)
ax1b.axis('equal')
plt.xlabel('theta')
plt.ylabel("v")
plt.xticks(np.array([0, bins[0]]) - 0.5, [0, r'$\pi$'])
plt.yticks(np.array([0, bins[2] / 2, bins[2]]) - 0.5, [-.5, 0, .5])
ax1c = plt.subplot(1, 3, 1)
plt.imshow(
np.min(H, axis=2), vmin=0, vmax=2, origin="lower", cmap=plt.cm.RdBu)
ax1c.axis('equal')
plt.ylabel('theta')
plt.xlabel("h")
plt.yticks(np.array([0, bins[0]]) - 0.5, [0, r'$\pi$'])
plt.xticks(np.array([0, bins[1] / 2, bins[1]]) - 0.5, [-.5, 0, .5])
return H
[docs]def plot_trajectories(theta, v, h, t):
"""Plot each trajectory as a function of time in the current figure.
Plots two subplots in the current figure. The top one shows horizonal
and vertical position as a function of time and the bottom shows angular
position as a function of time.
Returns
-------
ax1, ax1b : axes
Handles to the two axes
"""
ax1a = plt.subplot(2, 1, 1)
plt.plot(t, h, 'c--', t, v, 'm.')
plt.ylabel('position [cm]')
plt.legend(['h', 'v'])
plt.ylim([-.5, .5])
plt.setp(ax1a.get_xticklabels(), visible=False)
ax1b = plt.subplot(2, 1, 2, sharex=ax1a)
plt.plot(t, theta % (2 * np.pi) / (2 * np.pi), 'yellow')
plt.ylabel(r'theta [$2\pi$]')
plt.xlabel('time [s]')
plt.ylim([0, 1.])
return ax1a, ax1b