|
|
import numpy as np |
|
|
import trimesh |
|
|
import os |
|
|
import json |
|
|
import math |
|
|
import open3d as o3d |
|
|
import torch |
|
|
|
|
|
|
|
|
def sample_surface(mesh, count, face_weight=None, sample_color=False, seed=147): |
|
|
|
|
|
if face_weight is None: |
|
|
|
|
|
|
|
|
face_weight = mesh.area_faces |
|
|
|
|
|
|
|
|
weight_cum = np.cumsum(face_weight) |
|
|
|
|
|
|
|
|
random = np.random.default_rng(seed).random |
|
|
|
|
|
|
|
|
face_pick = random(count) * weight_cum[-1] |
|
|
|
|
|
face_index = np.searchsorted(weight_cum, face_pick) |
|
|
|
|
|
|
|
|
tri_origins = mesh.vertices[mesh.faces[:, 0]] |
|
|
tri_vectors = mesh.vertices[mesh.faces[:, 1:]].copy() |
|
|
tri_vectors -= np.tile(tri_origins, (1, 2)).reshape((-1, 2, 3)) |
|
|
|
|
|
|
|
|
tri_origins = tri_origins[face_index] |
|
|
tri_vectors = tri_vectors[face_index] |
|
|
|
|
|
if sample_color and hasattr(mesh.visual, "uv"): |
|
|
uv_origins = mesh.visual.uv[mesh.faces[:, 0]] |
|
|
uv_vectors = mesh.visual.uv[mesh.faces[:, 1:]].copy() |
|
|
uv_origins_tile = np.tile(uv_origins, (1, 2)).reshape((-1, 2, 2)) |
|
|
uv_vectors -= uv_origins_tile |
|
|
uv_origins = uv_origins[face_index] |
|
|
uv_vectors = uv_vectors[face_index] |
|
|
|
|
|
|
|
|
random_lengths = random((len(tri_vectors), 2, 1)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
random_test = random_lengths.sum(axis=1).reshape(-1) > 1.0 |
|
|
random_lengths[random_test] -= 1.0 |
|
|
random_lengths = np.abs(random_lengths) |
|
|
|
|
|
|
|
|
sample_vector = (tri_vectors * random_lengths).sum(axis=1) |
|
|
|
|
|
|
|
|
|
|
|
samples = sample_vector + tri_origins |
|
|
|
|
|
if sample_color: |
|
|
if hasattr(mesh.visual, "uv"): |
|
|
sample_uv_vector = (uv_vectors * random_lengths).sum(axis=1) |
|
|
uv_samples = sample_uv_vector + uv_origins |
|
|
try: |
|
|
texture = mesh.visual.material.baseColorTexture |
|
|
except: |
|
|
texture = mesh.visual.material.image |
|
|
colors = trimesh.visual.color.uv_to_interpolated_color(uv_samples, texture) |
|
|
else: |
|
|
colors = mesh.visual.face_colors[face_index] |
|
|
|
|
|
return samples, face_index, colors |
|
|
|
|
|
return samples, face_index |
|
|
|
|
|
|
|
|
def get_ray_directions(W, H, fx, fy, cx, cy, use_pixel_centers=True): |
|
|
pixel_center = 0.5 if use_pixel_centers else 0 |
|
|
i, j = np.meshgrid( |
|
|
np.arange(W, dtype=np.float32) + pixel_center, |
|
|
np.arange(H, dtype=np.float32) + pixel_center, |
|
|
indexing="xy", |
|
|
) |
|
|
directions = np.stack( |
|
|
[(i - cx) / fx, -(j - cy) / fy, -np.ones_like(i)], -1 |
|
|
) |
|
|
|
|
|
return directions |
|
|
|
|
|
|
|
|
def gen_pcd(depth, c2w_opengl, camera_angle_x): |
|
|
|
|
|
h, w = depth.shape |
|
|
|
|
|
depth_valid = depth < 65500.0 |
|
|
depth = depth[depth_valid] |
|
|
focal = ( |
|
|
0.5 * w / math.tan(0.5 * camera_angle_x) |
|
|
) |
|
|
ray_directions = get_ray_directions(w, h, focal, focal, w // 2, h // 2) |
|
|
points_c = ray_directions[depth_valid] * depth[:, None] |
|
|
points_c_homo = np.concatenate( |
|
|
[points_c, np.ones_like(points_c[..., :1])], axis=-1 |
|
|
) |
|
|
org_points = (points_c_homo @ c2w_opengl.T)[..., :3] |
|
|
|
|
|
return org_points |
|
|
|
|
|
|
|
|
def save_point_cloud(coord, color=None, file_path="pc.ply", logger=None): |
|
|
os.makedirs(os.path.dirname(file_path), exist_ok=True) |
|
|
coord = np.array(coord) |
|
|
if color is not None: |
|
|
color = np.array(color) |
|
|
pcd = o3d.geometry.PointCloud() |
|
|
pcd.points = o3d.utility.Vector3dVector(coord) |
|
|
pcd.colors = o3d.utility.Vector3dVector(np.ones_like(coord) if color is None else color) |
|
|
o3d.io.write_point_cloud(file_path, pcd) |
|
|
if logger is not None: |
|
|
logger.info(f"Save Point Cloud to: {file_path}") |
|
|
|
|
|
|
|
|
def vis_pcd_feat(coord, point_feat, save_path): |
|
|
class TorchPCA(object): |
|
|
|
|
|
def __init__(self, n_components): |
|
|
self.n_components = n_components |
|
|
|
|
|
def fit(self, X): |
|
|
self.mean_ = X.mean(dim=0) |
|
|
unbiased = X - self.mean_.unsqueeze(0) |
|
|
U, S, V = torch.pca_lowrank(unbiased, q=self.n_components, center=False, niter=4) |
|
|
self.components_ = V.T |
|
|
self.singular_values_ = S |
|
|
return self |
|
|
|
|
|
def transform(self, X): |
|
|
t0 = X - self.mean_.unsqueeze(0) |
|
|
projected = t0 @ self.components_.T |
|
|
return projected |
|
|
|
|
|
fit_pca = TorchPCA(n_components=3).fit(point_feat) |
|
|
x_red = fit_pca.transform(point_feat) |
|
|
if isinstance(x_red, np.ndarray): |
|
|
x_red = torch.from_numpy(x_red) |
|
|
x_red -= x_red.min(dim=0, keepdim=True).values |
|
|
x_red /= x_red.max(dim=0, keepdim=True).values |
|
|
|
|
|
save_point_cloud(coord.detach().cpu(), x_red.detach().cpu(), save_path) |