[点云配准]LCD(2D-3D特征配准算法)例程align_point_cloud.py解析

这篇具有很好参考价值的文章主要介绍了[点云配准]LCD(2D-3D特征配准算法)例程align_point_cloud.py解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写在前面

跨域描述符LCD可以实现二维图片特征点到三维点云特征点的配准,是个具有通用性的深度学习特征描述子。(图片来源于论文LCD: Learned Cross-Domain Descriptors for 2D-3D Matching
在Github开源的源码里面给出了利用LCD进行三维点云配准的例程。align_point_cloud.py,这里对例程如何使用已经训练好的模型来进行三维点云配准进行解析。

运行环境

python版本3.6.0以上
pytorch非CPU版本(可选)
Open3D
numpy及其它库,自行下载
需要注意的是,官方的源码中使用的Open3D版本较旧,在运行程序时回出现新版本对应函数不匹配的报错,详情可以参考我之前发的解决办法。

顺带一提,例程是不能在pycharm里面直接运行的,需要输入参数,具体是啥参照官方Github里面给的:

Aligning two point clouds with LCD This demo aligns two 3D colored
point clouds using our pre-trained LCD descriptor with RANSAC. How to
run:

$ python -m apps.align_point_cloud samples/000.ply samples/002.ply --logdir logs/LCD-D256/
For more information, use the --help option.

在代码的根目录下运行python -m apps.align_point_cloud samples/000.ply samples/002.ply --logdir logs/LCD-D256/即可运行调试。在pycharm里面需要带参数运行,具体方法自行搜索。

正文

1.库

把该用的库引一引:

import os
import json
import open3d
import torch
import argparse
import numpy as np

from lcd.models import *

但是pycharm提示我这个例程里面似乎并没有用到pytorch,删了也能正常运行。

这部分里面的from lcd.models import * 对应的lcd.models文件夹下有[点云配准]LCD(2D-3D特征配准算法)例程align_point_cloud.py解析
patchnet和pointnet两种分别对应了二维和三维描述子生成网络,例程中使用了pointnet下的PointNetAutoencoder类,之后会展开说说。

2.参数输入和模型建立

parser = argparse.ArgumentParser()
parser.add_argument("source", help="path to the source point cloud")
parser.add_argument("target", help="path to the target point cloud")
parser.add_argument("--logdir", help="path to the log directory")
parser.add_argument("--voxel_size", default=0.1, type=float)
parser.add_argument("--radius", default=0.15, type=float)
parser.add_argument("--num_points", default=1024, type=int)
args = parser.parse_args()

(1) source → 输入点云路径
(2) target → 目标点云路径
(3) --logdir → 模型文件路径
(4) --voxel_size → 体素大小,默认为0.1
(5) --radius → 最近邻搜索半径,默认为0.15
(6) --num_points → 采样点对应patch内特征点个数,默认为1024

得到输入args为:
[点云配准]LCD(2D-3D特征配准算法)例程align_point_cloud.py解析

接着是读取网络配置文件config.json

logdir = args.logdir
config = os.path.join(logdir, "config.json")
config = json.load(open(config))

device = config["device"]

logdir 是 Log/LCD-D256目录,保存了训练好的模型和配置等:

[点云配准]LCD(2D-3D特征配准算法)例程align_point_cloud.py解析
接下来

fname = os.path.join(logdir, "model.pth")
print("> Loading model from {}".format(fname))
model = PointNetAutoencoder(
    config["embedding_size"],
    config["input_channels"],
    config["output_channels"],
    config["normalize"],
)
model.load_state_dict(torch.load(fname)["pointnet"])
model.to(device)
model.eval()

fname为模型文件model.pth的路径
我们来看一下 PointNetAutoencoder类的输入[点云配准]LCD(2D-3D特征配准算法)例程align_point_cloud.py解析
程序这边model直接和config保持了一致。
在获得配置文件后进行模型加载:

model.load_state_dict(torch.load(fname)["pointnet"])

fname路径对应的model文件里面有 pointnet 和 patchnet 两批参数,这边因为只有点云的网络所以只需要读取[“pointnet”]。

model.to(device)

将模型加载在配置读取出来的设备上(GPU)

model.eval()

eval()挺重要的,在模型预测阶段我们需要Dropout层和batch normalization层设置到预测模式。

3.extract_uniform_patches

定义函数extract_uniform_patches,获得点云Patch。

def extract_uniform_patches(pcd, voxel_size, radius, num_points):
    kdtree = open3d.geometry.KDTreeFlann(pcd)
    downsampled = pcd.voxel_down_sample(voxel_size)
    points = np.asarray(downsampled.points)
    patches = []
    for i in range(points.shape[0]):
        k, index, _ = kdtree.search_hybrid_vector_3d(points[i], radius, num_points)
        if k < num_points:
            index = np.random.choice(index, num_points, replace=True)
        xyz = np.asarray(pcd.points)[index]
        rgb = np.asarray(pcd.colors)[index]
        xyz = (xyz - points[i]) / radius  # normalize to local coordinates
        patch = np.concatenate([xyz, rgb], axis=1)
        patches.append(patch)
    patches = np.stack(patches, axis=0)
    return downsampled, patches

输入pcd, voxel_size, radius, num_points

kdtree = open3d.geometry.KDTreeFlann(pcd)
downsampled = pcd.voxel_down_sample(voxel_size)
points = np.asarray(downsampled.points)

kdtree一会最近邻搜索用的;
对点云进行体素降采样得到downsampled,是一个Open3D点云对象;
得到采样点points。

 patches = []
    for i in range(points.shape[0]):
        k, index, _ = kdtree.search_hybrid_vector_3d(points[i], radius, num_points)
        if k < num_points:
            index = np.random.choice(index, num_points, replace=True)
        xyz = np.asarray(pcd.points)[index]
        rgb = np.asarray(pcd.colors)[index]
        xyz = (xyz - points[i]) / radius  # normalize to local coordinates
        patch = np.concatenate([xyz, rgb], axis=1)
        patches.append(patch)
    patches = np.stack(patches, axis=0)
    return downsampled, patche

最近邻搜索kdtree.search_hybrid_vector_3d对每个降采样之后的点进行最近邻搜索。
返回一个Tuple[int, open3d.utility.IntVector, open3d.utility.DoubleVector],程序中的K为搜索点的个数,index为搜索点对应的索引,是个一维数组。

后面的

 if k < num_points:
            index = np.random.choice(index, num_points, replace=True)

是当k的值小于给定目标点的个数(1024)时将进行随机填充,将index填充到目标长度。

xyz = np.asarray(pcd.points)[index]
rgb = np.asarray(pcd.colors)[index]

取xyz为采样点的位置信息,rgb为采样点的颜色信息。

xyz = (xyz - points[i]) / radius

点云位置归一化处理。

		patch = np.concatenate([xyz, rgb], axis=1)
        patches.append(patch)
    patches = np.stack(patches, axis=0)

将位置和颜色信息合并,对所有采样点处理完毕后将列表patches堆叠为一个[points.shape[0], num_points, 6](例程中为[1469, 1024, 6])维度的矩阵。

return downsampled, patches

返回值为Open3D点云对象downsampled和patches。可以看出函数功能就是将输入的点云降采样并得到所有采样点对应的patch。

4.compute_lcd_descriptors

计算LCD特征描述子

def compute_lcd_descriptors(patches, model, batch_size, device):
    batches = torch.tensor(patches, dtype=torch.float32)
    batches = torch.split(batches, batch_size)
    descriptors = []
    with torch.no_grad():
        for i, x in enumerate(batches):
            x = x.to(device)
            z = model.encode(x)
            z = z.cpu().numpy()
            descriptors.append(z)
    return np.concatenate(descriptors, axis=0)

输入为生成的patch和model以及选择参数batch_size(例程中为128)、device(例程中为CUDA)。

batches = torch.tensor(patches, dtype=torch.float32)
batches = torch.split(batches, batch_size)

将输入的patch转换为张量并将生成的张量按照batch_size进行分割。例程中的batch为若干(128, 1024, 6)的张量块。

	with torch.no_grad():
        for i, x in enumerate(batches):
            x = x.to(device)
            z = model.encode(x)
            z = z.cpu().numpy()
            descriptors.append(z)

只是想看一下训练的效果,并不是想通过验证集来更新网络时,可以使用with torch.no_grad()
对于z = model.encode(x),在类PointNetAutoencoder中有:

    def encode(self, x):
        z = self.encoder(x)
        if self.normalize:
            z = F.normalize(z)
        return z

然后pointnet.py前面有写

self.normalize = normalize
self.input_channels = input_channels
self.embedding_size = embedding_size
self.encoder = PointNetEncoder(embedding_size, input_channels)

其中类PointNetEncoder如下:

class PointNetEncoder(nn.Module):
    def __init__(self, embedding_size, input_channels=3):
        super(PointNetEncoder, self).__init__()
        self.input_channels = input_channels
        self.stn1 = STN3D(input_channels)
        self.stn2 = STN3D(64)
        self.mlp1 = nn.Sequential(
            nn.Conv1d(input_channels, 64, 1),
            nn.BatchNorm1d(64),
            nn.ReLU(),
            nn.Conv1d(64, 64, 1),
            nn.BatchNorm1d(64),
            nn.ReLU(),
        )
        self.mlp2 = nn.Sequential(
            nn.Conv1d(64, 64, 1),
            nn.BatchNorm1d(64),
            nn.ReLU(),
            nn.Conv1d(64, 128, 1),
            nn.BatchNorm1d(128),
            nn.ReLU(),
            nn.Conv1d(128, 1024, 1),
            nn.BatchNorm1d(1024),
            nn.ReLU(),
        )
        self.fc = nn.Linear(1024, embedding_size)

正是训练得到的三维点云编码器模型,得到一个CUDA tensor格式的数据、

z = z.cpu().numpy()

先将其转换成cpu float-tensor随后再转到numpy格式。 numpy不能读取CUDA tensor 需要将它转化为 CPU tensor,得到一个(128,256)的描述子矩阵。

return np.concatenate(descriptors, axis=0)

将得到的所有描述子堆叠后输出,例程中的输出格式为(1469,256)。

5.点云读取与处理

source = open3d.io.read_point_cloud(args.source)
target = open3d.io.read_point_cloud(args.target)

读取源点云和目标点云。

source_points, source_patches = extract_uniform_patches(
    source, args.voxel_size, args.radius, args.num_points)

通过exract_uniform_patches得到降采样后的源点云和其对应的patch。

source_descriptors = compute_lcd_descriptors(
    source_patches, model, batch_size=128, device=device)

通过compute_lcd_descriptors得到源点云的特征描述子source_descriptors。

source_features = open3d.pipelines.registration.Feature()
source_features.data = np.transpose(source_descriptors)  # T
print("Extracted {} features from source".format(len(source_descriptors)))

open3d.pipelines.registration.Feature()在官方文档中的解释如下:
[点云配准]LCD(2D-3D特征配准算法)例程align_point_cloud.py解析

大概可以理解为在Open3D的piplines里面用于储存用于配准的特征数据的类,需要将生成的特征描述子转置后赋给source_features中的data——source_features.data = np.transpose(source_descriptors)

目标点云的处理方式一致:

target_points, target_patches = extract_uniform_patches(
    target, args.voxel_size, args.radius, args.num_points
)
target_descriptors = compute_lcd_descriptors(
    target_patches, model, batch_size=128, device=device
)
target_features = open3d.pipelines.registration.Feature()
target_features.data = np.transpose(target_descriptors)
print("Extracted {} features from target".format(len(target_descriptors)))

6. 特征点配准

threshold = 0.075
result = open3d.pipelines.registration.registration_ransac_based_on_feature_matching(
    source_points,
    target_points,
    source_features,
    target_features,
    True,
    threshold,
    open3d.pipelines.registration.TransformationEstimationPointToPoint(False),
    4,
    [open3d.pipelines.registration.CorrespondenceCheckerBasedOnDistance(threshold)],
    open3d.pipelines.registration.RANSACConvergenceCriteria(4000000, 500),
)

注意:这部分的代码是在新版本Open3D的基础上修改的,并非原代码。
修改原因见我之前发的Open3D 15.1 报错 module ‘open3d‘ has no attribute ‘registration‘(跑LCD代码时报错)
open3d.pipelines.registration.registration_ransac_based_on_feature_matching函数新版本官方给出的解释如下:
[点云配准]LCD(2D-3D特征配准算法)例程align_point_cloud.py解析
对两点云进行特征配准得到结果result,

if result.transformation.trace() == 4.0:
    success = False

当变换矩阵的迹为4时(没有产生旋转)认定未完成配准。

information = open3d.pipelines.registration.get_information_matrix_from_point_clouds(
    source_points, target_points, threshold, result.transformation
)
n = min(len(source_points.points), len(target_points.points))
if (information[5, 5] / n) < 0.3:  # overlap threshold
    success = False

通过获得信息矩阵来判定是否配准成功。

if not success:
    print("Cannot align two point clouds")
    exit(0)

如果配准未成功,直接结束程序。

print("Success!")
print("Visualizing alignment result...")
source.estimate_normals(open3d.geometry.KDTreeSearchParamHybrid(radius=0.2, max_nn=30))
target.estimate_normals(open3d.geometry.KDTreeSearchParamHybrid(radius=0.2, max_nn=30))
source.paint_uniform_color([1, 0.706, 0])
target.paint_uniform_color([0, 0.651, 0.929])
source.transform(result.transformation)
open3d.visualization.draw_geometries([source, target])
print(result.transformation)

如果配准成功完成,将配准后的点云变换后着色进行可视化输出得到如下结果:
[点云配准]LCD(2D-3D特征配准算法)例程align_point_cloud.py解析文章来源地址https://www.toymoban.com/news/detail-481623.html

到了这里,关于[点云配准]LCD(2D-3D特征配准算法)例程align_point_cloud.py解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • CVPR2023新作:3D点云配准--3D Registration with Maximal Cliques

    Title: 3D Registration with Maximal Cliques Affiliation: School of Computer Science, Northwestern Polytechnical University, China Authors: Xiyu Zhang, Jiaqi Yang, Shikun Zhang, Yanning Zhang Keywords: 3D point cloud registration, maximal cliques, graph theory, SVD algorithm, deep learning Summary: (1): 本文主要解决3D点云配准的问题,并针对现有

    2024年02月15日
    浏览(43)
  • 多视图点云配准算法综述

    作者:杨佳琪,张世坤,范世超等 转载自:华中科技大学学报(自然科学版) 编辑:东岸因为@一点人工一点智能 原文:​​多视图点云配准算法综述​​ 摘要: 以多视图点云配准为研究对象,对近二十余年的多视图点云配准相关研究工作进行了全面的分类归纳及总结。首先

    2024年02月05日
    浏览(38)
  • 激光雷达点云基础-点云滤波算法与点云配准算法

    激光雷达点云处理在五年前就做了较多的工作,最近有一些新的接触发现激光雷达代码原理五年前未见重大更新,或许C++与激光雷达结合本身就是比较高的技术门槛。深度学习调包侠在硬核激光雷达技术面前可以说是完全的自愧不如啊。 1、点云滤波 在获取点云数据时,由于

    2024年03月19日
    浏览(44)
  • open3d点云配准函数registration_icp

    open3d快速上手 ICP, 即Iterative Closest Point, 迭代点算法。 ICP算法有多种形式,其中最简单的思路就是比较点与点之间的距离,对于点云 P = { p i } , Q = { q i } P={p_i}, Q={q_i} P = { p i ​ } , Q = { q i ​ } 而言,如果二者是同一目标,通过旋转、平移等操作可以实现重合的话,那么只需

    2023年04月19日
    浏览(42)
  • 基于深度学习方法的点云算法1——PointNetLK(点云配准)

    请点点赞,会持续更新!!! 基于深度学习方法的点云算法2——PointNet(点云分类分割) 基于深度学习方法的点云算法3——PointNet++(点云分类分割) 基于深度学习方法的点云算法4——PCT: Point Cloud Transformer(点云分类分割) 作者将PointNet看成一个可学习的成像函数(learn

    2024年02月10日
    浏览(40)
  • 点云配准的传统算法ICP与NDT概述

    公众号致力于分享点云处理,SLAM,三维视觉,高精地图相关的文章与技术,欢迎各位加入我们,一起交流一起进步,有兴趣的可联系微信:920177957。 本文来自点云PCL博主的分享,未经作者允许请勿转载,欢迎各位同学积极分享和交流。 什么是点云配准 点云配准是指将多个点

    2024年02月05日
    浏览(37)
  • 【PCL】—— 点云配准ICP(Iterative Closest Point)算法

    ​     由于三维扫描仪设备受到测量方式和被测物体形状的条件限制,一次扫描往往只能获取到局部的点云信息,进而需要进行多次扫描,然后每次扫描时得到的点云都有独立的坐标系,不可以直接进行拼接。在逆向工程、计算机视觉、文物数字化等领域中,由于点云的

    2024年02月13日
    浏览(49)
  • 点云配准--对称式ICP

    对称式ICP 针对于局部平面不完美的情况,提出了一种对称式ICP目标函数,相较于传统的ICP方法,增大了收敛域,提高了收敛速度。论文理论说明不甚清楚,实验较少,但代码开源。 对称目标函数 在icp中对于一对对应点p,q:在点到法线的度量中: ( p − q ) ⋅ n q (3) (p-q) cd

    2024年02月06日
    浏览(53)
  • 2D-3D配准指南[方法汇总]【入门指导向】(一)问题介绍+LCD跨域描述子+Triplet loss

    近年来,采用三维和二维数据的应用层出不穷,它们都需要将 三维模型 与 二维图像 进行匹配。大型定位识别系统可以估算出照片拍摄的位置。在全球定位系统可能失灵的情况下,地理定位系统可以进行地点识别,对自动驾驶非常有用。此外,法医警察也可以利用该系统破案

    2024年02月03日
    浏览(38)
  • 点云配准--gicp原理与其在pcl中的使用

    总结:gicp引入了概率信息(使用协方差阵),提出了icp的统一模型,既可以解释点到点和点到面的icp,也在新模型理论的基础上,提出了一种面到面的icp。 论文原文:《Generalized-ICP》 在概率模型中假设存在配准中两个点集, A ^ = { a i ^ } hat{A}=left{hat{a_{i}}right} A ^ = { a i ​

    2024年01月19日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包