Reputation: 315
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
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:
A few notes
Best
Upvotes: 1