-
-
Save chenzhaiyu/179fde5aedd1f909bb6ab8145f4366d0 to your computer and use it in GitHub Desktop.
Re-orientation of a mesh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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