|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import numpy as np |
|
from scipy.spatial import cKDTree |
|
import trimesh |
|
|
|
import logging |
|
|
|
logging.getLogger("trimesh").setLevel(logging.ERROR) |
|
|
|
|
|
def save_obj_mesh(mesh_path, verts, faces): |
|
file = open(mesh_path, 'w') |
|
for v in verts: |
|
file.write('v %.4f %.4f %.4f\n' % (v[0], v[1], v[2])) |
|
for f in faces: |
|
f_plus = f + 1 |
|
file.write('f %d %d %d\n' % (f_plus[0], f_plus[1], f_plus[2])) |
|
file.close() |
|
|
|
|
|
def save_obj_mesh_with_color(mesh_path, verts, faces, colors): |
|
file = open(mesh_path, 'w') |
|
|
|
for idx, v in enumerate(verts): |
|
c = colors[idx] |
|
file.write('v %.4f %.4f %.4f %.4f %.4f %.4f\n' % |
|
(v[0], v[1], v[2], c[0], c[1], c[2])) |
|
for f in faces: |
|
f_plus = f + 1 |
|
file.write('f %d %d %d\n' % (f_plus[0], f_plus[1], f_plus[2])) |
|
file.close() |
|
|
|
|
|
def save_ply(mesh_path, points, rgb): |
|
''' |
|
Save the visualization of sampling to a ply file. |
|
Red points represent positive predictions. |
|
Green points represent negative predictions. |
|
:param mesh_path: File name to save |
|
:param points: [N, 3] array of points |
|
:param rgb: [N, 3] array of rgb values in the range [0~1] |
|
:return: |
|
''' |
|
to_save = np.concatenate([points, rgb * 255], axis=-1) |
|
return np.savetxt( |
|
mesh_path, |
|
to_save, |
|
fmt='%.6f %.6f %.6f %d %d %d', |
|
comments='', |
|
header=( |
|
'ply\nformat ascii 1.0\nelement vertex {:d}\n' + |
|
'property float x\nproperty float y\nproperty float z\n' + |
|
'property uchar red\nproperty uchar green\nproperty uchar blue\n' + |
|
'end_header').format(points.shape[0])) |
|
|
|
|
|
class HoppeMesh: |
|
|
|
def __init__(self, verts, faces, vert_normals, face_normals): |
|
''' |
|
The HoppeSDF calculates signed distance towards a predefined oriented point cloud |
|
http://hhoppe.com/recon.pdf |
|
For clean and high-resolution pcl data, this is the fastest and accurate approximation of sdf |
|
:param points: pts |
|
:param normals: normals |
|
''' |
|
self.verts = verts |
|
self.faces = faces |
|
self.vert_normals = vert_normals |
|
self.face_normals = face_normals |
|
|
|
self.kd_tree = cKDTree(self.verts) |
|
self.len = len(self.verts) |
|
|
|
def query(self, points): |
|
dists, idx = self.kd_tree.query(points, n_jobs=1) |
|
|
|
|
|
dirs = points - self.verts[idx] |
|
signs = (dirs * self.vert_normals[idx]).sum(axis=1) |
|
signs = (signs > 0) * 2 - 1 |
|
return signs * dists |
|
|
|
def contains(self, points): |
|
|
|
labels = trimesh.Trimesh(vertices=self.verts, |
|
faces=self.faces).contains(points) |
|
return labels |
|
|
|
def export(self, path): |
|
if self.colors is not None: |
|
save_obj_mesh_with_color(path, self.verts, self.faces, |
|
self.colors[:, 0:3] / 255.0) |
|
else: |
|
save_obj_mesh(path, self.verts, self.faces) |
|
|
|
def export_ply(self, path): |
|
save_ply(path, self.verts, self.colors[:, 0:3] / 255.0) |
|
|
|
def triangles(self): |
|
return self.verts[self.faces] |
|
|