有没有比Bresenham的线算法更好的方法来找到曲线上的点

时间:2016-04-18 14:49:59

标签: python algorithm cython

我有一张旅行时间图,我希望得到从源到接收器的最短路径的整数点。

我现在的解决方案是我从接收器位置进行runge-kutta集成并得到一系列浮点数。然后我每隔5个或一些点进行采样,并假设它之间是一条直线,以便使用Bresenham的线算法。通过这种方法,我将得到整数点。

然而,这还不够快。因为我需要计算很多接收器的最短路径,所以时间总和会非常大。

我使用line_profiler来分析耗时,这表明时间的主要部分是函数ruge-kutta及其调用函数get_velocity

代码在

之下
def optimal_path_2d(gradx_interp,
                    grady_interp,
                    starting_point,
                    dx,
                    N=100):
    """
    Find the optimal path from starting_point to the zero contour
    of travel_time. dx is the grid spacing 
    Solve the equation x_t = - grad t / | grad t |
    """

    def get_velocity(position):
        """ return normalized velocity at pos """
        x, y = position
        vel = np.array([gradx_interp(y, x)[0][0], grady_interp(y, x)[0][0]])
        return vel / np.linalg.norm(vel)


    def runge_kutta(pos, ds):
        """ Fourth order Runge Kutta point update """
        k1 = ds * get_velocity(pos)
        k2 = ds * get_velocity(pos - k1 / 2.0)
        k3 = ds * get_velocity(pos - k2 / 2.0)
        k4 = ds * get_velocity(pos - k3)
        return pos - (k1 + 2 * k2 + 2 * k3 + k4) / 6.0

    x = runge_kutta(starting_point, dx)
    xl, yl = [], []
    for i in range(N):
        xl.append(x[0])
        yl.append(x[1])
        x = runge_kutta(x, dx)
        distance = ((x[0] - xl[-1])**2 +
                    (x[1] - yl[-1])**2)**0.5
        if distance < dx*0.9:
            break
    return yl, xl

def get_curve(x_curve, y_curve, num_interval):
    """Curve Algorithm based on Bresenham's Line Algorithm
    Produces a list of tuples 
    """
    num = len(x_curve)
    if num < num_interval:
        print("num_interval is too large.")
    ret_set = set()
    x0 = x_curve[0]
    y0 = y_curve[0]
    for i in range(num_interval, num, num_interval):
        x1 = x_curve[i]
        y1 = y_curve[i]
        points_on_line = get_line((x0, y0), (x1, y1))
        ret_set.update(points_on_line)
        x0 = x1
        y0 = y1
    if num % num_interval != 0:
        n = int(num/num_interval)*num_interval
        x0 = x_curve[n]
        y0 = y_curve[n]
        x1 = x_curve[-1]
        y1 = y_curve[-1]
        points_on_line = get_line((x0, y0), (x1, y1))
        ret_set.update(points_on_line)
    return list(ret_set)


def get_line(start, end):
    """modifed version of Bresenham's Line Algorithm
    Produces a list of tuples from start and end

    >>> points1 = get_line((0, 0), (3, 4))
    >>> points2 = get_line((3, 4), (0, 0))
    >>> assert(set(points1) == set(points2))
    >>> print points1
    [(0, 0), (1, 1), (1, 2), (2, 3), (3, 4)]
    >>> print points2
    [(3, 4), (2, 3), (1, 2), (1, 1), (0, 0)]
    """
    # Setup initial conditions
    x1, y1 = (int(x) for x in start)
    x2, y2 = (int(x) for x in end)
    dx = x2 - x1
    dy = y2 - y1

    # Determine how steep the line is
    is_steep = abs(dy) > abs(dx)

    # Rotate line
    if is_steep:
        x1, y1 = y1, x1
        x2, y2 = y2, x2

    # Swap start and end points if necessary and store swap state
    swapped = False
    if x1 > x2:
        x1, x2 = x2, x1
        y1, y2 = y2, y1
        swapped = True

    # Recalculate differentials
    dx = x2 - x1
    dy = y2 - y1

    # Calculate error
    error = int(dx / 2.0)
    ystep = 1 if y1 < y2 else -1

    # Iterate over bounding box generating points between start and end
    y = y1
    points = []
    for x in range(x1, x2 + 1):
        coord = (y, x) if is_steep else (x, y)
        points.append(coord)
        error -= abs(dy)
        if error < 0:
            y += ystep
            error += dx

    # Reverse the list if the coordinates were swapped
    if swapped:
        points.reverse()
    return points


nx = 100
ny = 100
num_interval = 5
loc_src = (10, 10)
loc_rec = (70, 90)
coordx = np.arange(nx)
coordy = np.arange(ny)
X, Y = np.meshgrid(coordx, coords)
travel_time = (X-loc_src[0])**2/5 + (Y-loc_src[1])**2/10  # for simplicity
grad_t_y, grad_t_x = np.gradient(travel_time, dx)
if isinstance(travel_time, np.ma.MaskedArray):
    grad_t_y[grad_t_y.mask] = 0.0
    grad_t_y = grad_t_y.data
    grad_t_x[grad_t_x.mask] = 0.0
    grad_t_x = grad_t_x.data

gradx_interp = RectBivariateSpline(coordy, coordx, grad_t_x)
grady_interp = RectBivariateSpline(coordy, coordx, grad_t_y)
yl, xl = optimal_path(gradx_interp, grady_interp, loc_rec, dx)
grid_indx = get_curve(xl, yl, num_interval)

我听说Cython会更快,然后我最近学到了一点并尝试一下。结果只比上面的代码快2,因为我是Cython的新手。下面的代码不完整,我只是编写它进行测试。

import numpy as np
from numpy.core.umath_tests import inner1d

def func(X_interp, Y_interp):

    def get_velocity(double x, double y ):
        """ return normalized velocity at pos """
        cdef double vel[2], norm
        a = X_interp(y, x)
        vel[0] = a[0][0]
        b = Y_interp(y, x)
        vel[1] = b[0][0]
        # norm = (vel[0]**2 + vel[1]**2)**0.5
        # vel[0] = vel[0]/norm
        # vel[1] = vel[1]/norm
        return vel


    def runge_kutta(double x, double y, double ds):
        """ Fourth order Runge Kutta point update """
        cdef double k1[2], k2[2], k3[2], k4[2], r[2], pos[2]
        pos[0] = x; pos[1] = y
        k1 = get_velocity(pos[0], pos[1])
        k2 = get_velocity(pos[0] - k1[0]/2.0*ds,pos[1] - k1[1]/2.0*ds)
        k3 = get_velocity(pos[0] - k2[0]/2.0*ds,pos[1] - k2[1]/2.0*ds)
        k4 = get_velocity(pos[0] - k3[0]/2.0*ds,pos[1] - k3[1]/2.0*ds)
        cdef size_t i
        for i in range(2):
            r[i] = pos[i] - ds * (k1[i] + 2*k2[i] + 2*k3[i] + k4[i])/6.0
        return r

    for i in range(50):
        runge_kutta(0, 0, 1.)
    # print(runge_kutta(0, 0, 1.))

enter image description here

0 个答案:

没有答案