Visualization Tutorial

Introduction

This tutorial describes how to use Zivid SDK and third party libraries to visualize 3D and 2D data captured by a Zivid camera.

Prerequisites

This tutorial starts with a Zivid Frame. See: Capture Tutorial for more information on how to capture a frame.

Point Cloud

Zivid SDK in C++ and C#

Having the frame allows you to visualize the point cloud.

Go to source

source

std::cout << "Setting up visualization" << std::endl;
Zivid::Visualization::Visualizer visualizer;

std::cout << "Visualizing point cloud" << std::endl;
visualizer.showMaximized();
visualizer.show(frame);
visualizer.resetToFit();

std::cout << "Running visualizer. Blocking until window closes." << std::endl;
visualizer.run();
Go to source

source

Console.WriteLine("Setting up visualization");
using (var visualizer = new Zivid.NET.Visualization.Visualizer())
{
    Console.WriteLine("Visualizing point cloud");
    visualizer.Show(frame);
    visualizer.ShowMaximized();
    visualizer.ResetToFit();

    Console.WriteLine("Running visualizer. Blocking until window closes.");
    visualizer.Run();
}

You can visualize the point cloud from the point cloud object as well.

Go to source

source

std::cout << "Getting point cloud from frame" << std::endl;
auto pointCloud = frame.pointCloud();

std::cout << "Setting up visualization" << std::endl;
Zivid::Visualization::Visualizer visualizer;

std::cout << "Visualizing point cloud" << std::endl;
visualizer.showMaximized();
visualizer.show(pointCloud);
visualizer.resetToFit();

std::cout << "Running visualizer. Blocking until window closes." << std::endl;
visualizer.run();
Go to source

source

Console.WriteLine("Getting point cloud from frame");
var pointCloud = frame.PointCloud;

Console.WriteLine("Setting up visualization");
var visualizer = new Zivid.NET.Visualization.Visualizer();

Console.WriteLine("Visualizing point cloud");
visualizer.Show(pointCloud);
visualizer.ShowMaximized();
visualizer.ResetToFit();

Console.WriteLine("Running visualizer. Blocking until window closes.");
visualizer.Run();

Open3D in Python

Since Zivid-Python does not support point cloud visualization, we have implemented it using Open3D.

Go to source

source

point_cloud = frame.point_cloud()
xyz = point_cloud.copy_data("xyz")
rgba = point_cloud.copy_data("rgba")

print("Visualizing point cloud")
display_pointcloud(xyz, rgba[:, :, 0:3])

The implementation of the function to visualize the point cloud is demonstrated below.

Go to source

source

def display_pointcloud(xyz: np.ndarray, rgb: np.ndarray, normals: Optional[np.ndarray] = None) -> None:
    """Display point cloud provided from 'xyz' with colors from 'rgb'.

    Args:
        rgb: RGB image
        xyz: A numpy array of X, Y and Z point cloud coordinates
        normals: Ordered array of normal vectors, mapped to xyz

    """
    xyz = np.nan_to_num(xyz).reshape(-1, 3)
    if normals is not None:
        normals = np.nan_to_num(normals).reshape(-1, 3)
    rgb = rgb.reshape(-1, 3)

    point_cloud_open3d = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(xyz))
    point_cloud_open3d.colors = o3d.utility.Vector3dVector(rgb / 255)
    if normals is not None:
        point_cloud_open3d.normals = o3d.utility.Vector3dVector(normals)
        print("Open 3D controls:")
        print("  n: for normals")
        print("  9: for point cloud colored by normals")
        print("  h: for all controls")

    visualizer = o3d.visualization.Visualizer()  # pylint: disable=no-member
    visualizer.create_window()
    visualizer.add_geometry(point_cloud_open3d)

    if normals is None:
        visualizer.get_render_option().background_color = (0, 0, 0)
    visualizer.get_render_option().point_size = 1
    visualizer.get_render_option().show_coordinate_frame = True
    visualizer.get_view_control().set_front([0, 0, -1])
    visualizer.get_view_control().set_up([0, -1, 0])

    visualizer.run()
    visualizer.destroy_window()

Color Image

Since Zivid SDK and Zivid-Python do not support 2D color image visualization, we have implemented it using third party libraries: OpenCV in C++ and Python, and Matplotlib in Python.

OpenCV in C++

First, we convert the point cloud to OpenCV color image.

Go to source

source

std::cout << "Converting point cloud to BGRA image in OpenCV format" << std::endl;
cv::Mat bgra = pointCloudToCvBGRA(pointCloud);

The implementation of the function to convert the point cloud to color image is demonstrated below.

Go to source

source

cv::Mat pointCloudToCvBGRA(const Zivid::PointCloud &pointCloud)

{
    auto bgra = cv::Mat(pointCloud.height(), pointCloud.width(), CV_8UC4);
    pointCloud.copyData(&(*bgra.begin<Zivid::ColorBGRA>()));

    return bgra;
}

Tip

It is also possible to get the OpenCV color image directly from the Zivid 2D color image

We can now visualize the color image.

Go to source

source

cv::namedWindow("BGR image", cv::WINDOW_AUTOSIZE);
cv::imshow("BGR image", bgra);
cv::waitKey(0);

OpenCV in Python

First, we convert the point cloud to OpenCV color image.

Go to source

source

print("Converting to BGR image in OpenCV format")
bgr = _point_cloud_to_cv_bgr(point_cloud)

The implementation of the function to convert the point cloud to color image is demonstrated below.

Go to source

source

def _point_cloud_to_cv_bgr(point_cloud: zivid.PointCloud) -> np.ndarray:
    """Get bgr image from frame.

    Args:
        point_cloud: Zivid point cloud

    Returns:
        bgr: BGR image (HxWx3 ndarray)

    """
    bgra = point_cloud.copy_data("bgra")

    return bgra[:, :, :3]

Tip

It is also possible to get the OpenCV color image directly from the Zivid 2D color image

We can now visualize the color image.

Go to source

source

_visualize_and_save_image(bgr, bgr_image_file, "BGR image")

The implementation of the visualization function is demonstrated below.

Go to source

source

def _visualize_and_save_image(image: np.ndarray, image_file: str, title: str) -> None:
    """Visualize and save image to file.

    Args:
        image: BGR image (HxWx3 ndarray)
        image_file: File name
        title: OpenCV Window name

    """
    display_bgr(image, title)
    cv2.imwrite(image_file, image)

Matplotlib in Python

Matplotlib offers a simpler way to visualize the color image in Python.

Go to source

source

display_rgb(rgba[:, :, 0:3], block=False)

The function implementation is demonstrated below.

Go to source

source

def display_rgb(rgb: np.ndarray, title: str = "RGB image", block: bool = True) -> None:
    """Display RGB image.

    Args:
        rgb: RGB image (HxWx3 ndarray)
        title: Image title
        block: Stops the running program until the windows is closed

    """
    plt.figure()
    plt.imshow(rgb)
    plt.title(title)
    plt.show(block=block)

Depth Map

Since Zivid SDK and Zivid-Python do not support depth map visualization, we have implemented it using third party libraries: OpenCV in C++ and Python, and Matplotlib in Python.

OpenCV in C++

First, we convert the point cloud to OpenCV depth map.

Go to source

source

std::cout << "Converting to Depth map in OpenCV format" << std::endl;
cv::Mat zColorMap = pointCloudToCvZ(pointCloud);

The implementation of the function to convert the point cloud to depth map is demonstrated below.

Go to source

source

cv::Mat pointCloudToCvZ(const Zivid::PointCloud &pointCloud)
{
    cv::Mat z(pointCloud.height(), pointCloud.width(), CV_8UC1, cv::Scalar(0)); // NOLINT(hicpp-signed-bitwise)
    const auto points = pointCloud.copyPointsZ();

    // Getting min and max values for X, Y, Z images
    const auto *maxZ = std::max_element(points.data(), points.data() + pointCloud.size(), isLesserOrNan);
    const auto *minZ = std::max_element(points.data(), points.data() + pointCloud.size(), isGreaterOrNaN);

    // Filling in OpenCV matrix with the cloud data
    for(size_t i = 0; i < pointCloud.height(); i++)
    {
        for(size_t j = 0; j < pointCloud.width(); j++)
        {
            if(std::isnan(points(i, j).z))
            {
                z.at<uchar>(i, j) = 0;
            }
            else
            {
                z.at<uchar>(i, j) =
                    static_cast<unsigned char>((255.0F * (points(i, j).z - minZ->z) / (maxZ->z - minZ->z)));
            }
        }
    }

    // Applying color map
    cv::Mat zColorMap;
    cv::applyColorMap(z, zColorMap, cv::COLORMAP_VIRIDIS);

    // Setting invalid points (nan) to black
    for(size_t i = 0; i < pointCloud.height(); i++)
    {
        for(size_t j = 0; j < pointCloud.width(); j++)
        {
            if(std::isnan(points(i, j).z))
            {
                auto &zRGB = zColorMap.at<cv::Vec3b>(i, j);
                zRGB[0] = 0;
                zRGB[1] = 0;
                zRGB[2] = 0;
            }
        }
    }

    return zColorMap;
}

We can now visualize the depth map.

Go to source

source

cv::namedWindow("Depth map", cv::WINDOW_AUTOSIZE);
cv::imshow("Depth map", zColorMap);
cv::waitKey(0);

OpenCV in Python

First, we convert the point cloud to OpenCV depth map.

Go to source

source

print("Converting to Depth map in OpenCV format")
z_color_map = _point_cloud_to_cv_z(point_cloud)

The implementation of the function to convert the point cloud to depth map is demonstrated below.

Go to source

source

def _point_cloud_to_cv_z(point_cloud: zivid.PointCloud) -> np.ndarray:
    """Get depth map from frame.

    Args:
        point_cloud: Zivid point cloud

    Returns:
        depth_map_color_map: Depth map (HxWx1 ndarray)

    """
    depth_map = point_cloud.copy_data("z")
    depth_map_uint8 = ((depth_map - np.nanmin(depth_map)) / (np.nanmax(depth_map) - np.nanmin(depth_map)) * 255).astype(
        np.uint8
    )

    depth_map_color_map = cv2.applyColorMap(depth_map_uint8, cv2.COLORMAP_VIRIDIS)

    # Setting nans to black
    depth_map_color_map[np.isnan(depth_map)[:, :]] = 0

    return depth_map_color_map

We can now visualize the depth map.

Go to source

source

_visualize_and_save_image(z_color_map, depth_map_file, "Depth map")

The function implementation is demonstrated below.

Go to source

source

def _visualize_and_save_image(image: np.ndarray, image_file: str, title: str) -> None:
    """Visualize and save image to file.

    Args:
        image: BGR image (HxWx3 ndarray)
        image_file: File name
        title: OpenCV Window name

    """
    display_bgr(image, title)
    cv2.imwrite(image_file, image)

Matplotlib in Python

Matplotlib offers a simpler way to visualize the depth map in Python.

Go to source

source

display_depthmap(xyz, block=True)

The function implementation is demonstrated below.

Go to source

source

def display_depthmap(xyz: np.ndarray, block: bool = True) -> None:
    """Create and display depthmap.

    Args:
        xyz: A numpy array of X, Y and Z point cloud coordinates
        block: Stops the running program until the windows is closed

    """
    plt.figure()
    plt.imshow(
        xyz[:, :, 2],
        vmin=np.nanmin(xyz[:, :, 2]),
        vmax=np.nanmax(xyz[:, :, 2]),
        cmap="viridis",
    )
    plt.colorbar()
    plt.title("Depth map")
    plt.show(block=block)

Normals

Since Zivid SDK does not support visualization of normals, we have implemented it using third party libraries, PCL in C++, and Open3D in Python.

PCL in C++

We can visualize normals as follows.

Go to source

source

std::cout << "Visualizing normals" << std::endl;
visualizePointCloudAndNormalsPCL(pointCloudPCL.makeShared(), pointCloudWithNormalsPCL.makeShared());

The function implementation is demonstrated below.

Go to source

source

void visualizePointCloudAndNormalsPCL(
    const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr &pointCloud,
    const pcl::PointCloud<pcl::PointXYZRGBNormal>::ConstPtr &pointCloudWithNormals)
{
    auto viewer = pcl::visualization::PCLVisualizer("Viewer");

    int viewRgb(0);
    viewer.createViewPort(0.0, 0.0, 0.5, 1.0, viewRgb);
    viewer.addText("Cloud RGB", 0, 0, "RGBText", viewRgb);
    viewer.addPointCloud<pcl::PointXYZRGB>(pointCloud, "cloud", viewRgb);

    const int normalsSkipped = 10;
    std::cout << "Note! 1 out of " << normalsSkipped << " normals are visualized" << std::endl;

    int viewNormals(0);
    viewer.createViewPort(0.5, 0.0, 1.0, 1.0, viewNormals);
    viewer.addText("Cloud Normals", 0, 0, "NormalsText", viewNormals);
    viewer.addPointCloud<pcl::PointXYZRGBNormal>(pointCloudWithNormals, "cloudNormals", viewNormals);
    viewer.addPointCloudNormals<pcl::PointXYZRGBNormal>(
        pointCloudWithNormals, normalsSkipped, 1, "normals", viewNormals);

    viewer.setCameraPosition(0, 0, -100, 0, -1, 0);

    std::cout << "Press r to centre and zoom the viewer so that the entire cloud is visible" << std::endl;
    std::cout << "Press q to exit the viewer application" << std::endl;
    while(!viewer.wasStopped())
    {
        viewer.spinOnce(100);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

Open3D in Python

We can visualize normals as follows.

Go to source

source

normals_colormap = 0.5 * (1 - normals)

print("Visualizing normals in 2D")
display_rgb(rgb=rgba[:, :, :3], title="RGB image", block=False)
display_rgb(rgb=normals_colormap, title="Colormapped normals", block=True)

print("Visualizing normals in 3D")
display_pointcloud_with_downsampled_normals(point_cloud, zivid.PointCloud.Downsampling.by4x4)

The function implementation is demonstrated below.

Go to source

source

def display_pointcloud_with_downsampled_normals(
    point_cloud: PointCloud,
    downsampling: PointCloud.Downsampling,
) -> None:
    """Display point cloud with downsampled normals.

    Args:
        point_cloud: A Zivid point cloud handle
        downsampling: A valid Zivid downsampling factor to apply to normals

    """
    rgb = point_cloud.copy_data("rgba")[:, :, :3]
    xyz = point_cloud.copy_data("xyz")
    point_cloud.downsample(downsampling)
    normals = point_cloud.copy_data("normals")

    display_pointcloud(xyz=xyz, rgb=rgb, normals=normals)
def display_pointcloud(xyz: np.ndarray, rgb: np.ndarray, normals: Optional[np.ndarray] = None) -> None:
    """Display point cloud provided from 'xyz' with colors from 'rgb'.

    Args:
        rgb: RGB image
        xyz: A numpy array of X, Y and Z point cloud coordinates
        normals: Ordered array of normal vectors, mapped to xyz

    """
    xyz = np.nan_to_num(xyz).reshape(-1, 3)
    if normals is not None:
        normals = np.nan_to_num(normals).reshape(-1, 3)
    rgb = rgb.reshape(-1, 3)

    point_cloud_open3d = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(xyz))
    point_cloud_open3d.colors = o3d.utility.Vector3dVector(rgb / 255)
    if normals is not None:
        point_cloud_open3d.normals = o3d.utility.Vector3dVector(normals)
        print("Open 3D controls:")
        print("  n: for normals")
        print("  9: for point cloud colored by normals")
        print("  h: for all controls")

    visualizer = o3d.visualization.Visualizer()  # pylint: disable=no-member
    visualizer.create_window()
    visualizer.add_geometry(point_cloud_open3d)

    if normals is None:
        visualizer.get_render_option().background_color = (0, 0, 0)
    visualizer.get_render_option().point_size = 1
    visualizer.get_render_option().show_coordinate_frame = True
    visualizer.get_view_control().set_front([0, 0, -1])
    visualizer.get_view_control().set_up([0, -1, 0])

    visualizer.run()
    visualizer.destroy_window()

Conclusion

This tutorial shows how to use Zivid SDK to visualize point clouds in C++ and C# and third party libraries to visualize it in Python. Using third party libraries is demonstrated to visualize point clouds in Python, and to visualize color images, depth maps, and normals in C++, C#, and Python.