mirror of
https://github.com/wassname/scikit-image.git
synced 2026-06-27 22:08:28 +08:00
213 lines
6.4 KiB
Python
213 lines
6.4 KiB
Python
import numpy as np
|
|
from ..qt import QtWidgets, has_qt, FigureManagerQT, FigureCanvasQTAgg
|
|
from ..._shared.utils import warn
|
|
import matplotlib as mpl
|
|
from matplotlib.figure import Figure
|
|
from matplotlib import _pylab_helpers
|
|
from matplotlib.colors import LinearSegmentedColormap
|
|
|
|
if has_qt and 'agg' not in mpl.get_backend().lower():
|
|
warn("Recommended matplotlib backend is `Agg` for full "
|
|
"skimage.viewer functionality.")
|
|
|
|
|
|
__all__ = ['init_qtapp', 'start_qtapp', 'RequiredAttr', 'figimage',
|
|
'LinearColormap', 'ClearColormap', 'FigureCanvas', 'new_plot',
|
|
'update_axes_image']
|
|
|
|
|
|
QApp = None
|
|
|
|
|
|
def init_qtapp():
|
|
"""Initialize QAppliction.
|
|
|
|
The QApplication needs to be initialized before creating any QWidgets
|
|
"""
|
|
global QApp
|
|
QApp = QtWidgets.QApplication.instance()
|
|
if QApp is None:
|
|
QApp = QtWidgets.QApplication([])
|
|
return QApp
|
|
|
|
|
|
def is_event_loop_running(app=None):
|
|
"""Return True if event loop is running."""
|
|
if app is None:
|
|
app = init_qtapp()
|
|
if hasattr(app, '_in_event_loop'):
|
|
return app._in_event_loop
|
|
else:
|
|
return False
|
|
|
|
|
|
def start_qtapp(app=None):
|
|
"""Start Qt mainloop"""
|
|
if app is None:
|
|
app = init_qtapp()
|
|
if not is_event_loop_running(app):
|
|
app._in_event_loop = True
|
|
app.exec_()
|
|
app._in_event_loop = False
|
|
else:
|
|
app._in_event_loop = True
|
|
|
|
|
|
class RequiredAttr(object):
|
|
"""A class attribute that must be set before use."""
|
|
|
|
instances = dict()
|
|
|
|
def __init__(self, init_val=None):
|
|
self.instances[self, None] = init_val
|
|
|
|
def __get__(self, obj, objtype):
|
|
value = self.instances[self, obj]
|
|
if value is None:
|
|
raise AttributeError('Required attribute not set')
|
|
return value
|
|
|
|
def __set__(self, obj, value):
|
|
self.instances[self, obj] = value
|
|
|
|
|
|
class LinearColormap(LinearSegmentedColormap):
|
|
"""LinearSegmentedColormap in which color varies smoothly.
|
|
|
|
This class is a simplification of LinearSegmentedColormap, which doesn't
|
|
support jumps in color intensities.
|
|
|
|
Parameters
|
|
----------
|
|
name : str
|
|
Name of colormap.
|
|
|
|
segmented_data : dict
|
|
Dictionary of 'red', 'green', 'blue', and (optionally) 'alpha' values.
|
|
Each color key contains a list of `x`, `y` tuples. `x` must increase
|
|
monotonically from 0 to 1 and corresponds to input values for a
|
|
mappable object (e.g. an image). `y` corresponds to the color
|
|
intensity.
|
|
|
|
"""
|
|
def __init__(self, name, segmented_data, **kwargs):
|
|
segmented_data = dict((key, [(x, y, y) for x, y in value])
|
|
for key, value in segmented_data.items())
|
|
LinearSegmentedColormap.__init__(self, name, segmented_data, **kwargs)
|
|
|
|
|
|
class ClearColormap(LinearColormap):
|
|
"""Color map that varies linearly from alpha = 0 to 1
|
|
"""
|
|
def __init__(self, rgb, max_alpha=1, name='clear_color'):
|
|
r, g, b = rgb
|
|
cg_speq = {'blue': [(0.0, b), (1.0, b)],
|
|
'green': [(0.0, g), (1.0, g)],
|
|
'red': [(0.0, r), (1.0, r)],
|
|
'alpha': [(0.0, 0.0), (1.0, max_alpha)]}
|
|
LinearColormap.__init__(self, name, cg_speq)
|
|
|
|
|
|
class FigureCanvas(FigureCanvasQTAgg):
|
|
"""Canvas for displaying images."""
|
|
def __init__(self, figure, **kwargs):
|
|
self.fig = figure
|
|
FigureCanvasQTAgg.__init__(self, self.fig)
|
|
FigureCanvasQTAgg.setSizePolicy(self,
|
|
QtWidgets.QSizePolicy.Expanding,
|
|
QtWidgets.QSizePolicy.Expanding)
|
|
FigureCanvasQTAgg.updateGeometry(self)
|
|
|
|
def resizeEvent(self, event):
|
|
FigureCanvasQTAgg.resizeEvent(self, event)
|
|
# Call to `resize_event` missing in FigureManagerQT.
|
|
# See https://github.com/matplotlib/matplotlib/pull/1585
|
|
self.resize_event()
|
|
|
|
|
|
def new_canvas(*args, **kwargs):
|
|
"""Return a new figure canvas."""
|
|
allnums = _pylab_helpers.Gcf.figs.keys()
|
|
num = max(allnums) + 1 if allnums else 1
|
|
|
|
FigureClass = kwargs.pop('FigureClass', Figure)
|
|
figure = FigureClass(*args, **kwargs)
|
|
canvas = FigureCanvas(figure)
|
|
fig_manager = FigureManagerQT(canvas, num)
|
|
return fig_manager.canvas
|
|
|
|
|
|
def new_plot(parent=None, subplot_kw=None, **fig_kw):
|
|
"""Return new figure and axes.
|
|
|
|
Parameters
|
|
----------
|
|
parent : QtWidget
|
|
Qt widget that displays the plot objects. If None, you must manually
|
|
call ``canvas.setParent`` and pass the parent widget.
|
|
subplot_kw : dict
|
|
Keyword arguments passed ``matplotlib.figure.Figure.add_subplot``.
|
|
fig_kw : dict
|
|
Keyword arguments passed ``matplotlib.figure.Figure``.
|
|
"""
|
|
if subplot_kw is None:
|
|
subplot_kw = {}
|
|
canvas = new_canvas(**fig_kw)
|
|
canvas.setParent(parent)
|
|
|
|
fig = canvas.figure
|
|
ax = fig.add_subplot(1, 1, 1, **subplot_kw)
|
|
return fig, ax
|
|
|
|
|
|
def figimage(image, scale=1, dpi=None, **kwargs):
|
|
"""Return figure and axes with figure tightly surrounding image.
|
|
|
|
Unlike pyplot.figimage, this actually plots onto an axes object, which
|
|
fills the figure. Plotting the image onto an axes allows for subsequent
|
|
overlays of axes artists.
|
|
|
|
Parameters
|
|
----------
|
|
image : array
|
|
image to plot
|
|
scale : float
|
|
If scale is 1, the figure and axes have the same dimension as the
|
|
image. Smaller values of `scale` will shrink the figure.
|
|
dpi : int
|
|
Dots per inch for figure. If None, use the default rcParam.
|
|
"""
|
|
dpi = dpi if dpi is not None else mpl.rcParams['figure.dpi']
|
|
kwargs.setdefault('interpolation', 'nearest')
|
|
kwargs.setdefault('cmap', 'gray')
|
|
|
|
h, w, d = np.atleast_3d(image).shape
|
|
figsize = np.array((w, h), dtype=float) / dpi * scale
|
|
|
|
fig, ax = new_plot(figsize=figsize, dpi=dpi)
|
|
fig.subplots_adjust(left=0, bottom=0, right=1, top=1)
|
|
|
|
ax.set_axis_off()
|
|
ax.imshow(image, **kwargs)
|
|
ax.figure.canvas.draw()
|
|
return fig, ax
|
|
|
|
|
|
def update_axes_image(image_axes, image):
|
|
"""Update the image displayed by an image plot.
|
|
|
|
This sets the image plot's array and updates its shape appropriately
|
|
|
|
Parameters
|
|
----------
|
|
image_axes : `matplotlib.image.AxesImage`
|
|
Image axes to update.
|
|
image : array
|
|
Image array.
|
|
"""
|
|
image_axes.set_array(image)
|
|
|
|
# Adjust size if new image shape doesn't match the original
|
|
h, w = image.shape[:2]
|
|
image_axes.set_extent((0, w, h, 0))
|