Search code examples
pythonpoint-cloudsopen3d

How to extract the bottom layer voxels using open3d?


I want to extract the bottom layer voxels. For example, extract the part from the armadillo. Does anyone know how to do that?

enter image description here

This is how I find the lowest voxel.

#Get voxels center coordinate
def get_voxel_center_coordinate(voxel_grid):
    voxels = voxel_grid.get_voxels()
    voxel_center = []
    
    for voxel in voxels:
        voxel_center.append(voxel_grid.get_voxel_center_coordinate(voxel.grid_index))
    
    return voxel_center

#Find lowest voxels index
def find_lowest_voxel_index(voxel_grid):
    voxels = voxel_grid.get_voxels()
    voxel_center = get_voxel_center_coordinate(voxel_grid)
    
    for i in range(0, len(voxel_center)):
        if i == 0:
            min_z = voxel_center[i, 2]
        else:
            if min_z > voxel_center[i, 2]:
                min_z = voxel_center[i, 2]
    ind = 0
    lowest_voxel_index = []
    
    for center in voxel_center:
        if center[2] == min_z:
            print(center[2])
            lowest_voxel_index.append(voxels[ind].grid_index)
            ind += 1
    
    return lowest_voxel_index

Solution

  • Your solution is almost correct. I'll point out a few things that you should change and then a way to verify the solution.

    Solution

    Things that should change in the given solution:

    1. You cannot access the z-axis through voxel_center[i, 2] since voxel_center is a list, not a numpy array. You should instead access them as voxel_center[i][2].
    2. You should increment ind at each loop, not only when the condition is true. Alternatively, you can use enumerate which overcomes this cleanly.

    Therefore, the correct implementation would be the following:

    #Get voxels center coordinate
    def get_voxel_center_coordinate(voxel_grid):
        voxels = voxel_grid.get_voxels()
        voxel_center = []
        
        for voxel in voxels:
            voxel_center.append(voxel_grid.get_voxel_center_coordinate(voxel.grid_index))
        
        return voxel_center
    
    #Find lowest voxels index
    def find_lowest_voxel_index(voxel_grid):
        voxels = voxel_grid.get_voxels()
        voxel_center = get_voxel_center_coordinate(voxel_grid)
        
        for i in range(0, len(voxel_center)):
            if i == 0:
                min_z = voxel_center[i][2]
            else:
                if min_z > voxel_center[i][2]:
                    min_z = voxel_center[i][2]
        ind = 0
        lowest_voxel_index = []
        
        for center in voxel_center:
            if center[2] == min_z:
                print(center[2])
                lowest_voxel_index.append(voxels[ind].grid_index)
            ind += 1
        
        return lowest_voxel_index
    

    Verification

    To verify the solution, you might want to visualize the results. However, VoxelGrid does not have any method that allows the user to e.g., change the color of a particular voxel. Therefore, you can think of creating extra meshes to visualize the filtered voxels. Since VoxelGrid provides get_voxel_bounding_points, you can make use of these points to create an AxisAlignedBoundingBox.

    lowest_index = find_lowest_voxel_index(voxel_grid)  # this uses your solution after modifications
    
    bounding_box = []
    for i in lowest_index:
        bb = o3d.geometry.AxisAlignedBoundingBox.create_from_points(voxel_grid.get_voxel_bounding_points(i))
        bb.color = np.array([1,0,1])  # feel free to choose any other color
        bounding_box.append(bb)
    
    
    axes = o3d.geometry.TriangleMesh.create_coordinate_frame(origin=np.array([100,100,100]), size=100)  # in order to be sure of the direction
    
    o3d.visualization.draw_geometries([voxel_grid, *bounding_box, axes])
    

    This is how running this code on an armadillo mesh looks like. Note that I added the coordinate frame to make sure of the direction. Blue arrow points toward +z. The voxel with the lowest z value has purple edges. enter image description here