点云处理【七】(点云配准)

点云处理

第一章 点云数据采集


1.点云配准

点云配准是将两个或多个点云数据集融合到一个统一的坐标系统中的过程。这通常是为了创建一个完整的模型或融合从不同视角采集的数据。
点云配准一般分为粗配准和精配准,粗配准指的是在两幅点云之间的变换完全未知的情况下进行较为粗糙的配准,目的主要是为精配准提供较好的变换初值;精配准则是给定一个初始变换,进一步优化得到更精确的变换。这里我们主要介绍精配准。

2.点云粗配准算法

2.1 基于特征匹配的配准算法

SAC-IA

2.2 基于穷举搜索的配准算法

4PCS:四点配准算法(4PCS)基于寻找四个点的一致集合,并尝试找到最佳的变换,使得这些点在源和目标点云中都是一致的。
该方法适用于重叠区域较小或者重叠区域发生较大变化场景点云配准,无需对输入数据进行预滤波和去噪,算法能够快速准确的完成点云配准
Sper4PCS

2.3 基于概率分布的配准算法

NDT

3.点云精配准算法

3.1 基于优化的配准方法

大部分基于优化的方法在于找对应点搜索和变换估计。对应点搜索是在另一个点云中找到每个点的匹配点。变换估计就是利用对应关系来估计变换矩阵。这两个阶段将进行迭代,以找到最佳的变换。
基于优化的配准方法大致可分为4种方法:基于ICP的变种方法、基于图优化的、基于GMM的和半定的配准方法

3.1.1 基于ICP的变种方法

3.1.1.1 ICP

迭代最近点(ICP)通过最小化平移矩阵t和旋转矩阵R,使两个点云重合度最高(每个点到点距离最短)。
ICP需要一个相对好的初始估计,否则容易陷入局部最小值。
open3d:

import open3d as o3d
import numpy as np
import copy
def draw_registration_result(source, target, transformation):"""Visualize the registration result."""source_temp = copy.deepcopy(source)source_temp.transform(transformation)o3d.visualization.draw_geometries([source_temp, target])# 1. 读取两个点云
source = o3d.io.read_point_cloud("peizhun/1697938934952.pcd")
target = o3d.io.read_point_cloud("peizhun/1697938935423.pcd")# 2. 下采样点云 (可选)
source_down = source.voxel_down_sample(voxel_size=5)
target_down = target.voxel_down_sample(voxel_size=5)# 3. 估计法线 (对于某些 ICP 变体可能需要)
# source_down.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=100, max_nn=30))
# target_down.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=100, max_nn=30))# 4. 执行 ICP
init_guess = np.eye(4)  # 如果你有一个初步的变换猜测,可以替换这里
threshold = 50  # 两点之间的最大距离
reg_p2p = o3d.pipelines.registration.registration_icp(source_down, target_down, threshold, init_guess,o3d.pipelines.registration.TransformationEstimationPointToPoint())# 5. 显示结果
print("Transformation is:")
print(reg_p2p.transformation)
draw_registration_result(source, target, reg_p2p.transformation)

请添加图片描述
pcl

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/icp.h>
#include <pcl/visualization/pcl_visualizer.h>int main(int argc, char** argv) {pcl::PointCloud<pcl::PointXYZ>::Ptr source(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr target(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr output(new pcl::PointCloud<pcl::PointXYZ>);// 读取点云数据if (pcl::io::loadPCDFile<pcl::PointXYZ>("../data/1695551473330.pcd", *source) == -1 || pcl::io::loadPCDFile<pcl::PointXYZ>("../data/1695551473719.pcd", *target) == -1) {std::cout << "Failed to read the PCD files!" << std::endl;return -1;}// 创建 ICP 对象并设置参数pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;icp.setInputSource(source);icp.setInputTarget(target);icp.setMaximumIterations(50);icp.setTransformationEpsilon(1e-8);icp.setMaxCorrespondenceDistance(50);  // 可以根据需要调整icp.align(*output);pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("ICP"));viewer->setBackgroundColor(0, 0, 0);viewer->addPointCloud<pcl::PointXYZ>(target, "target cloud");viewer->addPointCloud<pcl::PointXYZ>(output, "output cloud");viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "target cloud");viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "output cloud");viewer->addCoordinateSystem(1.0);viewer->initCameraParameters();viewer->spin();return 0;
}

请添加图片描述

ICP的改进算法:
(1)Point-to-Plane ICP:将icp中点到点的距离改为点到目标面的距离,这样就不容易陷入局部最优,但也增加了计算量。
(2)Plane-to-Plane ICP:将icp中点到点的距离改为面到面的距离,精度更高,但进一步增加了计算量。
(3)Generalized ICP (GICP):结合了点到点、点到面、面到面的距离,效果更好。
(4)Normal Iterative Closest Point (NICP):考虑了法向量、局部曲率信息,效果进一步提高。
mbicp

3.1.1.2 GICP

pcl和open3d都提供了gicp,可直接调用:

Open3d

import open3d as o3d
import numpy as np
import copy
import os
def delete_zero(pcd):# 将点云转为 numpy 数组points = np.asarray(pcd.points)# 找到非0的点non_zero_indices = np.where(np.any(points != 0, axis=1))[0]# 根据这些索引筛选点filtered_points = points[non_zero_indices]# 更新点云对象pcd.points = o3d.utility.Vector3dVector(filtered_points)return pcdpath = "peizhun"
paths = os.listdir(path)
target = o3d.io.read_point_cloud(os.path.join(path,paths[0]))
target = delete_zero(target)for index in paths[:2]:source = o3d.io.read_point_cloud(os.path.join(path,index))print(source)print("---"+index)source = delete_zero(source)print(source)threshold = 100  # 距离阈值trans_init = np.asarray([[1.0, 0.0, 0.0, 0.0],[0.0, 1.0, 0.0, 0.0],[0.0, 0.0, 1.0, 0],[0.0, 0.0, 0.0, 1.0]])  # 初始变换矩阵,一般由粗配准提供# -------------------------------------------------# 计算两个重要指标,fitness计算重叠区域(内点对应关系/目标点数)。越高越好。# inlier_rmse计算所有内在对应关系的均方根误差RMSE。越低越好。evaluation = o3d.pipelines.registration.evaluate_registration(source, target, threshold, trans_init)generalized_icp = o3d.pipelines.registration.registration_generalized_icp(source, target, threshold, trans_init,o3d.pipelines.registration.TransformationEstimationForGeneralizedICP(),o3d.pipelines.registration.ICPConvergenceCriteria(max_iteration=35))  # 设置最大迭代次数source.transform(generalized_icp.transformation)target = target+source# -----------------可视化配准结果--------------------
o3d.visualization.draw_geometries([target])

请添加图片描述

PCL

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/gicp.h>
#include <pcl/visualization/pcl_visualizer.h>int main()
{pcl::PointCloud<pcl::PointXYZ>::Ptr source(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr target(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr result(new pcl::PointCloud<pcl::PointXYZ>);// Load point cloudsif (pcl::io::loadPCDFile<pcl::PointXYZ>("1697938951044.pcd", *source) == -1 || pcl::io::loadPCDFile<pcl::PointXYZ>("1697938962416.pcd", *target) == -1){std::cerr << "Failed to load PCD files." << std::endl;return -1;}// Perform GICP alignmentpcl::GeneralizedIterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> gicp;gicp.setInputSource(source);gicp.setInputTarget(target);gicp.align(*result);if (gicp.hasConverged()){std::cout << "GICP has converged. Score: " << gicp.getFitnessScore() << std::endl;std::cout << "Transformation matrix:" << std::endl;std::cout << gicp.getFinalTransformation() << std::endl;}else{std::cerr << "GICP did not converge." << std::endl;return -1;}// Visualizationboost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("GICP Viewer"));viewer->setBackgroundColor(0, 0, 0);viewer->addPointCloud<pcl::PointXYZ>(source, "source cloud");viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "source cloud");viewer->addPointCloud<pcl::PointXYZ>(result, "result cloud");viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 1, 0, "result cloud");viewer->initCameraParameters();viewer->spin();return 0;
}

请添加图片描述

NICP

const转换错误,去const

缺liblz4
https://github.com/yorsh87/nicp/issues/23

3.1.2 基于图优化的配准方法

将点云转化成图结构,像上文中的GICP就是Graph+ICP。

3.1.3 基于GMM的配准方法

也就是基于高斯混合概率模型的方法,如GICP、NDT、CPD等,上面的GICP就是GMM+ICP实现的。

3.1.3.1 NDT

正态分布变换 (NDT)通过将点云表示为一系列的正态分布来工作。然后,通过最大化两个点云之间的概率分布的重叠来寻找最佳的变换。

PCL

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/ndt.h>
#include <pcl/visualization/pcl_visualizer.h>int main(int argc, char** argv)
{// 加载点云文件pcl::PointCloud<pcl::PointXYZ>::Ptr target_cloud (new pcl::PointCloud<pcl::PointXYZ>);if (pcl::io::loadPCDFile<pcl::PointXYZ> ("1697938951044.pcd", *target_cloud) == -1){PCL_ERROR ("Couldn't read target pcd file\n");return (-1);}pcl::PointCloud<pcl::PointXYZ>::Ptr input_cloud (new pcl::PointCloud<pcl::PointXYZ>);if (pcl::io::loadPCDFile<pcl::PointXYZ> ("1697938962416.pcd", *input_cloud) == -1){PCL_ERROR ("Couldn't read input pcd file\n");return (-1);}// 设置NDT的参数pcl::NormalDistributionsTransform<pcl::PointXYZ, pcl::PointXYZ> ndt;ndt.setTransformationEpsilon(10);ndt.setStepSize(1);ndt.setResolution(100);ndt.setMaximumIterations(35);ndt.setInputSource(input_cloud);ndt.setInputTarget(target_cloud);// 配准pcl::PointCloud<pcl::PointXYZ>::Ptr output_cloud (new pcl::PointCloud<pcl::PointXYZ>);ndt.align(*output_cloud);std::cout << "Normal Distributions Transform has converged:" << ndt.hasConverged()<< " score: " << ndt.getFitnessScore() << std::endl;// 可视化结果pcl::visualization::PCLVisualizer viewer("NDT");// 添加目标点云,设置为白色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> target_color(target_cloud, 255, 255, 255);viewer.addPointCloud<pcl::PointXYZ>(target_cloud, target_color, "target_cloud");// 添加输入点云,设置为绿色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> input_color(input_cloud, 0, 255, 0);viewer.addPointCloud<pcl::PointXYZ>(input_cloud, input_color, "input_cloud");// 添加输出点云,设置为红色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> output_color(output_cloud, 255, 0, 0);viewer.addPointCloud<pcl::PointXYZ>(output_cloud, output_color, "output_cloud");viewer.spin();return 0;
}

3.1.4 基于半定规划的配准方法

半定规划在二次规划的基础上进一步扩展研究,半定规划是一类特殊的凸优化问题,属于非凸优化的一类问题。
如CPD、TEASER等算法。

3.2 特征学习的配准方法

这类方法使用深度学习作为特征提取工具提取有效特征点,然后通过简单的RANSAC方法,可以得到准确的配准结果。
如PPF等。

3.3 基于端到端学习的方法

端到端学习方法的基本思想是将配准问题转化为回归问题。

此外还有多视角配准、多源配准等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/119573.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

宝塔Python3.7安装模块报错ModuleNotFoundError: No module named ‘Crypto‘解决办法

前言 今晚遇到一个问题&#xff0c;宝塔服务器上安装脚本的模块时&#xff0c;出现以下报错&#xff0c;这里找到了解决办法 Traceback (most recent call last):File "/www/wwwroot/unifysign/fuck_chaoxing/fuck_xxt.py", line 4, in <module>from Crypto.…

[SQL开发笔记]BETWEEN操作符:选取介于两个值之间的数据范围内的值

一、功能描述&#xff1a; BETWEEN操作符&#xff1a;选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。 二、BETWEEN操作符语法详解&#xff1a; BETWEEN操作符语法&#xff1a; SELECT column1, column2,…FROM table_nameWHERE column BETWEEN val…

基于aop 代理 Sentinel Nacos配置控制包装类实现原理

基于aop & 代理 & Sentinel & Nacos配置控制包装类实现原理 Hi&#xff0c;我是阿昌&#xff0c;今天记录下看sentinel源码结合业务实现的思路基于aop & 代理 & Sentinel & Nacos配置控制包装类实现原理&#xff1b;下面并不会手把手的记录方案的实现…

Redis | 数据结构(02)SDS

一、键值对数据库是怎么实现的&#xff1f; 在开始讲数据结构之前&#xff0c;先给介绍下 Redis 是怎样实现键值对&#xff08;key-value&#xff09;数据库的。 Redis 的键值对中的 key 就是字符串对象&#xff0c;而 value 可以是字符串对象&#xff0c;也可以是集合数据类型…

【算法练习Day29】柠檬水找零根据身高重建队列用最少数量的箭引爆气球

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 柠檬水找零根据身高重建队列…

Web进阶

身份认证 当我们在使用互联网时&#xff0c;经常会遇到一些需要身份验证或者保持用户状态的情况。为了实现这些功能&#xff0c;常用的方法有使用cookie、session和token。 Cookie&#xff08;HTTP Cookie&#xff09;&#xff1a; Cookie是服务器发送到用户浏览器并保存在用户…

STM32-LCD中英文显示及应用

目录 字符编码 ASCII码&#xff08;8位&#xff09; 中文编码&#xff08;16位&#xff09; GB2312标准 GBK编码 GB18030标准&#xff08;32位&#xff09; Big5编码 Unicode字符集和编码 UTF-32&#xff08;32位&#xff09; UTF-16&#xff08;16位/32位&#xff0…

二十三、设计模式之组合模式![

目录 二十三、设计模式之组合模式能帮我们干什么&#xff1f;主要解决什么问题&#xff1f;优缺点优点缺点&#xff1a; 使用的场景理解实现角色组合模式 总结 魔战已经完结。成功登顶。占领敌军最高峰。 二十三、设计模式之组合模式 “组合模式”也被称为“部分整体模式”该…

方舟生存进化ARK个人服务器搭建教程保姆级

方舟生存进化ARK个人服务器搭建教程保姆级 大家好我是艾西&#xff0c;在很久之前我有给大家分享过方舟生存进化的搭建架设教程&#xff0c;但时间久远且以前的教程我现在回头看去在某些地方说的并不是那么清楚。最近也是闲暇无事打算重新巩固下方舟生存进化的搭建架设教程&…

argparse模块介绍

argparse是一个Python模块&#xff1a;命令行选项、参数和子命令解析器。argparse 模块可以让人轻松编写用户友好的命令行接口。程序定义了所需的参数&#xff0c;而 argparse 将找出如何从 sys.argv &#xff08;命令行&#xff09;中解析这些参数。argparse 模块还会自动生成…

B-3:Web安全之综合渗透测试

B-3:Web安全之综合渗透测试 任务环境说明: 服务器场景:Server2104(关闭链接) 服务器场景用户名、密码:未知 1.通过URL访问http://靶机IP/1,对该页面进行渗透测试,将完成后返回的结果内容作为FLAG值提交; 通过访问IP/1,查看源代码发现flagishere,访问后发现什么也没…

【RabbitMQ】常用消息模型详解

文章目录 AMQP协议的回顾RabbitMQ支持的消息模型第一种模型(直连)开发生产者开发消费者生产者、消费者开发优化API参数细节 第二种模型(work quene)开发生产者开发消费者消息自动确认机制 第三种模型(fanout)开发生产者开发消费者 第四种模型(Routing)开发生产者开发消费者 第五…

jvm摘要

第 2 章 Java 内存区域与内存溢出异常 2.2 运行时数据区域 程序计数器-线程私有:是一块较小的内存空间&#xff0c;它可以看作是当前线程所执行的字节码的行号指示器。 程序计数器是唯一一个没有规定任何OutOfMemoryError 情况的区域。 Java 虚拟机栈-线程私有:用于执行Java …

悟空crm安装搭建 报错[0] RedisException in Redis.php line 56问题处理办法

相信很多朋友进行安装悟空crm的时候 提示错误&#xff1a; [0] RedisException in Redis.php line 56 Connection refused 不知道怎么样处理是吧~~~ $this->options array_merge($this->options, $options);}# redis 密码$password config(cache.password);if (!empty…

(二开)Flink 修改源码拓展 SQL 语法

1、Flink 扩展 calcite 中的语法解析 1&#xff09;定义需要的 SqlNode 节点类-以 SqlShowCatalogs 为例 a&#xff09;类位置 flink/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlShowCatalogs.java 核心方法&#xff1a; Override pu…

【C++】priority_queue仿函数

今天我们来学习C中另一个容器适配器&#xff1a;优先级队列——priority_queue&#xff1b;和C一个重要组件仿函数&#xff1a; 目录 一、priority_queue 1.1 priority_queue是什么 1.2 priority_queue的接口 1.2.1 priority_queue使用举例 二、仿函数 三、关于priority…

Docker GitLab-Runner安装

Docker GitLab-Runner安装 GitLab-Runner安装 问题合集GitLab 域名的配置修改Runner容器内注册失败&#xff0c;提示 dial tcp: lookup home.zsl0.com on 192.168.254.2:53: no such host GitLab-Runner 安装 拉去gitlab/gitlab-runner镜像 docker pull gitlab/gitlab-runne…

适用于 Windows 10 和 Windows 11 设备的笔记本电脑管理软件

便携式计算机管理软件使 IT 管理员能够简化企业中使用的便携式计算机的部署和管理&#xff0c;当今大多数员工使用Windows 笔记本电脑作为他们的主要工作机器&#xff0c;他们确实已成为几乎每个组织不可或缺的一部分。由于与台式机相比&#xff0c;笔记本电脑足够便携&#xf…

Git的远程仓库

Git的远程仓库 添加远程仓库从远程库克隆 添加远程仓库 你在本地创建了一个Git仓库后&#xff0c;又想在GitHub创建一个Git仓库&#xff0c;并且让这两个仓库进行远程同步&#xff0c;这样&#xff0c;GitHub上的仓库既可以作为备份&#xff0c;又可以让其他人通过该仓库来协作…

JoySSL:免费SSL证书的新选择

阿里云曾经提供了一年期的免费SSL证书&#xff0c;然而从下个月14号开始&#xff0c;阿里云不再提供免费的一年期SSL证书&#xff0c;而是改为68元/张/年&#xff0c;这对于很多网站建设公司需要大量基本证书来说&#xff0c;无疑是一种负担&#xff0c;又不知道该去哪里获取可…