diff --git a/doc/examples/plot_rag_meancolor.py b/doc/examples/plot_rag_meancolor.py index 1f19dd5e..129814e2 100644 --- a/doc/examples/plot_rag_meancolor.py +++ b/doc/examples/plot_rag_meancolor.py @@ -9,9 +9,7 @@ difference in mean color. We then join regions with similar mean color. """ -from skimage import graph -from skimage import segmentation -from skimage import data, io +from skimage import graph, data, io, segmentation, color from matplotlib import pyplot as plt from skimage import color diff --git a/skimage/graph/graph_cut.py b/skimage/graph/graph_cut.py index 87fa79a8..165091b1 100644 --- a/skimage/graph/graph_cut.py +++ b/skimage/graph/graph_cut.py @@ -5,13 +5,13 @@ import numpy as np def threshold_cut(labels, rag, thresh): """Combine regions seperated by weight less than threshold. - Given an image's labels and its RAG, outputs new labels by + Given an image's labels and its RAG, output new labels by combining regions whose nodes are seperated by a weight less than the given threshold. Parameters ---------- - labels : (width, height) or (width, height, 3) ndarray + labels : ndarray The array of labels. rag : RAG The region adjacency graph. @@ -21,12 +21,12 @@ def threshold_cut(labels, rag, thresh): Returns ------- - out : (width, height, 3) or (width, height, depth, 3) ndarray + out : ndarray The new labelled array. Examples -------- - >>> from skimage import data,graph,segmentation + >>> from skimage import data, graph, segmentation >>> img = data.lena() >>> labels = segmentation.slic(img) >>> rag = graph.rag_meancolor(img, labels) @@ -46,7 +46,7 @@ def threshold_cut(labels, rag, thresh): comps = nx.connected_components(rag) - map_array = np.arange(labels.max() + 1, dtype=np.int) + map_array = np.arange(labels.max() + 1, dtype=labels.dtype) for i, nodes in enumerate(comps): for node in nodes: for label in rag.node[node]['labels']: diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index e42a5707..1012ccb5 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -7,56 +7,50 @@ from scipy import ndimage as nd class RAG(nx.Graph): """ - The class for holding the Region Adjacency Graph (RAG). - - Each region is a contiguous set of pixels in an image, usually - sharing some common property. Adjacent regions have an edge - between their corresponding nodes. + The Region Adjacency Graph (RAG) of an image. """ - def merge_nodes(self, i, j, function=None, extra_arguments=[], + def merge_nodes(self, src, dst, weight_func=None, extra_arguments=[], extra_keywords={}): - """Merge node `i` into `j`. + """Merge two nodes. - The new combined node is adjacent to all the neighbors of `i` - and `j`. In case of conflicting edges the given function is + The new combined node is adjacent to all the neighbors of `src` + and `dst`. In case of conflicting edges the given function is called. Parameters ---------- i, j : int Nodes to be merged. The resulting node will have ID `j`. - function : callable, optional - Function to decide which edge weight to keep when a node is - adjacent to both `i` and `j`. The arguments passed to the - function are, the tuples represnting both the conflicting edges - and the graph.The default behaviour is that the edge with higher - weight is kept. + weight_func : callable, optional + Function to decide edge weight between existing nodes and the new + node.The arguments passed to the function are, the graph, `src`, + `dst` and the existing node whose edge weight need to be updated. extra_arguments : sequence, optional The sequence of extra positional arguments passed to - `function` + `weight_func` extra_keywords : - The dict of keyword arguments passed to the `function`. + The dict of keyword arguments passed to the `weight_func`. """ - for x in self.neighbors(i): - if x == j: + for neighbor in self.neighbors(src): + if neighbor == dst: continue - w1 = self.get_edge_data(x, i)['weight'] - w2 = -1 - if self.has_edge(x, j): - w2 = self.get_edge_data(x, j)['weight'] - - w = w1 - if w2 > 0: - if not function: - w = max(w1, w2) + w1 = self.get_edge_data(neighbor, src)['weight'] + w2 = None + if self.has_edge(neighbor, dst): + w2 = self.get_edge_data(neighbor, dst)['weight'] + if not weight_func: + if w2 is None: + w = w1 else: - w = function((i, x), (j, x), self, - *extra_arguments, **extra_keywords) - self.add_edge(x, j, weight=w) + w = min(w1, w2) + else: + w = weight_func(self, src, dst, neighbor, + *extra_arguments, **extra_keywords) + self.add_edge(neighbor, dst, weight=w) - self.node[j]['labels'] += self.node[i]['labels'] - self.remove_node(i) + self.node[dst]['labels'] += self.node[src]['labels'] + self.remove_node(src) def _add_edge_filter(values, g): @@ -86,24 +80,27 @@ def _add_edge_filter(values, g): def rag_meancolor(image, labels, connectivity=2): - """Compute the Region Adjacency Graph of a color image using - difference in mean color of regions as edge weights. + """Compute the Region Adjacency Graph using mean colors. Given an image and its segmentation, this method constructs the corresponsing Region Adjacency Graph (RAG). Each node in the RAG represents a contiguous pixels with in `img` the same label in - `arr`. + `arr`. The weight between two adjacent regions is the difference + int their mean color. Parameters ---------- image : ndarray Input image. labels : ndarray - The array with labels. This should have one dimention lesser than - `image` + The array with labels. This should have one dimention less than + `image`. If `image` has dimensions `(M,N,3)` `labels` should have + dimensions `(M, N)`. connectivity : float, optional - Pixels with a squared distance less than `connectivity`from each other - are considered adjacent. + Pixels with a squared distance less than `connectivity` from each other + are considered adjacent. It can range from 1 to `labels.ndim`. It's + behaviour is the same as `connectivity` parameter in + `scipy.ndimage.filters.generate_binary_structure`. Returns ------- @@ -126,28 +123,28 @@ def rag_meancolor(image, labels, connectivity=2): """ g = RAG() + # The footprint is constructed in such a way that the first + # element in the array being passed to _add_edge_filter is + # the central value. fp = nd.generate_binary_structure(labels.ndim, connectivity) for d in range(fp.ndim): fp = fp.swapaxes(0, d) fp[0, ...] = 0 fp = fp.swapaxes(0, d) - # The footprint is constructed in such a way that the first - # element in the array being passed to _add_edge_filter is - # the central value. - - for i in range(labels.max() + 1): - g.add_node( - i, {'labels': [i], 'pixel count': 0, 'total color': - np.array([0, 0, 0], dtype=np.double)}) - filters.generic_filter( labels, function=_add_edge_filter, footprint=fp, mode='nearest', + output=np.zeros(labels.shape, dtype=np.uint8), extra_arguments=(g,)) + for n in g: + g.node[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] g.node[current]['pixel count'] += 1 diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index bdaf8277..5d382a43 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -3,10 +3,18 @@ from skimage import graph import random -def _min_edge(e1, e2, g): - w1 = g.edge[e1[0]][e1[1]]['weight'] - w2 = g.edge[e2[0]][e2[1]]['weight'] - return min(w1, w2) +def _max_edge(g, src, dst, neighbor): + try: + w1 = g.edge[src][neighbor]['weight'] + except KeyError: + w1 = None + + try: + w2 = g.edge[dst][neighbor]['weight'] + except KeyError: + w2 = None + + return max(w1, w2) def test_rag_merge(): @@ -27,7 +35,7 @@ def test_rag_merge(): y = random.choice(g.nodes()) while x == y: y = random.choice(g.nodes()) - g.merge_nodes(x, y, _min_edge) + g.merge_nodes(x, y, _max_edge) idx = g.nodes()[0] assert sorted(g.node[idx]['labels']) == list(range(10))