From febef86bbbd75038c548d1ec9fd7f5bc372872a6 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sat, 4 Oct 2014 20:19:58 +0530 Subject: [PATCH 1/8] added non in-place merge --- doc/examples/plot_rag.py | 4 ++-- skimage/graph/rag.py | 32 ++++++++++++++++++++++++++------ skimage/graph/tests/test_rag.py | 4 ++-- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/doc/examples/plot_rag.py b/doc/examples/plot_rag.py index b26a266d..f360af52 100644 --- a/doc/examples/plot_rag.py +++ b/doc/examples/plot_rag.py @@ -75,7 +75,7 @@ display(g, "Original Graph") g.merge_nodes(1, 3) display(g, "Merged with default (min)") -gc.merge_nodes(1, 3, weight_func=max_edge) -display(gc, "Merged with max") +gc.merge_nodes(1, 3, weight_func=max_edge, in_place=False) +display(gc, "Merged with max without in_place") plt.show() diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index 5995fb5d..8f605b1e 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -59,9 +59,9 @@ class RAG(nx.Graph): `networx.Graph `_ """ - def merge_nodes(self, src, dst, weight_func=min_weight, extra_arguments=[], - extra_keywords={}): - """Merge node `src` into `dst`. + def merge_nodes(self, src, dst, weight_func=min_weight, in_place=True, + extra_arguments=[], extra_keywords={}): + """Merge node `src` and `dst`. The new combined node is adjacent to all the neighbors of `src` and `dst`. `weight_func` is called to decide the weight of edges @@ -78,24 +78,44 @@ class RAG(nx.Graph): **extra_keywords)`. `src`, `dst` and `n` are IDs of vertices in the RAG object which is in turn a subclass of `networkx.Graph`. + in_place : bool + If set to `True`, the merged node has the id `dst`, else merged + node has a new id which is returned. extra_arguments : sequence, optional The sequence of extra positional arguments passed to `weight_func`. extra_keywords : dictionary, optional The dict of keyword arguments passed to the `weight_func`. + Returns + ------- + id : int + The id of the new node if `in_place` is `True`. """ src_nbrs = set(self.neighbors(src)) dst_nbrs = set(self.neighbors(dst)) neighbors = (src_nbrs & dst_nbrs) - set([src, dst]) + if not in_place: + new = self.number_of_nodes() + 1 + self.add_node(new) for neighbor in neighbors: w = weight_func(self, src, dst, neighbor, *extra_arguments, **extra_keywords) - self.add_edge(neighbor, dst, weight=w) + if in_place: + self.add_edge(neighbor, dst, weight=w) + else: + self.add_edge(neighbor, new, weight=w) - self.node[dst]['labels'] += self.node[src]['labels'] - self.remove_node(src) + if in_place: + self.node[dst]['labels'] += self.node[src]['labels'] + self.remove_node(src) + else: + self.node[new]['labels'] = (self.node[src]['labels'] + + self.node[dst]['labels']) + self.remove_node(src) + self.remove_node(dst) + return new def _add_edge_filter(values, graph): diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index 26f5a85c..04b82ff7 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -41,8 +41,8 @@ def test_rag_merge(): assert gc.edge[1][2]['weight'] == 20 assert gc.edge[2][3]['weight'] == 40 - g.merge_nodes(1, 4) - g.merge_nodes(2, 3) + g.merge_nodes(1, 4, in_place=True) + g.merge_nodes(2, 3, in_place=True) g.merge_nodes(3, 4) assert sorted(g.node[4]['labels']) == list(range(5)) assert g.edges() == [] From 3a9df73590ae4323bbcfa47588cdcf02f13a743d Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sat, 4 Oct 2014 22:33:10 +0530 Subject: [PATCH 2/8] Corrected new id logic --- skimage/graph/rag.py | 6 +++++- skimage/graph/tests/test_rag.py | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index 8f605b1e..26808b0a 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -15,6 +15,7 @@ from scipy.ndimage import filters from scipy import ndimage as nd import math from .. import draw, measure, segmentation, util, color +import random try: from matplotlib import colors from matplotlib import cm @@ -96,7 +97,10 @@ class RAG(nx.Graph): dst_nbrs = set(self.neighbors(dst)) neighbors = (src_nbrs & dst_nbrs) - set([src, dst]) if not in_place: - new = self.number_of_nodes() + 1 + # Randomly select an id + new = random.randint(1, 99999999) + while new in self: + new = random.randint(1, 99999999) self.add_node(new) for neighbor in neighbors: diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index 04b82ff7..35eb5c38 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -41,10 +41,10 @@ def test_rag_merge(): assert gc.edge[1][2]['weight'] == 20 assert gc.edge[2][3]['weight'] == 40 - g.merge_nodes(1, 4, in_place=True) - g.merge_nodes(2, 3, in_place=True) - g.merge_nodes(3, 4) - assert sorted(g.node[4]['labels']) == list(range(5)) + g.merge_nodes(1, 4) + g.merge_nodes(2, 3) + n = g.merge_nodes(3, 4, in_place=False) + assert sorted(g.node[n]['labels']) == list(range(5)) assert g.edges() == [] From 02fec246ea86901fd20e7f13a1cb4048aef2f6e8 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sat, 4 Oct 2014 22:37:12 +0530 Subject: [PATCH 3/8] change in random number max logic --- skimage/graph/rag.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index 26808b0a..5820250c 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -96,11 +96,12 @@ class RAG(nx.Graph): src_nbrs = set(self.neighbors(src)) dst_nbrs = set(self.neighbors(dst)) neighbors = (src_nbrs & dst_nbrs) - set([src, dst]) + n = self.number_of_nodes() if not in_place: # Randomly select an id - new = random.randint(1, 99999999) + new = random.randint(1, 2*n) while new in self: - new = random.randint(1, 99999999) + new = random.randint(1, 2*n) self.add_node(new) for neighbor in neighbors: From 4f91a4ded3468a2a5bd3c8f3bb9c7145c5b4ea2a Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sun, 5 Oct 2014 17:03:56 +0530 Subject: [PATCH 4/8] cleanup of if else block --- skimage/graph/rag.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index 5820250c..a300c486 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -79,7 +79,7 @@ class RAG(nx.Graph): **extra_keywords)`. `src`, `dst` and `n` are IDs of vertices in the RAG object which is in turn a subclass of `networkx.Graph`. - in_place : bool + in_place : bool, optional If set to `True`, the merged node has the id `dst`, else merged node has a new id which is returned. extra_arguments : sequence, optional @@ -95,30 +95,26 @@ class RAG(nx.Graph): """ src_nbrs = set(self.neighbors(src)) dst_nbrs = set(self.neighbors(dst)) - neighbors = (src_nbrs & dst_nbrs) - set([src, dst]) - n = self.number_of_nodes() - if not in_place: - # Randomly select an id + neighbors = (src_nbrs | dst_nbrs) - set([src, dst]) + + if in_place: + new = dst + else: + n = self.number_of_nodes() new = random.randint(1, 2*n) while new in self: new = random.randint(1, 2*n) self.add_node(new) for neighbor in neighbors: - w = weight_func(self, src, dst, neighbor, *extra_arguments, + w = weight_func(self, src, new, neighbor, *extra_arguments, **extra_keywords) - if in_place: - self.add_edge(neighbor, dst, weight=w) - else: - self.add_edge(neighbor, new, weight=w) + self.add_edge(neighbor, new, weight=w) - if in_place: - self.node[dst]['labels'] += self.node[src]['labels'] - self.remove_node(src) - else: - self.node[new]['labels'] = (self.node[src]['labels'] + - self.node[dst]['labels']) - self.remove_node(src) + self.node[new]['labels'] = (self.node[src]['labels'] + + self.node[dst]['labels']) + self.remove_node(src) + if not in_place: self.remove_node(dst) return new From 0419c1e6f7d0af99d38ff0a1a02fab4dd9f51655 Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Thu, 16 Oct 2014 23:22:56 +0530 Subject: [PATCH 5/8] sequential numbering of nodes using max id --- skimage/graph/rag.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index a300c486..1fd1ddc9 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -15,7 +15,6 @@ from scipy.ndimage import filters from scipy import ndimage as nd import math from .. import draw, measure, segmentation, util, color -import random try: from matplotlib import colors from matplotlib import cm @@ -60,6 +59,13 @@ class RAG(nx.Graph): `networx.Graph `_ """ + def __init__(self, data=None, **attr): + + nx.Graph.__init__(self, data, **attr) + self.max_id = None + for n in self.nodes_iter(): + self.max_id = max(self.max_id, n) + def merge_nodes(self, src, dst, weight_func=min_weight, in_place=True, extra_arguments=[], extra_keywords={}): """Merge node `src` and `dst`. @@ -100,10 +106,7 @@ class RAG(nx.Graph): if in_place: new = dst else: - n = self.number_of_nodes() - new = random.randint(1, 2*n) - while new in self: - new = random.randint(1, 2*n) + new = self.max_id + 1 self.add_node(new) for neighbor in neighbors: @@ -118,6 +121,19 @@ class RAG(nx.Graph): self.remove_node(dst) return new + def add_node(self, n, attr_dict=None, **attr): + nx.Graph.add_node(self, n, attr_dict, **attr) + self.max_id = max(n, self.max_id) + + def add_edge(self, u, v, attr_dict=None, **attr): + nx.Graph.add_edge(self, u, v, attr_dict, **attr) + self.max_id = max(u, v, self.max_id) + + def copy(self): + g = nx.Graph.copy(self) + g.max_id = self.max_id + return g + def _add_edge_filter(values, graph): """Create edge in `g` between the first element of `values` and the rest. From 31ecc3db86d216d0fb8687be0a41daeb5d45dd3a Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Fri, 17 Oct 2014 00:06:12 +0530 Subject: [PATCH 6/8] Change None to 0 --- skimage/graph/rag.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index 1fd1ddc9..3200980b 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -62,7 +62,7 @@ class RAG(nx.Graph): def __init__(self, data=None, **attr): nx.Graph.__init__(self, data, **attr) - self.max_id = None + self.max_id = 0 for n in self.nodes_iter(): self.max_id = max(self.max_id, n) From 96bf74719777abd60b15817bf1433d98de707eec Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Sat, 18 Oct 2014 21:33:42 +0530 Subject: [PATCH 7/8] always return new id and put new id logic in a function --- skimage/graph/rag.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index 3200980b..fbfe13f9 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -61,10 +61,12 @@ class RAG(nx.Graph): def __init__(self, data=None, **attr): - nx.Graph.__init__(self, data, **attr) - self.max_id = 0 - for n in self.nodes_iter(): - self.max_id = max(self.max_id, n) + super(RAG, self).__init__(data, **attr) + try: + self.max_id = max(self.nodes_iter()) + except ValueError: + # Empty sequence + self.max_id = 0 def merge_nodes(self, src, dst, weight_func=min_weight, in_place=True, extra_arguments=[], extra_keywords={}): @@ -106,7 +108,7 @@ class RAG(nx.Graph): if in_place: new = dst else: - new = self.max_id + 1 + new = self.next_id() self.add_node(new) for neighbor in neighbors: @@ -117,23 +119,37 @@ class RAG(nx.Graph): self.node[new]['labels'] = (self.node[src]['labels'] + self.node[dst]['labels']) self.remove_node(src) + if not in_place: self.remove_node(dst) - return new + + return new def add_node(self, n, attr_dict=None, **attr): - nx.Graph.add_node(self, n, attr_dict, **attr) + super(RAG, self).add_node(n, attr_dict, **attr) self.max_id = max(n, self.max_id) def add_edge(self, u, v, attr_dict=None, **attr): - nx.Graph.add_edge(self, u, v, attr_dict, **attr) + super(RAG, self).add_edge(u, v, attr_dict, **attr) self.max_id = max(u, v, self.max_id) def copy(self): - g = nx.Graph.copy(self) + g = super(RAG, self).copy() g.max_id = self.max_id return g + def next_id(self): + """Returns the `id` for the new node to be inserted. + + The current implementation returns one more than the maximum `id`. + + Returns + ------- + id : int + The `id` of the new node to be inserted. + """ + return self.max_id + 1 + def _add_edge_filter(values, graph): """Create edge in `g` between the first element of `values` and the rest. From b8988190b008b8010594cf3c5c2d08e11a9eea8f Mon Sep 17 00:00:00 2001 From: Vighnesh Birodkar Date: Thu, 23 Oct 2014 13:17:29 +0530 Subject: [PATCH 8/8] Docstring changes --- skimage/graph/rag.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index fbfe13f9..81b3c0d6 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -99,7 +99,12 @@ class RAG(nx.Graph): Returns ------- id : int - The id of the new node if `in_place` is `True`. + The id of the new node. + + Notes + ----- + If `in_place` is `False` the resulting node has a new id, rather than + `dst`. """ src_nbrs = set(self.neighbors(src)) dst_nbrs = set(self.neighbors(dst)) @@ -126,14 +131,23 @@ class RAG(nx.Graph): return new def add_node(self, n, attr_dict=None, **attr): + """Add node `n` while updating the maximum node id. + + .. seealso:: :func:`networkx.Graph.add_node`.""" super(RAG, self).add_node(n, attr_dict, **attr) self.max_id = max(n, self.max_id) def add_edge(self, u, v, attr_dict=None, **attr): + """Add an edge between `u` and `v` while updating max node id. + + .. seealso:: :func:`networkx.Graph.add_edge`.""" super(RAG, self).add_edge(u, v, attr_dict, **attr) self.max_id = max(u, v, self.max_id) def copy(self): + """Copy the graph with its max node id. + + .. seealso:: :func:`networkx.Graph.copy`.""" g = super(RAG, self).copy() g.max_id = self.max_id return g