Spaces:
Build error
Build error
| import torch | |
| from torch.nn import functional as F | |
| import numpy as np | |
| from torch import nn | |
| def geodesic_loss(R, Rgt, eps=1e-7): | |
| # see: Silvia tiger pose model 3d code | |
| # other implementations could be found here: | |
| # 1.) https://github.com/papagina/RotationContinuity/blob/master/sanity_test/code/tools.py | |
| # 2.) https://github.com/airalcorn2/pytorch-geodesic-loss/blob/master/geodesic_loss.py | |
| num_joints = R.shape[1] | |
| RT = R.permute(0,1,3,2) | |
| A = torch.matmul(RT.view(-1,3,3),Rgt.view(-1,3,3)) | |
| # torch.trace works only for 2D tensors | |
| n = A.shape[0] | |
| po_loss = 0 | |
| T = torch.sum(A[:,torch.eye(3).bool()],1) | |
| theta = torch.clamp(0.5*(T-1), -1+eps, 1-eps) | |
| angles = torch.acos(theta) | |
| loss = torch.sum(angles)/(n*num_joints) | |
| return loss | |
| class geodesic_loss_R(nn.Module): | |
| def __init__(self, reduction='mean'): | |
| super(geodesic_loss_R, self).__init__() | |
| self.reduction = reduction | |
| self.eps = 1e-7 # 1e-6 | |
| # batch geodesic loss for rotation matrices | |
| def bgdR(self,bRgts,bRps): | |
| #return((bRgts - bRps)**2.).mean() | |
| return geodesic_loss(bRgts, bRps, eps=self.eps) | |
| def forward(self, ypred, ytrue): | |
| theta = geodesic_loss(ypred,ytrue,eps=self.eps) | |
| if self.reduction == 'mean': | |
| return torch.mean(theta) | |
| else: | |
| return theta | |
| def batch_rodrigues_numpy(theta): | |
| """ Code adapted from spin | |
| Convert axis-angle representation to rotation matrix. | |
| Remark: | |
| this leads to the same result as kornia.angle_axis_to_rotation_matrix(theta) | |
| Args: | |
| theta: size = [B, 3] | |
| Returns: | |
| Rotation matrix corresponding to the quaternion -- size = [B, 3, 3] | |
| """ | |
| l1norm = np.linalg.norm(theta + 1e-8, ord = 2, axis = 1) | |
| # angle = np.unsqueeze(l1norm, -1) | |
| angle = l1norm.reshape((-1, 1)) | |
| # normalized = np.div(theta, angle) | |
| normalized = theta / angle | |
| angle = angle * 0.5 | |
| v_cos = np.cos(angle) | |
| v_sin = np.sin(angle) | |
| # quat = np.cat([v_cos, v_sin * normalized], dim = 1) | |
| quat = np.concatenate([v_cos, v_sin * normalized], axis = 1) | |
| return quat_to_rotmat_numpy(quat) | |
| def quat_to_rotmat_numpy(quat): | |
| """Code from: https://github.com/nkolot/SPIN/blob/master/utils/geometry.py | |
| Convert quaternion coefficients to rotation matrix. | |
| Args: | |
| quat: size = [B, 4] 4 <===>(w, x, y, z) | |
| Returns: | |
| Rotation matrix corresponding to the quaternion -- size = [B, 3, 3] | |
| """ | |
| norm_quat = quat | |
| # norm_quat = norm_quat/norm_quat.norm(p=2, dim=1, keepdim=True) | |
| norm_quat = norm_quat/np.linalg.norm(norm_quat, ord=2, axis=1, keepdims=True) | |
| w, x, y, z = norm_quat[:,0], norm_quat[:,1], norm_quat[:,2], norm_quat[:,3] | |
| B = quat.shape[0] | |
| # w2, x2, y2, z2 = w.pow(2), x.pow(2), y.pow(2), z.pow(2) | |
| w2, x2, y2, z2 = w**2, x**2, y**2, z**2 | |
| wx, wy, wz = w*x, w*y, w*z | |
| xy, xz, yz = x*y, x*z, y*z | |
| rotMat = np.stack([w2 + x2 - y2 - z2, 2*xy - 2*wz, 2*wy + 2*xz, | |
| 2*wz + 2*xy, w2 - x2 + y2 - z2, 2*yz - 2*wx, | |
| 2*xz - 2*wy, 2*wx + 2*yz, w2 - x2 - y2 + z2], axis=1).reshape(B, 3, 3) | |
| return rotMat | |
| def batch_rodrigues(theta): | |
| """Code from: https://github.com/nkolot/SPIN/blob/master/utils/geometry.py | |
| Convert axis-angle representation to rotation matrix. | |
| Remark: | |
| this leads to the same result as kornia.angle_axis_to_rotation_matrix(theta) | |
| Args: | |
| theta: size = [B, 3] | |
| Returns: | |
| Rotation matrix corresponding to the quaternion -- size = [B, 3, 3] | |
| """ | |
| l1norm = torch.norm(theta + 1e-8, p = 2, dim = 1) | |
| angle = torch.unsqueeze(l1norm, -1) | |
| normalized = torch.div(theta, angle) | |
| angle = angle * 0.5 | |
| v_cos = torch.cos(angle) | |
| v_sin = torch.sin(angle) | |
| quat = torch.cat([v_cos, v_sin * normalized], dim = 1) | |
| return quat_to_rotmat(quat) | |
| def batch_rot2aa(Rs, epsilon=1e-7): | |
| """ Code from: https://github.com/vchoutas/expose/blob/dffc38d62ad3817481d15fe509a93c2bb606cb8b/expose/utils/rotation_utils.py#L55 | |
| Rs is B x 3 x 3 | |
| void cMathUtil::RotMatToAxisAngle(const tMatrix& mat, tVector& out_axis, | |
| double& out_theta) | |
| { | |
| double c = 0.5 * (mat(0, 0) + mat(1, 1) + mat(2, 2) - 1); | |
| c = cMathUtil::Clamp(c, -1.0, 1.0); | |
| out_theta = std::acos(c); | |
| if (std::abs(out_theta) < 0.00001) | |
| { | |
| out_axis = tVector(0, 0, 1, 0); | |
| } | |
| else | |
| { | |
| double m21 = mat(2, 1) - mat(1, 2); | |
| double m02 = mat(0, 2) - mat(2, 0); | |
| double m10 = mat(1, 0) - mat(0, 1); | |
| double denom = std::sqrt(m21 * m21 + m02 * m02 + m10 * m10); | |
| out_axis[0] = m21 / denom; | |
| out_axis[1] = m02 / denom; | |
| out_axis[2] = m10 / denom; | |
| out_axis[3] = 0; | |
| } | |
| } | |
| """ | |
| cos = 0.5 * (torch.einsum('bii->b', [Rs]) - 1) | |
| cos = torch.clamp(cos, -1 + epsilon, 1 - epsilon) | |
| theta = torch.acos(cos) | |
| m21 = Rs[:, 2, 1] - Rs[:, 1, 2] | |
| m02 = Rs[:, 0, 2] - Rs[:, 2, 0] | |
| m10 = Rs[:, 1, 0] - Rs[:, 0, 1] | |
| denom = torch.sqrt(m21 * m21 + m02 * m02 + m10 * m10 + epsilon) | |
| axis0 = torch.where(torch.abs(theta) < 0.00001, m21, m21 / denom) | |
| axis1 = torch.where(torch.abs(theta) < 0.00001, m02, m02 / denom) | |
| axis2 = torch.where(torch.abs(theta) < 0.00001, m10, m10 / denom) | |
| return theta.unsqueeze(1) * torch.stack([axis0, axis1, axis2], 1) | |
| def quat_to_rotmat(quat): | |
| """Code from: https://github.com/nkolot/SPIN/blob/master/utils/geometry.py | |
| Convert quaternion coefficients to rotation matrix. | |
| Args: | |
| quat: size = [B, 4] 4 <===>(w, x, y, z) | |
| Returns: | |
| Rotation matrix corresponding to the quaternion -- size = [B, 3, 3] | |
| """ | |
| norm_quat = quat | |
| norm_quat = norm_quat/norm_quat.norm(p=2, dim=1, keepdim=True) | |
| w, x, y, z = norm_quat[:,0], norm_quat[:,1], norm_quat[:,2], norm_quat[:,3] | |
| B = quat.size(0) | |
| w2, x2, y2, z2 = w.pow(2), x.pow(2), y.pow(2), z.pow(2) | |
| wx, wy, wz = w*x, w*y, w*z | |
| xy, xz, yz = x*y, x*z, y*z | |
| rotMat = torch.stack([w2 + x2 - y2 - z2, 2*xy - 2*wz, 2*wy + 2*xz, | |
| 2*wz + 2*xy, w2 - x2 + y2 - z2, 2*yz - 2*wx, | |
| 2*xz - 2*wy, 2*wx + 2*yz, w2 - x2 - y2 + z2], dim=1).view(B, 3, 3) | |
| return rotMat | |
| def rot6d_to_rotmat(rot6d): | |
| """ Code from: https://github.com/nkolot/SPIN/blob/master/utils/geometry.py | |
| Convert 6D rotation representation to 3x3 rotation matrix. | |
| Based on Zhou et al., "On the Continuity of Rotation Representations in Neural Networks", CVPR 2019 | |
| Input: | |
| (B,6) Batch of 6-D rotation representations | |
| Output: | |
| (B,3,3) Batch of corresponding rotation matrices | |
| """ | |
| assert rot6d.ndim == 2 | |
| rot6d = rot6d.view(-1,3,2) | |
| a1 = rot6d[:, :, 0] | |
| a2 = rot6d[:, :, 1] | |
| b1 = F.normalize(a1) | |
| b2 = F.normalize(a2 - torch.einsum('bi,bi->b', b1, a2).unsqueeze(-1) * b1) | |
| b3 = torch.cross(b1, b2) | |
| rotmat = torch.stack((b1, b2, b3), dim=-1) | |
| return rotmat | |
| def rotmat_to_rot6d(rotmat): | |
| """ Convert 3x3 rotation matrix to 6D rotation representation. | |
| Based on Zhou et al., "On the Continuity of Rotation Representations in Neural Networks", CVPR 2019 | |
| Input: | |
| (B,3,3) Batch of corresponding rotation matrices | |
| Output: | |
| (B,6) Batch of 6-D rotation representations | |
| """ | |
| assert rotmat.ndim == 3 | |
| rot6d = rotmat[:, :, :2].reshape((-1, 6)) | |
| return rot6d | |
| def main(): | |
| # rotation matrix and 6d representation | |
| # see "On the Continuity of Rotation Representations in Neural Networks" | |
| from pyquaternion import Quaternion | |
| batch_size = 5 | |
| rotmat = np.zeros((batch_size, 3, 3)) | |
| for ind in range(0, batch_size): | |
| rotmat[ind, :, :] = Quaternion.random().rotation_matrix | |
| rotmat_torch = torch.Tensor(rotmat) | |
| rot6d = rotmat_to_rot6d(rotmat_torch) | |
| rotmat_rec = rot6d_to_rotmat(rot6d) | |
| print('..................... 1 ....................') | |
| print(rotmat_torch[0, :, :]) | |
| print(rotmat_rec[0, :, :]) | |
| print('Conversion from rotmat to rot6d and inverse are ok!') | |
| # rotation matrix and axis angle representation | |
| import kornia | |
| input = torch.rand(1, 3) | |
| output = kornia.angle_axis_to_rotation_matrix(input) | |
| input_rec = kornia.rotation_matrix_to_angle_axis(output) | |
| print('..................... 2 ....................') | |
| print(input) | |
| print(input_rec) | |
| print('Kornia implementation for rotation_matrix_to_angle_axis is wrong!!!!') | |
| # For non-differential conversions use scipy: | |
| # https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.transform.Rotation.html | |
| from scipy.spatial.transform import Rotation as R | |
| r = R.from_matrix(rotmat[0, :, :]) | |
| print('..................... 3 ....................') | |
| print(r.as_matrix()) | |
| print(r.as_rotvec()) | |
| print(r.as_quaternion) | |
| # one might furthermore have a look at: | |
| # https://github.com/silviazuffi/smalst/blob/master/utils/transformations.py | |
| if __name__ == "__main__": | |
| main() | |