Python實現RANSAC進行點云直線、平面、曲面、圓、球體和圓柱擬合

????????本節我們分享使用RANSAC算法進行點云的擬合。RANSAC算法是什么?不知道的同學們前排罰站!(前面有)

????????總的來說,RANSAC(Random Sample Consensus)是一種通用的迭代魯棒估計框架,無論擬合何種幾何模型,其思想完全一致: ?1. 隨機采樣 → 2. 最小樣本擬合 → 3. 內點判定 → 4. 模型評估 → 5. 迭代收斂 → 6. 輸出最優模型。 ?
下面按“直線、平面、一般二次曲面、二維圓、三維球面、圓柱面”六種情形,給出統一的實現步驟與關鍵公式,便于對照查閱。

--------------------------------
1. 擬合 3D 空間直線
--------------------------------
隨機采樣 ?
- 每次隨機抽取 2 個點 p?、p?(兩點確定一條直線)。

模型估計 ?
- 方向向量 v = (p? ? p?) / ‖p? ? p?‖ ?
- 直線方程:L(t) = p? + t v,t∈?

內點判定 ?
- 點 p 到直線的距離 ?
d = ‖(p ? p?) × v‖ ?
若 d < d_th,則為內點。

迭代終止 ?
- 最大迭代次數 N 或內點比例達到要求即可停止。

--------------------------------
2. 擬合 3D 平面
--------------------------------
隨機采樣 ?
- 每次隨機抽取 3 個不共線的點 p?,p?,p?。

模型估計 ?
- 平面法向量 n = (p? ? p?) × (p? ? p?) 并歸一化 ?
- 平面方程:n·(x ? p?)=0,即 ax+by+cz+d=0

內點判定 ?
- 點 p 到平面距離 ?
d = |a·x + b·y + c·z + d| / √(a2+b2+c2) ?
若 d < d_th,則為內點。

--------------------------------
3. 擬合一般二次曲面
--------------------------------
隨機采樣 ?
- 二次曲面一般式 Ax2+By2+Cz2+Dxy+Eyz+Fzx+Gx+Hy+Iz+J=0 共 10 個參數,因此最少需要 9 個點(秩缺 1,需額外約束 J=1 或 ‖參數‖=1)。

模型估計 ?
- 構造設計矩陣 A ∈ ?^{m×9} 和觀測向量 b ∈ ?^m,用最小二乘解參數向量 θ=[A,B,…,I]^T。 ?
- 得到隱式曲面方程 f(x,y,z)=0。

內點判定 ?
- 點 p 到隱式曲面的代數距離 |f(p)| / ‖?f(p)‖ 與閾值比較。

--------------------------------
4. 擬合 2D 圓
--------------------------------
隨機采樣 ?
- 每次隨機抽取 3 個非共線的 2D 點 (x?,y?),(x?,y?),(x?,y?)。

模型估計 ?
- 圓心 (a,b) 和半徑 r 的閉合公式 ?
a = [ (x?2+y?2?x?2?y?1)(y??y?) ? (x?2+y?2?x?2?y?2)(y??y?) ] / D ?
b = [ (x?2+y?2?x?2?y?2)(x??x?) ? (x?2+y?2?x?2?y?2)(x??x?) ] / D ?
D = 2[(x??x?)(y??y?) ? (x??x?)(y??y?)] ?
r = √[(x??a)2+(y??b)2]

內點判定 ?
- 點 (x,y) 到圓的距離 ?
|√[(x?a)2+(y?b)2] ? r| < d_th。

--------------------------------
5. 擬合 3D 球面
--------------------------------
隨機采樣 ?
- 每次隨機抽取 4 個非共面 3D 點 p?…p?。

模型估計 ?
- 球心 c 和半徑 R 的線性方程組 ?
‖p_i ? c‖2 = R2, i=1…4 → 4 線性方程 → 解 c、R。

內點判定 ?
- 點 p 到球面距離 ?
|‖p ? c‖ ? R| < d_th。

--------------------------------
6. 擬合 3D 圓柱面
--------------------------------
隨機采樣 ?
- 每次隨機抽取 5 個 3D 點(圓柱面有 7 個自由度,但 5 點可解唯一參數,見下)。

模型估計 ?
- 將圓柱面參數化為: ?
中心軸:直線 L(t)=c + t d(方向向量 d,單位化) ?
半徑 r ?
- 5 點約束:任意點 p_i 到軸的距離等于 r ?
‖(p_i ? c) × d‖ = r, i=1…5 ?
可通過非線性最小二乘或幾何代數法一次性求出 d、c、r。

內點判定 ?
- 點 p 到圓柱面距離 ?
|‖(p ? c) × d‖ ? r| < d_th。

--------------------------------
下面書寫一段RANSAC的統一處理流程(偽代碼)
--------------------------------
輸入:點云 Q,幾何模型類型 T,閾值 d_th,最大迭代 N ?
for k = 1…N ?
S ← RandomSample(Q, m) ? ? ? ? ? # m 由模型決定(2,3,4,5…) ?
M ← FitModel(S, T) ? ? ? ? ? ? ? # 見上各小節 ?
Inliers ← {p ∈ Q | Distance(p,M) < d_th} ?
if |Inliers| > best_inliers ?
best_model ← M ?
best_inliers ← Inliers ?
輸出:best_model, best_inliers

可視化 ?
- 用 Open3D / Matplotlib / PCL 等庫將原始點云和擬合幾何體(直線、平面、網格化曲面、圓環、球體、圓柱網格)同時渲染。

????????至此,六種常見幾何模型的 RANSAC 實現步驟全部給出,可直接對照代碼模板進行落地。當然,本次的數據豬腳依然是我們的老朋友——兔砸!

一、RANSAC各種擬合程序

import open3d as o3d
import numpy as np
import pyransac3d as pyrsc
import randomFILE = "E:/CSDN/規則點云/bunny.pcd"      # 改成自己的文件# ---------------- 通用工具 -----------------
def load_pcd():pcd = o3d.io.read_point_cloud(FILE)if not pcd.has_points():raise RuntimeError("找不到 {}".format(FILE))return pcd, np.asarray(pcd.points)def show(title, in_pcd, out_pcd, geom):"""統一可視化"""o3d.visualization.draw_geometries([in_pcd, out_pcd, geom],window_name=title,width=800, height=600)# AXIS = o3d.geometry.TriangleMesh.create_coordinate_frame(size=1)# ---------------- 1. 直線 -----------------
def fit_line(pcd, pts):def ransac_3d(points, thresh=0.05, max_iter=1000):best_in, best_model = [], Nonefor _ in range(max_iter):idx = random.sample(range(len(points)), 2)p1, p2 = points[idx]vec = p2 - p1if np.linalg.norm(vec) < 1e-6:continuevec = vec / np.linalg.norm(vec)dists = np.linalg.norm(np.cross(points - p1, vec), axis=1)inliers = np.where(dists < thresh)[0]if len(inliers) > len(best_in):best_in = inliersbest_model = (p1, vec)return best_model, best_inmodel, idx = ransac_3d(pts, 0.05, 1000)if model is None:print("直線擬合失敗")returnp1, vec = modelin_pcd  = pcd.select_by_index(idx).paint_uniform_color([1,0,0])out_pcd = pcd.select_by_index(idx, invert=True).paint_uniform_color([0,1,0])length = np.linalg.norm(pcd.get_max_bound() - pcd.get_min_bound())start, end = p1 - vec * length, p1 + vec * lengthls = o3d.geometry.LineSet(points=o3d.utility.Vector3dVector([start, end]),lines=o3d.utility.Vector2iVector([[0,1]]))ls.colors = o3d.utility.Vector3dVector([[0,0,1]])show("1. 直線擬合", in_pcd, out_pcd, ls)# ---------------- 2. 平面 -----------------
def fit_plane(pcd, pts):plane = pyrsc.Plane()eq, idx = plane.fit(pts, thresh=0.01, maxIteration=1000)in_pcd  = pcd.select_by_index(idx).paint_uniform_color([1,0,0])out_pcd = pcd.select_by_index(idx, invert=True).paint_uniform_color([0,1,0])# 平面網格aabb = in_pcd.get_axis_aligned_bounding_box()size = max(aabb.get_extent()) * 1.5mesh = o3d.geometry.TriangleMesh.create_box(size, size, 0.001)mesh.translate([-size/2, -size/2, 0])mesh.rotate(in_pcd.get_rotation_matrix_from_xyz((0,0,0)), center=(0,0,0))normal = np.array(eq[:3])z = np.array([0,0,1])if np.linalg.norm(np.cross(z, normal)) > 1e-6:rot = o3d.geometry.get_rotation_matrix_from_axis_angle(np.cross(z, normal))mesh.rotate(rot, center=(0,0,0))mesh.translate(in_pcd.get_center())mesh.paint_uniform_color([0,0,1])mesh.compute_vertex_normals()show("2. 平面擬合", in_pcd, out_pcd, mesh)# ---------------- 3. 二次曲面 -----------------
def fit_quadric(pcd, pts):"""擬合二次曲面 z = a x2 + b y2 + c xy + d x + e y + f用 RANSAC 選 6 個內點,然后用最小二乘解 6 參數。"""def quadric_model(pts_sample):A = np.c_[pts_sample[:,0]**2, pts_sample[:,1]**2,pts_sample[:,0]*pts_sample[:,1],pts_sample[:,0], pts_sample[:,1],np.ones(len(pts_sample))]b = pts_sample[:,2]coeffs, *_ = np.linalg.lstsq(A, b, rcond=None)return coeffsdef residuals(pts, coeffs):a,b,c,d,e,f = coeffsreturn np.abs(pts[:,2] - (a*pts[:,0]**2 + b*pts[:,1]**2 + c*pts[:,0]*pts[:,1] + d*pts[:,0] + e*pts[:,1] + f))best_in, best_coeff = [], Nonefor _ in range(1000):idx = random.sample(range(len(pts)), 6)sample = pts[idx]try:coeff = quadric_model(sample)except np.linalg.LinAlgError:continuedists = residuals(pts, coeff)inliers = np.where(dists < 0.05)[0]if len(inliers) > len(best_in):best_in, best_coeff = inliers, coeffif best_coeff is None:print("二次曲面擬合失敗")returnin_pcd  = pcd.select_by_index(best_in).paint_uniform_color([1,0,0])out_pcd = pcd.select_by_index(best_in, invert=True).paint_uniform_color([0,1,0])# 網格可視化aabb = in_pcd.get_axis_aligned_bounding_box()xx, yy = np.meshgrid(np.linspace(aabb.min_bound[0], aabb.max_bound[0], 50),np.linspace(aabb.min_bound[1], aabb.max_bound[1], 50))a,b,c,d,e,f = best_coeffzz = a*xx**2 + b*yy**2 + c*xx*yy + d*xx + e*yy + fvertices = np.stack([xx.ravel(), yy.ravel(), zz.ravel()], axis=1)mesh = o3d.geometry.TriangleMesh()mesh.vertices = o3d.utility.Vector3dVector(vertices)idx = np.arange(50*50).reshape(50,50)triangles = []for i in range(49):for j in range(49):triangles.append([idx[i,j], idx[i+1,j], idx[i,j+1]])triangles.append([idx[i+1,j], idx[i+1,j+1], idx[i,j+1]])mesh.triangles = o3d.utility.Vector3iVector(np.array(triangles))mesh.paint_uniform_color([0,0,1])mesh.compute_vertex_normals()show("3. 曲面擬合", in_pcd, out_pcd, mesh)# ---------------- 4. 圓 -----------------
def fit_circle(pcd, pts):pts2d = pts[:, :2]   # 假設圓在 XY 平面def ransac_circle(pts2d, thresh=0.05, max_iter=1000):best_in, best_model = [], Nonefor _ in range(max_iter):idx = random.sample(range(len(pts2d)), 3)A,B,C = pts2d[idx]D = 2*((B[0]-A[0])*(C[1]-A[1]) - (B[1]-A[1])*(C[0]-A[0]))if abs(D) < 1e-6: continuecx = ((C[1]-A[1])*(B[0]**2+B[1]**2-A[0]**2-A[1]**2) - (B[1]-A[1])*(C[0]**2+C[1]**2-A[0]**2-A[1]**2)) / Dcy = ((B[0]-A[0])*(C[0]**2+C[1]**2-A[0]**2-A[1]**2) - (C[0]-A[0])*(B[0]**2+B[1]**2-A[0]**2-A[1]**2)) / Dr  = np.linalg.norm([A[0]-cx, A[1]-cy])dists = np.abs(np.linalg.norm(pts2d - [cx,cy], axis=1) - r)inliers = np.where(dists < thresh)[0]if len(inliers) > len(best_in):best_in, best_model = inliers, (cx, cy, r)return best_model, best_inmodel, idx = ransac_circle(pts2d, 0.05, 1000)if model is None:print("圓擬合失敗")returncx,cy,r = modelin_pcd  = pcd.select_by_index(idx).paint_uniform_color([1,0,0])out_pcd = pcd.select_by_index(idx, invert=True).paint_uniform_color([0,1,0])theta = np.linspace(0, 2*np.pi, 100)x = cx + r*np.cos(theta)y = cy + r*np.sin(theta)z = np.zeros_like(x)circle_pts = np.vstack([x,y,z]).Tls = o3d.geometry.LineSet(points=o3d.utility.Vector3dVector(circle_pts),lines=o3d.utility.Vector2iVector([[i,(i+1)%100] for i in range(100)]))ls.colors = o3d.utility.Vector3dVector([[0,0,1] for _ in range(100)])show("4. 圓擬合", in_pcd, out_pcd, ls)# ---------------- 5. 球 -----------------
def fit_sphere(pcd, pts):sph = pyrsc.Sphere()center, radius, idx = sph.fit(pts, thresh=0.05, maxIteration=1000)in_pcd  = pcd.select_by_index(idx).paint_uniform_color([1,0,0])out_pcd = pcd.select_by_index(idx, invert=True).paint_uniform_color([0,1,0])mesh = o3d.geometry.TriangleMesh.create_sphere(radius=radius)mesh.translate(center)mesh.paint_uniform_color([0,0,1])mesh.compute_vertex_normals()show("5. 球擬合", in_pcd, out_pcd, mesh)# ---------------- 6. 圓柱 -----------------
def fit_cylinder(pcd, pts):cyl = pyrsc.Cylinder()axis, center, radius, idx = cyl.fit(pts, thresh=0.05, maxIteration=1000)in_pcd  = pcd.select_by_index(idx).paint_uniform_color([1,0,0])out_pcd = pcd.select_by_index(idx, invert=True).paint_uniform_color([0,1,0])height = np.linalg.norm(pcd.get_max_bound() - pcd.get_min_bound()) * 1.2mesh = o3d.geometry.TriangleMesh.create_cylinder(radius=radius, height=height, resolution=50)mesh.compute_vertex_normals()mesh.paint_uniform_color([0,0,1])z = np.array([0,0,1])axis = axis / np.linalg.norm(axis)if np.linalg.norm(np.cross(z, axis)) > 1e-6:rot = o3d.geometry.get_rotation_matrix_from_axis_angle(np.cross(z, axis))mesh.rotate(rot, center=(0,0,0))mesh.translate(center)show("6. 圓柱擬合", in_pcd, out_pcd, mesh)# ---------------- 主菜單 -----------------
def main():pcd, pts = load_pcd()menu = """
1 直線
2 平面
3 二次曲面
4 圓
5 球
6 圓柱
q 退出
請選擇(1-6):"""while True:choice = input(menu).strip()if choice == 'q': breakif choice not in {'1','2','3','4','5','6'}:print("輸入無效")continueif choice == '1': fit_line(pcd, pts)if choice == '2': fit_plane(pcd, pts)if choice == '3': fit_quadric(pcd, pts)if choice == '4': fit_circle(pcd, pts)if choice == '5': fit_sphere(pcd, pts)if choice == '6': fit_cylinder(pcd, pts)if __name__ == "__main__":main()

二、RANSAC各種擬合結果

????????本次的程序添加了選擇按鈕,1-6分別是RANSAC擬合直線、平面、曲面、圓、球面、圓柱面。除了圓柱面擬合過于離譜(主要兔砸長得不像柱子),其他的都挺好。

就醬,下次見^-^

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

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

相關文章

實驗2 天氣預報

實驗1 天氣預報一、實驗目標二、實驗步驟&#xff08;一&#xff09;準備工作&#xff08;二&#xff09;小程序開發項目創建頁面配置視圖設計邏輯實現三、程序運行結果四、問題總結與體會主要問題及解決方案主要收獲chunk的博客地址一、實驗目標 1、掌握服務器域名配置和臨時…

【CVE-2025-5419】(內附EXP) Google Chrome 越界讀寫漏洞【內附EXP】

前言 近日,奇安信CERT監測到Google Chrome中曝出一枚高危安全漏洞(CVE-2025-5419,QVD-2025-21836),該漏洞屬于越界讀寫問題,攻擊者只需通過構造惡意網頁,就可能觸發漏洞,從而繞過Chrome的沙箱防護,直接實現遠程代碼執行,最終完全控制用戶設備。目前,安全社區已確認…

【科研繪圖系列】R語言在海洋生態學中的應用:浮游植物糖類組成與溶解性有機碳的關系

禁止商業或二改轉載,僅供自學使用,侵權必究,如需截取部分內容請后臺聯系作者! 文章目錄 介紹 數據準備 數據處理 糖類組成隨年齡的變化 糖類組成與DOC含量的關系 數據可視化 加載R包 數據下載 導入數據 數據預處理 畫圖 總結 系統信息 介紹 本教材通過R語言及其強大的數據…

webpack文件指紋:hash、chunkhash與contenthash詳解

文件指紋就是打包后輸出文件的后綴&#xff0c;每次構建都會生成不同的文件后綴&#xff0c;這樣可以防止瀏覽器的默認緩存&#xff0c;使客戶端代碼可以及時修改。文件指紋的三種方式&#xff1a;? hash ?&#xff1a;基于整個項目構建內容生成全局哈希值&#xff0c;任何文…

Pytest 插件怎么寫:從0開發一個你自己的插件

概述 你用過 pytest-html 生成報告,或用 pytest-xdist 并行運行測試嗎?這些強大的功能,其實都是 Pytest 插件 這些都是我們引入項目后直接使用的,當然 你也可以自己寫一個 Pytest 插件 基本原理 Pytest 的強大,源于它的 插件系統。它允許你通過定義特定的函數(稱為 H…

Java:IO流——基礎篇

目錄 前言 一、File 類 1、概述 ①構造方法 ②實例對象 2、使用 ①查看名稱、路徑、長度 ②判斷、創建和刪除操作 ③目錄遍歷操作 二、IO流 1、流的概念 2、流的分類 ①按數據流向 ②按數據類型 ③按功能 3、字節流 ⑴FileInputStream——文件輸入流 ⑵FileOutputStream——文件…

數據挖掘 5.1~5.2 PCA——前言

5.1 Twelve ways to fool the masses 5.1 愚弄大眾的十二種方法 5.2.1 Prelim: Old MacDonald meets Lagrange 5.2.1 前言&#xff1a;老麥克唐納遇見拉格朗日 5.2. Prelim: Meet stubborn vectors 5.2. 前言&#xff1a;遇見頑固向量 5.2.3 Prelim: Covariance and its friend…

DeepSeek分析

(非走向數字時代,融入數字生活,構建數字生態的分解,只是感覺可以分享給大家---因此現設置VIP,旺海涵) 這是deepseek剛爆的時候,春節緊急對其做的分析。 內容還是私藏狀態,做了初步評估,感覺可以分享給大家!!! 但是非共享的構建數字生態的核心,因此添加了vip設置…

2025第五屆人工智能、自動化與高性能計算國際會議 (AIAHPC 2025)

重要信息 官網&#xff1a;www.aiahpc.org 時間&#xff1a;2025年9月19-21日 地點&#xff1a;中國合肥 主題 1、高性能計算 并行和分布式系統架構 高性能計算的語言和編譯器 并行和分布式軟件技術 并行和分布式算法 嵌入式系統 計算智能 點對點計算 網格和集群計算…

CORS解決跨域問題的多個方案 - nginx站點配置 / thinkphp框架內置中間件 / 純前端vue、vite的server.proxy代理

效果圖 跨域報錯 跨域解決 方案實測 1. nginx、apache站點配置 > OK 2. thinkphp框架內置中間件 “跨域請求支持” > OK 3. 純前端vue、vite的server.proxy代理 > 不OK 方案具體設置 1. nginx、apache站點配置 > OK 修改nginx服務器的站點的跨域信息 日志下…

什么是Omni-Hub?一套面向“萬物智聯”時代的操作系統級方法論

Omni-Hub&#xff08;中文常譯“全向中樞”&#xff09;&#xff0c;是一套面向未來數字化生態的開放型系統級框架&#xff0c;由“Omni”&#xff08;全域、全向、全模態&#xff09;與“Hub”&#xff08;中樞、樞紐&#xff09;組合而成&#xff0c;旨在通過統一接口、協議與…

ARP地址解析協議

工作原理ARP是一個封裝于數據鏈路層的二層協議&#xff0c;其目的主要是將IP地址解析為MAC地址&#xff0c;通過廣播&#x1f509;詢問Who is x.x.x.x&#xff0c;對方收到后單播回應自己的mac地址動態ARP動態ARP通過ARP協議自動學習和維護IP與MAC的映射關系&#xff0c;表項具…

PortSwigger靶場之Blind SQL injection with out-of-band interaction通關秘籍

一、題目分析 該實驗室存在一個盲 SQL 注入漏洞。該應用程序使用跟蹤 cookie 進行分析&#xff0c;并執行包含所提交 cookie 值的 SQL 查詢。該 SQL 查詢是異步執行的&#xff0c;不會對應用程序的響應產生影響。不過&#xff0c;我們可以與外部域觸發非帶內交互。要解決此漏洞…

筆試-筆記3

1.在以下聲明中哪一個表示“指向常量的指針”(指針指向的內容不能修改)&#xff1f; A.char* const p B.const char* p C.char *p const D.char const p 解析&#xff1a; 選B&#xff0c;const修飾的變量為常量&#xff0c;意味著不能修改 A是常量指針&#xff0c;const修飾的…

Linux正則表達式

文章目錄一、Linux正則表達式與三劍客知識1.什么是正則表達式&#xff1f;2.為什么要學習正則表達式&#xff1f;3.有關正則表達式容易混淆的事項4.學習正則表達式注意事項5. 正則表達式的分類5.1 基本的正則表達式&#xff08;BRE&#xff09;集合6. 正則表達式測試題7. 擴展正…

MATLAB Figure畫布中繪制表格詳解

文章目錄 1 使用uitable創建帶有樣式和顏色映射的表格 2 使用imagesc和text創建自定義表格 3 使用patch和text創建完全自定義的表格 4 代碼詳細講解 4.1 使用uitable 4.2 使用imagesc和text 4.3 使用patch和text 5 顏色映射技巧 5.1 使用內置顏色映射 5.2 自定義顏色映射函數 5…

Python在語料庫建設中的應用:文本收集、數據清理與文件名管理

一、問題的提出在日常語言學習與教學中&#xff0c;語料庫是一個不可或缺的工具。它可以幫助我們查找高頻詞&#xff0c;獲取搭配信息、例句信息、關鍵詞信息等。由于建庫過程操作步驟多&#xff0c;有時還要用到圖片識別、格式轉化、文本清理等技巧&#xff0c;很多人往往都止…

STL——priority_queue的使用(快速入門詳細)

目錄 前言 一、基本知識 二、使用 前言 priority_queue是在queue庫里的&#xff0c;所以使用的時候要包含queue頭文件。使用方法和堆類似&#xff0c;因為它的底層其實就是大根堆。 一、基本知識 優先隊列優先級隊列是一種容器適配器&#xff0c;根據一些嚴格的弱排序標準&…

MATLAB中函數的詳細使用

一、函數基本知識function語法&#xff1a; function [,...,] myfun(,...,)&#xff0c; …

服務器初始化流程***

前言在云計算與自動化運維日益成熟的今天&#xff0c;快速、批量地部署服務器已成為常態。然而&#xff0c;一臺新構建的云服務器或新安裝的物理服務器&#xff0c;僅僅是一個可運行的操作系統內核&#xff0c;遠未達到投入生產環境或開發測試的標準。一個缺乏標準化配置的“裸…