Visualization Tutorial

Introduction

이 튜토리얼에서는 Zivid SDK 및 타사 라이브러리를 사용하여 Zivid 카메라로 캡처한 3D 및 2D 데이터를 시각화하는 방법을 설명합니다.

Prerequisites

이 튜토리얼은 Zivid Frame으로 시작합니다. Capture Tutorial 을 보면 프레임 캡처 방법에 대한 자세한 내용을 확인할 수 있습니다.

Point Cloud

Zivid SDK in C++ and C#

프레임이 있으면 포인트 클라우드를 시각화할 수 있습니다.

소스로 이동

소스

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();
소스로 이동

소스

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();
}

포인트 클라우드 객체에서도 포인트 클라우드를 시각화할 수 있습니다.

소스로 이동

소스

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();
소스로 이동

소스

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

Zivid-Python 은 포인트 클라우드 시각화를 지원하지 않으므로 Open3D 를 사용하여 구현했습니다.

소스로 이동

source

point_cloud = frame.point_cloud()

print("Visualizing point cloud")
display_pointcloud(point_cloud)

포인트 클라우드를 시각화하는 기능의 구현은 아래에 설명되어 있습니다.

소스로 이동

source

def display_pointcloud(data: Union[zivid.Frame, zivid.PointCloud, zivid.UnorganizedPointCloud]) -> None:
    """Display point cloud provided either as Frame, PointCloud or UnorganizedPointCloud.

    Args:
        data: Union[zivid.Frame, zivid.PointCloud, zivid.UnorganizedPointCloud]
        normals: If True, display normals as color map

    """
    with zivid.visualization.Visualizer() as visualizer:
        visualizer.set_window_title("Zivid Point Cloud Visualizer")
        visualizer.colors_enabled = True
        visualizer.axis_indicator_enabled = True
        visualizer.show(data)
        visualizer.reset_to_fit()
        visualizer.run()

Live 3D Point Cloud

The Zivid SDK can be used to continuously capture and visualize 3D point clouds in a loop.

Zivid SDK in C++

The visualizer runs in a dedicated thread while the main thread continuously captures new frames and pushes them to the visualizer.

소스로 이동

source

std::cout << "Setting up visualization" << std::endl;
std::atomic_bool visualizerRunning{ false };
auto visualizerPromise = std::promise<Zivid::Visualization::Visualizer *>();
auto visualizerFuture = visualizerPromise.get_future();

std::thread visualizationThread([&frame, &visualizerPromise, &visualizerRunning]() {
    auto visualizer = Zivid::Visualization::Visualizer();

    // Pass the visualizer to the main thread
    visualizerPromise.set_value(&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;
    visualizerRunning = true;
    visualizer.run();
    visualizerRunning = false;
});

// Get the visualizer handle in the main thread
auto visualizerHandle = visualizerFuture.get();

while(!visualizerRunning)
{
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
while(visualizerRunning)
{
    frame = camera.capture2D3D(settings);
    if(!visualizerRunning)
    {
        break;
    }
    visualizerHandle->show(frame);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
visualizationThread.join();
std::cout << "Visualizer closed" << std::endl;
소스로 이동

source

Console.WriteLine("Setting up visualization");
var visualizerRunning = new ManualResetEventSlim(false);
Zivid.NET.Visualization.Visualizer visualizerHandle = null;
var visualizerReady = new ManualResetEventSlim(false);

var visualizationThread = new Thread(() =>
{
    using (var visualizer = new Zivid.NET.Visualization.Visualizer())
    {
        // Pass the visualizer to the main thread
        visualizerHandle = visualizer;
        visualizerReady.Set();

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

        Console.WriteLine("Running visualizer. Blocking until window closes.");
        visualizerRunning.Set();
        visualizer.Run();
        visualizerRunning.Reset();
    }
});
visualizationThread.Start();

// Get the visualizer handle in the main thread
visualizerReady.Wait();

visualizerRunning.Wait();
while (visualizerRunning.IsSet)
{
    frame = camera.Capture2D3D(settings);
    if (!visualizerRunning.IsSet)
        break;
    visualizerHandle.Show(frame);
    Thread.Sleep(10);
}
visualizationThread.Join();
Console.WriteLine("Visualizer closed");
소스로 이동

source

print("Setting up visualization")
visualizer_running = threading.Event()

print("Visualizing point cloud")
with zivid.visualization.Visualizer() as visualizer:
    visualizer.show(frame)
    visualizer.reset_to_fit()

    def _capture_thread() -> None:
        while visualizer_running.is_set():
            new_frame = camera.capture_2d_3d(settings)
            if visualizer_running.is_set():
                visualizer.show(new_frame)
            time.sleep(0.01)

    capture_thread = threading.Thread(target=_capture_thread)

    print("Running visualizer. Blocking until window closes.")
    visualizer_running.set()
    capture_thread.start()
    visualizer.run()
    visualizer_running.clear()

    capture_thread.join()

print("Visualizer closed")

To exit the loop by pressing ‘q’ in the terminal:

소스로 이동

source

std::cout << "Setting up visualization" << std::endl;
std::atomic_bool visualizerRunning{ false };
std::atomic_bool acceptEnd{ true };
std::atomic_bool quitRequested{ false };
auto visualizerPromise = std::promise<Zivid::Visualization::Visualizer *>();
auto visualizerFuture = visualizerPromise.get_future();

std::thread visualizationThread([&frame, &visualizerPromise, &visualizerRunning, &acceptEnd, &quitRequested]() {
    auto visualizer = Zivid::Visualization::Visualizer();

    // Pass the visualizer to the main thread
    visualizerPromise.set_value(&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;
    do
    {
        visualizerRunning = true;
        visualizer.run();
        visualizerRunning = false;

        if(quitRequested)
        {
            break;
        }
        std::cout << "Visualizer window closed by user. It will be reopened if we're currently capturing."
                  << std::endl;

    } while(!acceptEnd);
});

// Get the visualizer handle in the main thread
auto visualizerHandle = visualizerFuture.get();

while(!visualizerRunning)
{
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
std::cout << "Press 'q' in the terminal to quit " << std::endl;
while(visualizerRunning)
{
    int key = getKeyNonBlocking();
    if(key == 'q')
    {
        std::cout << "Closing application because user pressed 'q'" << std::endl;
        quitRequested = true;
        visualizerHandle->close();
    }
    else
    {
        acceptEnd = false;
        frame = camera.capture2D3D(settings);
        visualizerHandle->show(frame);
        acceptEnd = true;
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
visualizationThread.join();
std::cout << "Visualizer closed" << std::endl;
소스로 이동

source

Console.WriteLine("Setting up visualization");
var visualizerRunning = new ManualResetEventSlim(false);
var acceptEnd = new ManualResetEventSlim(true);
var quitRequested = new ManualResetEventSlim(false);
Zivid.NET.Visualization.Visualizer visualizerHandle = null;
var visualizerReady = new ManualResetEventSlim(false);

var visualizationThread = new Thread(() =>
{
    using (var visualizer = new Zivid.NET.Visualization.Visualizer())
    {
        // Pass the visualizer to the main thread
        visualizerHandle = visualizer;
        visualizerReady.Set();

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

        Console.WriteLine("Running visualizer. Blocking until window closes.");
        do
        {
            visualizerRunning.Set();
            visualizer.Run();
            visualizerRunning.Reset();

            if (quitRequested.IsSet)
                break;
            Console.WriteLine(
                "Visualizer window closed by user. It will be reopened if we're currently capturing."
            );
        } while (!acceptEnd.IsSet);
    }
});
visualizationThread.Start();

// Get the visualizer handle in the main thread
visualizerReady.Wait();

visualizerRunning.Wait();
Console.WriteLine("Press 'q' in the terminal to quit");
while (visualizerRunning.IsSet)
{
    if (Console.KeyAvailable)
    {
        var key = Console.ReadKey(intercept: true);
        if (key.KeyChar == 'q')
        {
            Console.WriteLine("Closing application because user pressed 'q'");
            quitRequested.Set();
            Application.DoEvents();
            foreach (Form form in Application.OpenForms)
            {
                if (form.Text.Contains("Zivid"))
                {
                    form.Invoke(new Action(() => form.Close()));
                    break;
                }
            }
        }
    }
    else
    {
        acceptEnd.Reset();
        frame = camera.Capture2D3D(settings);
        visualizerHandle.Show(frame);
        acceptEnd.Set();
    }
    Thread.Sleep(10);
}
visualizationThread.Join();
Console.WriteLine("Visualizer closed");
소스로 이동

source

print("Setting up visualization")
visualizer_running = threading.Event()
accept_end = threading.Event()
accept_end.set()
quit_requested = threading.Event()

use_raw_terminal = sys.platform != "win32" and sys.stdin.isatty()
if use_raw_terminal:
    old_terminal_settings = termios.tcgetattr(sys.stdin)
    tty.setcbreak(sys.stdin.fileno())
try:
    print("Visualizing point cloud")
    with zivid.visualization.Visualizer() as visualizer:
        visualizer.show(frame)
        visualizer.reset_to_fit()

        def _capture_and_keypress_thread() -> None:
            print("Press 'q' in the terminal to quit")
            while not quit_requested.is_set():
                if not visualizer_running.wait(timeout=0.01):
                    continue
                key = _get_key_non_blocking()
                if key == "q":
                    print("Closing application because user pressed 'q'")
                    quit_requested.set()
                    visualizer.close()
                else:
                    accept_end.clear()
                    new_frame = camera.capture_2d_3d(settings)
                    if visualizer_running.is_set():
                        visualizer.show(new_frame)
                    accept_end.set()
                time.sleep(0.01)

        capture_thread = threading.Thread(target=_capture_and_keypress_thread)
        capture_thread.start()

        print("Running visualizer. Blocking until window closes.")
        while True:
            visualizer_running.set()
            visualizer.run()
            visualizer_running.clear()

            if quit_requested.is_set():
                break
            print("Visualizer window closed by user. It will be reopened if we're currently capturing.")
            if accept_end.is_set():
                break

        capture_thread.join()

    print("Visualizer closed")
finally:
    if use_raw_terminal:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_terminal_settings)

Color Image

Zivid SDK와 Zivid-Python 은 2D 컬러 이미지 시각화를 지원하지 않기 때문에 타사 라이브러리를 사용하여 구현했습니다. C++ 및 Python에서는 OpenCV , Python에서는 Matplotlib 을 사용합니다.

OpenCV in C++

먼저 포인트 클라우드를 OpenCV 컬러 이미지로 변환합니다.

소스로 이동

소스

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

포인트 클라우드를 컬러 이미지로 변환하는 기능의 구현은 아래에 설명되어 있습니다.

소스로 이동

소스

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

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

    return bgra;
}

Zivid 2D 컬러 이미지에서 직접 OpenCV 컬러 이미지를 가져오는 것도 가능합니다.

이제 컬러 이미지를 시각화할 수 있습니다.

소스로 이동

소스

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

OpenCV in Python

먼저 포인트 클라우드를 OpenCV 컬러 이미지로 변환합니다.

소스로 이동

소스

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

포인트 클라우드를 컬러 이미지로 변환하는 기능의 구현은 아래에 설명되어 있습니다.

소스로 이동

소스

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_srgb")

    return bgra[:, :, :3]

Zivid 2D 컬러 이미지에서 직접 OpenCV 컬러 이미지를 가져오는 것도 가능합니다.

이제 컬러 이미지를 시각화할 수 있습니다.

소스로 이동

소스

_visualize_and_save_image(bgr, bgr_image_file, "BGR image")

시각화 기능의 구현은 아래에 설명되어 있습니다.

소스로 이동

소스

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는 Python에서 컬러 이미지를 시각화하는 더 간단한 방법을 제공합니다.

소스로 이동

소스

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

함수 구현은 아래에 설명되어 있습니다.

소스로 이동

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

Zivid SDK와 Zivid-Python 은 Depth 맵(Depth Map) 시각화를 지원하지 않기 때문에 타사 라이브러리를 사용하여 구현했습니다. C++ 및 Python에서는 OpenCV , Python에서는 Matplotlib 을 사용합니다.

OpenCV in C++

먼저 포인트 클라우드를 OpenCV Depth 맵으로 변환합니다.

소스로 이동

소스

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

포인트 클라우드를 Depth 맵으로 변환하는 기능의 구현은 아래에 설명되어 있습니다.

소스로 이동

소스

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.begin(), points.end(), isLesserOrNan);
    const auto *minZ = std::max_element(points.begin(), points.end(), 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;
}

이제 Depth 맵을 시각화할 수 있습니다.

소스로 이동

소스

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

OpenCV in Python

먼저 포인트 클라우드를 OpenCV Depth 맵으로 변환합니다.

소스로 이동

소스

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

포인트 클라우드를 Depth 맵으로 변환하는 기능의 구현은 아래에 설명되어 있습니다.

소스로 이동

소스

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

이제 Depth 맵을 시각화할 수 있습니다.

소스로 이동

소스

_visualize_and_save_image(z_color_map, depth_map_file, "Depth map")

함수 구현은 아래에 설명되어 있습니다.

소스로 이동

소스

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는 Python에서 Depth 맵을 시각화하는 더 간단한 방법을 제공합니다.

소스로 이동

소스

display_depthmap(xyz, block=True)

함수 구현은 아래에 설명되어 있습니다.

소스로 이동

소스

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

Zivid SDK는 노멀/법선(Normals)의 시각화를 지원하지 않기 때문에 타사 라이브러리를 사용하여 구현했으며, C++에서 PCL , Python에서는 Open3D 를 사용합니다.

PCL in C++

다음과 같이 노멀을 시각화할 수 있습니다.

소스로 이동

소스

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

함수 구현은 아래에 설명되어 있습니다.

소스로 이동

소스

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, 0, 1000, 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

다음과 같이 노멀을 시각화할 수 있습니다.

소스로 이동

source

normals_colormap = 0.5 * (1 - normals)
normals_colormap = np.nan_to_num(normals_colormap, nan=0.0, posinf=1.0, neginf=0.0)

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)

함수 구현은 아래에 설명되어 있습니다.

소스로 이동

source

def _copy_to_open3d_point_cloud(xyz: np.ndarray, rgb: np.ndarray, normals: np.ndarray) -> o3d.geometry.PointCloud:
    """Copy point cloud data to Open3D PointCloud object.

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

    Returns:
        An Open3D PointCloud object
    """
    xyz = np.nan_to_num(xyz).reshape(-1, 3)
    normals = np.nan_to_num(normals).reshape(-1, 3)
    rgb = rgb.reshape(-1, rgb.shape[-1])[:, :3]

    open3d_point_cloud = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(xyz))
    open3d_point_cloud.colors = o3d.utility.Vector3dVector(rgb / 255)
    open3d_point_cloud.normals = o3d.utility.Vector3dVector(normals)

    return open3d_point_cloud


def _display_open3d_point_cloud(open3d_point_cloud: o3d.geometry.PointCloud) -> None:
    """Display Open3D PointCloud object.

    Args:
        open3d_point_cloud: Open3D PointCloud object to display
    """
    visualizer = o3d.visualization.Visualizer()  # pylint: disable=no-member
    visualizer.create_window()
    visualizer.add_geometry(open3d_point_cloud)

    if len(open3d_point_cloud.normals) > 0:
        print("Open 3D controls:")
        print("  n: for normals")
        print("  9: for point cloud colored by normals")
        print("  h: for all controls")
    visualizer.get_render_option().background_color = (0, 0, 0)
    visualizer.get_render_option().point_size = 2
    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()


def display_pointcloud_with_downsampled_normals(
    point_cloud: zivid.PointCloud,
    downsampling: zivid.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

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

    open3d_point_cloud = _copy_to_open3d_point_cloud(xyz, rgb, normals)
    _display_open3d_point_cloud(open3d_point_cloud)

Conclusion

이 튜토리얼에서는 Zivid SDK를 사용하여 C++ 및 C#에서 포인트 클라우드를 시각화하고 Python에서 시각화하기 위해 타사 라이브러리를 사용하는 방법을 보여줍니다. 타사 라이브러리를 사용하여 Python에서 포인트 클라우드를 시각화하고 C++, C# 및 Python에서 컬러 이미지, 깊이 맵 및 노멀을 시각화하는 방법을 시연합니다.