-
Notifications
You must be signed in to change notification settings - Fork 582
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inaccuracies in mesh.contains #242
Comments
I ended up writing my own cython code for checking if a point is inside a mesh, based on this answer stack overflow: |
Thanks for the report and reproducible example! Yeah this looks like it's related to embree. Trimesh has two ray implementations with the same API, one with embree (which is hilariously faster, like 600K rays/sec) and one that's mostly straight numpy (12K RPS). The embree one only returns the first hit by default, and then trimesh has some wangling code to offset (to avoid a self-hit) then re-query. Avoiding the self hit means that in your example mesh with internal faces (from the non- surface voxelization) don't get counted correctly. The easiest thing to do is probably use the native ray- tracer, which gets the correct answer but is kinda slow. Once I push the changes that allow no- face PLY files to load, this works pretty well:
One other option for the future- you could marching- cubes convert your voxels to a mesh which wouldn't have the internal faces. The nice thing about embree is that it is fast, although it doesn't handle this case all that well. A robust cython ray implementation could be great! Trimesh currently doesn't have any built components, so adding any cython would be a substantial architectural change. The way we've been doing it is breaking up compiled libraries into small separate packages and setting up I think the contains logic would probably be mostly the same (count ray hits) but with more accurate rays. Some other things we might do to help with this:
|
Quick follow up: we released our cython code where we perform the inside/outside check. You can find it here. More specifically, we have implemented the function check_mesh_contains that performs the inside/outside check using the MeshIntersector class. Most code is in vanilla python, although there is a small cython kernel (the TriangleHash). In our tests, this function gave more accurate results than the trimesh |
I am also getting strange results sometimes with "mesh.contains". I want to try your check_mesh_contains routine to compare but I'm getting the following error: from .triangle_hash import TriangleHash as _TriangleHash I included triangle_hash.pyx in my directory, but how can i import it properly in my main script ? |
@oliverstefanov
If you experience any issues, please open a separate issue there. |
@LMescheder Ok thanks! |
Hi, For easy testing, you can download the inside_mesh.py and triangle_hash.pyx files from the Occupancy Network repo, and compile the pyx file. You just have to create a setup.py file with the following lines : `from distutils.core import setup setup( And then compile it using cython : The compilation creates a triangle_hash.so which can be imported in python normally, making the inside_mesh module work as a standalone one for using the check_mesh_contains method |
So if a mesh is loaded with |
After looking a bit more in depth, I found that the mesh I was using didn't have any normals computed. I didn't look into the details of the computations but I thought that trimesh was using ray tracing, which should not require face normals ? Anyway, here are two dummy STL files which I used. They are simple STLs generated using PyMesh : I used the following code : `import trimesh mesh = trimesh.load('dummy.stl') print mesh.contains(np.array([[0,0,0],[200,200,200]])) mesh3 = trimesh.load('dummy2.stl') print mesh3.contains(np.array([[0,0,0],[200,200,200]])) The output is :
One can see, that the mesh without normals behaves incorrectly irregardless of the pyembree setting. The mesh with normals behaves good also irregardless of the pyembree setting. Perhaps having some check for mesh normals (if they are required) could be a good idea ? For the same meshes, the is_watertight property is OK (True for all) |
@benoitrosa Bonjour Benoit, do you confirm that precomputing normals on the mesh resolves the issues encountered with mesh.contains ? |
I just re-tested with another (more complex) mesh and indeed, the mesh.contains function behaves correctly when the mesh normals are pre-computed. Note that I did compute the normals using Cloudcompare. The fix_normals() function of trimesh does not do anything in this case (but that's perhaps another bug due to the format of the input mesh, all normals are with [0,0,0] values) |
It is still not working for me, I get wrong results for points located beyong the mesh in the y+ direction only...!? Computing or recomputing the normals makes no difference. I tried installing the inside_mesh/triangle_hash as @benoitrosa describes but I am not getting a triangle_hash.so file, only .cpp file although the compiling seems ok (after including numpy in the setup.py). So I don't know how to import the check_mesh_contains method into my python code... Any help or insight would be greatly appreciated because this unreliability in the mesh.contains function kinda ruins everything I've done so far in my program :-( |
@oliverstefanov There was a mistake in the command needed. It should be |
@benoitrosa Compiling it with |
Thank you for your response. When compiling I am getting .pyd/.obj/.lib/.exp (as I am on Windows?) in the build directory. I moved everything to my main directory along with triangle_hash.pyx and inside_mesh.py. I included "import inside_mesh" in my code, which leads to the following error:
|
@oliverstefanov Can you try to replace the line with
as I did in my gist? |
Oh ok thanks, but I think i've tried this before, I still get an error:
|
Hey @LMescheder that self contained bit is awesome! What do you think about putting it on pypi, kind of like triangle or python-fcl? I made a first pass at packaging your gist here: https://github.com/mikedh/contains It compiles and works nicely, with cibuildwheel and automatic pypi releases it could be pretty slick. I'm happy to transfer that repo to you or add you to it, and apologies for the automatic license in there (delete away). |
@mikedh Sure, I can look into that if you add me or transfer the repo. |
Sweet! Sorry this fell off my radar, transferred! |
Hi @mikedh , is this bug fixed or should I be using the gist supplied by @LMescheder ? Thanks, A |
Is the Pyembree issue solved by temporally removing the face that was hit instead of translating the ray origin beyond the face? |
Hello I still have problems with contains. Normal contains is too slow in my case and pyembree return wrong results. I tried check_mesh_contains but the computing time is very strange. I tested on a mesh generated by an algorithm Computing time is crazy for the cylinder mesh despite he contains 2 times less vertices and faces. I tried to check the type of variable inside the mesh but they all seems identical. Here is my script
And here the result :
Why this function in the cylinder mesh is so long ? |
For issue #263 and #331 I think the following could be done while still utilizing embree, in pseudo code
This doesn't solve the multi-hit problem, but does give some tolerance for each ray to hit. And I think all of this could be implemented in a vectorized way without needing any loops. Also to note, embree3 now has an example to gather all hits along a single ray. I've taken a brief look, and it seems to be just tracking the most recent hit, then offsetting the ray. Unfortunately the current wrapper for embree that Trimesh uses is for embree2, and the next_hit example relies on features only introduced in embree 3.7.0, |
More experimentation w/ embree3 using the I think the current Trimesh implementation is probably as good as it gets:
The second/third/non-first collision is totally unreliable. |
Nice thanks for experimenting! Yeah the "second hit" logic with offsetting is not great. Does robust mode solve the "leaking through on-vertex or on-edge" issues with embree? Embree seems like it is pretty much only designed for first-hits as far as I can tell. If they actually solved the leaks, I was wondering if something like this would work:
|
As far as I can tell, not when the triangles are large. Individual rays fluctuate between runs (the id it hits changes). I've attempted the following to get second hit working:
|
I have it working now. For my use case the results appear to be correct with embree3 (when they were wrong with embree2). |
Just to note, I'm not certain this solution gives points that are on the mesh. I'm comparing this purely in my use case where I retrieve all hits along a ray. When using embree2 and the trimesh ray-offset, there were multiple holes in the ray-traced solution (very obvious when performing visualization as sudden gaps in the z-dist along the ray). I no longer see this issue with embree3 + So the current pipeline is as follows:
|
Is this issue solved? or the gist by @LMescheder is better? @mikedh |
I experiencing some issues with the
Trimesh.contains
method. I tried to debug it myself, but I couldn't find any issues in the trimesh code. I suspect it's inaccuracies in PyEmbree.Here is the code I'm using:
The result looks like this:
Here are the input / output files:
https://www.dropbox.com/sh/6i0222tk9lpkqgr/AAC1mZ0yblFsXxg9cEMmfneJa?dl=0
Of course, in this special case I can do it in a simpler way by exploiting the regular grid structure of the mesh. However, I also experience similar problems for other meshes (usually at higher resolutions).
I would appreciate any help!
The text was updated successfully, but these errors were encountered: