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
|
@ -0,0 +1,9 @@
|
|||
from .base import Plugin
|
||||
from .canny import CannyPlugin
|
||||
from .color_histogram import ColorHistogram
|
||||
from .crop import Crop
|
||||
from .labelplugin import LabelPainter
|
||||
from .lineprofile import LineProfile
|
||||
from .measure import Measure
|
||||
from .overlayplugin import OverlayPlugin
|
||||
from .plotplugin import PlotPlugin
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
261
venv/Lib/site-packages/skimage/viewer/plugins/base.py
Normal file
261
venv/Lib/site-packages/skimage/viewer/plugins/base.py
Normal file
|
@ -0,0 +1,261 @@
|
|||
"""
|
||||
Base class for Plugins that interact with ImageViewer.
|
||||
"""
|
||||
from warnings import warn
|
||||
|
||||
import numpy as np
|
||||
from ..qt import QtWidgets, QtCore, Signal
|
||||
from ..utils import RequiredAttr, init_qtapp
|
||||
|
||||
|
||||
class Plugin(QtWidgets.QDialog):
|
||||
"""Base class for plugins that interact with an ImageViewer.
|
||||
|
||||
A plugin connects an image filter (or another function) to an image viewer.
|
||||
Note that a Plugin is initialized *without* an image viewer and attached in
|
||||
a later step. See example below for details.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image_viewer : ImageViewer
|
||||
Window containing image used in measurement/manipulation.
|
||||
image_filter : function
|
||||
Function that gets called to update image in image viewer. This value
|
||||
can be `None` if, for example, you have a plugin that extracts
|
||||
information from an image and doesn't manipulate it. Alternatively,
|
||||
this function can be defined as a method in a Plugin subclass.
|
||||
height, width : int
|
||||
Size of plugin window in pixels. Note that Qt will automatically resize
|
||||
a window to fit components. So if you're adding rows of components, you
|
||||
can leave `height = 0` and just let Qt determine the final height.
|
||||
useblit : bool
|
||||
If True, use blitting to speed up animation. Only available on some
|
||||
Matplotlib backends. If None, set to True when using Agg backend.
|
||||
This only has an effect if you draw on top of an image viewer.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
image_viewer : ImageViewer
|
||||
Window containing image used in measurement.
|
||||
name : str
|
||||
Name of plugin. This is displayed as the window title.
|
||||
artist : list
|
||||
List of Matplotlib artists and canvastools. Any artists created by the
|
||||
plugin should be added to this list so that it gets cleaned up on
|
||||
close.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage.viewer import ImageViewer
|
||||
>>> from skimage.viewer.widgets import Slider
|
||||
>>> from skimage import data
|
||||
>>>
|
||||
>>> plugin = Plugin(image_filter=lambda img,
|
||||
... threshold: img > threshold) # doctest: +SKIP
|
||||
>>> plugin += Slider('threshold', 0, 255) # doctest: +SKIP
|
||||
>>>
|
||||
>>> image = data.coins()
|
||||
>>> viewer = ImageViewer(image) # doctest: +SKIP
|
||||
>>> viewer += plugin # doctest: +SKIP
|
||||
>>> thresholded = viewer.show()[0][0] # doctest: +SKIP
|
||||
|
||||
The plugin will automatically delegate parameters to `image_filter` based
|
||||
on its parameter type, i.e., `ptype` (widgets for required arguments must
|
||||
be added in the order they appear in the function). The image attached
|
||||
to the viewer is **automatically passed as the first argument** to the
|
||||
filter function.
|
||||
|
||||
#TODO: Add flag so image is not passed to filter function by default.
|
||||
|
||||
`ptype = 'kwarg'` is the default for most widgets so it's unnecessary here.
|
||||
|
||||
"""
|
||||
name = 'Plugin'
|
||||
image_viewer = RequiredAttr("%s is not attached to ImageViewer" % name)
|
||||
|
||||
# Signals used when viewers are linked to the Plugin output.
|
||||
image_changed = Signal(np.ndarray)
|
||||
_started = Signal(int)
|
||||
|
||||
def __init__(self, image_filter=None, height=0, width=400, useblit=True,
|
||||
dock='bottom'):
|
||||
init_qtapp()
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
self.dock = dock
|
||||
|
||||
self.image_viewer = None
|
||||
# If subclass defines `image_filter` method ignore input.
|
||||
if not hasattr(self, 'image_filter'):
|
||||
self.image_filter = image_filter
|
||||
elif image_filter is not None:
|
||||
warn("If the Plugin class defines an `image_filter` method, "
|
||||
"then the `image_filter` argument is ignored.")
|
||||
|
||||
self.setWindowTitle(self.name)
|
||||
self.layout = QtWidgets.QGridLayout(self)
|
||||
self.resize(width, height)
|
||||
self.row = 0
|
||||
|
||||
self.arguments = []
|
||||
self.keyword_arguments = {}
|
||||
|
||||
self.useblit = useblit
|
||||
self.cids = []
|
||||
self.artists = []
|
||||
|
||||
def attach(self, image_viewer):
|
||||
"""Attach the plugin to an ImageViewer.
|
||||
|
||||
Note that the ImageViewer will automatically call this method when the
|
||||
plugin is added to the ImageViewer. For example::
|
||||
|
||||
viewer += Plugin(...)
|
||||
|
||||
Also note that `attach` automatically calls the filter function so that
|
||||
the image matches the filtered value specified by attached widgets.
|
||||
"""
|
||||
self.setParent(image_viewer)
|
||||
self.setWindowFlags(QtCore.Qt.Dialog)
|
||||
|
||||
self.image_viewer = image_viewer
|
||||
self.image_viewer.plugins.append(self)
|
||||
#TODO: Always passing image as first argument may be bad assumption.
|
||||
self.arguments = [self.image_viewer.original_image]
|
||||
|
||||
# Call filter so that filtered image matches widget values
|
||||
self.filter_image()
|
||||
|
||||
def add_widget(self, widget):
|
||||
"""Add widget to plugin.
|
||||
|
||||
Alternatively, Plugin's `__add__` method is overloaded to add widgets::
|
||||
|
||||
plugin += Widget(...)
|
||||
|
||||
Widgets can adjust required or optional arguments of filter function or
|
||||
parameters for the plugin. This is specified by the Widget's `ptype`.
|
||||
"""
|
||||
if widget.ptype == 'kwarg':
|
||||
name = widget.name.replace(' ', '_')
|
||||
self.keyword_arguments[name] = widget
|
||||
widget.callback = self.filter_image
|
||||
elif widget.ptype == 'arg':
|
||||
self.arguments.append(widget)
|
||||
widget.callback = self.filter_image
|
||||
elif widget.ptype == 'plugin':
|
||||
widget.callback = self.update_plugin
|
||||
widget.plugin = self
|
||||
self.layout.addWidget(widget, self.row, 0)
|
||||
self.row += 1
|
||||
|
||||
def __add__(self, widget):
|
||||
self.add_widget(widget)
|
||||
return self
|
||||
|
||||
def filter_image(self, *widget_arg):
|
||||
"""Call `image_filter` with widget args and kwargs
|
||||
|
||||
Note: `display_filtered_image` is automatically called.
|
||||
"""
|
||||
# `widget_arg` is passed by the active widget but is unused since all
|
||||
# filter arguments are pulled directly from attached the widgets.
|
||||
|
||||
if self.image_filter is None:
|
||||
return
|
||||
arguments = [self._get_value(a) for a in self.arguments]
|
||||
kwargs = {name: self._get_value(a)
|
||||
for name, a in self.keyword_arguments.items()}
|
||||
filtered = self.image_filter(*arguments, **kwargs)
|
||||
|
||||
self.display_filtered_image(filtered)
|
||||
self.image_changed.emit(filtered)
|
||||
|
||||
def _get_value(self, param):
|
||||
# If param is a widget, return its `val` attribute.
|
||||
return param if not hasattr(param, 'val') else param.val
|
||||
|
||||
def _update_original_image(self, image):
|
||||
"""Update the original image argument passed to the filter function.
|
||||
|
||||
This method is called by the viewer when the original image is updated.
|
||||
"""
|
||||
self.arguments[0] = image
|
||||
self._on_new_image(image)
|
||||
self.filter_image()
|
||||
|
||||
def _on_new_image(self, image):
|
||||
"""Override this method to update your plugin for new images."""
|
||||
pass
|
||||
|
||||
@property
|
||||
def filtered_image(self):
|
||||
"""Return filtered image."""
|
||||
return self.image_viewer.image
|
||||
|
||||
def display_filtered_image(self, image):
|
||||
"""Display the filtered image on image viewer.
|
||||
|
||||
If you don't want to simply replace the displayed image with the
|
||||
filtered image (e.g., you want to display a transparent overlay),
|
||||
you can override this method.
|
||||
"""
|
||||
self.image_viewer.image = image
|
||||
|
||||
def update_plugin(self, name, value):
|
||||
"""Update keyword parameters of the plugin itself.
|
||||
|
||||
These parameters will typically be implemented as class properties so
|
||||
that they update the image or some other component.
|
||||
"""
|
||||
setattr(self, name, value)
|
||||
|
||||
def show(self, main_window=True):
|
||||
"""Show plugin."""
|
||||
super(Plugin, self).show()
|
||||
self.activateWindow()
|
||||
self.raise_()
|
||||
|
||||
# Emit signal with x-hint so new windows can be displayed w/o overlap.
|
||||
size = self.frameGeometry()
|
||||
x_hint = size.x() + size.width()
|
||||
self._started.emit(x_hint)
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""On close disconnect all artists and events from ImageViewer.
|
||||
|
||||
Note that artists must be appended to `self.artists`.
|
||||
"""
|
||||
self.clean_up()
|
||||
self.close()
|
||||
|
||||
def clean_up(self):
|
||||
self.remove_image_artists()
|
||||
if self in self.image_viewer.plugins:
|
||||
self.image_viewer.plugins.remove(self)
|
||||
self.image_viewer.reset_image()
|
||||
self.image_viewer.redraw()
|
||||
|
||||
def remove_image_artists(self):
|
||||
"""Remove artists that are connected to the image viewer."""
|
||||
for a in self.artists:
|
||||
a.remove()
|
||||
|
||||
def output(self):
|
||||
"""Return the plugin's representation and data.
|
||||
|
||||
Returns
|
||||
-------
|
||||
image : array, same shape as ``self.image_viewer.image``, or None
|
||||
The filtered image.
|
||||
data : None
|
||||
Any data associated with the plugin.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Derived classes should override this method to return a tuple
|
||||
containing an *overlay* of the same shape of the image, and a
|
||||
*data* object. Either of these is optional: return ``None`` if
|
||||
you don't want to return a value.
|
||||
"""
|
||||
return (self.image_viewer.image, None)
|
30
venv/Lib/site-packages/skimage/viewer/plugins/canny.py
Normal file
30
venv/Lib/site-packages/skimage/viewer/plugins/canny.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
import numpy as np
|
||||
|
||||
import skimage
|
||||
from ...feature import canny
|
||||
from .overlayplugin import OverlayPlugin
|
||||
from ..widgets import Slider, ComboBox
|
||||
|
||||
|
||||
class CannyPlugin(OverlayPlugin):
|
||||
"""Canny filter plugin to show edges of an image."""
|
||||
|
||||
name = 'Canny Filter'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CannyPlugin, self).__init__(image_filter=canny, **kwargs)
|
||||
|
||||
def attach(self, image_viewer):
|
||||
image = image_viewer.image
|
||||
imin, imax = skimage.dtype_limits(image, clip_negative=False)
|
||||
itype = 'float' if np.issubdtype(image.dtype, np.floating) else 'int'
|
||||
self.add_widget(Slider('sigma', 0, 5, update_on='release'))
|
||||
self.add_widget(Slider('low threshold', imin, imax, value_type=itype,
|
||||
update_on='release'))
|
||||
self.add_widget(Slider('high threshold', imin, imax, value_type=itype,
|
||||
update_on='release'))
|
||||
self.add_widget(ComboBox('color', self.color_names, ptype='plugin'))
|
||||
# Call parent method at end b/c it calls `filter_image`, which needs
|
||||
# the values specified by the widgets. Alternatively, move call to
|
||||
# parent method to beginning and add a call to `self.filter_image()`
|
||||
super(CannyPlugin,self).attach(image_viewer)
|
|
@ -0,0 +1,93 @@
|
|||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from ... import color, exposure
|
||||
from .plotplugin import PlotPlugin
|
||||
from ..canvastools import RectangleTool
|
||||
|
||||
|
||||
class ColorHistogram(PlotPlugin):
|
||||
name = 'Color Histogram'
|
||||
|
||||
def __init__(self, max_pct=0.99, **kwargs):
|
||||
super(ColorHistogram, self).__init__(height=400, **kwargs)
|
||||
self.max_pct = max_pct
|
||||
|
||||
print(self.help())
|
||||
|
||||
def attach(self, image_viewer):
|
||||
super(ColorHistogram, self).attach(image_viewer)
|
||||
|
||||
self.rect_tool = RectangleTool(self,
|
||||
on_release=self.ab_selected)
|
||||
self._on_new_image(image_viewer.image)
|
||||
|
||||
def _on_new_image(self, image):
|
||||
self.lab_image = color.rgb2lab(image)
|
||||
|
||||
# Calculate color histogram in the Lab colorspace:
|
||||
L, a, b = self.lab_image.T
|
||||
left, right = -100, 100
|
||||
ab_extents = [left, right, right, left]
|
||||
self.mask = np.ones(L.shape, bool)
|
||||
bins = np.arange(left, right)
|
||||
hist, x_edges, y_edges = np.histogram2d(a.flatten(), b.flatten(),
|
||||
bins, normed=True)
|
||||
self.data = {'bins': bins, 'hist': hist, 'edges': (x_edges, y_edges),
|
||||
'extents': (left, right, left, right)}
|
||||
# Clip bin heights that dominate a-b histogram
|
||||
max_val = pct_total_area(hist, percentile=self.max_pct)
|
||||
hist = exposure.rescale_intensity(hist, in_range=(0, max_val))
|
||||
self.ax.imshow(hist, extent=ab_extents, cmap=plt.cm.gray)
|
||||
|
||||
self.ax.set_title('Color Histogram')
|
||||
self.ax.set_xlabel('b')
|
||||
self.ax.set_ylabel('a')
|
||||
|
||||
def help(self):
|
||||
helpstr = ("Color Histogram tool:",
|
||||
"Select region of a-b colorspace to highlight on image.")
|
||||
return '\n'.join(helpstr)
|
||||
|
||||
def ab_selected(self, extents):
|
||||
x0, x1, y0, y1 = extents
|
||||
self.data['extents'] = extents
|
||||
|
||||
lab_masked = self.lab_image.copy()
|
||||
L, a, b = lab_masked.T
|
||||
|
||||
self.mask = ((a > y0) & (a < y1)) & ((b > x0) & (b < x1))
|
||||
lab_masked[..., 1:][~self.mask.T] = 0
|
||||
|
||||
self.image_viewer.image = color.lab2rgb(lab_masked)
|
||||
|
||||
def output(self):
|
||||
"""Return the image mask and the histogram data.
|
||||
|
||||
Returns
|
||||
-------
|
||||
mask : array of bool, same shape as image
|
||||
The selected pixels.
|
||||
data : dict
|
||||
The data describing the histogram and the selected region.
|
||||
The dictionary contains:
|
||||
|
||||
- 'bins' : array of float
|
||||
The bin boundaries for both `a` and `b` channels.
|
||||
- 'hist' : 2D array of float
|
||||
The normalized histogram.
|
||||
- 'edges' : tuple of array of float
|
||||
The bin edges along each dimension
|
||||
- 'extents' : tuple of float
|
||||
The left and right and top and bottom of the selected region.
|
||||
"""
|
||||
return (self.mask, self.data)
|
||||
|
||||
|
||||
def pct_total_area(image, percentile=0.80):
|
||||
"""Return threshold value based on percentage of total area.
|
||||
|
||||
The specified percent of pixels less than the given intensity threshold.
|
||||
"""
|
||||
idx = int((image.size - 1) * percentile)
|
||||
sorted_pixels = np.sort(image.flat)
|
||||
return sorted_pixels[idx]
|
45
venv/Lib/site-packages/skimage/viewer/plugins/crop.py
Normal file
45
venv/Lib/site-packages/skimage/viewer/plugins/crop.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
from .base import Plugin
|
||||
from ..canvastools import RectangleTool
|
||||
from ...viewer.widgets import SaveButtons, Button
|
||||
|
||||
|
||||
__all__ = ['Crop']
|
||||
|
||||
|
||||
class Crop(Plugin):
|
||||
name = 'Crop'
|
||||
|
||||
def __init__(self, maxdist=10, **kwargs):
|
||||
super(Crop, self).__init__(**kwargs)
|
||||
self.maxdist = maxdist
|
||||
self.add_widget(SaveButtons())
|
||||
print(self.help())
|
||||
|
||||
def attach(self, image_viewer):
|
||||
super(Crop, self).attach(image_viewer)
|
||||
|
||||
self.rect_tool = RectangleTool(image_viewer,
|
||||
maxdist=self.maxdist,
|
||||
on_enter=self.crop)
|
||||
self.artists.append(self.rect_tool)
|
||||
|
||||
self.reset_button = Button('Reset', self.reset)
|
||||
self.add_widget(self.reset_button)
|
||||
|
||||
def help(self):
|
||||
helpstr = ("Crop tool",
|
||||
"Select rectangular region and press enter to crop.")
|
||||
return '\n'.join(helpstr)
|
||||
|
||||
def crop(self, extents):
|
||||
xmin, xmax, ymin, ymax = extents
|
||||
if xmin == xmax or ymin == ymax:
|
||||
return
|
||||
image = self.image_viewer.image[ymin:ymax+1, xmin:xmax+1]
|
||||
self.image_viewer.image = image
|
||||
self.image_viewer.ax.relim()
|
||||
|
||||
def reset(self):
|
||||
self.rect_tool.extents = -10, -10, -10, -10
|
||||
self.image_viewer.image = self.image_viewer.original_image
|
||||
self.image_viewer.ax.relim()
|
67
venv/Lib/site-packages/skimage/viewer/plugins/labelplugin.py
Normal file
67
venv/Lib/site-packages/skimage/viewer/plugins/labelplugin.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
import numpy as np
|
||||
|
||||
from .base import Plugin
|
||||
from ..widgets import ComboBox, Slider
|
||||
from ..canvastools import PaintTool
|
||||
|
||||
|
||||
__all__ = ['LabelPainter']
|
||||
|
||||
|
||||
rad2deg = 180 / np.pi
|
||||
|
||||
|
||||
class LabelPainter(Plugin):
|
||||
name = 'LabelPainter'
|
||||
|
||||
def __init__(self, max_radius=20, **kwargs):
|
||||
super(LabelPainter, self).__init__(**kwargs)
|
||||
|
||||
# These widgets adjust plugin properties instead of an image filter.
|
||||
self._radius_widget = Slider('radius', low=1, high=max_radius,
|
||||
value=5, value_type='int', ptype='plugin')
|
||||
labels = [str(i) for i in range(6)]
|
||||
labels[0] = 'Erase'
|
||||
self._label_widget = ComboBox('label', labels, ptype='plugin')
|
||||
self.add_widget(self._radius_widget)
|
||||
self.add_widget(self._label_widget)
|
||||
|
||||
print(self.help())
|
||||
|
||||
def help(self):
|
||||
helpstr = ("Label painter",
|
||||
"Hold left-mouse button and paint on canvas.")
|
||||
return '\n'.join(helpstr)
|
||||
|
||||
def attach(self, image_viewer):
|
||||
super(LabelPainter, self).attach(image_viewer)
|
||||
|
||||
image = image_viewer.original_image
|
||||
self.paint_tool = PaintTool(image_viewer, image.shape,
|
||||
on_enter=self.on_enter)
|
||||
self.paint_tool.radius = self.radius
|
||||
self.paint_tool.label = self._label_widget.index = 1
|
||||
self.artists.append(self.paint_tool)
|
||||
|
||||
def _on_new_image(self, image):
|
||||
"""Update plugin for new images."""
|
||||
self.paint_tool.shape = image.shape
|
||||
|
||||
def on_enter(self, overlay):
|
||||
pass
|
||||
|
||||
@property
|
||||
def radius(self):
|
||||
return self._radius_widget.val
|
||||
|
||||
@radius.setter
|
||||
def radius(self, val):
|
||||
self.paint_tool.radius = val
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return self._label_widget.val
|
||||
|
||||
@label.setter
|
||||
def label(self, val):
|
||||
self.paint_tool.label = val
|
165
venv/Lib/site-packages/skimage/viewer/plugins/lineprofile.py
Normal file
165
venv/Lib/site-packages/skimage/viewer/plugins/lineprofile.py
Normal file
|
@ -0,0 +1,165 @@
|
|||
|
||||
import numpy as np
|
||||
from ...util.dtype import dtype_range
|
||||
from ... import draw, measure
|
||||
|
||||
from .plotplugin import PlotPlugin
|
||||
from ..canvastools import ThickLineTool
|
||||
|
||||
|
||||
__all__ = ['LineProfile']
|
||||
|
||||
|
||||
class LineProfile(PlotPlugin):
|
||||
"""Plugin to compute interpolated intensity under a scan line on an image.
|
||||
|
||||
See PlotPlugin and Plugin classes for additional details.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
maxdist : float
|
||||
Maximum pixel distance allowed when selecting end point of scan line.
|
||||
limits : tuple or {None, 'image', 'dtype'}
|
||||
(minimum, maximum) intensity limits for plotted profile. The following
|
||||
special values are defined:
|
||||
|
||||
None : rescale based on min/max intensity along selected scan line.
|
||||
'image' : fixed scale based on min/max intensity in image.
|
||||
'dtype' : fixed scale based on min/max intensity of image dtype.
|
||||
"""
|
||||
name = 'Line Profile'
|
||||
|
||||
def __init__(self, maxdist=10, epsilon='deprecated',
|
||||
limits='image', **kwargs):
|
||||
super(LineProfile, self).__init__(**kwargs)
|
||||
self.maxdist = maxdist
|
||||
self._limit_type = limits
|
||||
print(self.help())
|
||||
|
||||
def attach(self, image_viewer):
|
||||
super(LineProfile, self).attach(image_viewer)
|
||||
|
||||
image = image_viewer.original_image
|
||||
|
||||
if self._limit_type == 'image':
|
||||
self.limits = (np.min(image), np.max(image))
|
||||
elif self._limit_type == 'dtype':
|
||||
self.limits = dtype_range[image.dtype.type]
|
||||
elif self._limit_type is None or len(self._limit_type) == 2:
|
||||
self.limits = self._limit_type
|
||||
else:
|
||||
raise ValueError("Unrecognized `limits`: %s" % self._limit_type)
|
||||
|
||||
if not self._limit_type is None:
|
||||
self.ax.set_ylim(self.limits)
|
||||
|
||||
h, w = image.shape[0:2]
|
||||
x = [w / 3, 2 * w / 3]
|
||||
y = [h / 2] * 2
|
||||
|
||||
self.line_tool = ThickLineTool(self.image_viewer,
|
||||
maxdist=self.maxdist,
|
||||
on_move=self.line_changed,
|
||||
on_change=self.line_changed)
|
||||
self.line_tool.end_points = np.transpose([x, y])
|
||||
|
||||
scan_data = measure.profile_line(image,
|
||||
*self.line_tool.end_points[:, ::-1])
|
||||
self.scan_data = scan_data
|
||||
if scan_data.ndim == 1:
|
||||
scan_data = scan_data[:, np.newaxis]
|
||||
|
||||
self.reset_axes(scan_data)
|
||||
|
||||
self._autoscale_view()
|
||||
|
||||
def help(self):
|
||||
helpstr = ("Line profile tool",
|
||||
"+ and - keys or mouse scroll changes width of scan line.",
|
||||
"Select and drag ends of the scan line to adjust it.")
|
||||
return '\n'.join(helpstr)
|
||||
|
||||
def get_profiles(self):
|
||||
"""Return intensity profile of the selected line.
|
||||
|
||||
Returns
|
||||
-------
|
||||
end_points: (2, 2) array
|
||||
The positions ((x1, y1), (x2, y2)) of the line ends.
|
||||
profile: list of 1d arrays
|
||||
Profile of intensity values. Length 1 (grayscale) or 3 (rgb).
|
||||
"""
|
||||
self._update_data()
|
||||
profiles = [data.get_ydata() for data in self.profile]
|
||||
return self.line_tool.end_points, profiles
|
||||
|
||||
def _autoscale_view(self):
|
||||
if self.limits is None:
|
||||
self.ax.autoscale_view(tight=True)
|
||||
else:
|
||||
self.ax.autoscale_view(scaley=False, tight=True)
|
||||
|
||||
def line_changed(self, end_points):
|
||||
x, y = np.transpose(end_points)
|
||||
self.line_tool.end_points = end_points
|
||||
self._update_data()
|
||||
self.ax.relim()
|
||||
|
||||
self._autoscale_view()
|
||||
self.redraw()
|
||||
|
||||
def _update_data(self):
|
||||
scan = measure.profile_line(self.image_viewer.image,
|
||||
*self.line_tool.end_points[:, ::-1],
|
||||
linewidth=self.line_tool.linewidth)
|
||||
self.scan_data = scan
|
||||
if scan.ndim == 1:
|
||||
scan = scan[:, np.newaxis]
|
||||
|
||||
if scan.shape[1] != len(self.profile):
|
||||
self.reset_axes(scan)
|
||||
|
||||
for i in range(len(scan[0])):
|
||||
self.profile[i].set_xdata(np.arange(scan.shape[0]))
|
||||
self.profile[i].set_ydata(scan[:, i])
|
||||
|
||||
def reset_axes(self, scan_data):
|
||||
# Clear lines out
|
||||
for line in self.ax.lines:
|
||||
self.ax.lines = []
|
||||
|
||||
if scan_data.shape[1] == 1:
|
||||
self.profile = self.ax.plot(scan_data, 'k-')
|
||||
else:
|
||||
self.profile = self.ax.plot(scan_data[:, 0], 'r-',
|
||||
scan_data[:, 1], 'g-',
|
||||
scan_data[:, 2], 'b-')
|
||||
|
||||
def output(self):
|
||||
"""Return the drawn line and the resulting scan.
|
||||
|
||||
Returns
|
||||
-------
|
||||
line_image : (M, N) uint8 array, same shape as image
|
||||
An array of 0s with the scanned line set to 255.
|
||||
If the linewidth of the line tool is greater than 1,
|
||||
sets the values within the profiled polygon to 128.
|
||||
scan : (P,) or (P, 3) array of int or float
|
||||
The line scan values across the image.
|
||||
"""
|
||||
end_points = self.line_tool.end_points
|
||||
line_image = np.zeros(self.image_viewer.image.shape[:2],
|
||||
np.uint8)
|
||||
width = self.line_tool.linewidth
|
||||
if width > 1:
|
||||
rp, cp = measure.profile._line_profile_coordinates(
|
||||
*end_points[:, ::-1], linewidth=width)
|
||||
# the points are aliased, so create a polygon using the corners
|
||||
yp = np.rint(rp[[0, 0, -1, -1],[0, -1, -1, 0]]).astype(int)
|
||||
xp = np.rint(cp[[0, 0, -1, -1],[0, -1, -1, 0]]).astype(int)
|
||||
rp, cp = draw.polygon(yp, xp, line_image.shape)
|
||||
line_image[rp, cp] = 128
|
||||
(x1, y1), (x2, y2) = end_points.astype(int)
|
||||
rr, cc = draw.line(y1, x1, y2, x2)
|
||||
line_image[rr, cc] = 255
|
||||
return line_image, self.scan_data
|
49
venv/Lib/site-packages/skimage/viewer/plugins/measure.py
Normal file
49
venv/Lib/site-packages/skimage/viewer/plugins/measure.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
import numpy as np
|
||||
|
||||
from .base import Plugin
|
||||
from ..widgets import Text
|
||||
from ..canvastools import LineTool
|
||||
|
||||
|
||||
__all__ = ['Measure']
|
||||
|
||||
|
||||
rad2deg = 180 / np.pi
|
||||
|
||||
|
||||
class Measure(Plugin):
|
||||
name = 'Measure'
|
||||
|
||||
def __init__(self, maxdist=10, **kwargs):
|
||||
super(Measure, self).__init__(**kwargs)
|
||||
|
||||
self.maxdist = maxdist
|
||||
self._length = Text('Length:')
|
||||
self._angle = Text('Angle:')
|
||||
|
||||
self.add_widget(self._length)
|
||||
self.add_widget(self._angle)
|
||||
|
||||
print(self.help())
|
||||
|
||||
def attach(self, image_viewer):
|
||||
super(Measure, self).attach(image_viewer)
|
||||
|
||||
image = image_viewer.original_image
|
||||
h, w = image.shape
|
||||
self.line_tool = LineTool(self.image_viewer,
|
||||
maxdist=self.maxdist,
|
||||
on_move=self.line_changed)
|
||||
self.artists.append(self.line_tool)
|
||||
|
||||
def help(self):
|
||||
helpstr = ("Measure tool",
|
||||
"Select line to measure distance and angle.")
|
||||
return '\n'.join(helpstr)
|
||||
|
||||
def line_changed(self, end_points):
|
||||
x, y = np.transpose(end_points)
|
||||
dx = np.diff(x)[0]
|
||||
dy = np.diff(y)[0]
|
||||
self._length.text = '%.1f' % np.hypot(dx, dy)
|
||||
self._angle.text = '%.1f°' % (180 - np.arctan2(dy, dx) * rad2deg)
|
115
venv/Lib/site-packages/skimage/viewer/plugins/overlayplugin.py
Normal file
115
venv/Lib/site-packages/skimage/viewer/plugins/overlayplugin.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
|
||||
from ...util.dtype import dtype_range
|
||||
from .base import Plugin
|
||||
from ..utils import ClearColormap, update_axes_image
|
||||
|
||||
from ..._shared.version_requirements import is_installed
|
||||
|
||||
|
||||
__all__ = ['OverlayPlugin']
|
||||
|
||||
|
||||
class OverlayPlugin(Plugin):
|
||||
"""Plugin for ImageViewer that displays an overlay on top of main image.
|
||||
|
||||
The base Plugin class displays the filtered image directly on the viewer.
|
||||
OverlayPlugin will instead overlay an image with a transparent colormap.
|
||||
|
||||
See base Plugin class for additional details.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
overlay : array
|
||||
Overlay displayed on top of image. This overlay defaults to a color map
|
||||
with alpha values varying linearly from 0 to 1.
|
||||
color : int
|
||||
Color of overlay.
|
||||
"""
|
||||
colors = {'red': (1, 0, 0),
|
||||
'yellow': (1, 1, 0),
|
||||
'green': (0, 1, 0),
|
||||
'cyan': (0, 1, 1)}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(OverlayPlugin, self).__init__(**kwargs)
|
||||
self._overlay_plot = None
|
||||
self._overlay = None
|
||||
self.cmap = None
|
||||
self.color_names = sorted(list(self.colors.keys()))
|
||||
|
||||
def attach(self, image_viewer):
|
||||
super(OverlayPlugin, self).attach(image_viewer)
|
||||
#TODO: `color` doesn't update GUI widget when set manually.
|
||||
self.color = 0
|
||||
|
||||
@property
|
||||
def overlay(self):
|
||||
return self._overlay
|
||||
|
||||
@overlay.setter
|
||||
def overlay(self, image):
|
||||
self._overlay = image
|
||||
ax = self.image_viewer.ax
|
||||
if image is None:
|
||||
ax.images.remove(self._overlay_plot)
|
||||
self._overlay_plot = None
|
||||
elif self._overlay_plot is None:
|
||||
vmin, vmax = dtype_range[image.dtype.type]
|
||||
self._overlay_plot = ax.imshow(image, cmap=self.cmap,
|
||||
vmin=vmin, vmax=vmax)
|
||||
else:
|
||||
update_axes_image(self._overlay_plot, image)
|
||||
|
||||
if self.image_viewer.useblit:
|
||||
self.image_viewer._blit_manager.background = None
|
||||
|
||||
self.image_viewer.redraw()
|
||||
|
||||
@property
|
||||
def color(self):
|
||||
return self._color
|
||||
|
||||
@color.setter
|
||||
def color(self, index):
|
||||
# Update colormap whenever color is changed.
|
||||
if isinstance(index, str) and \
|
||||
index not in self.color_names:
|
||||
raise ValueError("%s not defined in OverlayPlugin.colors" % index)
|
||||
else:
|
||||
name = self.color_names[index]
|
||||
self._color = name
|
||||
rgb = self.colors[name]
|
||||
self.cmap = ClearColormap(rgb)
|
||||
|
||||
if self._overlay_plot is not None:
|
||||
self._overlay_plot.set_cmap(self.cmap)
|
||||
self.image_viewer.redraw()
|
||||
|
||||
@property
|
||||
def filtered_image(self):
|
||||
"""Return filtered image.
|
||||
|
||||
This "filtered image" is used when saving from the plugin.
|
||||
"""
|
||||
return self.overlay
|
||||
|
||||
def display_filtered_image(self, image):
|
||||
"""Display filtered image as an overlay on top of image in viewer."""
|
||||
self.overlay = image
|
||||
|
||||
def closeEvent(self, event):
|
||||
# clear overlay from ImageViewer on close
|
||||
self.overlay = None
|
||||
super(OverlayPlugin, self).closeEvent(event)
|
||||
|
||||
def output(self):
|
||||
"""Return the overlaid image.
|
||||
|
||||
Returns
|
||||
-------
|
||||
overlay : array, same shape as image
|
||||
The overlay currently displayed.
|
||||
data : None
|
||||
"""
|
||||
return (self.overlay, None)
|
||||
|
74
venv/Lib/site-packages/skimage/viewer/plugins/plotplugin.py
Normal file
74
venv/Lib/site-packages/skimage/viewer/plugins/plotplugin.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
import numpy as np
|
||||
|
||||
from ..qt import QtGui
|
||||
from ..utils import new_plot
|
||||
from ..utils.canvas import BlitManager, EventManager
|
||||
from .base import Plugin
|
||||
|
||||
|
||||
__all__ = ['PlotPlugin']
|
||||
|
||||
|
||||
class PlotPlugin(Plugin):
|
||||
"""Plugin for ImageViewer that contains a plot canvas.
|
||||
|
||||
Base class for plugins that contain a Matplotlib plot canvas, which can,
|
||||
for example, display an image histogram.
|
||||
|
||||
See base Plugin class for additional details.
|
||||
"""
|
||||
|
||||
def __init__(self, image_filter=None, height=150, width=400, **kwargs):
|
||||
super(PlotPlugin, self).__init__(image_filter=image_filter,
|
||||
height=height, width=width, **kwargs)
|
||||
|
||||
self._height = height
|
||||
self._width = width
|
||||
self._blit_manager = None
|
||||
self._tools = []
|
||||
self._event_manager = None
|
||||
|
||||
def attach(self, image_viewer):
|
||||
super(PlotPlugin, self).attach(image_viewer)
|
||||
# Add plot for displaying intensity profile.
|
||||
self.add_plot()
|
||||
if image_viewer.useblit:
|
||||
self._blit_manager = BlitManager(self.ax)
|
||||
self._event_manager = EventManager(self.ax)
|
||||
|
||||
def redraw(self):
|
||||
"""Redraw plot."""
|
||||
self.canvas.draw_idle()
|
||||
|
||||
def add_plot(self):
|
||||
self.fig, self.ax = new_plot()
|
||||
self.fig.set_figwidth(self._width / float(self.fig.dpi))
|
||||
self.fig.set_figheight(self._height / float(self.fig.dpi))
|
||||
|
||||
self.canvas = self.fig.canvas
|
||||
#TODO: Converted color is slightly different than Qt background.
|
||||
qpalette = QtGui.QPalette()
|
||||
qcolor = qpalette.color(QtGui.QPalette.Window)
|
||||
bgcolor = qcolor.toRgb().value()
|
||||
if np.isscalar(bgcolor):
|
||||
bgcolor = str(bgcolor / 255.)
|
||||
self.fig.patch.set_facecolor(bgcolor)
|
||||
self.layout.addWidget(self.canvas, self.row, 0)
|
||||
|
||||
def _update_original_image(self, image):
|
||||
super(PlotPlugin, self)._update_original_image(image)
|
||||
self.redraw()
|
||||
|
||||
def add_tool(self, tool):
|
||||
if self._blit_manager:
|
||||
self._blit_manager.add_artists(tool.artists)
|
||||
self._tools.append(tool)
|
||||
self._event_manager.attach(tool)
|
||||
|
||||
def remove_tool(self, tool):
|
||||
if tool not in self._tools:
|
||||
return
|
||||
if self._blit_manager:
|
||||
self._blit_manager.remove_artists(tool.artists)
|
||||
self._tools.remove(tool)
|
||||
self._event_manager.detach(tool)
|
Loading…
Add table
Add a link
Reference in a new issue