道格拉斯-普克算法 python实现

 道格拉斯-普克算法(Douglas–Peucker algorithm)

将曲线近似表示为一系列点,并减少点的数量的一种算法。它的优点是具有平移和旋转不变性,给定曲线与阈值后,抽样结果一定。可用于矢量地图数字水印算法

算法的基本思路是:对每一条曲线的首末点虚连一条直线,求所有点与直线的距离,并找出最大距离值dmax ,用dmax与限差D相比:若dmax<D,这条曲线上的中间点全部舍去;若dmax ≥D,保留dmax 对应的做标点,并以该点为界,把曲线分为两部分,对这两部分重复使用该方法

 

 

以下为Python代码实现

输入:坐标数组以及阈值

返回:一个要素中的特征点以及对应的索引

import math
from decimal import Decimal


# 道格拉斯普克算法 输出一条直线上的特征点以及对应的索引
def point_to_line_Distance(point_a, point_b, point_c):
    """
    计算点 a 到点 b 和点 c 所在直线的垂直距离
    :param point_a: 要计算距离的点
    :param point_b: 直线的一端点
    :param point_c: 直线的另一端点
    :return: 垂直距离
    """
    if point_b.all() == point_c.all():  # 若直线两端点为同一点,则范围点与直线其中一端点的距离
        distance = Decimal(math.sqrt((point_b[0] - point_a[0]) ** 2 + (point_b[1] - point_a[1]) ** 2))
    elif point_b[0] == point_c[0]:  # 若直线为垂线
        distance = Decimal(abs(point_a[0] - point_b[0]))
    else:
        slope = Decimal(point_b[1] - point_c[1]) / Decimal(point_b[0] - point_c[0])
        intercept = Decimal(point_b[1]) - slope * Decimal(point_b[0])
        distance = abs(slope * Decimal(point_a[0]) - Decimal(point_a[1]) + intercept) / Decimal(
            math.sqrt(1 + slope ** 2))
    return distance


def douglas_peuker(point_list, threshold, lowerLimit=2, ceiling=None):
    """
    道格拉斯-普克算法用于线简化
    :param point_list: 点列表 形似[[0, 0], [1, 2], [2, 1], [3, 5], [4, 0], [0, 0]]
    :param threshold: 简化的距离阈值
    :param lowerLimit: 简化后点的最小数量
    :param ceiling: 简化后点的最大数量
    :return: 简化后的点列表及其索引
    """

    def diluting(points, current_disqualify_indices, threshold, qualify_points, disqualify_points, qualify_indices,
                 disqualify_indices):
        if len(points) < 3:
            qualify_points.extend(points[::-1])
            qualify_indices.extend(current_disqualify_indices[::-1])
        else:
            max_distance_index, max_distance = 0,0
            for index, point in enumerate(points):
                if index in [0, len(points) - 1]:
                    continue
                distance = point_to_line_Distance(point, points[0], points[-1])
                if distance > max_distance:
                    max_distance_index = index
                    max_distance = distance
            if max_distance < threshold:
                qualify_points.append(points[-1])
                qualify_points.append(points[0])
                qualify_indices.append(current_disqualify_indices[-1])
                qualify_indices.append(current_disqualify_indices[0])
            else:
                sequence_a = points[:max_distance_index]
                sequence_b = points[max_distance_index:]
                indices_a = current_disqualify_indices[:max_distance_index]
                indices_b = current_disqualify_indices[max_distance_index:]

                disqualify_points.append(sequence_a)
                disqualify_points.append(sequence_b)
                disqualify_indices.append(indices_a)
                disqualify_indices.append(indices_b)

        return qualify_points, disqualify_points, qualify_indices, disqualify_indices

    def get_qualify_list(points, threshold):
        qualify_points = []
        disqualify_points = [points]
        qualify_indices = []
        disqualify_indices = [list(range(len(points)))]

        while len(disqualify_indices) > 0:
            current_disqualify_points = disqualify_points.pop()
            current_disqualify_indices = disqualify_indices.pop()
            if not isinstance(current_disqualify_indices, list):
                current_disqualify_indices = [current_disqualify_indices]
            qualify_points, disqualify_points, qualify_indices, disqualify_indices = diluting(
                current_disqualify_points, current_disqualify_indices, threshold, qualify_points, disqualify_points,
                qualify_indices,
                disqualify_indices)

        return qualify_points, qualify_indices

    if len(point_list) < 5:
        return point_list, list(range(len(point_list)))

    result, indices = get_qualify_list(point_list, threshold)

    if len(result) < lowerLimit:
        while len(result) < lowerLimit:
            threshold *= 0.9
            result, indices = get_qualify_list(point_list, threshold)

    if ceiling and len(result) > ceiling:
        while len(result) > ceiling:
            threshold *= 1.1
            result, indices = get_qualify_list(point_list, threshold)

    if len(result) > len(point_list):
        return point_list, list(range(len(point_list)))

    return result, indices


if __name__ == '__main__':
    # 测试示例
    point_list = [[0, 0], [1, 2], [2, 1], [3, 5], [4, 0], [0, 0]]
    threshold = 4
    result, indices = douglas_peuker(point_list, threshold)
    print("简化后的点:", result)
    print("索引:", indices)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值