From b35fe6948f279dd65202e51bc01e5b9983b3ec62 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Fri, 15 Aug 2014 04:10:51 +0530 Subject: [PATCH 01/31] working code --- doc/examples/plot_rag_merge.py | 13 ++++++++++++ skimage/graph/__init__.py | 5 +++++ skimage/graph/graph_merge.py | 37 ++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 doc/examples/plot_rag_merge.py create mode 100644 skimage/graph/graph_merge.py diff --git a/doc/examples/plot_rag_merge.py b/doc/examples/plot_rag_merge.py new file mode 100644 index 00000000..c310ed5a --- /dev/null +++ b/doc/examples/plot_rag_merge.py @@ -0,0 +1,13 @@ +from skimage import graph, data, io, segmentation, color + + +img = data.coffee() +labels = segmentation.slic(img, compactness=30, n_segments=400) +g = graph.rag_mean_color(img, labels) +labels2 = graph.merge_hierarchical(g, labels, 40) +g2 = graph.rag_mean_color(img, labels2) + +out = color.label2rgb(labels2, img, kind='avg') +out = segmentation.mark_boundaries(out, labels2, (0, 0, 0)) +io.imshow(out) +io.show() diff --git a/skimage/graph/__init__.py b/skimage/graph/__init__.py index 6da4fcc8..50f69154 100644 --- a/skimage/graph/__init__.py +++ b/skimage/graph/__init__.py @@ -1,7 +1,11 @@ from .spath import shortest_path from .mcp import MCP, MCP_Geometric, MCP_Connect, MCP_Flexible, route_through_array from .graph_cut import cut_threshold, cut_normalized +<<<<<<< HEAD from .rag import rag_mean_color, RAG, draw_rag +======= +from .graph_merge import merge_hierarchical +>>>>>>> working code ncut = cut_normalized @@ -16,4 +20,5 @@ __all__ = ['shortest_path', 'cut_normalized', 'ncut', 'draw_rag', + 'merge_hierarchical', 'RAG'] diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py new file mode 100644 index 00000000..379f11fc --- /dev/null +++ b/skimage/graph/graph_merge.py @@ -0,0 +1,37 @@ +import numpy as np + + +def _hmerge(rag, x, y, n): + diff = rag.node[y]['mean color'] - rag.node[n]['mean color'] + diff = np.linalg.norm(diff) + return diff + + +def merge_hierarchical(rag, labels, thresh): + + min_wt = 0 + while min_wt < thresh: + + valid_edges = ((x, y, d) + for x, y, d in rag.edges(data=True) if x != y) + x, y, d = min(valid_edges, key=lambda x: x[2]['weight']) + min_wt = d['weight'] + + if min_wt < thresh: + total_color = (rag.node[y]['total color'] + + rag.node[x]['total color']) + n_pixels = rag.node[x]['pixel count'] + rag.node[y]['pixel count'] + rag.node[y]['total color'] = total_color + rag.node[y]['pixel count'] = n_pixels + rag.node[y]['mean color'] = total_color / n_pixels + + rag.merge_nodes(x, y, _hmerge) + + count = 0 + arr = np.arange(labels.max() + 1) + for n, d in rag.nodes_iter(data=True): + for l in d['labels']: + arr[l] = count + count += 1 + + return arr[labels] From 40698c835547858f702fd2db9e4392af87de1d93 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Fri, 15 Aug 2014 04:24:16 +0530 Subject: [PATCH 02/31] docstrings --- skimage/graph/graph_merge.py | 50 +++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 379f11fc..69aa94f1 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -2,18 +2,62 @@ import numpy as np def _hmerge(rag, x, y, n): + """Callback to handle merging nodes by recomputing mean color. + + The method expects that the mean color of `y` is already computed. + + 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 + ------- + weight : float + The absolute difference of the mean color between node `y` and `n`. + """ diff = rag.node[y]['mean color'] - rag.node[n]['mean color'] diff = np.linalg.norm(diff) return diff -def merge_hierarchical(rag, labels, thresh): +def merge_hierarchical(labels, rag, thresh): + """Perform hierarchical merging of a RAG. + Given an image's labels and its RAG, the method merges the similar nodes + until the weight between every two nodes is more than `thresh`. + + Parameters + ---------- + labels : ndarray + The array of labels. + rag : RAG + The Region Adjacency Graph. + thresh : float + The threshold. Nodes are merged until the minimum edge weight in the + graph exceeds `thresh`. + + Returns + ------- + out : ndarray + The new labelled array. + + Examples + -------- + >>> from skimage import data, graph, segmentation + >>> img = data.coffee() + >>> labels = segmentation.slic(img) + >>> rag = graph.rag_mean_color(img, labels) + >>> new_labels = graph.merge_hierarchical(labels, rag, 40) + """ min_wt = 0 while min_wt < thresh: - valid_edges = ((x, y, d) - for x, y, d in rag.edges(data=True) if x != y) + valid_edges = ((x, y, d) for x, y, d in rag.edges(data=True) if x != y) x, y, d = min(valid_edges, key=lambda x: x[2]['weight']) min_wt = d['weight'] From 62d50d972eb9fb9cb5fada3298f5adcbd3627bd7 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Fri, 15 Aug 2014 04:28:51 +0530 Subject: [PATCH 03/31] unit tests --- skimage/graph/tests/test_rag.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index 35eb5c38..0d5fa2ab 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -100,7 +100,6 @@ def test_cut_normalized(): assert new_labels.max() == 1 -@skipif(not is_installed('networkx')) def test_rag_error(): img = np.zeros((10, 10, 3), dtype='uint8') labels = np.zeros((10, 10), dtype='uint8') @@ -108,3 +107,23 @@ def test_rag_error(): labels[5:, :] = 1 testing.assert_raises(ValueError, graph.rag_mean_color, img, labels, 2, 'non existant mode') + +@skipif(not is_installed('networkx')) +def test_merge_hierarchical(): + img = np.zeros((100, 100, 3), dtype='uint8') + img[:50, :50] = 255, 255, 255 + img[:50, 50:] = 254, 254, 254 + img[50:, :50] = 2, 2, 2 + img[50:, 50:] = 1, 1, 1 + + labels = np.zeros((100, 100), dtype='uint8') + labels[:50, :50] = 0 + labels[:50, 50:] = 1 + labels[50:, :50] = 2 + labels[50:, 50:] = 3 + + rag = graph.rag_mean_color(img, labels) + new_labels = graph.merge_hierarchical(labels, rag, 10) + + # Two labels + assert new_labels.max() == 1 From f33c2cad1317631f8902bd487bc80edc7d6515ed Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Fri, 15 Aug 2014 04:40:59 +0530 Subject: [PATCH 04/31] Description of example --- doc/examples/plot_rag_merge.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/examples/plot_rag_merge.py b/doc/examples/plot_rag_merge.py index c310ed5a..22cae6da 100644 --- a/doc/examples/plot_rag_merge.py +++ b/doc/examples/plot_rag_merge.py @@ -1,3 +1,15 @@ +""" +=========== +RAG Merging +=========== + +This example constructs a Region Adjacency Graph (RAG) and progressively merges +regions which are similar in color. Merging two adjacent regions produces +a new regions with all the pixels from the merged regions. Regions are merged +till no two adjacent regions are similar enough. + +""" + from skimage import graph, data, io, segmentation, color From 9a192bf1cb899632253619385ad9814dbd61f63a Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Fri, 15 Aug 2014 13:57:43 +0530 Subject: [PATCH 05/31] fix example --- doc/examples/plot_rag_merge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/examples/plot_rag_merge.py b/doc/examples/plot_rag_merge.py index 22cae6da..ac7eab7f 100644 --- a/doc/examples/plot_rag_merge.py +++ b/doc/examples/plot_rag_merge.py @@ -16,7 +16,7 @@ from skimage import graph, data, io, segmentation, color img = data.coffee() labels = segmentation.slic(img, compactness=30, n_segments=400) g = graph.rag_mean_color(img, labels) -labels2 = graph.merge_hierarchical(g, labels, 40) +labels2 = graph.merge_hierarchical(labels, g, 40) g2 = graph.rag_mean_color(img, labels2) out = color.label2rgb(labels2, img, kind='avg') From a080aa724a35ac6ce14db85772c9ba0ea345183e Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Fri, 15 Aug 2014 15:00:58 +0530 Subject: [PATCH 06/31] rebase and docstring changes --- doc/examples/plot_rag_merge.py | 4 ++-- skimage/graph/graph_merge.py | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/doc/examples/plot_rag_merge.py b/doc/examples/plot_rag_merge.py index ac7eab7f..00236d95 100644 --- a/doc/examples/plot_rag_merge.py +++ b/doc/examples/plot_rag_merge.py @@ -4,9 +4,9 @@ RAG Merging =========== This example constructs a Region Adjacency Graph (RAG) and progressively merges -regions which are similar in color. Merging two adjacent regions produces +regions that are similar in color. Merging two adjacent regions produces a new regions with all the pixels from the merged regions. Regions are merged -till no two adjacent regions are similar enough. +until no highly similar regions remain. """ diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 69aa94f1..59a623a5 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -38,8 +38,9 @@ def merge_hierarchical(labels, rag, thresh): rag : RAG The Region Adjacency Graph. thresh : float - The threshold. Nodes are merged until the minimum edge weight in the - graph exceeds `thresh`. + The threshold. Regions connected by an edges with smaller wegiht than + `thresh` are merged. A high value of `thresh` would mean that a lot of + regions are merged, and the output will contain fewer regions. Returns ------- @@ -71,11 +72,9 @@ def merge_hierarchical(labels, rag, thresh): rag.merge_nodes(x, y, _hmerge) - count = 0 arr = np.arange(labels.max() + 1) - for n, d in rag.nodes_iter(data=True): - for l in d['labels']: - arr[l] = count - count += 1 + for ix, (n, d) in enumerate(rag.nodes_iter(data=True)): + for label in d['labels']: + arr[label] = ix return arr[labels] From fe5761461d0684e5ad2c7b86397417580fe4836b Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Fri, 15 Aug 2014 15:03:49 +0530 Subject: [PATCH 07/31] added nx check in test --- skimage/graph/tests/test_rag.py | 1 + 1 file changed, 1 insertion(+) diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index 0d5fa2ab..4e287ceb 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -100,6 +100,7 @@ def test_cut_normalized(): assert new_labels.max() == 1 +@skipif(not is_installed('networkx')) def test_rag_error(): img = np.zeros((10, 10, 3), dtype='uint8') labels = np.zeros((10, 10), dtype='uint8') From bb11caa1e1ca2341d47ccce4639a44f225c6e911 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sat, 16 Aug 2014 14:52:51 +0530 Subject: [PATCH 08/31] edge heap --- skimage/graph/graph_merge.py | 44 ++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 59a623a5..d7499066 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -1,4 +1,5 @@ import numpy as np +import heapq def _hmerge(rag, x, y, n): @@ -21,7 +22,13 @@ def _hmerge(rag, x, y, n): The absolute difference of the mean color between node `y` and `n`. """ diff = rag.node[y]['mean color'] - rag.node[n]['mean color'] + diff = np.linalg.norm(diff) + + #rag.add_edge(y,n) + #rag[y][n]['valid'] = True + #heapq.heappush(heap,(diff, y , n, data)) + return diff @@ -56,21 +63,48 @@ def merge_hierarchical(labels, rag, thresh): >>> new_labels = graph.merge_hierarchical(labels, rag, 40) """ min_wt = 0 + + edge_heap = [] + for x,y,data in rag.edges_iter(data=True): + if x != y: + data['valid'] = True + wt = data['weight'] + heapq.heappush(edge_heap, (wt, x, y, data)) + + while min_wt < thresh: - valid_edges = ((x, y, d) for x, y, d in rag.edges(data=True) if x != y) - x, y, d = min(valid_edges, key=lambda x: x[2]['weight']) - min_wt = d['weight'] - - if min_wt < thresh: + #valid_edges = ((x, y, d) for x, y, d in rag.edges(data=True) if x != y) + #x, y, d = min(valid_edges, key=lambda x: x[2]['weight']) + #min_wt = d['weight'] + min_wt,x,y,data = heapq.heappop(edge_heap) + print min_wt + if min_wt < thresh and data['valid']: total_color = (rag.node[y]['total color'] + rag.node[x]['total color']) n_pixels = rag.node[x]['pixel count'] + rag.node[y]['pixel count'] rag.node[y]['total color'] = total_color rag.node[y]['pixel count'] = n_pixels rag.node[y]['mean color'] = total_color / n_pixels + + + #print x,y + for n in rag.neighbors(x): + rag[x][n]['valid'] = False + + for n in rag.neighbors(y): + rag[y][n]['valid'] = False rag.merge_nodes(x, y, _hmerge) + + + for n in rag.neighbors(y): + if n!= y: + rag[y][n]['valid'] = True + wt = rag[y][n]['weight'] + heapq.heappush(edge_heap, (wt, y, n, rag[y][n])) + + arr = np.arange(labels.max() + 1) for ix, (n, d) in enumerate(rag.nodes_iter(data=True)): From d0e25528635636f27a3554fd97db95abdf7b2598 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sat, 16 Aug 2014 16:03:50 +0530 Subject: [PATCH 09/31] complete heap implementation --- skimage/graph/graph_merge.py | 80 ++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index d7499066..68be4392 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -2,10 +2,10 @@ import numpy as np import heapq -def _hmerge(rag, x, y, n): +def _hmerge_mean_color(graph, src, dst, n): """Callback to handle merging nodes by recomputing mean color. - The method expects that the mean color of `y` is already computed. + The method expects that the mean color of `dst` is already computed. Parameters ---------- @@ -19,20 +19,14 @@ def _hmerge(rag, x, y, n): Returns ------- weight : float - The absolute difference of the mean color between node `y` and `n`. + The absolute difference of the mean color between node `dst` and `n`. """ - diff = rag.node[y]['mean color'] - rag.node[n]['mean color'] - + diff = graph.node[dst]['mean color'] - graph.node[n]['mean color'] diff = np.linalg.norm(diff) - - #rag.add_edge(y,n) - #rag[y][n]['valid'] = True - #heapq.heappush(heap,(diff, y , n, data)) - return diff -def merge_hierarchical(labels, rag, thresh): +def merge_hierarchical(labels, rag, thresh, in_place=True): """Perform hierarchical merging of a RAG. Given an image's labels and its RAG, the method merges the similar nodes @@ -48,6 +42,8 @@ def merge_hierarchical(labels, rag, thresh): The threshold. Regions connected by an edges with smaller wegiht than `thresh` are merged. A high value of `thresh` would mean that a lot of regions are merged, and the output will contain fewer regions. + in_place : bool, optional + If set, the RAG is modified in place. Returns ------- @@ -63,22 +59,23 @@ def merge_hierarchical(labels, rag, thresh): >>> new_labels = graph.merge_hierarchical(labels, rag, 40) """ min_wt = 0 - + + if not in_place: + rag = rag.copy() + edge_heap = [] - for x,y,data in rag.edges_iter(data=True): + for x, y, data in rag.edges_iter(data=True): if x != y: + # Validate all edges and push them in heap data['valid'] = True wt = data['weight'] heapq.heappush(edge_heap, (wt, x, y, data)) - - + while min_wt < thresh: - #valid_edges = ((x, y, d) for x, y, d in rag.edges(data=True) if x != y) - #x, y, d = min(valid_edges, key=lambda x: x[2]['weight']) - #min_wt = d['weight'] - min_wt,x,y,data = heapq.heappop(edge_heap) - print min_wt + min_wt, x, y, data = heapq.heappop(edge_heap) + + # Ensure popped edge is valid, if not, the edge is discarded if min_wt < thresh and data['valid']: total_color = (rag.node[y]['total color'] + rag.node[x]['total color']) @@ -86,25 +83,38 @@ def merge_hierarchical(labels, rag, thresh): rag.node[y]['total color'] = total_color rag.node[y]['pixel count'] = n_pixels rag.node[y]['mean color'] = total_color / n_pixels - - - #print x,y - for n in rag.neighbors(x): - rag[x][n]['valid'] = False - - for n in rag.neighbors(y): - rag[y][n]['valid'] = False - rag.merge_nodes(x, y, _hmerge) - - + # This will invalidate all the below edges in the heap + for n in rag.neighbors(x): + rag[x][n]['valid'] = False + for n in rag.neighbors(y): - if n!= y: - rag[y][n]['valid'] = True + rag[y][n]['valid'] = False + + rag.merge_nodes(x, y, _hmerge_mean_color) + for n in rag.neighbors(y): + if n != y: + # 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 + + # invalidates the edge in the heap, if it all it exists + data = rag[y][n] + data['valid'] = False + + # allocate a new dictionary for the edge + data_copy = data.copy() + rag[y][n] = data_copy + rag[n][y] = data_copy + + # validate this edge + rag.add_edge(y, n, valid=True) + + # push the new validated edge in the heap, this will be + # moved to its proper position wt = rag[y][n]['weight'] heapq.heappush(edge_heap, (wt, y, n, rag[y][n])) - - arr = np.arange(labels.max() + 1) for ix, (n, d) in enumerate(rag.nodes_iter(data=True)): From 931f40af6868646d8be1d2d80e05c7b9748544e4 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sat, 16 Aug 2014 16:10:22 +0530 Subject: [PATCH 10/31] blank line --- skimage/graph/graph_merge.py | 1 - 1 file changed, 1 deletion(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 68be4392..7e8acd58 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -72,7 +72,6 @@ def merge_hierarchical(labels, rag, thresh, in_place=True): heapq.heappush(edge_heap, (wt, x, y, data)) while min_wt < thresh: - min_wt, x, y, data = heapq.heappop(edge_heap) # Ensure popped edge is valid, if not, the edge is discarded From e0edc91b1727855356d71b7721b52843c5f7ded9 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Mon, 18 Aug 2014 19:53:50 +0530 Subject: [PATCH 11/31] lists in the heap to avoid copying of dict --- skimage/graph/graph_merge.py | 135 ++++++++++++++++++++++------------- 1 file changed, 85 insertions(+), 50 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 7e8acd58..221294af 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -26,11 +26,49 @@ def _hmerge_mean_color(graph, src, dst, n): return diff +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 vaidated/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 n in rag.neighbors(node): + # The figure these comments refer to is drawn below + # You'll know it when you see it + data = rag[node][n] + try: + # invalidates (4, 5) and (4, 6) + # their weights in the heap are no longer valid + data['heap item'][3] = False + except KeyError: + # (1, 4) and (2, 4) never existed in the graph before + pass + + # Add (1, 4), (2, 4), (4, 5) and (5, 6) with updated weights + wt = data['weight'] + heap_item = [wt, node, n, True] + data['heap item'] = heap_item + heapq.heappush(heap_list, heap_item) + + def merge_hierarchical(labels, rag, thresh, in_place=True): """Perform hierarchical merging of a RAG. - Given an image's labels and its RAG, the method merges the similar nodes - until the weight between every two nodes is more than `thresh`. + Greedily merges the most similar pair of nodes until no edges lower than + `thresh` remain. + Parameters ---------- @@ -39,9 +77,8 @@ def merge_hierarchical(labels, rag, thresh, in_place=True): rag : RAG The Region Adjacency Graph. thresh : float - The threshold. Regions connected by an edges with smaller wegiht than - `thresh` are merged. A high value of `thresh` would mean that a lot of - regions are merged, and the output will contain fewer regions. + Regions connected by an edge with weight smaller than `thresh` are + merged. in_place : bool, optional If set, the RAG is modified in place. @@ -58,62 +95,60 @@ def merge_hierarchical(labels, rag, thresh, in_place=True): >>> rag = graph.rag_mean_color(img, labels) >>> new_labels = graph.merge_hierarchical(labels, rag, 40) """ - min_wt = 0 - if not in_place: rag = rag.copy() edge_heap = [] - for x, y, data in rag.edges_iter(data=True): - if x != y: - # Validate all edges and push them in heap - data['valid'] = True - wt = data['weight'] - heapq.heappush(edge_heap, (wt, x, y, data)) + for src, dst, data in rag.edges_iter(data=True): + # Push a valid edge in the heap + wt = data['weight'] + heap_item = [wt, src, dst, data] + heapq.heappush(edge_heap, heap_item) - while min_wt < thresh: - min_wt, x, y, data = heapq.heappop(edge_heap) + # Reference to the heap item in the graph + data['heap item'] = heap_item + + while edge_heap[0][0] < thresh: + _, src, dst, valid = heapq.heappop(edge_heap) # Ensure popped edge is valid, if not, the edge is discarded - if min_wt < thresh and data['valid']: - total_color = (rag.node[y]['total color'] + - rag.node[x]['total color']) - n_pixels = rag.node[x]['pixel count'] + rag.node[y]['pixel count'] - rag.node[y]['total color'] = total_color - rag.node[y]['pixel count'] = n_pixels - rag.node[y]['mean color'] = total_color / n_pixels + if valid: + total_color = (rag.node[src]['total color'] + + rag.node[dst]['total color']) + n_pixels = (rag.node[src]['pixel count'] + + rag.node[dst]['pixel count']) + rag.node[dst]['total color'] = total_color + rag.node[dst]['pixel count'] = n_pixels + rag.node[dst]['mean color'] = total_color / n_pixels - # This will invalidate all the below edges in the heap - for n in rag.neighbors(x): - rag[x][n]['valid'] = False + # Conider a graph with edges + # (1, 2) -> 50 + # (1, 3) -> 60 + # (3, 4) -> 70 + # (4, 5) -> 80 + # (4, 6) -> 90 + # + # 1 5 + # \ / + # 3---4 + # / \ + # 2 6 :-) - for n in rag.neighbors(y): - rag[y][n]['valid'] = False + # After merging 3 and 4 + # + # 1 5 + # \ / + # 4 + # / \ + # 2 6 B-) - rag.merge_nodes(x, y, _hmerge_mean_color) - for n in rag.neighbors(y): - if n != y: - # 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 + # Will take care of (1, 3) and (2, 3) + # they are no longer in the graph + for n in rag.neighbors(src): + rag[src][n]['heap item'][3] = False - # invalidates the edge in the heap, if it all it exists - data = rag[y][n] - data['valid'] = False - - # allocate a new dictionary for the edge - data_copy = data.copy() - rag[y][n] = data_copy - rag[n][y] = data_copy - - # validate this edge - rag.add_edge(y, n, valid=True) - - # push the new validated edge in the heap, this will be - # moved to its proper position - wt = rag[y][n]['weight'] - heapq.heappush(edge_heap, (wt, y, n, rag[y][n])) + rag.merge_nodes(src, dst, _hmerge_mean_color) + _revalidate_node_edges(rag, dst, edge_heap) arr = np.arange(labels.max() + 1) for ix, (n, d) in enumerate(rag.nodes_iter(data=True)): From 1af5aad6200962121c7426f77cfa234110231dd6 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Mon, 18 Aug 2014 20:17:12 +0530 Subject: [PATCH 12/31] doc string --- skimage/graph/graph_merge.py | 1 - 1 file changed, 1 deletion(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 221294af..22984327 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -69,7 +69,6 @@ def merge_hierarchical(labels, rag, thresh, in_place=True): Greedily merges the most similar pair of nodes until no edges lower than `thresh` remain. - Parameters ---------- labels : ndarray From 8e6990f6968c0885e7d45a9cb58eebec503c6ef6 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Mon, 18 Aug 2014 20:40:09 +0530 Subject: [PATCH 13/31] addition inline --- skimage/graph/graph_merge.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 22984327..0c3d4026 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -112,13 +112,10 @@ def merge_hierarchical(labels, rag, thresh, in_place=True): # Ensure popped edge is valid, if not, the edge is discarded if valid: - total_color = (rag.node[src]['total color'] + - rag.node[dst]['total color']) - n_pixels = (rag.node[src]['pixel count'] + - rag.node[dst]['pixel count']) - rag.node[dst]['total color'] = total_color - rag.node[dst]['pixel count'] = n_pixels - rag.node[dst]['mean color'] = total_color / n_pixels + rag.node[dst]['total color'] += rag.node[src]['total color'] + rag.node[dst]['pixel count'] += rag.node[src]['pixel count'] + rag.node[dst]['mean color'] = (rag.node[dst]['total color'] / + rag.node[dst]['pixel count']) # Conider a graph with edges # (1, 2) -> 50 From 72b9e9fe45c91fa5b3417febc5822c7174720513 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Mon, 18 Aug 2014 22:34:18 +0530 Subject: [PATCH 14/31] Rebase work --- skimage/graph/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/skimage/graph/__init__.py b/skimage/graph/__init__.py index 50f69154..f93708c9 100644 --- a/skimage/graph/__init__.py +++ b/skimage/graph/__init__.py @@ -1,11 +1,8 @@ from .spath import shortest_path from .mcp import MCP, MCP_Geometric, MCP_Connect, MCP_Flexible, route_through_array from .graph_cut import cut_threshold, cut_normalized -<<<<<<< HEAD from .rag import rag_mean_color, RAG, draw_rag -======= from .graph_merge import merge_hierarchical ->>>>>>> working code ncut = cut_normalized From 7f9be8995f0f8d8b011a7bd92bea82dd3bed6489 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Tue, 19 Aug 2014 00:08:03 +0530 Subject: [PATCH 15/31] Spelling --- skimage/graph/graph_merge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 0c3d4026..7d13d39b 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -84,7 +84,7 @@ def merge_hierarchical(labels, rag, thresh, in_place=True): Returns ------- out : ndarray - The new labelled array. + The new labeled array. Examples -------- From c68729e0218be1dcbfba46767d24a9dcabff5f01 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Tue, 19 Aug 2014 00:24:42 +0530 Subject: [PATCH 16/31] spelling check --- skimage/graph/graph_merge.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 7d13d39b..a5bd6d9b 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -12,7 +12,7 @@ def _hmerge_mean_color(graph, src, dst, n): graph : RAG The graph under consideration. src, dst : int - The verices in `graph` to be merged. + The vertices in `graph` to be merged. n : int A neighbor of `src` or `dst` or both. @@ -35,7 +35,7 @@ def _revalidate_node_edges(rag, node, heap_list): rag : RAG The Region Adjacency Graph. node : int - The id of the node whose incident edges are to be vaidated/invalidated. + The id of the node whose incident edges are to be validated/invalidated. heap_list : list The list containing the existing heap of edges. """ @@ -117,7 +117,7 @@ def merge_hierarchical(labels, rag, thresh, in_place=True): rag.node[dst]['mean color'] = (rag.node[dst]['total color'] / rag.node[dst]['pixel count']) - # Conider a graph with edges + # Consider a graph with edges # (1, 2) -> 50 # (1, 3) -> 60 # (3, 4) -> 70 From e41b34314a1f469f8ae10f7ed8ee8e7fb9e57887 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Wed, 27 Aug 2014 20:12:35 +0530 Subject: [PATCH 17/31] Removed ASCII art --- skimage/graph/graph_merge.py | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index a5bd6d9b..cd103325 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -35,7 +35,8 @@ def _revalidate_node_edges(rag, node, heap_list): rag : RAG The Region Adjacency Graph. node : int - The id of the node whose incident edges are to be validated/invalidated. + The id of the node whose incident edges are to be validated/invalidated + . heap_list : list The list containing the existing heap of edges. """ @@ -45,18 +46,15 @@ def _revalidate_node_edges(rag, node, heap_list): # instead we invalidate them for n in rag.neighbors(node): - # The figure these comments refer to is drawn below - # You'll know it when you see it data = rag[node][n] try: - # invalidates (4, 5) and (4, 6) - # their weights in the heap are no longer valid + # invalidate existing neghbors of `dst`, they have new weights data['heap item'][3] = False except KeyError: - # (1, 4) and (2, 4) never existed in the graph before + # will hangle the case where the edge did not exist in the existing + # graph pass - # Add (1, 4), (2, 4), (4, 5) and (5, 6) with updated weights wt = data['weight'] heap_item = [wt, node, n, True] data['heap item'] = heap_item @@ -117,29 +115,7 @@ def merge_hierarchical(labels, rag, thresh, in_place=True): rag.node[dst]['mean color'] = (rag.node[dst]['total color'] / rag.node[dst]['pixel count']) - # Consider a graph with edges - # (1, 2) -> 50 - # (1, 3) -> 60 - # (3, 4) -> 70 - # (4, 5) -> 80 - # (4, 6) -> 90 - # - # 1 5 - # \ / - # 3---4 - # / \ - # 2 6 :-) - - # After merging 3 and 4 - # - # 1 5 - # \ / - # 4 - # / \ - # 2 6 B-) - - # Will take care of (1, 3) and (2, 3) - # they are no longer in the graph + # Invalidate all neigbors of `src` before its deleted for n in rag.neighbors(src): rag[src][n]['heap item'][3] = False From 4c4d7108e149fbdaa741d665ae152ea8297b576a Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Wed, 27 Aug 2014 21:09:25 +0530 Subject: [PATCH 18/31] handled the function with callbacks --- doc/examples/plot_rag_merge.py | 6 +-- skimage/graph/__init__.py | 3 +- skimage/graph/graph_merge.py | 76 ++++++++++++++++++++++++++------- skimage/graph/tests/test_rag.py | 2 +- 4 files changed, 67 insertions(+), 20 deletions(-) diff --git a/doc/examples/plot_rag_merge.py b/doc/examples/plot_rag_merge.py index 00236d95..d15ee954 100644 --- a/doc/examples/plot_rag_merge.py +++ b/doc/examples/plot_rag_merge.py @@ -5,8 +5,8 @@ RAG Merging This example constructs a Region Adjacency Graph (RAG) and progressively merges regions that are similar in color. Merging two adjacent regions produces -a new regions with all the pixels from the merged regions. Regions are merged -until no highly similar regions remain. +a new region with all the pixels from the merged regions. Regions are merged +until no highly similar region pairs remain. """ @@ -16,7 +16,7 @@ from skimage import graph, data, io, segmentation, color 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, 40) +labels2 = graph.merge_hierarchical_mean_color(labels, g, 40) g2 = graph.rag_mean_color(img, labels2) out = color.label2rgb(labels2, img, kind='avg') diff --git a/skimage/graph/__init__.py b/skimage/graph/__init__.py index f93708c9..7122bb3c 100644 --- a/skimage/graph/__init__.py +++ b/skimage/graph/__init__.py @@ -2,7 +2,7 @@ from .spath import shortest_path from .mcp import MCP, MCP_Geometric, MCP_Connect, MCP_Flexible, route_through_array from .graph_cut import cut_threshold, cut_normalized from .rag import rag_mean_color, RAG, draw_rag -from .graph_merge import merge_hierarchical +from .graph_merge import merge_hierarchical, merge_hierarchical_mean_color ncut = cut_normalized @@ -18,4 +18,5 @@ __all__ = ['shortest_path', 'ncut', 'draw_rag', 'merge_hierarchical', + 'merge_hierarchical_mean_color', 'RAG'] diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index cd103325..077d9723 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -2,7 +2,7 @@ import numpy as np import heapq -def _hmerge_mean_color(graph, src, dst, n): +def _weight_mean_color(graph, src, dst, n): """Callback to handle merging nodes by recomputing mean color. The method expects that the mean color of `dst` is already computed. @@ -26,6 +26,24 @@ def _hmerge_mean_color(graph, src, dst, n): return diff +def _pre_merge_mean_color(graph, src, dst): + """Callback called before merging two nodes of a mean color distance graph. + + This method computes the mean color of `dst`. + + Parameters + ---------- + graph : RAG + The graph under consideration. + src, dst : int + The vertices in `graph` to be merged. + """ + graph.node[dst]['total color'] += graph.node[src]['total color'] + graph.node[dst]['pixel count'] += graph.node[src]['pixel count'] + graph.node[dst]['mean color'] = (graph.node[dst]['total color'] / + graph.node[dst]['pixel count']) + + def _revalidate_node_edges(rag, node, heap_list): """Handles validation and invalidation of edges incident to a node. @@ -61,8 +79,10 @@ def _revalidate_node_edges(rag, node, heap_list): heapq.heappush(heap_list, heap_item) -def merge_hierarchical(labels, rag, thresh, in_place=True): - """Perform hierarchical merging of a RAG. +def merge_hierarchical_mean_color(labels, rag, thresh, in_place=True): + return merge_hierarchical(labels, rag, thresh, in_place, + _pre_merge_mean_color, _weight_mean_color) + """Perform hierarchical merging of a color distance RAG. Greedily merges the most similar pair of nodes until no edges lower than `thresh` remain. @@ -79,18 +99,48 @@ def merge_hierarchical(labels, rag, thresh, in_place=True): in_place : bool, optional If set, the RAG is modified in place. - Returns - ------- - out : ndarray - The new labeled array. - Examples -------- >>> from skimage import data, graph, segmentation >>> img = data.coffee() >>> labels = segmentation.slic(img) >>> rag = graph.rag_mean_color(img, labels) - >>> new_labels = graph.merge_hierarchical(labels, rag, 40) + >>> new_labels = graph.merge_hierarchical_mean_color(labels, rag, 40) + """ + + +def merge_hierarchical(labels, rag, thresh, in_place, pre_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. + in_place : bool, optional + If set, the RAG is modified in place. + pre_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 + ``pre_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 not in_place: rag = rag.copy() @@ -110,16 +160,12 @@ def merge_hierarchical(labels, rag, thresh, in_place=True): # Ensure popped edge is valid, if not, the edge is discarded if valid: - rag.node[dst]['total color'] += rag.node[src]['total color'] - rag.node[dst]['pixel count'] += rag.node[src]['pixel count'] - rag.node[dst]['mean color'] = (rag.node[dst]['total color'] / - rag.node[dst]['pixel count']) - + _pre_merge_mean_color(rag, src, dst) # Invalidate all neigbors of `src` before its deleted for n in rag.neighbors(src): rag[src][n]['heap item'][3] = False - rag.merge_nodes(src, dst, _hmerge_mean_color) + rag.merge_nodes(src, dst, _weight_mean_color) _revalidate_node_edges(rag, dst, edge_heap) arr = np.arange(labels.max() + 1) diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index 4e287ceb..ba9ef994 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -124,7 +124,7 @@ def test_merge_hierarchical(): labels[50:, 50:] = 3 rag = graph.rag_mean_color(img, labels) - new_labels = graph.merge_hierarchical(labels, rag, 10) + new_labels = graph.merge_hierarchical_mean_color(labels, rag, 10) # Two labels assert new_labels.max() == 1 From 409c3612ffdbc4cc2aff6d5aa50452d867617d98 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sun, 14 Dec 2014 05:29:43 +0530 Subject: [PATCH 19/31] added merge_in_place option --- skimage/graph/graph_merge.py | 48 +++++++++++++++++++++++++++--------- skimage/graph/rag.py | 3 +++ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 077d9723..4158b0cf 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -21,6 +21,8 @@ def _weight_mean_color(graph, src, dst, n): weight : float The absolute difference of the mean color between node `dst` and `n`. """ + + #print 'merging diff = graph.node[dst]['mean color'] - graph.node[n]['mean color'] diff = np.linalg.norm(diff) return diff @@ -79,9 +81,7 @@ def _revalidate_node_edges(rag, node, heap_list): heapq.heappush(heap_list, heap_item) -def merge_hierarchical_mean_color(labels, rag, thresh, in_place=True): - return merge_hierarchical(labels, rag, thresh, in_place, - _pre_merge_mean_color, _weight_mean_color) +def merge_hierarchical_mean_color(labels, rag, thresh, in_place=True, merge_in_place=False): """Perform hierarchical merging of a color distance RAG. Greedily merges the most similar pair of nodes until no edges lower than @@ -107,9 +107,11 @@ def merge_hierarchical_mean_color(labels, rag, thresh, in_place=True): >>> rag = graph.rag_mean_color(img, labels) >>> new_labels = graph.merge_hierarchical_mean_color(labels, rag, 40) """ + return merge_hierarchical(labels, rag, thresh, in_place, merge_in_place, + _pre_merge_mean_color, _weight_mean_color) -def merge_hierarchical(labels, rag, thresh, in_place, pre_merge_func, +def merge_hierarchical(labels, rag, thresh, in_place, merge_in_place,pre_merge_func, weight_func): """Perform hierarchical merging of a RAG. @@ -146,27 +148,49 @@ def merge_hierarchical(labels, rag, thresh, in_place, pre_merge_func, rag = rag.copy() edge_heap = [] - for src, dst, data in rag.edges_iter(data=True): + for n1, n2, data in rag.edges_iter(data=True): # Push a valid edge in the heap wt = data['weight'] - heap_item = [wt, src, dst, data] + heap_item = [wt, n1, n2, data] heapq.heappush(edge_heap, heap_item) # Reference to the heap item in the graph data['heap item'] = heap_item while edge_heap[0][0] < thresh: - _, src, dst, valid = heapq.heappop(edge_heap) + _, n1, n2, valid = heapq.heappop(edge_heap) # Ensure popped edge is valid, if not, the edge is discarded if valid: - _pre_merge_mean_color(rag, src, dst) + pre_merge_func(rag, n1, n2) # Invalidate all neigbors of `src` before its deleted - for n in rag.neighbors(src): - rag[src][n]['heap item'][3] = False + for n in rag.neighbors(n1): + rag[n1][n]['heap item'][3] = False - rag.merge_nodes(src, dst, _weight_mean_color) - _revalidate_node_edges(rag, dst, edge_heap) + if not merge_in_place: + for n in rag.neighbors(n2): + rag[n2][n]['heap item'][3] = False + + if not merge_in_place: + + #print 'added',next_id + next_id = rag.next_id() + + rag._add_node(next_id) + rag.node[next_id] = rag.node[n2] + + for nbr in rag.neighbors(n2): + rag.add_edge(nbr, next_id, {'weight':rag[n][n2]['weight']}) + + rag.remove_node(n2) + + src, dst = n1, next_id + else: + src, dst = n1, n2 + + new_id = rag.merge_nodes(src, dst, weight_func) + + _revalidate_node_edges(rag, new_id, edge_heap) arr = np.arange(labels.max() + 1) for ix, (n, d) in enumerate(rag.nodes_iter(data=True)): diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index 5f148c5e..21498106 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -164,6 +164,9 @@ class RAG(nx.Graph): """ return self.max_id + 1 + def _add_node(self, u): + super(RAG, self).add_node(u) + def _add_edge_filter(values, graph): """Create edge in `g` between the first element of `values` and the rest. From c5f8f27e4460316454ffa6ddb18507431de88b12 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sun, 14 Dec 2014 06:27:57 +0530 Subject: [PATCH 20/31] code cleanup and removed test --- doc/examples/plot_rag_merge.py | 81 +++++++++++++++++++++- skimage/graph/__init__.py | 3 +- skimage/graph/graph_merge.py | 115 +++++++------------------------- skimage/graph/tests/test_rag.py | 20 ------ 4 files changed, 104 insertions(+), 115 deletions(-) diff --git a/doc/examples/plot_rag_merge.py b/doc/examples/plot_rag_merge.py index d15ee954..5120d96b 100644 --- a/doc/examples/plot_rag_merge.py +++ b/doc/examples/plot_rag_merge.py @@ -11,12 +11,91 @@ until no highly similar region pairs remain. """ from skimage import graph, data, io, segmentation, color +import numpy as np +def _weight_mean_color(graph, src, dst, n): + """Callback to handle merging nodes by recomputing mean color. + + The method expects that the mean color of `dst` is already computed. + + 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 + ------- + weight : float + The absolute difference of the mean color between node `dst` and `n`. + """ + + #print 'merging + diff = graph.node[dst]['mean color'] - graph.node[n]['mean color'] + diff = np.linalg.norm(diff) + return diff + + +def _pre_merge_mean_color(graph, src, dst): + """Callback called before merging two nodes of a mean color distance graph. + + This method computes the mean color of `dst`. + + Parameters + ---------- + graph : RAG + The graph under consideration. + src, dst : int + The vertices in `graph` to be merged. + """ + graph.node[dst]['total color'] += graph.node[src]['total color'] + graph.node[dst]['pixel count'] += graph.node[src]['pixel count'] + graph.node[dst]['mean color'] = (graph.node[dst]['total color'] / + graph.node[dst]['pixel count']) + + +def merge_hierarchical_mean_color(labels, rag, thresh, rag_copy=True, + in_place_merge=False): + """Perform hierarchical merging of a color distance 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, optional + If set, the RAG copied before modifying. + in_place_merge : bool, optional + If set, the nodes are merged in place. Otherwise, a new node is + created for each merge. + + Examples + -------- + >>> from skimage import data, graph, segmentation + >>> img = data.coffee() + >>> labels = segmentation.slic(img) + >>> rag = graph.rag_mean_color(img, labels) + >>> new_labels = graph.merge_hierarchical_mean_color(labels, rag, 40) + """ + return graph.merge_hierarchical(labels, rag, thresh, rag_copy, + in_place_merge, _pre_merge_mean_color, + _weight_mean_color) + img = data.coffee() labels = segmentation.slic(img, compactness=30, n_segments=400) g = graph.rag_mean_color(img, labels) -labels2 = graph.merge_hierarchical_mean_color(labels, g, 40) +labels2 = merge_hierarchical_mean_color(labels, g, 40) g2 = graph.rag_mean_color(img, labels2) out = color.label2rgb(labels2, img, kind='avg') diff --git a/skimage/graph/__init__.py b/skimage/graph/__init__.py index 7122bb3c..f93708c9 100644 --- a/skimage/graph/__init__.py +++ b/skimage/graph/__init__.py @@ -2,7 +2,7 @@ from .spath import shortest_path from .mcp import MCP, MCP_Geometric, MCP_Connect, MCP_Flexible, route_through_array from .graph_cut import cut_threshold, cut_normalized from .rag import rag_mean_color, RAG, draw_rag -from .graph_merge import merge_hierarchical, merge_hierarchical_mean_color +from .graph_merge import merge_hierarchical ncut = cut_normalized @@ -18,5 +18,4 @@ __all__ = ['shortest_path', 'ncut', 'draw_rag', 'merge_hierarchical', - 'merge_hierarchical_mean_color', 'RAG'] diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 4158b0cf..d101cd60 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -2,50 +2,6 @@ import numpy as np import heapq -def _weight_mean_color(graph, src, dst, n): - """Callback to handle merging nodes by recomputing mean color. - - The method expects that the mean color of `dst` is already computed. - - 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 - ------- - weight : float - The absolute difference of the mean color between node `dst` and `n`. - """ - - #print 'merging - diff = graph.node[dst]['mean color'] - graph.node[n]['mean color'] - diff = np.linalg.norm(diff) - return diff - - -def _pre_merge_mean_color(graph, src, dst): - """Callback called before merging two nodes of a mean color distance graph. - - This method computes the mean color of `dst`. - - Parameters - ---------- - graph : RAG - The graph under consideration. - src, dst : int - The vertices in `graph` to be merged. - """ - graph.node[dst]['total color'] += graph.node[src]['total color'] - graph.node[dst]['pixel count'] += graph.node[src]['pixel count'] - graph.node[dst]['mean color'] = (graph.node[dst]['total color'] / - graph.node[dst]['pixel count']) - - def _revalidate_node_edges(rag, node, heap_list): """Handles validation and invalidation of edges incident to a node. @@ -81,38 +37,21 @@ def _revalidate_node_edges(rag, node, heap_list): heapq.heappush(heap_list, heap_item) -def merge_hierarchical_mean_color(labels, rag, thresh, in_place=True, merge_in_place=False): - """Perform hierarchical merging of a color distance RAG. +def _copy_node(graph, node_id, copy_id): + """ Copies `node_id` into `copy_id` along with all its edges. """ - Greedily merges the most similar pair of nodes until no edges lower than - `thresh` remain. + graph._add_node(copy_id) + graph.node[copy_id] = graph.node[node_id] - 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. - in_place : bool, optional - If set, the RAG is modified in place. + for nbr in graph.neighbors(node_id): + wt = graph[node_id][nbr]['weight'] + graph.add_edge(nbr, copy_id, {'weight': wt}) - Examples - -------- - >>> from skimage import data, graph, segmentation - >>> img = data.coffee() - >>> labels = segmentation.slic(img) - >>> rag = graph.rag_mean_color(img, labels) - >>> new_labels = graph.merge_hierarchical_mean_color(labels, rag, 40) - """ - return merge_hierarchical(labels, rag, thresh, in_place, merge_in_place, - _pre_merge_mean_color, _weight_mean_color) + graph.remove_node(node_id) -def merge_hierarchical(labels, rag, thresh, in_place, merge_in_place,pre_merge_func, - weight_func): +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 @@ -127,12 +66,15 @@ def merge_hierarchical(labels, rag, thresh, in_place, merge_in_place,pre_merge_f thresh : float Regions connected by an edge with weight smaller than `thresh` are merged. - in_place : bool, optional - If set, the RAG is modified in place. - pre_merge_func : callable + rag_copy : bool, optional + If set, the RAG copied before modifying. + in_place_merge : bool, optional + 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 - ``pre_merge_func(graph, src, dst)``. + ``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` @@ -144,7 +86,7 @@ def merge_hierarchical(labels, rag, thresh, in_place, merge_in_place,pre_merge_f The new labeled array. """ - if not in_place: + if rag_copy: rag = rag.copy() edge_heap = [] @@ -162,34 +104,23 @@ def merge_hierarchical(labels, rag, thresh, in_place, merge_in_place,pre_merge_f # Ensure popped edge is valid, if not, the edge is discarded if valid: - pre_merge_func(rag, n1, n2) # Invalidate all neigbors of `src` before its deleted for n in rag.neighbors(n1): rag[n1][n]['heap item'][3] = False - if not merge_in_place: + if not in_place_merge: for n in rag.neighbors(n2): rag[n2][n]['heap item'][3] = False - if not merge_in_place: - - #print 'added',next_id + if not in_place_merge: next_id = rag.next_id() - - rag._add_node(next_id) - rag.node[next_id] = rag.node[n2] - - for nbr in rag.neighbors(n2): - rag.add_edge(nbr, next_id, {'weight':rag[n][n2]['weight']}) - - rag.remove_node(n2) - + _copy_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) arr = np.arange(labels.max() + 1) diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index ba9ef994..35eb5c38 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -108,23 +108,3 @@ def test_rag_error(): labels[5:, :] = 1 testing.assert_raises(ValueError, graph.rag_mean_color, img, labels, 2, 'non existant mode') - -@skipif(not is_installed('networkx')) -def test_merge_hierarchical(): - img = np.zeros((100, 100, 3), dtype='uint8') - img[:50, :50] = 255, 255, 255 - img[:50, 50:] = 254, 254, 254 - img[50:, :50] = 2, 2, 2 - img[50:, 50:] = 1, 1, 1 - - labels = np.zeros((100, 100), dtype='uint8') - labels[:50, :50] = 0 - labels[:50, 50:] = 1 - labels[50:, :50] = 2 - labels[50:, 50:] = 3 - - rag = graph.rag_mean_color(img, labels) - new_labels = graph.merge_hierarchical_mean_color(labels, rag, 10) - - # Two labels - assert new_labels.max() == 1 From 52124a49eb2492e223b532df91b44c2af1d370d9 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sun, 14 Dec 2014 06:29:08 +0530 Subject: [PATCH 21/31] for iterator change --- skimage/graph/graph_merge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index d101cd60..5d918e29 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -99,7 +99,7 @@ def merge_hierarchical(labels, rag, thresh, rag_copy, in_place_merge, # Reference to the heap item in the graph data['heap item'] = heap_item - while edge_heap[0][0] < thresh: + 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 From 58cc399e8375af382a30b671c22c16c7b2b089a4 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sun, 14 Dec 2014 06:33:21 +0530 Subject: [PATCH 22/31] rename mapping array --- skimage/graph/graph_merge.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 5d918e29..88b8d9ab 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -27,7 +27,7 @@ def _revalidate_node_edges(rag, node, heap_list): # invalidate existing neghbors of `dst`, they have new weights data['heap item'][3] = False except KeyError: - # will hangle the case where the edge did not exist in the existing + # will handle the case where the edge did not exist in the existing # graph pass @@ -123,9 +123,9 @@ def merge_hierarchical(labels, rag, thresh, rag_copy, in_place_merge, new_id = rag.merge_nodes(src, dst, weight_func) _revalidate_node_edges(rag, new_id, edge_heap) - arr = np.arange(labels.max() + 1) + label_map = np.arange(labels.max() + 1) for ix, (n, d) in enumerate(rag.nodes_iter(data=True)): for label in d['labels']: - arr[label] = ix + label_map[label] = ix - return arr[labels] + return label_map[labels] From b11ae90fd0db5bcf6c007d8a86caa953a964f369 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sun, 14 Dec 2014 06:35:23 +0530 Subject: [PATCH 23/31] rename mapping array --- skimage/graph/graph_merge.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 88b8d9ab..d035daf7 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -21,8 +21,8 @@ def _revalidate_node_edges(rag, node, heap_list): # heap if their weight is updated. # instead we invalidate them - for n in rag.neighbors(node): - data = rag[node][n] + for nbr in rag.neighbors(node): + data = rag[node][nbr] try: # invalidate existing neghbors of `dst`, they have new weights data['heap item'][3] = False @@ -32,7 +32,7 @@ def _revalidate_node_edges(rag, node, heap_list): pass wt = data['weight'] - heap_item = [wt, node, n, True] + heap_item = [wt, node, nbr, True] data['heap item'] = heap_item heapq.heappush(heap_list, heap_item) @@ -105,12 +105,12 @@ def merge_hierarchical(labels, rag, thresh, rag_copy, in_place_merge, # Ensure popped edge is valid, if not, the edge is discarded if valid: # Invalidate all neigbors of `src` before its deleted - for n in rag.neighbors(n1): - rag[n1][n]['heap item'][3] = False + for nbr in rag.neighbors(n1): + rag[n1][nbr]['heap item'][3] = False if not in_place_merge: - for n in rag.neighbors(n2): - rag[n2][n]['heap item'][3] = False + for nbr in rag.neighbors(n2): + rag[n2][nbr]['heap item'][3] = False if not in_place_merge: next_id = rag.next_id() From 3466a68a6f9e1ac6a9d205d80ec2e809e24643ea Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sun, 14 Dec 2014 07:05:51 +0530 Subject: [PATCH 24/31] put code into functions --- skimage/graph/graph_merge.py | 20 +++++++++++--------- skimage/graph/rag.py | 9 +++++++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index d035daf7..531c5775 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -37,10 +37,10 @@ def _revalidate_node_edges(rag, node, heap_list): heapq.heappush(heap_list, heap_item) -def _copy_node(graph, node_id, copy_id): - """ Copies `node_id` into `copy_id` along with all its edges. """ +def _rename_node(graph, node_id, copy_id): + """ Renames `node_id` in `graph` to `copy_id`. """ - graph._add_node(copy_id) + graph._add_node_silent(copy_id) graph.node[copy_id] = graph.node[node_id] for nbr in graph.neighbors(node_id): @@ -49,6 +49,11 @@ def _copy_node(graph, node_id, copy_id): graph.remove_node(node_id) +def _invalidate_neighbors(graph, node): + """ Invalidates all neighbors of `node` in the heap. """ + for nbr in graph.neighbors(node): + graph[node][nbr]['heap item'][3] = False + def merge_hierarchical(labels, rag, thresh, rag_copy, in_place_merge, merge_func, weight_func): @@ -105,16 +110,13 @@ def merge_hierarchical(labels, rag, thresh, rag_copy, in_place_merge, # 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): - rag[n1][nbr]['heap item'][3] = False - if not in_place_merge: - for nbr in rag.neighbors(n2): - rag[n2][nbr]['heap item'][3] = False + _invalidate_neighbors(rag, n1) + _invalidate_neighbors(rag, n2) if not in_place_merge: next_id = rag.next_id() - _copy_node(rag, n2, next_id) + _rename_node(rag, n2, next_id) src, dst = n1, next_id else: src, dst = n1, n2 diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index 21498106..120e9e91 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -164,8 +164,13 @@ class RAG(nx.Graph): """ return self.max_id + 1 - def _add_node(self, u): - super(RAG, self).add_node(u) + 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 _add_edge_filter(values, graph): From 05a996a84691b8048c6a6f882c05dbded08ec034 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sun, 14 Dec 2014 22:41:28 +0530 Subject: [PATCH 25/31] changed function calls --- skimage/graph/graph_merge.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 531c5775..f9ec6425 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -49,10 +49,9 @@ def _rename_node(graph, node_id, copy_id): graph.remove_node(node_id) -def _invalidate_neighbors(graph, node): - """ Invalidates all neighbors of `node` in the heap. """ - for nbr in graph.neighbors(node): - graph[node][nbr]['heap item'][3] = False +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, @@ -98,7 +97,7 @@ def merge_hierarchical(labels, rag, thresh, rag_copy, in_place_merge, for n1, n2, data in rag.edges_iter(data=True): # Push a valid edge in the heap wt = data['weight'] - heap_item = [wt, n1, n2, data] + heap_item = [wt, n1, n2, True] heapq.heappush(edge_heap, heap_item) # Reference to the heap item in the graph @@ -111,8 +110,11 @@ def merge_hierarchical(labels, rag, thresh, rag_copy, in_place_merge, if valid: # Invalidate all neigbors of `src` before its deleted - _invalidate_neighbors(rag, n1) - _invalidate_neighbors(rag, n2) + 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() From ec17cb29c00dbf7074e98117e985fcbabb1f8091 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Tue, 27 Jan 2015 22:31:15 +0530 Subject: [PATCH 26/31] Added tests --- skimage/graph/graph_merge.py | 9 +++--- skimage/graph/tests/test_rag.py | 51 ++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index f9ec6425..5bafbd1f 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -24,8 +24,9 @@ def _revalidate_node_edges(rag, node, heap_list): for nbr in rag.neighbors(node): data = rag[node][nbr] try: - # invalidate existing neghbors of `dst`, they have new weights + # 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 @@ -38,7 +39,7 @@ def _revalidate_node_edges(rag, node, heap_list): def _rename_node(graph, node_id, copy_id): - """ Renames `node_id` in `graph` to `copy_id`. """ + """ Rename `node_id` in `graph` to `copy_id`. """ graph._add_node_silent(copy_id) graph.node[copy_id] = graph.node[node_id] @@ -70,9 +71,9 @@ def merge_hierarchical(labels, rag, thresh, rag_copy, in_place_merge, thresh : float Regions connected by an edge with weight smaller than `thresh` are merged. - rag_copy : bool, optional + rag_copy : bool If set, the RAG copied before modifying. - in_place_merge : bool, optional + in_place_merge : bool If set, the nodes are merged in place. Otherwise, a new node is created for each merge.. merge_func : callable diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index 35eb5c38..95764ea4 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -2,7 +2,7 @@ import numpy as np from skimage import graph from skimage._shared.version_requirements import is_installed from numpy.testing.decorators import skipif -from skimage import segmentation +from skimage import segmentation, io from numpy import testing @@ -108,3 +108,52 @@ def test_rag_error(): labels[5:, :] = 1 testing.assert_raises(ValueError, graph.rag_mean_color, img, labels, 2, 'non existant mode') + + +@skipif(not is_installed('networkx')) +def test_rag_error(): + img = np.zeros((10, 10, 3), dtype='uint8') + labels = np.zeros((10, 10), dtype='uint8') + labels[:5, :] = 0 + labels[5:, :] = 1 + testing.assert_raises(ValueError, graph.rag_mean_color, img, labels, + 2, 'non existant mode') + +def _weight_mean_color(graph, src, dst, n): + #print 'merging + diff = graph.node[dst]['mean color'] - graph.node[n]['mean color'] + diff = np.linalg.norm(diff) + return diff + + +def _pre_merge_mean_color(graph, src, dst): + graph.node[dst]['total color'] += graph.node[src]['total color'] + graph.node[dst]['pixel count'] += graph.node[src]['pixel count'] + graph.node[dst]['mean color'] = (graph.node[dst]['total color'] / + graph.node[dst]['pixel count']) + + +def merge_hierarchical_mean_color(labels, rag, thresh, rag_copy=True, + in_place_merge=False): + return graph.merge_hierarchical(labels, rag, thresh, rag_copy, + in_place_merge, _pre_merge_mean_color, + _weight_mean_color) + +@skipif(not is_installed('networkx')) +def test_rag_hierarchical(): + img = np.zeros((8, 8, 3), dtype='uint8') + labels = np.zeros((8, 8), dtype='uint8') + + img[:, :, :] = 128 + labels[:,:] = 1 + + img[0:4,0:4,:] = 255,255,255 + labels[0:4, 0:4] = 2 + + img[4:, 0:4,:] = 0,0,0 + labels[4:, 0:4] = 3 + + g = graph.rag_mean_color(img, labels) + result = merge_hierarchical_mean_color(labels, g, 300) + assert len(np.unique(result)) == 1 + io.imsave('/home/vighnesh/Desktop/test.png', img) From c96fbe49267d8a919eb3d8fcec0f21e0058effb6 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Tue, 27 Jan 2015 22:33:39 +0530 Subject: [PATCH 27/31] minor formatting --- skimage/graph/graph_merge.py | 1 + 1 file changed, 1 insertion(+) diff --git a/skimage/graph/graph_merge.py b/skimage/graph/graph_merge.py index 5bafbd1f..308f7f37 100644 --- a/skimage/graph/graph_merge.py +++ b/skimage/graph/graph_merge.py @@ -50,6 +50,7 @@ def _rename_node(graph, node_id, copy_id): 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 From 237b11fefdc67d3a251845f9a5fc988db63878d5 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Wed, 28 Jan 2015 11:35:57 +0530 Subject: [PATCH 28/31] Removed local debug statement. --- skimage/graph/tests/test_rag.py | 1 - 1 file changed, 1 deletion(-) diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index 95764ea4..5887b502 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -156,4 +156,3 @@ def test_rag_hierarchical(): g = graph.rag_mean_color(img, labels) result = merge_hierarchical_mean_color(labels, g, 300) assert len(np.unique(result)) == 1 - io.imsave('/home/vighnesh/Desktop/test.png', img) From e79bbed001309167b3df5116e75271a4479118c7 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Wed, 28 Jan 2015 22:01:30 +0530 Subject: [PATCH 29/31] Improved test case and removed duplicate test --- doc/examples/plot_rag_merge.py | 42 ++++----------------------------- skimage/graph/tests/test_rag.py | 33 +++++++++++++------------- 2 files changed, 22 insertions(+), 53 deletions(-) diff --git a/doc/examples/plot_rag_merge.py b/doc/examples/plot_rag_merge.py index 5120d96b..0f8ca1cb 100644 --- a/doc/examples/plot_rag_merge.py +++ b/doc/examples/plot_rag_merge.py @@ -34,13 +34,12 @@ def _weight_mean_color(graph, src, dst, n): The absolute difference of the mean color between node `dst` and `n`. """ - #print 'merging diff = graph.node[dst]['mean color'] - graph.node[n]['mean color'] diff = np.linalg.norm(diff) return diff -def _pre_merge_mean_color(graph, src, dst): +def merge_mean_color(graph, src, dst): """Callback called before merging two nodes of a mean color distance graph. This method computes the mean color of `dst`. @@ -58,44 +57,13 @@ def _pre_merge_mean_color(graph, src, dst): graph.node[dst]['pixel count']) -def merge_hierarchical_mean_color(labels, rag, thresh, rag_copy=True, - in_place_merge=False): - """Perform hierarchical merging of a color distance 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, optional - If set, the RAG copied before modifying. - in_place_merge : bool, optional - If set, the nodes are merged in place. Otherwise, a new node is - created for each merge. - - Examples - -------- - >>> from skimage import data, graph, segmentation - >>> img = data.coffee() - >>> labels = segmentation.slic(img) - >>> rag = graph.rag_mean_color(img, labels) - >>> new_labels = graph.merge_hierarchical_mean_color(labels, rag, 40) - """ - return graph.merge_hierarchical(labels, rag, thresh, rag_copy, - in_place_merge, _pre_merge_mean_color, - _weight_mean_color) - img = data.coffee() labels = segmentation.slic(img, compactness=30, n_segments=400) g = graph.rag_mean_color(img, labels) -labels2 = merge_hierarchical_mean_color(labels, g, 40) + +labels2 = graph.merge_hierarchical(labels, g, 40, False, True, + merge_mean_color, _weight_mean_color) + g2 = graph.rag_mean_color(img, labels2) out = color.label2rgb(labels2, img, kind='avg') diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index 5887b502..df78ed7d 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -2,7 +2,7 @@ import numpy as np from skimage import graph from skimage._shared.version_requirements import is_installed from numpy.testing.decorators import skipif -from skimage import segmentation, io +from skimage import segmentation from numpy import testing @@ -110,17 +110,7 @@ def test_rag_error(): 2, 'non existant mode') -@skipif(not is_installed('networkx')) -def test_rag_error(): - img = np.zeros((10, 10, 3), dtype='uint8') - labels = np.zeros((10, 10), dtype='uint8') - labels[:5, :] = 0 - labels[5:, :] = 1 - testing.assert_raises(ValueError, graph.rag_mean_color, img, labels, - 2, 'non existant mode') - def _weight_mean_color(graph, src, dst, n): - #print 'merging diff = graph.node[dst]['mean color'] - graph.node[n]['mean color'] diff = np.linalg.norm(diff) return diff @@ -139,20 +129,31 @@ def merge_hierarchical_mean_color(labels, rag, thresh, rag_copy=True, in_place_merge, _pre_merge_mean_color, _weight_mean_color) + @skipif(not is_installed('networkx')) def test_rag_hierarchical(): img = np.zeros((8, 8, 3), dtype='uint8') labels = np.zeros((8, 8), dtype='uint8') - img[:, :, :] = 128 - labels[:,:] = 1 + img[:, :, :] = 30 + labels[:, :] = 1 - img[0:4,0:4,:] = 255,255,255 + img[0:4, 0:4, :] = 10, 10, 10 labels[0:4, 0:4] = 2 - img[4:, 0:4,:] = 0,0,0 + img[4:, 0:4, :] = 20, 20, 20 labels[4:, 0:4] = 3 g = graph.rag_mean_color(img, labels) - result = merge_hierarchical_mean_color(labels, g, 300) + g2 = g.copy() + thresh = 17.3206 # just above 10*sqrt(3) + + result = merge_hierarchical_mean_color(labels, g, thresh) + assert len(np.unique(result)) == 2 + + result = merge_hierarchical_mean_color(labels, g2, thresh, + in_place_merge=True) + assert len(np.unique(result)) == 2 + + result = graph.cut_threshold(labels, g, thresh) assert len(np.unique(result)) == 1 From d3775f9a3c073272019abbc25eefa79ba24bf8eb Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Thu, 29 Jan 2015 20:33:14 +0530 Subject: [PATCH 30/31] Improved test case --- doc/examples/plot_rag_merge.py | 6 ++++-- skimage/graph/tests/test_rag.py | 14 +++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/doc/examples/plot_rag_merge.py b/doc/examples/plot_rag_merge.py index 0f8ca1cb..d454a096 100644 --- a/doc/examples/plot_rag_merge.py +++ b/doc/examples/plot_rag_merge.py @@ -61,8 +61,10 @@ 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, 40, False, True, - merge_mean_color, _weight_mean_color) +labels2 = graph.merge_hierarchical(labels, g, thresh=40, rag_copy=False, + in_place_merge=True, + merge_func=merge_mean_color, + weight_func=_weight_mean_color) g2 = graph.rag_mean_color(img, labels2) diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index df78ed7d..ca21b27c 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -135,7 +135,7 @@ def test_rag_hierarchical(): img = np.zeros((8, 8, 3), dtype='uint8') labels = np.zeros((8, 8), dtype='uint8') - img[:, :, :] = 30 + img[:, :, :] = 31 labels[:, :] = 1 img[0:4, 0:4, :] = 10, 10, 10 @@ -146,14 +146,18 @@ def test_rag_hierarchical(): g = graph.rag_mean_color(img, labels) g2 = g.copy() - thresh = 17.3206 # just above 10*sqrt(3) + thresh = 20 # more than 11*sqrt(3) result = merge_hierarchical_mean_color(labels, g, thresh) - assert len(np.unique(result)) == 2 + assert(np.all(result[0:4, 0:4] == result[0, 0])) + assert(np.all(result[4:, 0:4] == result[4, 0])) + assert(np.all(result[:, 4:] == result[-1, -1])) result = merge_hierarchical_mean_color(labels, g2, thresh, in_place_merge=True) - assert len(np.unique(result)) == 2 + assert(np.all(result[0:4, 0:4] == result[0, 0])) + assert(np.all(result[4:, 0:4] == result[4, 0])) + assert(np.all(result[:, 4:] == result[-1, -1])) result = graph.cut_threshold(labels, g, thresh) - assert len(np.unique(result)) == 1 + assert np.all(result == result[0, 0]) From 2ffa4847acea18dce1ff7cbddfdcab4aa3e970b7 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Thu, 29 Jan 2015 21:06:10 +0530 Subject: [PATCH 31/31] Fixed tests --- skimage/graph/tests/test_rag.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index ca21b27c..cfa49cdf 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -146,17 +146,15 @@ def test_rag_hierarchical(): g = graph.rag_mean_color(img, labels) g2 = g.copy() - thresh = 20 # more than 11*sqrt(3) + thresh = 20 # more than 11*sqrt(3) but less than result = merge_hierarchical_mean_color(labels, g, thresh) - assert(np.all(result[0:4, 0:4] == result[0, 0])) - assert(np.all(result[4:, 0:4] == result[4, 0])) + assert(np.all(result[:, :4] == result[0, 0])) assert(np.all(result[:, 4:] == result[-1, -1])) result = merge_hierarchical_mean_color(labels, g2, thresh, in_place_merge=True) - assert(np.all(result[0:4, 0:4] == result[0, 0])) - assert(np.all(result[4:, 0:4] == result[4, 0])) + assert(np.all(result[:, :4] == result[0, 0])) assert(np.all(result[:, 4:] == result[-1, -1])) result = graph.cut_threshold(labels, g, thresh)