341 lines
15 KiB
Python
341 lines
15 KiB
Python
|
import os
|
|||
|
import numpy as np
|
|||
|
import cv2
|
|||
|
import pyrender
|
|||
|
import trimesh
|
|||
|
import copy
|
|||
|
# 这个顺序是BGR的。虽然render的使用的是RGB的,但是由于和图像拼接了,所以又变成BGR的了
|
|||
|
colors = [
|
|||
|
# (0.5, 0.2, 0.2, 1.), # Defalut BGR
|
|||
|
(.5, .5, .7, 1.), # Pink BGR
|
|||
|
(.44, .50, .98, 1.), # Red
|
|||
|
(.7, .7, .6, 1.), # Neutral
|
|||
|
(.5, .5, .7, 1.), # Blue
|
|||
|
(.5, .55, .3, 1.), # capsule
|
|||
|
(.3, .5, .55, 1.), # Yellow
|
|||
|
# (.6, .6, .6, 1.), # gray
|
|||
|
(.9, 1., 1., 1.),
|
|||
|
(0.95, 0.74, 0.65, 1.),
|
|||
|
(.9, .7, .7, 1.)
|
|||
|
]
|
|||
|
|
|||
|
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]
|
|||
|
|
|||
|
from pyrender import RenderFlags
|
|||
|
render_flags = {
|
|||
|
'flip_wireframe': False,
|
|||
|
'all_wireframe': False,
|
|||
|
'all_solid': True,
|
|||
|
'shadows': False, # TODO:bug exists in shadow mode
|
|||
|
'vertex_normals': False,
|
|||
|
'face_normals': False,
|
|||
|
'cull_faces': False, # set to False
|
|||
|
'point_size': 1.0,
|
|||
|
'rgba':True
|
|||
|
}
|
|||
|
|
|||
|
flags = RenderFlags.NONE
|
|||
|
if render_flags['flip_wireframe']:
|
|||
|
flags |= RenderFlags.FLIP_WIREFRAME
|
|||
|
elif render_flags['all_wireframe']:
|
|||
|
flags |= RenderFlags.ALL_WIREFRAME
|
|||
|
elif render_flags['all_solid']:
|
|||
|
flags |= RenderFlags.ALL_SOLID
|
|||
|
|
|||
|
if render_flags['shadows']:
|
|||
|
flags |= RenderFlags.SHADOWS_DIRECTIONAL | RenderFlags.SHADOWS_SPOT
|
|||
|
if render_flags['vertex_normals']:
|
|||
|
flags |= RenderFlags.VERTEX_NORMALS
|
|||
|
if render_flags['face_normals']:
|
|||
|
flags |= RenderFlags.FACE_NORMALS
|
|||
|
if not render_flags['cull_faces']:
|
|||
|
flags |= RenderFlags.SKIP_CULL_FACES
|
|||
|
if render_flags['rgba']:
|
|||
|
flags |= RenderFlags.RGBA
|
|||
|
|
|||
|
|
|||
|
class Renderer(object):
|
|||
|
def __init__(self, focal_length=1000, height=512, width=512, faces=None,
|
|||
|
bg_color=[1.0, 1.0, 1.0, 0.0], down_scale=1, # render 配置
|
|||
|
extra_mesh=[]
|
|||
|
):
|
|||
|
self.renderer = pyrender.OffscreenRenderer(height, width)
|
|||
|
self.faces = faces
|
|||
|
self.focal_length = focal_length
|
|||
|
self.bg_color = bg_color
|
|||
|
self.ambient_light = (0.5, 0.5, 0.5)
|
|||
|
self.down_scale = down_scale
|
|||
|
self.extra_mesh = extra_mesh
|
|||
|
|
|||
|
def add_light(self, scene):
|
|||
|
trans = [0, 0, 0]
|
|||
|
# Use 3 directional lights
|
|||
|
# Create light source
|
|||
|
light = pyrender.DirectionalLight(color=[1.0, 1.0, 1.0], intensity=3)
|
|||
|
light_forward = np.eye(4)
|
|||
|
light_forward[:3, 3] = np.array([0, 1, 1])
|
|||
|
scene.add(light, pose=light_forward)
|
|||
|
light_z = np.eye(4)
|
|||
|
light_z[:3, :3] = cv2.Rodrigues(np.array([-np.pi/2, 0, 0]))[0]
|
|||
|
scene.add(light, pose=light_z)
|
|||
|
|
|||
|
def render(self, render_data, cameras, images,
|
|||
|
use_white=False, add_back=True,
|
|||
|
ret_depth=False, ret_color=False):
|
|||
|
# Need to flip x-axis
|
|||
|
rot = trimesh.transformations.rotation_matrix(
|
|||
|
np.radians(180), [1, 0, 0])
|
|||
|
output_images, output_colors, output_depths = [], [], []
|
|||
|
for nv, img_ in enumerate(images):
|
|||
|
if use_white:
|
|||
|
img = np.zeros_like(img_, dtype=np.uint8) + 255
|
|||
|
else:
|
|||
|
img = img_.copy()
|
|||
|
K, R, T = cameras['K'][nv].copy(), cameras['R'][nv], cameras['T'][nv]
|
|||
|
# down scale the image to speed up rendering
|
|||
|
img = cv2.resize(img, None, fx=1/self.down_scale, fy=1/self.down_scale)
|
|||
|
K[:2, :] /= self.down_scale
|
|||
|
|
|||
|
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(self.extra_mesh):
|
|||
|
if True:
|
|||
|
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))
|
|||
|
else:
|
|||
|
vert = np.asarray(_mesh.vertices).copy()
|
|||
|
faces = np.asarray(_mesh.faces)
|
|||
|
vert = vert @ R.T + T.T
|
|||
|
mesh = trimesh.Trimesh(vert, faces, process=False)
|
|||
|
mesh.apply_transform(rot)
|
|||
|
material = pyrender.MetallicRoughnessMaterial(
|
|||
|
metallicFactor=0.0,
|
|||
|
alphaMode='OPAQUE',
|
|||
|
baseColorFactor=(0., 0., 0., 1.))
|
|||
|
mesh = pyrender.Mesh.from_trimesh(
|
|||
|
mesh,
|
|||
|
material=material)
|
|||
|
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的颜色
|
|||
|
col = get_colors(data.get('vid', trackId))
|
|||
|
mesh = trimesh.Trimesh(vert, faces, process=False)
|
|||
|
mesh.apply_transform(rot)
|
|||
|
material = pyrender.MetallicRoughnessMaterial(
|
|||
|
metallicFactor=0.0,
|
|||
|
alphaMode='OPAQUE',
|
|||
|
baseColorFactor=col)
|
|||
|
mesh = pyrender.Mesh.from_trimesh(
|
|||
|
mesh,
|
|||
|
material=material)
|
|||
|
scene.add(mesh, data['name'])
|
|||
|
else:
|
|||
|
mesh = trimesh.Trimesh(vert, faces, vertex_colors=data['colors'], process=False)
|
|||
|
mesh.apply_transform(rot)
|
|||
|
material = pyrender.MetallicRoughnessMaterial(
|
|||
|
metallicFactor=0.0,
|
|||
|
alphaMode='OPAQUE',
|
|||
|
baseColorFactor=(1., 1., 1.))
|
|||
|
mesh = pyrender.Mesh.from_trimesh(mesh, material=material)
|
|||
|
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)
|
|||
|
# pyrender.Viewer(scene, use_raymond_lighting=True)
|
|||
|
# Alpha channel was not working previously need to check again
|
|||
|
# Until this is fixed use hack with depth image to get the opacity
|
|||
|
rend_rgba, rend_depth = self.renderer.render(scene, flags=flags)
|
|||
|
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 add_back:
|
|||
|
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 = rend_rgba
|
|||
|
|
|||
|
output_colors.append(rend_rgba)
|
|||
|
output_depths.append(rend_depth)
|
|||
|
output_images.append(rend_cat)
|
|||
|
if ret_depth:
|
|||
|
return output_images, output_depths
|
|||
|
elif ret_color:
|
|||
|
return output_colors
|
|||
|
else:
|
|||
|
return output_images
|
|||
|
|
|||
|
def _render_multiview(self, vertices, K, R, T, imglist, trackId=0, return_depth=False, return_color=False,
|
|||
|
bg_color=[0.0, 0.0, 0.0, 0.0], camera=None):
|
|||
|
# List to store rendered scenes
|
|||
|
output_images, output_colors, output_depths = [], [], []
|
|||
|
# Need to flip x-axis
|
|||
|
rot = trimesh.transformations.rotation_matrix(
|
|||
|
np.radians(180), [1, 0, 0])
|
|||
|
nViews = len(imglist)
|
|||
|
for nv in range(nViews):
|
|||
|
img = imglist[nv]
|
|||
|
self.renderer.viewport_height = img.shape[0]
|
|||
|
self.renderer.viewport_width = img.shape[1]
|
|||
|
# Create a scene for each image and render all meshes
|
|||
|
scene = pyrender.Scene(bg_color=bg_color,
|
|||
|
ambient_light=(0.3, 0.3, 0.3))
|
|||
|
camera_pose = np.eye(4)
|
|||
|
|
|||
|
# for every person in the scene
|
|||
|
if isinstance(vertices, dict):
|
|||
|
for trackId, data in vertices.items():
|
|||
|
vert = data['vertices'].copy()
|
|||
|
faces = data['faces']
|
|||
|
col = data.get('col', trackId)
|
|||
|
vert = vert @ R[nv].T + T[nv]
|
|||
|
mesh = trimesh.Trimesh(vert, faces)
|
|||
|
mesh.apply_transform(rot)
|
|||
|
trans = [0, 0, 0]
|
|||
|
|
|||
|
material = pyrender.MetallicRoughnessMaterial(
|
|||
|
metallicFactor=0.0,
|
|||
|
alphaMode='OPAQUE',
|
|||
|
baseColorFactor=colors[col % len(colors)])
|
|||
|
mesh = pyrender.Mesh.from_trimesh(
|
|||
|
mesh,
|
|||
|
material=material)
|
|||
|
scene.add(mesh, 'mesh')
|
|||
|
else:
|
|||
|
verts = vertices @ R[nv].T + T[nv]
|
|||
|
mesh = trimesh.Trimesh(verts, self.faces)
|
|||
|
mesh.apply_transform(rot)
|
|||
|
trans = [0, 0, 0]
|
|||
|
|
|||
|
material = pyrender.MetallicRoughnessMaterial(
|
|||
|
metallicFactor=0.0,
|
|||
|
alphaMode='OPAQUE',
|
|||
|
baseColorFactor=colors[trackId % len(colors)])
|
|||
|
mesh = pyrender.Mesh.from_trimesh(
|
|||
|
mesh,
|
|||
|
material=material)
|
|||
|
scene.add(mesh, 'mesh')
|
|||
|
|
|||
|
if camera is not None:
|
|||
|
light = pyrender.PointLight(color=[1.0, 1.0, 1.0], intensity=70)
|
|||
|
light_pose = np.eye(4)
|
|||
|
light_pose[:3, 3] = [0, 0, 4.5]
|
|||
|
scene.add(light, pose=light_pose)
|
|||
|
|
|||
|
light_pose[:3, 3] = [0, 1, 4]
|
|||
|
scene.add(light, pose=light_pose)
|
|||
|
|
|||
|
light_pose[:3, 3] = [0, -1, 4]
|
|||
|
scene.add(light, pose=light_pose)
|
|||
|
else:
|
|||
|
trans = [0, 0, 0]
|
|||
|
# Use 3 directional lights
|
|||
|
# Create light source
|
|||
|
light = pyrender.DirectionalLight(color=[1.0, 1.0, 1.0], intensity=1)
|
|||
|
light_pose = np.eye(4)
|
|||
|
light_pose[:3, 3] = np.array([0, -1, 1]) + trans
|
|||
|
scene.add(light, pose=light_pose)
|
|||
|
light_pose[:3, 3] = np.array([0, 1, 1]) + trans
|
|||
|
scene.add(light, pose=light_pose)
|
|||
|
light_pose[:3, 3] = np.array([1, 1, 2]) + trans
|
|||
|
scene.add(light, pose=light_pose)
|
|||
|
if camera is None:
|
|||
|
if K is None:
|
|||
|
camera_center = np.array([img.shape[1] / 2., img.shape[0] / 2.])
|
|||
|
camera = pyrender.camera.IntrinsicsCamera(fx=self.focal_length, fy=self.focal_length, cx=camera_center[0], cy=camera_center[1])
|
|||
|
else:
|
|||
|
camera = pyrender.camera.IntrinsicsCamera(fx=K[nv][0, 0], fy=K[nv][1, 1], cx=K[nv][0, 2], cy=K[nv][1, 2])
|
|||
|
scene.add(camera, pose=camera_pose)
|
|||
|
# Alpha channel was not working previously need to check again
|
|||
|
# Until this is fixed use hack with depth image to get the opacity
|
|||
|
color, rend_depth = self.renderer.render(scene, flags=flags)
|
|||
|
# color = color[::-1,::-1]
|
|||
|
# rend_depth = rend_depth[::-1,::-1]
|
|||
|
output_depths.append(rend_depth)
|
|||
|
color = color.astype(np.uint8)
|
|||
|
valid_mask = (rend_depth > 0)[:, :, None]
|
|||
|
if color.shape[2] == 3: # 在服务器上透明通道失败
|
|||
|
color = np.dstack((color, (valid_mask*255).astype(np.uint8)))
|
|||
|
output_colors.append(color)
|
|||
|
output_img = (color[:, :, :3] * valid_mask +
|
|||
|
(1 - valid_mask) * img)
|
|||
|
|
|||
|
output_img = output_img.astype(np.uint8)
|
|||
|
output_images.append(output_img)
|
|||
|
if return_depth:
|
|||
|
return output_images, output_depths
|
|||
|
elif return_color:
|
|||
|
return output_colors
|
|||
|
else:
|
|||
|
return output_images
|
|||
|
|
|||
|
def render_results(img, render_data, cam_params, outname=None, rotate=False, degree=90, axis=[1.,0.,0],
|
|||
|
fix_center=None):
|
|||
|
render_data = copy.deepcopy(render_data)
|
|||
|
render = Renderer(height=1024, width=1024, faces=None)
|
|||
|
Ks, Rs, Ts = [cam_params['K']], [cam_params['Rc']], [cam_params['Tc']]
|
|||
|
imgsrender = render.render_multiview(render_data, Ks, Rs, Ts, [img], return_color=True)[0]
|
|||
|
render0 = cv2.addWeighted(cv2.bitwise_and(img, 255 - imgsrender[:, :, 3:4].repeat(3, 2)), 1, imgsrender[:, :, :3], 1, 0.0)
|
|||
|
if rotate:
|
|||
|
# simple rotate the vertices
|
|||
|
if fix_center is None:
|
|||
|
center = np.mean(np.vstack([v['vertices'] for i, v in render_data.items()]), axis=0, keepdims=True)
|
|||
|
new_center = center.copy()
|
|||
|
new_center[:, 0:2] = 0
|
|||
|
else:
|
|||
|
center = fix_center.copy()
|
|||
|
new_center = fix_center.copy()
|
|||
|
new_center[:, 2] *= 1.5
|
|||
|
direc = np.array(axis)
|
|||
|
rot, _ = cv2.Rodrigues(direc*degree/90*np.pi/2)
|
|||
|
for key in render_data.keys():
|
|||
|
vertices = render_data[key]['vertices']
|
|||
|
vert = (vertices - center) @ rot.T + new_center
|
|||
|
render_data[key]['vertices'] = vert
|
|||
|
blank = np.zeros(())
|
|||
|
blank = np.zeros((img.shape[0], img.shape[1], 3), dtype=img.dtype) + 255
|
|||
|
imgsrender = render.render_multiview(render_data, Ks, Rs, Ts, [blank], return_color=True)[0]
|
|||
|
render1 = cv2.addWeighted(cv2.bitwise_and(blank, 255- imgsrender[:, :, 3:4].repeat(3, 2)), 1, imgsrender[:, :, :3], 1, 0.0)
|
|||
|
render0 = np.vstack([render0, render1])
|
|||
|
if outname is not None:
|
|||
|
os.makedirs(os.path.dirname(outname), exist_ok=True)
|
|||
|
cv2.imwrite(outname, render0)
|
|||
|
return render0
|