AiOS / detrsmpl /utils /mesh_utils.py
ttxskk
update
d7e58f0
import warnings
from typing import List, Optional, Union
import torch
from pytorch3d.io import IO
from pytorch3d.io import load_objs_as_meshes as _load_objs_as_meshes
from pytorch3d.io import save_obj
from pytorch3d.renderer import TexturesUV, TexturesVertex
from pytorch3d.structures import (
Meshes,
Pointclouds,
join_meshes_as_batch,
join_meshes_as_scene,
padded_to_list,
)
from .path_utils import prepare_output_path
def join_batch_meshes_as_scene(
meshes: List[Meshes],
include_textures: bool = True,
) -> Meshes:
"""Join `meshes` as a scene each batch. Only for Pytorch3D `meshes`. The
Meshes must share the same batch size, and topology could be different.
They must all be on the same device. If `include_textures` is true, the
textures should be the same type, all be None is not accepted. If
`include_textures` is False, textures are ignored. The return meshes will
have no textures.
Args:
meshes (List[Meshes]): A `list` of `Meshes` with the same batches.
Required.
include_textures: (bool) whether to try to join the textures.
Returns:
New Meshes which has join different Meshes by each batch.
"""
for mesh in meshes:
mesh._verts_list = padded_to_list(mesh.verts_padded(),
mesh.num_verts_per_mesh().tolist())
num_scene_size = len(meshes)
num_batch_size = len(meshes[0])
for i in range(num_scene_size):
assert len(
meshes[i]
) == num_batch_size, 'Please make sure that the Meshes all have'
'the same batch size.'
meshes_all = []
for j in range(num_batch_size):
meshes_batch = []
for i in range(num_scene_size):
meshes_batch.append(meshes[i][j])
meshes_all.append(join_meshes_as_scene(meshes_batch, include_textures))
meshes_final = join_meshes_as_batch(meshes_all, include_textures)
return meshes_final
def mesh_to_pointcloud_vc(
meshes: Meshes,
include_textures: bool = True,
alpha: float = 1.0,
) -> Pointclouds:
"""Convert PyTorch3D vertex color `Meshes` to `PointClouds`.
Args:
meshes (Meshes): input meshes.
include_textures (bool, optional): Whether include colors.
Require the texture of input meshes is vertex color.
Defaults to True.
alpha (float, optional): transparency.
Defaults to 1.0.
Returns:
Pointclouds: output pointclouds.
"""
assert isinstance(
meshes.textures,
TexturesVertex), 'textures of input meshes should be `TexturesVertex`'
vertices = meshes.verts_padded()
if include_textures:
verts_rgb = meshes.textures.verts_features_padded()
verts_rgba = torch.cat(
[verts_rgb,
torch.ones_like(verts_rgb)[..., 0:1] * alpha], dim=-1)
else:
verts_rgba = None
pointclouds = Pointclouds(points=vertices, features=verts_rgba)
return pointclouds
def texture_uv2vc(meshes: Meshes) -> Meshes:
"""Convert a Pytorch3D meshes's textures from TexturesUV to TexturesVertex.
Args:
meshes (Meshes): input Meshes.
Returns:
Meshes: converted Meshes.
"""
assert isinstance(meshes.textures, TexturesUV)
device = meshes.device
vert_uv = meshes.textures.verts_uvs_padded()
batch_size = vert_uv.shape[0]
verts_features = []
num_verts = meshes.verts_padded().shape[1]
for index in range(batch_size):
face_uv = vert_uv[index][meshes.textures.faces_uvs_padded()
[index].view(-1)]
img = meshes.textures._maps_padded[index]
width, height, _ = img.shape
face_uv = face_uv * torch.Tensor([width - 1, height - 1
]).long().to(device)
face_uv[:, 0] = torch.clip(face_uv[:, 0], 0, width - 1)
face_uv[:, 1] = torch.clip(face_uv[:, 1], 0, height - 1)
face_uv = face_uv.long()
faces = meshes.faces_padded()
verts_rgb = torch.zeros(1, num_verts, 3).to(device)
verts_rgb[:, faces.view(-1)] = img[height - 1 - face_uv[:, 1],
face_uv[:, 0]]
verts_features.append(verts_rgb)
verts_features = torch.cat(verts_features)
meshes = meshes.clone()
meshes.textures = TexturesVertex(verts_features)
return meshes
def load_objs_as_meshes(files: List[str],
device: Optional[Union[torch.device, str]] = None,
load_textures: bool = True,
**kwargs) -> Meshes:
if not isinstance(files, list):
files = [files]
return _load_objs_as_meshes(files=files,
device=device,
load_textures=load_textures,
**kwargs)
def load_plys_as_meshes(
files: List[str],
device: Optional[Union[torch.device, str]] = None,
load_textures: bool = True,
) -> Meshes:
writer = IO()
meshes = []
if not isinstance(files, list):
files = [files]
for idx in range(len(files)):
assert files[idx].endswith('.ply'), 'Please input .ply files.'
mesh = writer.load_mesh(path=files[idx],
include_textures=load_textures,
device=device)
meshes.append(mesh)
meshes = join_meshes_as_batch(meshes, include_textures=load_textures)
return meshes
def save_meshes_as_plys(files: List[str],
meshes: Meshes = None,
verts: torch.Tensor = None,
faces: torch.Tensor = None,
verts_rgb: torch.Tensor = None) -> None:
"""Save meshes as .ply files. Mainly for vertex color meshes.
Args:
files (List[str]): Output .ply file list.
meshes (Meshes, optional): higher priority than
(verts & faces & verts_rgb). Defaults to None.
verts (torch.Tensor, optional): lower priority than meshes.
Defaults to None.
faces (torch.Tensor, optional): lower priority than meshes.
Defaults to None.
verts_rgb (torch.Tensor, optional): lower priority than meshes.
Defaults to None.
"""
if meshes is None:
assert verts is not None and faces is not None, 'Not mesh input.'
meshes = Meshes(
verts=verts,
faces=faces,
textures=TexturesVertex(
verts_features=verts_rgb) if verts_rgb is not None else None)
else:
if verts is not None or faces is not None or verts_rgb is not None:
warnings.warn('Redundant input, will use meshes only.')
assert files is not None
if not isinstance(files, list):
files = [files]
assert len(files) >= len(meshes), 'Not enough output files.'
writer = IO()
for idx in range(len(meshes)):
assert files[idx].endswith('.ply'), 'Please save as .ply files.'
writer.save_mesh(meshes[idx],
files[idx],
colors_as_uint8=True,
binary=False)
def save_meshes_as_objs(files: List[str], meshes: Meshes = None) -> None:
"""Save meshes as .obj files. Pytorch3D will not save vertex color for.
.obj, please use `save_meshes_as_plys`.
Args:
files (List[str]): Output .obj file list.
meshes (Meshes, optional):
Defaults to None.
"""
if not isinstance(files, list):
files = [files]
assert len(files) >= len(meshes), 'Not enough output files.'
for idx in range(len(meshes)):
prepare_output_path(files[idx],
allowed_suffix=['.obj'],
path_type='file'), 'Please save as .obj files.'
if isinstance(meshes.textures, TexturesUV):
verts_uvs = meshes.textures.verts_uvs_padded()[idx]
faces_uvs = meshes.textures.faces_uvs_padded()[idx]
texture_map = meshes.textures.maps_padded()[idx]
else:
verts_uvs = None
faces_uvs = None
texture_map = None
save_obj(f=files[idx],
verts=meshes.verts_padded()[idx],
faces=meshes.faces_padded()[idx],
verts_uvs=verts_uvs,
faces_uvs=faces_uvs,
texture_map=texture_map)