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| / √(a²+b²+c²)  
若 d < d_th,则为内点。

--------------------------------
3. 拟合一般二次曲面
--------------------------------
随机采样  
- 二次曲面一般式 Ax²+By²+Cz²+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₂²+y₂²−x₁²−y₁¹)(y₃−y₁) − (x₃²+y₃²−x₁²−y₁²)(y₂−y₁) ] / D  
b = [ (x₃²+y₃²−x₁²−y₁²)(x₂−x₁) − (x₂²+y₂²−x₁²−y₁²)(x₃−x₁) ] / D  
D = 2[(x₂−x₁)(y₃−y₁) − (x₃−x₁)(y₂−y₁)]  
r = √[(x₁−a)²+(y₁−b)²]

内点判定  
- 点 (x,y) 到圆的距离  
|√[(x−a)²+(y−b)²] − r| < d_th。

--------------------------------
5. 拟合 3D 球面
--------------------------------
随机采样  
- 每次随机抽取 4 个非共面 3D 点 p₁…p₄。

模型估计  
- 球心 c 和半径 R 的线性方程组  
‖p_i − c‖² = R², 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 x² + b y² + 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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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;远未达到投入生产环境或开发测试的标准。一个缺乏标准化配置的“裸…