EasyMocap/easymocap/visualize/pyrender_wrapper.py

188 lines
7.8 KiB
Python
Raw Permalink Normal View History

'''
@ Date: 2021-05-13 14:20:13
@ Author: Qing Shuai
@ LastEditors: Qing Shuai
2022-10-25 20:57:27 +08:00
@ 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
# )
2022-10-25 20:57:27 +08:00
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 = [
2022-10-25 20:57:27 +08:00
(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