userX
userX

Reputation: 315

open3d voxel downsampling then dbscan cluster then approximately upscale clustering to the original image

dbscan clustering is very memory intensive. I'd like to downsample, calculate clusters, then approximately relate these calculated clusters to the original image.

This is the standard way to downsample is:

    # examples/Python/Basic/pointcloud.py

import numpy as np
import open3d as o3d

if __name__ == "__main__":

    print("Load a ply point cloud, print it, and render it")
    pcd = o3d.io.read_point_cloud("../../TestData/fragment.ply")
    print(pcd)
    print(np.asarray(pcd.points))
    o3d.visualization.draw_geometries([pcd])

    print("Downsample the point cloud with a voxel of 0.05")
    downpcd = pcd.voxel_down_sample(voxel_size=0.05)
    o3d.visualization.draw_geometries([downpcd])

    #Then this could be clustered via dbscan
    labels = np.array(
            downpcd.cluster_dbscan(eps=0.05, min_points=30, print_progress=True))

How would I then relate these labels back to the original PCD (pcd)?

Thanks!

Upvotes: 0

Views: 1190

Answers (1)

Lady Be Good
Lady Be Good

Reputation: 271

import numpy as np


def voxel_downsampling(point_cloud, voxel_size):
    """
    Downsample an Open3D point cloud using voxel downsampling.

    Parameters:
    - point_cloud (open3d.geometry.PointCloud): Input point cloud.
    - voxel_size (float): Voxel size for downsampling.

    Returns:
    - open3d.geometry.PointCloud: Downsampled point cloud.
    """

    points_np = np.asarray(point_cloud.points)

    min_bound = np.min(points_np, axis=0)
    max_bound = np.max(points_np, axis=0)

    voxel_counts = np.ceil((max_bound - min_bound) / voxel_size).astype(int)

    voxel_indices = np.floor((points_np - min_bound) / voxel_size).astype(int)

    packed_indices = voxel_indices[:, 0] + voxel_indices[:, 1] * voxel_counts[0] + voxel_indices[:, 2] * voxel_counts[
        0] * voxel_counts[1]

    voxel_dict = {}
    voxel_to_point_index_dict = {}
    for i, index in enumerate(packed_indices):
        if index in voxel_dict:
            voxel_dict[index].append(points_np[i])
            voxel_to_point_index_dict[index].append(i)
        else:
            voxel_dict[index] = [points_np[i]]
            voxel_to_point_index_dict[index] = [i]

    downsampled_points = []
    tm_arr = []

    for points_in_voxel in voxel_dict.keys():
        downsampled_points.append(np.mean(voxel_dict[points_in_voxel], axis=0))
        tm_arr.append(points_in_voxel)
    downsampled_points = np.array(downsampled_points)

    downsampled_pc = o3d.geometry.PointCloud()
    downsampled_pc.points = o3d.utility.Vector3dVector(downsampled_points)

    return downsampled_pc, voxel_to_point_index_dict, tm_arr


def upsample(pcd, pcd_downsampled, labels, voxel_to_point_index, tmp_arr):
    colors = {}
    for b in range(len(pcd_downsampled.points)):
        if labels[b] in colors:
            for pt in voxel_to_point_index[tmp_arr[b]]:
                pcd.colors[pt] = colors[labels[b]]
        else:
            color = np.random.rand(3)
            colors[labels[b]] = color
            for pt in voxel_to_point_index[tmp_arr[b]]:
                pcd.colors[pt] = colors[labels[b]]

    return pcd


import open3d as o3d

if __name__ == "__main__":
    print("Load a ply point cloud, print it, and render it")
    pcd = o3d.io.read_point_cloud("fragment.ply")

    pcd_ds, voxel_to_point_index, tm_arr = voxel_downsampling(pcd, 0.01)

    # open3d_down = pcd.voxel_down_sample(voxel_size=0.001)

    labels = np.array(
        pcd_ds.cluster_dbscan(eps=0.02, min_points=10, print_progress=True))

    up_pcd = upsample(pcd, pcd_ds, labels, voxel_to_point_index, tm_arr)

    max_label = max(labels)
    print(f"point cloud has {max_label + 1} clusters")

    o3d.io.write_point_cloud("output.ply", up_pcd)

I have used open3d fragments.ply file, and results are as follows:

It could find 8 clusters with the given parameters.

A few notes

  • I implemented this voxel down sample some time ago, so if you catch any mistake please let me know.
  • As I said this an heavy approach and I guess I can come up with a better solution using a custom data-set, but I currently don't have much time. I will try to improve when I can.
  • I am still not sure if downsampling harms the dbscan or not, but you can play with parameters to have a better result.

Best

Upvotes: 1

Related Questions