Skip to content

Instantly share code, notes, and snippets.

@chenzhaiyu
Last active December 6, 2022 22:01
Show Gist options
  • Save chenzhaiyu/179fde5aedd1f909bb6ab8145f4366d0 to your computer and use it in GitHub Desktop.
Save chenzhaiyu/179fde5aedd1f909bb6ab8145f4366d0 to your computer and use it in GitHub Desktop.
Re-orientation of a mesh
def reorient_mesh(self, ply_vertex, ply_face, ply_face_label, graph):
"""To orient the mesh (normal), only need to decide the correct vertex order in a plane"""
# lowest vertex and its incident faces
id_low_vertex, id_low_faces = self.lowest(ply_vertex, ply_face, ply_face_label)
# get lowest vertex coordinates
low_vertex = ply_vertex[id_low_vertex].tolist()
# get its incident faces
low_faces = ply_face[id_low_faces]
# define target as mean local vertices
local_vertices = ply_vertex[np.hstack([face.tolist() for face in low_faces])]
local_vertices = [vertex.tolist() for vertex in local_vertices]
target = np.mean(local_vertices, axis=0)
# predicate: vec & (one of the) norms of the low_faces point towards different directions
vec = target - low_vertex
id_face = id_low_faces[-1] # which face to start
norm = self.normal(ply_face[id_face], ply_vertex)
if np.dot(vec, norm) > 0:
ply_face[id_face] = ply_face[id_face][::-1]
# recursively trace adjacent polygons
global id_unvisited
id_unvisited = np.where(ply_face_label == True)[0]
self.trace_and_correct(id_face, ply_face, graph, ply_vertex) # recursion
@staticmethod
def normal(id_vertices, ply_vertex):
p = ply_vertex[id_vertices]
p = p.view(np.float32).reshape(p.shape + (-1,))
# These two vectors are in the plane
v1 = p[1] - p[0]
v2 = p[2] - p[1]
# the cross product is a vector normal to the plane
return np.cross(v1, v2)
def trace_and_correct(self, id_source, ply_face, graph, ply_vertex, depth=0):
"""Trace adjacent polygons and correct its vertex order when necessary. To be executed recursively."""
# source polygon is ensured with correct vertex order
global id_unvisited
# termination condition
if not len(id_unvisited):
return
else:
# find neighbours
id_neighbours = graph[id_source]
for id_neighbour in id_neighbours:
# skip if processed
if id_neighbour not in id_unvisited:
continue
# print('depth: {}'.format(depth))
# print('number of unvisited: {}'.format(len(id_unvisited)))
print('visiting face: {}'.format(id_neighbour))
id_unvisited = np.delete(id_unvisited, np.where(id_unvisited == id_neighbour)[0][0], axis=0)
source = ply_face[id_source]
neighbour = ply_face[id_neighbour]
# identify common edge
vertices_source = ply_vertex[source].tolist()
vertices_neighbour = ply_vertex[neighbour].tolist()
vertices_common = list(set(vertices_source) & set(vertices_neighbour))
seq_source = []
seq_neighbour = []
for v in np.array(vertices_common):
seq_source.append(np.where(vertices_source == v)[0][0])
seq_neighbour.append(np.where(vertices_neighbour == v)[0][0])
assert len(seq_source) == 2 and len(seq_neighbour) == 2
# circular monotonic check
# case 1: monotonic increase (normal adjacent and source n-0 or neighbour n'-0)
case_1 = ((seq_source[1] - seq_source[0] == 1) or (seq_source[0] == len(source)-1 and seq_source[1] == 0)) \
and ((seq_neighbour[1] - seq_neighbour[0] == 1) or (seq_neighbour[0] == len(neighbour)-1 and seq_neighbour[1] == 0))
# case 2: monotonic decrease (normal adjacent and source 0-n or neighbour 0-n')
case_2 = ((seq_source[1] - seq_source[0] == -1) or (seq_source[0] == 0 and seq_source[1] == len(source)-1)) \
and ((seq_neighbour[1] - seq_neighbour[0] == -1) or (seq_neighbour[0] == 0 and seq_neighbour[1] == len(neighbour)-1))
orientation_ok = False if (case_1 or case_2) else True
if not orientation_ok:
# neighbour vertex order needs to be reversed
ply_face[id_neighbour] = neighbour[::-1]
print('face {} reversed'.format(id_neighbour))
self.trace_and_correct(id_neighbour, ply_face, graph, ply_vertex, depth=depth + 1)
@staticmethod
def lowest(ply_vertex, ply_face, ply_face_label):
# find the lowest vertex (one index) and its incident faces (multiple indices)
all_v = np.array([face for face in ply_face]) # each row represents a face's vertex indices
all_z = np.array([np.array(ply_vertex[face].view(np.float32).reshape(ply_vertex[face].shape + (-1,)))[:, 2]
for face in ply_face]) # each row represents a face's z
# indices of gt faces
gt_i = np.where(ply_face_label == True)[0]
# get minimal z from gt vertices
min_z = np.inf
id_face_min_z = None
which_vertex = None
for i in gt_i:
z = np.min(all_z[i])
if z < min_z:
min_z = z
id_face_min_z = i
which_vertex = np.argmin(all_z[i])
# index of lowest vertex in property 'vertex'
# due to data's bug (duplicate vertices), this vertex be not shared by several gt faces, but fine here because
# only its value is needed
id_low_vertex = all_v[id_face_min_z][which_vertex]
# brute-force search for identical vertices
# due to data's bug (duplicate vertices)
low_vertex = ply_vertex[id_low_vertex]
id_low_vertices = [] # identical vertices of the lowest value
tol = 1e-4
for i, value in enumerate(ply_vertex):
dist = np.linalg.norm(np.array(value.tolist()) - np.array(low_vertex.tolist()), 1)
if dist < tol:
# found identical vertex
id_low_vertices.append(i)
# indices of all its incident faces in property 'face'
id_low_faces = []
for id_face, vlist in enumerate(all_v):
if set(id_low_vertices) & set(vlist) and id_face in gt_i:
id_low_faces.append(id_face)
return id_low_vertex, id_low_faces
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment