使用blender进行点云可视化


使用blender进行点云可视化

最近做了一些点云可视化的工作,感觉应该可以经常复用,特此记录一下相关的一些内容。由于使用blender进行可视化需要借助额外的插件,其仅支持ply格式文件,因此本文将从ply格式文件的生成和处理以及配色选择搭配开始介绍,最后再介绍如何使用blender进行点云可视化。

ply文件生成

ply格式

PLY文件格式是Stanford大学开发的一套三维模型数据格式。可以存放顶点,面片,或其他的一种或者多种。举例读取一种简单的,只包含了顶点数据。数据格式如下所示:

ply
format binary_little_endian 1.0 # 编码格式
comment Created by Open3D # 使用的创建工具
element vertex 1000 # 顶点元素数量
property double x # 下列皆为顶点属性
property double y
property double z
property uchar red
property uchar green
property uchar blue
end_header # 数据头结束符 后接二进制数据

数据格式转换

理论上,按照这个格式可以自己通过编辑文本的方式生成ply文件,但是为了使用和维护的方便,我们选择使用Open3D库进行处理.

从数据格式看出,对于点云数据,我们只需要获得其x,y,z坐标以及rgb颜色值即可。由于我们本次拿到的初始文件格式为x,y,z,label的形式,只需要将label转化为相应的rgb值即可。

这里,由于label数量较多,人工选择色彩容易出现易混淆的情况,这个问题在stackoverflow上已经有很多讨论了,这里我们直接从GitHub上找到了一个自动生成n种区分度较大的颜色的项目,为n种标签生成区分度较大的配色方案,代码如下:

# get_n_colors.py
import colorsys
import random
import matplotlib.pyplot as plt

# hls格式色彩生成算法
def get_n_hls_colors(num):
    hls_colors = []
    i = 0
    step = 360.0 / num
    while i < 360:
        h = i
        s = 90 + random.random() * 10
        l = 50 + random.random() * 10
        _hlsc = [h / 360.0, l / 100.0, s / 100.0]
        hls_colors.append(_hlsc)
        i += step
    return hls_colors

 # generate colors for ply file
def ncolors(num, rgb=False):
    """
    num: points number
    rgb: hls color by Default
    return: hls or rgb color in [0,1]
    """
    if num < 1:
        return rgb_colors
    hls_colors = get_n_hls_colors(num)
    if rgb:
        return [colorsys.hls_to_rgb(h, l, s) for h, l, s in hls_colors]
    else:
        return hls_colors


if __name__ == '__main__':
    n = 13
    rgb_colors = ncolors(n, rgb=True)
    col_map = {}
    for i in range(len(rgb_colors)):   
        col_map[i] = [x for x in rgb_colors[i]]
    print(col_map) # 输出得到的color map
    x = [x for x in range(n)]
    y = [1]*n
    # 使用plt简单查看颜色显示效果
    plt.bar(x, y, color=rgb_colors) # 这里只能正确显示rgb颜色!
    plt.show()

其结果如下:

需要注意的是,这里返回的颜色值都在[0,1]区间内,当时没注意到这一点,增加了很多调试的时间。但是这也是符合后续ply文件需求的(颜色值需要在[0,1]区间内)。

格式转换与ply文件生成

这一步其实相对简单,只需要根据上述生成的标签和颜色的对应关系,使用Open3D提供的一些api直接操作就可以了,代码如下:

from asyncore import read
import open3d as o3d
import numpy as np
import os

col_map = {0: [0.968598945752023, 0.21265177717633077, 0.21265177717633077], 1: [0.9593424395567822, 0.43938701079007103, 0.04942043921503769], 2: [0.9564083671463547, 0.8441574631810681, 0.17065203938934836], 3: [0.7119784414524474, 0.9707467808994562, 0.06505759283492496], 4: [0.4216596845499031, 0.9746740070101805, 0.20045395556579249], 5: [0.09410395585610232, 0.9975270445459243, 0.22316439709750563], 6: [0.08875664925141424, 0.955616406724503, 0.5841050820931795], 7: [0.22495255248804136, 0.9708864249338678, 0.9708864249338685], 8: [0.15853756200060765, 0.6307409064043225, 0.9848934147071097], 9: [0.08291138196093573, 0.20910827660750841, 0.9662896444869518], 10: [0.3989335698651475, 0.17449579837215634, 0.9600279985976244], 11: [0.7194960807027336, 0.07306822754967035, 0.9780672219639588], 12: [0.9692772940469393, 0.05250565523033324, 0.8383099170731375], 13: [0.9881871645219139, 0.04214938209626784, 0.4475941459929724]}

def txt2ply(txt_path, ply_path, label=False):
    """
    Convert txt file to ply file.
    label = True: the last column is label: x y z label
    label = False: the last column is color: x y z r g b
    """
    with open(txt_path, 'r') as f:
        lines = f.readlines()
    points = []
    colors = []
    if label:
        for line in lines:
            line = line.strip().split()
            points.append([float(line[0]), float(line[1]), float(line[2])])
            colors.append(col_map[int(line[3])])
            #colors.append(col_map[int(float(line[3]))])
    else:
        for line in lines:
            line = line.strip().split()
            points.append([float(line[0]), float(line[1]), float(line[2])])
            colors.append([float(line[3])/255, float(line[4])/255, float(line[5])/255])
    # for line in lines:
    #     line = line.strip().split()
    #     points.append([float(line[0]), float(line[1]), float(line[2])])
    #     colors.append(col_map[int(float(line[3]))])
    #     print(int(float(line[3])))
    # print(colors)
    points = np.array(points)
    mean_x, mean_y, mean_z = np.mean(points, axis=0)
    # move to center
    points[:, 0] -= mean_x
    points[:, 1] -= mean_y
    points[:, 2] -= mean_z
    # print(points)
    colors = np.array(colors)
    # print(colors)
    write_ply(ply_path, points, colors)


def write_ply(ply_path, points, colors):
    """
    Write ply file.
    """
    pcd = o3d.geometry.PointCloud()
    # print(colors)
    pcd.points = o3d.utility.Vector3dVector(points)
    pcd.colors = o3d.utility.Vector3dVector(colors)
    o3d.io.write_point_cloud(ply_path, pcd)


def read_ply(ply_path):
    """
    Read ply file.
    """
    pcd = o3d.io.read_point_cloud(ply_path)
    return pcd

def generate_testData(num=1000):
    points = np.random.randint(0, 10, size=(num, 3))
    labels = np.random.randint(0, 13, size=num)
    data = np.hstack((points, labels.reshape(-1, 1)))
    with open('testData.txt', 'w') as f:
        for line in data:
            f.write(' '.join(map(str, line)) + '\n')

if __name__ == '__main__':
    data_path = 'data'
    save_path = 'ply'
    if not os.path.exists(save_path):
        os.mkdir(save_path)
    for file in os.listdir(data_path):
        txt2ply(os.path.join(data_path, file), os.path.join(save_path, file[:-4]+'.ply'), label=False)

为了能够在中心展示点云数据,在24行处将点云进行了平移操作;blender中的点云坐标数值不能太大,一般10左右就可以。使用Open3D进行初步可视化结果如下:

这里,ply文件的转换就完成了,下面就进行blender的可视化操作

blender可视化

由于我们想要达到一个点云旋转的效果,``Open3d`只能静态展示,因此需要借用blender这类专业3D建模软件。首先我们将介绍如何使用blender可视化一个点云,接着我们再介绍如何实现旋转的效果,最后说明一下如何导出视频.

点云可视化-bpy插件

使用blender进行点云可视化需要借助一个插件bpy,插件的功能和参数非常多,这里我们只需要用到几个比较简单的功能即可

安装

安装主要参考这篇博文,整体过程比较简单。首先需要将bpy插件从GitHub上下载下来,然后在菜单的“编辑->偏好设置->插件->安装”完成,选择需要安装的脚本文件即可,对于bpy插件即选择space_view3d_point_cloud_visualizer.py这个文件即可。然后在blender的右侧菜单就能看到该插件了。

数据导入

首先新建一个blender项目,删除项目初始的cube

然后新建一个空物体

单击新建的空物体,在右侧插件中选择要可视化的ply文件,再点击“自由线”即可绘制出点云

可以调整参数对点云的点大小,透明度等效果进行调节

旋转效果生成

第一步我们已经得到了点云的静态可视化结果,下一步我们就要让点云旋转起来。说是点云旋转,但由于运动是相对的,我们可以通过旋转相机来实现相同的效果。下面我们就来设置相机旋转,主要参考教程的内容。

首先切换到顶视图(菜单栏:视图->视图->顶视图),然后添加一个圆形曲线(菜单栏: 添加->曲线->圆环),按“S”键调整圆环大小,使相机顶点处于圆环之上

调整到前视图,将圆环移动到和相机端点相同高度(快捷键“G”),然后为相机添加“跟随路径”约束(在右侧菜单栏),目标就选择刚才设置的曲线,并同时点击“动画路径”按钮。此时相机会偏移到较远的位置,找到相机之后进行坐标归零操作(alt+G),操作完成后效果如图,此时相机已经可以跟随圆环选择,但是视角不正确

接下来就是调整视角的操作。首先将鼠标移动到窗口右上角,点击并拖动可以新建一个工作窗口,其中一个窗口进入相机的活动视角(右键相机:设置活动摄像机)。再为相机添加一个标准约束,目标就选择空物体即可,这样就完成了初步的设置,再调整一下相机成像窗口的大小即可。最终效果如下图所示,根据这种方法还可以调节速度,定义其他更复杂的轨迹,但是目前展示没有用到,因此先到这一步即可。

视频导出和生成

由于blender无法渲染空物体,因此视频的导出需要借助插件本身来完成。点击插件的“渲染->animation”,即可生成相机视角的帧序列。此时生成的png图像默认为透明背景,导出视频后为黑色背景,因此我们先将其转换为jpg格式,即默认为白色背景:for i in *.png; do sips -s format jpeg $i --out ${i%.*}.jpg;done;

然后,我们借助ffmpeg工具即可将所有帧生成一个完整的视频,命令为:ffmpeg -threads 8 -i pcv_render_%03d.jpg output.mp4,最后效果如下:


评论
 本篇
使用blender进行点云可视化 使用blender进行点云可视化
使用blender进行点云可视化最近做了一些点云可视化的工作,感觉应该可以经常复用,特此记录一下相关的一些内容。由于使用blender进行可视化需要借助额外的插件,其仅支持ply格式文件,因此本文将从ply格式文件的生成和处理以及配色选择搭
2022-08-30
下一篇 
zotero通过multiple_profiles实现多文库 zotero通过multiple_profiles实现多文库
zotero通过multiple-profiles实现多文库最近计划写一篇综述,这就需要读大量的文章,然而数百篇的文章如何有效管理就是一个很大的问题。简单的使用文件夹+tags的方法已经不能解决这个问题了,因此在和导师保持一致多方对比之后,
2022-02-11
  目录