Skip to content
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

Cleaning curvature fit #83

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 13 additions & 14 deletions slam/curvature.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@ def curvature_fit(mesh, tol=1e-12, neighbour_size=2):
Computation of the two principal curvatures based on:
Petitjean, A survey of methods for recovering quadrics
in triangle meshes, ACM Computing Surveys, 2002
:param mesh:
:param tol:
:param neighbour_size:
:return:
:param mesh: trimesh mesh
:param tol: input for determine_local_basis call. set by default with 1e-12
:param neighbour_size: set by default with 2
:return: curvature : array. length = number of vertex
direction : array. length = number of vertex
"""
N = mesh.vertices.shape[0]
# vertex_normals = mean_vertex_normals(N,
# faces=mesh.faces, face_normals = mesh.face_normals)
# vertex_normals, Avertex, Acorner, up, vp =
# calcvertex_normals(mesh, mesh.face_normals)

vertex_normals = mesh.vertex_normals

curvature = np.zeros((N, 2))
Expand All @@ -40,17 +38,12 @@ def curvature_fit(mesh, tol=1e-12, neighbour_size=2):
normal = np.reshape(normal, (3, 1))
proj_matrix = np.identity(3) - np.matmul(normal, normal.transpose())
vec1, vec2 = determine_local_basis(normal, proj_matrix, tol)
# rotation_matrix = np.concatenate((vec1.transpose(),
# np.reshape(vec2, (1, 3)),
# normal.transpose()))
rotation_matrix = \
np.concatenate((vec1, vec2, normal), axis=1).transpose()

# neighbours
neigh = stop.k_ring_neighborhood(mesh, index=i, k=neighbour_size,
adja=adjacency_matrix)
# neigh =
# np.logical_and(neigh <= neighbour_size, neigh > 0).nonzero()[0]
neigh = (neigh <= neighbour_size).nonzero()[0]
neigh_len = len(neigh)
vertices_neigh = mesh.vertices[neigh, :].transpose()
Expand All @@ -64,7 +57,6 @@ def curvature_fit(mesh, tol=1e-12, neighbour_size=2):
Z = np.reshape(rotated_vertices_neigh[2, :], (neigh_len, 1))
XY = np.concatenate((X ** 2, X * Y, Y ** 2), axis=1)
parameters = np.matmul(np.linalg.pinv(XY), Z)
# parameters = np.linalg.lstsq(XY,Z)

# Curvature tensor
tensor = np.array([[parameters[0, 0], parameters[1, 0] / 2],
Expand All @@ -87,6 +79,13 @@ def curvature_fit(mesh, tol=1e-12, neighbour_size=2):


def determine_local_basis(normal, proj_matrix, tol, approach='proj'):
"""
@param normal:
@param proj_matrix:
@param tol:
@param approach: set by default with 'proj'
@return: vec1, vec2
"""
if approach == 'proj':
# Original code: use projection matrix
vec1 = np.matmul(proj_matrix, np.array([[1], [0], [0]]))
Expand Down
130 changes: 96 additions & 34 deletions slam/vertex_voronoi.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,105 @@
import numpy as np
import itertools
import slam.io as sio
import slam.texture as stex
import os
import time
import pandas as pd


def vertex_voronoi(mesh):
def voronoi_de_papa(mesh):
"""
@author : Maxime Dieudonné [email protected]
compute vertex voronoi of a mesh as described in
Meyer, M., Desbrun, M., Schroder, P., Barr, A. (2002).
Discrete differential geometry operators for triangulated 2manifolds.
Visualization and Mathematics, 1..26.
:param mesh: trimesh object
:return: numpy array of shape (mesh.vertices.shape[0],)
Meyer, M., Desbrun, M., Schröder, P., & Barr, A. (2002).
Discrete differential-geometry operators for triangulated 2-manifolds.
Visualization and Mathematics, 1–26.
Voronoi texture : Area of Voronoi at each vertex.
@param mesh: a Trimesh mesh object
@return: a ndarray of the voronoi texture.
"""
Nbv = mesh.vertices.shape[0]
Nbp = mesh.faces.shape[0]
obt_angs = mesh.face_angles > np.pi / 2
obt_poly = obt_angs[:, 0] | obt_angs[:, 1] | obt_angs[:, 2]
print(' -percent polygon with obtuse angle ',
100.0 * len(np.where(obt_poly)[0]) / Nbp)
obt_poly_df = np.array([[x]*3 for x in obt_poly]).flatten()
area_face = mesh.area_faces
area_face_df = np.array([[x]*3 for x in area_face]).flatten()
print(' -percent polygon with obtuse angle ', 100.0 * len(np.where(obt_poly)[0]) / Nbp)
cot = 1 / np.tan(mesh.face_angles)
vert_voronoi = np.zeros(Nbv)
for ind_p, p in enumerate(mesh.faces):
if obt_poly[ind_p]:
obt_verts = p[obt_angs[ind_p, :]]
vert_voronoi[obt_verts] = vert_voronoi[obt_verts] + \
mesh.area_faces[ind_p] / 2.0
non_obt_verts = p[[not x for x in obt_angs[ind_p, :]]]
vert_voronoi[non_obt_verts] = vert_voronoi[non_obt_verts] + \
mesh.area_faces[ind_p] / 4.0
else:
d0 = np.sum(
np.power(mesh.vertices[p[1], :] - mesh.vertices[p[2], :], 2))
d1 = np.sum(
np.power(mesh.vertices[p[2], :] - mesh.vertices[p[0], :], 2))
d2 = np.sum(
np.power(mesh.vertices[p[0], :] - mesh.vertices[p[1], :], 2))
vert_voronoi[p[0]] = vert_voronoi[p[0]] + \
(d1 * cot[ind_p, 1] + d2 * cot[ind_p, 2]) / 8.0
vert_voronoi[p[1]] = vert_voronoi[p[1]] + \
(d2 * cot[ind_p, 2] + d0 * cot[ind_p, 0]) / 8.0
vert_voronoi[p[2]] = vert_voronoi[p[2]] + \
(d0 * cot[ind_p, 0] + d1 * cot[ind_p, 1]) / 8.0

return vert_voronoi
#
comb = np.array([list(itertools.combinations(mesh.faces[i], 2)) for i in range(Nbp)])
comb = comb.reshape([comb.size // 2, 2])
V1 = np.array([mesh.vertices[i] for i in comb[:, 0]])
V2 = np.array([mesh.vertices[i] for i in comb[:, 1]])
#
# Dist array : compute the sqare of the norm of each segments.We need to switch the first and the last colums to gets athe end
# the right order : D0, D1, D2
# faces | segments of the faces
# F0 | D0 D1 D2
# F1 | D0 D1 D2
# FN | D0 D1 D2
Dist = np.sum(np.power(V1 - V2, 2), axis=1)
Dist = Dist.reshape([Dist.size // 3, 3])
Dist[:, [2, 0]] = Dist[:, [0, 2]]
#
# VorA0 : compute the voronoi area (hypothesis : the face triangle is Aigu) for all the V0 of the faces
# VorA1 : compute the voronoi area (hypothesis : the face triangle is Aigu) for all the V1 of the faces
# VorA2 : compute the voronoi area (hypothesis : the face triangle is Aigu) for all the V2 of the faces
# VorA : concatenation of VorA0, VorA1, VorA2.
# faces | segments of the faces
# F0 | VorA0 VorA1 VorA2
# F1 | VorA0 VorA1 VorA2
# FN | VorA0 VorA1 VorA2
VorA0 = Dist[:, 1] * cot[:, 1] + Dist[:, 2] * cot[:, 2]
VorA1 = Dist[:, 0] * cot[:, 0] + Dist[:, 2] * cot[:, 2]
VorA2 = Dist[:, 0] * cot[:, 0] + Dist[:, 1] * cot[:, 1]
VorA = np.array([VorA0, VorA1, VorA2]).transpose()
# df_Vor
# faces : the index of the faces (it doesn't appear in the dataframe)
# Vertex : the index of the vertex
# VorA : the Voronoi area with the hypothesis "the triangle face is aigu"
# Area : the area of the face (usefull for the next because the voronoi area in case the triangle is obtu depends of it)
# Fobt : Face obtu true or false
# Aobt : Angle Obtu true or false
#
# faces | Vertex | VorA | Area |Fobt |Aobt
# F0 | V0 | 0.2 | 1.2 | true | false
# F0 | V1 |
# F0 | V2 |
# ...
# FN | V0 |
# FN | V1 |
# FN | V2 |
VorA = VorA.flatten()
face_flat = mesh.faces.flatten()
df_Vor = pd.DataFrame(dict(VorA=VorA,Area = area_face_df, Vertex=face_flat, Fobt = obt_poly_df, Aobt = obt_angs.flatten()))
# we create 3 subdataframe according the condition on Fobt and Aobt. The 3 possibilities are :
# (Fobt, Aobt) = (False, False) -> the voronoi area of a vertex in 1 triangle face is given in VorA
# (Fobt, Aobt) = (True, False) -> the voronoi area of a vertex in 1 triangle face is given by area_face/4
# (Fobt, Aobt) = (True, True) -> the voronoi area of a vertex in 1 triangle face is given by area_face/2
# area_VorA : sum of the vornoi area only for vertex with angle in aigue faces
# area_VorOA : sum of the vornoi area only for vertex with Aigue angle in Obtue faces
# area_VorOO : sum of the vornoi area only for vertex with Obtue angle in Obtue faces
# area_VorA
area_VorA = df_Vor[df_Vor['Fobt']==False].groupby('Vertex',as_index=False).sum()[['Vertex','VorA']]
area_VorA['VorA'] = area_VorA['VorA']/8
# area_VorOA
area_VorOA = df_Vor[(df_Vor['Fobt'] == True) & (df_Vor['Aobt'] == False)].groupby('Vertex',as_index=False).sum()[['Vertex','Area']]
area_VorOA.rename(columns={'Area':'A4'}, inplace=True)
area_VorOA['A4'] = area_VorOA['A4']/4
# area_VorOO
Area_VorOO = df_Vor[(df_Vor['Fobt'] ==True) & (df_Vor['Aobt'] == True)].groupby('Vertex',as_index=False).sum()[['Vertex','Area']]
Area_VorOO.rename(columns={'Area':'A2'}, inplace=True)
Area_VorOO['A2'] = Area_VorOO['A2']/2
# Then the voronoi area of one vertex in the mesh is given by the sum of all the vornoi area in its neighboored triangle faces
# so we sum for each vertex the area given in area_VorA,area_VorOA and area_VorOO if the value exist
#area_Vor2 :
# | idx Vertex | VorA | A2 | A4 |
# | 1 | 0.2 | 1.2 | Nan | -> sum = area_vor
# | 2 | 0.7 | Nan | Nan | -> sum
# | ... |
# | 480 562 | -> sum
df_area_Vor = pd.merge(area_VorA, area_VorOA, on='Vertex', how="outer")
df_area_Vor2 = pd.merge(df_area_Vor, Area_VorOO, on='Vertex', how="outer")
df_area_Vor2 = df_area_Vor2.sort_values(['Vertex'])
area_Vor = df_area_Vor2[['VorA', 'A2', 'A4']].sum(axis=1)
return area_Vor.values