利用深度学习框架实现点云配准(以PointNetLK项目为例)

1.前言

之前已经介绍过了基于BnB算法的点云配准应用。熟悉点云配准近年来发展趋势的同学应该知道,目前CVPR,ICCV这些会议,比较主流的方法还是基于深度学习来建立点云的对应关系,尤其以基于PointNet变种网络的方法居多。所以如果要想搞搞点云配准的话,这条技术路线是没办法忽视的。这对完全不懂深度学习的我来说确实是个挑战。但是既然决定深耕点云应用,就不得不面对。综合考虑,选择了Pycharm平台建立项目的实现。当然,很多代码内嵌的方法,是基于linux系统建立的,部分源码在windows上的兼容并不好。因此,我是用了vBox虚拟机和Ubuntu操作系统搭建了linux操作系统。python这门语言当初在学校就学了一点皮毛,平常主要用来画个图。要是真搞起来,怕是还要下点功夫的。闲话少叙,让我们进入正题。

2.Pycharm

首先还是要下载Pycharm软件,Link: Download PyCharm: Python IDE for Professional Developers by JetBrains

下载完成后,需要使用账号激活。这里如果有教育后缀名邮箱的同学,请使用邮箱注册一个账号,这样就可以使用免费的正版Pycharm。

Pycharm比较方便的地方就是扩展包的安装。相比VS需要自己下载自己配置,使用Pycharm可以非常方便的找到对应的功能包,只要点击以下就能够实现下载和安装。

一般比较常用的也就是那几个包,包括pytorch,tensorflow,numpy,matplotlib还有一些专门的数据读取,图形图像显示的包。一般都是针对源码需要进行下载就可以。

默认情况下还是建议选择Ubuntu系统进行配置。原因是很多项目都是基于该系统进行开发与部署的。虽然Pycharm同时支持window平台,但是一些源码的通用性上,还是会常常遇到问题。

3.h5格式文件的理解与操作

如果你了解过点云深度学习的项目,就会发现项目涉及的数据很多都是有h5后缀的单一文件存储全部的训练数据和测试数据。

h5文件中有两个核心的概念:组“group”和数据集“dataset”。 一个h5文件就是 “dataset” 和 “group” 二合一的容器。

dataset :简单来讲类似数组组织形式的数据集合,像 numpy 数组一样工作,一个dataset即一个numpy.ndarray。具体的dataset可以是图像、表格,甚至是pdf文件和excel。 

group:包含了其它 dataset(数组) 和 其它 group ,像字典一样工作。

我的理解是,h5这样的数据组织形式,在进行神经网络训练的时候,就不需要对单独模型进行逐一提取。但是这也提高了对h5数据本身理解的难度。我们没有办法根据后缀了解到文件本身存储了什么样的内容。这是比较尴尬的地方。

对于点云来说,h5文件相当于存储了一组点云数据。这个给出两组对h5数据的操作方法:

3.1 读取h5点云数据并显示:

import h5py
import numpy as np
import os
import math
from open3d import *

filename = './data/test_data/templates.h5'
f = h5py.File(filename, 'r')
templates = np.array(f.get('templates'))
template_ = open3d.geometry.PointCloud()
template_.points = open3d.utility.Vector3dVector(templates[5])
template_.paint_uniform_color([1, 0, 0])
open3d.visualization.draw_geometries([template_])

这里需要注意的是,template.h5文件中存储了不止一个模型,所以在显示的时候,需要指定数据中的index,来实现显示。

3.2 存储h5点云数据:

import h5py
import numpy as np
import os
import math

#imgData stores point cloud:3*M*N  3:point dimension; M: point number in a point cloud; N: point cloud number

if not os.path.exists('store.h5'):
    with h5py.File('store.h5') as f: 
        #这里f的key是可以自己定义的,如果需要查找,使用f.key()查看就可以       
        f['data'] = imgData
        f['labels'] = range(100)

3.3 一些其他数据类型的读取:

有的时候我们需要把其他类型的数据文件读出来,存成h5文件以方便使用。这里我们给出一些方法:

读取ply数据:

import os
from plyfile import PlyData, PlyElement
import numpy as np
import pandas as pd
 
file_dir = '文件的路径'  #文件的路径
plydata = PlyData.read(file_dir)  # 读取文件
data = plydata.elements[0].data  # 读取数据
data_pd = pd.DataFrame(data)  # 转换成DataFrame, 因为DataFrame可以解析结构化的数据
data_np = np.zeros(data_pd.shape, dtype=np.float)  # 初始化储存数据的array
property_names = data[0].dtype.names  # 读取property的名字
for i, name in enumerate(property_names):  # 按property读取数据,这样可以保证读出的数据是同样的数据类型。
    data_np[:, i] = data_pd[name]

h5转换成pcd:

import h5py
import numpy as np
import os
import math
from open3d import *
 
filename = '/home/jp/project/pointnet/sem_seg/indoor3d_sem_seg_hdf5_data/ply_data_all_0.h5'
f = h5py.File(filename, 'r')
 
index_start = 0
index_end = 1
data = f['data']
label = f['label']
one_data = data[index_start:index_end, :, :]
one_label = label[index_start:index_end]
one_data = np.concatenate(one_data, axis=0)
one_label = np.concatenate(one_label, axis=0)
 
 
data_shape = np.shape(one_data)
 
WIDTH = data_shape[0]
POINTS = WIDTH
 
# pack RGB
rgb = []
for i in range(POINTS):
	value = 0
	value += one_data[i, 3]*255*65536
	value += one_data[i, 4]*255*256
	value += one_data[i, 5]*255
	value = value + 16777216
	rgb.append(value)
 
path = '/home/jp/project/test/out/seg.pcd'
if os.path.exists(path):
	os.remove(path)
 
out = open(path, 'a')
 
# headers
out.write('# .PCD v.7 - Point Cloud Data file format\nVERSION .7\nFIELDS x y z rgb\nSIZE 4 4 4 4\nTYPE F F F U\nCOUNT 1 1 1 1')
string = '\nWIDTH ' + str(WIDTH)
out.write(string)
out.write('\nHEIGHT 1\nVIEWPOINT 0 0 -2 0 1 0 0')
string = '\nPOINTS ' + str(POINTS)
out.write(string)
out.write('\nDATA ascii')
 
#datas
for i in range(POINTS):
	string = '\n' + str(one_data[i, 0]) + ' ' + str(one_data[i, 1]) + ' ' +str(one_data[i, 2]) + ' ' + str(int(rgb[i]))
	out.write(string)
 
 
out.close()

4.训练数据库ModelNet40

这里我们介绍下训练数据库ModelNet40。该库为目前主流方法广泛使用的公共数据库,包含共40个大类的若干点云数据。

项目主页链接为:Princeton ModelNet

我是从CSDN里找的链接:modelNet40数据集_modelnet40数据集介绍,modelnet40数据集-深度学习文档类资源-CSDN下载

这个数据库有两种形式,一种是存储了一组off文件,另一种是几个打包好的h5文件。关于h5文件,我们已经介绍过使用方法了,此处不再赘述。

5.基于PointNet的点云配准方法:PointNetLK

源码下载:https://github.com/hmgoforth/PointNetLK

论文:PointNetLK: Pointnetlk: Robust & efficient point cloud registration using pointnet

PointNet是较早提出基于深度学习框架的点云分析方法。通过PointNet,一些点云的关联关系能够被获得,从而指导点云应用,包括分类,分割等。

PointNetLK(PNLK)方法结合了PointNet与Lucas&Kanade(LK)算法,建立配准。LK算法最早用于解决图像对应的问题。结合LK与PointNet, PNLK建立了一个点云配准方法,其创新性包括:

1)调整LK算法以适应PointNet方法的imaging function

2)组合两种算法成一个单一可重复训练的深度神经网络

算法框架为:

输入源点云和模板点云通过共享的MLP和一个对称池函数(symmetric pooling function),以计算全局特征向量:。 雅可比行列式J通过被计算出来。最佳捻度参数被找到,用来迭代的更新的姿态,进而被更新。在训练过程中,一个loss function被使用,基于估计刚体变化和ground truth变换。

一些配准结果如下:

程序基于ModelNet40库给出的一些结果如下:红色的点云为模板,绿色的点云为源,蓝色为变换源到模板的结果。(我有点怀疑这个是作弊,因为没有什么姿态变化)

6.如何使用自己的数据进行实验?

这是很多刚刚入门深度学习的同学比较苦恼的问题。因为不熟悉tensorflow和pytorch这些工具以及python的基本语法,通常下载下来的深度学习源码根本就跑不起来。

即使跑起来了,也是基于原始代码已经做好的数据输出一些结果。我们希望使用我们自己的数据来验证算法的性能。这个过程,老甘也是很头疼。经过一些摸索,总算是得到了一些经验,但是还有很多细节没有搞清楚,希望懂行的专家可以留言交流。

1. 把需要配准的点云数据读入到数组中,我们已经在本文的第三部分给出一些方法。这里,我们使用ply数据来存储原始点云

2. 我们需要将数组数据转换成pytorch能够识别的tensor形式。

3. 代码中默认的数据形式是Float32,为了统一,要做一步数据转换。

代码如下:

sourcePath = 'E:\chen_database\_Registration\_2048PLY\BunnyPart.ply'
templatePath = 'E:\chen_database\_Registration\_2048PLY\Bunny.ply'

#1. 读ply到数组
sourceR = read_plyFile(sourcePath)
templateR = read_plyFile(templatePath)

#2. 数组转换成tensor
source = torch.from_numpy(sourceR)
template = torch.from_numpy(templateR)

#3. tensor中的数据类型转换
sourceF = source.to(torch.float32)
templateF = template.to(torch.float32)

基于上述四步,就可以将我们的数据导入到PNLK中进行配准计算了。以下是结果,不知道为啥,结果完全是错的。这充分说明,如果深度学习框架训练阶段的模型规模不够大,是不能得到优秀的结果的。

由此可见,该方案虽然是目前的主流方法,但是也还是存在一些缺陷,对点云数据初始的状态,以及尺度都会敏感。

猜你喜欢

转载自blog.csdn.net/aliexken/article/details/113646842