예제 #1
0
def _safe_dot(vector1, vector2, eps):
  """Calculates dot product while ensuring it is in the range [-1, 1]."""
  dot_product = vector.dot(vector1, vector2)
  # Safely shrink to make sure machine precision does not cause the dot
  # product to be outside the [-1.0, 1.0] range.
  return safe_ops.safe_shrink(
      vector=dot_product, minval=-1.0, maxval=1.0, open_bounds=False, eps=eps)
예제 #2
0
    def test_safe_shrink_exception_not_raised(self, dtype):
        """Checks whether safe shrinking makes tensor safe for tf.acos(x)."""
        tensor = tf.convert_to_tensor(value=_pick_random_vector(), dtype=dtype)
        tensor = tensor * tensor
        norm_tensor = tensor / tf.reduce_max(
            input_tensor=tensor, axis=-1, keepdims=True)
        eps = asserts.select_eps_for_addition(dtype)
        norm_tensor += eps

        safe_tensor = safe_ops.safe_shrink(norm_tensor, -1.0, 1.0)
        self.assert_exception_is_not_raised(tf.acos, shapes=[], x=safe_tensor)
예제 #3
0
  def test_safe_shrink_exception_raised(self, dtype):
    """Checks whether safe shrinking fails when eps is zero."""
    tensor = tf.convert_to_tensor(value=_pick_random_vector(), dtype=dtype)
    tensor = tensor * tensor
    norm_tensor = tensor / tf.reduce_max(
        input_tensor=tensor, axis=-1, keepdims=True)
    eps = asserts.select_eps_for_addition(dtype)
    norm_tensor += eps

    with self.assertRaises(tf.errors.InvalidArgumentError):
      self.evaluate(safe_ops.safe_shrink(norm_tensor, -1.0, 1.0, eps=0.0))
예제 #4
0
def relative_angle(quaternion1: type_alias.TensorLike,
                   quaternion2: type_alias.TensorLike,
                   name: str = "quaternion_relative_angle") -> tf.Tensor:
    r"""Computes the unsigned relative rotation angle between 2 unit quaternions.

  Given two normalized quanternions $$\mathbf{q}_1$$ and $$\mathbf{q}_2$$, the
  relative angle is computed as
  $$\theta = 2\arccos(\mathbf{q}_1^T\mathbf{q}_2)$$.

  Note:
    In the following, A1 to An are optional batch dimensions.

  Args:
    quaternion1: A tensor of shape `[A1, ..., An, 4]`, where the last dimension
      represents a normalized quaternion.
    quaternion2: A tensor of shape `[A1, ..., An, 4]`, where the last dimension
      represents a normalized quaternion.
    name: A name for this op that defaults to "quaternion_relative_angle".

  Returns:
    A tensor of shape `[A1, ..., An, 1]` where the last dimension represents
    rotation angles in the range [0.0, pi].

  Raises:
    ValueError: If the shape of `quaternion1` or `quaternion2` is not supported.
  """
    with tf.name_scope(name):
        quaternion1 = tf.convert_to_tensor(value=quaternion1)
        quaternion2 = tf.convert_to_tensor(value=quaternion2)

        shape.check_static(tensor=quaternion1,
                           tensor_name="quaternion1",
                           has_dim_equals=(-1, 4))
        shape.check_static(tensor=quaternion2,
                           tensor_name="quaternion2",
                           has_dim_equals=(-1, 4))
        quaternion1 = asserts.assert_normalized(quaternion1)
        quaternion2 = asserts.assert_normalized(quaternion2)

        dot_product = vector.dot(quaternion1, quaternion2, keepdims=False)
        # Ensure dot product is in range [-1. 1].
        eps_dot_prod = 4.0 * asserts.select_eps_for_addition(dot_product.dtype)
        dot_product = safe_ops.safe_shrink(dot_product,
                                           -1.0,
                                           1.0,
                                           False,
                                           eps=eps_dot_prod)
        return 2.0 * tf.acos(tf.abs(dot_product))
예제 #5
0
def d_q(q1, q2):
    """Distance between 2 quaternions
    
    The quaternion distance takes values between [0, pi]
    
    Parameters
    ----------
    q1: tf.tensor/np.ndarray
        1st quaternion
    q2: tf.tensor/np.ndarray
        2nd quaternion
    
    Returns
    -------
    : distnace between these 2 quaternions
    
    """
    q1 = tf.cast(tf.convert_to_tensor(value=q1), dtype=tf.float64)
    q2 = tf.cast(tf.convert_to_tensor(value=q2), dtype=tf.float64)

    shape.check_static(tensor=q1,
                       tensor_name="quaternion1",
                       has_dim_equals=(-1, 4))
    shape.check_static(tensor=q2,
                       tensor_name="quaternion2",
                       has_dim_equals=(-1, 4))

    q1 = quaternion.normalize(q1)
    q2 = quaternion.normalize(q2)

    dot_product = vector.dot(q1, q2, keepdims=False)

    # Ensure dot product is in range [-1. 1].
    eps_dot_prod = 1.8 * asserts.select_eps_for_addition(dot_product.dtype)
    dot_product = safe_ops.safe_shrink(dot_product,
                                       -1,
                                       1,
                                       open_bounds=False,
                                       eps=eps_dot_prod)

    return 2.0 * tf.acos(tf.abs(dot_product))
예제 #6
0
파일: euler.py 프로젝트: zhou745/graphics
def from_quaternion(quaternions, name=None):
    """Converts quaternions to Euler angles.

  Args:
    quaternions: A tensor of shape `[A1, ..., An, 4]`, where the last dimension
      represents a normalized quaternion.
    name: A name for this op that defaults to "euler_from_quaternion".

  Returns:
    A tensor of shape `[A1, ..., An, 3]`, where the last dimension represents
    the three Euler angles.
  """
    def general_case(r00, r10, r21, r22, r20, eps_addition):
        """Handles the general case."""
        theta_y = -tf.asin(r20)
        sign_cos_theta_y = safe_ops.nonzero_sign(tf.cos(theta_y))
        r00 = safe_ops.nonzero_sign(r00) * eps_addition + r00
        r22 = safe_ops.nonzero_sign(r22) * eps_addition + r22
        theta_z = tf.atan2(r10 * sign_cos_theta_y, r00 * sign_cos_theta_y)
        theta_x = tf.atan2(r21 * sign_cos_theta_y, r22 * sign_cos_theta_y)
        return tf.stack((theta_x, theta_y, theta_z), axis=-1)

    def gimbal_lock(r01, r02, r20, eps_addition):
        """Handles Gimbal locks."""
        sign_r20 = safe_ops.nonzero_sign(r20)
        r02 = safe_ops.nonzero_sign(r02) * eps_addition + r02
        theta_x = tf.atan2(-sign_r20 * r01, -sign_r20 * r02)
        theta_y = -sign_r20 * tf.constant(math.pi / 2.0, dtype=r20.dtype)
        theta_z = tf.zeros_like(theta_x)
        angles = tf.stack((theta_x, theta_y, theta_z), axis=-1)
        return angles

    with tf.compat.v1.name_scope(name, "euler_from_quaternion", [quaternions]):
        quaternions = tf.convert_to_tensor(value=quaternions)

        shape.check_static(tensor=quaternions,
                           tensor_name="quaternions",
                           has_dim_equals=(-1, 4))

        x, y, z, w = tf.unstack(quaternions, axis=-1)
        tx = safe_ops.safe_shrink(2.0 * x, -2.0, 2.0, True)
        ty = safe_ops.safe_shrink(2.0 * y, -2.0, 2.0, True)
        tz = safe_ops.safe_shrink(2.0 * z, -2.0, 2.0, True)
        twx = tx * w
        twy = ty * w
        twz = tz * w
        txx = tx * x
        txy = ty * x
        txz = tz * x
        tyy = ty * y
        tyz = tz * y
        tzz = tz * z

        # The following is clipped due to numerical instabilities that can take some
        # enties outside the [-1;1] range.
        r00 = safe_ops.safe_shrink(1.0 - (tyy + tzz), -1.0, 1.0, True)
        r10 = safe_ops.safe_shrink(txy + twz, -1.0, 1.0, True)
        r21 = safe_ops.safe_shrink(tyz + twx, -1.0, 1.0, True)
        r22 = safe_ops.safe_shrink(1.0 - (txx + tyy), -1.0, 1.0, True)
        r20 = safe_ops.safe_shrink(txz - twy, -1.0, 1.0, True)
        r01 = safe_ops.safe_shrink(txy - twz, -1.0, 1.0, True)
        r02 = safe_ops.safe_shrink(txz + twy, -1.0, 1.0, True)
        eps_addition = asserts.select_eps_for_addition(quaternions.dtype)
        general_solution = general_case(r00, r10, r21, r22, r20, eps_addition)
        gimbal_solution = gimbal_lock(r01, r02, r20, eps_addition)
        # The general solution is unstable close to the Gimbal lock, and the gimbal
        # solution is not toooff in these cases.
        is_gimbal = tf.less(tf.abs(tf.abs(r20) - 1.0), 1.0e-6)
        gimbal_mask = tf.stack((is_gimbal, is_gimbal, is_gimbal), axis=-1)
        return tf.compat.v1.where(gimbal_mask, gimbal_solution,
                                  general_solution)
예제 #7
0
def quaternion2euler(quaternions):
    """ Convert quaternion to Euler angles
    
    Parameters
    ----------
    quaternions: np.ndarray
        The array of shape (N, 4), where 4 is the 1 real part of quaternion and 3 imaginary parts of quaternion.
    
    Returns
    -------
    angles: np.ndarray
        The array of shape (N, 3), where 3 are the 3 anles of rotation around Z-Y-Z axes respectivelly.
    """
    def general_case(r02, r12, r20, r21, r22, eps_addition):
        """Handles the general case."""
        theta_y = tf.acos(r22)
        #sign_sin_theta_y = safe_ops.nonzero_sign(tf.sin(theta_y))

        r02 = safe_ops.nonzero_sign(r02) * eps_addition + r02
        r22 = safe_ops.nonzero_sign(r22) * eps_addition + r22

        theta_z0 = tf.atan2(r12, r02)
        theta_z1 = tf.atan2(r21, -r20)
        return tf.stack((theta_z1, theta_y, theta_z0), axis=-1)

    def gimbal_lock(r22, r11, r10, eps_addition):
        """Handles Gimbal locks.
        It is gimbal when r22 is -1 or 1"""
        sign_r22 = safe_ops.nonzero_sign(r22)
        r11 = safe_ops.nonzero_sign(r11) * eps_addition + r11

        theta_z0 = tf.atan2(sign_r22 * r10, r11)

        theta_y = tf.constant(math.pi / 2.0,
                              dtype=r20.dtype) - sign_r22 * tf.constant(
                                  math.pi / 2.0, dtype=r20.dtype)
        theta_z1 = tf.zeros_like(theta_z0)
        angles = tf.stack((theta_z1, theta_y, theta_z0), axis=-1)
        return angles

    with tf.compat.v1.name_scope(None, "euler_from_quaternion", [quaternions]):
        quaternions = tf.convert_to_tensor(value=quaternions)

        shape.check_static(tensor=quaternions,
                           tensor_name="quaternions",
                           has_dim_equals=(-1, 4))

        x, y, z, w = tf.unstack(quaternions, axis=-1)
        tx = safe_ops.safe_shrink(2.0 * x, -2.0, 2.0, True)
        ty = safe_ops.safe_shrink(2.0 * y, -2.0, 2.0, True)
        tz = safe_ops.safe_shrink(2.0 * z, -2.0, 2.0, True)
        twx = tx * w
        twy = ty * w
        twz = tz * w
        txx = tx * x
        txy = ty * x
        txz = tz * x
        tyy = ty * y
        tyz = tz * y
        tzz = tz * z

        # The following is clipped due to numerical instabilities that can take some
        # enties outside the [-1;1] range.

        r00 = safe_ops.safe_shrink(1.0 - (tyy + tzz), -1.0, 1.0, True)
        r01 = safe_ops.safe_shrink(txy - twz, -1.0, 1.0, True)
        r02 = safe_ops.safe_shrink(txz + twy, -1.0, 1.0, True)

        r10 = safe_ops.safe_shrink(txy + twz, -1.0, 1.0, True)
        r11 = safe_ops.safe_shrink(1.0 - (txx + tzz), -1.0, 1.0, True)
        r12 = safe_ops.safe_shrink(tyz - twx, -1.0, 1.0, True)

        r20 = safe_ops.safe_shrink(txz - twy, -1.0, 1.0, True)
        r21 = safe_ops.safe_shrink(tyz + twx, -1.0, 1.0, True)
        r22 = safe_ops.safe_shrink(1.0 - (txx + tyy), -1.0, 1.0, True)

        eps_addition = asserts.select_eps_for_addition(quaternions.dtype)
        general_solution = general_case(r02, r12, r20, r21, r22, eps_addition)
        gimbal_solution = gimbal_lock(r22, r11, r10, eps_addition)

        # The general solution is unstable close to the Gimbal lock, and the gimbal
        # solution is not toooff in these cases.
        # Check if r22 is 1 or -1
        is_gimbal = tf.less(tf.abs(tf.abs(r22) - 1.0), 1.0e-6)
        gimbal_mask = tf.stack((is_gimbal, is_gimbal, is_gimbal), axis=-1)

        return tf.compat.v1.where(gimbal_mask, gimbal_solution,
                                  general_solution)