| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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] |
| |
|