jkorstad's picture
Correctly add UniRig source files
f499d3b
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:
# len(mesh.faces) float, array of the areas
# of each face of the mesh
face_weight = mesh.area_faces
# cumulative sum of weights (len(mesh.faces))
weight_cum = np.cumsum(face_weight)
# seed the random number generator as requested
random = np.random.default_rng(seed).random
# last value of cumulative sum is total summed weight/area
face_pick = random(count) * weight_cum[-1]
# get the index of the selected faces
face_index = np.searchsorted(weight_cum, face_pick)
# pull triangles into the form of an origin + 2 vectors
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))
# pull the vectors for the faces we are going to sample from
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]
# randomly generate two 0-1 scalar components to multiply edge vectors b
random_lengths = random((len(tri_vectors), 2, 1))
# points will be distributed on a quadrilateral if we use 2 0-1 samples
# if the two scalar components sum less than 1.0 the point will be
# inside the triangle, so we find vectors longer than 1.0 and
# transform them to be inside the triangle
random_test = random_lengths.sum(axis=1).reshape(-1) > 1.0
random_lengths[random_test] -= 1.0
random_lengths = np.abs(random_lengths)
# multiply triangle edge vectors by the random lengths and sum
sample_vector = (tri_vectors * random_lengths).sum(axis=1)
# finally, offset by the origin to generate
# (n,3) points in space on the triangle
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)
) # scaled focal length
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)