panda3d 渲染

目录

安装

设置渲染宽高:

渲染3d


安装

pip install Panda3D

设置渲染宽高:

python 复制代码
import panda3d.core as pd


margin = 100
screen = Tk().winfo_screenwidth() - margin, Tk().winfo_screenheight() - margin
width, height = (screen[0], int(screen[0] / 1.8)) if screen[0] / screen[1] < 1.8 else (int(screen[1] * 1.8), screen[1])
width,height=1200,800
pd.loadPrcFileData("", "win-origin {} {}".format((screen[0] + margin - width) // 2, (screen[1] + margin - height) // 2))
pd.loadPrcFileData("", "win-size {} {}".format(width, height))
pd.loadPrcFileData("", "win-fixed-size 1")

渲染3d

UnderPressure/visualization.py at main · InterDigitalInc/UnderPressure · GitHub

python 复制代码
"""
	Copyright (c) 2022, InterDigital R&D France. All rights reserved. This source
	code is made available under the license found in the LICENSE.txt at the root
	directory of the repository.
"""

# UnderPressure
import anim, util
from data import FRAMERATE, TOPOLOGY, Forces

# Python
import math, time
from pathlib import Path
from tkinter import Tk

# PyTorch
import torch

# Panda3D
import panda3d.core as pd
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.gui.DirectGui import DGG, DirectButton, DirectLabel, DirectSlider

def rotate(pts, axis, angle):
	axis = torch.nn.functional.normalize(axis, p=2, dim=-1)
	rmat = util.so3.to_SO3(angle * axis)
	return torch.einsum("ij,...j->...i", rmat, pts)

class Geo:
	@classmethod
	def xy2xyz(cls, tensor, z=0.0, dim=-1):													# [...] x 2 x [...]
		assert tensor.shape[dim] == 2
		x, y = tensor.unbind(dim)															# [...] x	 [...]
		return torch.stack([x, y, torch.full_like(x, z)], dim)								# [...] x 3 x [...]

class GeomBuilder:
	def __init__(self, name, color=None):
		self.name = name
		self.vdata = pd.GeomVertexData(name, pd.GeomVertexFormat.getV3n3c4(), pd.Geom.UHStatic)
		self.vertexWriter = pd.GeomVertexWriter(self.vdata, "vertex")
		self.normalWriter = pd.GeomVertexWriter(self.vdata, "normal")
		self.colorWriter = pd.GeomVertexWriter(self.vdata, "color")
		
		self.triangles = pd.GeomTriangles(pd.Geom.UHStatic)
		self.currentRow = 0
		self.color = color

	def _vertex(self, point, normal, prim=None, color=None):
		color = color or self.color or [1, 1, 1, 1]
		self.vertexWriter.addData3(*point)
		self.normalWriter.addData3(*normal)
		self.colorWriter.addData4(color[0], color[1], color[2], color[3])
		if prim is not None:
			prim.addVertex(self.currentRow)
		self.currentRow += 1

	def triangle(self, p1, p2, p3, color=None, normal=None):
		if normal is None:
			normal = (p2 - p1).cross(p3 - p1).normalized()
		self._vertex(p1, normal, self.triangles, color)
		self._vertex(p2, normal, self.triangles, color)
		self._vertex(p3, normal, self.triangles, color)
		self.triangles.closePrimitive()

	def polygon(self, pts, color=None):
		triangulator = pd.Triangulator3()
		for pt in pts:
			triangulator.addPolygonVertex(triangulator.addVertex(*pt))
		triangulator.triangulate()
		for i in range(triangulator.getNumTriangles()):
			self.triangle(
				triangulator.getVertex(triangulator.getTriangleV0(i)),
				triangulator.getVertex(triangulator.getTriangleV1(i)),
				triangulator.getVertex(triangulator.getTriangleV2(i)),
				color=color
			)

	def thick_polygon(self, pts, tickness, color=None, face_color=None, edge_color=None):
		pts = torch.as_tensor(pts)
		bottom, top = pts, pts + torch.as_tensor([0, 0, tickness])
		self.polygon(top, face_color or color)
		self.polygon(bottom, face_color or color)
		for i in range(0, top.shape[0]):
			j = (i + 1) % top.shape[0]
			self.polygon([top[i], top[j], bottom[j], bottom[i]], edge_color or color)

	def cylinder(self, p1, p2, radius, n=24, color=None):
		p1, p2 = torch.as_tensor(p1), torch.as_tensor(p2)
		axis = torch.nn.functional.normalize(p2 - p1, p=2, dim=-1)					# 3
		
		ex = torch.as_tensor([1.0, 0.0, 0.0])
		if (axis != ex).any():
			rvec = radius * torch.nn.functional.normalize(ex.cross(axis), p=2, dim=-1)
		else:
			rvec = torch.as_tensor([0.0, radius, 0.0])
		
		for i in range(n):
			self.triangle(
				p1 + rotate(rvec, axis, i / n * math.tau),
				p2 + rotate(rvec, axis, (i + 0.5) / n * math.tau),
				p2 + rotate(rvec, axis, (i - 0.5) / n * math.tau),
				color=color,
				normal=rotate(rvec, axis, i / n * math.tau),
			)
			self.triangle(
				p2 + rotate(rvec, axis, (i + 0.5) / n * math.tau),
				p1 + rotate(rvec, axis, (i + 1) / n * math.tau),
				p1 + rotate(rvec, axis, i / n * math.tau),
				color=color,
				normal=rotate(rvec, axis, (i + 0.5) / n * math.tau),
			)

	def cube(self, center, side, color=None):
		return self.box(center, [side, side, side], color=color)

	def box(self, center, sides, color=None):
		center = torch.as_tensor(center)
		sx, sy, sz = sides[0] / 2, sides[1] / 2, sides[2] / 2
		vertices = torch.stack([
			center + torch.as_tensor([-sx, -sy, -sz]),
			center + torch.as_tensor([-sx, sy, -sz]),
			center + torch.as_tensor([-sx, sy, sz]),
			center + torch.as_tensor([-sx, -sy, sz]),
			center + torch.as_tensor([sx, -sy, -sz]),
			center + torch.as_tensor([sx, sy, -sz]),
			center + torch.as_tensor([sx, sy, sz]),
			center + torch.as_tensor([sx, -sy, sz]),
		])
		self.polygon(vertices[[0, 1, 2, 3]], color)
		self.polygon(vertices[[0, 4, 5, 1]], color)
		self.polygon(vertices[[1, 5, 6, 2]], color)
		self.polygon(vertices[[2, 6, 7, 3]], color)
		self.polygon(vertices[[3, 7, 4, 0]], color)
		self.polygon(vertices[[7, 6, 5, 4]], color)

	def build(self):
		geom = pd.Geom(self.vdata)
		geom.addPrimitive(self.triangles)
		node = pd.GeomNode(self.name)
		node.addGeom(geom)
		return node

class AnimationLoop:
	def __init__(self, length: int, fps: float, start: bool = True):
		self._length, self._fps = int(length), float(fps)
		self._time, self._frame = 0.0, 0.0
		self._stop, self._running = False, bool(start)
		self.functions = []

	@property
	def length(self) -> int:
		return self._length
	@length.setter
	def length(self, length: int):
		self._length = int(length)
	def __len__(self) -> int:
		return self.length

	@property
	def fps(self) -> float:
		return self._fps
	@fps.setter
	def fps(self, value: float):
		self._fps = float(value)

	@property
	def frame(self) -> float:
		return self._frame % self.length
	@property
	def running(self) -> bool:
		return self._running

	def reset(self):
		self._time = 0.0
		self._frame = 0.0
	def pause(self):
		self._running = False
	def unpause(self):
		self._running = True
	def stop(self):
		self._stop = True

	def bind(self, fn):
		self.functions.append(fn)
	def unbind(self, fn):
		self.functions.remove(fn)

	def __call__(self, state):
		if self._stop:
			return Task.done
		if self._running:
			self._frame += (state.time - self._time) * self.fps
			for fn in self.functions:
				fn(self.frame)
		self._frame %= self.length
		self._time = state.time
		return Task.cont

class Animatable:
	def __init__(self, loop: AnimationLoop):
		self.loop = loop
		
	@property
	def loop(self):
		return self._loop
		
	@loop.setter
	def loop(self, loop: AnimationLoop):
		del self.loop
		assert isinstance(loop, AnimationLoop)
		self._loop = loop
		self._loop.bind(self.update)

	@loop.deleter
	def loop(self):
		if hasattr(self, "_loop"):
			self._loop.unbind(self.update)
			del self._loop

	@property
	def nframes(self):
		return len(self.loop)
	
	def update(self, frame: int):
		prev_frame, next_frame = int(math.floor(frame)), min(int(math.ceil(frame)), self.nframes-1)
		dframe = frame - prev_frame
		self.__update__(frame, prev_frame, next_frame, dframe)
		
	def __update__(self, frame: int, prev_frame: int, next_frame: int, dframe: float):
		raise NotImplementedError()

class Ground(Animatable):
	DEFAULT_COLOR1 = 3 * (0.4, ) + (1.0, )
	DEFAULT_COLOR2 = 3 * (0.6, ) + (1.0, )
	
	def __init__(self, parent, grid, size, origin=(0, 0), color1=DEFAULT_COLOR1, color2=DEFAULT_COLOR2, loop=None, trajectory=None):
		grid, size = torch.as_tensor(grid).int(), torch.as_tensor(size)
		origin = torch.as_tensor(origin)
		cell = size / grid
		builder = GeomBuilder("ground")
		
		for x in range(grid[0]):
			for y in range(grid[1]):
				vertices = origin - 0.5*size + torch.stack([
					torch.as_tensor([x, y]) * cell,
					torch.as_tensor([x, y+1]) * cell,
					torch.as_tensor([x+1, y+1]) * cell,
					torch.as_tensor([x+1, y]) * cell,
				])
				color = color1 if x%2 == y%2 else color2
				builder.polygon(Geo.xy2xyz(vertices), color)
		self._node = parent.attachNewNode(builder.build())
		
		if loop is not None:
			super().__init__(loop)
		if trajectory is not None:
			self.trajectory = trajectory
		
	@property
	def trajectory(self):
		return self._trajectory.clone()
	@trajectory.setter
	def trajectory(self, trajectory):
		self._trajectory = trajectory.clone()
			
	def __update__(self, frame: int, prev_frame: int, next_frame: int, dframe: float):
		position = torch.lerp(*self.trajectory[[prev_frame, next_frame]], dframe)
		self._node.setPos(*position.tolist())

class Insole(Animatable):
	# geometry
	INSOLE_VERTICES = torch.load(Path(__file__).parent / "geo_insoles.pth")
	INSOLE_VERTICES_BBOX = torch.stack([INSOLE_VERTICES.amin(dim=0), INSOLE_VERTICES.amax(dim=0)])
	INSOLE_VERTICES_LENGTH = (INSOLE_VERTICES_BBOX[1, 1] - INSOLE_VERTICES_BBOX[0, 1])
	INSOLE_VERTICES_CENTER = 0.5 * (INSOLE_VERTICES_BBOX[0] + INSOLE_VERTICES_BBOX[1])
	CELLS_VERTICES = torch.load(Path(__file__).parent / "geo_insole_cells.pth")

	# colors
	INSOLE_COLOR = (0.2, 0.2, 0.2, 1.0)
	TOE_COLOR = (150/255, 48/255, 48/255, 1.0)
	ARCH_COLOR = (0.5, 0.5, 0.5, 1.0)
	HEEL_COLOR = (48/255, 84/255, 150/255, 1.0)

	def __init__(self, parent, side, origin, length, angle, zscale=0.025, loop=None, forces=None):
		self._values = torch.zeros(16)
		self._side = side
		self.__init_geometry__(parent, side, origin, length, angle)
		self._zscale = float(zscale)
		if loop is not None:
			super().__init__(loop)
		if forces is not None:
			self.forces = forces

	@classmethod
	def left(cls, parent, *args, **kwargs):
		return cls(parent, "left", *args, **kwargs)
		
	@classmethod
	def right(cls, parent, *args, **kwargs):
		return cls(parent, "right", *args, **kwargs)

	def is_left(self) -> bool:
		return self._side == "left"
		
	def is_right(self) -> bool:
		return self._side == "right"

	def __init_geometry__(self, parent, side, origin, length, angle):
		self._origin, length = torch.as_tensor(origin), torch.as_tensor(length)
		angle = (180 + torch.as_tensor(angle)) / 180 * math.pi
		side = {"right": 1, "left": -1}[side]
		
		# prepare global transform
		transform_mat = torch.as_tensor([[angle.cos(), -angle.sin()], [angle.sin(), angle.cos()]]) # rotation
		transform_mat = torch.matmul(transform_mat, (length / self.INSOLE_VERTICES_LENGTH).expand(2).diag()) # scale
		transform_mat = torch.matmul(transform_mat, torch.as_tensor([side, 1.0]).diag()) # left
		transform = lambda pts: Geo.xy2xyz(torch.einsum("ij,nj->ni", transform_mat, pts.clone() - self.INSOLE_VERTICES_CENTER))
		
		self._base_tickness = 0.002
		
		# insole
		builders = {}
		insole_vertices = transform(self.INSOLE_VERTICES)
		if side == -1:
			insole_vertices = insole_vertices.flip(0)
		builders["insole"] = GeomBuilder("insole")
		builders["insole"].thick_polygon(
			self._origin + insole_vertices, tickness=self._base_tickness,
			face_color=self.INSOLE_COLOR, edge_color=self.INSOLE_COLOR
		)
		
		# cells
		for i, cell_vertices in enumerate(self.CELLS_VERTICES):
			cell_vertices = transform(cell_vertices)
			if side == -1:
				cell_vertices = cell_vertices.flip(0)
			cell_name = "cell{}".format(i)
			cell_face_color = self.TOE_COLOR if i in Forces.FRONT_CELLS else self.HEEL_COLOR if i in Forces.BACK_CELLS else self.ARCH_COLOR
			builders[cell_name] = GeomBuilder(cell_name)
			builders[cell_name].thick_polygon(
				cell_vertices, tickness=1.0,
				face_color=cell_face_color, edge_color=cell_face_color
			)
			
		self._nodes = {key: parent.attachNewNode(builder.build()) for key, builder in builders.items()}		
		cells_origin = self._origin + torch.as_tensor([0.0, 0.0, self._base_tickness])
		for i in range(len(self.CELLS_VERTICES)):		
			self._nodes["cell{}".format(i)].setPos(*cells_origin.tolist())
	
	def __getitem__(self, index):
		return self._values.__getitem__(index)
	def __setitem__(self, index, value):
		self._values.__setitem__(index, value)
		for i in range(16):
			z = self._zscale * self._values[i].item() + 1e-5
			self._nodes["cell{}".format(i)].setSz(z)

	@property
	def forces(self):
		return self._forces.clone()
	@forces.setter
	def forces(self, forces):
		self._forces = forces.clone()
		
	def __update__(self, frame: int, prev_frame: int, next_frame: int, dframe: float):
		self[:] = torch.lerp(*self.forces[[prev_frame, next_frame]], dframe)		

class InsolesPair:
	def __init__(self, left, right, loop=None, forces=None):
		self.left, self.right = left, right
		if loop is not None:
			self.left.loop = loop
			self.right.loop = loop
		if forces is not None:
			self.forces = forces

	@property
	def left(self):
		return self._left
	@left.setter
	def left(self, left):
		assert isinstance(left, Insole) and left.is_left()
		self._left = left
		
	@property
	def right(self):
		return self._right
	@right.setter
	def right(self, right):
		assert isinstance(right, Insole) and right.is_right()
		self._right = right

	def __setitem__(self, index, value):
		values = self[:]
		values.__setitem__(index, value)
		self.left[:] = values[0]
		self.right[:] = values[1]

	def __getitem__(self, index):
		return torch.stack([self.left[:], self.right[:]]).__getitem__(index)
	
	@property
	def forces(self):
		return self.left.forces, self.right.forces
	@forces.setter
	def forces(self, forces):
		self.left.forces = forces[0]
		self.right.forces = forces[1]
	
	def update(self, frame):
		self.left.update(frame)
		self.right.update(frame)

class Skeleton(Animatable):
	def __init__(self, parent, skeleton, loop=None, angles=None, trajectory=None, label=""):
		self._skeleton = skeleton
		self._joints = []
		for joint in TOPOLOGY:
			node = GeomBuilder(joint)
			node.cube((0, 0, 0), side=0.05, color=[0.2, 0.2, 0.2, 0.0])
			self._joints.append(parent.attachNewNode(node.build()))
		if loop is not None:
			super().__init__(loop)
		if angles is not None:
			self.angles = angles
		if trajectory is not None:
			self.trajectory = trajectory	
		if label:
			self._label = pd.TextNode(label)
			self._label.setText(label)
			self._label.setAlign(pd.TextNode.ACenter)
			self._label = parent.attachNewNode(self._label)
			self._label.setScale(0.1)
			self._label.setPos(0.0, 0.0, 1.8)
		
	@property
	def angles(self):
		return self._angles.clone()
	@angles.setter
	def angles(self, angles):
		self._angles = angles.clone()
		
	@property
	def trajectory(self):
		return self._trajectory.clone()
	@trajectory.setter
	def trajectory(self, trajectory):
		self._trajectory = trajectory.clone()
		self._label_traj = util.gma(self.trajectory, size=50, std=50/3, dim=0)
	
	def __update__(self, frame: int, prev_frame: int, next_frame: int, dframe: float):
		angles = util.SU2.slerp(*self.angles[[prev_frame, next_frame]], dframe)
		positions = anim.FK(angles, self._skeleton, 0, TOPOLOGY)
		if hasattr(self, "_trajectory"):
			positions += torch.lerp(*self.trajectory[[prev_frame, next_frame]], dframe)		
		if hasattr(self, "_label") and hasattr(self, "_label_traj"):
			self._label.setPos(*torch.lerp(*self._label_traj[[prev_frame, next_frame]], dframe)[:2].tolist(), 1.8)				
		for j, joint in enumerate(self._joints):
			joint.setQuat(pd.LQuaternion(*angles[j].tolist()))
			joint.setPos(*positions[j].tolist())
	
class MocapAndvGRFsApp(ShowBase):
	def __init__(self, motions: list, vGRFs: list = [], motion_labels=None, vGRF_labels=None):
		motion_labels = [None for _ in motions] if motion_labels is None else motion_labels
		vGRF_labels = [None for _ in vGRFs] if vGRF_labels is None else vGRF_labels
		
		margin = 100
		screen = Tk().winfo_screenwidth() - margin, Tk().winfo_screenheight() - margin
		width, height = (screen[0], int(screen[0] / 1.8)) if screen[0] / screen[1] < 1.8 else (int(screen[1] * 1.8), screen[1])
		width,height=1200,800
		pd.loadPrcFileData("", "win-origin {} {}".format((screen[0] + margin - width) // 2, (screen[1] + margin - height) // 2))
		pd.loadPrcFileData("", "win-size {} {}".format(width, height))
		pd.loadPrcFileData("", "win-fixed-size 1")
		
		super().__init__()
		self.render.setTwoSided(True)
		self.disableMouse()
		self.__init_lights__()
		self.__init_camera__(fov=50, near=0.01, far=50, pos=[0.0, -7.0, 3.0], dir=[0.0, 1.2, -0.5])
		
		# init animation loop	
		assert len({v.shape[0] for v in vGRFs} | {m[0].shape[0] for m in motions}) == 1		
		nframes = motions[0][0].shape[0]
		self.animation_loop = AnimationLoop(length=nframes, fps=FRAMERATE, start=True)
		self.taskMgr.add(self.animation_loop, "animation_loop")

		# insoles
		if len(vGRFs) > 0:
			vGRFs_scale = 1 / torch.stack(list(vGRFs)).max()	
			for index, (value, label) in enumerate(zip(vGRFs, vGRF_labels)):
				x = index * 0.111 - 0.111 * (len(vGRFs) - 1) / 2
				self.add_insoles([
					x, self.cam.getPos()[1] + 0.60 + 0.08, self.cam.getPos()[2] - 0.435],
					vGRFs_scale * value, plate=True, label=label,
				)

		# get global trajectory
		trajectories = torch.cat([motion[2] for motion in motions], dim=-2)
		global_trajectory = trajectories.mean(dim=-2)
		global_trajectory = util.gma(global_trajectory, size=30, std=30/3, dim=0)
		global_trajectory[..., 2] = 0.0
				
		c = 0.25
		global_bbox = trajectories.amax(dim=(0, 1)) - trajectories.amin(dim=(0, 1))
		global_position = global_trajectory.mean(dim=0)
		ground_grid = ((global_bbox[:2] + 3) / c).ceil()
		self._ground = Ground(
			self.render, grid=ground_grid, size=c*ground_grid, origin=global_position[:2],
			loop=self.animation_loop,
			trajectory=-global_trajectory,
		)
		
		for motion, label in zip(motions, motion_labels):
			angles, skeleton, trajectory = motion[0], motion[1].reshape(-1, 3), motion[2].reshape(-1, 3)
			trajectory = trajectory - global_trajectory
			self._skeleton = Skeleton(
				self.render, skeleton,
				loop=self.animation_loop, angles=angles, trajectory=trajectory,
				label=label,
			)
		
		# Framerate buttons
		button025 = DirectButton(text="0.25x", scale=0.05, pos=(-0.2, 0.0, 0.96), command=lambda: self.change_framerate(0.25))
		button050 = DirectButton(text="0.50x", scale=0.05, pos=(0.0, 0.0, 0.96), command=lambda: self.change_framerate(0.50))
		button100 = DirectButton(text="1.00x", scale=0.05, pos=(0.2, 0.0, 0.96), command=lambda: self.change_framerate(1.00))
	
	def change_framerate(self, rate):
		self.animation_loop.fps = rate * FRAMERATE
		
	def __init_lights__(self):
		def point_light(position, color, name=""):
			light = pd.PointLight(name)
			light.setColor(color)
			light = self.render.attachNewNode(light)
			light.setPos(*position)
			self.render.setLight(light)
		
		intensity = 0.6
		point_light((5, 5, -5), (intensity, intensity, intensity, 1))
		point_light((0, 5, -5), (intensity, intensity, intensity, 1))
		point_light((5, 0, -5), (intensity, intensity, intensity, 1))
		
	def __init_camera__(self, **kwargs):
		if "near" in kwargs:
			self.camLens.setNear(kwargs["near"])
		if "far" in kwargs:
			self.camLens.setFar(kwargs["far"])		
		if "fov" in kwargs:
			self.camLens.setFov(kwargs["fov"])
		if "pos" in kwargs:
			self.cam.setPos(*kwargs["pos"])
		if "target" in kwargs:
			pos, target = torch.as_tensor(self.cam.getPos()), torch.as_tensor(kwargs["target"])
			dir = (target - pos).tolist()
		elif "dir" in kwargs:
			dir = kwargs["dir"]
		self.camLens.setViewVector(
			*dir,
			*kwargs.get("up", [0.0, 0.0, 1.0]),
		)

	def add_insoles(self, origin, forces, plate=False, label=None):
		insoles = InsolesPair(
			Insole.left(self.render, origin=[origin[0] - 0.021, origin[1], origin[2]], length=0.15, angle=10.0),
			Insole.right(self.render, origin=[origin[0] + 0.021, origin[1], origin[2]], length=0.15, angle=-10.0),
			loop=self.animation_loop, forces=forces.unbind(dim=-2),
		)
		
		width, depth, thickness = 0.108, 0.16, 0.01
		if plate:
			builder = GeomBuilder("insoles_plate")
			builder.box([origin[0], origin[1], origin[2] - thickness/2], [width, depth, thickness], color=(0.8, 0.8, 0.8, 1.0))
			self.render.attachNewNode(builder.build())
		
		if isinstance(label, str):
			text = pd.TextNode(label)
			text.setText(label)
			text.setAlign(pd.TextNode.ACenter)
			text = self.render.attachNewNode(text)
			text.setScale(thickness)
			text.setPos(origin[0], origin[1] - depth/2 - 1e-4, origin[2] - 0.8*thickness)		

if __name__ == "__main__":
	import models
	from data import Dataset
	from argparse import ArgumentParser
	parser = ArgumentParser()
	parser.add_argument("-subj", "-subject", default="S9", help="Subject to be selected; default: S9")
	parser.add_argument("-seq", "-sequence", default="WalkRandomSlow", help="Sequence to be selected; default: WalkRandomSlow")
	parser.add_argument("-checkpoint", default="pretrained.tar")
	args = parser.parse_args()
	
	dataset = Dataset("{}-{}".format(args.subj, args.seq))
	item = dataset[0]
	angles, skeleton, trajectory = item["angles"], item["skeleton"], item["trajectory"]
	vGRFs_gt = item["forces"]
	positions = anim.FK(angles, skeleton, trajectory, TOPOLOGY)
	
	checkpoint = torch.load(args.checkpoint)
	model = models.DeepNetwork(state_dict=checkpoint["model"]).eval()
	with torch.no_grad():
		vGRFs_pred = model.vGRFs(positions.unsqueeze(0)).squeeze(0)
	vGRFs_abs_error = (vGRFs_gt - vGRFs_pred).abs()

	MocapAndvGRFsApp(
		[(angles, skeleton, trajectory)],
		[vGRFs_gt, vGRFs_pred, vGRFs_abs_error],
		vGRF_labels=["Ground Truth", "Estimated", "Abs. Error"],
	).run()
相关推荐
朝花惜时10 分钟前
供应链-库存管理之补货计算模型(二)
python·数据分析·运营
神经星星19 分钟前
入选CVPR 2025!深圳大学团队等提出EchoONE,可精准分割多切面超声心动图
人工智能·深度学习·机器学习
失去妙妙屋的米奇1 小时前
Python库与Excel
开发语言·python·excel
winfredzhang1 小时前
将 Markdown 表格结构转换为Excel 文件
python·excel·markdown
API小爬虫1 小时前
如何设置动态代理提高Python爬虫稳定性?
开发语言·爬虫·python
篝火悟者1 小时前
自学-python-爬虫入门
开发语言·爬虫·python
小白狮ww1 小时前
支持视频检测, YOLOv12 目标检测刷新速度、精度双记录
人工智能·深度学习·机器学习
企业软文推广2 小时前
媒体直播的力量:解锁新时代传播密码,引爆传播效应,媒介盒子分享
python
wgc2k2 小时前
吴恩达深度学习复盘(2)神经网络的基本原理轮廓
人工智能·深度学习
紫雾凌寒2 小时前
自然语言处理|人工智能如何革新作文批改:技术全解析
人工智能·深度学习·自然语言处理·easyui·长文本处理·语义理解·ai批改作文