|
import imp |
|
import os |
|
from pickle import NONE |
|
|
|
import torch |
|
import trimesh |
|
import numpy as np |
|
|
|
from skimage.transform import resize |
|
from torchvision.utils import make_grid |
|
import torch.nn.functional as F |
|
|
|
from models.smpl import get_smpl_faces, get_model_faces, get_model_tpose |
|
from utils.densepose_methods import DensePoseMethods |
|
from core import constants, path_config |
|
import json |
|
from .geometry import convert_to_full_img_cam |
|
from utils.imutils import crop |
|
|
|
try: |
|
import math |
|
import pyrender |
|
from pyrender.constants import RenderFlags |
|
except: |
|
pass |
|
try: |
|
from opendr.renderer import ColoredRenderer |
|
from opendr.lighting import LambertianPointLight, SphericalHarmonics |
|
from opendr.camera import ProjectPoints |
|
except: |
|
pass |
|
|
|
from pytorch3d.structures.meshes import Meshes |
|
|
|
|
|
from pytorch3d.renderer import ( |
|
look_at_view_transform, FoVPerspectiveCameras, PerspectiveCameras, AmbientLights, PointLights, |
|
RasterizationSettings, BlendParams, MeshRenderer, MeshRasterizer, SoftPhongShader, |
|
SoftSilhouetteShader, HardPhongShader, HardGouraudShader, HardFlatShader, TexturesVertex |
|
) |
|
|
|
import logging |
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
class WeakPerspectiveCamera(pyrender.Camera): |
|
def __init__( |
|
self, scale, translation, znear=pyrender.camera.DEFAULT_Z_NEAR, zfar=None, name=None |
|
): |
|
super(WeakPerspectiveCamera, self).__init__( |
|
znear=znear, |
|
zfar=zfar, |
|
name=name, |
|
) |
|
self.scale = scale |
|
self.translation = translation |
|
|
|
def get_projection_matrix(self, width=None, height=None): |
|
P = np.eye(4) |
|
P[0, 0] = self.scale[0] |
|
P[1, 1] = self.scale[1] |
|
P[0, 3] = self.translation[0] * self.scale[0] |
|
P[1, 3] = -self.translation[1] * self.scale[1] |
|
P[2, 2] = -1 |
|
return P |
|
|
|
|
|
class PyRenderer: |
|
def __init__( |
|
self, resolution=(224, 224), orig_img=False, wireframe=False, scale_ratio=1., vis_ratio=1. |
|
): |
|
self.resolution = (resolution[0] * scale_ratio, resolution[1] * scale_ratio) |
|
|
|
|
|
self.faces = { |
|
'smplx': get_model_faces('smplx'), |
|
'smpl': get_model_faces('smpl'), |
|
|
|
|
|
} |
|
self.orig_img = orig_img |
|
self.wireframe = wireframe |
|
self.renderer = pyrender.OffscreenRenderer( |
|
viewport_width=self.resolution[0], viewport_height=self.resolution[1], point_size=1.0 |
|
) |
|
|
|
self.vis_ratio = vis_ratio |
|
|
|
|
|
self.scene = pyrender.Scene(bg_color=[0.0, 0.0, 0.0, 0.0], ambient_light=(0.3, 0.3, 0.3)) |
|
|
|
light = pyrender.PointLight(color=np.array([1.0, 1.0, 1.0]) * 0.2, intensity=1) |
|
|
|
yrot = np.radians(120) |
|
|
|
light_pose = np.eye(4) |
|
light_pose[:3, 3] = [0, -1, 1] |
|
self.scene.add(light, pose=light_pose) |
|
|
|
light_pose[:3, 3] = [0, 1, 1] |
|
self.scene.add(light, pose=light_pose) |
|
|
|
light_pose[:3, 3] = [1, 1, 2] |
|
self.scene.add(light, pose=light_pose) |
|
|
|
spot_l = pyrender.SpotLight( |
|
color=np.ones(3), intensity=15.0, innerConeAngle=np.pi / 3, outerConeAngle=np.pi / 2 |
|
) |
|
|
|
light_pose[:3, 3] = [1, 2, 2] |
|
self.scene.add(spot_l, pose=light_pose) |
|
|
|
light_pose[:3, 3] = [-1, 2, 2] |
|
self.scene.add(spot_l, pose=light_pose) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.colors_dict = { |
|
'red': np.array([0.5, 0.2, 0.2]), |
|
'pink': np.array([0.7, 0.5, 0.5]), |
|
'neutral': np.array([0.7, 0.7, 0.6]), |
|
|
|
'purple': np.array([0.55, 0.4, 0.9]), |
|
'green': np.array([0.5, 0.55, 0.3]), |
|
'sky': np.array([0.3, 0.5, 0.55]), |
|
'white': np.array([1.0, 0.98, 0.94]), |
|
} |
|
|
|
def __call__( |
|
self, |
|
verts, |
|
faces=None, |
|
img=np.zeros((224, 224, 3)), |
|
cam=np.array([1, 0, 0]), |
|
focal_length=[5000, 5000], |
|
camera_rotation=np.eye(3), |
|
crop_info=None, |
|
angle=None, |
|
axis=None, |
|
mesh_filename=None, |
|
color_type=None, |
|
color=[1.0, 1.0, 0.9], |
|
iwp_mode=True, |
|
crop_img=True, |
|
mesh_type='smpl', |
|
scale_ratio=1., |
|
rgba_mode=False |
|
): |
|
|
|
if faces is None: |
|
faces = self.faces[mesh_type] |
|
mesh = trimesh.Trimesh(vertices=verts, faces=faces, process=False) |
|
|
|
Rx = trimesh.transformations.rotation_matrix(math.radians(180), [1, 0, 0]) |
|
mesh.apply_transform(Rx) |
|
|
|
if mesh_filename is not None: |
|
mesh.export(mesh_filename) |
|
|
|
if angle and axis: |
|
R = trimesh.transformations.rotation_matrix(math.radians(angle), axis) |
|
mesh.apply_transform(R) |
|
|
|
cam = cam.copy() |
|
if iwp_mode: |
|
resolution = np.array(img.shape[:2]) * scale_ratio |
|
if len(cam) == 4: |
|
sx, sy, tx, ty = cam |
|
|
|
camera_translation = np.array( |
|
[tx, ty, 2 * focal_length[0] / (resolution[0] * sy + 1e-9)] |
|
) |
|
elif len(cam) == 3: |
|
sx, tx, ty = cam |
|
sy = sx |
|
camera_translation = np.array( |
|
[-tx, ty, 2 * focal_length[0] / (resolution[0] * sy + 1e-9)] |
|
) |
|
render_res = resolution |
|
self.renderer.viewport_width = render_res[1] |
|
self.renderer.viewport_height = render_res[0] |
|
else: |
|
if crop_info['opt_cam_t'] is None: |
|
camera_translation = convert_to_full_img_cam( |
|
pare_cam=cam[None], |
|
bbox_height=crop_info['bbox_scale'] * 200., |
|
bbox_center=crop_info['bbox_center'], |
|
img_w=crop_info['img_w'], |
|
img_h=crop_info['img_h'], |
|
focal_length=focal_length[0], |
|
) |
|
else: |
|
camera_translation = crop_info['opt_cam_t'] |
|
if torch.is_tensor(camera_translation): |
|
camera_translation = camera_translation[0].cpu().numpy() |
|
camera_translation = camera_translation.copy() |
|
camera_translation[0] *= -1 |
|
if 'img_h' in crop_info and 'img_w' in crop_info: |
|
render_res = (int(crop_info['img_h'][0]), int(crop_info['img_w'][0])) |
|
else: |
|
render_res = img.shape[:2] if type(img) is not list else img[0].shape[:2] |
|
self.renderer.viewport_width = render_res[1] |
|
self.renderer.viewport_height = render_res[0] |
|
camera_rotation = camera_rotation.T |
|
camera = pyrender.IntrinsicsCamera( |
|
fx=focal_length[0], fy=focal_length[1], cx=render_res[1] / 2., cy=render_res[0] / 2. |
|
) |
|
|
|
if color_type != None: |
|
color = self.colors_dict[color_type] |
|
|
|
material = pyrender.MetallicRoughnessMaterial( |
|
metallicFactor=0.2, |
|
roughnessFactor=0.6, |
|
alphaMode='OPAQUE', |
|
baseColorFactor=(color[0], color[1], color[2], 1.0) |
|
) |
|
|
|
mesh = pyrender.Mesh.from_trimesh(mesh, material=material) |
|
|
|
mesh_node = self.scene.add(mesh, 'mesh') |
|
|
|
camera_pose = np.eye(4) |
|
camera_pose[:3, :3] = camera_rotation |
|
camera_pose[:3, 3] = camera_rotation @ camera_translation |
|
cam_node = self.scene.add(camera, pose=camera_pose) |
|
|
|
if self.wireframe: |
|
render_flags = RenderFlags.RGBA | RenderFlags.ALL_WIREFRAME | RenderFlags.SHADOWS_SPOT |
|
else: |
|
render_flags = RenderFlags.RGBA | RenderFlags.SHADOWS_SPOT |
|
|
|
rgb, _ = self.renderer.render(self.scene, flags=render_flags) |
|
if crop_info is not None and crop_img: |
|
crop_res = img.shape[:2] |
|
rgb, _, _ = crop(rgb, crop_info['bbox_center'][0], crop_info['bbox_scale'][0], crop_res) |
|
|
|
valid_mask = (rgb[:, :, -1] > 0)[:, :, np.newaxis] |
|
|
|
image_list = [img] if type(img) is not list else img |
|
|
|
return_img = [] |
|
for item in image_list: |
|
if scale_ratio != 1: |
|
orig_size = item.shape[:2] |
|
item = resize( |
|
item, (orig_size[0] * scale_ratio, orig_size[1] * scale_ratio), |
|
anti_aliasing=True |
|
) |
|
item = (item * 255).astype(np.uint8) |
|
output_img = rgb[:, :, :-1] * valid_mask * self.vis_ratio + ( |
|
1 - valid_mask * self.vis_ratio |
|
) * item |
|
|
|
|
|
|
|
if rgba_mode: |
|
output_img_rgba = np.zeros((output_img.shape[0], output_img.shape[1], 4)) |
|
output_img_rgba[:, :, :3] = output_img |
|
output_img_rgba[:, :, 3][valid_mask[:, :, 0]] = 255 |
|
output_img = output_img_rgba.astype(np.uint8) |
|
image = output_img.astype(np.uint8) |
|
return_img.append(image) |
|
return_img.append(item) |
|
|
|
if type(img) is not list: |
|
|
|
return_img = return_img[0] |
|
|
|
self.scene.remove_node(mesh_node) |
|
self.scene.remove_node(cam_node) |
|
|
|
return return_img |
|
|
|
|
|
class OpenDRenderer: |
|
def __init__(self, resolution=(224, 224), ratio=1): |
|
self.resolution = (resolution[0] * ratio, resolution[1] * ratio) |
|
self.ratio = ratio |
|
self.focal_length = 5000. |
|
self.K = np.array( |
|
[ |
|
[self.focal_length, 0., self.resolution[1] / 2.], |
|
[0., self.focal_length, self.resolution[0] / 2.], [0., 0., 1.] |
|
] |
|
) |
|
self.colors_dict = { |
|
'red': np.array([0.5, 0.2, 0.2]), |
|
'pink': np.array([0.7, 0.5, 0.5]), |
|
'neutral': np.array([0.7, 0.7, 0.6]), |
|
'purple': np.array([0.5, 0.5, 0.7]), |
|
'green': np.array([0.5, 0.55, 0.3]), |
|
'sky': np.array([0.3, 0.5, 0.55]), |
|
'white': np.array([1.0, 0.98, 0.94]), |
|
} |
|
self.renderer = ColoredRenderer() |
|
self.faces = get_smpl_faces() |
|
|
|
def reset_res(self, resolution): |
|
self.resolution = (resolution[0] * self.ratio, resolution[1] * self.ratio) |
|
self.K = np.array( |
|
[ |
|
[self.focal_length, 0., self.resolution[1] / 2.], |
|
[0., self.focal_length, self.resolution[0] / 2.], [0., 0., 1.] |
|
] |
|
) |
|
|
|
def __call__( |
|
self, |
|
verts, |
|
faces=None, |
|
color=None, |
|
color_type='white', |
|
R=None, |
|
mesh_filename=None, |
|
img=np.zeros((224, 224, 3)), |
|
cam=np.array([1, 0, 0]), |
|
rgba=False, |
|
addlight=True |
|
): |
|
'''Render mesh using OpenDR |
|
verts: shape - (V, 3) |
|
faces: shape - (F, 3) |
|
img: shape - (224, 224, 3), range - [0, 255] (np.uint8) |
|
axis: rotate along with X/Y/Z axis (by angle) |
|
R: rotation matrix (used to manipulate verts) shape - [3, 3] |
|
Return: |
|
rendered img: shape - (224, 224, 3), range - [0, 255] (np.uint8) |
|
''' |
|
|
|
rn = self.renderer |
|
h, w = self.resolution |
|
K = self.K |
|
|
|
f = np.array([K[0, 0], K[1, 1]]) |
|
c = np.array([K[0, 2], K[1, 2]]) |
|
|
|
if faces is None: |
|
faces = self.faces |
|
if len(cam) == 4: |
|
t = np.array([cam[2], cam[3], 2 * K[0, 0] / (w * cam[0] + 1e-9)]) |
|
elif len(cam) == 3: |
|
t = np.array([cam[1], cam[2], 2 * K[0, 0] / (w * cam[0] + 1e-9)]) |
|
|
|
rn.camera = ProjectPoints(rt=np.array([0, 0, 0]), t=t, f=f, c=c, k=np.zeros(5)) |
|
rn.frustum = {'near': 1., 'far': 1000., 'width': w, 'height': h} |
|
|
|
albedo = np.ones_like(verts) * .9 |
|
|
|
if color is not None: |
|
color0 = np.array(color) |
|
color1 = np.array(color) |
|
color2 = np.array(color) |
|
elif color_type == 'white': |
|
color0 = np.array([1., 1., 1.]) |
|
color1 = np.array([1., 1., 1.]) |
|
color2 = np.array([0.7, 0.7, 0.7]) |
|
color = np.ones_like(verts) * self.colors_dict[color_type][None, :] |
|
else: |
|
color0 = self.colors_dict[color_type] * 1.2 |
|
color1 = self.colors_dict[color_type] * 1.2 |
|
color2 = self.colors_dict[color_type] * 1.2 |
|
color = np.ones_like(verts) * self.colors_dict[color_type][None, :] |
|
|
|
|
|
if R is not None: |
|
assert R.shape == (3, 3), "Shape of rotation matrix should be (3, 3)" |
|
verts = np.dot(verts, R) |
|
|
|
rn.set(v=verts, f=faces, vc=color, bgcolor=np.zeros(3)) |
|
|
|
if addlight: |
|
yrot = np.radians(120) |
|
|
|
rn.vc = LambertianPointLight( |
|
f=rn.f, |
|
v=rn.v, |
|
num_verts=len(rn.v), |
|
light_pos=rotateY(np.array([-200, -100, -100]), yrot), |
|
vc=albedo, |
|
light_color=color0 |
|
) |
|
|
|
|
|
rn.vc += LambertianPointLight( |
|
f=rn.f, |
|
v=rn.v, |
|
num_verts=len(rn.v), |
|
light_pos=rotateY(np.array([800, 10, 300]), yrot), |
|
vc=albedo, |
|
light_color=color1 |
|
) |
|
|
|
|
|
rn.vc += LambertianPointLight( |
|
f=rn.f, |
|
v=rn.v, |
|
num_verts=len(rn.v), |
|
light_pos=rotateY(np.array([-500, 500, 1000]), yrot), |
|
vc=albedo, |
|
light_color=color2 |
|
) |
|
|
|
rendered_image = rn.r |
|
visibility_image = rn.visibility_image |
|
|
|
image_list = [img] if type(img) is not list else img |
|
|
|
return_img = [] |
|
for item in image_list: |
|
if self.ratio != 1: |
|
img_resized = resize( |
|
item, (item.shape[0] * self.ratio, item.shape[1] * self.ratio), |
|
anti_aliasing=True |
|
) |
|
else: |
|
img_resized = item / 255. |
|
|
|
try: |
|
img_resized[visibility_image != (2**32 - 1) |
|
] = rendered_image[visibility_image != (2**32 - 1)] |
|
except: |
|
logger.warning('Can not render mesh.') |
|
|
|
img_resized = (img_resized * 255).astype(np.uint8) |
|
res = img_resized |
|
|
|
if rgba: |
|
img_resized_rgba = np.zeros((img_resized.shape[0], img_resized.shape[1], 4)) |
|
img_resized_rgba[:, :, :3] = img_resized |
|
img_resized_rgba[:, :, 3][visibility_image != (2**32 - 1)] = 255 |
|
res = img_resized_rgba.astype(np.uint8) |
|
return_img.append(res) |
|
|
|
if type(img) is not list: |
|
return_img = return_img[0] |
|
|
|
return return_img |
|
|
|
|
|
|
|
def rotateY(points, angle): |
|
"""Rotate all points in a 2D array around the y axis.""" |
|
ry = np.array( |
|
[[np.cos(angle), 0., np.sin(angle)], [0., 1., 0.], [-np.sin(angle), 0., |
|
np.cos(angle)]] |
|
) |
|
return np.dot(points, ry) |
|
|
|
|
|
def rotateX(points, angle): |
|
"""Rotate all points in a 2D array around the x axis.""" |
|
rx = np.array( |
|
[[1., 0., 0.], [0., np.cos(angle), -np.sin(angle)], [0., np.sin(angle), |
|
np.cos(angle)]] |
|
) |
|
return np.dot(points, rx) |
|
|
|
|
|
def rotateZ(points, angle): |
|
"""Rotate all points in a 2D array around the z axis.""" |
|
rz = np.array( |
|
[[np.cos(angle), -np.sin(angle), 0.], [np.sin(angle), np.cos(angle), 0.], [0., 0., 1.]] |
|
) |
|
return np.dot(points, rz) |
|
|
|
|
|
class IUV_Renderer(object): |
|
def __init__( |
|
self, |
|
focal_length=5000., |
|
orig_size=224, |
|
output_size=56, |
|
mode='iuv', |
|
device=torch.device('cuda'), |
|
mesh_type='smpl' |
|
): |
|
|
|
self.focal_length = focal_length |
|
self.orig_size = orig_size |
|
self.output_size = output_size |
|
|
|
if mode in ['iuv']: |
|
if mesh_type == 'smpl': |
|
DP = DensePoseMethods() |
|
|
|
vert_mapping = DP.All_vertices.astype('int64') - 1 |
|
self.vert_mapping = torch.from_numpy(vert_mapping) |
|
|
|
faces = DP.FacesDensePose |
|
faces = faces[None, :, :] |
|
self.faces = torch.from_numpy( |
|
faces.astype(np.int32) |
|
) |
|
|
|
num_part = float(np.max(DP.FaceIndices)) |
|
self.num_part = num_part |
|
|
|
dp_vert_pid_fname = 'data/dp_vert_pid.npy' |
|
if os.path.exists(dp_vert_pid_fname): |
|
dp_vert_pid = list(np.load(dp_vert_pid_fname)) |
|
else: |
|
print('creating data/dp_vert_pid.npy') |
|
dp_vert_pid = [] |
|
for v in range(len(vert_mapping)): |
|
for i, f in enumerate(DP.FacesDensePose): |
|
if v in f: |
|
dp_vert_pid.append(DP.FaceIndices[i]) |
|
break |
|
np.save(dp_vert_pid_fname, np.array(dp_vert_pid)) |
|
|
|
textures_vts = np.array( |
|
[ |
|
(dp_vert_pid[i] / num_part, DP.U_norm[i], DP.V_norm[i]) |
|
for i in range(len(vert_mapping)) |
|
] |
|
) |
|
self.textures_vts = torch.from_numpy( |
|
textures_vts[None].astype(np.float32) |
|
) |
|
elif mode == 'pncc': |
|
self.vert_mapping = None |
|
self.faces = torch.from_numpy( |
|
get_model_faces(mesh_type)[None].astype(np.int32) |
|
) |
|
textures_vts = get_model_tpose(mesh_type).unsqueeze( |
|
0 |
|
) |
|
|
|
texture_min = torch.min(textures_vts) - 0.001 |
|
texture_range = torch.max(textures_vts) - texture_min + 0.001 |
|
self.textures_vts = (textures_vts - texture_min) / texture_range |
|
elif mode in ['seg']: |
|
self.vert_mapping = None |
|
body_model = 'smpl' |
|
|
|
self.faces = torch.from_numpy(get_smpl_faces().astype(np.int32)[None]) |
|
|
|
with open( |
|
os.path.join( |
|
path_config.SMPL_MODEL_DIR, '{}_vert_segmentation.json'.format(body_model) |
|
), 'rb' |
|
) as json_file: |
|
smpl_part_id = json.load(json_file) |
|
|
|
v_id = [] |
|
for k in smpl_part_id.keys(): |
|
v_id.extend(smpl_part_id[k]) |
|
|
|
v_id = torch.tensor(v_id) |
|
n_verts = len(torch.unique(v_id)) |
|
num_part = len(constants.SMPL_PART_ID.keys()) |
|
self.num_part = num_part |
|
|
|
seg_vert_pid = np.zeros(n_verts) |
|
for k in smpl_part_id.keys(): |
|
seg_vert_pid[smpl_part_id[k]] = constants.SMPL_PART_ID[k] |
|
|
|
print('seg_vert_pid', seg_vert_pid.shape) |
|
textures_vts = seg_vert_pid[:, None].repeat(3, axis=1) / num_part |
|
print('textures_vts', textures_vts.shape) |
|
|
|
|
|
|
|
self.textures_vts = torch.from_numpy(textures_vts[None].astype(np.float32)) |
|
|
|
K = np.array( |
|
[ |
|
[self.focal_length, 0., self.orig_size / 2.], |
|
[0., self.focal_length, self.orig_size / 2.], [0., 0., 1.] |
|
] |
|
) |
|
|
|
R = np.array([[-1., 0., 0.], [0., -1., 0.], [0., 0., 1.]]) |
|
|
|
t = np.array([0, 0, 5]) |
|
|
|
if self.orig_size != 224: |
|
rander_scale = self.orig_size / float(224) |
|
K[0, 0] *= rander_scale |
|
K[1, 1] *= rander_scale |
|
K[0, 2] *= rander_scale |
|
K[1, 2] *= rander_scale |
|
|
|
self.K = torch.FloatTensor(K[None, :, :]) |
|
self.R = torch.FloatTensor(R[None, :, :]) |
|
self.t = torch.FloatTensor(t[None, None, :]) |
|
|
|
camK = F.pad(self.K, (0, 1, 0, 1), "constant", 0) |
|
camK[:, 2, 2] = 0 |
|
camK[:, 3, 2] = 1 |
|
camK[:, 2, 3] = 1 |
|
|
|
self.K = camK |
|
|
|
self.device = device |
|
lights = AmbientLights(device=self.device) |
|
|
|
raster_settings = RasterizationSettings( |
|
image_size=output_size, |
|
blur_radius=0, |
|
faces_per_pixel=1, |
|
) |
|
self.renderer = MeshRenderer( |
|
rasterizer=MeshRasterizer(raster_settings=raster_settings), |
|
shader=HardFlatShader( |
|
device=self.device, |
|
lights=lights, |
|
blend_params=BlendParams(background_color=[0, 0, 0], sigma=0.0, gamma=0.0) |
|
) |
|
) |
|
|
|
def camera_matrix(self, cam): |
|
batch_size = cam.size(0) |
|
|
|
K = self.K.repeat(batch_size, 1, 1) |
|
R = self.R.repeat(batch_size, 1, 1) |
|
t = torch.stack( |
|
[-cam[:, 1], -cam[:, 2], 2 * self.focal_length / (self.orig_size * cam[:, 0] + 1e-9)], |
|
dim=-1 |
|
) |
|
|
|
if cam.is_cuda: |
|
|
|
K = K.to(cam.device) |
|
R = R.to(cam.device) |
|
t = t.to(cam.device) |
|
|
|
return K, R, t |
|
|
|
def verts2iuvimg(self, verts, cam, iwp_mode=True): |
|
batch_size = verts.size(0) |
|
|
|
K, R, t = self.camera_matrix(cam) |
|
|
|
if self.vert_mapping is None: |
|
vertices = verts |
|
else: |
|
vertices = verts[:, self.vert_mapping, :] |
|
|
|
mesh = Meshes(vertices, self.faces.to(verts.device).expand(batch_size, -1, -1)) |
|
mesh.textures = TexturesVertex( |
|
verts_features=self.textures_vts.to(verts.device).expand(batch_size, -1, -1) |
|
) |
|
|
|
cameras = PerspectiveCameras( |
|
device=verts.device, |
|
R=R, |
|
T=t, |
|
K=K, |
|
in_ndc=False, |
|
image_size=[(self.orig_size, self.orig_size)] |
|
) |
|
|
|
iuv_image = self.renderer(mesh, cameras=cameras) |
|
iuv_image = iuv_image[..., :3].permute(0, 3, 1, 2) |
|
|
|
return iuv_image |
|
|