diff --git a/skimage/viewer/plugins/lineprofile.py b/skimage/viewer/plugins/lineprofile.py index 69c9ebfb..09aa5d7e 100644 --- a/skimage/viewer/plugins/lineprofile.py +++ b/skimage/viewer/plugins/lineprofile.py @@ -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()