开源动捕GVHMR,效果测试及导入blender
子渊大叔
编辑于 2025年01月27日 16:56

我安装库的版本号供参考:aiohappyeyeballs     2.4.4

aiohttp          3.11.9

aiosignal         1.3.1

antlr4-python3-runtime  4.9.3

anyio           4.6.2.post1

argon2-cffi        23.1.0

argon2-cffi-bindings   21.2.0

arrow           1.3.0

asttokens         3.0.0

async-lru         2.0.4

async-timeout       5.0.1

attrs           24.2.0

autocommand        2.2.2

av            13.0.0

babel           2.16.0

backports.tarfile     1.2.0

beautifulsoup4      4.12.3

black           24.10.0

bleach          6.2.0

certifi          2024.8.30

cffi           1.17.1

chardet          5.2.0

charset-normalizer    3.4.0

cheroot          10.0.1

CherryPy         18.10.0

chumpy          0.70

click           8.1.7

colorlog         6.9.0

comm           0.2.2

contourpy         1.3.1

cycler          0.12.1

Cython          3.0.11

cython_bbox        0.1.5

debugpy          1.8.9

decorator         5.1.1

defusedxml        0.7.1

docstring_parser     0.16

dpvo           0.0.0       /home/sxxzwbl/GVHMR/GVHMR/third-party/DPVO

einops          0.8.0

embreex          2.17.7.post5

exceptiongroup      1.2.2

executing         2.1.0

fastjsonschema      2.21.1

ffmpeg-python       0.2.0

filelock         3.16.1

fonttools         4.55.1

fqdn           1.5.1

frozenlist        1.5.0

fsspec          2024.10.0

future          1.0.0

fvcore          0.1.5.post20221221

gvhmr           1.0.0       /home/sxxzwbl/GVHMR/GVHMR

h11            0.14.0

httpcore         1.0.7

httpx           0.28.0

huggingface-hub      0.26.3

hydra-colorlog      1.2.0

hydra-core        1.3.0

hydra_zen         0.13.0

idna           3.10

imageio          2.34.1

iniconfig         2.0.0

iopath          0.1.10

ipdb           0.13.13

ipykernel         6.29.5

ipython          8.30.0

ipywidgets        8.1.5

isoduration        20.11.0

jaraco.collections    5.1.0

jaraco.context      6.0.1

jaraco.functools     4.1.0

jaraco.text        4.0.0

jedi           0.19.2

Jinja2          3.1.4

joblib          1.4.2

json5           0.10.0

jsonpointer        3.0.0

jsonschema        4.23.0

jsonschema-specifications 2024.10.1

jupyter          1.1.1

jupyter_client      8.6.3

jupyter-console      6.6.3

jupyter_core       5.7.2

jupyter-events      0.10.0

jupyter-lsp        2.2.5

jupyter_server      2.14.2

jupyter_server_terminals 0.5.3

jupyterlab        4.2.6

jupyterlab_pygments    0.3.0

jupyterlab_server     2.27.3

jupyterlab_widgets    3.0.13

kiwisolver        1.4.7

lapx           0.5.11.post1

lazy_loader        0.4

lightning         2.3.0

lightning-utilities    0.11.9

llvmlite         0.43.0

loguru          0.7.3

lxml           5.3.0

manifold3d        3.0.0

mapbox_earcut       1.0.2

markdown-it-py      3.0.0

MarkupSafe        3.0.2

matplotlib        3.9.3

matplotlib-inline     0.1.7

mdurl           0.1.2

mistune          3.0.2

more-itertools      10.5.0

mpmath          1.3.0

msgspec          0.18.6

multidict         6.1.0

mypy-extensions      1.0.0

nbclient         0.10.1

nbconvert         7.16.4

nbformat         5.10.4

nest-asyncio       1.6.0

networkx         3.4.2

nodeenv          1.9.1

notebook         7.2.2

notebook_shim       0.2.4

numba           0.60.0

numpy           1.23.5

nvidia-cublas-cu12    12.1.3.1

nvidia-cuda-cupti-cu12  12.1.105

nvidia-cuda-nvrtc-cu12  12.1.105

nvidia-cuda-runtime-cu12 12.1.105

nvidia-cudnn-cu12     8.9.2.26

nvidia-cufft-cu12     11.0.2.54

nvidia-curand-cu12    10.3.2.106

nvidia-cusolver-cu12   11.4.5.107

nvidia-cusparse-cu12   12.1.0.106

nvidia-nccl-cu12     2.20.5

nvidia-nvjitlink-cu12   12.6.85

nvidia-nvtx-cu12     12.1.105

omegaconf         2.3.0

opencv-python       4.10.0.84

overrides         7.7.0

packaging         24.2

pandas          2.2.3

pandocfilters       1.5.1

parso           0.8.4

pathspec         0.12.1

pexpect          4.9.0

pillow          11.0.0

pip            24.2

platformdirs       4.3.6

pluggy          1.5.0

plyfile          1.1

portalocker        3.0.0

portend          3.2.0

prometheus_client     0.21.1

prompt_toolkit      3.0.48

propcache         0.2.1

protobuf         5.29.0

psutil          6.1.0

ptyprocess        0.7.0

pure_eval         0.2.3

py-cpuinfo        9.0.0

pycollada         0.8

pycparser         2.22

Pygments         2.18.0

pyliblzfse        0.4.1

pyparsing         3.2.0

pypose          0.6.9

pytest          8.3.4

python-dateutil      2.9.0.post0

python-json-logger    2.0.7

pytorch-lightning     2.4.0

pytorch3d         0.7.6

pytz           2024.2

PyYAML          6.0.2

pyzmq           26.2.0

referencing        0.35.1

requests         2.32.3

rfc3339-validator     0.1.4

rfc3986-validator     0.1.1

rich           13.9.4

rpds-py          0.22.1

Rtree           1.3.0

safetensors        0.4.5

scikit-image       0.24.0

scipy           1.14.1

seaborn          0.13.2

Send2Trash        1.8.3

setuptools        75.1.0

shapely          2.0.6

shtab           1.7.1

six            1.16.0

smplx           0.1.28

sniffio          1.3.1

soupsieve         2.6

stack-data        0.6.3

svg.path         6.3

sympy           1.13.3

tabulate         0.9.0

tempora          5.7.0

tensorboardX       2.6.2.2

termcolor         2.5.0

terminado         0.18.1

tifffile         2024.9.20

timm           0.9.12

tinycss2         1.4.0

tomli           2.2.1

torch           2.3.0+cu121

torch_scatter       2.1.2+pt23cu121

torchmetrics       1.6.0

torchvision        0.18.0+cu121

tornado          6.4.2

tqdm           4.67.1

traitlets         5.14.3

transforms3d       0.4.2

trimesh          4.5.3

triton          2.3.0

typeguard         4.4.1

types-python-dateutil   2.9.0.20241003

typing_extensions     4.12.2

tyro           0.9.2

tzdata          2024.2

ultralytics        8.2.42

ultralytics-thop     2.0.12

uri-template       1.3.0

urllib3          2.2.3

vhacdx          0.0.8.post1

viser           0.2.19

wcwidth          0.2.13

webcolors         24.11.1

webencodings       0.5.1

websocket-client     1.8.0

websockets        14.1

wheel           0.44.0

widgetsnbextension    4.0.13

wis3d           1.0.1

xatlas          0.0.9

xxhash          3.5.0

yacs           0.1.8

yarl           1.18.3

yourdfpy         0.0.56

zc.lockfile        3.0.post1

动作导入blender+写出betas数据

import bpy

import os

import math

import numpy as np

from mathutils import Matrix, Vector, Quaternion, Euler

import json

import pickle

#### Change the variables here

###############################

smpl_model = r"/home/sxxzwbl/GVHMR/GVHMR/inputs/checkpoints/body_models/smpl/basicModel_f_lbs_10_207_0_v1.0.2.fbx" #写入自己的路径

file = r'/home/sxxzwbl/GVHMR/GVHMR/outputs/hmr4d_results.pt_person-1.pkl'  #写入自己的路径

high_from_floor = 1.5 #离地高度自定义

##############################

with open(file, 'rb') as handle:

  results = pickle.load(handle)

# 保存 betas 数据为文本文件

def save_betas_to_file(betas, file_path):

  with open(file_path, 'w') as f:

    for value in betas:

      f.write(f"{value}\n")

# 获取 betas 数据并保存

betas = results['smpl_params_global']['betas']

output_file_path = os.path.splitext(file)[0] + '_betas.txt'

save_betas_to_file(betas, output_file_path)

print(f"betas 数据已保存到 {output_file_path}")

part_match_custom_less2 = {'root': 'root', 'bone_00': 'Pelvis', 'bone_01': 'L_Hip', 'bone_02': 'R_Hip', 

          'bone_03': 'Spine1', 'bone_04': 'L_Knee', 'bone_05': 'R_Knee', 'bone_06': 'Spine2', 

          'bone_07': 'L_Ankle', 'bone_08': 'R_Ankle', 'bone_09': 'Spine3', 'bone_10': 'L_Foot', 

          'bone_11': 'R_Foot', 'bone_12': 'Neck', 'bone_13': 'L_Collar', 'bone_14': 'R_Collar', 

          'bone_15': 'Head', 'bone_16': 'L_Shoulder', 'bone_17': 'R_Shoulder', 'bone_18': 'L_Elbow', 

          'bone_19': 'R_Elbow', 'bone_20': 'L_Wrist', 'bone_21': 'R_Wrist',

          'bone_22': 'L_Hand', 'bone_23': 'R_Hand',

          }

### INICIO --- Inseri para utilizar no WHAM

def Rodrigues(rotvec):

  theta = np.linalg.norm(rotvec)

  r = (rotvec/theta).reshape(3, 1) if theta > 0. else rotvec

  cost = np.cos(theta)

  mat = np.asarray([[0, -r[2], r[1]],

          [r[2], 0, -r[0]],

          [-r[1], r[0], 0]],dtype=object) #adicionei "",dtype=object" por que estava dando erro

  return(cost*np.eye(3) + (1-cost)*r.dot(r.T) + np.sin(theta)*mat)

def rodrigues2bshapes(pose):

  rod_rots = np.asarray(pose).reshape(22, 3)

  mat_rots = [Rodrigues(rod_rot) for rod_rot in rod_rots]

  bshapes = np.concatenate([(mat_rot - np.eye(3)).ravel()

              for mat_rot in mat_rots[1:]])

  return(mat_rots, bshapes)

############

def get_global_pose(global_pose, arm_ob, frame=None):

  arm_ob.pose.bones['f_avg_root'].rotation_quaternion.w = 0.0

  arm_ob.pose.bones['f_avg_root'].rotation_quaternion.x = -1.0

  bone = arm_ob.pose.bones['f_avg_Pelvis']

  # if frame is not None:

  #   bone.keyframe_insert('rotation_quaternion', frame=frame)

  root_orig = arm_ob.pose.bones['f_avg_root'].rotation_quaternion

  mw_orig = arm_ob.matrix_world.to_quaternion()

  pelvis_quat = Matrix(global_pose[0]).to_quaternion()

  bone.rotation_quaternion = pelvis_quat

  bone.keyframe_insert('rotation_quaternion', frame=frame)

  pelvis_applyied = arm_ob.pose.bones['f_avg_Pelvis'].rotation_quaternion

  bpy.context.view_layer.update()

  rot_world_orig = root_orig @ pelvis_applyied @ mw_orig #pegar a rotacao em relacao ao mundo

  return rot_world_orig

###############

# apply trans pose and shape to character

# def apply_trans_pose_shape(trans, body_pose, shape, ob, arm_ob, obname, scene, cam_ob, frame=None):

def apply_trans_pose_shape(trans, body_pose, arm_ob, obname, frame=None):

  # transform pose into rotation matrices (for pose) and pose blendshapes

#  if self.option in [2,3]: #​para WHAM ou slahmr

#    mrots, bsh = rodrigues2bshapes(body_pose)

#  else: #​para 4d humans

#    mrots = body_pose

  mrots, bsh = rodrigues2bshapes(body_pose)

#  mrots = body_pose

  part_bones = part_match_custom_less2

   

#  trans = Vector((trans[0],trans[1]-2.2,trans[2]))

  trans = Vector((trans[0],trans[1]-high_from_floor,trans[2]))

   

  # print('frame in apply pose:', frame)

  arm_ob.pose.bones['f_avg_Pelvis'].location = trans

  arm_ob.pose.bones['f_avg_Pelvis'].keyframe_insert('location', frame=frame)

   

   

  arm_ob.pose.bones['f_avg_root'].rotation_quaternion.w = 0.0

  arm_ob.pose.bones['f_avg_root'].rotation_quaternion.x = -1.0

   

   

  for ibone, mrot in enumerate(mrots):

     if ibone < 22: #incui essa parte por que no modelo que eu to usando nao tem bone para a mao

      bone = arm_ob.pose.bones['f_avg_'+part_bones['bone_%02d' % ibone]]

      bone.rotation_quaternion = Matrix(mrot).to_quaternion()

       

      if frame is not None:

        bone.keyframe_insert('rotation_quaternion', frame=frame)

import os

def init_scene(scene, params, gender='female', angle=0):

   

  # path_fbx = os.path.join(path_code,smpl_model)

#  smpl_model = r"D:\AI_Stuff\GVHMR\basicModel_m_lbs_10_207_0_v1.0.2.fbx"

  path_fbx = smpl_model

  bpy.ops.import_scene.fbx(filepath=path_fbx, axis_forward='-Y', axis_up='-Z', global_scale=100)#, automatic_bone_orientation=True)

#  arm_obj = bpy.context.selected_objects[0]

  arm_ob = bpy.context.selected_objects[0]

   

  obj_gender = 'f'

  obname = '%s_avg' % obj_gender

  ob = bpy.data.objects[obname]

  # arm_obj = 'Armature'

#  bpy.context.scene.source = arm_obj

  print('success load')

   

  # ob.data.use_auto_smooth = False # autosmooth creates artifacts

  bpy.ops.object.select_all(action='DESELECT')

  bpy.ops.object.select_all(action='DESELECT')

  cam_ob = ''

  # ob.data.shape_keys.animation_data_clear()

  # arm_ob = bpy.data.objects[arm_obj]

#  arm_ob = context.scene.source

  arm_ob.animation_data_clear()

   

  return(ob, obname, arm_ob, cam_ob)

# qtd_frames = len(results[character]['pose'])

params = []

object_name = 'f_avg'

obj_gender = 'm'

scene = bpy.data.scenes['Scene']

ob, obname, arm_ob, cam_ob= init_scene(scene, params, obj_gender)

qtd_frames = len(results['smpl_params_global']['transl'])

print('qtd frames: ',qtd_frames)

# shape = results[character]['betas'].tolist()

for fframe in range(0,qtd_frames):

  bpy.context.scene.frame_set(fframe)

  #print('data',data)

  # trans = [0.0, 0.0, 1.521]

  trans = results['smpl_params_global']['transl'][fframe]

  # shape = data[1]['smpl'][character]['betas']

  #

  global_orient = results['smpl_params_global']['global_orient'][fframe]

  # pelvis = fixed_pelvis_quat[fframe]

  # global_orient = np.array(Quaternion(pelvis).to_matrix()).reshape(1,3,3)

  #

  ##o trtamento abaixo nao deu certo

  # rotation_x = Matrix.Rotation(math.radians(180.0),3,'X') #rodar ao redor de X

  # rotation_y = Matrix.Rotation(math.radians(90.0),3,'Y') #rodar ao redor de X

  # global_orient = global_orient @ rotation_x @rotation_y

  #

  body_pose = results['smpl_params_global']['body_pose'][fframe]

  body_pose_fim = body_pose.reshape(int(len(body_pose)/3), 3)

  final_body_pose = np.vstack([global_orient, body_pose_fim])

  # apply_trans_pose_shape(Vector(trans), final_body_pose, shape, obj,arm_ob, obname, scene, cam_ob, fframe)

  #

  #

  apply_trans_pose_shape(Vector(trans), final_body_pose, arm_ob, obname, fframe)

  bpy.context.view_layer.update()

arm_ob.pose.bones['f_avg_root'].rotation_quaternion.w = 1.0

arm_ob.pose.bones['f_avg_root'].rotation_quaternion.x = 0.0

arm_ob.pose.bones['f_avg_root'].rotation_quaternion.y = 0.0

arm_ob.pose.bones['f_avg_root'].rotation_quaternion.z = 0.0

arm_ob.pose.bones['f_avg_Pelvis'].constraints.new('COPY_LOCATION')

# arm_ob.pose.bones["f_avg_Pelvis"].constraints["Copy Location"].target = armature_ref

arm_ob.pose.bones["f_avg_Pelvis"].constraints[0].target = arm_ob

arm_ob.pose.bones["f_avg_Pelvis"].constraints[0].subtarget = "f_avg_Pelvis"

# arm_ob.pose.bones["f_avg_Pelvis"].constraints["Copy Location"].subtarget = "f_avg_Pelvis"

arm_ob.pose.bones['f_avg_Pelvis'].constraints.new('COPY_ROTATION')

# arm_ob.pose.bones["f_avg_Pelvis"].constraints["Copy Rotation"].target = armature_ref

arm_ob.pose.bones["f_avg_Pelvis"].constraints[1].target = arm_ob

# arm_ob.pose.bones["f_avg_Pelvis"].constraints["Copy Rotation"].subtarget = "f_avg_Pelvis"

arm_ob.pose.bones["f_avg_Pelvis"].constraints[1].subtarget = "f_avg_Pelvis"  

导入第一帧betas到blendershape000-009

import bpy

import os

import re

# 定义 .pkl 文件所在的文件夹路径(你可以在这里修改路径)

pkl_folder = "/home/sxxzwbl/GVHMR/GVHMR/outputs/pkl_blender/"

# 获取指定文件夹下所有 .pkl 文件的列表

pkl_files = [f for f in os.listdir(pkl_folder) if f.endswith('.pkl')]

# 检查 .pkl 文件的数量

if len(pkl_files) == 0:

  raise FileNotFoundError(f"在文件夹 '{pkl_folder}' 中未找到任何 .pkl 文件。")

elif len(pkl_files) > 1:

  raise ValueError(f"在文件夹 '{pkl_folder}' 中找到多个 .pkl 文件。脚本只支持处理一个 .pkl 文件。")

# 由于只存在一个 .pkl 文件,获取该文件的名称

pkl_filename = pkl_files[0]

# 构建对应的 .txt 文件的完整路径

txt_filename = os.path.splitext(pkl_filename)[0] + "_betas.txt" # 替换文件扩展名为 .txt

txt_path = os.path.join(pkl_folder, txt_filename)

# 检查 .txt 文件是否存在

if not os.path.exists(txt_path):

  raise FileNotFoundError(f"未找到与 '{pkl_filename}' 同名的 '{txt_filename}' 文件。")

# 读取 .txt 文件内容

with open(txt_path, 'r') as file:

  content = file.read()

# 使用正则表达式提取所有浮点数

numbers = re.findall(r'[-+]?\d*\.\d+|\d+', content)

# 将提取的字符串转换为浮点数,并取相反数

betas = [-float(num) for num in numbers]

# 打印读取到的数值(可选)

print(f"从 '{txt_filename}' 读取到的数值(取相反数后):", betas)

# 检查是否读取到足够的数值

if len(betas) < 10:

  raise ValueError(f"文件 '{txt_filename}' 中没有足够的数值来填充形态键。")

# 截取前10个数值

betas = betas[:10]

# 遍历所有选中的对象

for obj in bpy.context.selected_objects:

  if obj.type == 'MESH' and obj.data.shape_keys:

    shape_keys = obj.data.shape_keys.key_blocks

     

    # 获取对象的名称

    obj_name = obj.name

     

    # 构建 .pkl 文件的完整路径

    pkl_path = os.path.join(pkl_folder, pkl_filename)

     

    # 构建对应的 .txt 文件的完整路径

    txt_path = os.path.join(pkl_folder, txt_filename)

     

    # 读取 .txt 文件内容(如果需要再次使用)

    with open(txt_path, 'r') as file:

      content = file.read()

     

    # 使用正则表达式提取所有浮点数

    numbers = re.findall(r'[-+]?\d*\.\d+|\d+', content)

     

    # 将提取的字符串转换为浮点数,并取相反数

    betas = [-float(num) for num in numbers]

     

    # 打印读取到的数值(可选)

    print(f"从 '{txt_filename}' 读取到的数值(取相反数后):", betas)

     

    # 检查是否读取到足够的数值

    if len(betas) < 10:

      print(f"文件 '{txt_filename}' 中没有足够的数值来填充形态键。")

      continue # 跳过当前对象,继续下一个

     

    # 截取前10个数值

    betas = betas[:10]

     

    # 遍历指定范围内的形态键 (从shape000到shape009)

    for i in range(10):

      key_name = f"Shape{i:03d}" # 生成形态键名称,如"Shape000", "Shape001", ..., "Shape009"

       

      # 检查是否存在指定名称的形态键

      if key_name in shape_keys:

        shape_key = shape_keys[key_name]

         

        # 设置范围下限和上限

        shape_key.slider_min = -3.0

        shape_key.slider_max = 3.0

         

        # 设置当前值

        shape_key.value = betas[i]

         

        # 打印结果(可选)

        print(f"对象 '{obj.name}' 的形态键 '{key_name}' 的值已设置为 {shape_key.value},范围设置为 [-3, 3]")

      else:

        print(f"对象 '{obj.name}' 中未找到形态键 '{key_name}'")