Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
238
venv/Lib/site-packages/skimage/viewer/canvastools/painttool.py
Normal file
238
venv/Lib/site-packages/skimage/viewer/canvastools/painttool.py
Normal file
|
@ -0,0 +1,238 @@
|
|||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.colors as mcolors
|
||||
LABELS_CMAP = mcolors.ListedColormap(['white', 'red', 'dodgerblue', 'gold',
|
||||
'greenyellow', 'blueviolet'])
|
||||
from ...viewer.canvastools.base import CanvasToolBase
|
||||
|
||||
|
||||
__all__ = ['PaintTool']
|
||||
|
||||
|
||||
class PaintTool(CanvasToolBase):
|
||||
"""Widget for painting on top of a plot.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
manager : Viewer or PlotPlugin.
|
||||
Skimage viewer or plot plugin object.
|
||||
overlay_shape : shape tuple
|
||||
2D shape tuple used to initialize overlay image.
|
||||
radius : int
|
||||
The size of the paint cursor.
|
||||
alpha : float (between [0, 1])
|
||||
Opacity of overlay.
|
||||
on_move : function
|
||||
Function called whenever a control handle is moved.
|
||||
This function must accept the end points of line as the only argument.
|
||||
on_release : function
|
||||
Function called whenever the control handle is released.
|
||||
on_enter : function
|
||||
Function called whenever the "enter" key is pressed.
|
||||
rect_props : dict
|
||||
Properties for :class:`matplotlib.patches.Rectangle`. This class
|
||||
redefines defaults in :class:`matplotlib.widgets.RectangleSelector`.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
overlay : array
|
||||
Overlay of painted labels displayed on top of image.
|
||||
label : int
|
||||
Current paint color.
|
||||
|
||||
Examples
|
||||
----------
|
||||
>>> from skimage.data import camera
|
||||
>>> import matplotlib.pyplot as plt
|
||||
>>> from skimage.viewer.canvastools import PaintTool
|
||||
>>> import numpy as np
|
||||
|
||||
>>> img = camera() #doctest: +SKIP
|
||||
|
||||
>>> ax = plt.subplot(111) #doctest: +SKIP
|
||||
>>> plt.imshow(img, cmap=plt.cm.gray) #doctest: +SKIP
|
||||
>>> p = PaintTool(ax,np.shape(img[:-1]),10,0.2) #doctest: +SKIP
|
||||
>>> plt.show() #doctest: +SKIP
|
||||
|
||||
>>> mask = p.overlay #doctest: +SKIP
|
||||
>>> plt.imshow(mask,cmap=plt.cm.gray) #doctest: +SKIP
|
||||
>>> plt.show() #doctest: +SKIP
|
||||
"""
|
||||
def __init__(self, manager, overlay_shape, radius=5, alpha=0.3,
|
||||
on_move=None, on_release=None, on_enter=None,
|
||||
rect_props=None):
|
||||
super(PaintTool, self).__init__(manager, on_move=on_move,
|
||||
on_enter=on_enter,
|
||||
on_release=on_release)
|
||||
|
||||
props = dict(edgecolor='r', facecolor='0.7', alpha=0.5, animated=True)
|
||||
props.update(rect_props if rect_props is not None else {})
|
||||
|
||||
self.alpha = alpha
|
||||
self.cmap = LABELS_CMAP
|
||||
self._overlay_plot = None
|
||||
self.shape = overlay_shape[:2]
|
||||
|
||||
self._cursor = plt.Rectangle((0, 0), 0, 0, **props)
|
||||
self._cursor.set_visible(False)
|
||||
self.ax.add_patch(self._cursor)
|
||||
|
||||
# `label` and `radius` can only be set after initializing `_cursor`
|
||||
self.label = 1
|
||||
self.radius = radius
|
||||
|
||||
# Note that the order is important: Redraw cursor *after* overlay
|
||||
self.artists = [self._overlay_plot, self._cursor]
|
||||
self.manager.add_tool(self)
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return self._label
|
||||
|
||||
@label.setter
|
||||
def label(self, value):
|
||||
if value >= self.cmap.N:
|
||||
raise ValueError('Maximum label value = %s' % len(self.cmap - 1))
|
||||
self._label = value
|
||||
self._cursor.set_edgecolor(self.cmap(value))
|
||||
|
||||
@property
|
||||
def radius(self):
|
||||
return self._radius
|
||||
|
||||
@radius.setter
|
||||
def radius(self, r):
|
||||
self._radius = r
|
||||
self._width = 2 * r + 1
|
||||
self._cursor.set_width(self._width)
|
||||
self._cursor.set_height(self._width)
|
||||
self.window = CenteredWindow(r, self._shape)
|
||||
|
||||
@property
|
||||
def overlay(self):
|
||||
return self._overlay
|
||||
|
||||
@overlay.setter
|
||||
def overlay(self, image):
|
||||
self._overlay = image
|
||||
if image is None:
|
||||
self.ax.images.remove(self._overlay_plot)
|
||||
self._overlay_plot = None
|
||||
elif self._overlay_plot is None:
|
||||
props = dict(cmap=self.cmap, alpha=self.alpha,
|
||||
norm=mcolors.NoNorm(vmin=0, vmax=self.cmap.N),
|
||||
animated=True)
|
||||
self._overlay_plot = self.ax.imshow(image, **props)
|
||||
else:
|
||||
self._overlay_plot.set_data(image)
|
||||
self.redraw()
|
||||
|
||||
@property
|
||||
def shape(self):
|
||||
return self._shape
|
||||
|
||||
@shape.setter
|
||||
def shape(self, shape):
|
||||
self._shape = shape
|
||||
if not self._overlay_plot is None:
|
||||
self._overlay_plot.set_extent((-0.5, shape[1] + 0.5,
|
||||
shape[0] + 0.5, -0.5))
|
||||
self.radius = self._radius
|
||||
self.overlay = np.zeros(shape, dtype='uint8')
|
||||
|
||||
def on_key_press(self, event):
|
||||
if event.key == 'enter':
|
||||
self.callback_on_enter(self.geometry)
|
||||
self.redraw()
|
||||
|
||||
def on_mouse_press(self, event):
|
||||
if event.button != 1 or not self.ax.in_axes(event):
|
||||
return
|
||||
self.update_cursor(event.xdata, event.ydata)
|
||||
self.update_overlay(event.xdata, event.ydata)
|
||||
|
||||
def on_mouse_release(self, event):
|
||||
if event.button != 1:
|
||||
return
|
||||
self.callback_on_release(self.geometry)
|
||||
|
||||
def on_move(self, event):
|
||||
if not self.ax.in_axes(event):
|
||||
self._cursor.set_visible(False)
|
||||
self.redraw() # make sure cursor is not visible
|
||||
return
|
||||
self._cursor.set_visible(True)
|
||||
|
||||
self.update_cursor(event.xdata, event.ydata)
|
||||
if event.button != 1:
|
||||
self.redraw() # update cursor position
|
||||
return
|
||||
self.update_overlay(event.xdata, event.ydata)
|
||||
self.callback_on_move(self.geometry)
|
||||
|
||||
def update_overlay(self, x, y):
|
||||
overlay = self.overlay
|
||||
overlay[self.window.at(y, x)] = self.label
|
||||
# Note that overlay calls `redraw`
|
||||
self.overlay = overlay
|
||||
|
||||
def update_cursor(self, x, y):
|
||||
x = x - self.radius - 1
|
||||
y = y - self.radius - 1
|
||||
self._cursor.set_xy((x, y))
|
||||
|
||||
@property
|
||||
def geometry(self):
|
||||
return self.overlay
|
||||
|
||||
|
||||
class CenteredWindow(object):
|
||||
"""Window that create slices numpy arrays over 2D windows.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> a = np.arange(16).reshape(4, 4)
|
||||
>>> w = CenteredWindow(1, a.shape)
|
||||
>>> a[w.at(1, 1)]
|
||||
array([[ 0, 1, 2],
|
||||
[ 4, 5, 6],
|
||||
[ 8, 9, 10]])
|
||||
>>> a[w.at(0, 0)]
|
||||
array([[0, 1],
|
||||
[4, 5]])
|
||||
>>> a[w.at(4, 3)]
|
||||
array([[14, 15]])
|
||||
"""
|
||||
def __init__(self, radius, array_shape):
|
||||
self.radius = radius
|
||||
self.array_shape = array_shape
|
||||
|
||||
def at(self, row, col):
|
||||
h, w = self.array_shape
|
||||
r = round(self.radius)
|
||||
# Note: the int() cast is necessary because row and col are np.float64,
|
||||
# which does not get cast by round(), unlike a normal Python float:
|
||||
# >>> round(4.5)
|
||||
# 4
|
||||
# >>> round(np.float64(4.5))
|
||||
# 4.0
|
||||
# >>> int(round(np.float64(4.5)))
|
||||
# 4
|
||||
row, col = int(round(row)), int(round(col))
|
||||
xmin = max(0, col - r)
|
||||
xmax = min(w, col + r + 1)
|
||||
ymin = max(0, row - r)
|
||||
ymax = min(h, row + r + 1)
|
||||
return (slice(ymin, ymax), slice(xmin, xmax))
|
||||
|
||||
|
||||
if __name__ == '__main__': # pragma: no cover
|
||||
np.testing.rundocs()
|
||||
from ... import data
|
||||
from ...viewer import ImageViewer
|
||||
|
||||
image = data.camera()
|
||||
|
||||
viewer = ImageViewer(image)
|
||||
paint_tool = PaintTool(viewer, image.shape)
|
||||
viewer.show()
|
Loading…
Add table
Add a link
Reference in a new issue