isl-org / Open3D

Open3D: A Modern Library for 3D Data Processing
http://www.open3d.org
Other
11.19k stars 2.27k forks source link

Distance queries offer only positive values and occupancy 0 #6919

Closed w8jcik closed 3 weeks ago

w8jcik commented 3 weeks ago

Checklist

Describe the issue

My goal is to select points that are inside of a mesh.

I am following Distance Queries tutorial. Using RaycastingScene and compute_occupancy I am detecting which points are inside of a mesh. All points are reported to be outside of the mesh. Occupancy is always 0 which is (I believe) not correct.

Additional observation is that compute_signed_distance gives only positive values, while points inside of envelope should give negative numbers.

Example code is calling compute_signed_distance to filter points that are in arbitrary distance (10.0) from the mesh.

Distance generally works, but all points have positive distance, while according to the tutorial points inside the mesh should have negative distance. This might be somehow related to why occupancy is always 0.

Selection of points inside of a mesh is not possible.

image

Steps to reproduce the bug

Files envelope.ply and atoms.pcd are attached.

import numpy
import open3d
import open3d.visualization

envelope = open3d.io.read_triangle_mesh("envelope.ply")
envelope.compute_vertex_normals()
envelope.compute_triangle_normals()

assert envelope.is_watertight()

atoms = open3d.io.read_point_cloud("atoms.pcd")

scene = open3d.t.geometry.RaycastingScene()
scene.add_triangles(open3d.t.geometry.TriangleMesh.from_legacy(envelope))

filtered_points = []

for point in atoms.points:
    open3d_point = open3d.core.Tensor([ point ], dtype=open3d.core.Dtype.Float32)
    signed_distance = scene.compute_signed_distance(open3d_point).numpy()[0]    

    if signed_distance > 10:
        filtered_points.append(point)

atoms.points = open3d.cpu.pybind.utility.Vector3dVector(numpy.array(filtered_points))

def transparent_draw(envelope, atoms):
    """
    Transparent replacement for
        open3d.visualization.draw_geometries([ envelope, atoms ])
    """
    material = open3d.visualization.rendering.MaterialRecord()
    material.shader = 'defaultLitTransparency'
    material.base_color = [0.467, 0.467, 0.467, 0.2]
    material.base_roughness = 0.0
    material.base_reflectance = 0.0
    material.base_clearcoat = 1.0
    material.thickness = 1.0
    material.transmission = 1.0
    material.absorption_distance = 10
    material.absorption_color = [0.5, 0.5, 0.5]

    open3d.visualization.draw([
        { 'name': 'envelope', 'geometry': envelope, 'material': material },
        { 'name': 'atoms', 'geometry': atoms, 'material': None }
    ], show_skybox=False, bg_color=[0.8, 0.8, 0.8, 1.0])

transparent_draw(envelope, atoms)

Error message

There is no crash or warning.

Expected behavior

Distance from a point inside of a mesh should be negative and occupancy should be 1.

Open3D, Python and System information

- Operating system: Ubuntu 24.04
- Python version: Python 3.11, tested other (3.9, 3.10)
- Open3D version: 0.18.0, tested other (latest, 0.17, 1.16)
- System architecture: x86
- Is this a remote workstation?: no
- How did you install Open3D?: pip

Additional information

example mesh and point cloud.zip

nicolaloi commented 3 weeks ago

I have tried to reproduce the issue, and upon closer examination of your mesh surface, I think your "envelope.ply" seems to create something like a "double surface". The signed distance is negative when a cast ray intersects the mesh an odd number of times (1,3,5...). However, since there are "two surfaces" when the point is inside your mesh the intersections are 2 and not 1.

w8jcik commented 3 weeks ago

Calling .cluster_connected_triangles() on envelope.ply found two clusters (two meshes).

After removing one of them .calculate_occupancy() works fine.

image

Thank you a lot for help and sorry for bothering.