GithubHelp home page GithubHelp logo

sxyu / sdf Goto Github PK

View Code? Open in Web Editor NEW
357.0 8.0 34.0 1.79 MB

Parallelized triangle mesh --> continuous signed distance field on CPU

License: BSD 2-Clause "Simplified" License

C++ 92.79% CMake 1.98% Python 5.23%

sdf's Introduction

Triangle mesh to signed-distance function (SDF)

Given a triangle mesh and a set of points, this library supports:

  1. Computing SDF of mesh at each point: sdf(points)
  2. Computing whether each point is inside mesh: sdf.contains(points)
  3. Computing nearest neighbor vertex index for each point: sdf.nn(points) All operations are CPU-only and parallelized.

Quickstart

Install python binding: pip install pysdf

Usage example:

from pysdf import SDF

# Load some mesh (don't necessarily need trimesh)
import trimesh
o = trimesh.load('some.obj')
f = SDF(o.vertices, o.faces); # (num_vertices, 3) and (num_faces, 3)

# Compute some SDF values (negative outside);
# takes a (num_points, 3) array, converts automatically
origin_sdf = f([0, 0, 0])
sdf_multi_point = f([[0, 0, 0],[1,1,1],[0.1,0.2,0.2]])

# Contains check
origin_contained = f.contains([0, 0, 0])

# Misc: nearest neighbor
origin_nn = f.nn([0, 0, 0])

# Misc: uniform surface point sampling
random_surface_points = f.sample_surface(10000)

# Misc: surface area
the_surface_area = f.surface_area

# All the functions also support an additional argument 'n_threads=<int>' to specify number of threads.
# by default we use min(32, n_cpus) 

To modify the vertices/faces, you can change f.vertices_mutable and f.faces_mutable, then call f.update() to update the internal data structures. You can also use f.vertices and f.faces to access vertices and faces (non-writable).

Screenshots

Robustness under self-intersections:

Reasonable result for non-watertight mesh with multiple parts:

Reasonable result for voxel grid

C++ Library Usage

sdf::SDF sdf(verts, faces); // verts (n, 3) float, faces (m, 3) float

// SDF. points (k, 3) float; return (k) float
Eigen::Vector3f sdf_at_points = sdf(points);

// Containment test, equal (but maybe faster) than sdf >= 0.
// points (k, 3) float; return (k) bool
Eigen::Matrix<bool, -1, 1> contains_points = sdf.contains(points);

// Miscellaneous: nearest neighbor. points (k, 3) float; return (k) int
Eigen::VectorXi nn_verts_idxs = sdf.nn(points);

// Miscellaneous: uniformly random points on surface (generates 10000 in this case)
Eigen::Matrix<float, -1, 3> random_surface_points = sdf.sample_surface(10000);

// Surface area
float surface_area = sdf.surface_area

Note SDF is > 0 inside and < 0 outside mesh.

Use in CMake project

  • find_package(sdf)
  • After defining a target: target_link_libraries(your_target sdf::sdf)

Robust mode

By default 'robust' mode is used. sdf::SDF sdf(verts, faces, false) to disable. The SDF computation will be slightly faster but may be incorrect if the mesh has self-intersections or incorrect winding (not CCW) on some faces.

Python

See the quickstart section for a usage example. help(pysdf) will show more unimportant miscellaneous functions you may want to use (surface normal, area, etc.).

Copying

By default, SDF(verts, faces) will copy the vertices/faces to ensure memory safety, especially since the arguments may be automatically converted. Use SDF(verts, faces, copy=False) to prevent this, if you are sure verts/faces are of types np.float32/np.uint32 respectively and will not be destroyed before the SDF instance. In this mode, vertices_mutable/faces_mutable are unavailable.

Warning about reliability

In robust mode (default) we use raytracing (parity count) to check containment. Currently the ray tracing has the same limitation as embree, that is when ray exactly hits an edge the intersection gets double counted, inverting the sign of the distance function. This is theoretically unlikely for random points but can occur either due to floating point error or if points and mesh vertices are both taken from a grid. In practice, we (1) randomize the ray tracing direction and (2) trace 3 rays along different axes and take majority to decrease the likelihood of this occurring.

In non-robust mode we use nearest surface normal to check containment. The contains check (and SDF sign) will be wrong under self-intersection or if normals are incorrectly oriented.

Dependencies

  • Eigen 3 (Python: automatically downloaded if needed)
  • Vendored (no installation needed):
    • nanoflann
    • nushoin/RTree
  • Optional:

Build + Install

mkdir build && cd build && cmake .. && make -j4 && sudo make install

Demo

A demo program can optionally be built, if meshview is installed. To use it, run ./sdf-demo BASIC_OBJ_FILE. Try the sample-obj/*.obj included in the project.

Benchmark

vs. trimesh

All benchmarks are ran on a 6-core CPU (Intel i7 8th generation, high-performance laptop). More is better.

Model vertices trimesh contains eval/s (numpy) trimesh contains eval/s (pyembree) our SDF evals / s (robust) our SDF evals / s (non-robust)
3241 29,555 237,855 5,077,725 8,187,117
49246 6,835 62,058 2,971,137 4,407,045
179282 1,301 20,157 1,672,859 1,987,869

vs. JianWenPL/multiperson (CUDA)

Here we compare to https://github.com/JiangWenPL/multiperson/tree/master/sdf. The GPU code is ran on a single GTX 1080 Ti, and CPU is a 6-core i7 5820K. I evaluate the SDF on an x-by-x-by-x grid in [-1,1]^3.

Results for SMPL model (13776 faces, 6890 vertices)

Grid resolution multiperson SDF runtime, ms our runtime, ms (robust) our runtime, ms (non-robust) speedup (robust)
32 46.70460891723633 13.006210327148438 7.317066192626953 3.59
64 236.6414031982422 62.57128715515137 51.19466781616211 3.78
128 1521.0322265625 400.36678314208984 347.3823070526123 3.80

Results for SMPL-X model (20908 faces, 10475 vertices).

Grid resolution multiperson SDF runtime, ms our runtime, ms (robust) our runtime, ms (non-robust) speedup (robust)
32 71.34893035888672 13.09061050415039 8.291006088256836 5.45
64 353.7056579589844 66.21336936950684 57.36279487609863 5.34
128 2303.649658203125 477.12063789367676 396.78120613098145 4.83

Notes:

  • This is not really a fair comparison, since the SDF computed in multiperson is more exact (computes distance to all faces) and mine is approximate (only distance to faces adjacent to nearest neighbors). Both methods are prone to numerical error but perhaps mine is more so.
  • Basically the more faces the mesh has, the more performance advantage our method has. For small meshes with very few faces the CUDA implementation should be somewhat faster.
  • My implementation is designed to evaluate on arbitrary continuous points, so it requires the meshgrid points to be generated and passed, while the implementation in multiperson assumes a grid which it generates on the fly, potentially saving some memory access time especially when resolution is high.

License

BSD 2-clause

This library relies on the Eigen (MPL2) nanoflann (BSD) and RTree (MIT) libraries.

sdf's People

Contributors

cnpcshangbo avatar sxyu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sdf's Issues

what(): Library not found error in SimNet v21.06

Hi, I'm trying to run a nvidia code known as SimNet. It uses your pysdf library to input stl files.

I've used a docker image of SimNet so things should also been installed properly. Anyway, I also did a bare bone installation.

However, when I tried to run the python code which uses the pysdf lib, I got the error below. Not sure if you can help with it.

Thanks!

terminate called after throwing an instance of 'std::runtime_error' what(): Library not found [25eb7142085d:00661] *** Process received signal *** [25eb7142085d:00661] Signal: Aborted (6) [25eb7142085d:00661] Signal code: (-6) [25eb7142085d:00661] [ 0] /lib/x86_64-linux-gnu/libc.so.6(+0x3ef20)[0x7f04d29b2f20] [25eb7142085d:00661] [ 1] /lib/x86_64-linux-gnu/libc.so.6(gsignal+0xc7)[0x7f04d29b2e97] [25eb7142085d:00661] [ 2] /lib/x86_64-linux-gnu/libc.so.6(abort+0x141)[0x7f04d29b4801] [25eb7142085d:00661] [ 3] /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x8c957)[0x7f0363633957] [25eb7142085d:00661] [ 4] /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x92ae6)[0x7f0363639ae6] [25eb7142085d:00661] [ 5] /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x92b21)[0x7f0363639b21] [25eb7142085d:00661] [ 6] /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x92d54)[0x7f0363639d54] [25eb7142085d:00661] [ 7] /simnet/external/lib/libsdf.so(_Z11initContextv+0x10a)[0x7f02e3fd6dda] [25eb7142085d:00661] [ 8] /simnet/external/lib/libsdf.so(signedDistanceField+0x4d)[0x7f02e3fd99bd] [25eb7142085d:00661] [ 9] /usr/local/lib/python3.6/dist-packages/pysdf-0.1-py3.6-linux-x86_64.egg/pysdf/sdf.cpython-36m-x86_64-linux-gnu.so(+0xb14f)[0x7f02e42a014f] [25eb7142085d:00661] [10] python[0x50a635] [25eb7142085d:00661] [11] python(_PyEval_EvalFrameDefault+0x1226)[0x50cd96] [25eb7142085d:00661] [12] python[0x507d64] [25eb7142085d:00661] [13] python[0x509a90] [25eb7142085d:00661] [14] python[0x50a48d] [25eb7142085d:00661] [15] python(_PyEval_EvalFrameDefault+0x444)[0x50bfb4] [25eb7142085d:00661] [16] python[0x507d64] [25eb7142085d:00661] [17] python[0x509a90] [25eb7142085d:00661] [18] python[0x50a48d] [25eb7142085d:00661] [19] python(_PyEval_EvalFrameDefault+0x1226)[0x50cd96] [25eb7142085d:00661] [20] python[0x507d64] [25eb7142085d:00661] [21] python[0x509a90] [25eb7142085d:00661] [22] python[0x50a48d] [25eb7142085d:00661] [23] python(_PyEval_EvalFrameDefault+0x1226)[0x50cd96] [25eb7142085d:00661] [24] python[0x507d64] [25eb7142085d:00661] [25] python(_PyFunction_FastCallDict+0x2e2)[0x509042] [25eb7142085d:00661] [26] python[0x594931] [25eb7142085d:00661] [27] python[0x549e5f] [25eb7142085d:00661] [28] python[0x5513d1] [25eb7142085d:00661] [29] python(PyObject_Call+0x3e)[0x59fc4e] [25eb7142085d:00661] *** End of error message *** Aborted

Building Python Bindings

Hi, I'm trying to chase down a segfault and am rebuilding this without optimisations & with debug symbols, however it seems the included Makefile & CMake config doesn't build the python bindings. I have pointed CMake to the pybind11 directory as CMakeLists.txt asks, but I'm not sure what comes next, it doesn't look like pybind.cpp is included in any of the target rules (or if that's what I should be looking at at all).

How do I build and link the python bindings?

Thanks.

a small amount of abnormal data for all obj file

Hi! Thanks for your library! It's extremely useful. However, when I try to convert obj file to sdf, the majority works properly but I have around 1000 points (out of 260,000) that has very large/small data e^18. I want to know what might cause this problem, and if I were to fix it, currently I'm clipping the sdf value to my normal max and min value, but the histogram looks funny if I do the clipping because I got a significant amount of large value. Any idea what cause this and potential fixation? Thank you!
histogram before clipping
hist

histogram after clipping
hist
s

PROBLEM IN SDF

Hi,
in JianWenPL/multiperson this implementation all phi values come out to be zero do you know why?

Thank you

I met with erratic results

I encounter unstable results when I run Python code. Is there any randomness to this method?
This is my code:

import numpy as np
from pysdf import SDF
import trimesh
o = trimesh.load('aa.obj')
o.vertices = o.vertices - o.vertices.min()
resolution = 100

tick = np.expand_dims(np.linspace((o.vertices).min(),(o.vertices).max(),resolution),1)
[x,y,z] = np.meshgrid(tick,tick,tick)
queryPoint = np.c_[x.reshape(-1),y.reshape(-1),z.reshape(-1)]

f = SDF(o.vertices, o.faces)
sdf0 = f(queryPoint)
f1 = SDF(o.vertices, o.faces)
sdf1 = f1(queryPoint)
print((sdf0 - sdf1).max())

environment:
Intel(R) Core(TM) i5-9500 CPU; Windows 10; pyhton 3.7.9; No GPUs

expected output: 0
actual output: 37.03153 (stochastic)

Can you provide a method without randomness? Thank you!

adding eigen in distributed package on pypi

Hi,

thank you for the great work.

I have some trouble installing pysdf from pypi.org through pip install pysdf as it fails on the eigen download step in setup.py. Would it make sense to add eigen source code as a submodule and include eigen 's source in the source in the source file distributed on pypi.org ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.