带你玩转 3D 检测和分割 (三):有趣的可视化(上)

简介: 在 MMDetection3D 的官方可视化文档中,我们已经提供了如何进行可视化的一些指令和效果,这篇文章我们将从代码层级介绍可视化的细节过程,带大家一探究竟。

小伙伴们好呀,3D 检测和分割系列文章继续更新啦~在第一篇文章中,我们带领大家了解了整个框架的大致流程。第二篇文章我们给大家解析了 MMDetection3D 中的坐标系和核心组件 Box,今天我们将带大家看看 3D 场景中的可视化组件 Visualizer,如何在多个模态数据上轻松可视化并且自由切换?为什么在可视化的时候经常出现一些莫名其妙的问题?

640.gif

话不多说,接下来就让我们进入今天的正题吧~


在 MMDetection3D 的官方可视化文档中,我们已经提供了如何进行可视化的一些指令和效果,这篇文章我们将从代码层级介绍可视化的细节过程,带大家一探究竟。


640.png

可视化代码解析



在线可视化 Visualizer


MMDetection3D 基于 Open3D 构建了一个在线 Visualizer,用于在有 GUI 界面的情况下提供实时可视化结果,Open3D 提供了非常丰富的功能。相关代码位于 mmdet3d/core/visualizer/open3d_vis.py 。

640.png

MMDetection3D 目前只使用了 Open3D 的部分 API 进行可视化,同时也非常容易实现可视化功能的扩展,目前 Visualizer 支持绘制 3D 框的 add_bboxes,绘制点云分类结果的 add_seg_mask,以及统一的 show 显示方法。在这一节中我们不具体介绍 Open3D 的各个 API 的具体作用,而是带大家来看看如何使用 Open3D 的 API 完成需求,包括实现自己的自定义的可视化需求。

#----------------  mmdet3d/core/visualizer/open3d_vis.py ----------------#
class Visualizer(object):
    r"""Online visualizer implemented with Open3d."""
    def __init__(self,
                 points,
                 bbox3d=None,
                 save_path=None,
                 points_size=2,
                 point_color=(0.5, 0.5, 0.5),
                 bbox_color=(0, 1, 0),
                 points_in_box_color=(1, 0, 0),
                 rot_axis=2,
                 center_mode='lidar_bottom',
                 mode='xyz'):
        super(Visualizer, self).__init__()
        assert 0 <= rot_axis <= 2
        # 调用 Open3D 的 API 来初始化 visualizer
        self.o3d_visualizer = o3d.visualization.Visualizer()
        self.o3d_visualizer.create_window()
        # 创建坐标系帧
        mesh_frame = geometry.TriangleMesh.create_coordinate_frame(
            size=1, origin=[0, 0, 0])
        self.o3d_visualizer.add_geometry(mesh_frame)
        # 设定点云尺寸
        self.points_size = points_size
        # 设定点云颜色
        self.point_color = point_color
        # 设定 3D 框颜色
        self.bbox_color = bbox_color
        # 设定 3D 框内点云的颜色
        self.points_in_box_color = points_in_box_color
        self.rot_axis = rot_axis
        self.center_mode = center_mode
        self.mode = mode
        self.seg_num = 0
        # 绘制点云
        if points is not None:
            self.pcd, self.points_colors = _draw_points(
                points, self.o3d_visualizer, points_size, point_color, mode)
    def add_bboxes(self, bbox3d, bbox_color=None, points_in_box_color=None):
        """Add bounding box to visualizer."""
        if bbox_color is None:
            bbox_color = self.bbox_color
        if points_in_box_color is None:
            points_in_box_color = self.points_in_box_color
        _draw_bboxes(bbox3d, self.o3d_visualizer, self.points_colors, self.pcd,
                     bbox_color, points_in_box_color, self.rot_axis,
                     self.center_mode, self.mode)
    def add_seg_mask(self, seg_mask_colors):
        """Add segmentation mask to visualizer via per-point colorization."""
        self.seg_num += 1
        offset = (np.array(self.pcd.points).max(0) -
                  np.array(self.pcd.points).min(0))[0] * 1.2 * self.seg_num
        mesh_frame = geometry.TriangleMesh.create_coordinate_frame(
            size=1, origin=[offset, 0, 0])  # create coordinate frame for seg
        self.o3d_visualizer.add_geometry(mesh_frame)
        seg_points = copy.deepcopy(seg_mask_colors)
        seg_points[:, 0] += offset
        _draw_points(
            seg_points, self.o3d_visualizer, self.points_size, mode='xyzrgb')
    def show(self, save_path=None):
        """Visualize the points cloud."""
        self.o3d_visualizer.run()
        if save_path is not None:
            self.o3d_visualizer.capture_screen_image(save_path)
        self.o3d_visualizer.destroy_window()
        return

如上述代码所示,在检测任务中,关于 3D 框的绘制主要是通过调用 add_bboxes 方法,我们可以在绘制 3D 框的时候,通过 bbox_color 和 points_in_box_color 分别设定框的颜色以及包含在框内的点云的颜色,而其最终调用的还是 _draw_bboxes 方法。而 add_seg_masks 则是用来绘制分割结果,其中传入的 seg_mask_colors 是带颜色(rgb)信息的点云。需要注意的是,我们在绘制点云分割结果的时候,不会在原点云上进行颜色的更新,因为如果同时在原点云上绘制预测结果和真值标签往往会有重叠现象,所以我们对每一个分割结果的点云图都会沿 x 轴设置较大的偏移量,单个场景会生成多个分割结果图,而最终对每个分割结果图都会调用 _draw_points 方法进行绘制。当完成所有的绘制之后,就可以调用 show 方法启动 Visualizer,显示场景绘制结果。除此以外,我们也可以很轻松地通过给 Visualizer 添加方法实现自己的需求。


我们可以先来看看如何绘制点云:

#----------------  mmdet3d/core/visualizer/open3d_vis.py ----------------#
import open3d as o3d
from open3d import geometry
def _draw_points(points,
                 vis,
                 points_size=2,
                 point_color=(0.5, 0.5, 0.5),
                 mode='xyz'):
    # 获取 Open3D Visualizer 的渲染设置,更改点云尺寸
    vis.get_render_option().point_size = points_size 
    if isinstance(points, torch.Tensor):
        points = points.cpu().numpy()
    points = points.copy()
    # 构建 Open3D 中提供的 gemoetry 点云类
    pcd = geometry.PointCloud()
    # 如果传入的点云 points 只包含位置信息 xyz
    # 根据指定的 point_color 为每个点云赋予相同的颜色
    if mode == 'xyz':
        pcd.points = o3d.utility.Vector3dVector(points[:, :3])
        points_colors = np.tile(np.array(point_color), (points.shape[0], 1))
    # 如果传入的点云 points 本身还包含颜色信息(通常是分类预测结果或者标签)
    # 直接从 points 获取点云的颜色信息
    elif mode == 'xyzrgb':
        pcd.points = o3d.utility.Vector3dVector(points[:, :3])
        points_colors = points[:, 3:6]
        # 将颜色归一化到 [0, 1] 用于 Open3D 绘制
        if not ((points_colors >= 0.0) & (points_colors <= 1.0)).all():
            points_colors /= 255.0
    else:
        raise NotImplementedError
    # 为点云着色
    pcd.colors = o3d.utility.Vector3dVector(points_colors)
    # 将点云加入到 Visualizer 中
    vis.add_geometry(pcd)
    return pcd, points_colors

以上就是绘制点云的过程,当我们了解了如何绘制点云后,我们就可以实现自己的需求了,比如有社区用户希望能够给点云的强度进行染色,当然我们可以直接加在 _draw_points 方法内,但是为了演示更清晰,这里我们可以在 Visiualizer 内实现一个 render_points_intensity 方法做演示:

#----------------  mmdet3d/core/visualizer/open3d_vis.py ----------------#
import open3d as o3d
from open3d import geometry
def render_points_intensity(self, points, vis):
    # 假设传入的 points 包含 x-y-z-intensity 四个维度
    # 自定义一个 colormap
    colormap = np.array([[128, 130, 120],
                         [235, 0, 205],
                         [0, 215, 0],
                         [235, 155, 0]]) / 255.0
    points = points.copy()
    # 构建 Open3D 中提供的 gemoetry 点云类
    pcd = geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(points[:, :3])
    points_intensity = points[:, 3]  # intensity
    points_colors = [colormap[int(points_intensity[i]) % colormap.shape[0]] for i in range(points_intensity.shape[0])]
    pcd.colors = o3d.utility.Vector3dVector(points_colors)  # 根据 intensity 为点云着色
    # 将点云加入到 Visualizer 中
    vis.add_geometry(pcd)

还有比如在社区用户的反馈中,想在可视化时调整场景的背景颜色,我们可以很简单地添加一个 change_bg_color 方法:

def change_bg_color(self, bg_colors):
    """Change back ground color of Visualizer"""
    # 获取渲染配置
    opt = self.o3d_visualizer.get_render_option()
    # 将场景背景颜色设为指定颜色
    opt.background_color = bg_colors

接下来我们具体来看看如何在点云中绘制 3D 框  _draw_bboxes:

#----------------  mmdet3d/core/visualizer/open3d_vis.py ----------------#
def _draw_bboxes(bbox3d,
                 vis,
                 points_colors,
                 pcd=None,
                 bbox_color=(0, 1, 0),
                 points_in_box_color=(1, 0, 0),
                 rot_axis=2,
                 center_mode='lidar_bottom',
                 mode='xyz'):
    in_box_color = np.array(points_in_box_color)
    for i in range(len(bbox3d)):
        center = bbox3d[i, 0:3]
        dim = bbox3d[i, 3:6]
        yaw = np.zeros(3)
        yaw[rot_axis] = -bbox3d[i, 6]
        # 在 Open3D 中需要将 yaw 朝向角转为旋转矩阵
        rot_mat = geometry.get_rotation_matrix_from_xyz(yaw)
        # 底部中心点转为几何中心店
        if center_mode == 'lidar_bottom':
            center[rot_axis] += dim[
                rot_axis] / 2  
        elif center_mode == 'camera_bottom':
            center[rot_axis] -= dim[
                rot_axis] / 2  
        box3d = geometry.OrientedBoundingBox(center, rot_mat, dim)
        line_set = geometry.LineSet.create_from_oriented_bounding_box(box3d)
        line_set.paint_uniform_color(bbox_color)
        # 在 Visualizer 中绘制 Box
        vis.add_geometry(line_set)
        # 更改 3D Box 中的点云颜色
        if pcd is not None and mode == 'xyz':
            indices = box3d.get_point_indices_within_bounding_box(pcd.points)
            points_colors[indices] = in_box_color
    # 更新点云颜色
    if pcd is not None:
        pcd.colors = o3d.utility.Vector3dVector(points_colors)
        vis.update_geometry(pcd)

需要注意 MMDetection3D 中在 3D 视角可视化时的 bbox3d 是 (x, y, z, x_size, y_size, z_size, yaw) 格式的,同时在 3D 视角可视化的时候都是会被转换到深度坐标系,而且 x, y, z 表示的是底部中心点位置,而并不是几何中心点位置。所以在用 Open3D 可视化的时候需要从底部中心点转换为几何中心点。


使用 MeshLab 可视化


对于 MeshLab 来说,可视化需要提供相应的 obj 文件,文件内包含点云信息、分割结果、检测结果等等。而在目前 MMDetection3D 中,我们提供下述方法,可以将模型输出结果转换为 obj 文件。

#----------------  mmdet3d/core/visualizer/show_result.py ----------------#
def _write_obj(points, out_filename):
    """Write points into ``obj`` format for meshlab visualization."""
    N = points.shape[0]
    fout = open(out_filename, 'w')
    # 保存点云的时候如果有颜色信息也会进行保存
    for i in range(N):
        if points.shape[1] == 6:
            c = points[i, 3:].astype(int)
            fout.write(
                'v %f %f %f %d %d %d\n' %
                (points[i, 0], points[i, 1], points[i, 2], c[0], c[1], c[2]))
        else:
            fout.write('v %f %f %f\n' %
                       (points[i, 0], points[i, 1], points[i, 2]))
    fout.close()
def _write_oriented_bbox(scene_bbox, out_filename):
    """Export oriented (around Z axis) scene bbox to meshes."""
    def heading2rotmat(heading_angle):
        rotmat = np.zeros((3, 3))
        rotmat[2, 2] = 1
        cosval = np.cos(heading_angle)
        sinval = np.sin(heading_angle)
        rotmat[0:2, 0:2] = np.array([[cosval, -sinval], [sinval, cosval]])
        return rotmat
    # 将 MMDetection3D 内部格式的 3D 框转换为 trimesh 格式
    def convert_oriented_box_to_trimesh_fmt(box):
        ctr = box[:3]
        lengths = box[3:6]
        trns = np.eye(4)
        trns[0:3, 3] = ctr
        trns[3, 3] = 1.0
        trns[0:3, 0:3] = heading2rotmat(box[6])
        box_trimesh_fmt = trimesh.creation.box(lengths, trns)
        return box_trimesh_fmt
    if len(scene_bbox) == 0:
        scene_bbox = np.zeros((1, 7))
    # 利用 trimesh 构建场景
    scene = trimesh.scene.Scene()
    for box in scene_bbox:
        scene.add_geometry(convert_oriented_box_to_trimesh_fmt(box))
    mesh_list = trimesh.util.concatenate(scene.dump())
    # 保存为 obj 文件
    trimesh.io.export.export_mesh(mesh_list, out_filename, file_type='obj')
    return

_write_points 和 _write_oriented_bbox 分别用来保存点云(及其分割结果)和 3D Box 为 obj 文件,在实际使用的时候只需要将生成的 obj 文件导入到 MeshLab 即可。


文章来源:【OpenMMLab

 2022-04-25 18:04






目录
相关文章
|
机器学习/深度学习 算法 计算机视觉
3D目标检测框架 MMDetection3D环境搭建 docker篇
本文介绍如何搭建3D目标检测框架,使用docker快速搭建MMDetection3D的开发环境,实现视觉3D目标检测、点云3D目标检测、多模态3D目标检测等等。
1604 0
|
机器学习/深度学习 存储 编解码
Open3d系列 | 3. Open3d实现点云上采样、点云聚类、点云分割以及点云重建
Open3d系列 | 3. Open3d实现点云上采样、点云聚类、点云分割以及点云重建
14251 1
Open3d系列 | 3. Open3d实现点云上采样、点云聚类、点云分割以及点云重建
|
传感器 机器学习/深度学习 编解码
最新综述!基于视觉的自动驾驶环境感知(单目、双目和RGB-D)
目相机使用来自单个视点的图像数据作为输入来估计对象深度,相比之下,立体视觉是基于视差和匹配不同视图的特征点,深度学习的应用也进一步提高了准确性。此外,SLAM可以建立道路环境模型,从而帮助车辆感知周围环境并完成任务。本文介绍并比较了各种目标检测和识别方法,然后解释了深度估计的发展,并比较了基于单目、立体和RGB-D传感器的各种方法,接下来回顾并比较了SLAM的各种方法。最后总结了当前存在的问题,并提出了视觉技术的未来发展趋势。
最新综述!基于视觉的自动驾驶环境感知(单目、双目和RGB-D)
|
存储 数据采集 数据可视化
Open3d系列 | 1. Open3d实现点云数据读写、点云配准、点云法向量计算
Open3d系列 | 1. Open3d实现点云数据读写、点云配准、点云法向量计算
17081 1
Open3d系列 | 1. Open3d实现点云数据读写、点云配准、点云法向量计算
|
存储 传感器 数据可视化
3D目标检测数据集 KITTI(标签格式解析、3D框可视化、点云转图像、BEV鸟瞰图)
本文介绍在3D目标检测中,理解和使用KITTI 数据集,包括KITTI 的基本情况、下载数据集、标签格式解析、3D框可视化、点云转图像、画BEV鸟瞰图等,并配有实现代码。
4107 1
|
6月前
|
监控 容灾 测试技术
第三方API的稳定性如何保障?
第三方API的稳定性对电商至关重要,直接影响业务连续性和用户体验。为确保稳定,可采取以下措施:选择可靠提供商、评估技术架构高可用性、实现限流重试机制、实时监控预警、充分测试、优化网络连接、使用API网关、保持与提供商沟通、制定应急预案及定期评估改进。这些方法能有效提升API稳定性,保障业务高效运行和用户满意度。
206 0
|
存储 安全 数据安全/隐私保护
Docker的安全性体现在哪些方面?
综上所述,Docker 通过一系列的安全机制和措施,为用户提供了相对可靠的安全保障。然而,如同任何技术一样,Docker 的安全也需要持续的关注和维护,用户应保持警惕,不断加强安全管理和防范措施,以应对不断变化的安全威胁。在利用 Docker 带来便利的同时,也要确保其安全性,为应用的稳定运行和数据的安全提供坚实的保障。
483 60
|
存储 Ubuntu 机器人
机械臂手眼标定详解
这篇文章是关于机械臂手眼标定的详细教程,包括了使用ROS1 Noetic和Realsense D415相机在Ubuntu 20.04环境下进行标定的步骤和配置方法。
1839 0
机械臂手眼标定详解
|
人工智能 数据挖掘 大数据
538个代码示例!麻省理工教授的Python程序设计+人工智能案例实践
Python简单易学,且提供了丰富的第三方库,可以用较少的代码完成较多的工作,使开发者能够专注于如何解决问题而只花较少的时间去考虑如何编程。 此外,Python还具有免费开源、跨平台、面向对象、胶水语言等优点,在系统编程、图形界面开发、科学计算、Web开发、数据分析、人工智能等方面有广泛应用。 尤其是在数据分析和人工智能方面,Python已成为最受开发者欢迎的编程语言之一,不仅大量计算机专业人员选择使用Python进行快速开发,许多非计算机专业人员也纷纷选择Python语言来解决专业问题。 由于Python应用广泛,关于Python的参考书目前已经有很多,但将Python编程与数据分析、人工智
|
缓存 自然语言处理 并行计算
Python 提速大杀器之 numba 篇
你是不是曾经有这样的苦恼,python 真的太好用了,但是它真的好慢啊(哭死) ; C++ 很快,但是真的好难写啊,此生能不碰它就不碰它。老天啊,有没有什么两全其美的办法呢?俗话说的好:办法总是比困难多,大家都有这个问题,自然也就有大佬来试着解决这个问题,这就请出我们今天的主角: numba
1268 0
Python 提速大杀器之 numba 篇

热门文章

最新文章