| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- from functools import reduce
- import numpy as np
- from ..draw import polygon
- from .._shared.version_requirements import require
- LEFT_CLICK = 1
- RIGHT_CLICK = 3
- def _mask_from_vertices(vertices, shape, label):
- mask = np.zeros(shape, dtype=int)
- pr = [y for x, y in vertices]
- pc = [x for x, y in vertices]
- rr, cc = polygon(pr, pc, shape)
- mask[rr, cc] = label
- return mask
- @require("matplotlib", ">=3.3")
- def _draw_polygon(ax, vertices, alpha=0.4):
- from matplotlib.patches import Polygon
- from matplotlib.collections import PatchCollection
- import matplotlib.pyplot as plt
- polygon = Polygon(vertices, closed=True)
- p = PatchCollection([polygon], match_original=True, alpha=alpha)
- polygon_object = ax.add_collection(p)
- plt.draw()
- return polygon_object
- @require("matplotlib", ">=3.3")
- def manual_polygon_segmentation(image, alpha=0.4, return_all=False):
- """Return a label image based on polygon selections made with the mouse.
- Parameters
- ----------
- image : (M, N[, 3]) array
- Grayscale or RGB image.
- alpha : float, optional
- Transparency value for polygons drawn over the image.
- return_all : bool, optional
- If True, an array containing each separate polygon drawn is returned.
- (The polygons may overlap.) If False (default), latter polygons
- "overwrite" earlier ones where they overlap.
- Returns
- -------
- labels : array of int, shape ([Q, ]M, N)
- The segmented regions. If mode is `'separate'`, the leading dimension
- of the array corresponds to the number of regions that the user drew.
- Notes
- -----
- Use left click to select the vertices of the polygon
- and right click to confirm the selection once all vertices are selected.
- Examples
- --------
- >>> from skimage import data, future
- >>> import matplotlib.pyplot as plt # doctest: +SKIP
- >>> camera = data.camera()
- >>> mask = future.manual_polygon_segmentation(camera) # doctest: +SKIP
- >>> fig, ax = plt.subplots() # doctest: +SKIP
- >>> ax.imshow(mask) # doctest: +SKIP
- >>> plt.show() # doctest: +SKIP
- """
- import matplotlib
- import matplotlib.pyplot as plt
- list_of_vertex_lists = []
- polygons_drawn = []
- temp_list = []
- preview_polygon_drawn = []
- if image.ndim not in (2, 3):
- raise ValueError('Only 2D grayscale or RGB images are supported.')
- fig, ax = plt.subplots()
- fig.subplots_adjust(bottom=0.2)
- ax.imshow(image, cmap="gray")
- ax.set_axis_off()
- def _undo(*args, **kwargs):
- if list_of_vertex_lists:
- list_of_vertex_lists.pop()
- # Remove last polygon from list of polygons...
- last_poly = polygons_drawn.pop()
- # ... then from the plot
- last_poly.remove()
- fig.canvas.draw_idle()
- undo_pos = fig.add_axes([0.85, 0.05, 0.075, 0.075])
- undo_button = matplotlib.widgets.Button(undo_pos, '\u27f2')
- undo_button.on_clicked(_undo)
- def _extend_polygon(event):
- # Do not record click events outside axis or in undo button
- if event.inaxes is None or event.inaxes is undo_pos:
- return
- # Do not record click events when toolbar is active
- if ax.get_navigate_mode():
- return
- if event.button == LEFT_CLICK: # Select vertex
- temp_list.append([event.xdata, event.ydata])
- # Remove previously drawn preview polygon if any.
- if preview_polygon_drawn:
- poly = preview_polygon_drawn.pop()
- poly.remove()
- # Preview polygon with selected vertices.
- polygon = _draw_polygon(ax, temp_list, alpha=(alpha / 1.4))
- preview_polygon_drawn.append(polygon)
- elif event.button == RIGHT_CLICK: # Confirm the selection
- if not temp_list:
- return
- # Store the vertices of the polygon as shown in preview.
- # Redraw polygon and store it in polygons_drawn so that
- # `_undo` works correctly.
- list_of_vertex_lists.append(temp_list[:])
- polygon_object = _draw_polygon(ax, temp_list, alpha=alpha)
- polygons_drawn.append(polygon_object)
- # Empty the temporary variables.
- preview_poly = preview_polygon_drawn.pop()
- preview_poly.remove()
- del temp_list[:]
- plt.draw()
- fig.canvas.mpl_connect('button_press_event', _extend_polygon)
- plt.show(block=True)
- labels = (
- _mask_from_vertices(vertices, image.shape[:2], i)
- for i, vertices in enumerate(list_of_vertex_lists, start=1)
- )
- if return_all:
- return np.stack(labels)
- else:
- return reduce(np.maximum, labels, np.broadcast_to(0, image.shape[:2]))
- @require("matplotlib", ">=3.3")
- def manual_lasso_segmentation(image, alpha=0.4, return_all=False):
- """Return a label image based on freeform selections made with the mouse.
- Parameters
- ----------
- image : (M, N[, 3]) array
- Grayscale or RGB image.
- alpha : float, optional
- Transparency value for polygons drawn over the image.
- return_all : bool, optional
- If True, an array containing each separate polygon drawn is returned.
- (The polygons may overlap.) If False (default), latter polygons
- "overwrite" earlier ones where they overlap.
- Returns
- -------
- labels : array of int, shape ([Q, ]M, N)
- The segmented regions. If mode is `'separate'`, the leading dimension
- of the array corresponds to the number of regions that the user drew.
- Notes
- -----
- Press and hold the left mouse button to draw around each object.
- Examples
- --------
- >>> from skimage import data, future
- >>> import matplotlib.pyplot as plt # doctest: +SKIP
- >>> camera = data.camera()
- >>> mask = future.manual_lasso_segmentation(camera) # doctest: +SKIP
- >>> fig, ax = plt.subplots() # doctest: +SKIP
- >>> ax.imshow(mask) # doctest: +SKIP
- >>> plt.show() # doctest: +SKIP
- """
- import matplotlib
- import matplotlib.pyplot as plt
- list_of_vertex_lists = []
- polygons_drawn = []
- if image.ndim not in (2, 3):
- raise ValueError('Only 2D grayscale or RGB images are supported.')
- fig, ax = plt.subplots()
- fig.subplots_adjust(bottom=0.2)
- ax.imshow(image, cmap="gray")
- ax.set_axis_off()
- def _undo(*args, **kwargs):
- if list_of_vertex_lists:
- list_of_vertex_lists.pop()
- # Remove last polygon from list of polygons...
- last_poly = polygons_drawn.pop()
- # ... then from the plot
- last_poly.remove()
- fig.canvas.draw_idle()
- undo_pos = fig.add_axes([0.85, 0.05, 0.075, 0.075])
- undo_button = matplotlib.widgets.Button(undo_pos, '\u27f2')
- undo_button.on_clicked(_undo)
- def _on_lasso_selection(vertices):
- if len(vertices) < 3:
- return
- list_of_vertex_lists.append(vertices)
- polygon_object = _draw_polygon(ax, vertices, alpha=alpha)
- polygons_drawn.append(polygon_object)
- plt.draw()
- matplotlib.widgets.LassoSelector(ax, _on_lasso_selection)
- plt.show(block=True)
- labels = (
- _mask_from_vertices(vertices, image.shape[:2], i)
- for i, vertices in enumerate(list_of_vertex_lists, start=1)
- )
- if return_all:
- return np.stack(labels)
- else:
- return reduce(np.maximum, labels, np.broadcast_to(0, image.shape[:2]))
|