Skip to content

Commit

Permalink
add qslim decimation feature (#115)
Browse files Browse the repository at this point in the history
  • Loading branch information
odedstein committed Apr 25, 2024
1 parent 7427ea8 commit 089a060
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 28 deletions.
17 changes: 15 additions & 2 deletions src/cpp/binding_decimate.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <igl/decimate.h>
#include <igl/qslim.h>
#include <pybind11/stl.h>
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
Expand All @@ -13,12 +14,24 @@ using EigenDRef = Ref<MatrixType, 0, EigenDStride>; //allows passing column/row

void binding_decimate(py::module& m) {
m.def("_decimate_cpp_impl",[](EigenDRef<MatrixXd> v,
EigenDRef<MatrixXi> f, int num_faces)
EigenDRef<MatrixXi> f,
int num_faces,
int method)
{
Eigen::MatrixXd SV;
Eigen::MatrixXi SF;
Eigen::VectorXi J, I;
igl::decimate(v,f,num_faces,SV,SF,I,J);
if(method==0) {
igl::decimate(v,f,num_faces,
//This will be required when we bump the libigl version.
//true,
SV,SF,I,J);
} else if(method==1) {
igl::qslim(v,f,num_faces,
//This will be required when we bump the libigl version.
//true,
SV,SF,I,J);
}
return std::make_tuple(SV,SF,I,J);
});

Expand Down
19 changes: 17 additions & 2 deletions src/gpytoolbox/decimate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import numpy as np

def decimate(V,F,face_ratio=0.1,num_faces=None):
def decimate(V,F,
face_ratio=0.1,
num_faces=None,
method='shortest_edge'):
"""Reduce the number of faces of a triangle mesh.
From a manifold triangle mesh, builds a new triangle mesh with fewer faces than the original one using libigl's decimation algorithm.
Expand All @@ -15,6 +18,9 @@ def decimate(V,F,face_ratio=0.1,num_faces=None):
Desired ratio of output faces to input faces
num_faces : int, optional (default None)
Desired number of faces in output mesh (superseeds face_ratio if set)
method : string, optional (default shortest_edge)
Which mesh decimation algorithm to use.
Options are 'shortest_edge' and 'qslim'
Returns
-------
Expand Down Expand Up @@ -53,6 +59,15 @@ def decimate(V,F,face_ratio=0.1,num_faces=None):
if (num_faces is None):
num_faces = np.floor(face_ratio*F.shape[0]).astype(np.int32)

v, f, i, j = _decimate_cpp_impl(V.astype(np.float64),F.astype(np.int32),num_faces)
method_int = 0
if method == 'shortest_edge':
method_int = 0
elif method == 'qslim':
method_int = 1
else:
raise Exception("Not a valid decimation method.")
v, f, i, j = _decimate_cpp_impl(V.astype(np.float64),F.astype(np.int32),
num_faces,
method_int)

return v,f,i,j
50 changes: 26 additions & 24 deletions test/test_decimate.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,37 @@ def test_armadillo(self):
np.random.seed(0)
v,f = gpytoolbox.read_mesh("test/unit_tests_data/armadillo.obj")
for nn in range(20,2000,301):
u,g,i,j = gpytoolbox.decimate(v,f,num_faces=nn)
self.assertTrue(np.isclose(g.shape[0]-nn,0,atol=3))
ratio = nn/f.shape[0]
u,g,i,j = gpytoolbox.decimate(v,f,face_ratio=ratio)
# print(nn)
# print(g.shape[0])
# print(g.shape[0]/f.shape[0])
self.assertTrue(np.isclose(ratio - (g.shape[0]/f.shape[0]),0,atol=0.001))
# Are the outputs what they claim they are?
# Is i the size of g and j the size of u?
self.assertTrue(g.shape[0]==i.shape[0])
self.assertTrue(u.shape[0]==j.shape[0])
# There isn't really a good way to check that one is the birth vertex of the other...
for method in {'shortest_edge', 'qslim'}:
u,g,i,j = gpytoolbox.decimate(v,f,method=method,num_faces=nn)
self.assertTrue(np.isclose(g.shape[0]-nn,0,atol=3))
ratio = nn/f.shape[0]
u,g,i,j = gpytoolbox.decimate(v,f,method=method,face_ratio=ratio)
# print(nn)
# print(g.shape[0])
# print(g.shape[0]/f.shape[0])
self.assertTrue(np.isclose(ratio - (g.shape[0]/f.shape[0]),0,atol=0.001))
# Are the outputs what they claim they are?
# Is i the size of g and j the size of u?
self.assertTrue(g.shape[0]==i.shape[0])
self.assertTrue(u.shape[0]==j.shape[0])
# There isn't really a good way to check that one is the birth vertex of the other...

def test_with_boundary(self):
np.random.seed(0)
v,f = gpytoolbox.read_mesh("test/unit_tests_data/airplane.obj")
for nn in range(200,2000,301):
u,g,i,j = gpytoolbox.decimate(v,f,num_faces=nn)
self.assertTrue(np.isclose(g.shape[0]-nn,0,atol=3))
ratio = nn/f.shape[0]
u,g,i,j = gpytoolbox.decimate(v,f,face_ratio=ratio)
self.assertTrue(np.isclose(ratio - (g.shape[0]/f.shape[0]),0,atol=0.001))
gpytoolbox.write_mesh("output.obj",u,g)
# Are the outputs what they claim they are?
# Is i the size of g and j the size of u?
self.assertTrue(g.shape[0]==i.shape[0])
self.assertTrue(u.shape[0]==j.shape[0])
# There isn't really a good way to check that one is the birth vertex of the other...
for method in {'shortest_edge', 'qslim'}:
u,g,i,j = gpytoolbox.decimate(v,f,method=method,num_faces=nn)
self.assertTrue(np.isclose(g.shape[0]-nn,0,atol=3))
ratio = nn/f.shape[0]
u,g,i,j = gpytoolbox.decimate(v,f,method=method,face_ratio=ratio)
self.assertTrue(np.isclose(ratio - (g.shape[0]/f.shape[0]),0,atol=0.001))
gpytoolbox.write_mesh("output.obj",u,g)
# Are the outputs what they claim they are?
# Is i the size of g and j the size of u?
self.assertTrue(g.shape[0]==i.shape[0])
self.assertTrue(u.shape[0]==j.shape[0])
# There isn't really a good way to check that one is the birth vertex of the other...

if __name__ == '__main__':
unittest.main()

0 comments on commit 089a060

Please sign in to comment.