UniRig / src /model /pointcept /datasets /sampart3d_util.py
MohamedRashad's picture
Add skin and tokenizer systems with parsing and tokenization functionalities
11b119e
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)