Finish commits of changes to graph: __init__ and setup and remove old cruft.

This commit is contained in:
Zachary Pincus
2009-12-29 13:31:55 -05:00
parent 8b2a8b5031
commit ad141b680d
6 changed files with 11 additions and 316 deletions
+3 -3
View File
@@ -1,10 +1,10 @@
try:
from spath import shortest_path
from trace_path import trace_path
from mcp import MCP, MCP_Geometric, route_through_array
except ImportError:
print """*** The shortest path extension has not been compiled. Run
print """*** The cython extensions have not been compiled. Run
python setup.py build_ext -i
in the source directory to build in-place. Please refer to INSTALL.txt
for further detail."""
for further detail."""
+8 -6
View File
@@ -1,7 +1,6 @@
#!/usr/bin/env python
from scikits.image._build import cython
import os.path
base_path = os.path.abspath(os.path.dirname(__file__))
@@ -14,12 +13,15 @@ def configuration(parent_package='', top_path=None):
# This function tries to create C files from the given .pyx files. If
# it fails, we build the checked-in .c files.
cython(['spath.pyx'], working_path=base_path)
cython(['trace_path.pyx'], working_path=base_path)
cython(['_spath.pyx'], working_path=base_path)
cython(['_mcp.pyx'], working_path=base_path)
cython(['heap.pyx'], working_path=base_path)
config.add_extension('spath', sources=['spath.c'],
config.add_extension('_spath', sources=['_spath.c'],
include_dirs=[get_numpy_include_dirs()])
config.add_extension('trace_path', sources=['trace_path.c'],
config.add_extension('_mcp', sources=['_mcp.c'],
include_dirs=[get_numpy_include_dirs()])
config.add_extension('heap', sources=['heap.c'],
include_dirs=[get_numpy_include_dirs()])
return config
@@ -32,4 +34,4 @@ if __name__ == '__main__':
url = 'http://stefanv.github.com/scikits.image/',
license = 'Modified BSD',
**(configuration(top_path='').todict())
)
)
-3
View File
@@ -1,3 +0,0 @@
cimport numpy as np
cpdef shortest_path(np.ndarray, int reach=?)
-82
View File
@@ -1,82 +0,0 @@
# -*- python -*-
import numpy as np
cimport numpy as np
cdef extern from "math.h":
double fabs(double f)
cpdef shortest_path(np.ndarray arr, int reach=1):
"""Find the shortest left-to-right path through an array.
Parameters
----------
arr : (M, N) ndarray of float64
reach : int, optional
By default (``reach = 1``), the shortest path can only move
one row up or down for every column it moves forward (i.e.,
the path gradient is limited to 1). `reach` defines the
number of rows that can be skipped at each step.
Returns
-------
p : ndarray of int
For each column, give the row-coordinate of the
shortest path.
cost : float
Cost of path. This is the absolute sum of all the
differences along the path.
"""
if arr.ndim != 2:
raise ValueError("Expected 2-D array as input")
cdef np.ndarray[np.double_t, ndim=2] data = \
np.ascontiguousarray(arr, dtype=np.double)
cdef int M = arr.shape[0]
cdef int N = arr.shape[1]
cdef np.ndarray[np.int_t, ndim=2] node = \
np.empty((M, N), dtype=int)
cdef np.ndarray[np.double_t, ndim=2] cost = \
np.empty((M, N), dtype=np.double)
cdef np.ndarray[np.int_t] out = np.empty((N,), dtype=int)
cdef int c, r, rb, r_min_node
cdef int r_bracket_min = 0, r_bracket_max = 0
cdef double delta0 = 0, delta1 = 0
cost[:, 0] = 0
for c in range(1, N):
for r in range(M):
r_bracket_min = r - reach
r_bracket_max = r + reach
if r_bracket_min < 0:
r_bracket_min = 0
if r_bracket_max > M - 1:
r_bracket_max = M - 1
node[r, c] = r_bracket_min
for rb in range(r_bracket_min, r_bracket_max + 1):
delta0 = fabs(data[r, c] - data[rb, c - 1])
delta1 = fabs(data[r, c] - data[node[r, c], c - 1])
if delta0 < delta1:
node[r, c] = rb
cost[r, c] = cost[node[r, c], c - 1] + \
fabs(data[r, c] - data[node[r, c], c - 1])
# Find minimum cost path
r_min_node = cost[:,-1].argmin()
# Backtrack
out[N - 1] = r_min_node
for c in range(N - 1, 0, -1):
out[c - 1] = node[out[c], c]
return out, cost[r_min_node, N - 1]
@@ -1,71 +0,0 @@
import numpy as np
from numpy.testing import *
from scikits.image.graph import trace_path
a = np.ones((8,8), dtype=np.float32)
a[1:-1, 1] = 0
a[1, 1:-1] = 0
## array([[ 1., 1., 1., 1., 1., 1., 1., 1.],
## [ 1., 0., 0., 0., 0., 0., 0., 1.],
## [ 1., 0., 1., 1., 1., 1., 1., 1.],
## [ 1., 0., 1., 1., 1., 1., 1., 1.],
## [ 1., 0., 1., 1., 1., 1., 1., 1.],
## [ 1., 0., 1., 1., 1., 1., 1., 1.],
## [ 1., 0., 1., 1., 1., 1., 1., 1.],
## [ 1., 1., 1., 1., 1., 1., 1., 1.]], dtype=float32)
def test_basic():
costs, return_path = trace_path(a, (1, 6), [(7, 2)])
assert_array_equal(costs,
[[ 1., 1., 1., 1., 1., 1., 1., 1.],
[ 1., 0., 0., 0., 0., 0., 0., 1.],
[ 1., 0., 1., 1., 1., 1., 1., 1.],
[ 1., 0., 1., 2., 2., 2., 2., 2.],
[ 1., 0., 1., 2., 3., 3., 3., 3.],
[ 1., 0., 1., 2., 3., 4., 4., 4.],
[ 1., 0., 1., 2., 3., 4., 5., 5.],
[ 1., 1., 1., 2., 3., 4., 5., 6.]])
assert_array_equal(return_path,
[[(1, 6),
(1, 5),
(1, 4),
(1, 3),
(1, 2),
(2, 1),
(3, 1),
(4, 1),
(5, 1),
(6, 1),
(7, 2)]])
def test_no_diagonal():
costs, path = trace_path(a, (1, 6), [(7, 2)], diagonal_steps=False)
assert_array_equal(costs,
[[ 2., 1., 1., 1., 1., 1., 1., 2.],
[ 1., 0., 0., 0., 0., 0., 0., 1.],
[ 1., 0., 1., 1., 1., 1., 1., 2.],
[ 1., 0., 1., 2., 2., 2., 2., 3.],
[ 1., 0., 1., 2., 3., 3., 3., 4.],
[ 1., 0., 1., 2., 3., 4., 4., 5.],
[ 1., 0., 1., 2., 3., 4., 5., 6.],
[ 2., 1., 2., 3., 4., 5., 6., 7.]])
assert_array_equal(path,
[[(1, 6),
(1, 5),
(1, 4),
(1, 3),
(1, 2),
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1),
(6, 1),
(6, 2),
(7, 2)]])
if __name__ == "__main__":
run_module_suite()
-151
View File
@@ -1,151 +0,0 @@
# -*- python -*-
import numpy as numpy
cimport numpy as numpy
cimport cython
@cython.boundscheck(False)
def trace_path(numpy.ndarray[numpy.float32_t, ndim=2] costs not None,
start, ends, diagonal_steps=True):
"""Find the lowest-cost path from the start point to each given end point.
Costs are given by the input array: a move onto any given position in the
costs array adds that cost to the path. Paths may be constrained to
vertical and horizontal moves only by passing False for the diagonal_steps
parameter. Costs must be non-negative!
The array of cumulative costs from the starting point, and a list of paths
from the start to each end point are returned.
Parameters
----------
costs : ndarray
start : tuple of ints
``(x, y)`` position (i.e., ``(column, row)``) of starting position.
ends : list of tuple of ints
``[(x1, y1), (x2, y2), ...]`` List of end points.
diagonal_steps : bool
Whether to allow diagonal steps (True, by default).
Notes
-----
Paths are found by (more or less) breadth-first search outward from the
starting point: each time a lower-cost route to a given pixel is found, that
pixel is marked "active"; the neighbors of all active pixels are then
examined to see if their costs can be lowered as well. This continues until
no pixels are marked active.
"""
if costs.min() < 0:
raise ValueError("All costs must be non-negative.")
try:
a, b = start
except:
raise ValueError("The start point must be an (x, y) pair")
if not (0 <= a < costs.shape[0] and 0 <= b < costs.shape[1]):
raise ValueError("The start point must fall within the array")
for end in ends:
try:
a, b = end
except:
raise ValueError("All end points must be (x, y) pairs")
if not (0 <= a < costs.shape[0] and 0 <= b < costs.shape[1]):
raise ValueError("The end points must fall within the array")
cdef numpy.ndarray[numpy.float32_t, ndim=2] cumulative_costs = \
numpy.empty_like(costs)
cumulative_costs.fill(numpy.inf)
cumulative_costs[start] = 0
costs_shape = (costs.shape[0], costs.shape[1])
cdef numpy.ndarray[numpy.uint8_t, ndim=2] active_nodes = \
numpy.zeros(costs_shape, dtype=numpy.uint8)
active_nodes[start] = 1
cdef numpy.ndarray[numpy.uint8_t, ndim=2] parent_nodes = \
numpy.empty(costs_shape, dtype=numpy.uint8)
parent_nodes.fill(255)
cdef numpy.ndarray[numpy.int8_t, ndim=2] offsets
if diagonal_steps:
offsets = numpy.array([[-1, -1],
[-1, 0],
[-1, 1],
[ 0, -1],
[ 0, 1],
[ 1, -1],
[ 1, 0],
[ 1, 1]], dtype=numpy.int8)
else:
offsets = numpy.array([[-1, 0],
[0, -1],
[0, 1],
[1, 0]], dtype=numpy.int8)
cdef Py_ssize_t x, y, ox, oy, xo, yo, i
cdef Py_ssize_t a_xmax, a_xmin, a_ymax, a_ymin, tmp_xmax, \
tmp_xmin, tmp_ymax, tmp_ymin
cdef unsigned int xmax, ymax, active, num_steps
xmax = costs.shape[0] - 1
ymax = costs.shape[1] - 1
num_steps = 0
tmp_xmax = tmp_xmin = start[0]
tmp_ymax = tmp_ymin = start[1]
cdef float current_cost, current_cumulative_cost, cumulative_cost, new_cost
while True:
active = 0
# iterate over array
for x in range(0, xmax + 1):
for y in range(0, ymax + 1):
if active_nodes[x, y]:
active_nodes[x, y] = 0
active = 1
current_cumulative_cost = cumulative_costs[x, y]
# iterate over offsets
for i in range(8):
ox = offsets[i, 0]
oy = offsets[i, 1]
xo = x + ox
yo = y + oy
if xo < 0 or xo > xmax or yo < 0 or yo > ymax:
continue
current_cost = costs[xo, yo]
new_cost = current_cost + current_cumulative_cost
# if a cheaper path to a given point is found,
# activate that point
if cumulative_costs[xo, yo] > new_cost:
cumulative_costs[xo, yo] = new_cost
parent_nodes[xo, yo] = i
active_nodes[xo, yo] = 1
if not active:
break
cdef unsigned int startx, starty
startx = start[0]
starty = start[1]
return_paths = []
# Trace the paths from the endpoints to the start
for end in ends:
path = None
x = end[0]
y = end[1]
if cumulative_costs[x, y] != numpy.inf:
path = [(x, y)]
while not (x == startx and y == starty):
i = parent_nodes[x, y]
ox = offsets[i, 0]
oy = offsets[i, 1]
x -= ox
y -= oy
path.append((x, y))
path.reverse()
return_paths.append(path)
return cumulative_costs, return_paths