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
15
venv/Lib/site-packages/skimage/future/__init__.py
Normal file
15
venv/Lib/site-packages/skimage/future/__init__.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
"""Functionality with an experimental API. Although you can count on the
|
||||
functions in this package being around in the future, the API may change with
|
||||
any version update **and will not follow the skimage two-version deprecation
|
||||
path**. Therefore, use the functions herein with care, and do not use them in
|
||||
production code that will depend on updated skimage versions.
|
||||
"""
|
||||
|
||||
from . import graph
|
||||
from .manual_segmentation import manual_polygon_segmentation
|
||||
from .manual_segmentation import manual_lasso_segmentation
|
||||
|
||||
|
||||
__all__ = ['graph',
|
||||
'manual_lasso_segmentation',
|
||||
'manual_polygon_segmentation']
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
13
venv/Lib/site-packages/skimage/future/graph/__init__.py
Normal file
13
venv/Lib/site-packages/skimage/future/graph/__init__.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from .graph_cut import cut_threshold, cut_normalized
|
||||
from .rag import rag_mean_color, RAG, show_rag, rag_boundary
|
||||
from .graph_merge import merge_hierarchical
|
||||
ncut = cut_normalized
|
||||
|
||||
__all__ = ['rag_mean_color',
|
||||
'cut_threshold',
|
||||
'cut_normalized',
|
||||
'ncut',
|
||||
'show_rag',
|
||||
'merge_hierarchical',
|
||||
'rag_boundary',
|
||||
'RAG']
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
68
venv/Lib/site-packages/skimage/future/graph/_ncut.py
Normal file
68
venv/Lib/site-packages/skimage/future/graph/_ncut.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
try:
|
||||
import networkx as nx
|
||||
except ImportError:
|
||||
from ..._shared.utils import warn
|
||||
warn('RAGs require networkx')
|
||||
import numpy as np
|
||||
from scipy import sparse
|
||||
from . import _ncut_cy
|
||||
|
||||
|
||||
def DW_matrices(graph):
|
||||
"""Returns the diagonal and weight matrices of a graph.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
graph : RAG
|
||||
A Region Adjacency Graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
D : csc_matrix
|
||||
The diagonal matrix of the graph. ``D[i, i]`` is the sum of weights of
|
||||
all edges incident on `i`. All other entries are `0`.
|
||||
W : csc_matrix
|
||||
The weight matrix of the graph. ``W[i, j]`` is the weight of the edge
|
||||
joining `i` to `j`.
|
||||
"""
|
||||
# sparse.eighsh is most efficient with CSC-formatted input
|
||||
W = nx.to_scipy_sparse_matrix(graph, format='csc')
|
||||
entries = W.sum(axis=0)
|
||||
D = sparse.dia_matrix((entries, 0), shape=W.shape).tocsc()
|
||||
|
||||
return D, W
|
||||
|
||||
|
||||
def ncut_cost(cut, D, W):
|
||||
"""Returns the N-cut cost of a bi-partition of a graph.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cut : ndarray
|
||||
The mask for the nodes in the graph. Nodes corresponding to a `True`
|
||||
value are in one set.
|
||||
D : csc_matrix
|
||||
The diagonal matrix of the graph.
|
||||
W : csc_matrix
|
||||
The weight matrix of the graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
cost : float
|
||||
The cost of performing the N-cut.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Normalized Cuts and Image Segmentation, Jianbo Shi and
|
||||
Jitendra Malik, IEEE Transactions on Pattern Analysis and Machine
|
||||
Intelligence, Page 889, Equation 2.
|
||||
"""
|
||||
cut = np.array(cut)
|
||||
cut_cost = _ncut_cy.cut_cost(cut, W)
|
||||
|
||||
# D has elements only along the diagonal, one per node, so we can directly
|
||||
# index the data attribute with cut.
|
||||
assoc_a = D.data[cut].sum()
|
||||
assoc_b = D.data[~cut].sum()
|
||||
|
||||
return (cut_cost / assoc_a) + (cut_cost / assoc_b)
|
Binary file not shown.
305
venv/Lib/site-packages/skimage/future/graph/graph_cut.py
Normal file
305
venv/Lib/site-packages/skimage/future/graph/graph_cut.py
Normal file
|
@ -0,0 +1,305 @@
|
|||
try:
|
||||
import networkx as nx
|
||||
except ImportError:
|
||||
from ..._shared.utils import warn
|
||||
warn('RAGs require networkx')
|
||||
import numpy as np
|
||||
from . import _ncut
|
||||
from . import _ncut_cy
|
||||
from ..._shared.utils import check_random_state
|
||||
from scipy.sparse import linalg
|
||||
|
||||
|
||||
def cut_threshold(labels, rag, thresh, in_place=True):
|
||||
"""Combine regions separated by weight less than threshold.
|
||||
|
||||
Given an image's labels and its RAG, output new labels by
|
||||
combining regions whose nodes are separated by a weight less
|
||||
than the given threshold.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
labels : ndarray
|
||||
The array of labels.
|
||||
rag : RAG
|
||||
The region adjacency graph.
|
||||
thresh : float
|
||||
The threshold. Regions connected by edges with smaller weights are
|
||||
combined.
|
||||
in_place : bool
|
||||
If set, modifies `rag` in place. The function will remove the edges
|
||||
with weights less that `thresh`. If set to `False` the function
|
||||
makes a copy of `rag` before proceeding.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The new labelled array.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data, segmentation
|
||||
>>> from skimage.future import graph
|
||||
>>> img = data.astronaut()
|
||||
>>> labels = segmentation.slic(img)
|
||||
>>> rag = graph.rag_mean_color(img, labels)
|
||||
>>> new_labels = graph.cut_threshold(labels, rag, 10)
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Alain Tremeau and Philippe Colantoni
|
||||
"Regions Adjacency Graph Applied To Color Image Segmentation"
|
||||
:DOI:`10.1109/83.841950`
|
||||
|
||||
"""
|
||||
if not in_place:
|
||||
rag = rag.copy()
|
||||
|
||||
# Because deleting edges while iterating through them produces an error.
|
||||
to_remove = [(x, y) for x, y, d in rag.edges(data=True)
|
||||
if d['weight'] >= thresh]
|
||||
rag.remove_edges_from(to_remove)
|
||||
|
||||
comps = nx.connected_components(rag)
|
||||
|
||||
# We construct an array which can map old labels to the new ones.
|
||||
# All the labels within a connected component are assigned to a single
|
||||
# label in the output.
|
||||
map_array = np.arange(labels.max() + 1, dtype=labels.dtype)
|
||||
for i, nodes in enumerate(comps):
|
||||
for node in nodes:
|
||||
for label in rag.nodes[node]['labels']:
|
||||
map_array[label] = i
|
||||
|
||||
return map_array[labels]
|
||||
|
||||
|
||||
def cut_normalized(labels, rag, thresh=0.001, num_cuts=10, in_place=True,
|
||||
max_edge=1.0,
|
||||
*,
|
||||
random_state=None,
|
||||
):
|
||||
"""Perform Normalized Graph cut on the Region Adjacency Graph.
|
||||
|
||||
Given an image's labels and its similarity RAG, recursively perform
|
||||
a 2-way normalized cut on it. All nodes belonging to a subgraph
|
||||
that cannot be cut further are assigned a unique label in the
|
||||
output.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
labels : ndarray
|
||||
The array of labels.
|
||||
rag : RAG
|
||||
The region adjacency graph.
|
||||
thresh : float
|
||||
The threshold. A subgraph won't be further subdivided if the
|
||||
value of the N-cut exceeds `thresh`.
|
||||
num_cuts : int
|
||||
The number or N-cuts to perform before determining the optimal one.
|
||||
in_place : bool
|
||||
If set, modifies `rag` in place. For each node `n` the function will
|
||||
set a new attribute ``rag.nodes[n]['ncut label']``.
|
||||
max_edge : float, optional
|
||||
The maximum possible value of an edge in the RAG. This corresponds to
|
||||
an edge between identical regions. This is used to put self
|
||||
edges in the RAG.
|
||||
random_state : int, RandomState instance or None, optional
|
||||
If int, random_state is the seed used by the random number generator;
|
||||
If RandomState instance, random_state is the random number generator;
|
||||
If None, the random number generator is the RandomState instance used
|
||||
by `np.random`. The random state is used for the starting point
|
||||
of `scipy.sparse.linalg.eigsh`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The new labeled array.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data, segmentation
|
||||
>>> from skimage.future import graph
|
||||
>>> img = data.astronaut()
|
||||
>>> labels = segmentation.slic(img)
|
||||
>>> rag = graph.rag_mean_color(img, labels, mode='similarity')
|
||||
>>> new_labels = graph.cut_normalized(labels, rag)
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Shi, J.; Malik, J., "Normalized cuts and image segmentation",
|
||||
Pattern Analysis and Machine Intelligence,
|
||||
IEEE Transactions on, vol. 22, no. 8, pp. 888-905, August 2000.
|
||||
|
||||
"""
|
||||
random_state = check_random_state(random_state)
|
||||
if not in_place:
|
||||
rag = rag.copy()
|
||||
|
||||
for node in rag.nodes():
|
||||
rag.add_edge(node, node, weight=max_edge)
|
||||
|
||||
_ncut_relabel(rag, thresh, num_cuts, random_state)
|
||||
|
||||
map_array = np.zeros(labels.max() + 1, dtype=labels.dtype)
|
||||
# Mapping from old labels to new
|
||||
for n, d in rag.nodes(data=True):
|
||||
map_array[d['labels']] = d['ncut label']
|
||||
|
||||
return map_array[labels]
|
||||
|
||||
|
||||
def partition_by_cut(cut, rag):
|
||||
"""Compute resulting subgraphs from given bi-partition.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cut : array
|
||||
A array of booleans. Elements set to `True` belong to one
|
||||
set.
|
||||
rag : RAG
|
||||
The Region Adjacency Graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
sub1, sub2 : RAG
|
||||
The two resulting subgraphs from the bi-partition.
|
||||
"""
|
||||
# `cut` is derived from `D` and `W` matrices, which also follow the
|
||||
# ordering returned by `rag.nodes()` because we use
|
||||
# nx.to_scipy_sparse_matrix.
|
||||
|
||||
# Example
|
||||
# rag.nodes() = [3, 7, 9, 13]
|
||||
# cut = [True, False, True, False]
|
||||
# nodes1 = [3, 9]
|
||||
# nodes2 = [7, 10]
|
||||
|
||||
nodes1 = [n for i, n in enumerate(rag.nodes()) if cut[i]]
|
||||
nodes2 = [n for i, n in enumerate(rag.nodes()) if not cut[i]]
|
||||
|
||||
sub1 = rag.subgraph(nodes1)
|
||||
sub2 = rag.subgraph(nodes2)
|
||||
|
||||
return sub1, sub2
|
||||
|
||||
|
||||
def get_min_ncut(ev, d, w, num_cuts):
|
||||
"""Threshold an eigenvector evenly, to determine minimum ncut.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ev : array
|
||||
The eigenvector to threshold.
|
||||
d : ndarray
|
||||
The diagonal matrix of the graph.
|
||||
w : ndarray
|
||||
The weight matrix of the graph.
|
||||
num_cuts : int
|
||||
The number of evenly spaced thresholds to check for.
|
||||
|
||||
Returns
|
||||
-------
|
||||
mask : array
|
||||
The array of booleans which denotes the bi-partition.
|
||||
mcut : float
|
||||
The value of the minimum ncut.
|
||||
"""
|
||||
mcut = np.inf
|
||||
mn = ev.min()
|
||||
mx = ev.max()
|
||||
|
||||
# If all values in `ev` are equal, it implies that the graph can't be
|
||||
# further sub-divided. In this case the bi-partition is the the graph
|
||||
# itself and an empty set.
|
||||
min_mask = np.zeros_like(ev, dtype=np.bool)
|
||||
if np.allclose(mn, mx):
|
||||
return min_mask, mcut
|
||||
|
||||
# Refer Shi & Malik 2001, Section 3.1.3, Page 892
|
||||
# Perform evenly spaced n-cuts and determine the optimal one.
|
||||
for t in np.linspace(mn, mx, num_cuts, endpoint=False):
|
||||
mask = ev > t
|
||||
cost = _ncut.ncut_cost(mask, d, w)
|
||||
if cost < mcut:
|
||||
min_mask = mask
|
||||
mcut = cost
|
||||
|
||||
return min_mask, mcut
|
||||
|
||||
|
||||
def _label_all(rag, attr_name):
|
||||
"""Assign a unique integer to the given attribute in the RAG.
|
||||
|
||||
This function assumes that all labels in `rag` are unique. It
|
||||
picks up a random label from them and assigns it to the `attr_name`
|
||||
attribute of all the nodes.
|
||||
|
||||
rag : RAG
|
||||
The Region Adjacency Graph.
|
||||
attr_name : string
|
||||
The attribute to which a unique integer is assigned.
|
||||
"""
|
||||
node = min(rag.nodes())
|
||||
new_label = rag.nodes[node]['labels'][0]
|
||||
for n, d in rag.nodes(data=True):
|
||||
d[attr_name] = new_label
|
||||
|
||||
|
||||
def _ncut_relabel(rag, thresh, num_cuts, random_state):
|
||||
"""Perform Normalized Graph cut on the Region Adjacency Graph.
|
||||
|
||||
Recursively partition the graph into 2, until further subdivision
|
||||
yields a cut greater than `thresh` or such a cut cannot be computed.
|
||||
For such a subgraph, indices to labels of all its nodes map to a single
|
||||
unique value.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
rag : RAG
|
||||
The region adjacency graph.
|
||||
thresh : float
|
||||
The threshold. A subgraph won't be further subdivided if the
|
||||
value of the N-cut exceeds `thresh`.
|
||||
num_cuts : int
|
||||
The number or N-cuts to perform before determining the optimal one.
|
||||
random_state: RandomState instance
|
||||
Provides initial values for eigenvalue solver.
|
||||
"""
|
||||
d, w = _ncut.DW_matrices(rag)
|
||||
m = w.shape[0]
|
||||
|
||||
if m > 2:
|
||||
d2 = d.copy()
|
||||
# Since d is diagonal, we can directly operate on its data
|
||||
# the inverse of the square root
|
||||
d2.data = np.reciprocal(np.sqrt(d2.data, out=d2.data), out=d2.data)
|
||||
|
||||
# Refer Shi & Malik 2001, Equation 7, Page 891
|
||||
A = d2 * (d - w) * d2
|
||||
# Initialize the vector to ensure reproducibility.
|
||||
v0 = random_state.rand(A.shape[0])
|
||||
vals, vectors = linalg.eigsh(A, which='SM', v0=v0,
|
||||
k=min(100, m - 2))
|
||||
|
||||
# Pick second smallest eigenvector.
|
||||
# Refer Shi & Malik 2001, Section 3.2.3, Page 893
|
||||
vals, vectors = np.real(vals), np.real(vectors)
|
||||
index2 = _ncut_cy.argmin2(vals)
|
||||
ev = vectors[:, index2]
|
||||
|
||||
cut_mask, mcut = get_min_ncut(ev, d, w, num_cuts)
|
||||
if (mcut < thresh):
|
||||
# Sub divide and perform N-cut again
|
||||
# Refer Shi & Malik 2001, Section 3.2.5, Page 893
|
||||
sub1, sub2 = partition_by_cut(cut_mask, rag)
|
||||
|
||||
_ncut_relabel(sub1, thresh, num_cuts, random_state)
|
||||
_ncut_relabel(sub2, thresh, num_cuts, random_state)
|
||||
return
|
||||
|
||||
# The N-cut wasn't small enough, or could not be computed.
|
||||
# The remaining graph is a region.
|
||||
# Assign `ncut label` by picking any label from the existing nodes, since
|
||||
# `labels` are unique, `new_label` is also unique.
|
||||
_label_all(rag, 'ncut label')
|
137
venv/Lib/site-packages/skimage/future/graph/graph_merge.py
Normal file
137
venv/Lib/site-packages/skimage/future/graph/graph_merge.py
Normal file
|
@ -0,0 +1,137 @@
|
|||
import numpy as np
|
||||
import heapq
|
||||
|
||||
|
||||
def _revalidate_node_edges(rag, node, heap_list):
|
||||
"""Handles validation and invalidation of edges incident to a node.
|
||||
|
||||
This function invalidates all existing edges incident on `node` and inserts
|
||||
new items in `heap_list` updated with the valid weights.
|
||||
|
||||
rag : RAG
|
||||
The Region Adjacency Graph.
|
||||
node : int
|
||||
The id of the node whose incident edges are to be validated/invalidated
|
||||
.
|
||||
heap_list : list
|
||||
The list containing the existing heap of edges.
|
||||
"""
|
||||
# networkx updates data dictionary if edge exists
|
||||
# this would mean we have to reposition these edges in
|
||||
# heap if their weight is updated.
|
||||
# instead we invalidate them
|
||||
|
||||
for nbr in rag.neighbors(node):
|
||||
data = rag[node][nbr]
|
||||
try:
|
||||
# invalidate edges incident on `dst`, they have new weights
|
||||
data['heap item'][3] = False
|
||||
_invalidate_edge(rag, node, nbr)
|
||||
except KeyError:
|
||||
# will handle the case where the edge did not exist in the existing
|
||||
# graph
|
||||
pass
|
||||
|
||||
wt = data['weight']
|
||||
heap_item = [wt, node, nbr, True]
|
||||
data['heap item'] = heap_item
|
||||
heapq.heappush(heap_list, heap_item)
|
||||
|
||||
|
||||
def _rename_node(graph, node_id, copy_id):
|
||||
""" Rename `node_id` in `graph` to `copy_id`. """
|
||||
|
||||
graph._add_node_silent(copy_id)
|
||||
graph.nodes[copy_id].update(graph.nodes[node_id])
|
||||
|
||||
for nbr in graph.neighbors(node_id):
|
||||
wt = graph[node_id][nbr]['weight']
|
||||
graph.add_edge(nbr, copy_id, {'weight': wt})
|
||||
|
||||
graph.remove_node(node_id)
|
||||
|
||||
|
||||
def _invalidate_edge(graph, n1, n2):
|
||||
""" Invalidates the edge (n1, n2) in the heap. """
|
||||
graph[n1][n2]['heap item'][3] = False
|
||||
|
||||
|
||||
def merge_hierarchical(labels, rag, thresh, rag_copy, in_place_merge,
|
||||
merge_func, weight_func):
|
||||
"""Perform hierarchical merging of a RAG.
|
||||
|
||||
Greedily merges the most similar pair of nodes until no edges lower than
|
||||
`thresh` remain.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
labels : ndarray
|
||||
The array of labels.
|
||||
rag : RAG
|
||||
The Region Adjacency Graph.
|
||||
thresh : float
|
||||
Regions connected by an edge with weight smaller than `thresh` are
|
||||
merged.
|
||||
rag_copy : bool
|
||||
If set, the RAG copied before modifying.
|
||||
in_place_merge : bool
|
||||
If set, the nodes are merged in place. Otherwise, a new node is
|
||||
created for each merge..
|
||||
merge_func : callable
|
||||
This function is called before merging two nodes. For the RAG `graph`
|
||||
while merging `src` and `dst`, it is called as follows
|
||||
``merge_func(graph, src, dst)``.
|
||||
weight_func : callable
|
||||
The function to compute the new weights of the nodes adjacent to the
|
||||
merged node. This is directly supplied as the argument `weight_func`
|
||||
to `merge_nodes`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The new labeled array.
|
||||
|
||||
"""
|
||||
if rag_copy:
|
||||
rag = rag.copy()
|
||||
|
||||
edge_heap = []
|
||||
for n1, n2, data in rag.edges(data=True):
|
||||
# Push a valid edge in the heap
|
||||
wt = data['weight']
|
||||
heap_item = [wt, n1, n2, True]
|
||||
heapq.heappush(edge_heap, heap_item)
|
||||
|
||||
# Reference to the heap item in the graph
|
||||
data['heap item'] = heap_item
|
||||
|
||||
while len(edge_heap) > 0 and edge_heap[0][0] < thresh:
|
||||
_, n1, n2, valid = heapq.heappop(edge_heap)
|
||||
|
||||
# Ensure popped edge is valid, if not, the edge is discarded
|
||||
if valid:
|
||||
# Invalidate all neigbors of `src` before its deleted
|
||||
|
||||
for nbr in rag.neighbors(n1):
|
||||
_invalidate_edge(rag, n1, nbr)
|
||||
|
||||
for nbr in rag.neighbors(n2):
|
||||
_invalidate_edge(rag, n2, nbr)
|
||||
|
||||
if not in_place_merge:
|
||||
next_id = rag.next_id()
|
||||
_rename_node(rag, n2, next_id)
|
||||
src, dst = n1, next_id
|
||||
else:
|
||||
src, dst = n1, n2
|
||||
|
||||
merge_func(rag, src, dst)
|
||||
new_id = rag.merge_nodes(src, dst, weight_func)
|
||||
_revalidate_node_edges(rag, new_id, edge_heap)
|
||||
|
||||
label_map = np.arange(labels.max() + 1)
|
||||
for ix, (n, d) in enumerate(rag.nodes(data=True)):
|
||||
for label in d['labels']:
|
||||
label_map[label] = ix
|
||||
|
||||
return label_map[labels]
|
558
venv/Lib/site-packages/skimage/future/graph/rag.py
Normal file
558
venv/Lib/site-packages/skimage/future/graph/rag.py
Normal file
|
@ -0,0 +1,558 @@
|
|||
import networkx as nx
|
||||
import numpy as np
|
||||
from numpy.lib.stride_tricks import as_strided
|
||||
from scipy import ndimage as ndi
|
||||
from scipy import sparse
|
||||
import math
|
||||
from ... import measure, segmentation, util, color
|
||||
|
||||
|
||||
def _edge_generator_from_csr(csr_matrix):
|
||||
"""Yield weighted edge triples for use by NetworkX from a CSR matrix.
|
||||
|
||||
This function is a straight rewrite of
|
||||
`networkx.convert_matrix._csr_gen_triples`. Since that is a private
|
||||
function, it is safer to include our own here.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
csr_matrix : scipy.sparse.csr_matrix
|
||||
The input matrix. An edge (i, j, w) will be yielded if there is a
|
||||
data value for coordinates (i, j) in the matrix, even if that value
|
||||
is 0.
|
||||
|
||||
Yields
|
||||
------
|
||||
i, j, w : (int, int, float) tuples
|
||||
Each value `w` in the matrix along with its coordinates (i, j).
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
>>> dense = np.eye(2, dtype=np.float)
|
||||
>>> csr = sparse.csr_matrix(dense)
|
||||
>>> edges = _edge_generator_from_csr(csr)
|
||||
>>> list(edges)
|
||||
[(0, 0, 1.0), (1, 1, 1.0)]
|
||||
"""
|
||||
nrows = csr_matrix.shape[0]
|
||||
values = csr_matrix.data
|
||||
indptr = csr_matrix.indptr
|
||||
col_indices = csr_matrix.indices
|
||||
for i in range(nrows):
|
||||
for j in range(indptr[i], indptr[i + 1]):
|
||||
yield i, col_indices[j], values[j]
|
||||
|
||||
|
||||
def min_weight(graph, src, dst, n):
|
||||
"""Callback to handle merging nodes by choosing minimum weight.
|
||||
|
||||
Returns a dictionary with `"weight"` set as either the weight between
|
||||
(`src`, `n`) or (`dst`, `n`) in `graph` or the minimum of the two when
|
||||
both exist.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
graph : RAG
|
||||
The graph under consideration.
|
||||
src, dst : int
|
||||
The verices in `graph` to be merged.
|
||||
n : int
|
||||
A neighbor of `src` or `dst` or both.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data : dict
|
||||
A dict with the `"weight"` attribute set the weight between
|
||||
(`src`, `n`) or (`dst`, `n`) in `graph` or the minimum of the two when
|
||||
both exist.
|
||||
|
||||
"""
|
||||
|
||||
# cover the cases where n only has edge to either `src` or `dst`
|
||||
default = {'weight': np.inf}
|
||||
w1 = graph[n].get(src, default)['weight']
|
||||
w2 = graph[n].get(dst, default)['weight']
|
||||
return {'weight': min(w1, w2)}
|
||||
|
||||
|
||||
def _add_edge_filter(values, graph):
|
||||
"""Create edge in `graph` between central element of `values` and the rest.
|
||||
|
||||
Add an edge between the middle element in `values` and
|
||||
all other elements of `values` into `graph`. ``values[len(values) // 2]``
|
||||
is expected to be the central value of the footprint used.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
values : array
|
||||
The array to process.
|
||||
graph : RAG
|
||||
The graph to add edges in.
|
||||
|
||||
Returns
|
||||
-------
|
||||
0 : float
|
||||
Always returns 0. The return value is required so that `generic_filter`
|
||||
can put it in the output array, but it is ignored by this filter.
|
||||
"""
|
||||
values = values.astype(int)
|
||||
center = values[len(values) // 2]
|
||||
for value in values:
|
||||
if value != center and not graph.has_edge(center, value):
|
||||
graph.add_edge(center, value)
|
||||
return 0.
|
||||
|
||||
|
||||
class RAG(nx.Graph):
|
||||
|
||||
"""
|
||||
The Region Adjacency Graph (RAG) of an image, subclasses
|
||||
`networx.Graph <http://networkx.github.io/documentation/latest/reference/classes/graph.html>`_
|
||||
|
||||
Parameters
|
||||
----------
|
||||
label_image : array of int
|
||||
An initial segmentation, with each region labeled as a different
|
||||
integer. Every unique value in ``label_image`` will correspond to
|
||||
a node in the graph.
|
||||
connectivity : int in {1, ..., ``label_image.ndim``}, optional
|
||||
The connectivity between pixels in ``label_image``. For a 2D image,
|
||||
a connectivity of 1 corresponds to immediate neighbors up, down,
|
||||
left, and right, while a connectivity of 2 also includes diagonal
|
||||
neighbors. See `scipy.ndimage.generate_binary_structure`.
|
||||
data : networkx Graph specification, optional
|
||||
Initial or additional edges to pass to the NetworkX Graph
|
||||
constructor. See `networkx.Graph`. Valid edge specifications
|
||||
include edge list (list of tuples), NumPy arrays, and SciPy
|
||||
sparse matrices.
|
||||
**attr : keyword arguments, optional
|
||||
Additional attributes to add to the graph.
|
||||
"""
|
||||
|
||||
def __init__(self, label_image=None, connectivity=1, data=None, **attr):
|
||||
|
||||
super(RAG, self).__init__(data, **attr)
|
||||
if self.number_of_nodes() == 0:
|
||||
self.max_id = 0
|
||||
else:
|
||||
self.max_id = max(self.nodes())
|
||||
|
||||
if label_image is not None:
|
||||
fp = ndi.generate_binary_structure(label_image.ndim, connectivity)
|
||||
# In the next ``ndi.generic_filter`` function, the kwarg
|
||||
# ``output`` is used to provide a strided array with a single
|
||||
# 64-bit floating point number, to which the function repeatedly
|
||||
# writes. This is done because even if we don't care about the
|
||||
# output, without this, a float array of the same shape as the
|
||||
# input image will be created and that could be expensive in
|
||||
# memory consumption.
|
||||
ndi.generic_filter(
|
||||
label_image,
|
||||
function=_add_edge_filter,
|
||||
footprint=fp,
|
||||
mode='nearest',
|
||||
output=as_strided(np.empty((1,), dtype=np.float_),
|
||||
shape=label_image.shape,
|
||||
strides=((0,) * label_image.ndim)),
|
||||
extra_arguments=(self,))
|
||||
|
||||
def merge_nodes(self, src, dst, weight_func=min_weight, in_place=True,
|
||||
extra_arguments=[], extra_keywords={}):
|
||||
"""Merge node `src` and `dst`.
|
||||
|
||||
The new combined node is adjacent to all the neighbors of `src`
|
||||
and `dst`. `weight_func` is called to decide the weight of edges
|
||||
incident on the new node.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
src, dst : int
|
||||
Nodes to be merged.
|
||||
weight_func : callable, optional
|
||||
Function to decide the attributes of edges incident on the new
|
||||
node. For each neighbor `n` for `src and `dst`, `weight_func` will
|
||||
be called as follows: `weight_func(src, dst, n, *extra_arguments,
|
||||
**extra_keywords)`. `src`, `dst` and `n` are IDs of vertices in the
|
||||
RAG object which is in turn a subclass of `networkx.Graph`. It is
|
||||
expected to return a dict of attributes of the resulting edge.
|
||||
in_place : bool, optional
|
||||
If set to `True`, the merged node has the id `dst`, else merged
|
||||
node has a new id which is returned.
|
||||
extra_arguments : sequence, optional
|
||||
The sequence of extra positional arguments passed to
|
||||
`weight_func`.
|
||||
extra_keywords : dictionary, optional
|
||||
The dict of keyword arguments passed to the `weight_func`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
id : int
|
||||
The id of the new node.
|
||||
|
||||
Notes
|
||||
-----
|
||||
If `in_place` is `False` the resulting node has a new id, rather than
|
||||
`dst`.
|
||||
"""
|
||||
src_nbrs = set(self.neighbors(src))
|
||||
dst_nbrs = set(self.neighbors(dst))
|
||||
neighbors = (src_nbrs | dst_nbrs) - {src, dst}
|
||||
|
||||
if in_place:
|
||||
new = dst
|
||||
else:
|
||||
new = self.next_id()
|
||||
self.add_node(new)
|
||||
|
||||
for neighbor in neighbors:
|
||||
data = weight_func(self, src, new, neighbor, *extra_arguments,
|
||||
**extra_keywords)
|
||||
self.add_edge(neighbor, new, attr_dict=data)
|
||||
|
||||
self.nodes[new]['labels'] = (self.nodes[src]['labels'] +
|
||||
self.nodes[dst]['labels'])
|
||||
self.remove_node(src)
|
||||
|
||||
if not in_place:
|
||||
self.remove_node(dst)
|
||||
|
||||
return new
|
||||
|
||||
def add_node(self, n, attr_dict=None, **attr):
|
||||
"""Add node `n` while updating the maximum node id.
|
||||
|
||||
.. seealso:: :func:`networkx.Graph.add_node`."""
|
||||
if attr_dict is None: # compatibility with old networkx
|
||||
attr_dict = attr
|
||||
else:
|
||||
attr_dict.update(attr)
|
||||
super(RAG, self).add_node(n, **attr_dict)
|
||||
self.max_id = max(n, self.max_id)
|
||||
|
||||
def add_edge(self, u, v, attr_dict=None, **attr):
|
||||
"""Add an edge between `u` and `v` while updating max node id.
|
||||
|
||||
.. seealso:: :func:`networkx.Graph.add_edge`."""
|
||||
if attr_dict is None: # compatibility with old networkx
|
||||
attr_dict = attr
|
||||
else:
|
||||
attr_dict.update(attr)
|
||||
super(RAG, self).add_edge(u, v, **attr_dict)
|
||||
self.max_id = max(u, v, self.max_id)
|
||||
|
||||
def copy(self):
|
||||
"""Copy the graph with its max node id.
|
||||
|
||||
.. seealso:: :func:`networkx.Graph.copy`."""
|
||||
g = super(RAG, self).copy()
|
||||
g.max_id = self.max_id
|
||||
return g
|
||||
|
||||
def fresh_copy(self):
|
||||
"""Return a fresh copy graph with the same data structure.
|
||||
|
||||
A fresh copy has no nodes, edges or graph attributes. It is
|
||||
the same data structure as the current graph. This method is
|
||||
typically used to create an empty version of the graph.
|
||||
|
||||
This is required when subclassing Graph with networkx v2 and
|
||||
does not cause problems for v1. Here is more detail from
|
||||
the network migrating from 1.x to 2.x document::
|
||||
|
||||
With the new GraphViews (SubGraph, ReversedGraph, etc)
|
||||
you can't assume that ``G.__class__()`` will create a new
|
||||
instance of the same graph type as ``G``. In fact, the
|
||||
call signature for ``__class__`` differs depending on
|
||||
whether ``G`` is a view or a base class. For v2.x you
|
||||
should use ``G.fresh_copy()`` to create a null graph of
|
||||
the correct type---ready to fill with nodes and edges.
|
||||
|
||||
"""
|
||||
return RAG()
|
||||
|
||||
def next_id(self):
|
||||
"""Returns the `id` for the new node to be inserted.
|
||||
|
||||
The current implementation returns one more than the maximum `id`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
id : int
|
||||
The `id` of the new node to be inserted.
|
||||
"""
|
||||
return self.max_id + 1
|
||||
|
||||
def _add_node_silent(self, n):
|
||||
"""Add node `n` without updating the maximum node id.
|
||||
|
||||
This is a convenience method used internally.
|
||||
|
||||
.. seealso:: :func:`networkx.Graph.add_node`."""
|
||||
super(RAG, self).add_node(n)
|
||||
|
||||
|
||||
def rag_mean_color(image, labels, connectivity=2, mode='distance',
|
||||
sigma=255.0):
|
||||
"""Compute the Region Adjacency Graph using mean colors.
|
||||
|
||||
Given an image and its initial segmentation, this method constructs the
|
||||
corresponding Region Adjacency Graph (RAG). Each node in the RAG
|
||||
represents a set of pixels within `image` with the same label in `labels`.
|
||||
The weight between two adjacent regions represents how similar or
|
||||
dissimilar two regions are depending on the `mode` parameter.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : ndarray, shape(M, N, [..., P,] 3)
|
||||
Input image.
|
||||
labels : ndarray, shape(M, N, [..., P])
|
||||
The labelled image. This should have one dimension less than
|
||||
`image`. If `image` has dimensions `(M, N, 3)` `labels` should have
|
||||
dimensions `(M, N)`.
|
||||
connectivity : int, optional
|
||||
Pixels with a squared distance less than `connectivity` from each other
|
||||
are considered adjacent. It can range from 1 to `labels.ndim`. Its
|
||||
behavior is the same as `connectivity` parameter in
|
||||
``scipy.ndimage.generate_binary_structure``.
|
||||
mode : {'distance', 'similarity'}, optional
|
||||
The strategy to assign edge weights.
|
||||
|
||||
'distance' : The weight between two adjacent regions is the
|
||||
:math:`|c_1 - c_2|`, where :math:`c_1` and :math:`c_2` are the mean
|
||||
colors of the two regions. It represents the Euclidean distance in
|
||||
their average color.
|
||||
|
||||
'similarity' : The weight between two adjacent is
|
||||
:math:`e^{-d^2/sigma}` where :math:`d=|c_1 - c_2|`, where
|
||||
:math:`c_1` and :math:`c_2` are the mean colors of the two regions.
|
||||
It represents how similar two regions are.
|
||||
sigma : float, optional
|
||||
Used for computation when `mode` is "similarity". It governs how
|
||||
close to each other two colors should be, for their corresponding edge
|
||||
weight to be significant. A very large value of `sigma` could make
|
||||
any two colors behave as though they were similar.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : RAG
|
||||
The region adjacency graph.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data, segmentation
|
||||
>>> from skimage.future import graph
|
||||
>>> img = data.astronaut()
|
||||
>>> labels = segmentation.slic(img)
|
||||
>>> rag = graph.rag_mean_color(img, labels)
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Alain Tremeau and Philippe Colantoni
|
||||
"Regions Adjacency Graph Applied To Color Image Segmentation"
|
||||
:DOI:`10.1109/83.841950`
|
||||
"""
|
||||
graph = RAG(labels, connectivity=connectivity)
|
||||
|
||||
for n in graph:
|
||||
graph.nodes[n].update({'labels': [n],
|
||||
'pixel count': 0,
|
||||
'total color': np.array([0, 0, 0],
|
||||
dtype=np.double)})
|
||||
|
||||
for index in np.ndindex(labels.shape):
|
||||
current = labels[index]
|
||||
graph.nodes[current]['pixel count'] += 1
|
||||
graph.nodes[current]['total color'] += image[index]
|
||||
|
||||
for n in graph:
|
||||
graph.nodes[n]['mean color'] = (graph.nodes[n]['total color'] /
|
||||
graph.nodes[n]['pixel count'])
|
||||
|
||||
for x, y, d in graph.edges(data=True):
|
||||
diff = graph.nodes[x]['mean color'] - graph.nodes[y]['mean color']
|
||||
diff = np.linalg.norm(diff)
|
||||
if mode == 'similarity':
|
||||
d['weight'] = math.e ** (-(diff ** 2) / sigma)
|
||||
elif mode == 'distance':
|
||||
d['weight'] = diff
|
||||
else:
|
||||
raise ValueError("The mode '%s' is not recognised" % mode)
|
||||
|
||||
return graph
|
||||
|
||||
|
||||
def rag_boundary(labels, edge_map, connectivity=2):
|
||||
""" Comouter RAG based on region boundaries
|
||||
|
||||
Given an image's initial segmentation and its edge map this method
|
||||
constructs the corresponding Region Adjacency Graph (RAG). Each node in the
|
||||
RAG represents a set of pixels within the image with the same label in
|
||||
`labels`. The weight between two adjacent regions is the average value
|
||||
in `edge_map` along their boundary.
|
||||
|
||||
labels : ndarray
|
||||
The labelled image.
|
||||
edge_map : ndarray
|
||||
This should have the same shape as that of `labels`. For all pixels
|
||||
along the boundary between 2 adjacent regions, the average value of the
|
||||
corresponding pixels in `edge_map` is the edge weight between them.
|
||||
connectivity : int, optional
|
||||
Pixels with a squared distance less than `connectivity` from each other
|
||||
are considered adjacent. It can range from 1 to `labels.ndim`. Its
|
||||
behavior is the same as `connectivity` parameter in
|
||||
`scipy.ndimage.filters.generate_binary_structure`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data, segmentation, filters, color
|
||||
>>> from skimage.future import graph
|
||||
>>> img = data.chelsea()
|
||||
>>> labels = segmentation.slic(img)
|
||||
>>> edge_map = filters.sobel(color.rgb2gray(img))
|
||||
>>> rag = graph.rag_boundary(labels, edge_map)
|
||||
|
||||
"""
|
||||
|
||||
conn = ndi.generate_binary_structure(labels.ndim, connectivity)
|
||||
eroded = ndi.grey_erosion(labels, footprint=conn)
|
||||
dilated = ndi.grey_dilation(labels, footprint=conn)
|
||||
boundaries0 = (eroded != labels)
|
||||
boundaries1 = (dilated != labels)
|
||||
labels_small = np.concatenate((eroded[boundaries0], labels[boundaries1]))
|
||||
labels_large = np.concatenate((labels[boundaries0], dilated[boundaries1]))
|
||||
n = np.max(labels_large) + 1
|
||||
|
||||
# use a dummy broadcast array as data for RAG
|
||||
ones = as_strided(np.ones((1,), dtype=np.float), shape=labels_small.shape,
|
||||
strides=(0,))
|
||||
count_matrix = sparse.coo_matrix((ones, (labels_small, labels_large)),
|
||||
dtype=np.int_, shape=(n, n)).tocsr()
|
||||
data = np.concatenate((edge_map[boundaries0], edge_map[boundaries1]))
|
||||
|
||||
data_coo = sparse.coo_matrix((data, (labels_small, labels_large)))
|
||||
graph_matrix = data_coo.tocsr()
|
||||
graph_matrix.data /= count_matrix.data
|
||||
|
||||
rag = RAG()
|
||||
rag.add_weighted_edges_from(_edge_generator_from_csr(graph_matrix),
|
||||
weight='weight')
|
||||
rag.add_weighted_edges_from(_edge_generator_from_csr(count_matrix),
|
||||
weight='count')
|
||||
|
||||
for n in rag.nodes():
|
||||
rag.nodes[n].update({'labels': [n]})
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
def show_rag(labels, rag, image, border_color='black', edge_width=1.5,
|
||||
edge_cmap='magma', img_cmap='bone', in_place=True, ax=None):
|
||||
"""Show a Region Adjacency Graph on an image.
|
||||
|
||||
Given a labelled image and its corresponding RAG, show the nodes and edges
|
||||
of the RAG on the image with the specified colors. Edges are displayed between
|
||||
the centroid of the 2 adjacent regions in the image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
labels : ndarray, shape (M, N)
|
||||
The labelled image.
|
||||
rag : RAG
|
||||
The Region Adjacency Graph.
|
||||
image : ndarray, shape (M, N[, 3])
|
||||
Input image. If `colormap` is `None`, the image should be in RGB
|
||||
format.
|
||||
border_color : color spec, optional
|
||||
Color with which the borders between regions are drawn.
|
||||
edge_width : float, optional
|
||||
The thickness with which the RAG edges are drawn.
|
||||
edge_cmap : :py:class:`matplotlib.colors.Colormap`, optional
|
||||
Any matplotlib colormap with which the edges are drawn.
|
||||
img_cmap : :py:class:`matplotlib.colors.Colormap`, optional
|
||||
Any matplotlib colormap with which the image is draw. If set to `None`
|
||||
the image is drawn as it is.
|
||||
in_place : bool, optional
|
||||
If set, the RAG is modified in place. For each node `n` the function
|
||||
will set a new attribute ``rag.nodes[n]['centroid']``.
|
||||
ax : :py:class:`matplotlib.axes.Axes`, optional
|
||||
The axes to draw on. If not specified, new axes are created and drawn
|
||||
on.
|
||||
|
||||
Returns
|
||||
-------
|
||||
lc : :py:class:`matplotlib.collections.LineCollection`
|
||||
A colection of lines that represent the edges of the graph. It can be
|
||||
passed to the :meth:`matplotlib.figure.Figure.colorbar` function.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data, segmentation
|
||||
>>> from skimage.future import graph
|
||||
>>> import matplotlib.pyplot as plt
|
||||
>>>
|
||||
>>> img = data.coffee()
|
||||
>>> labels = segmentation.slic(img)
|
||||
>>> g = graph.rag_mean_color(img, labels)
|
||||
>>> lc = graph.show_rag(labels, g, img)
|
||||
>>> cbar = plt.colorbar(lc)
|
||||
"""
|
||||
from matplotlib import colors, cm
|
||||
from matplotlib import pyplot as plt
|
||||
from matplotlib.collections import LineCollection
|
||||
|
||||
if not in_place:
|
||||
rag = rag.copy()
|
||||
|
||||
if ax is None:
|
||||
fig, ax = plt.subplots()
|
||||
out = util.img_as_float(image, force_copy=True)
|
||||
|
||||
if img_cmap is None:
|
||||
if image.ndim < 3 or image.shape[2] not in [3, 4]:
|
||||
msg = 'If colormap is `None`, an RGB or RGBA image should be given'
|
||||
raise ValueError(msg)
|
||||
# Ignore the alpha channel
|
||||
out = image[:, :, :3]
|
||||
else:
|
||||
img_cmap = cm.get_cmap(img_cmap)
|
||||
out = color.rgb2gray(image)
|
||||
# Ignore the alpha channel
|
||||
out = img_cmap(out)[:, :, :3]
|
||||
|
||||
edge_cmap = cm.get_cmap(edge_cmap)
|
||||
|
||||
# Handling the case where one node has multiple labels
|
||||
# offset is 1 so that regionprops does not ignore 0
|
||||
offset = 1
|
||||
map_array = np.arange(labels.max() + 1)
|
||||
for n, d in rag.nodes(data=True):
|
||||
for label in d['labels']:
|
||||
map_array[label] = offset
|
||||
offset += 1
|
||||
|
||||
rag_labels = map_array[labels]
|
||||
regions = measure.regionprops(rag_labels)
|
||||
|
||||
for (n, data), region in zip(rag.nodes(data=True), regions):
|
||||
data['centroid'] = tuple(map(int, region['centroid']))
|
||||
|
||||
cc = colors.ColorConverter()
|
||||
if border_color is not None:
|
||||
border_color = cc.to_rgb(border_color)
|
||||
out = segmentation.mark_boundaries(out, rag_labels, color=border_color)
|
||||
|
||||
ax.imshow(out)
|
||||
|
||||
# Defining the end points of the edges
|
||||
# The tuple[::-1] syntax reverses a tuple as matplotlib uses (x,y)
|
||||
# convention while skimage uses (row, column)
|
||||
lines = [[rag.nodes[n1]['centroid'][::-1], rag.nodes[n2]['centroid'][::-1]]
|
||||
for (n1, n2) in rag.edges()]
|
||||
|
||||
lc = LineCollection(lines, linewidths=edge_width, cmap=edge_cmap)
|
||||
edge_weights = [d['weight'] for x, y, d in rag.edges(data=True)]
|
||||
lc.set_array(np.array(edge_weights))
|
||||
ax.add_collection(lc)
|
||||
|
||||
return lc
|
29
venv/Lib/site-packages/skimage/future/graph/setup.py
Normal file
29
venv/Lib/site-packages/skimage/future/graph/setup.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from skimage._build import cython
|
||||
import os.path
|
||||
|
||||
base_path = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
|
||||
def configuration(parent_package='', top_path=None):
|
||||
from numpy.distutils.misc_util import Configuration, get_numpy_include_dirs
|
||||
|
||||
config = Configuration('graph', parent_package, top_path)
|
||||
|
||||
# This function tries to create C files from the given .pyx files. If
|
||||
# it fails, try to build with pre-generated .c files.
|
||||
cython(['_ncut_cy.pyx'], working_path=base_path)
|
||||
config.add_extension('_ncut_cy', sources=['_ncut_cy.c'],
|
||||
include_dirs=[get_numpy_include_dirs()])
|
||||
return config
|
||||
|
||||
if __name__ == '__main__':
|
||||
from numpy.distutils.core import setup
|
||||
setup(maintainer='scikit-image Developers',
|
||||
maintainer_email='scikit-image@python.org',
|
||||
description='Graph-based Image-processing Algorithms',
|
||||
url='https://github.com/scikit-image/scikit-image',
|
||||
license='Modified BSD',
|
||||
**(configuration(top_path='').todict())
|
||||
)
|
227
venv/Lib/site-packages/skimage/future/manual_segmentation.py
Normal file
227
venv/Lib/site-packages/skimage/future/manual_segmentation.py
Normal file
|
@ -0,0 +1,227 @@
|
|||
from functools import reduce
|
||||
import numpy as np
|
||||
from ..draw import polygon
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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, io
|
||||
>>> camera = data.camera()
|
||||
>>> mask = future.manual_polygon_segmentation(camera) # doctest: +SKIP
|
||||
>>> io.imshow(mask) # doctest: +SKIP
|
||||
>>> io.show() # doctest: +SKIP
|
||||
"""
|
||||
import matplotlib
|
||||
from matplotlib.patches import Polygon
|
||||
from matplotlib.collections import PatchCollection
|
||||
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, u'\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 fig.canvas.manager.toolbar._active is not None:
|
||||
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]))
|
||||
|
||||
|
||||
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, io
|
||||
>>> camera = data.camera()
|
||||
>>> mask = future.manual_lasso_segmentation(camera) # doctest: +SKIP
|
||||
>>> io.imshow(mask) # doctest: +SKIP
|
||||
>>> io.show() # doctest: +SKIP
|
||||
"""
|
||||
import matplotlib
|
||||
from matplotlib.patches import Polygon
|
||||
from matplotlib.collections import PatchCollection
|
||||
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, u'\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()
|
||||
|
||||
lasso = 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]))
|
12
venv/Lib/site-packages/skimage/future/setup.py
Normal file
12
venv/Lib/site-packages/skimage/future/setup.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
def configuration(parent_package='skimage', top_path=None):
|
||||
from numpy.distutils.misc_util import Configuration
|
||||
config = Configuration('future', parent_package, top_path)
|
||||
config.add_subpackage('graph')
|
||||
return config
|
||||
|
||||
if __name__ == "__main__":
|
||||
from numpy.distutils.core import setup
|
||||
|
||||
config = configuration(top_path='').todict()
|
||||
setup(**config)
|
Loading…
Add table
Add a link
Reference in a new issue