| """This script is the differentiable renderer for Deep3DFaceRecon_pytorch
|
| Attention, antialiasing step is missing in current version.
|
| """
|
| import pytorch3d.ops
|
| import torch
|
| import torch.nn.functional as F
|
| import kornia
|
| from kornia.geometry.camera import pixel2cam
|
| import numpy as np
|
| from typing import List
|
| from scipy.io import loadmat
|
| from torch import nn
|
|
|
| from pytorch3d.structures import Meshes
|
| from pytorch3d.renderer import (
|
| look_at_view_transform,
|
| FoVPerspectiveCameras,
|
| DirectionalLights,
|
| RasterizationSettings,
|
| MeshRenderer,
|
| MeshRasterizer,
|
| SoftPhongShader,
|
| TexturesUV,
|
| )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| class MeshRenderer(nn.Module):
|
| def __init__(self,
|
| rasterize_fov,
|
| znear=0.1,
|
| zfar=10,
|
| rasterize_size=224):
|
| super(MeshRenderer, self).__init__()
|
|
|
|
|
|
|
|
|
| self.rasterize_size = rasterize_size
|
| self.fov = rasterize_fov
|
| self.znear = znear
|
| self.zfar = zfar
|
|
|
| self.rasterizer = None
|
|
|
| def forward(self, vertex, tri, feat=None):
|
| """
|
| Return:
|
| mask -- torch.tensor, size (B, 1, H, W)
|
| depth -- torch.tensor, size (B, 1, H, W)
|
| features(optional) -- torch.tensor, size (B, C, H, W) if feat is not None
|
|
|
| Parameters:
|
| vertex -- torch.tensor, size (B, N, 3)
|
| tri -- torch.tensor, size (B, M, 3) or (M, 3), triangles
|
| feat(optional) -- torch.tensor, size (B, N ,C), features
|
| """
|
| device = vertex.device
|
| rsize = int(self.rasterize_size)
|
|
|
|
|
| if vertex.shape[-1] == 3:
|
| vertex = torch.cat([vertex, torch.ones([*vertex.shape[:2], 1]).to(device)], dim=-1)
|
| vertex[..., 0] = -vertex[..., 0]
|
|
|
|
|
|
|
| if self.rasterizer is None:
|
| self.rasterizer = MeshRasterizer()
|
| print("create rasterizer on device cuda:%d"%device.index)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| tri = tri.type(torch.int32).contiguous()
|
|
|
|
|
| cameras = FoVPerspectiveCameras(
|
| device=device,
|
| fov=self.fov,
|
| znear=self.znear,
|
| zfar=self.zfar,
|
| )
|
|
|
| raster_settings = RasterizationSettings(
|
| image_size=rsize
|
| )
|
|
|
|
|
| mesh = Meshes(vertex.contiguous()[...,:3], tri.unsqueeze(0).repeat((vertex.shape[0],1,1)))
|
|
|
| fragments = self.rasterizer(mesh, cameras = cameras, raster_settings = raster_settings)
|
| rast_out = fragments.pix_to_face.squeeze(-1)
|
| depth = fragments.zbuf
|
|
|
|
|
| depth = depth.permute(0, 3, 1, 2)
|
| mask = (rast_out > 0).float().unsqueeze(1)
|
| depth = mask * depth
|
|
|
|
|
| image = None
|
| if feat is not None:
|
| attributes = feat.reshape(-1,3)[mesh.faces_packed()]
|
| image = pytorch3d.ops.interpolate_face_attributes(fragments.pix_to_face,
|
| fragments.bary_coords,
|
| attributes)
|
|
|
| image = image.squeeze(-2).permute(0, 3, 1, 2)
|
| image = mask * image
|
|
|
| return mask, depth, image
|
|
|
|
|