道格拉斯-普克算法 - 把一堆復雜的線條變得簡單,同時盡量保持原來的樣子

道格拉斯-普克算法 - 把一堆復雜的線條變得簡單,同時盡量保持原來的樣子

flyfish

道格拉斯-普克算法(Douglas-Peucker Algorithm解決的問題其實很日常:把一堆復雜的線條(比如地圖上的道路、河流,或者GPS記錄的軌跡)變得簡單,同時盡量保持原來的樣子。

舉個例子,假設用GPS記錄了一條徒步路線,每走1米就記一個點,最后生成了1000個點的折線。但其實很多相鄰的點幾乎在一條直線上,完全沒必要都保留——存起來占空間,畫出來也累贅。這時候這個算法就派上用場了:它能自動刪掉那些“多余”的點,比如直線段中間的點,只留下關鍵的拐點,讓線條變簡單,但看起來還是你走的那條路。

大概是上世紀70年代初。1972年有個叫烏爾斯·拉默的人先提出了類似思路,1973年道格拉斯和普克兩個人又完善了這個方法,所以后來就用他們的名字命名了。

在這里插入圖片描述

import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import io# 設置中文字體,確保中文正常顯示
plt.rcParams["font.family"] = ["SimHei", "sans-serif"]
plt.rcParams['axes.unicode_minus'] = False  # 解決負號顯示問題def point_to_segment_dist(point, start, end):"""計算點到線段的垂直距離"""if np.allclose(start, end):return np.linalg.norm(point - start)# 計算線段的單位向量line_vec = end - startline_len = np.linalg.norm(line_vec)unit_line_vec = line_vec / line_len# 計算從起點到點的向量point_vec = point - start# 計算投影長度projection_length = np.dot(point_vec, unit_line_vec)# 如果投影長度超出線段范圍,則計算到端點的距離if projection_length < 0:return np.linalg.norm(point - start)elif projection_length > line_len:return np.linalg.norm(point - end)# 計算投影點projection = start + projection_length * unit_line_vec# 計算點到投影點的距離return np.linalg.norm(point - projection)def douglas_peucker(points, epsilon, frames=None):"""道格拉斯-普克算法實現,并記錄每一步的處理過程用于可視化參數:points: 待簡化的點集epsilon: 距離閾值frames: 存儲每一步處理結果的列表"""if len(points) < 3:if frames is not None:frames.append({'points': points.copy(),'start_idx': 0,'end_idx': len(points) - 1,'max_dist_idx': None,'is_terminal': True})return pointsstart_point = points[0]end_point = points[-1]# 計算所有中間點到線段的距離distances = []for i in range(1, len(points) - 1):dist = point_to_segment_dist(points[i], start_point, end_point)distances.append((dist, i))if not distances:if frames is not None:frames.append({'points': points.copy(),'start_idx': 0,'end_idx': len(points) - 1,'max_dist_idx': None,'is_terminal': True})return np.array([start_point, end_point])# 找到最大距離的點max_dist, max_dist_idx = max(distances, key=lambda x: x[0])# 記錄當前步驟if frames is not None:frames.append({'points': points.copy(),'start_idx': 0,'end_idx': len(points) - 1,'max_dist_idx': max_dist_idx if max_dist > epsilon else None,'is_terminal': max_dist <= epsilon})# 如果最大距離大于閾值,則遞歸處理if max_dist > epsilon:left_points = points[:max_dist_idx + 1]right_points = points[max_dist_idx:]left_simplified = douglas_peucker(left_points, epsilon, frames)right_simplified = douglas_peucker(right_points, epsilon, frames)# 合并結果(去掉重復的分割點)return np.vstack([left_simplified[:-1], right_simplified])else:# 所有點都足夠接近線段,直接返回起點和終點return np.array([start_point, end_point])def create_frames(points, epsilon):"""創建算法執行過程的幀列表"""frames = []douglas_peucker(points, epsilon, frames)return framesdef draw_frame(frame, frames, points, epsilon, step_num, total_steps, figsize=(10, 6)):"""繪制單個幀"""fig, (ax1, ax2) = plt.subplots(1, 2, figsize=figsize)# 左側子圖:當前處理的線段ax1.set_title("當前處理的線段")ax1.set_xlabel("X")ax1.set_ylabel("Y")ax1.set_xlim(points[:, 0].min() - 1, points[:, 0].max() + 1)ax1.set_ylim(points[:, 1].min() - 1, points[:, 1].max() + 1)# 繪制原始曲線(半透明)ax1.plot(points[:, 0], points[:, 1], 'b-', alpha=0.3, label='原始曲線')# 繪制當前處理的線段current_points = frame['points']start_idx = frame['start_idx']end_idx = frame['end_idx']start_point = current_points[start_idx]end_point = current_points[end_idx]ax1.plot([start_point[0], end_point[0]], [start_point[1], end_point[1]], 'r-', linewidth=2, label='當前線段')# 繪制最大距離點if frame['max_dist_idx'] is not None:max_dist_idx = frame['max_dist_idx']max_point = current_points[max_dist_idx]# 繪制最大距離點ax1.plot(max_point[0], max_point[1], 'go', markersize=8, label='最遠點')# 計算并繪制垂直線projection = compute_projection(max_point, start_point, end_point)ax1.plot([max_point[0], projection[0]], [max_point[1], projection[1]], 'g--', linewidth=1, label='垂直距離')# 顯示距離值dist = point_to_segment_dist(max_point, start_point, end_point)mid_x = (max_point[0] + projection[0]) / 2mid_y = (max_point[1] + projection[1]) / 2ax1.text(mid_x, mid_y, f'd={dist:.2f}', ha='center', va='bottom', bbox=dict(facecolor='white', alpha=0.8))ax1.legend()# 右側子圖:累積簡化結果ax2.set_title("累積簡化結果")ax2.set_xlabel("X")ax2.set_ylabel("Y")ax2.set_xlim(points[:, 0].min() - 1, points[:, 0].max() + 1)ax2.set_ylim(points[:, 1].min() - 1, points[:, 1].max() + 1)# 繪制原始曲線(半透明)ax2.plot(points[:, 0], points[:, 1], 'b-', alpha=0.3, label='原始曲線')# 收集所有已處理的線段simplified_points = []# 從第一步到當前步,收集所有終端節點(即已處理完的線段)for f in frames[:step_num + 1]:if f['is_terminal']:p = f['points']simplified_points.append(p[0])simplified_points.append(p[-1])# 去重并排序(按X坐標)if simplified_points:simplified_points = np.array(simplified_points)_, idx = np.unique(simplified_points[:, 0], return_index=True)simplified_points = simplified_points[np.sort(idx)]# 繪制簡化后的曲線ax2.plot(simplified_points[:, 0], simplified_points[:, 1], 'r-', linewidth=2, label='簡化曲線')ax2.plot(simplified_points[:, 0], simplified_points[:, 1], 'ro', markersize=5)ax2.legend()# 添加標題和步驟信息if frame['is_terminal']:step_text = f"步驟 {step_num + 1}/{total_steps}: 所有點距離 ≤ ε,保留首尾點"else:step_text = f"步驟 {step_num + 1}/{total_steps}: 保留最遠點,分割曲線"fig.suptitle(f"道格拉斯-普克算法 (閾值 ε = {epsilon})", fontsize=14)plt.figtext(0.5, 0.01, step_text, ha="center", fontsize=12)# 保存當前幀為圖像buf = io.BytesIO()plt.savefig(buf, format='png', bbox_inches='tight')buf.seek(0)image = Image.open(buf)plt.close(fig)return imagedef compute_projection(point, start, end):"""計算點在直線上的投影"""if np.allclose(start, end):return startline_vec = end - startpoint_vec = point - startline_len_sq = np.sum(line_vec ** 2)# 計算投影系數t = np.dot(point_vec, line_vec) / line_len_sq# 限制投影在端點之間t = max(0, min(1, t))return start + t * line_vecdef create_gif(points, epsilon, output_path='douglas_peucker.gif', duration=1000):"""創建道格拉斯-普克算法執行過程的GIF動畫"""# 生成所有幀frames = create_frames(points, epsilon)# 繪制每一幀并保存為GIFimages = []for i, frame in enumerate(frames):image = draw_frame(frame, frames, points, epsilon, i, len(frames))images.append(image)# 保存為GIFimages[0].save(output_path,save_all=True,append_images=images[1:],duration=duration,loop=0  # 0表示無限循環)print(f"GIF動畫已保存至: {output_path}")return output_path# 示例:創建一個帶噪聲的正弦曲線并生成GIF
if __name__ == "__main__":# 生成示例數據np.random.seed(42)  # 設置隨機種子,確保結果可重現x = np.linspace(0, 10, 100)y = np.sin(x) + np.random.normal(0, 0.3, size=len(x))  # 添加隨機噪聲points = np.column_stack([x, y])# 設置閾值epsilon = 0.5# 創建GIF動畫create_gif(points, epsilon, output_path='douglas_peucker.gif', duration=1000)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/94234.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/94234.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/94234.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

團購商城 app 系統架構分析

一、引言 團購商城 APP 作為一種融合了電子商務與團購模式的應用程序&#xff0c;近年來在市場上取得了顯著的發展。它為用戶提供了便捷的購物體驗&#xff0c;同時也為商家創造了更多的銷售機會。一個完善且高效的系統架構是保障團購商城 APP 穩定運行、提供優質服務的基礎。本…

【AI平臺】n8n入門7:本地n8n更新

?0、前言 目標&#xff1a;本地n8n部署后&#xff0c;有新版本&#xff0c;然后進行更新。官方文檔&#xff1a;Docker | n8n Docs特別說明&#xff1a; n8n鏡像更新后&#xff0c;容器重建&#xff0c;所以之前在n8n配置的東西&#xff0c;就莫有了&#xff0c;工作流提前導…

還在使用Milvus向量庫?2025-AI智能體選型架構防坑指南

前言說明&#xff1a;數據來源&#xff1a;主要基于 Milvus&#xff08;v2.3&#xff09;和 Qdrant&#xff08;v1.8&#xff09;的最新穩定版&#xff0c;參考官方文檔、GitHub Issues、CNCF報告、以及第三方評測&#xff08;如DB-Engines、TechEmpower&#xff09;。評估原則…

3-verilog的使用-1

verilog的使用-1 1.判斷上升沿 reg s_d0; reg s_d1; wire signal_up ; //判斷信號的上升沿 assign signal_up (~touch_key_d1) & touch_key_d0; always (posedge clk or negedge rst_n) beginif(rst_n 1b0) begins_d0< 1b0;s_d1< 1b0;endelse begins_d0&…

ESXI虛擬交換機 + H3C S5120交換機 + GR5200路由器組網筆記

文章目錄一、組網拓撲與核心邏輯1. 拓撲結構2. 核心邏輯二、詳細規劃方案1. VLAN 與 IP 地址規劃2. 設備連接規劃三、配置步驟1. H3C S5120 交換機配置&#xff08;VLAN 與端口&#xff09;2. H3C GR5200 路由器配置&#xff08;路由、網關、NAT&#xff09;3. ESXi 虛擬交換機…

python的駕校培訓預約管理系統

前端開發框架:vue.js 數據庫 mysql 版本不限 后端語言框架支持&#xff1a; 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 數據庫工具&#xff1a;Navicat/SQLyog等都可以 該系統通…

webrtc弱網-QualityScaler 源碼分析與算法原理

一. 核心功能QualityScaler 是 WebRTC 中用于動態調整視頻編碼質量的模塊&#xff0c;主要功能包括&#xff1a;QP 監控&#xff1a;持續監測編碼器輸出的量化參數&#xff08;QP&#xff09;丟幀率分析&#xff1a;跟蹤媒體優化和編碼器導致的丟幀情況自適應決策&#xff1a;根…

Maven 快照(SNAPSHOT)

Maven 快照(SNAPSHOT) 引言 Maven 快照(SNAPSHOT)是 Maven 中的一個重要概念,主要用于版本管理。它允許開發者在構建過程中使用尚未發布的版本。本文將詳細介紹 Maven 快照的原理、用途以及如何在項目中配置和使用快照。 Maven 快照原理 Maven 快照是版本號的一部分,…

2025-0803學習記錄20——畢業論文快速整理成小論文

本科畢業論文寫好啦&#xff0c;但是C導要我整理成一篇約8000字的小論文&#xff0c;準備投稿。畢業論文到投稿的小論文&#xff0c;這其實是從“全景展示”到“聚焦精煉”的過程。目前我已經有完整的大論文&#xff08;約6萬字&#xff09;&#xff0c;材料是充足的&#xff0…

VUE2 學習筆記16 插槽、Vuex

插槽在編寫組件時&#xff0c;可能存在這種情況&#xff0c;頁面需要顯示不同的內容&#xff0c;但是頁面結構是類似的&#xff0c;在這種情況下&#xff0c;雖然也可以使用傳參來進行&#xff0c;但傳參時&#xff0c;還需要編寫props等邏輯&#xff0c;略顯重復&#xff0c;而…

IntelliJ IDEA開發編輯器摸魚看股票數據

在IDEA的插件市場中心搜索stock&#xff0c;檢索結果里面的插件&#xff0c;點擊安裝即可安裝后的效果

Linux Deepin深度操作系統應用商店加載失敗,安裝星火應用商店

Linux Deepin國產操作系統優點 Deepin&#xff08;原名Linux Deepin&#xff09;是一款由中國團隊開發的Linux發行版&#xff0c;基于Debian stable分支&#xff0c;以美觀易用的界面和本土化體驗著稱。以下是其核心優點總結&#xff1a; 1. 極致美觀的界面設計 Deepin Deskt…

postgresql創建只讀用戶并授權

postgresql創建只讀用戶并授權 CREATE USER yk WITH ENCRYPTED PASSWORD <your_password>;GRANT USAGE ON SCHEMA public to yk; GRANT SELECT ON ALL TABLES IN SCHEMA public TO yk;根據以上創建的用戶&#xff0c;出現一個問題&#xff0c;對新建的表沒有查詢權限&am…

pytest vs unittest: 區別與優缺點比較

主要區別特性pytestunittest起源第三方庫Python標準庫語法風格更簡潔的Pythonic語法基于Java風格的JUnit測試發現自動發現測試需要繼承TestCase類斷言方式使用Python原生assert使用各種assert方法(assertEqual等)夾具系統強大的fixture系統簡單的setUp/tearDown方法參數化測試內…

Boost.Asio學習(5):c++的協程

協程是什么&#xff1f;協程就是可以“暫停”和“繼續”的函數&#xff0c;像在函數里打個斷點&#xff0c;然后以后可以從斷點繼續運行&#xff0c;而不是重新開始。線程 vs 協程&#xff1a;類比想象你在寫小說&#xff1a;線程&#xff1a;你開了 3 個作者&#xff08;線程&…

Linux 中,命令查看系統版本和內核信息

在 Linux 中&#xff0c;可以通過以下命令查看系統版本和內核信息&#xff1a;1. 查看內核版本uname -a或精簡顯示&#xff1a;uname -r # 只顯示內核版本示例輸出&#xff1a;Linux ubuntu 5.4.0-135-generic #152-Ubuntu SMP Tue Nov 15 08:12:21 UTC 2022 x86_64 x86_64 x8…

數據結構總綱以及單向鏈表詳解:

以下是基于筆記更詳細的知識梳理&#xff0c;從概念到細節逐層拆解&#xff0c;幫你吃透數據結構核心要點&#xff1a; 數據結構部分的重點內容&#xff1a;一、數據結構基礎框架 &#xff08;一&#xff09;邏輯結構&#xff08;關注元素間“邏輯關系”&#xff09; 筆記里提到…

模型學習系列之參數

背景 “GLM-4.5擁有 3550 億總參數量&#xff0c;其中 320 億活躍參數&#xff1b;GLM-4.5-Air 采用更緊湊的設計&#xff0c;擁有 1060 億總參數量&#xff0c;其中 120 億活躍參數。” 定義與關系 總參數量&#xff1a;模型中所有可訓練參數的總和&#xff08;包括嵌入層、注…

[創業之路-535]:軟件需要原型驗證、產品需要原型驗證、商業模式也需要原型驗證

原型驗證在軟件、產品開發以及商業模式探索中均扮演著至關重要的角色&#xff0c;它通過低成本、快速迭代的方式&#xff0c;幫助團隊驗證核心假設、降低風險并優化方案。以下是針對這三個領域的具體分析&#xff1a;一、軟件原型驗證&#xff1a;從概念到可交互的模型核心目的…

sublime text2配置

sublime text2配置背景配置其他背景 之前下載了就把它當記事本在使用。但是&#xff0c;在使用過程中&#xff0c;有些場景很痛苦。如果說找一個字符串中的某一部分&#xff0c;雖然它通過了這個功能&#xff0c;但是不夠明顯&#xff0c;看瞎了。。。 配置 下面是我改的一些選…