From 2ebeee2df466c0829ab412deb9733b2584d16ac0 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Sat, 14 Dec 2013 23:55:48 +0100 Subject: [PATCH] update-mcp: Change when examine_neighbors is called. Also renamed Mcp_Connect to MCP_Connect. Makes more sense to call it when the neighbor is frozen. In that way the method is called exactly once for each pair of neighbours. Plus the costs and paths are known and fixed. --- skimage/graph/_mcp.pyx | 70 +++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/skimage/graph/_mcp.pyx b/skimage/graph/_mcp.pyx index 98c71bdb..3b1ceb08 100644 --- a/skimage/graph/_mcp.pyx +++ b/skimage/graph/_mcp.pyx @@ -385,8 +385,8 @@ cdef class MCP: cdef void _examine_neighbor(self, INDEX_T index, INDEX_T new_index, FLOAT_T offset_length): """ _examine_neighbor(self, int index, int new_index, float offset_length) - This method is called for every neighbor examined, even before - checking whether it is frozen. + This method is called once for every pair of neighboring nodes, + as soon as both nodes become frozen. """ pass @@ -574,13 +574,17 @@ cdef class MCP: # using the flat offsets, calculate the new flat index new_index = index + flat_offsets[i] - # Allow subclasses to examine this neighbour + # Get offset length offset_length = offset_lengths[i] - self._examine_neighbor(index, new_index, offset_length) # If we have already found the best path here then # ignore this point if flat_cumulative_costs[new_index] != inf: + # Give subclass the oportunity to examine these two nodes + # Note that only when both nodes are "frozen" their + # cumulative cost is set. By doing the check here, each + # pair of nodes is checked exactly once. + self._examine_neighbor(index, new_index, offset_length) continue # Get cost and new cost @@ -766,15 +770,15 @@ cdef class MCP_Geometric(MCP): @cython.boundscheck(True) @cython.wraparound(True) -cdef class Mcp_Connect(MCP): - """Mcp_Connect(costs, offsets=None, fully_connected=True) +cdef class MCP_Connect(MCP): + """MCP_Connect(costs, offsets=None, fully_connected=True) Connect source points using the distance-weighted minimum cost function. A front is grown from each seed point simultaneously, while the - origin of the front is tracked as well. When two fronts A and - B meet, create_connection() is called. This method must be overloaded - to deal with the found edges in a way that is appropriate for the + origin of the front is tracked as well. When two fronts meet, + create_connection() is called. This method must be overloaded to + deal with the found edges in a way that is appropriate for the application. """ @@ -828,51 +832,41 @@ cdef class Mcp_Connect(MCP): if id2 < 0 or id1 < 0: pass elif id2 != id1: - # we reached the 'front' of another seed point! - # We need to establish the traceback now, because the traceback - # may be overridden in future iterations. - path1 = self._flat_traceback(index) - path2 = self._flat_traceback(new_index) + # We reached the 'front' of another seed point! + # Get position/coordinates + pos1, pos2 = _unravel_index_fortran( + [index, new_index], self.costs_shape) # Also get the costs, so we can keep the path with the least cost cost1 = flat_cumulative_costs[index] cost2 = flat_cumulative_costs[new_index] # Create connection - self.create_connection(id1, id2, path1, path2, cost1, cost2) - - - def unravel_traceback(self, flat_traceback): - """ unravel_traceback(flat_traceback) - - This method can be used to unravel a flat traceback that is passed - to create_connection(). The result is equivalent to a traceback - returned by traceback(). - """ - return self._unravel_traceback(flat_traceback) + self.create_connection(id1, id2, pos1, pos2, cost1, cost2) def create_connection(self, id1, id2, tb1, tb2, cost1, cost2): - """ create_connection id1, id2, traceback1, traceback2, cost1, cost2) + """ create_connection id1, id2, pos1, pos2, cost1, cost2) Overload this method to keep track of the connections that are found during MCP processing. Note that a connection with the same ids can be found multiple times (but with different costs). + At the time that this method is called, both points are "frozen" + and will not be visited again by the MCP algorithm. + Parameters ---------- id1 : int - The index of the seed point where front A originated from. + The seed point id where the first neighbor originated from. id2 : int - The index of the seed point where front B originated from. - traceback1 : list - A list of integers representing the flat traceback from the - meeting point to seed point id1. Use the unravel_traceback() method - to turn obtain a list of coordinates from source to meeting point. - traceback2 : list - Dito for id2. + The seed point id where the second neighbor originated from. + pos1 : tuple + The index of of the first neighbour in the connection. + pos2 : tuple + The index of of the second neighbour in the connection. cost1 : float - The cumulative cost at side A of the connection. + The cumulative cost at pos1. cost2 : float - The cumulative costs at side B of the connection. + The cumulative costs at pos2. """ pass @@ -914,8 +908,8 @@ cdef class MCP_Flexible(MCP): def examine_neighbor(self, INDEX_T index, INDEX_T new_index, FLOAT_T offset_length): """ examine_neighbor(self, index, new_index, offset_length) - This method is called for every neighbor examined, even before - checking whether it is frozen. + This method is called once for every pair of neighboring nodes, + as soon as both nodes become frozen. Overload this method to adapt the behaviour of the algorithm. """ pass