EasyMocap/easymocap/visualize/pyrender_wrapper.py
2022-10-25 20:57:27 +08:00

188 lines
7.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'''
@ Date: 2021-05-13 14:20:13
@ Author: Qing Shuai
@ LastEditors: Qing Shuai
@ LastEditTime: 2022-09-13 12:24:20
@ FilePath: /EasyMocapPublic/easymocap/visualize/pyrender_wrapper.py
'''
import pyrender
import numpy as np
import trimesh
import cv2
from .pyrender_flags import get_flags
from ..mytools.vis_base import get_rgb
def offscree_render(renderer, scene, img, flags):
rend_rgba, rend_depth = renderer.render(scene, flags=flags)
assert rend_depth.max() < 65, 'depth should less than 65.536: {}'.format(rend_depth.max())
rend_depth = (rend_depth * 1000).astype(np.uint16)
if rend_rgba.shape[2] == 3: # fail to generate transparent channel
valid_mask = (rend_depth > 0)[:, :, None]
rend_rgba = np.dstack((rend_rgba, (valid_mask*255).astype(np.uint8)))
rend_rgba = rend_rgba[..., [2, 1, 0, 3]]
if False:
rend_cat = cv2.addWeighted(
cv2.bitwise_and(img, 255 - rend_rgba[:, :, 3:4].repeat(3, 2)), 1,
cv2.bitwise_and(rend_rgba[:, :, :3], rend_rgba[:, :, 3:4].repeat(3, 2)), 1, 0)
else:
rend_cat = img.copy()
rend_cat[rend_rgba[:,:,-1]==255] = rend_rgba[:,:,:3][rend_rgba[:,:,-1]==255]
return rend_rgba, rend_depth, rend_cat
class Renderer:
def __init__(self, bg_color=[1.0, 1.0, 1.0, 0.0], ambient_light=[0.5, 0.5, 0.5], flags={}) -> None:
self.bg_color = bg_color
self.ambient_light = ambient_light
self.renderer = pyrender.OffscreenRenderer(1024, 1024)
self.flags = get_flags(flags)
@staticmethod
def add_light(scene, camera=None):
# Use 3 directional lights
# Create light source
light = pyrender.DirectionalLight(color=[1.0, 1.0, 1.0], intensity=3)
light_forward = np.eye(4)
# here the location of the light is set to be the origin
# and this location doesn't affect the render results
scene.add(light, pose=light_forward)
light_z = np.eye(4)
light_z[:3, :3] = cv2.Rodrigues(np.array([np.pi, 0, 0]))[0]
# if camera is not None:
# light_z[:3, :3] = camera['R'] @ light_z[:3, :3]
scene.add(light, pose=light_z)
def __call__(self, render_data, images, cameras, extra_mesh=[],
ret_image=False, ret_depth=False, ret_color=False, ret_mask=False, ret_all=True):
if isinstance(images, np.ndarray) and isinstance(cameras, dict):
images, cameras = [images], [cameras]
assert isinstance(cameras, list)
rot = trimesh.transformations.rotation_matrix(
np.radians(180), [1, 0, 0])
output_images, output_colors, output_depths = [], [], []
for nv, img in enumerate(images):
cam = cameras[nv]
K, R, T = cam['K'], cam['R'], cam['T']
self.renderer.viewport_height = img.shape[0]
self.renderer.viewport_width = img.shape[1]
scene = pyrender.Scene(bg_color=self.bg_color,
ambient_light=self.ambient_light)
for iextra, _mesh in enumerate(extra_mesh):
mesh = _mesh.copy()
trans_cam = np.eye(4)
trans_cam[:3, :3] = R
trans_cam[:3, 3:] = T
mesh.apply_transform(trans_cam)
mesh.apply_transform(rot)
# mesh.vertices = np.asarray(mesh.vertices) @ R.T + T.T
mesh_ = pyrender.Mesh.from_trimesh(mesh)
scene.add(mesh_, 'extra{}'.format(iextra))
for trackId, data in render_data.items():
vert = data['vertices'].copy()
faces = data['faces']
vert = vert @ R.T + T.T
if 'colors' not in data.keys():
# 如果使用了vid这个键那么可视化的颜色使用vid的颜色
if False:
col = get_rgb(data.get('vid', trackId))
else:
col = get_colors(data.get('vid', trackId))
mesh = trimesh.Trimesh(vert, faces, process=False)
mesh.apply_transform(rot)
material = pyrender.MetallicRoughnessMaterial(
metallicFactor=0.0,
roughnessFactor=0.0,
alphaMode='OPAQUE',
baseColorFactor=col)
# material = pyrender.material.SpecularGlossinessMaterial(
# diffuseFactor=1.0, glossinessFactor=0.0
# )
mesh = pyrender.Mesh.from_trimesh(mesh, material=material, smooth=data.get('smooth', True))
else:
mesh = trimesh.Trimesh(vert, faces, vertex_colors=data['colors'], process=False)
mesh.apply_transform(rot)
mesh = pyrender.Mesh.from_trimesh(mesh, smooth=False)
scene.add(mesh, data['name'])
camera_pose = np.eye(4)
camera = pyrender.camera.IntrinsicsCamera(fx=K[0, 0], fy=K[1, 1], cx=K[0, 2], cy=K[1, 2])
scene.add(camera, pose=camera_pose)
self.add_light(scene, camera=cam)
# pyrender.Viewer(scene, use_raymond_lighting=True)
rend_rgba, rend_depth, rend_cat = offscree_render(self.renderer, scene, img, self.flags)
output_colors.append(rend_rgba)
output_depths.append(rend_depth)
output_images.append(rend_cat)
res = None
if ret_depth:
res = output_depths
elif ret_color:
res = output_colors
elif ret_mask:
res = [val[:, :, 3] for val in output_colors]
elif ret_image:
res = output_images
else:
res = output_colors, output_depths, output_images
return res
def render_image(self, render_data, images, cameras, extra_mesh,
**kwargs):
return self.__call__(render_data, images, cameras, extra_mesh,
ret_all=True, **kwargs)
def plot_meshes(img, meshes, K, R, T, mode='image'):
renderer = Renderer()
out = renderer.render_image(meshes, img, {'K': K, 'R': R, 'T': T}, [])
if mode == 'image':
return out[2][0]
elif mode == 'mask':
return out[0][0][..., -1]
elif mode == 'hstack':
return np.hstack([img, out[0][0][:, :, :3]])
elif mode == 'left':
out = out[0][0]
rend_rgba = np.roll(out, out.shape[1]//10, axis=1)
rend_cat = img.copy()
rend_cat[rend_rgba[:,:,-1]==255] = rend_rgba[:,:,:3][rend_rgba[:,:,-1]==255]
return rend_cat
# 这个顺序是BGR的。虽然render的使用的是RGB的但是由于和图像拼接了所以又变成BGR的了
colors = [
(94/255, 124/255, 226/255), # 青色
(255/255, 200/255, 87/255), # yellow
(74/255., 189/255., 172/255.), # green
(8/255, 76/255, 97/255), # blue
(219/255, 58/255, 52/255), # red
(77/255, 40/255, 49/255), # brown
]
colors_table = {
# colorblind/print/copy safe:
'_blue': [0.65098039, 0.74117647, 0.85882353],
'_pink': [.9, .7, .7],
'_mint': [ 166/255., 229/255., 204/255.],
'_mint2': [ 202/255., 229/255., 223/255.],
'_green': [ 153/255., 216/255., 201/255.],
'_green2': [ 171/255., 221/255., 164/255.],
'_red': [ 251/255., 128/255., 114/255.],
'_orange': [ 253/255., 174/255., 97/255.],
'_yellow': [ 250/255., 230/255., 154/255.],
'r':[255/255,0,0],
'g':[0,255/255,0],
'b':[0,0,255/255],
'k':[0,0,0],
'y':[255/255,255/255,0],
'purple':[128/255,0,128/255]
}
def get_colors(pid):
if isinstance(pid, int):
return colors[pid % len(colors)]
elif isinstance(pid, str):
return colors_table[pid]
elif isinstance(pid, list) or isinstance(pid, tuple):
if len(pid) == 3:
pid = (pid[0], pid[1], pid[2], 1.)
assert len(pid) == 4
return pid