基于Open3D的点云处理16-特征点匹配

点云配准

将点云数据统一到一个世界坐标系的过程称之为点云配准或者点云拼接。(registration/align)

点云配准的过程其实就是找到同名点对;即找到在点云中处在真实世界同一位置的点。

常见的点云配准算法:
ICP、Color ICP、Trimed-ICP 算法流程:

  1. 选点:
    确定参与到配准过程中的点集。
  2. 匹配确定同名点对:
    ICP以两片点云中欧式空间距离最小的点对为同名点
  3. 非线性优化求解:
    采用SVD或者四元数求解变换
  4. 变换:
    将求解的变换参数应用到待配准点云上
  5. 迭代:
    计算此时的状态参数判断配准是否完成。以前后两次参数迭代变
    化的差或者RMSE值是否小于给定阈值为迭代终止条件。否则返回(1)

ICP 算法以两片点云中欧式空间距离最小的点对为同名点,如果初始点选择不合适,可能会陷入局部最优配准失败。

基于点特征的配准方法
两种方式:
一种通过特征描述,先分割出场景里的点线等特征,利用这些点进行同名点的匹配,如基于几何空间一致性筛选同名点对。
一种通过点特征描述符确定同名点,如基于FPFH双向最近邻确定同名点对,FPFH描述向量最大的特性是对于点云的同名点的特征向量表现出相似性,即该点云的同名点之间的FPFH特征描述子的二范数趋于零。

测试用例

基于icp的点云匹配(参考)

  • 点到点的配准
import open3d as o3d
import numpy as np# 获取示例数据
source_cloud = o3d.io.read_point_cloud("./data/cloud_bin_0.pcd")
target_cloud = o3d.io.read_point_cloud("./data/cloud_bin_1.pcd")
source_cloud.paint_uniform_color([1, 0.706, 0])
target_cloud.paint_uniform_color([0, 0.651, 0.929])
threshold = 0.02# RMSE残差阈值,小于该残差阈值,迭代终止#初始位姿
trans_init = np.asarray([[0.862, 0.011, -0.507, 0.5],[-0.139, 0.967, -0.215, 0.7],[0.487, 0.255, 0.835, -1.4], [0.0, 0.0, 0.0, 1.0]])# 显示未配准点云
o3d.visualization.draw_geometries([source_cloud, target_cloud],zoom=0.4459,front=[0.9288, -0.2951, -0.2242],lookat=[1.6784, 2.0612, 1.4451],up=[-0.3402, -0.9189, -0.1996])# 点到点的ICP
result = o3d.pipelines.registration.registration_icp(source_cloud, target_cloud, threshold,trans_init,o3d.pipelines.registration.TransformationEstimationPointToPoint())
print(result)
print("Transformation is:")
print(result.transformation)# 显示点到点的配准结果
source_cloud.transform(result.transformation)
o3d.visualization.draw_geometries([source_cloud, target_cloud],zoom=0.4459,front=[0.9288, -0.2951, -0.2242],lookat=[1.6784, 2.0612, 1.4451],up=[-0.3402, -0.9189, -0.1996])

配准前:
配准前
配准结果:
配准结果
在这里插入图片描述

  • 点到面的配准
import open3d as o3d
import numpy as np# 获取示例数据
source_cloud = o3d.io.read_point_cloud("./data/cloud_bin_0.pcd")
target_cloud = o3d.io.read_point_cloud("./data/cloud_bin_1.pcd")
source_cloud.paint_uniform_color([1, 0.706, 0])
target_cloud.paint_uniform_color([0, 0.651, 0.929])
threshold = 0.02# RMSE残差阈值,小于该残差阈值,迭代终止#初始位姿
trans_init = np.asarray([[0.862, 0.011, -0.507, 0.5],[-0.139, 0.967, -0.215, 0.7],[0.487, 0.255, 0.835, -1.4], [0.0, 0.0, 0.0, 1.0]])source_cloud = o3d.io.read_point_cloud("./data/cloud_bin_0.pcd")
target_cloud = o3d.io.read_point_cloud("./data/cloud_bin_1.pcd")
source_cloud.paint_uniform_color([1, 0.706, 0])
target_cloud.paint_uniform_color([0, 0.651, 0.929])# 显示未配准点云
o3d.visualization.draw_geometries([source_cloud, target_cloud],zoom=0.4459,front=[0.9288, -0.2951, -0.2242],lookat=[1.6784, 2.0612, 1.4451],up=[-0.3402, -0.9189, -0.1996])# 点到面的ICP
result = o3d.pipelines.registration.registration_icp(source_cloud, target_cloud, threshold,trans_init,o3d.pipelines.registration.TransformationEstimationPointToPlane())
print(result)
print("Transformation is:")
print(result.transformation, "\n")# 显示点到面的配准结果
source_cloud.transform(result.transformation)
o3d.visualization.draw_geometries([source_cloud, target_cloud],zoom=0.4459,front=[0.9288, -0.2951, -0.2242],lookat=[1.6784, 2.0612, 1.4451],up=[-0.3402, -0.9189, -0.1996])

配准结果如图:
在这里插入图片描述
在这里插入图片描述

基于Color ICP的点云匹配 参考


import open3d as o3d
import numpy as np
import copydef draw_registration_result_original_color(source, target, transformation):source_temp = copy.deepcopy(source)source_temp.transform(transformation)o3d.visualization.draw_geometries([source_temp, target],zoom=0.5,front=[-0.2458, -0.8088, 0.5342],lookat=[1.7745, 2.2305, 0.9787],up=[0.3109, -0.5878, -0.7468])
print("1. Load two point clouds and show initial pose")
demo_colored_icp_pcds = o3d.data.DemoColoredICPPointClouds()
source = o3d.io.read_point_cloud(demo_colored_icp_pcds.paths[0])
target = o3d.io.read_point_cloud(demo_colored_icp_pcds.paths[1])# draw initial alignment
current_transformation = np.identity(4)
draw_registration_result_original_color(source, target, current_transformation)# point to plane ICP
current_transformation = np.identity(4)
print("2. Point-to-plane ICP registration is applied on original point")
print("   clouds to refine the alignment. Distance threshold 0.02.")
result_icp = o3d.pipelines.registration.registration_icp(source, target, 0.02, current_transformation,o3d.pipelines.registration.TransformationEstimationPointToPlane())
print(result_icp)
draw_registration_result_original_color(source, target,result_icp.transformation)# colored pointcloud registration
# This is implementation of following paper
# J. Park, Q.-Y. Zhou, V. Koltun,
# Colored Point Cloud Registration Revisited, ICCV 2017
voxel_radius = [0.04, 0.02, 0.01]
max_iter = [50, 30, 14]
current_transformation = np.identity(4)
print("3. Colored point cloud registration")
for scale in range(3):iter = max_iter[scale]radius = voxel_radius[scale]print([iter, radius, scale])print("3-1. Downsample with a voxel size %.2f" % radius)source_down = source.voxel_down_sample(radius)target_down = target.voxel_down_sample(radius)print("3-2. Estimate normal.")source_down.estimate_normals(o3d.geometry.KDTreeSearchParamHybrid(radius=radius * 2, max_nn=30))target_down.estimate_normals(o3d.geometry.KDTreeSearchParamHybrid(radius=radius * 2, max_nn=30))print("3-3. Applying colored point cloud registration")result_icp = o3d.pipelines.registration.registration_colored_icp(source_down, target_down, radius, current_transformation,o3d.pipelines.registration.TransformationEstimationForColoredICP(),o3d.pipelines.registration.ICPConvergenceCriteria(relative_fitness=1e-6,relative_rmse=1e-6,max_iteration=iter))current_transformation = result_icp.transformationprint(result_icp)
draw_registration_result_original_color(source, target,result_icp.transformation)

配准前:
在这里插入图片描述

基于点到平面icp的配准:
在这里插入图片描述

基于color icp的配准结果:
在这里插入图片描述

基于fpfh特征的点云匹配

import numpy as np
import copy
import open3d as o3ddef draw_registration_result(source, target, transformation):source_temp = copy.deepcopy(source)target_temp = copy.deepcopy(target)source_temp.paint_uniform_color([1, 0.706, 0])target_temp.paint_uniform_color([0, 0.651, 0.929])source_temp.transform(transformation)o3d.visualization.draw_geometries([source_temp, target_temp],zoom=0.4559,front=[0.6452, -0.3036, -0.7011],lookat=[1.9892, 2.0208, 1.8945],up=[-0.2779, -0.9482, 0.1556])def preprocess_point_cloud(pcd, voxel_size):print(":: Downsample with a voxel size %.3f." % voxel_size)pcd_down = pcd.voxel_down_sample(voxel_size)radius_normal = voxel_size * 2print(":: Estimate normal with search radius %.3f." % radius_normal)pcd_down.estimate_normals(o3d.geometry.KDTreeSearchParamHybrid(radius=radius_normal, max_nn=30))radius_feature = voxel_size * 5print(":: Compute FPFH feature with search radius %.3f." % radius_feature)pcd_fpfh = o3d.pipelines.registration.compute_fpfh_feature(pcd_down,o3d.geometry.KDTreeSearchParamHybrid(radius=radius_feature, max_nn=100))return pcd_down, pcd_fpfhdef prepare_dataset(voxel_size):print(":: Load two point clouds and disturb initial pose.")demo_icp_pcds = o3d.data.DemoICPPointClouds()source = o3d.io.read_point_cloud(demo_icp_pcds.paths[0])target = o3d.io.read_point_cloud(demo_icp_pcds.paths[1])trans_init = np.asarray([[0.0, 0.0, 1.0, 0.0], [1.0, 0.0, 0.0, 0.0],[0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0]])source.transform(trans_init)draw_registration_result(source, target, np.identity(4))source_down, source_fpfh = preprocess_point_cloud(source, voxel_size)target_down, target_fpfh = preprocess_point_cloud(target, voxel_size)return source, target, source_down, target_down, source_fpfh, target_fpfhdef execute_global_registration(source_down, target_down, source_fpfh,target_fpfh, voxel_size):distance_threshold = voxel_size * 1.5print(":: RANSAC registration on downsampled point clouds.")print("   Since the downsampling voxel size is %.3f," % voxel_size)print("   we use a liberal distance threshold %.3f." % distance_threshold)result = o3d.pipelines.registration.registration_ransac_based_on_feature_matching(source_down, target_down, source_fpfh, target_fpfh, True,distance_threshold,o3d.pipelines.registration.TransformationEstimationPointToPoint(False),3, [o3d.pipelines.registration.CorrespondenceCheckerBasedOnEdgeLength(0.9),o3d.pipelines.registration.CorrespondenceCheckerBasedOnDistance(distance_threshold)], o3d.pipelines.registration.RANSACConvergenceCriteria(100000, 0.999))return resultvoxel_size = 0.05  # means 5cm for this dataset
source, target, source_down, target_down, source_fpfh, target_fpfh = prepare_dataset(voxel_size)result_ransac = execute_global_registration(source_down, target_down,source_fpfh, target_fpfh,voxel_size)
print(result_ransac)
draw_registration_result(source_down, target_down, result_ransac.transformation)

匹配前:
在这里插入图片描述
匹配结果:
在这里插入图片描述

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

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

相关文章

车联网仿真工具Veins安装指南(基于官方文档)

Veins安装指南 官方文档: tutorial 直接选择最easy的way: 下载一个虚拟机:VMware Workstation Player 下载即可。这个普通版是免费的,pro版正版是要收费的,可以找一些其他的方法下载pro版本,可以同时开…

RabbitMQ从原理到实战—基于Golang【万字详解】

文章目录 前言一、MQ是什么?优势劣势 二、MQ的用途1、应用解耦2、异步加速3、削峰填谷4、消息分发 三、RabbitMQ是什么1、AMQP 协议2、RabbitMQ 包含的要素3、RabbitMQ 基础架构 四、实战1、Simple模式(即最简单的收发模式)2、Work Queues 模型3、Publish/Subscribe…

matlab使用教程(28)—微分方程(ODE)求解常见问题

1.非负 ODE 解 本博客说明如何将 ODE 解约束为非负解。施加非负约束不一定总是可有可无,在某些情况下,由于方程的物理解释或解性质的原因,可能有必要施加非负约束。仅在必要时对解施加此约束,例如不这样做积分就会失败或者解将不…

DQL语句的用法(MySQL)

文章目录 前言一、DQL语句间接和语法1、DQL简介2、DQL语法 二、DQL语句使用1、基础查询(1)查询多个字段(2)为字段设置别名(3)去除重复记录 总结 前言 本文主要介绍SQL语句中DQL语句的功能和使用方法&#…

无涯教程-Android - 应用组件

应用程序组件是Android应用程序的基本组成部分,这些组件需要在应用程序清单文件 AndroidManifest.xml 注册,该文件描述了应用程序的每个组件以及它们如何交互。 Android应用程序可以使用以下四个主要组件- Sr.NoComponents & 描述1 Activities 它们…

【Maven】如何发现,定位,解决依赖冲突

发现冲突 运行的时候可能报出错误xx类找不到xx方法,xx类找不到,很有可能就是冲突导致的。 定位冲突根因 通过idea maven插件 idea安装插件,maven helper 比如我有两个依赖,guava和findbug。 他们都用到了jsr305,…

Linux入门之进程信号|信号产生的方式

文章目录 一、信号入门 1.linux信号的基本概念 2.使用kill -l 命令可以查看系统定义的信号列表 3.信号处理常见方式 二、产生信号 1.通过终端按键产生信号 2.通过调用系统函数向进程发信号 3.由软条件产生信号 4.硬件异常产生信号 1. /0异常 2.模拟野指针 一、信号入门…

iOS开发Swift-3-UI与按钮Button-摇骰子App

1.创建新项目Dice 2.图标 删去AppIcon,将解压后的AppIcon.appiconset文件拖入Assets包。 3.将素材点数1-6通过网页制作成2x,3x版本并拖入Asset。 4.设置对应的UI。 5.拖入Button组件并设置style。 6.Ctrl加拖拽将Button拖拽到ViewController里&#xff0…

Qt无边框青绿色主题

收费产品,学生党、闹眼子党勿扰 收费金额:500元 1 概述 最近因项目需要,写了一个炫酷的青绿色、无边框界面,和3DSMax的界面有点类似。 2 截图 首先看看3DSMax的界面 不知道大家看出来没,这个ui其实很简单&#xff…

[贪心] 拼接最小数

这道题思路并不难&#xff0c;我主要想学习其一些对于字符串的处理。 代码如下&#xff1a; #include <iostream> #include <string> #include <algorithm> using namespace std;const int MAXN 10000; string nums[MAXN];bool cmp(string a, string b) {…

Maven入门教程(三):Maven语法

视频教程&#xff1a;Maven保姆级教程 Maven入门教程(一)&#xff1a;安装Maven环境 Maven入门教程(二)&#xff1a;idea/Eclipse使用Maven Maven入门教程(三)&#xff1a;Maven语法 Maven入门教程(四)&#xff1a;Nexus私服 Maven入门教程(五)&#xff1a;自定义脚手架 6.Mav…

APP爬虫之-Protobuf协议逆向解析

在做APP抓取时&#xff0c;会发现有的APP Response回来的数据有“加密”。不知道返回的内容是什么。 如下&#xff1a; 如上&#xff0c;内容不是明文的&#xff0c;没办法解析数据。APP常见的对数据加密有三种情况&#xff1a;第一种是&#xff0c;用诸如AES这类加密算法对数…