From 2a87c07800d82593f19df6113f5d3865fe09e0e3 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Tue, 26 Apr 2016 14:23:32 -0400 Subject: [PATCH 1/4] midway thru changes --- .../segmentation/plot_boundary_merge.py | 36 +++++++++++++++++++ skimage/future/graph/rag.py | 18 +++++----- skimage/future/graph/tests/test_rag.py | 4 +-- 3 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 doc/examples/segmentation/plot_boundary_merge.py diff --git a/doc/examples/segmentation/plot_boundary_merge.py b/doc/examples/segmentation/plot_boundary_merge.py new file mode 100644 index 00000000..5c649195 --- /dev/null +++ b/doc/examples/segmentation/plot_boundary_merge.py @@ -0,0 +1,36 @@ +from skimage import data, io, segmentation, color +from skimage.future import graph +import numpy as np + + +def weight_boundary(graph, src, dst, n): + if graph.has_edge(src, n) and graph.has_edge(dst, n): + count_src = graph[src][n]['count'] + count_dst = graph[dst][n]['count'] + + weight_src = graph[src][n]['weight'] + weight_dst = graph[dst][n]['weight'] + + count = count_src + count_dst + return { + 'count': count, + 'weight': (count_src*weight_src + count_dst*weight_dst)/count + } + + elif graph.has_edge(src, n): + return graph[src][n] + elif graph.has_edge(dst, n): + return graph[dst][n] + + +def merge_boundary(graph, src, dst): + pass + +img = data.coffee() +labels = segmentation.slic(img, compactness=30, n_segments=400) +g = graph.rag_mean_color(img, labels) + +labels2 = graph.merge_hierarchical(labels, g, thresh=40, rag_copy=False, + in_place_merge=True, + merge_func=merge_boundary, + weight_func=weight_boundary) diff --git a/skimage/future/graph/rag.py b/skimage/future/graph/rag.py index 480ef4eb..d0d26d84 100644 --- a/skimage/future/graph/rag.py +++ b/skimage/future/graph/rag.py @@ -74,7 +74,7 @@ def min_weight(graph, src, dst, n): default = {'weight': np.inf} w1 = graph[n].get(src, default)['weight'] w2 = graph[n].get(dst, default)['weight'] - return min(w1, w2) + return {'weight': min(w1, w2)} def _add_edge_filter(values, graph): @@ -171,12 +171,12 @@ class RAG(nx.Graph): src, dst : int Nodes to be merged. weight_func : callable, optional - Function to decide edge weight 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, + 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`. + 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. @@ -207,9 +207,9 @@ class RAG(nx.Graph): self.add_node(new) for neighbor in neighbors: - w = weight_func(self, src, new, neighbor, *extra_arguments, - **extra_keywords) - self.add_edge(neighbor, new, weight=w) + data = weight_func(self, src, new, neighbor, *extra_arguments, + **extra_keywords) + self.add_edge(neighbor, new, attr_dict=data) self.node[new]['labels'] = (self.node[src]['labels'] + self.node[dst]['labels']) diff --git a/skimage/future/graph/tests/test_rag.py b/skimage/future/graph/tests/test_rag.py index 4b2e4cff..dadbe5ce 100644 --- a/skimage/future/graph/tests/test_rag.py +++ b/skimage/future/graph/tests/test_rag.py @@ -10,7 +10,7 @@ def max_edge(g, src, dst, n): default = {'weight': -np.inf} w1 = g[n].get(src, default)['weight'] w2 = g[n].get(dst, default)['weight'] - return max(w1, w2) + return {'weight': max(w1, w2)} @skipif(not is_installed('networkx')) @@ -113,7 +113,7 @@ def test_rag_error(): def _weight_mean_color(graph, src, dst, n): diff = graph.node[dst]['mean color'] - graph.node[n]['mean color'] diff = np.linalg.norm(diff) - return diff + return {'weight': diff} def _pre_merge_mean_color(graph, src, dst): From b3f6b545cb649abc38ae1743e616d98d3732e627 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Wed, 27 Apr 2016 01:03:09 -0400 Subject: [PATCH 2/4] Added example for region boundary merging --- .../segmentation/plot_boundary_merge.py | 78 ++++++++++++++----- doc/examples/segmentation/plot_rag.py | 19 ++--- doc/examples/segmentation/plot_rag_merge.py | 9 ++- skimage/future/graph/rag.py | 12 +-- 4 files changed, 82 insertions(+), 36 deletions(-) diff --git a/doc/examples/segmentation/plot_boundary_merge.py b/doc/examples/segmentation/plot_boundary_merge.py index 5c649195..ed38f679 100644 --- a/doc/examples/segmentation/plot_boundary_merge.py +++ b/doc/examples/segmentation/plot_boundary_merge.py @@ -1,36 +1,78 @@ -from skimage import data, io, segmentation, color +""" +============================================ +Hierarchical Merging of Region Boundary RAGs +============================================ + +TODO: Description +""" + +from skimage import data, segmentation, filters, color from skimage.future import graph -import numpy as np +from matplotlib import pyplot as plt def weight_boundary(graph, src, dst, n): - if graph.has_edge(src, n) and graph.has_edge(dst, n): - count_src = graph[src][n]['count'] - count_dst = graph[dst][n]['count'] + """ + Callback to handle merging of nodes of a region boundary RAG. - weight_src = graph[src][n]['weight'] - weight_dst = graph[dst][n]['weight'] + This function computes the `"weight"` and the count `"count"` + attributes of the edge between `n` and the node formed after + merging `src` and `dst`. - count = count_src + count_dst - return { - 'count': count, - 'weight': (count_src*weight_src + count_dst*weight_dst)/count - } - elif graph.has_edge(src, n): - return graph[src][n] - elif graph.has_edge(dst, n): - return graph[dst][n] + Parameters + ---------- + graph : RAG + The graph under consideration. + src, dst : int + The vertices in `graph` to be merged. + n : int + A neighbor of `src` or `dst` or both. + + Returns + ------- + data : dict + A dictionary with the `"weight"` and `"count"` attributes to be + assigned for the merged node. + + """ + default = {'weight': 0.0, 'count': 0} + + count_src = graph[src].get(n, default)['count'] + count_dst = graph[dst].get(n, default)['count'] + + weight_src = graph[src].get(n, default)['weight'] + weight_dst = graph[dst].get(n, default)['weight'] + + count = count_src + count_dst + return { + 'count': count, + 'weight': (count_src*weight_src + count_dst*weight_dst)/count + } def merge_boundary(graph, src, dst): pass img = data.coffee() +edges = filters.sobel(color.rgb2gray(img)) labels = segmentation.slic(img, compactness=30, n_segments=400) -g = graph.rag_mean_color(img, labels) +g = graph.rag_boundary(labels, edges) -labels2 = graph.merge_hierarchical(labels, g, thresh=40, rag_copy=False, +graph.show_rag(labels, g, img) +plt.title('Initial RAG') + +labels2 = graph.merge_hierarchical(labels, g, thresh=0.08, rag_copy=False, in_place_merge=True, merge_func=merge_boundary, weight_func=weight_boundary) + +graph.show_rag(labels, g, img) +plt.title('RAG after hierarchical merging') + +plt.figure() +out = color.label2rgb(labels2, img, kind='avg') +plt.imshow(out) +plt.title('Final segmentation') + +plt.show() diff --git a/doc/examples/segmentation/plot_rag.py b/doc/examples/segmentation/plot_rag.py index d4f2711c..45d2afc5 100644 --- a/doc/examples/segmentation/plot_rag.py +++ b/doc/examples/segmentation/plot_rag.py @@ -23,29 +23,30 @@ import numpy as np def max_edge(g, src, dst, n): """Callback to handle merging nodes by choosing maximum weight. - Returns either the weight between (`src`, `n`) or (`dst`, `n`) - in `g` or the maximum of the two when both exist. + Returns a dictionary with `"weight"` set as either the weight between + (`src`, `n`) or (`dst`, `n`) in `graph` or the maximum of the two when + both exist. Parameters ---------- - g : RAG + graph : RAG The graph under consideration. src, dst : int - The vertices in `g` to be merged. + The verices in `graph` to be merged. n : int A neighbor of `src` or `dst` or both. Returns ------- - weight : float - The weight between (`src`, `n`) or (`dst`, `n`) in `g` or the - maximum of the two when both exist. - + data : dict + A dict with the `"weight"` attribute set the weight between + (`src`, `n`) or (`dst`, `n`) in `graph` or the maximum of the two when + both exist. """ w1 = g[n].get(src, {'weight': -np.inf})['weight'] w2 = g[n].get(dst, {'weight': -np.inf})['weight'] - return max(w1, w2) + return {'weight': max(w1, w2)} def display(g, title): diff --git a/doc/examples/segmentation/plot_rag_merge.py b/doc/examples/segmentation/plot_rag_merge.py index 37712d5b..1ff68079 100644 --- a/doc/examples/segmentation/plot_rag_merge.py +++ b/doc/examples/segmentation/plot_rag_merge.py @@ -31,13 +31,14 @@ def _weight_mean_color(graph, src, dst, n): Returns ------- - weight : float - The absolute difference of the mean color between node `dst` and `n`. + data : dict + A dictionary with the `"weight"` attribute set as the absolute + difference of the mean color between node `dst` and `n`. """ diff = graph.node[dst]['mean color'] - graph.node[n]['mean color'] diff = np.linalg.norm(diff) - return diff + return {'weight': diff} def merge_mean_color(graph, src, dst): @@ -62,7 +63,7 @@ img = data.coffee() labels = segmentation.slic(img, compactness=30, n_segments=400) g = graph.rag_mean_color(img, labels) -labels2 = graph.merge_hierarchical(labels, g, thresh=40, rag_copy=False, +labels2 = graph.merge_hierarchical(labels, g, thresh=35, rag_copy=False, in_place_merge=True, merge_func=merge_mean_color, weight_func=_weight_mean_color) diff --git a/skimage/future/graph/rag.py b/skimage/future/graph/rag.py index d0d26d84..b6ded5fe 100644 --- a/skimage/future/graph/rag.py +++ b/skimage/future/graph/rag.py @@ -50,8 +50,9 @@ def _edge_generator_from_csr(csr_matrix): def min_weight(graph, src, dst, n): """Callback to handle merging nodes by choosing minimum weight. - Returns either the weight between (`src`, `n`) or (`dst`, `n`) - in `graph` or the minimum of the two when both exist. + 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 ---------- @@ -64,9 +65,10 @@ def min_weight(graph, src, dst, n): Returns ------- - weight : float - The weight between (`src`, `n`) or (`dst`, `n`) in `graph` or the - minimum of the two when both exist. + 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. """ From 35b193aa09db552b8323cf1ea904df4ea3288c4d Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Wed, 27 Apr 2016 01:11:09 -0400 Subject: [PATCH 3/4] clarified merge function in docstring --- doc/examples/segmentation/plot_boundary_merge.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/examples/segmentation/plot_boundary_merge.py b/doc/examples/segmentation/plot_boundary_merge.py index ed38f679..d4f3c26d 100644 --- a/doc/examples/segmentation/plot_boundary_merge.py +++ b/doc/examples/segmentation/plot_boundary_merge.py @@ -52,6 +52,10 @@ def weight_boundary(graph, src, dst, n): def merge_boundary(graph, src, dst): + """Call back called before merging 2 nodes. + + In this case we don't need to do any computation here. + """ pass img = data.coffee() From a89f1735d4c1428d1eb8af2ab271334fa98c2896 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Wed, 11 May 2016 17:33:14 -0400 Subject: [PATCH 4/4] docstring corrections and pep8 --- doc/examples/segmentation/plot_boundary_merge.py | 6 +++--- doc/examples/segmentation/plot_rag.py | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/examples/segmentation/plot_boundary_merge.py b/doc/examples/segmentation/plot_boundary_merge.py index d4f3c26d..e7cd89ce 100644 --- a/doc/examples/segmentation/plot_boundary_merge.py +++ b/doc/examples/segmentation/plot_boundary_merge.py @@ -13,7 +13,7 @@ from matplotlib import pyplot as plt def weight_boundary(graph, src, dst, n): """ - Callback to handle merging of nodes of a region boundary RAG. + Handle merging of nodes of a region boundary region adjacency graph. This function computes the `"weight"` and the count `"count"` attributes of the edge between `n` and the node formed after @@ -32,7 +32,7 @@ def weight_boundary(graph, src, dst, n): Returns ------- data : dict - A dictionary with the `"weight"` and `"count"` attributes to be + A dictionary with the "weight" and "count" attributes to be assigned for the merged node. """ @@ -47,7 +47,7 @@ def weight_boundary(graph, src, dst, n): count = count_src + count_dst return { 'count': count, - 'weight': (count_src*weight_src + count_dst*weight_dst)/count + 'weight': (count_src * weight_src + count_dst * weight_dst)/count } diff --git a/doc/examples/segmentation/plot_rag.py b/doc/examples/segmentation/plot_rag.py index 45d2afc5..ba6e2d3b 100644 --- a/doc/examples/segmentation/plot_rag.py +++ b/doc/examples/segmentation/plot_rag.py @@ -24,23 +24,23 @@ def max_edge(g, src, dst, n): """Callback to handle merging nodes by choosing maximum weight. Returns a dictionary with `"weight"` set as either the weight between - (`src`, `n`) or (`dst`, `n`) in `graph` or the maximum of the two when + (`src`, `n`) or (`dst`, `n`) in `g` or the maximum of the two when both exist. Parameters ---------- - graph : RAG + g : RAG The graph under consideration. src, dst : int - The verices in `graph` to be merged. + The vertices in `g` 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 maximum of the two when + A dict with the "weight" attribute set the weight between + (`src`, `n`) or (`dst`, `n`) in `g` or the maximum of the two when both exist. """