Source code for bvhsdk.retarget

# -*- coding: utf-8 -*-
"""
Created on Tue Sep  4 15:12:57 2018

@author: Rodolfo Luis Tonoli
"""

import os
import numpy as np
from . import bvh
from . import anim
from . import surface
from . import mathutils
from . import skeletonmap
import time
from . import egocentriccoord
from . import ik


[docs] def PostureInitialization(tgtMap, srcMap, heightRatio, frame, getpositions=False, headAlign=True, spineAlign=False, handAlign=True): """ Copy the rotation from the mocap joints to the corresponding avatar joint. ani_ava: Avatar animation ani_mocap: Mocap animation """ ground_normal = np.array([0, 1, 0]) # Adjust roots/hips height # Eray Molla Eq 1 srcPosHips = srcMap.hips.getPosition(frame) srcGroundHips = np.asarray([srcPosHips[0], 0, srcPosHips[2]]) tgtGroundHips = srcGroundHips*heightRatio srcHHips = np.dot(srcMap.hips.getPosition(frame), ground_normal) tgtHHips = srcHHips*heightRatio tgtMap.root.translation[frame] = [0, tgtHHips, 0] + tgtGroundHips if frame == 0: mocapbones = [] avabones = [] if spineAlign: mocapbones.append([srcMap.hips, srcMap.spine3]) avabones.append([tgtMap.hips, tgtMap.spine3]) if headAlign: mocapbones.append([srcMap.neck, srcMap.head]) avabones.append([tgtMap.neck, tgtMap.head]) mocapbones = mocapbones + [[srcMap.rarm, srcMap.rforearm],[srcMap.larm, srcMap.lforearm],[srcMap.rforearm, srcMap.rhand],[srcMap.lforearm, srcMap.lhand],[srcMap.rupleg, srcMap.rlowleg],[srcMap.lupleg, srcMap.llowleg],[srcMap.rlowleg, srcMap.rfoot],[srcMap.llowleg, srcMap.lfoot]] avabones = avabones + [[tgtMap.rarm, tgtMap.rforearm],[tgtMap.larm, tgtMap.lforearm],[tgtMap.rforearm, tgtMap.rhand],[tgtMap.lforearm, tgtMap.lhand],[tgtMap.rupleg, tgtMap.rlowleg],[tgtMap.lupleg, tgtMap.llowleg],[tgtMap.rlowleg, tgtMap.rfoot],[tgtMap.llowleg, tgtMap.lfoot]] if handAlign and srcMap.lhandmiddle and srcMap.rhandmiddle and tgtMap.lhandmiddle and tgtMap.rhandmiddle: mocapbones = mocapbones + [[srcMap.rhand, srcMap.rhandmiddle],[srcMap.lhand, srcMap.lhandmiddle]] avabones = avabones + [[tgtMap.rhand, tgtMap.rhandmiddle],[tgtMap.lhand, tgtMap.lhandmiddle]] for mocapbone, avabone in zip(mocapbones,avabones): #Get source and target global transform and rotation matrices from the start of the bone p0 = mocapbone[0].getPosition(0) p1 = mocapbone[1].getPosition(0) srcDirection = mathutils.unitVector(p1-p0) #Get source and target global transform and rotation matrices from the end of the bone p0 = avabone[0].getPosition(0) p1 = avabone[1].getPosition(0) tgtDirection = mathutils.unitVector(p1-p0) #Align vectors alignMat = mathutils.alignVectors(tgtDirection, srcDirection) #Get new global rotation matrix tgtGlbTransformMat = avabone[0].getGlobalTransform(frame) tgtGlbRotationMat = mathutils.shape4ToShape3(tgtGlbTransformMat) tgtNewGblRotationMat = np.dot(alignMat,tgtGlbRotationMat) #Get new local rotation matrix if not avabone[0] == tgtMap.root: #Does not have a parent, transform is already local tgtParentGblRotationMat = mathutils.shape4ToShape3(avabone[0].parent.getGlobalTransform(frame)) tgtNewLclRotationMat = np.dot(tgtParentGblRotationMat.T, tgtNewGblRotationMat) else: tgtNewLclRotationMat = tgtNewGblRotationMat[:] #Get new local rotation euler angles tgtNewLclRotationEuler, warning = mathutils.eulerFromMatrix(tgtNewLclRotationMat, avabone[0].order) avabone[0].setLocalRotation(frame,tgtNewLclRotationEuler) else: for joint_ava, joint_mocap in zip(tgtMap.getJointsNoRootHips(), srcMap.getJointsNoRootHips()): if joint_ava is not None and joint_mocap is not None: previousframe = frame-1 if frame!= 0 else 0 #Get source and target global transform and rotation matrices #Even if frame == 0 the matrices need to be recalculated srcGlbTransformMat = joint_mocap.getGlobalTransform(frame) srcGlbRotationMat = mathutils.shape4ToShape3(srcGlbTransformMat) tgtGlbTransformMat = joint_ava.getGlobalTransform(previousframe) tgtGlbRotationMat = mathutils.shape4ToShape3(tgtGlbTransformMat) #Get previous source global transform and rotation matrices srcPreviousGlbTransformMat = joint_mocap.getGlobalTransform(previousframe) srcPreviousGlbRotationMat = mathutils.shape4ToShape3(srcPreviousGlbTransformMat) #Get the transform of the source from the previous frame to the present frame transform = np.dot(srcGlbRotationMat, srcPreviousGlbRotationMat.T) #Apply transform tgtNewGblRotationMat = np.dot(transform, tgtGlbRotationMat) #Get new local rotation matrix tgtParentGblRotationMat = mathutils.shape4ToShape3(joint_ava.parent.getGlobalTransform(frame)) tgtNewLclRotationMat = np.dot(tgtParentGblRotationMat.T, tgtNewGblRotationMat) #Get new local rotation euler angles tgtNewLclRotationEuler, warning = mathutils.eulerFromMatrix(tgtNewLclRotationMat, joint_ava.order) joint_ava.setLocalRotation(frame,tgtNewLclRotationEuler[:])
[docs] def checkName(name, currentpath=None): """ Return True if the file in the path/name provided exists inside current path :type name: string :param name: Local path/name of the back file """ if not currentpath: currentpath = os.path.dirname(os.path.realpath(__file__)) fullpath = os.path.join(currentpath, name) return os.path.isfile(fullpath)
[docs] def MotionRetargeting(sourceAnimationPath, sourceSurfacePath, targetSkeletonPath, targetSurfacePath, customSkeletomMap=None, computeEgo=True, computeIK=True, adjustOrientation=True, saveFile=True, saveInitAndFull=True, out_path=None): retargettime = time.time() # Surface Calibration ##################################################### start = time.time() srcSurface = surface.GetMoCapSurfaceFromTXT(sourceSurfacePath, highpolymesh=False) print('Surface from file done. %s seconds.' % (time.time()-start)) # Read mocap bvh file ##################################################### source_filename = os.path.basename(sourceAnimationPath) start = time.time() # TODO: I think that ReadFile does not need surfaceinfo anymore. Test it and remove it. srcAnimation = bvh.ReadFile(sourceAnimationPath, surfaceinfo=srcSurface) #srcMap = srcAnimation.getskeletonmap() srcMap = skeletonmap.SkeletonMap(srcAnimation) print('MoCap BVH read done. %s seconds.' % (time.time()-start)) # Read TPose bvh file ##################################################### start = time.time() tgtAnimation = bvh.ReadFile(targetSkeletonPath) # Get skeleton map # TODO: Acho que não é necessário pegar o mapa aqui, remover e testar #tgtMap = tgtAnimation.getskeletonmap(mapfile=customSkeletomMap) tgtMap = skeletonmap.SkeletonMap(tgtAnimation, mapfile=customSkeletomMap) # Get the avatar surface data tgtSurface = surface.GetAvatarSurfaceFromCSV(targetSurfacePath, highpolymesh=False) # Scale the avatar surface data accordingly to the TPose bvh data tgtSurface.NormalizeSurfaceData(hipsjoint=tgtMap.hips) surface.GetAvatarSurfaceLocalTransform(tgtAnimation, tgtSurface) print('Avatar BVH read done. %s seconds.' % (time.time()-start)) # Initialize pose ######################################################### tgtAnimation.frames = srcAnimation.frames tgtAnimation.frametime = srcAnimation.frametime # Save the reference TPose for joint in tgtAnimation.root: joint.tposerot = joint.rotation[0] joint.tposetrans = joint.translation[0] tgtAnimation.expandFrames(srcMap.root.translation.shape[0]) # Get the Height of the root in the base position (Frame = 0) # If the source animation (mocap) is not in the TPose, it will fail ground_normal = np.array([0, 1, 0]) srcHHips = np.dot(srcMap.hips.getPosition(0), ground_normal) tgtHHips = np.dot(tgtMap.hips.getPosition(0), ground_normal) heightRatio = tgtHHips/srcHHips #JacRHand = ik.SimpleJacobian(tgtAnimation, tgtAnimation.getskeletonmap().rhand, depth=5) #JacLHand = ik.SimpleJacobian(tgtAnimation, tgtAnimation.getskeletonmap().lhand, depth=5) JacRHand = ik.SimpleJacobian(tgtAnimation, tgtAnimation.skeletonmap.rhand, depth=5) JacLHand = ik.SimpleJacobian(tgtAnimation, tgtAnimation.skeletonmap.lhand, depth=5) iklogRHand = [] iklogLHand = [] print('Starting Motion Retargeting') start = time.time() for frame in range(srcAnimation.frames): # Perform Simple Retargeting ################################################## PostureInitialization(tgtMap, srcMap, heightRatio, frame, getpositions=False, headAlign=True, spineAlign=False) # Calculate egocentric coordinates ############################################ egocoord = egocentriccoord.GetEgocentricCoordinatesTargets(srcAnimation, srcSurface, tgtAnimation, tgtSurface, frame) # TODO: I can't remember why I'm doing this. Probably to get some info for my thesis. Remove it later MotionRetargeting.importance.append(egocoord[0].importance) # Applies Inverse Kinematics ################################################### logR = JacRHand.jacobianTranspose(frame=frame, target=egocoord[0].getTarget(frame)) logL = JacLHand.jacobianTranspose(frame=frame, target=egocoord[1].getTarget(frame)) iklogRHand.append(logR) iklogLHand.append(logL) # Adjust Limb Extremities ################################################## egocentriccoord.AdjustExtremityOrientation(tgtAnimation, tgtSurface, egocoord, srcAnimation, frame) # I was working on some different adjustment but it did not work. Remove it later: # egocentriccoord.AdjustExtremityOrientation2(tgtAnimation, srcAnimation) if np.mod(frame+1, 100) == 0: print('%i frames done. %s seconds.' % (int((frame+1)/100)*100, time.time()-start)) start = time.time() if not saveFile: # TODO: This method used to have different returns (thus the 'None'). Clean it up later return tgtAnimation, tgtSurface, srcAnimation, srcSurface, None, egocoord # Save File ################################################### if not out_path: currentpath = os.path.dirname(os.path.realpath(__file__)) else: currentpath = out_path output_filename = source_filename[:-4] + '_retarget' of_aux = output_filename i = 0 while checkName(output_filename+'.bvh', currentpath): i = i + 1 output_filename = of_aux + str(i) bvh.WriteBVH(tgtAnimation, currentpath, output_filename, refTPose=True) # if saveInitAndFull: # output_filename = source_filename[:-4] + '_initialRetarget' # of_aux = output_filename # i = 0 # while checkName(output_filename+'.bvh'): # i = i + 1 # output_filename = of_aux + str(i) # bvh.WriteBVH(tgtAnimation_onlyInitial, currentpath, output_filename,refTPose=True) # return tgtAnimation, tgtSurface, srcAnimation, srcSurface, tgtAnimation_onlyInitial, egocoord print('Done! Total time: %s seconds.' % (time.time()-retargettime)) return tgtAnimation, tgtSurface, srcAnimation, srcSurface, None, egocoord
[docs] def SimpleMotionRetargeting(srcAnimationPath, tgtAnimationPath, outputPath, srcSkeletonMap=None, tgtSkeletonMap=None, frameStop=False, trackProgress=False, forceFaceZ=False): """ Perform a simple motion retargeting. The bones of the target skeleton is aligned with the bones of the source animation Parameters ---------- srcAnimationPath : string Path to the source animation (.bvh) tgtAnimationPath : string, optional Path to the source animation (.bvh). The animation file may have one or several frames, but only the first take will be used. Returns ------- tgtAnimation : Animation object Retargeted animation. """ srcAnimation = bvh.ReadFile(srcAnimationPath) tgtAnimation = bvh.ReadFile(tgtAnimationPath) #srcMap = srcAnimation.getskeletonmap(mapfile=srcSkeletonMap) #tgtMap = tgtAnimation.getskeletonmap(mapfile=tgtSkeletonMap) srcMap = skeletonmap.SkeletonMap(srcAnimation, mapfile=srcSkeletonMap) tgtMap = skeletonmap.SkeletonMap(tgtAnimation, mapfile=tgtSkeletonMap) if trackProgress: print('Retargeting start.') if frameStop: stop = np.min([srcAnimation.frames, frameStop]) else: stop = srcAnimation.frames # TODO: NÃO É COM O LOCAL,MAS SIM COM O GLOBAL EM Y QUE TENHO QUE GIRAR if forceFaceZ: initrot = srcAnimation.root.getLocalRotation(frame=0) for frame in range(stop): srcAnimation.root.RotateGlobal([0,180,0], frame) ############## DEBUG: ######### srcAnimation.frames = stop output_name = os.path.basename(tgtAnimationPath)[:-4] + '_ForceZTest' i = 0 of_aux = output_name while checkName(output_name+'.bvh', outputPath): i = i + 1 output_name = of_aux + str(i) bvh.WriteBVH(srcAnimation, path=outputPath, name=output_name, refTPose=True) return None tgtAnimation.expandFrames(stop) # Get the Height of the root in the base position (Frame = 0) # If the source animation (mocap) is not in the TPose, it will fail ground_normal = np.array([0, 1, 0]) srcHHips = np.dot(srcMap.hips.getPosition(0), ground_normal) tgtHHips = np.dot(tgtMap.hips.getPosition(0), ground_normal) heightRatio = tgtHHips/srcHHips for frame in range(stop): # Perform Simple Retargeting ################################################## if trackProgress and np.mod(frame, 100) == 0: print('%i / %i done.' % (frame, stop)) PostureInitialization(tgtMap, srcMap, heightRatio, frame, getpositions=False, headAlign=True, spineAlign=False) #srcMap = srcAnimation.getskeletonmap() #tgtMap = tgtAnimation.getskeletonmap() #srcMap = skeletonmap.SkeletonMap(srcAnimation) #tgtMap = skeletonmap.SkeletonMap(tgtAnimation) output_name = os.path.basename(tgtAnimationPath)[:-4] + '_SimpleRetarget' i = 0 of_aux = output_name while checkName(output_name+'.bvh', outputPath): i = i + 1 output_name = of_aux + str(i) bvh.WriteBVH(tgtAnimation, path=outputPath, name=output_name, refTPose=False) return tgtAnimation