Comments (25)
Hi @Realdr4g0n, I think the code below may help you. The lbs
function would return twist_angle
. You can replace the original lbs
function in SMPL with ours.
def lbs(betas, pose, v_template, shapedirs, posedirs, J_regressor, J_regressor_h36m, parents,
lbs_weights, pose2rot=True, dtype=torch.float32):
''' Performs Linear Blend Skinning with the given shape and pose parameters
Parameters
----------
betas : torch.tensor BxNB
The tensor of shape parameters
pose : torch.tensor Bx(J + 1) * 3
The pose parameters in axis-angle format
v_template torch.tensor BxVx3
The template mesh that will be deformed
shapedirs : torch.tensor 1xNB
The tensor of PCA shape displacements
posedirs : torch.tensor Px(V * 3)
The pose PCA coefficients
J_regressor : torch.tensor JxV
The regressor array that is used to calculate the joints from
the position of the vertices
parents: torch.tensor J
The array that describes the kinematic tree for the model
lbs_weights: torch.tensor N x V x (J + 1)
The linear blend skinning weights that represent how much the
rotation matrix of each part affects each vertex
pose2rot: bool, optional
Flag on whether to convert the input pose tensor to rotation
matrices. The default value is True. If False, then the pose tensor
should already contain rotation matrices and have a size of
Bx(J + 1)x9
dtype: torch.dtype, optional
Returns
-------
verts: torch.tensor BxVx3
The vertices of the mesh after applying the shape and pose
displacements.
joints: torch.tensor BxJx3
The joints of the model
rot_mats: torch.tensor BxJx3x3
The rotation matrics of each joints
'''
batch_size = max(betas.shape[0], pose.shape[0])
device = betas.device
# Add shape contribution
v_shaped = v_template + blend_shapes(betas, shapedirs)
# Get the joints
# NxJx3 array
J = vertices2joints(J_regressor, v_shaped)
# 3. Add pose blend shapes
# N x J x 3 x 3
ident = torch.eye(3, dtype=dtype, device=device)
if pose2rot:
if pose.numel() == batch_size * 24 * 4:
rot_mats = quat_to_rotmat(pose.reshape(batch_size * 24, 4)).reshape(batch_size, 24, 3, 3)
else:
rot_mats = batch_rodrigues(
pose.view(-1, 3), dtype=dtype).view([batch_size, -1, 3, 3])
pose_feature = (rot_mats[:, 1:, :, :] - ident).view([batch_size, -1])
# (N x P) x (P, V * 3) -> N x V x 3
pose_offsets = torch.matmul(pose_feature, posedirs) \
.view(batch_size, -1, 3)
else:
pose_feature = pose[:, 1:].view(batch_size, -1, 3, 3) - ident
rot_mats = pose.view(batch_size, -1, 3, 3)
pose_offsets = torch.matmul(pose_feature.view(batch_size, -1),
posedirs).view(batch_size, -1, 3)
v_posed = pose_offsets + v_shaped
# 4. Get the global joint location
J_transformed, A = batch_rigid_transform(rot_mats, J.clone(), parents, dtype=dtype)
twist_angle = get_twist(rot_mats, J.clone(), parents)
# 5. Do skinning:
# W is N x V x (J + 1)
W = lbs_weights.unsqueeze(dim=0).expand([batch_size, -1, -1])
# (N x V x (J + 1)) x (N x (J + 1) x 16)
num_joints = J_regressor.shape[0]
T = torch.matmul(W, A.view(batch_size, num_joints, 16)) \
.view(batch_size, -1, 4, 4)
homogen_coord = torch.ones([batch_size, v_posed.shape[1], 1],
dtype=dtype, device=device)
v_posed_homo = torch.cat([v_posed, homogen_coord], dim=2)
v_homo = torch.matmul(T, torch.unsqueeze(v_posed_homo, dim=-1))
verts = v_homo[:, :, :3, 0]
J_from_verts = vertices2joints(J_regressor_h36m, verts)
return verts, J_transformed, rot_mats, J_from_verts, twist_angle
def get_twist(rot_mats, joints, parents):
joints = torch.unsqueeze(joints, dim=-1)
rel_joints = joints.clone()
rel_joints[:, 1:] -= joints[:, parents[1:]].clone()
# modified by xuchao
childs = -torch.ones((parents.shape[0]), dtype=parents.dtype, device=parents.device)
for i in range(1, parents.shape[0]):
childs[parents[i]] = i
dtype = rot_mats.dtype
batch_size = rot_mats.shape[0]
device = rot_mats.device
angle_twist = []
error = False
for i in range(1, parents.shape[0]):
# modified by xuchao
if childs[i] < 0:
angle_twist.append(torch.zeros((batch_size, 1), dtype=rot_mats.dtype, device=rot_mats.device))
continue
u = rel_joints[:, childs[i]]
rot = rot_mats[:, i]
v = torch.matmul(rot, u)
u_norm = torch.norm(u, dim=1, keepdim=True)
v_norm = torch.norm(v, dim=1, keepdim=True)
axis = torch.cross(u, v, dim=1)
axis_norm = torch.norm(axis, dim=1, keepdim=True)
# (B, 1, 1)
cos = torch.sum(u * v, dim=1, keepdim=True) / (u_norm * v_norm + 1e-8)
sin = axis_norm / (u_norm * v_norm + 1e-8)
# (B, 3, 1)
axis = axis / (axis_norm + 1e-8)
# Convert location revolve to rot_mat by rodrigues
# (B, 1, 1)
rx, ry, rz = torch.split(axis, 1, dim=1)
zeros = torch.zeros((batch_size, 1, 1), dtype=dtype, device=device)
K = torch.cat([zeros, -rz, ry, rz, zeros, -rx, -ry, rx, zeros], dim=1) \
.view((batch_size, 3, 3))
ident = torch.eye(3, dtype=dtype, device=device).unsqueeze(dim=0)
rot_mat_pivot = ident + sin * K + (1 - cos) * torch.bmm(K, K)
rot_mat_twist = torch.matmul(rot_mat_pivot.transpose(1, 2), rot)
_, axis, angle = rotmat_to_aa(rot_mat_twist)
axis = axis / torch.norm(axis, dim=1, keepdim=True)
spin_axis = u / u_norm
spin_axis = spin_axis.squeeze(-1)
pos = torch.norm(spin_axis - axis, dim=1)
neg = torch.norm(spin_axis + axis, dim=1)
if float(neg) < float(pos):
try:
assert float(pos) > 1.9, (pos, neg)
angle_twist.append(-1 * angle)
except AssertionError:
angle_twist.append(torch.ones_like(angle) * -999)
error = True
else:
try:
assert float(neg) > 1.9, (pos, neg, axis, angle, rot_mat_twist)
angle_twist.append(angle)
except AssertionError:
angle_twist.append(torch.ones_like(angle) * -999)
error = True
angle_twist = torch.stack(angle_twist, dim=1)
if error:
angle_twist[:] = -999
return angle_twist
def rotmat_to_aa(rotmat):
batch_size = rotmat.shape[0]
r11 = rotmat[:, 0, 0]
r22 = rotmat[:, 1, 1]
r33 = rotmat[:, 2, 2]
r12 = rotmat[:, 0, 1]
r21 = rotmat[:, 1, 0]
r13 = rotmat[:, 0, 2]
r31 = rotmat[:, 2, 0]
r23 = rotmat[:, 1, 2]
r32 = rotmat[:, 2, 1]
angle = torch.acos((r11 + r22 + r33 - 1) / 2).unsqueeze(dim=1)
'''
if -1e-6 < (r11 + r22 + r33 - 1) / 2 + 1 < 1e-6:
angle = torch.acos(-torch.ones_like(r11)).unsqueeze(dim=1)
else:
angle = torch.acos((r11 + r22 + r33 - 1) / 2).unsqueeze(dim=1)
'''
axis = torch.zeros((batch_size, 3))
axis[:, 0] = r32 - r23
axis[:, 1] = r13 - r31
axis[:, 2] = r21 - r12
axis = axis / (2 * torch.sin(angle) + 1e-8)
aa = axis * angle
return aa, axis, angle
from hybrik.
@dldaisy @Jeff-sjtu is the get_twist support batch style? the float(neg) < float(pos) seems to cause problem......
from hybrik.
This code is exactly what I want !!
Thank you very much!
from hybrik.
I use the code above and only got 18 twist angle while the GT kinematic rotation is with 24 rotation!
may I ask why? do you suppose the 3 bones connected with root joint and 3 leaf joints are without twist rotation?
additionally, are the 3 bones connected with the root joint have the same rotation as the global orientation?
from hybrik.
In the SMPL format, the i
th rotation indicates the relative rotation of the children of the i
th joint w.r.t the i
th joint. For example, the first rotation is the global rotation, and the first joint is the root joint. There are three joints connecting to the root joint. Therefore, the first rotation matrix indicates the rotation of the 3 children w.r.t to the root joint. For the leaf joints, since they have no child, the leaf rotation won't affect any joint positions. And thus we can't calculate the twist angle according to the joint positions.
from hybrik.
Thanks for the clarification!
I read here
HybrIK/hybrik/models/layers/smpl/lbs.py
Line 351 in ff6e61d
you concat another 5 ‘distal vertices’ to the leaf joints, does this operation makes too much performance (MPJPE or IK process) difference compared with only 24 joints setting?
from hybrik.
Hi, @wangzheallen
We follow the standard settings for setting, i.e. only evaluate the 14 LSP joints. The extra leaf joints are only used to estimate the leaf rotations.
from hybrik.
Thanks for the clarification, it helps to understand!
from hybrik.
Hi, @Jeff-sjtu
I have some questions about your reply #37 (comment), which is also relevant to several doubts regarding your IK code in models/layers/smpl/lbs.py.
As you mentioned:
In the SMPL format, the
i
th rotation indicates the relative rotation of the children of thei
th joint w.r.t thei
th joint. For example, the first rotation is the global rotation, and the first joint is the root joint. There are three joints connecting to the root joint. Therefore, the first rotation matrix indicates the rotation of the 3 children w.r.t to the root joint.
However, according to the smpl code, the i
th rotation should indicates the rotation of the i
th joint w.r.t its parent, except for the 0(root) joint. Thus, according to my understanding, the first rotation matrix, i.e., global orient, should mean the direction of pelvis instead of the rotation of the 3 children w.r.t to the root joint. Besides, in your hybrik function in models/layers/smpl/lbs.py, you also seemed to define rot_mat
the same way: when a joint i
has 3 children, the rot_mat[i]
is the rotation matrix of its 3 children w.r.t to i
solved by SVD. This way of defining rot_mat using its children instead of its parents also make me confused about your explanation of the leaf joints:
For the leaf joints, since they have no child, the leaf rotation won't affect any joint positions. And thus we can't calculate the twist angle according to the joint positions.
However, if the rot_mat means rotation w.r.t parent, the swing of a leaf node means the swing of the tip bone of a skeleton, which can be calculated through the position of the leaf node and its parent. In your models/layers/smpl/lbs.py code, you also directly predict the rot_mat of leaf nodes through neural networks instead of analytically calculate them.
May I know the reason why you define rot_mat using children instead of parent?
Thanks a lot!
from hybrik.
Hi @Jeff-sjtu , thanks for your great work! I have some questions following this issue #37
I adopted your get_twist
function and modified the SMPL_layer
to get angle_twist
from "ground-truth" betas
and thetas
(obtained from parsed h36m train annotations file downloaded from your GDrive link). However, my outputs are quite different from the "ground-truth" angle_twist
provided in your annotations file.
Would you mind confirming if the g-t angle_twist
is obtained using the above get_twist
function? If that's not the method, could you also provide the code to derive g-t angle_twist
?
Hope to get your advice on this, thank you so much!
from hybrik.
Hi, @Jeff-sjtu
I have some questions about your reply #37 (comment), which is also relevant to several doubts regarding your IK code in models/layers/smpl/lbs.py.
As you mentioned:In the SMPL format, the
i
th rotation indicates the relative rotation of the children of thei
th joint w.r.t thei
th joint. For example, the first rotation is the global rotation, and the first joint is the root joint. There are three joints connecting to the root joint. Therefore, the first rotation matrix indicates the rotation of the 3 children w.r.t to the root joint.However, according to the smpl code, the
i
th rotation should indicates the rotation of thei
th joint w.r.t its parent, except for the 0(root) joint. Thus, according to my understanding, the first rotation matrix, i.e., global orient, should mean the direction of pelvis instead of the rotation of the 3 children w.r.t to the root joint. Besides, in your hybrik function in models/layers/smpl/lbs.py, you also seemed to definerot_mat
the same way: when a jointi
has 3 children, therot_mat[i]
is the rotation matrix of its 3 children w.r.t toi
solved by SVD. This way of defining rot_mat using its children instead of its parents also make me confused about your explanation of the leaf joints:For the leaf joints, since they have no child, the leaf rotation won't affect any joint positions. And thus we can't calculate the twist angle according to the joint positions.
However, if the rot_mat means rotation w.r.t parent, the swing of a leaf node means the swing of the tip bone of a skeleton, which can be calculated through the position of the leaf node and its parent. In your models/layers/smpl/lbs.py code, you also directly predict the rot_mat of leaf nodes through neural networks instead of analytically calculate them.
May I know the reason why you define rot_mat using children instead of parent?
Thanks a lot!
Hi @dldaisy. When I saying the i
th joints, it includes the root joint (the pelvis). So in this context, the SMPL model has 24 joints in total. And in the official implementation of SMPL, the i
rotation is used to represent the rotation of the child of the i
th joint w.r.t the i
th joint. You can refer to the implementation of smplx.
from hybrik.
Hi @Jeff-sjtu, Thanks for reply!
I just looked at the smplx implementation but is still a bit confused. If the i rotation is used to represent the rotation of the child of the ith joint w.r.t the ith joint, which child is used to calculate the rot_mat of SPINE_3 in smpl? I don't find the code in smplx that exclusively deal with joints with multiple children like yours. Besides, if the rot_mats means rotation of the child of the a joint w.r.t it, what does rot_mat physically mean to leaf nodes in smpl ?
from hybrik.
Hi @pangyyyyy,
The code pasted before has some bugs. You need to replace the get_twist
function with the correct one.
def get_twist(rot_mats, joints, parents):
joints = torch.unsqueeze(joints, dim=-1)
rel_joints = joints.clone()
rel_joints[:, 1:] -= joints[:, parents[1:]].clone()
# modified by xuchao
childs = -torch.ones((parents.shape[0]), dtype=parents.dtype, device=parents.device)
for i in range(1, parents.shape[0]):
childs[parents[i]] = i
dtype = rot_mats.dtype
batch_size = rot_mats.shape[0]
device = rot_mats.device
angle_twist = []
error = False
for i in range(1, parents.shape[0]):
# modified by xuchao
if childs[i] < 0:
angle_twist.append(torch.zeros((batch_size, 1), dtype=rot_mats.dtype, device=rot_mats.device))
continue
u = rel_joints[:, childs[i]]
rot = rot_mats[:, i]
v = torch.matmul(rot, u)
u_norm = torch.norm(u, dim=1, keepdim=True)
v_norm = torch.norm(v, dim=1, keepdim=True)
axis = torch.cross(u, v, dim=1)
axis_norm = torch.norm(axis, dim=1, keepdim=True)
# (B, 1, 1)
cos = torch.sum(u * v, dim=1, keepdim=True) / (u_norm * v_norm + 1e-8)
sin = axis_norm / (u_norm * v_norm + 1e-8)
# (B, 3, 1)
axis = axis / (axis_norm + 1e-8)
# Convert location revolve to rot_mat by rodrigues
# (B, 1, 1)
rx, ry, rz = torch.split(axis, 1, dim=1)
zeros = torch.zeros((batch_size, 1, 1), dtype=dtype, device=device)
K = torch.cat([zeros, -rz, ry, rz, zeros, -rx, -ry, rx, zeros], dim=1) \
.view((batch_size, 3, 3))
ident = torch.eye(3, dtype=dtype, device=device).unsqueeze(dim=0)
rot_mat_pivot = ident + sin * K + (1 - cos) * torch.bmm(K, K)
rot_mat_twist = torch.matmul(rot_mat_pivot.transpose(1, 2), rot)
_, axis, angle = rotmat_to_aa(rot_mat_twist)
axis = axis / torch.norm(axis, dim=1, keepdim=True)
spin_axis = u / u_norm
spin_axis = spin_axis.squeeze(-1)
pos = torch.norm(spin_axis - axis, dim=1)
neg = torch.norm(spin_axis + axis, dim=1)
if float(neg) < float(pos):
try:
assert float(pos) > 1.9, (pos, neg)
angle_twist.append(-1 * angle)
except AssertionError:
angle_twist.append(torch.ones_like(angle) * -999)
error = True
else:
try:
assert float(neg) > 1.9, (pos, neg, axis, angle, rot_mat_twist)
angle_twist.append(angle)
except AssertionError:
angle_twist.append(torch.ones_like(angle) * -999)
error = True
angle_twist = torch.stack(angle_twist, dim=1)
if error:
angle_twist[:] = -999
return angle_twist
from hybrik.
Hi @Jeff-sjtu, Thanks for reply!
I just looked at the smplx implementation but is still a bit confused. If the i rotation is used to represent the rotation of the child of the ith joint w.r.t the ith joint, which child is used to calculate the rot_mat of SPINE_3 in smpl? I don't find the code in smplx that exclusively deal with joints with multiple children like yours. Besides, if the rot_mats means rotation of the child of the a joint w.r.t it, what does rot_mat physically mean to leaf nodes in smpl ?
In forward kinematics, we don't need to calculate rotmat based on the child. In inverse kinematics, we have two options. The first one is using three children and solve the rotmat by SVD. The second one is to pick one child to calculate.
For your second question, the rotmat of the leaf joints won't affect other joints, but will affect the distal vertices in the body mesh.
from hybrik.
@Jeff-sjtu the fix works! I am now able to obtain the correct angle_twist
as the ones provided in your g-t annotation files. Thank you so much for your help!
from hybrik.
You're welcome ;) @dldaisy
from hybrik.
Hi @Jeff-sjtu,
Thanks again for your patient reply!
Still a question:
May I ask what rot_mat[SPINE_3] in smpl FK means? If rot_mat means the rotation of SPINE_3's children w.r.t SPINE_3, does it indicate that the 3 children of SPINE_3 all have the same rotation w.r.t SPINE_3? But that's strange. This confuses me.
from hybrik.
Hi @Jeff-sjtu,
Thanks again for your patient reply!
Still a question:
May I ask what rot_mat[SPINE_3] in smpl FK means? If rot_mat means the rotation of SPINE_3's children w.r.t SPINE_3, does it indicate that the 3 children of SPINE_3 all have the same rotation w.r.t SPINE_3? But that's strange. This confuses me.
Yes, the 3 children of SPINE_3 share the same rotation. In SMPL, the body part with multiple children is treated as a rigid body. Similar to SPINE_3, the 3 children of the pelvis joint also form a rigid body.
from hybrik.
Yes, the 3 children of SPINE_3 share the same rotation. In SMPL, the body part with multiple children is treated as a rigid body. Similar to SPINE_3, the 3 children of the pelvis joint also form a rigid body.
I think you're right after reading the smplx code carefully. Thanks a lot!
from hybrik.
Hi @Jeff-sjtu ,
I have another question regarding the code get_twist
. It seems that this code assumes there's no twist of pelvis' 3 children regards to pelvis, but the twist of spine_3's children w.r.t spine_3 can be calculated. Does this go against the assumption that spine_3 and its 3 children forms a rigid body? Thanks!
from hybrik.
Hi @Jeff-sjtu ,
I have another question regarding the codeget_twist
. It seems that this code assumes there's no twist of pelvis' 3 children regards to pelvis, but the twist of spine_3's children w.r.t spine_3 can be calculated. Does this go against the assumption that spine_3 and its 3 children forms a rigid body? Thanks!
The twist rotation depends on the child you choose. Even the 3 children share the same rotation, their twist angle will be different, since the swing rotations are different.
from hybrik.
I see. Thanks a lot!
from hybrik.
Hi @Jeff-sjtu,
I have a question on this line of the function get_twist
: assert float(pos) > 1.9, (pos, neg)
. Why should this assertion be made to decide whether a twist angle is wrong? Thanks in advance!
from hybrik.
@heathentw yes, I remember I made some modifications on this part.
from hybrik.
I use the code above and only got 18 twist angle while the GT kinematic rotation is with 24 rotation! may I ask why? do you suppose the 3 bones connected with root joint and 3 leaf joints are without twist rotation? additionally, are the 3 bones connected with the root joint have the same rotation as the global orientation?
@Jeff-sjtu @wangzheallen 是不是可以理解成6个为0都是因为叶节点不能计算twist?至于为什么只得到 (24, 3)个(包含叶节点),就是因为骨盆节点也不能进行计算?
Can it be understood that 6 are 0 because the leaf node cannot calculate twist? As for why only (24, 3) (including leaf nodes) can be obtained, because the pelvic nodes can not be calculated?
twist_angle tensor([[[-0.2089], [ 0.0958], [ 0.0059], [-0.0613], [ 0.0255], [-0.0196], [-0.2302], [ 0.1953], [-0.0551], [ 0.0000], [ 0.0000], [ 0.0477], [-0.0429], [ 0.0555], [ 0.0000], [-0.1610], [ 0.1090], [-0.0854], [ 0.0906], [-0.0711], [-0.0739], [ 0.0000], [ 0.0000]]])
from hybrik.
Related Issues (20)
- my training is stuck in training phase HOT 1
- fail to import .pk file using Hybrlk add-on HOT 2
- SMPL Data Annotation Files for different Subjects HOT 3
- pose estimation in multiplayer scenarios HOT 1
- demo 的运行结果 HOT 2
- SMPL parameters thetas and betas are in which coordinate system? HOT 3
- Can I run the demo without a GPU?
- 训练时遇到的 RuntimeError HOT 2
- Errors while running Google Collab HOT 3
- Is it possible to use this code and model checkpoints freely? (include SMPL-Model license issue)
- Is it possible to train & test HybrIK-X?
- How did you make this animated video?
- demo_video_x.py: error: unrecognized arguments: --save-pk HOT 3
- There's a lot of shaking, and it doesn't seem to match the original video? 抖动非常厉害,并且好像跟原始视频对不上? HOT 2
- these image no good detect
- if i change better nvida gpu card,can improve the speed,now 2 it/s HOT 1
- Can we further get the SMPL and Skeleton infos with HybrIK?
- Are there 2D image coordinates for the joints?
- need HybrIKX for hand mocap
- Can you release the Multi-person demo code. HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from hybrik.