diff options
| author | Lex Neva <github.com@lexneva.name> | 2023-02-17 21:13:13 -0500 |
|---|---|---|
| committer | Lex Neva <github.com@lexneva.name> | 2023-02-20 15:27:56 -0500 |
| commit | 9ccf8b9b7780b997c1f801a87dafd99f86f048a1 (patch) | |
| tree | 75619e002dbf68dde0b9c2c2fd00858ec49bfd4b /lib/utils/geometry.py | |
| parent | 3da70348b03d8d40ef71d2f515bb9f179977e693 (diff) | |
better smoothing algorithm
Diffstat (limited to 'lib/utils/geometry.py')
| -rw-r--r-- | lib/utils/geometry.py | 96 |
1 files changed, 4 insertions, 92 deletions
diff --git a/lib/utils/geometry.py b/lib/utils/geometry.py index e7fd5b76..8f34c467 100644 --- a/lib/utils/geometry.py +++ b/lib/utils/geometry.py @@ -7,8 +7,6 @@ import math from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon, MultiPoint, GeometryCollection from shapely.geometry import Point as ShapelyPoint -from scipy.interpolate import splprep, splev -import numpy as np def cut(line, distance, normalized=False): @@ -149,96 +147,6 @@ def cut_path(points, length): return [Point(*point) for point in subpath.coords] -def _remove_duplicate_coordinates(coords_array): - """Remove consecutive duplicate points from an array. - - Arguments: - coords_array -- numpy.array - - Returns: - a numpy.array of coordinates, minus consecutive duplicates - """ - - differences = np.diff(coords_array, axis=0) - zero_differences = np.isclose(differences, 0) - keepers = np.r_[True, np.any(zero_differences == False, axis=1)] # noqa: E712 - - return coords_array[keepers] - - -def _add_extra_points(coords): - """Add points at the start and end of the path. - - The spline-based smoothing in smooth_path sometimes makes a wild deviation at the - start or end. Adding 3 extra points almost identical to the start and end points - seems to avoid this. - """ - - direction = coords[1] - coords[0] - amount = direction * 0.001 - - start_points = [coords[0], coords[0] + amount, coords[0] + amount * 2, coords[0] + amount * 3] - - direction = coords[-2] - coords[-1] - amount = direction * 0.001 - - end_points = [coords[-1] + amount * 3, coords[-1] + amount * 2, coords[-1] + amount, coords[-1]] - - return np.concatenate((start_points, coords[1:-1], end_points), axis=0) - - -def smooth_path(path, smoothness=1.0): - """Smooth a path of coordinates. - - Arguments: - path -- an iterable of coordinate tuples or Points - smoothness -- float, how much smoothing to apply. Bigger numbers - smooth more. - - Returns: - A list of Points. - """ - - if smoothness == 0: - # s of exactly zero seems to indicate a default level of smoothing - # in splprep, so we'll just exit instead. - return path - - # splprep blows up on duplicated consecutive points with "Invalid inputs" - coords = _remove_duplicate_coordinates(np.array(path)) - num_points = len(coords) - - if num_points <= 3: - # splprep throws an error unless num_points > k - return path - - # s is explained in this issue: https://github.com/scipy/scipy/issues/11916 - # the smoothness parameter limits how much the smoothed path can deviate - # from the original path. The standard deviation of the distance between - # the smoothed path and the original path is equal to the smoothness. - # In practical terms, if smoothness is 1mm, then the smoothed path can be - # up to 1mm away from the original path. - s = num_points * (smoothness ** 2) - - coords = _add_extra_points(coords) - - # .T transposes the array (for some reason splprep expects - # [[x1, x2, ...], [y1, y2, ...]] - tck, fp, ier, msg = splprep(coords.T, s=s, k=3, nest=-1, full_output=1) - if ier > 0: - from ..debug import debug - debug.log(f"error {ier} smoothing path: {msg}") - return path - - # Evaluate the spline curve at many points along its length to produce the - # smoothed point list. 2 * num_points seems to be a good number, but it - # does produce a lot of points. - smoothed_x_values, smoothed_y_values = splev(np.linspace(0, 1, num_points * 2), tck[0]) - coords = np.array([smoothed_x_values, smoothed_y_values]).T - - return [Point(x, y) for x, y in coords] - - class Point: def __init__(self, x: float, y: float): self.x = x @@ -333,3 +241,7 @@ class Point: def line_string_to_point_list(line_string): return [Point(*point) for point in line_string.coords] + + +def coordinate_list_to_point_list(coordinate_list): + return [Point.from_tuple(coords) for coords in coordinate_list] |
