December 14, 2018

3913 words 8 mins read

深度学习在几何空间中的应用(一):三维可视化

深度学习在几何空间中的应用(一):三维可视化

点云可视化的探索。

前言

  目前点云方面的研究是一个热门方向,点云分类、零件分割、语义分割等研究正处于热门研究时期; 深度学习大热以来,渐渐地把二维世界里的框架,或移植,或改进,运用到三维世界中来,那么如何将点云可视化呢?This is a question. 下面让我们一起来探索。

可视化

点云数据准备

  如无特别说明,本系列使用的数据是ModelNet40,ModelNet40包含了来自40类的12311个三维形状,其中9843个文件用来训练,2468个文件用来测试。

系统环境

  • 操作系统:ubuntu16.04或者windows10,建议在Ubuntu下开发
  • 编程软件:PyCharm
  • 语言环境:Python3.6

python包依赖

  使用python3.6读取点云文件,需要提取安装numpyh5pymayavimatplotlib,其中安装mayavi的方法如下(前提:python3.x和64位系统):

pip install mayavi    # mayavi包
pip install PyQt5     # GUI

故所需要的包有:

import os
import sys
import numpy as np
import h5py
from mayavi import mlab
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt

下载数据集ModelNet40

  • 在线方式(ubuntu16.04系统下)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))    # 当前py文件所在的路径
print(BASE_DIR)                                          # 显示py文件当前路径
sys.path.append(BASE_DIR)                                # 加入到系路径里

# download modelnet40 dataset
# 将数据集下载到当前py文件所处路径里的data文件夹里,假设当前py文件在
# BASE_DIR=E:/PointCloud路径里,那么数据集将下载到E:/PointCloud
DATA_DIR = os.path.join(BASE_DIR, 'data')                
if not os.path.exists(DATA_DIR):                         # 如果不存在此文件夹
    os.mkdir(DATA_DIR)                                   # 新建data文件夹

# 数据集应在BASE_DIR/data/modelnet40_ply_hdf5_2048里,如果不存在
# BASE_DIR/data/modelnet40_ply_hdf5_2048文件夹,则下载数据集
# modelnet40_ply_hdf5_2048.zip并解压,且输出.zip文件
if not os.path.exists(os.path.join(DATA_DIR, 'modelnet40_ply_hdf5_2048')):  
    # 数据集文件网址
    www = 'https://shapenet.cs.stanford.edu/media/modelnet40_ply_hdf5_2048.zip'
    # 返回path最后的文件名,即zipfile=modelnet40_ply_hdf5_2048.zip
    zipfile = os.path.basename(www)   
    os.system('wget %s; unzip %s' % (www, zipfile))    # wget:下载,unzp: 解压
    # zipfile=modelnet40_ply_hdf5_2048.zip
    # zipfile[:-4]=modelnet40_ply_hdf5_2048
    # 把解压后的文件夹modelnet40_ply_hdf5_2048移到BASE_DIR/data/路径下
    os.system('mv %s %s' % (zipfile[:-4], DATA_DIR))
    os.system('rm %s' % (zipfile))    # 删除压缩包  
  • 离线方式(ubuntu16.04系统或windows10) 如果没有合适的ubuntu环境,那么我们首先通过网站下载数据集,然后利用windows上的解压工具解压到对应的文件夹,文件夹可以对应到在线方式里的路径,本文采用的这种方法,简单粗暴,但是不推荐,不利于代码的自动化。

运行代码或者解压文件夹,将在BASE_DIR/data/modelnet40_ply_hdf5_2048/文件夹里看到一下内容,注意BASE_DIR指的是当前py文件的路径。 ModelNet40.png

读取点云

  由上图可以看到,点云文件由.h5文件格式来存储,有关h5格式的介绍请移步这里,下面我们就来读取点云文件,代码如下(以读取ply_data_train1.h5为例):

"""
    读取H5文件里的键值
"""
import h5py
# ply_data_train1.h5路径
H5_FILE = './data/modelnet40_ply_hdf5_2048/ply_data_train1.h5'


def read_h5file_keys(h5_filename):   # 定义python函数,函数体前后空两行
    """
    读取H5文件里的键值
    :param h5_filename:
    :return:
    """
    with h5py.File(h5_filename) as f:
        return [item for item in f.keys()]


keys = read_h5file_keys(H5_FILE)
print("key is : %s" % keys)

运行,得到以下结果:

key is : ['data', 'faceId', 'label', 'normal']

其中 'data'对应点云xyz坐标 'label'对应标签 'normal'对应法向量 'faceId'未知 我们感兴趣的是键值datalabel

紧接着,读取点云数据,代码如下:

with h5py.File(H5_FILE) as f:
    data = f['data'][:]
    label = f['label'][:]

  由于数据集较大,所以是否读取成功,可以利用PyCharm里的Scientific Mode来查看,开启姿势位View——>Scientific Mode,可追踪变量变化,如下图

运行程序后,就会看到这样的情形,

  点击变量data后的**_View as Array_**,就可以在右边的SciView里看见数据,**ply_data_train1.h5**里共有 **2048**个物体的点云数据,所以范围在data[0]到data[2047]之间,每一个物体共有 **2048** 个点,每个点的坐标位(x, y, z).

可以打印出datalabel的形状:

print(data.shape)    # (2048, 2048, 3)
print(label.shpae)   # (2048, 1)

显示点云

  • 获取坐标
import numpy as np
from mayavi import mlab

x = []
y = []
z = []
num_obj = 1079             # 比如显示第756 + 1个物体
max_num_point = 2048      # 最大渲染点的个数,最大是2048个点

# 依次获取x、y、z的坐标 —— 1D array
x = data[num_obj, 0:max_num_point, 0]   # shape为(max_num_point, 1)
y = data[num_obj, 0:max_num_point, 1]   # shape为(max_num_point, 1)
z = data[num_obj, 0:max_num_point, 2]   # shape为(max_num_point, 1)
  • 点云可视化方法一 —— mayavi
# ——————————————————————————————————————————————————————————————
#                        mayavi可视化
# ——————————————————————————————————————————————————————————————
# s为points3d()函数的第四个参数,与x,y和z具有相同的形状,为每个点
# 提供关联的标量值,或者返回标量值的函数f(x,y,z)
# 此标量值可用于调整点的颜色和大小。
s = np.sqrt(x ** 2 + y ** 2 + z ** 2 )
# 设置背景为白色,画布大小为300 * 200
fig = mlab.figure(bgcolor=(1, 1, 1), size=(450, 300))
# https://docs.enthought.com/mayavi/mayavi/auto/mlab_helper_functions.html#mayavi.mlab.points3d
figure = mlab.points3d(x, y, z, s, mode="point", colormap='spectral', scale_factor=1)
mlab.show()    # 显示3D点云图

  Notice: 使用这种方法,电脑较卡顿。 在这里,使用了mlab.points3d()函数来进行可视化,具体参数这里,需要注意的是,坐标(x, y, z)各为1D array,需要与mayavi里的surf等方法进行区别,在使用时,注意追踪变量变化,可视化结果

  • 点云可视化方法一 —— matplotlib
# ——————————————————————————————————————————————————————————————
#                matplotlib——Axes3D可视化
# ——————————————————————————————————————————————————————————————
# 使用Axes3D()创建3D图像对象
fig = plt.figure()
ax = Axes3D(fig)
# 调用散点图绘制方法绘图并显示出来
ax.scatter(x, y, z)
plt.show()

可视化结果

# shape_name.txt文件路径
SHAPE_NAME = '../data/modelnet40_ply_hdf5_2048/shape_names.txt'
# 显示对应物体的名称
# 读取SHAPE_NAME文件,并存储为numpy数组形式
shape_name_list = np.loadtxt(SHAPE_NAME, dtype=bytes).astype(str)
# 查询该物体名称
shape_name = shape_name_list[label[num_obj]]

print('label is: %s' % shape_name)

总结

  • mayavi和TVTK配合可以自定义点云颜色,可支持自定义点云颜色,官方范例请点击这里,mayavi运行时较卡顿,自定义程度较高,可视化语义分割和场景分割时很有用;
  • matplotlib库里的Axes3D对象可视化点云方法较为简单,自定义程度较低,在论文中要想显示精美的可视化效果图,不太建议用matplotlib库;

后续工作

  除了用这两个库,还有一些python工具可以进行可视化,如

  我的研究发现时利用深度学习对点云数据集进行分类、场景分割、语义分割等,可视化工具是必不缺少的一环,下一步准备学习meshlab,思路是把点云及相关颜色信息保存成.obj文件,然后在meshlab里显示,具体工作请关注我的博客,如果志同道合者,十分欢迎与联系,这是我的邮箱

  本文的完整代码如下(已在win10环境下,在PyCharm里运行成功,数据集是提前下好的,建议在Ubuntu系统尝试,体会linux的命令):

#! python3
# -*- coding:utf-8 -*-

"""
    Author     : adoredee
    Date       : November 28th 2018
    BlogName   : www.kangzhiheng.top
    Contact    : kangzhiheng@sjtu.edu.cn
    点云显示
"""

import os
import sys
import numpy as np
import h5py

from mayavi import mlab
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt

BASE_DIR = os.path.dirname(os.path.abspath(__file__))    # 当前py文件所在的路径
sys.path.append(BASE_DIR)                                # 显示py文件当前路径
print("the path is: %s" % BASE_DIR)                      # 加入到系路径里

# ply_data_train1.h5路径
H5_FILE = '../data/modelnet40_ply_hdf5_2048/ply_data_train1.h5'
# shape_names.txt文件路径
SHAPE_NAME = '../data/modelnet40_ply_hdf5_2048/shape_names.txt'


def read_h5file_keys(h5_filename):
    """
    读取H5文件里的键值
    :param h5_filename:
    :return:
    """
    with h5py.File(h5_filename) as f:
        return [item for item in f.keys()]


keys = read_h5file_keys(H5_FILE)
print("key is : %s" % keys)    # ['data', 'faceId', 'label', 'normal']

with h5py.File(H5_FILE) as f:
    data = f['data'][:]    # 读取主键'data'的值
    label = f['label'][:]

print(data.shape)    # 2048组,每组2048个点,每个的值位(x,y,z)
print(label.shape)

x = []
y = []
z = []

num_obj = 1079              # 比如显示第num_obj + 1个物体
max_num_point = 2048      # 最大渲染点的个数,最大是2048个点

x = data[num_obj, 0:max_num_point, 0] * 10  # shape为(max_num_point, 1)
y = data[num_obj, 0:max_num_point, 1] * 10  # shape为(max_num_point, 1)
z = data[num_obj, 0:max_num_point, 2] * 10  # shape为(max_num_point, 1)


# ——————————————————————————————————————————————————————————————
#                        mayavi可视化
# ——————————————————————————————————————————————————————————————
# s为points3d()函数的第四个参数,与x,y和z具有相同的形状,为每个点
# 提供关联的标量值,或者返回标量值的函数f(x,y,z)
# 此标量值可用于调整点的颜色和大小。
s = np.sqrt(x ** 2 + y ** 2 + z ** 2)

# visualization
fig = mlab.figure(bgcolor=(1, 1, 1), size=(450, 300))
figure = mlab.points3d(x, y, z, s, mode="point", colormap='spectral', scale_factor=0.25)

mlab.show()

'''
# ——————————————————————————————————————————————————————————————
#                         Axes3D可视化
# ——————————————————————————————————————————————————————————————
# 开始绘图
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(x, y, z)

plt.show()
'''
# 显示对应物体的名称
# 读取SHAPE_NAME文件,并存储为numpy数组形式
shape_name_list = np.loadtxt(SHAPE_NAME, dtype=bytes).astype(str)
# 查询该物体名称
shape_name = shape_name_list[label[num_obj]]

print('label is: %s' % shape_name)