Учитывая три точки [(x1, y1), (x2, y2), (x3, y3)]
, средняя точка (x2, y2)
может быть удалена, если наклоны последовательных отрезков совпадают. Т.е., если (y2 - y1) / (x2 - x1) == (y3 - y2) / (x3 - x2)
. Чтобы избежать возможности деления на ноль и устранить любую ошибку округления, условие устранения средней точки можно умножить на (x3 - x2) * (y2 - y1) == (x2 - x1) * (y3 - y2)
.
Допустим, у вас есть пустой массив в качестве пути:
path = [(1, 1), (1, 2), (1, 3), (2, 3), (2, 4), (2, 5)]
path = np.array(path)
Вы можете вычислить удерживающую маску следующим образом:
delta = np.diff(path, axis=0)
prod = delta[:-1, :] * delta[1:, ::-1]
diff
- это вектор из двух столбцов (x2 - x1), (y2 - y1)
. prod
затем становится вектором из двух столбцов, содержащим компоненты условия исключения. Вы можете сделать маску из этого:
mask = (prod[:, 0] != prod[:, 1])
и применить маску:
smoothed = np.concatenate((
path[np.newaxis, 0, :],
path[1:-1, :][mask, :],
path[np.newaxis, -1, :]), axis=0)
Объединение необходимо, потому что маска содержит len(path) - 2
элементов, как и следовало ожидать. Конечные точки гарантированно присутствуют в выходных данных. Таким образом, вам приходится конкатенировать первый ряд, замаскированную часть и последний ряд. Вставка np.newaxis
в индекс гарантирует, что срезы строк получаются 2D-векторами строк, а не одномерными массивами.
Результат
[[1 1]
[1 3]
[2 3]
[2 5]]
Вот ссылка IDEOne с демонстрацией: https://ideone.com/rykLCz
Если вам нужно преобразовать обратно в список:
smoothed = list(map(tuple, smoothed))