Spaces:
Sleeping
Sleeping
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) | |