STY: Reuse ThickLineTool in LineProfile

This commit is contained in:
Tony S Yu
2012-11-15 00:16:19 -05:00
parent 2ff3e78c52
commit 5be5813070
+26 -102
View File
@@ -1,14 +1,15 @@
import warnings
import numpy as np
import scipy.ndimage as ndi
from skimage.util.dtype import dtype_range
from .plotplugin import PlotPlugin
from ..canvastools import ThickLineTool
__all__ = ['LineProfile']
#TODO: Extract line tool and add it to a new `canvastools` subpackage.
class LineProfile(PlotPlugin):
"""Plugin to compute interpolated intensity under a scan line on an image.
@@ -19,8 +20,10 @@ class LineProfile(PlotPlugin):
----------
linewidth : float
Line width for interpolation. Wider lines average over more pixels.
epsilon : float
maxdist : float
Maximum pixel distance allowed when selecting end point of scan line.
epsilon : float
Deprecated. Use `maxdist` instead.
limits : tuple or {None, 'image', 'dtype'}
(minimum, maximum) intensity limits for plotted profile. The following
special values are defined:
@@ -32,14 +35,15 @@ class LineProfile(PlotPlugin):
name = 'Line Profile'
draws_on_image = True
def __init__(self, linewidth=1, epsilon=5, limits='image', **kwargs):
def __init__(self, linewidth=1, maxdist=5, epsilon='deprecated',
limits='image', **kwargs):
super(LineProfile, self).__init__(**kwargs)
self.linewidth = linewidth
self.epsilon = epsilon
self._active_pt = None
if not epsilon == 'deprecated':
warnings.warn("Parameter `epsilon` deprecated; use `maxdist`.")
maxdist = epsilon
self.maxdist = maxdist
self._limit_type = limits
self.line_kwargs = dict(color='y', lw=linewidth, alpha=0.5, marker='s',
markersize=5, solid_capstyle='butt')
print self.help()
def attach(self, image_viewer):
@@ -50,7 +54,7 @@ class LineProfile(PlotPlugin):
if self._limit_type == 'image':
self.limits = (np.min(image), np.max(image))
elif self._limit_type == 'dtype':
self.self._limit_type = dtype_range[image.dtype.type]
self._limit_type = dtype_range[image.dtype.type]
elif self._limit_type is None or len(self._limit_type) == 2:
self.limits = self._limit_type
else:
@@ -60,25 +64,18 @@ class LineProfile(PlotPlugin):
self.ax.set_ylim(self.limits)
h, w = image.shape
self._init_end_pts = np.array([[w / 3, h / 2], [2 * w / 3, h / 2]])
self.end_pts = self._init_end_pts.copy()
x = [w / 3, 2 * w / 3]
y = [h / 2] * 2
self.end_pts = np.transpose([x, y])
x, y = np.transpose(self.end_pts)
self.scan_line = image_viewer.ax.plot(x, y, **self.line_kwargs)[0]
self.artists.append(self.scan_line)
self.line_tool = ThickLineTool(self.image_viewer.ax, x, y,
maxdist=self.maxdist,
on_update=self.line_changed)
scan_data = profile_line(image, self.end_pts)
self.profile = self.ax.plot(scan_data, 'k-')[0]
self._autoscale_view()
self.connect_image_event('key_press_event', self.on_key_press)
self.connect_image_event('button_press_event', self.on_mouse_press)
self.connect_image_event('button_release_event', self.on_mouse_release)
self.connect_image_event('motion_notify_event', self.on_move)
self.connect_image_event('scroll_event', self.on_scroll)
self.image_viewer.redraw()
def help(self):
helpstr = ("Line profile tool",
"+ and - keys or mouse scroll changes width of scan line.",
@@ -95,36 +92,8 @@ class LineProfile(PlotPlugin):
profile: 1d array
Profile of intensity values.
"""
end_pts = self.scan_line.get_xydata()
profile = self.profile.get_ydata()
return end_pts, profile
def on_scroll(self, event):
if not event.inaxes:
return
if event.button == 'up':
self._thicken_scan_line()
elif event.button == 'down':
self._shrink_scan_line()
def on_key_press(self, event):
if not event.inaxes:
return
elif event.key == '+':
self._thicken_scan_line()
elif event.key == '-':
self._shrink_scan_line()
elif event.key == 'r':
self.reset()
def _thicken_scan_line(self):
self.linewidth += 1
self.line_changed(None, None)
def _shrink_scan_line(self):
if self.linewidth > 1:
self.linewidth -= 1
self.line_changed(None, None)
return self.end_pts, profile
def _autoscale_view(self):
if self.limits is None:
@@ -132,67 +101,22 @@ class LineProfile(PlotPlugin):
else:
self.ax.autoscale_view(scaley=False, tight=True)
def get_pt_under_cursor(self, event):
"""Return index of the end point under cursor, if sufficiently close"""
xy = np.asarray(self.scan_line.get_xydata())
xyt = self.scan_line.get_transform().transform(xy)
xt, yt = xyt[:, 0], xyt[:, 1]
d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2)
indseq = np.nonzero(np.equal(d, np.amin(d)))[0]
ind = indseq[0]
if d[ind] >= self.epsilon:
ind = None
return ind
def line_changed(self, end_pts):
x, y = np.transpose(end_pts)
def on_mouse_press(self, event):
if event.button != 1:
return
if event.inaxes == None:
return
self._active_pt = self.get_pt_under_cursor(event)
self.end_pts = end_pts
scan = profile_line(self.image_viewer.original_image, end_pts,
linewidth=self.line_tool.linewidth)
def on_mouse_release(self, event):
if event.button != 1:
return
self._active_pt = None
def on_move(self, event):
if event.button != 1:
return
if self._active_pt is None:
return
if not self.image_viewer.ax.in_axes(event):
return
x, y = event.xdata, event.ydata
self.line_changed(x, y)
def reset(self):
self.end_pts = self._init_end_pts.copy()
self.scan_line.set_data(np.transpose(self.end_pts))
self.line_changed(None, None)
def line_changed(self, x, y):
if x is not None:
self.end_pts[self._active_pt, :] = x, y
self.scan_line.set_data(np.transpose(self.end_pts))
self.scan_line.set_linewidth(self.linewidth)
scan = profile_line(self.image_viewer.original_image, self.end_pts,
linewidth=self.linewidth)
self.profile.set_xdata(np.arange(scan.shape[0]))
self.profile.set_ydata(scan)
self.ax.relim()
if self.useblit:
self.image_viewer.canvas.restore_region(self.img_background)
self.ax.draw_artist(self.scan_line)
self.ax.draw_artist(self.profile)
self.image_viewer.canvas.blit(self.image_viewer.ax.bbox)
self._autoscale_view()
self.image_viewer.redraw()
self.redraw()