blender 绘制离散顶点, SMPL骨架绘制

article/2025/9/28 4:54:40

给定一些点,如何绘制出来,借助 blender 看下效果。纠结于 unity 还是 blender, 最终还是 blender 了。

目前还都不太满意,思路一比较靠谱,但是需要更复杂的计算 思路一,第二版,已完成,吐血制作!

文章目录

  • blender 一些快捷键
  • 思路一: 绘制顶点,和边
    • 第二版,最终版
    • 带有背景和光照的
  • 思路二: 创建bvh 文件
  • 思路三,骨骼驱动

blender 一些快捷键

alt+p 运行脚本
h , alt+h 隐藏与显示

shift+c , / 如果找不到模型,可以通过这两个快捷键,或者 视图->局部视图 来选择。
f3 可以用于查找命令

1 3 7 三视图

blender 急救箱

思路一: 绘制顶点,和边

就是用物体代表顶点和边,并添加各自的材质。 动作通过设置关键帧,其中顶点比较好办就设置一个位置就行,边的话还需要计算旋转,就是旋转到点之间的向量

第一版,只画了点

import numpy as np
import os
import bpy# short-key alt+pfrom bpy import context
import builtins as __builtin__def console_print(*args, **kwargs):for a in context.screen.areas:if a.type == 'CONSOLE':c = {}c['area'] = ac['space_data'] = a.spaces.activec['region'] = a.regions[-1]c['window'] = context.windowc['screen'] = context.screens = " ".join([str(arg) for arg in args])for line in s.split("\n"):bpy.ops.console.scrollback_append(c, text=line)def print(*args, **kwargs):"""Console print() function."""console_print(*args, **kwargs) # to py consoles__builtin__.print(*args, **kwargs) # to system consolethis_file="D:/myprog/2021-10-25-dance/7.show_points/tmp/draw_by_blender.py"os.chdir( os.path.dirname(os.path.realpath(this_file)) )#basic info about limb composition
joint_relationship18 = [[1, 2], [1, 5], [2, 3], [3, 4], [5, 6], [6, 7], [1, 8], [8, 9], [9, 10],[1, 11], [11, 12], [12, 13], [1, 0], [0, 14], [14, 16], [0, 15], [15, 17],[2, 16], [5, 17]]
joint_relationship24 = [[0,1],[1,4],[4,7],[7,10],[0,2],[2,5],[5,8],[8,11],[0,3],[3,6],[6,9],[9,12],[12,15],[13,16],[16,18],[18,20],[20,22],[9,14],[14,17],[17,19],[19,21],[21,23]
]#for plot usage
colors_18 = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0],[85, 255, 0], [0, 255, 0], [0, 255, 85], [0, 255, 170], [0, 255, 255],[0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], [170, 0, 255],[255, 0, 255], [255, 0, 170], [255, 0, 85]]joint_relationship = joint_relationship24j3d=np.load("joint3d.npy")[:100] * 10
bpy.context.scene.render.fps = 60print(j3d.shape)# https://github.com/Apress/blender-python-api/blob/master/ch03_code/3-4.py
# https://blender.stackexchange.com/questions/134826/how-do-you-specify-the-name-of-a-primitive-created-via-the-python-api
# https://danielhnyk.cz/creating-animation-blender-using-python/# Must start in object modeprint( len( bpy.data.objects) )
if len(bpy.data.objects) > 0:#bpy.ops.object.mode_set(mode='OBJECT')bpy.ops.object.select_all(action='SELECT')bpy.ops.object.delete()#bpy.ops.wm.open_mainfile(filepath=bpy.data.filepath)#for obj in bpy.context.scene.objects:
#     if obj.type == 'MESH':
#         obj.select = True
#     else:
#         obj.select = False
#bpy.ops.object.delete()#print(111)
for m in bpy.data.materials:bpy.data.materials.remove(m)obj_scale = 0.5 
for i in range(24):bpy.ops.mesh.primitive_cube_add(location=(i,i,i), size=obj_scale )bpy.context.active_object.name = f'Joint{i}'object = bpy.data.objects.get( f'Joint{i}' )mat = bpy.data.materials.new(f'Jmat{i}')r, g, b = colors_18[ i%18 ][::-1]mat.diffuse_color = ( r/255, g/255, b/255, 1 )#bpy.data.materials[f'Jmat{i}'].node_tree.nodes["Principled BSDF"].inputs[0].default_value=(r/255, g/255, b/255, 1)object.data.materials.append(mat)# drop
'''
for i, rel in enumerate( joint_relationship ):bpy.ops.mesh.primitive_cylinder_add(radius=1, depth=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(obj_scale, obj_scale, obj_scale))bpy.context.active_object.name = f'JR{i}'object = bpy.data.objects.get( f'JR{i}' )mat = bpy.data.materials.new(f'JRmat{i}')r, g, b = colors_18[ i%18 ][::-1]mat.diffuse_color = ( r/255, g/255, b/255, 1 )object.data.materials.append(mat)
'''   FPS=60
for rcnt, frame in enumerate( j3d ):frame=frame.reshape(24, 3)for i in range(24):joint = bpy.data.objects.get( f'Joint{i}' )joint.location = frame[i]joint.keyframe_insert(data_path="location", frame=rcnt)#joint = bpy.data.objects.get( f'Joint{0}' )
#bpy.context.scene.objects.active = jointbpy.context.scene.frame_current = 1
bpy.context.scene.frame_end = len(j3d)

效果
在这里插入图片描述

第二版,最终版

#from _chj.comm.pic import *
import numpy as np
import os
import bpy
from scipy.spatial.transform import Rotation as sciR
# short-key alt+p
from mathutils import Eulerfrom bpy import context
import builtins as __builtin__def console_print(*args, **kwargs):for a in context.screen.areas:if a.type == 'CONSOLE':c = {}c['area'] = ac['space_data'] = a.spaces.activec['region'] = a.regions[-1]c['window'] = context.windowc['screen'] = context.screens = " ".join([str(arg) for arg in args])for line in s.split("\n"):bpy.ops.console.scrollback_append(c, text=line)def print(*args, **kwargs):"""Console print() function."""console_print(*args, **kwargs) # to py consoles__builtin__.print(*args, **kwargs) # to system consoledef rotation_matrix_from_vectors(vec1, vec2):""" Find the rotation matrix that aligns vec1 to vec2:param vec1: A 3d "source" vector:param vec2: A 3d "destination" vector:return mat: A transform matrix (3x3) which when applied to vec1, aligns it with vec2."""a, b = (vec1 / np.linalg.norm(vec1)).reshape(3), (vec2 / np.linalg.norm(vec2)).reshape(3)v = np.cross(a, b)if any(v):  # if not all zeros thenc = np.dot(a, b)s = np.linalg.norm(v)kmat = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])return np.eye(3) + kmat + kmat.dot(kmat) * ((1 - c) / (s ** 2))else:return np.eye(3)  # cross of all zeros only occurs on identical directionsthis_file="D:/myprog/2021-10-25-dance/7.show_points/tmp/draw_by_blender.py"os.chdir( os.path.dirname(os.path.realpath(this_file)) )#basic info about limb composition
joint_relationship18 = [[1, 2], [1, 5], [2, 3], [3, 4], [5, 6], [6, 7], [1, 8], [8, 9], [9, 10],[1, 11], [11, 12], [12, 13], [1, 0], [0, 14], [14, 16], [0, 15], [15, 17],[2, 16], [5, 17]]
joint_relationship24 = [[0,1],[1,4],[4,7],[7,10],[0,2],[2,5],[5,8],[8,11],[0,3],[3,6],[6,9],[9,12],[12,15],[9,13],[13,16],[16,18],[18,20],[20,22],[9,14],[14,17],[17,19],[19,21],[21,23]
]#for plot usage
colors_18 = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0],[85, 255, 0], [0, 255, 0], [0, 255, 85], [0, 255, 170], [0, 255, 255],[0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], [170, 0, 255],[255, 0, 255], [255, 0, 170], [255, 0, 85]]joint_relationship = joint_relationship24cord_cvt = sciR.from_rotvec( np.array([1, 0, 0])*np.deg2rad(90) ).as_matrix()
j3d=np.load("joint3d.npy")[1400:1500] * 5
j3d = np.einsum( "cd,bjd->bjc", cord_cvt, j3d.reshape(-1, 24, 3) )
j3d -= j3d[:1, :1].copy()bpy.context.scene.render.fps = 60print(j3d.shape)# https://github.com/Apress/blender-python-api/blob/master/ch03_code/3-4.py
# https://blender.stackexchange.com/questions/134826/how-do-you-specify-the-name-of-a-primitive-created-via-the-python-api
# https://danielhnyk.cz/creating-animation-blender-using-python/# Must start in object modeprint( len( bpy.data.objects) )
if len(bpy.data.objects) > 0:bpy.ops.object.mode_set(mode='OBJECT')bpy.ops.object.select_all(action='SELECT')bpy.ops.object.delete()for m in bpy.data.materials:bpy.data.materials.remove(m)# cam https://blender.stackexchange.com/questions/12318/get-position-of-focus-point-of-camera
cam_data = bpy.data.cameras.new(name="cam")  
cam_ob = bpy.data.objects.new(name="Kamerka", object_data=cam_data)  
bpy.context.collection.objects.link(cam_ob)cam_ob.data.lens = 15
cam_ob.location = ( 0, -10, 0 )
cam_ob.rotation_euler = Euler( ( 90/180*np.pi, 0, 0 ) , 'XYZ')bpy.context.scene.camera = cam_obobj_scale = 0.15 
for i in range(24):#bpy.ops.mesh.primitive_cube_add(location=(i,i,i), size=obj_scale )bpy.ops.mesh.primitive_uv_sphere_add(radius=obj_scale/2, location=(i,i,i))bpy.context.active_object.name = f'Joint{i}'object = bpy.data.objects.get( f'Joint{i}' )mat = bpy.data.materials.new(f'Jmat{i}')r, g, b = colors_18[ i%18 ][::-1]#mat.diffuse_color = ( r/255, g/255, b/255, 1 )mat.diffuse_color = ( 0, 0, 0, 1 )#bpy.data.materials[f'Jmat{i}'].node_tree.nodes["Principled BSDF"].inputs[0].default_value=(r/255, g/255, b/255, 1)object.data.materials.append(mat)first_pos = j3d[0]
for i, rel in enumerate( joint_relationship ):jlen = first_pos[ rel ]depth = np.linalg.norm( jlen[0] - jlen[1] ) / obj_scalebpy.ops.mesh.primitive_cylinder_add(radius=obj_scale, depth=depth, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(obj_scale, obj_scale, obj_scale))bpy.context.active_object.name = f'JR{i}'object = bpy.data.objects.get( f'JR{i}' )object.rotation_mode='XYZ'mat = bpy.data.materials.new(f'JRmat{i}')r, g, b = colors_18[ i%18 ][::-1]mat.diffuse_color = ( r/255, g/255, b/255, 1 )object.data.materials.append(mat)FPS=60
for rcnt, frame in enumerate( j3d ):frame=frame.reshape(24, 3)for i in range(24):joint = bpy.data.objects.get( f'Joint{i}' )joint.location = frame[i]joint.keyframe_insert(data_path="location", frame=rcnt)for i, rel in enumerate( joint_relationship ):object = bpy.data.objects.get( f'JR{i}' )jlen = frame[ rel ]matR = rotation_matrix_from_vectors( np.array([0,0,1]), jlen[1] - jlen[0] )eulr= sciR.from_matrix( matR ).as_euler('xyz', degrees=False)object.rotation_euler = Euler(tuple(eulr.tolist()), 'XYZ')#object.rotation_euler = Euler( ( 30/180*np.pi, 0, 0 ), 'XYZ') # object.keyframe_insert(data_path="rotation_euler", frame=rcnt)object.location = (jlen[0] + jlen[1]) / 2object.keyframe_insert(data_path="location", frame=rcnt)#joint = bpy.data.objects.get( f'Joint{0}' )
#bpy.context.scene.objects.active = jointbpy.context.scene.frame_current = 1
bpy.context.scene.frame_end = len(j3d)

在这里插入图片描述

带有背景和光照的

#from _chj.comm.pic import *
import numpy as np
import os
import bpy
from scipy.spatial.transform import Rotation as sciR
# short-key alt+p
from mathutils import Eulerfrom bpy import context
import builtins as __builtin__def console_print(*args, **kwargs):for a in context.screen.areas:if a.type == 'CONSOLE':c = {}c['area'] = ac['space_data'] = a.spaces.activec['region'] = a.regions[-1]c['window'] = context.windowc['screen'] = context.screens = " ".join([str(arg) for arg in args])for line in s.split("\n"):bpy.ops.console.scrollback_append(c, text=line)def print(*args, **kwargs):"""Console print() function."""console_print(*args, **kwargs) # to py consoles__builtin__.print(*args, **kwargs) # to system consoledef rotation_matrix_from_vectors(vec1, vec2):""" Find the rotation matrix that aligns vec1 to vec2:param vec1: A 3d "source" vector:param vec2: A 3d "destination" vector:return mat: A transform matrix (3x3) which when applied to vec1, aligns it with vec2."""a, b = (vec1 / np.linalg.norm(vec1)).reshape(3), (vec2 / np.linalg.norm(vec2)).reshape(3)v = np.cross(a, b)if any(v):  # if not all zeros thenc = np.dot(a, b)s = np.linalg.norm(v)kmat = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])return np.eye(3) + kmat + kmat.dot(kmat) * ((1 - c) / (s ** 2))else:return np.eye(3)  # cross of all zeros only occurs on identical directionsdef replace_with_emission(node, node_tree):new_node = node_tree.nodes.new('ShaderNodeEmission')connected_sockets_out = []sock = node.inputs[0]if len(sock.links)>0:color_link = sock.links[0].from_socketelse:color_link=Nonedefaults_in = sock.default_value[:]for sock in node.outputs:if len(sock.links)>0:connected_sockets_out.append( sock.links[0].to_socket)else:connected_sockets_out.append(None)#print( defaults_in )new_node.location = (node.location.x, node.location.y)if color_link is not None:node_tree.links.new(new_node.inputs[0], color_link)new_node.inputs[0].default_value = defaults_inif connected_sockets_out[0] is not None:node_tree.links.new(connected_sockets_out[0], new_node.outputs[0])def material_diffuse_to_emission(mat):doomed=[]for node in mat.node_tree.nodes:if node.type=='BSDF_DIFFUSE':replace_with_emission(node, mat.node_tree)doomed.append(node)# wait until we are done iterating and adding before we start wrecking thingsfor node in doomed:mat.node_tree.nodes.remove(node)def get_mat(mat, rgb , type="emission" ):r,g,b = rgbmat.use_nodes = Trueif mat.node_tree:mat.node_tree.links.clear()mat.node_tree.nodes.clear()nodes = mat.node_tree.nodeslinks = mat.node_tree.linksoutput = nodes.new(type='ShaderNodeOutputMaterial')if type == "diffuse":shader = nodes.new(type='ShaderNodeBsdfDiffuse')nodes["Diffuse BSDF"].inputs[0].default_value = (r, g, b, 1)elif type == "emission":shader = nodes.new(type='ShaderNodeEmission')shader.name='Emission'#object.data.materials.append(mat)nodes["Emission"].inputs[0].default_value = (r, g, b, 1)nodes["Emission"].inputs[1].default_value = 1elif type == "glossy":shader = nodes.new(type='ShaderNodeBsdfGlossy')nodes["Glossy BSDF"].inputs[0].default_value = (r, g, b, 1)nodes["Glossy BSDF"].inputs[1].default_value = 0links.new(shader.outputs[0], output.inputs[0])this_file="D:/myprog/2021-10-25-dance/7.show_points/tmp/draw_by_blender.py"os.chdir( os.path.dirname(os.path.realpath(this_file)) )#basic info about limb composition
joint_relationship18 = [[1, 2], [1, 5], [2, 3], [3, 4], [5, 6], [6, 7], [1, 8], [8, 9], [9, 10],[1, 11], [11, 12], [12, 13], [1, 0], [0, 14], [14, 16], [0, 15], [15, 17],[2, 16], [5, 17]]
joint_relationship24 = [[0,1],[1,4],[4,7],[7,10],[0,2],[2,5],[5,8],[8,11],[0,3],[3,6],[6,9],[9,12],[12,15],[9,13],[13,16],[16,18],[18,20],[20,22],[9,14],[14,17],[17,19],[19,21],[21,23]
]#for plot usage
colors_18 = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0],[85, 255, 0], [0, 255, 0], [0, 255, 85], [0, 255, 170], [0, 255, 255],[0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], [170, 0, 255],[255, 0, 255], [255, 0, 170], [255, 0, 85]]joint_relationship = joint_relationship24cord_cvt = sciR.from_rotvec( np.array([1, 0, 0])*np.deg2rad(90) ).as_matrix()
j3d=np.load("joint3d.npy")[1400:1500] * 5
j3d = np.einsum( "cd,bjd->bjc", cord_cvt, j3d.reshape(-1, 24, 3) )
j3d -= j3d[:1, :1].copy()fv="D:/myprog/2021-10-25-dance/7.show_points/tmp/res.mp4"
FPS=60
fv=None
print(j3d.shape)# https://github.com/Apress/blender-python-api/blob/master/ch03_code/3-4.py
# https://blender.stackexchange.com/questions/134826/how-do-you-specify-the-name-of-a-primitive-created-via-the-python-api
# https://danielhnyk.cz/creating-animation-blender-using-python/
# https://blender.stackexchange.com/questions/91108/using-python-to-create-cuboid-with-a-hole-in-the-centre# Must start in object modeprint( len( bpy.data.objects) )
if len(bpy.data.objects) > 0:try:bpy.ops.object.mode_set(mode='OBJECT')except:passbpy.ops.object.select_all(action='SELECT')bpy.ops.object.delete()for m in bpy.data.materials:bpy.data.materials.remove(m)# cam https://blender.stackexchange.com/questions/12318/get-position-of-focus-point-of-camera
cam_data = bpy.data.cameras.new(name="cam")  
cam_ob = bpy.data.objects.new(name="Kamerka", object_data=cam_data)  
bpy.context.collection.objects.link(cam_ob)cam_ob.data.lens = 15
cam_ob.location = ( 0, -10, 0 )
cam_ob.rotation_euler = Euler( ( 90/180*np.pi, 0, 0 ) , 'XYZ')bpy.context.scene.camera = cam_ob##### light
bpy.ops.object.light_add(type='AREA', radius=10, location=(0, -30, 20), rotation=(60/180*np.pi, 0, 0))
light_ob = bpy.context.object
light_ob.name = 'light_area'
light_ob =  bpy.data.objects['light_area']
light_ob.data.energy = 500
#bpy.context.active_object.name = 'light_area' # both ok#### background
bpy.ops.mesh.primitive_plane_add(size=100, location=(0, 30, 0), rotation=(np.pi/2, 0,0))
bpy.context.active_object.name = 'background'
object = bpy.data.objects.get( 'background' )
mat = bpy.data.materials.new(f'back_mat')
#mat.diffuse_color = ( 1, 1, 1, 1 ) # buliang2
get_mat(mat, (0.85,0.85,0.85))
object.data.materials.append(mat)obj_scale = 0.2 
for i in range(24):#bpy.ops.mesh.primitive_cube_add(location=(i,i,i), size=obj_scale )bpy.ops.mesh.primitive_uv_sphere_add(radius=obj_scale/2, location=(i,i,i))bpy.context.active_object.name = f'Joint{i}'object = bpy.data.objects.get( f'Joint{i}' )mat = bpy.data.materials.new(f'Jmat{i}')r, g, b = colors_18[ i%18 ][::-1]#mat.diffuse_color = ( r/255, g/255, b/255, 1 )mat.diffuse_color = ( 0, 0, 0, 1 )#bpy.data.materials[f'Jmat{i}'].node_tree.nodes["Principled BSDF"].inputs[0].default_value=(r/255, g/255, b/255, 1)object.data.materials.append(mat)first_pos = j3d[0]
for i, rel in enumerate( joint_relationship ):jlen = first_pos[ rel ]depth = np.linalg.norm( jlen[0] - jlen[1] ) / obj_scalebpy.ops.mesh.primitive_cylinder_add(radius=obj_scale, depth=depth, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(obj_scale, obj_scale, obj_scale))bpy.context.active_object.name = f'JR{i}'object = bpy.data.objects.get( f'JR{i}' )object.rotation_mode='XYZ'mat = bpy.data.materials.new(f'JRmat{i}')r, g, b = colors_18[ i%18 ][::-1]r, g, b = r/255, g/255, b/255#mat.diffuse_color = ( r, g, b, 1 )get_mat(mat, (r,g,b))#breakobject.data.materials.append(mat)FPS=60
for rcnt, frame in enumerate( j3d ):frame=frame.reshape(24, 3)for i in range(24):joint = bpy.data.objects.get( f'Joint{i}' )joint.location = frame[i]joint.keyframe_insert(data_path="location", frame=rcnt)for i, rel in enumerate( joint_relationship ):object = bpy.data.objects.get( f'JR{i}' )jlen = frame[ rel ]matR = rotation_matrix_from_vectors( np.array([0,0,1]), jlen[1] - jlen[0] )eulr= sciR.from_matrix( matR ).as_euler('xyz', degrees=False)object.rotation_euler = Euler(tuple(eulr.tolist()), 'XYZ')#object.rotation_euler = Euler( ( 30/180*np.pi, 0, 0 ), 'XYZ') # object.keyframe_insert(data_path="rotation_euler", frame=rcnt)object.location = (jlen[0] + jlen[1]) / 2object.keyframe_insert(data_path="location", frame=rcnt)#joint = bpy.data.objects.get( f'Joint{0}' )
#bpy.context.scene.objects.active = jointbpy.context.scene.frame_current = 1
bpy.context.scene.frame_end = len(j3d)def set_render_for_video(fv, fps=30, w=640, h=360 ):r = bpy.context.scene.render#r.alpha_mode = 'SKY'r.filepath = fv#scene.render.image_settings.file_format = 'PNG' # set output format to .pngr.image_settings.file_format = 'FFMPEG' # set output format to .png#r.ffmpeg.format = 'MPEG4'r.fps = fpsr.resolution_x = wr.resolution_y = hr.ffmpeg.format = 'AVI'r.ffmpeg.audio_codec = 'MP3'#bpy.ops.screen.animation_play()bpy.ops.render.render(animation=True)if fv:set_render_for_video(fv, fps=FPS, w=640, h=360 )print("render finished")

在这里插入图片描述

思路二: 创建bvh 文件

这个主要是骨骼朝向不好看, 感觉对 bvh 不了解,下面的write_bvh_v1 和 write_bvh 都没有太大区别。 也不清楚 bvh 为什么有 channel position X Y Z

from _chj.comm.pic import *
os.chdir( os.path.dirname(os.path.realpath(__file__)) )def main():#f2_save_offset()f1_make_bvh()def f2_save_offset():fbones="../../4.unity/debug_render/Assets/data/bones2smpl_official.txt"#ftemp="../../3.hd_lyg/5.baiye/data/smpl.bvh.template"ftemp="temp1.bvh"mp={}arr=readlines(fbones)#print(arr)flag=0nm=Nonefor line in  readlines(ftemp):e = line.split()if flag==0 and len(e) == 2 and e[0] in ['JOINT', 'ROOT']:flag = 1nm=e[1]if e[0]=='ROOT':nm='m_avg_root'elif flag==1 and len(e)==4 and e[0]=='OFFSET':flag=0mp[ nm ] = [ float(x) for x in e[1:] ]print(mp)offsets = []for e in arr:offsets.append( mp[e] )arr=np.array(offsets).astype(np.float32) / 10np.save("smpl_offsets.npy", arr)def f1_make_bvh():fpids="parents.txt"fjoint="joint3d.npy"fout="a.bvh"foffsets="smpl_offsets.npy"pids=np.loadtxt(fpids, delimiter=',')offsets=np.load(foffsets).astype(str).tolist()print( pids )mp={}for i, pid in enumerate(pids):mp[i] = Node(i)mp[i].offset = offsets[i]if pid==-1: assert i==0else:pnode = mp[ pid ]pnode.arr.append( mp[i] )j3d = np.load(fjoint).reshape(-1, 24, 3)FPS=60j3d[:, :3] = 0N = len(j3d)with open(fout, "w") as  fp:fp.write('HIERARCHY\n')write_joint_V1(fp, mp, 0, 0)fp.write(f"MOTION\nFrames:	{N}\nFrame Time:	{1/FPS:.8f}\n")for j in j3d:a= " ".join(j.reshape(-1).astype(str).tolist())fp.write(a+"\n")def write_joint(fp, mp, id, layer):if id == 0:blank = ' '*2fp.write(f"ROOT J{id}\n")fp.write("{\n")#OFFSET 5.19116e-05 8.61242 -0.246368#    CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotationnode = mp[id]fp.write(blank+f"OFFSET {' '.join(node.offset)}\n")fp.write(blank+f"CHANNELS 3 Xposition Yposition Zposition\n")fp.write("}\n")for e in node.arr:write_joint(fp, mp, e.val, layer)else:blank = ' '*2*layerfp.write(blank+f"JOINT J{id}\n")fp.write(blank+"{\n")#OFFSET 5.19116e-05 8.61242 -0.246368#    CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotationnode = mp[id]fp.write(blank+f"OFFSET {' '.join(node.offset)}\n")fp.write(blank+'  '+f"CHANNELS 3 Xposition Yposition Zposition\n")fp.write(blank+"}\n")for e in node.arr:write_joint(fp, mp, e.val, layer)return 0def write_joint_V1(fp, mp, id, layer):if id == 0:blank = ' '*2fp.write(f"ROOT J{id}\n")fp.write("{\n")#OFFSET 5.19116e-05 8.61242 -0.246368#    CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotationnode = mp[id]fp.write(blank+f"OFFSET {' '.join(node.offset)}\n")fp.write(blank+f"CHANNELS 3 Xposition Yposition Zposition\n")for e in node.arr:write_joint(fp, mp, e.val, layer+1)fp.write("}\n")else:blank = ' '*2*layerfp.write(blank+f"JOINT J{id}\n")fp.write(blank+"{\n")#OFFSET 5.19116e-05 8.61242 -0.246368#    CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotationnode = mp[id]fp.write(blank+f"OFFSET {' '.join(node.offset)}\n")fp.write(blank+'  '+f"CHANNELS 3 Xposition Yposition Zposition\n")for e in node.arr:write_joint(fp, mp, e.val, layer+1)fp.write(blank+"}\n")return 0class Node:def __init__(self, name):self.val = nameself.arr=[]if __name__=="__main__": main()

在这里插入图片描述

思路三,骨骼驱动

进入编辑模式,然后 alt+p 取消父子关系。

import sys, os, mathimport numpy as np
import subprocess
from scipy.spatial.transform import Rotation as sciR
from mathutils import Quaternion, Vector, Matrixfrom _chj.comm.pic import *
try:import bpy
except:passfrom bpy import context
import builtins as __builtin__def console_print(*args, **kwargs):for a in context.screen.areas:if a.type == 'CONSOLE':c = {}c['area'] = ac['space_data'] = a.spaces.activec['region'] = a.regions[-1]c['window'] = context.windowc['screen'] = context.screens = " ".join([str(arg) for arg in args])for line in s.split("\n"):bpy.ops.console.scrollback_append(c, text=line)def print(*args, **kwargs):"""Console print() function."""console_print(*args, **kwargs) # to py consoles__builtin__.print(*args, **kwargs) # to system consolethis_file="D:/myprog/2021-10-25-dance/7.show_points/tmp/draw_by_blender.py"
os.chdir( os.path.dirname(os.path.realpath(this_file)) )def main():f2_render_bones_video()passclass params: FPS=60stride=1n_frame=1200 # 负数就是全部width=800 #* 3height=450 #*3ob_scale=None #0.1 #11 #Nonet_pose_scale=100kf_step = 1 #5passj3d=np.load("joint3d.npy").reshape(-1, 24, 3)[1400:1500] #* params.t_pose_scale
j3d -= j3d[:1, :1].copy()#j3d[:, :] = 0
#j3d[:] = 0.01fsmpl_rig_info="bones2smpl_official_v2.txt"
fparents="parents.txt"fbx_ske_nm = "Armature" # smpl_skeleton# https://github.com/boycehbz/HumanRegistration# 
# https://blenderartists.org/t/using-bone-translate-to-move-a-bone/511333/14def f2_render_bones_video():cord_cvt = sciR.from_rotvec( np.array([1, 0, 0])*np.deg2rad(90) ).as_matrix()pms = np.einsum( "cd,bjd->bjc", cord_cvt, j3d.reshape(-1, 24, 3) )params.pms = pmsparams.parents = np.loadtxt( fparents, delimiter="," ).astype(np.int32)params.NJ = 24del_project()bpy.ops.import_scene.fbx( filepath = 'official_smpl.fbx' )bpy.context.scene.render.fps = 60bpy.context.scene.frame_current = 1bpy.context.scene.frame_end = len(j3d)#bpy.ops.wm.save_mainfile( filepath = fblend ); exit()#bpy.ops.object.select_all(action='DESELECT')bpy.context.view_layer.objects.active = bpy.data.objects['f_avg']bpy.data.objects['f_avg'].select_set(False)#bpy.ops.object.mode_set(mode='OBJECT')ob = bpy.data.objects[fbx_ske_nm]ob.select_set(True)bpy.context.view_layer.objects.active = ob#a = ob.pose.bones['m_avg_root']#a.bone.select = True#bpy.context.object.data.bones.active = a#ob.data.bones['m_avg_root'].select = True#bpy.context.object.hide_viewport = True#bpy.ops.object.hide_view_set(unselected=False)bpy.context.view_layer.objects.active = obbpy.ops.object.mode_set(mode='EDIT')bpy.ops.armature.parent_clear(type='CLEAR')#bpy.ops.object.hide_view_set() bpy.app.handlers.frame_change_pre.clear()#bpy.app.handlers.frame_change_pre.append(frame_change_pre)bpy.context.view_layer.objects.active = ob #相当于鼠标左键选中bpy.ops.object.mode_set(mode='POSE') #切换为pose更改模式arr = [] bones = []arr_nm_pid = readlines( fsmpl_rig_info )for i in range( len(arr_nm_pid) ):bnm = arr_nm_pid[ i ]bone=ob.pose.bones[bnm] bone.rotation_mode = 'AXIS_ANGLE' #'XYZ'bones.append( bone )arr.append( bone.location.copy() )arr.append( bone.rotation_axis_angle )  # tuple arr#print( [ e[:] for e in arr ] )params.T_pose = arrparams.bones = bonesset_key_frame( len(j3d) )passdef set_key_frame(nframe):NJ = params.NJbones = params.bonesfor rcnt in range(1, nframe+1, params.kf_step ):pm = params.pms[rcnt-1]for i in range(NJ-1, -1, -1):bones[i].location = params.T_pose[i*2]#a = 3+i*4#bones[i].rotation_axis_angle = tuple(pm[a:a+4].tolist())#bones[i].keyframe_insert(data_path="rotation_axis_angle", frame=rcnt)a = pm[i]#bones[i].matrix_world.translation = Vector((a[0], a[1], a[2]))bones[i].bone.select = Truebpy.ops.transform.translate(value=tuple(a.tolist()), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)bones[i].keyframe_insert(data_path="location", frame=rcnt)rcnt+=1bpy.context.scene.frame_current = 1def del_project():if len(bpy.data.objects):bpy.ops.object.mode_set(mode='OBJECT')#bpy.ops.object.select_all(action='SELECT')#bpy.ops.object.delete()for e in bpy.data.objects:print(e)e.select_set(True)bpy.ops.object.delete() for block in bpy.data.meshes:if block.users == 0:bpy.data.meshes.remove(block)for block in bpy.data.materials:if block.users == 0:bpy.data.materials.remove(block)for block in bpy.data.textures:if block.users == 0:bpy.data.textures.remove(block)for block in bpy.data.images:if block.users == 0:bpy.data.images.remove(block)if __name__=="__main__": main()

在这里插入图片描述


http://chatgpt.dhexx.cn/article/3ioClt89.shtml

相关文章

SMPL-CN

paper-reading 为方便理解smpl文章的主要实现思想,此文为论文中文解读,资料来源zju。 日后有空,会写出论文的主要推导过程以及值得注意的重点。 摘要: 我们提出了一个人体形状和姿势相关的形状变化的学习模型,它比以前…

SMPL源码解读

这是源码的整体结构,先简单说一下各个文件里面是什么。 一、models文件 包含3个模型的.pkl文件,.pkl文件是python提供的可以以字符串的形式记录对象、变量的文件格式。这个模型里面包括了: 1.J_regressor_prior:关节回归矩阵的先验,保存形…

SMPL 人体模型简要

smpl是指2015 马普的一篇文章“SMPL: a skinned multi-person linear model”中构建的人体参数化三维模型,人体可以理解为是一个基础模型和在该模型基础上进行形变的总和,在形变基础上进行PCA,得到刻画形状的低维参数——形状参数&#xff08…

SMPL-论文解读

文章目录 创新点算法shape blend shape:pose blend shapeJoint locationSMPL model 训练过程Pose Parameter Trainingjoint regressorShape Parameter TrainingOptimization summary DMPL实验结论 论文: 《SMPL: A skinned multi-person linear model》…

SMPL模型进阶

SMPL模型是一种参数化人体模型,是马普所提出的一种人体建模方法,该方法可以进行任意的人体建模和动画驱动。这种方法与传统的LBS的最大的不同在于其提出的人体姿态影像体表形貌的方法,这种方法可以模拟人的肌肉在肢体运动过程中的凸起和凹陷。…

SMPL源代码实现和模型解读

对于SLAM的工作已经告一段落了,传统的人体动态三维重建也要告一段落了,由于课题研究的方向是基于图像\视频的人体三维重建,三维shape、pose的恢复:所以今天和大家交流的是SMPL模型,对于SMPL模型的理论部分欢迎大家交流…

SMPL简析

SMPL模型是一种参数化人体模型,是马普所提出的一种人体建模方法。这种方法可以模拟人的肌肉在肢体运动过程中的凸起和凹陷。因此可以避免人体在运动过程中的表面失真,可以精准的刻画人的肌肉拉伸以及收缩运动的形貌。 该模型可以通过身体形状参数和姿势参…

SMPL学习笔记

文章目录 前言一、SMPL概述1.形状参数( β \beta β)2.姿态参数( θ \theta θ) 二、体姿转换过程原理1.基于形状的混合成形(Shape Blend Shapes)2.基于姿态的混合成形 (Pose Blend Shapes)3.蒙皮 (Skinning) 三、具体过程分析1.基于形状的混合成形2.基于姿态的混合成形3.骨骼点…

Java的生产者消费者模型

前言 学完了线程后,我又去找了一些线程相关的练习题来练手,其中印象最深的就是生产者消费者模型这一块,为什么呢,因为它每一篇练习题里都有,开始没看懂,后面就去仔细研究了一下,哦,…

多线程之生产者消费者模型

生产者消费者模型 1.为什么要使用生产者和消费者模式2.案例 1.为什么要使用生产者和消费者模式 在线程开发中,生产者就是生产线程的线程,消费者就是消费线程的线程。在多线程开发中,如果生产者如理数据很快,消费者处理数据很慢&am…

生产者消费者模型(多线程工作)

目录 1.模型前提 2.阻塞队列(消费场所) 3. 实验 4.有关效率 1.模型前提 以单生产者对单消费者为例子: 前提一:有一个缓冲区作为消费场所。 前提二:有两种功能不同的线程分别具有消费与生产的能力。 前提三&…

生产者消费者模型的实现(线程通信)

✅作者简介:我是18shou,一名即将秋招的java实习生 🔥系列专栏:牛客刷题专栏 📃推荐一款模拟面试、刷题神器👉 [在线刷题面经模拟面试](在线面试刷题) 目录 实现一: 实现二: 实现一…

生产者消费者模型【新版】

目录 啥是生产者消费者模型? 生产者消费者模型存在问题??如何进行解决呢?? 生产者消费者模型导致的问题 什么是阻塞队列 生产者消费者模型优点 生产者消费者模型实现 Message MessageQueue 获取消息get方法 生产消息take方法 测试生产者消费者模型 啥是生产者消…

【Linux】生产者消费者模型

文章目录 一. 什么是生产者消费者模型1. 基本概念2. 三种关系3. 再次理解生产者消费者模型 二. 生产者消费者模型的优点三. 基于BlockingQueue的生产者消费者模型1. 准备工作2. 阻塞队列实现3. 测试阻塞队列4. 阻塞队列完整代码5. 关于改进阻塞队列的几点补充5.1 多生产者多消费…

生产者与消费者模型

1、【什么是生产者与消费者模型呢?】 一种重要的模型,基于等待/通知机制。生产者/消费者模型描述的是有一块缓冲区作为仓库,生产者可将产品放入仓库,消费者可以从仓库中取出产品,生产者/消费者模型关注的是以下几个点&…

生产者消费者模型——C语言代码详解

概念 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据…

【Java总结】生产者消费者模型

生产者消费者模型主要结构如下,是一个典型的线程同步的案例。下面就来使用java做几种线程同步的方式来实现以下该模型 确保一个生产者消费者模型的稳定运行的前提有以下几个 生成者应该具备持续生成的能力消费者应该具备持续消费的能力生产者的生成和消费消费有一定…

【设计模式】生产者消费者模型

带你轻松理解生产者消费者模型!生产者消费者模型可以说是同步与互斥最典型的应用场景了!文末附有模型简单实现的代码,若有疑问可私信一起讨论。 文章目录 一:为什么要使用生产者消费者模型?二:生产者消费者…

模拟生产者消费者模型

生产者消费者是多线程很经典的一个模型 牵涉三个对象:仓库、生产者、消费者 仓库代表共享变量 生产者表示在仓库生产货物 消费者表示从仓库拿出货物 实现思路:利用synchronizedwait()notify() 对生产者消费者对应的操作用synchronized关键字保证线程安全…

生产者消费者模型java实现

做题的时候遇到了生产者消费者问题,这个问题可以说是线程学习的经典题目了,就忍不住研究了一波。它描述是有一块缓冲区(队列实现)作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。在Java…