rdp icon indicating copy to clipboard operation
rdp copied to clipboard

unoptimized for loop

Open kkonevets opened this issue 7 years ago • 5 comments

This code works MUCH faster. It does not use for loop, instead it uses numpy vectorization

import numpy as np


def line_dists(points, start, end):
    if np.all(start == end):
        return np.linalg.norm(points - start, axis=1)

    vec = end - start
    cross = np.cross(vec, start - points)
    return np.divide(abs(cross), np.linalg.norm(vec))


def rdp(M, epsilon=0):
    M = np.array(M)
    start, end = M[0], M[-1]
    dists = line_dists(M, start, end)

    index = np.argmax(dists)
    dmax = dists[index]

    if dmax > epsilon:
        result1 = rdp(M[:index + 1], epsilon)
        result2 = rdp(M[index:], epsilon)

        result = np.vstack((result1[:-1], result2))
    else:
        result = np.array([start, end])

    return result

kkonevets avatar Mar 15 '17 08:03 kkonevets

I can confirm that it works faster than the pure python version for me as well.

JustasB avatar May 05 '18 02:05 JustasB

@kkonevets I can't figure out how to convert your code to work on 3D data.. Is it possible to do something similar with 3D data?

soichih avatar Oct 24 '18 13:10 soichih

Dear all,

is there a way to limit number of simplified points together with max distance (epsilon)? Or to be more precise, to pick only subset of N the most relevant points for given dmax.

Thanks in advance!

Cheers, Ivica

ivicajan avatar Nov 29 '18 08:11 ivicajan

import numpy as np

def line_dists2D(points, start, end):
    if np.all(start == end):
        return np.linalg.norm(points - start, axis=1)

    vec = end - start
    cross = np.cross(vec, start - points)
    return np.divide(abs(cross), np.linalg.norm(vec))


# https://stackoverflow.com/questions/56463412/distance-from-a-point-to-a-line-segment-in-3d-python
def lineseg_dist3D(p: np.ndarray, a: np.ndarray, b: np.ndarray):

    # normalized tangent vector
    d = np.divide(b - a, np.linalg.norm(b - a))

    # signed parallel distance components
    s = np.dot(a - p, d)
    t = np.dot(p - b, d)

    # clamped parallel distance
    h = np.maximum.reduce([s, t, np.zeros(len(p))])

    # perpendicular distance component
    c = np.cross(p - a, d)

    res = np.hypot(h, np.linalg.norm(c, axis=1))
    res[res < np.finfo(np.float64).eps] = 0

    return res


def rdp(M, epsilon=0):
    M = np.array(M)
    start, end = M[0], M[-1]
    if M.shape[1] == 2:
        dists = line_dists2D(M, start, end)
    elif M.shape[1] == 3:
        dists = lineseg_dist3D(M, start, end)
    else:
        raise ValueError("point dimension must be 2d or 3d")
    index = np.argmax(dists)
    dmax = dists[index]

    if dmax == np.nan:
        raise ValueError

    if dmax > epsilon:
        result1 = rdp(M[: index + 1], epsilon)
        result2 = rdp(M[index:], epsilon)

        result = np.vstack((result1[:-1], result2))
    else:
        result = np.array([start, end])

    return result

  points = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4]).reshape(-1, 3)
  points = np.array([1, 1, 2, 2, 3, 3, 4, 4, 3, 3, 2, 3]).reshape(-1, 2)
  print(rdp(points))

@soichih I tried to make a sample on 3d Points, i think this is working

edit: fixed close to 0 and linalg norm over rows

darwinharianto avatar Jun 03 '22 05:06 darwinharianto

这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。

star-plu-cn-sk2 avatar Jun 03 '22 05:06 star-plu-cn-sk2