예제 #1
0
def inverse(quaternion, name=None):
  """Computes the inverse of a quaternion.

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

  Args:
    quaternion:  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_inverse".

  Returns:
    A tensor of shape `[A1, ..., An, 4]`, where the last dimension represents
    a normalized quaternion.

  Raises:
    ValueError: If the shape of `quaternion` is not supported.
  """
  with tf.compat.v1.name_scope(name, "quaternion_inverse", [quaternion]):
    quaternion = tf.convert_to_tensor(value=quaternion)

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

    squared_norm = tf.reduce_sum(
        input_tensor=tf.square(quaternion), axis=-1, keepdims=True)
    return safe_ops.safe_unsigned_div(conjugate(quaternion), squared_norm)
예제 #2
0
def cartesian_to_spherical_coordinates(point_cartesian, name=None):
  """Function to transform Cartesian coordinates to spherical coordinates.

  This function assumes a right handed coordinate system with `z` pointing up.
  When `x` and `y` are both `0`, the function outputs `0` for `phi`. Note that
  the function is not smooth when `x = y = 0`.

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

  Args:
    point_cartesian: A tensor of shape `[A1, ..., An, 3]`. In the last
      dimension, the data follows the `x`, `y`, `z` order.
    name: A name for this op. Defaults to `cartesian_to_spherical_coordinates`.

  Returns:
    A tensor of shape `[A1, ..., An, 3]`. The last dimensions contains
    (`r`,`theta`,`phi`), where `r` is the sphere radius, `theta` is the polar
    angle and `phi` is the azimuthal angle.
  """
  with tf.compat.v1.name_scope(name, "cartesian_to_spherical_coordinates",
                               [point_cartesian]):
    point_cartesian = tf.convert_to_tensor(value=point_cartesian)

    shape.check_static(
        tensor=point_cartesian,
        tensor_name="point_cartesian",
        has_dim_equals=(-1, 3))

    x, y, z = tf.unstack(point_cartesian, axis=-1)
    radius = tf.norm(tensor=point_cartesian, axis=-1)
    theta = tf.acos(safe_ops.safe_unsigned_div(z, radius))
    phi = tf.atan2(y, x)
    return tf.stack((radius, theta, phi), axis=-1)
예제 #3
0
def from_quaternion(quaternion, name=None):
  """Converts a quaternion to an axis-angle representation.

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

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

  Returns:
    Tuple of two tensors of shape `[A1, ..., An, 3]` and `[A1, ..., An, 1]`,
    where the first tensor represents the axis, and the second represents the
    angle. The resulting axis is a normalized vector.

  Raises:
    ValueError: If the shape of `quaternion` is not supported.
  """
  with tf.compat.v1.name_scope(name, "axis_angle_from_quaternion",
                               [quaternion]):
    quaternion = tf.convert_to_tensor(value=quaternion)

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

    # This prevents zero norm xyz and zero w, and is differentiable.
    quaternion += asserts.select_eps_for_addition(quaternion.dtype)
    xyz, w = tf.split(quaternion, (3, 1), axis=-1)
    norm = tf.norm(tensor=xyz, axis=-1, keepdims=True)
    angle = 2.0 * tf.atan2(norm, tf.abs(w))
    axis = safe_ops.safe_unsigned_div(safe_ops.nonzero_sign(w) * xyz, norm)
    return axis, angle
예제 #4
0
  def test_safe_signed_div_exception_raised(self, dtype):
    """Checks that signed division causes Inf values for zero eps."""
    vector = tf.convert_to_tensor(value=_pick_random_vector(), dtype=dtype)
    zero_vector = tf.zeros_like(vector)

    with self.assertRaises(tf.errors.InvalidArgumentError):
      self.evaluate(
          safe_ops.safe_unsigned_div(
              tf.norm(tensor=vector), tf.sin(zero_vector), eps=0.0))
예제 #5
0
def cartesian_to_spherical_coordinates(
        point_cartesian: TensorLike,
        eps: Float = None,
        name: str = "cartesian_to_spherical_coordinates") -> tf.Tensor:
    """Function to transform Cartesian coordinates to spherical coordinates.

  This function assumes a right handed coordinate system with `z` pointing up.
  When `x` and `y` are both `0`, the function outputs `0` for `phi`. Note that
  the function is not smooth when `x = y = 0`.

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

  Args:
    point_cartesian: A tensor of shape `[A1, ..., An, 3]`. In the last
      dimension, the data follows the `x`, `y`, `z` order.
    eps: A small `float`, to be added to the denominator. If left as `None`, its
      value is automatically selected using `point_cartesian.dtype`.
    name: A name for this op. Defaults to "cartesian_to_spherical_coordinates".

  Returns:
    A tensor of shape `[A1, ..., An, 3]`. The last dimensions contains
    (`r`,`theta`,`phi`), where `r` is the sphere radius, `theta` is the polar
    angle and `phi` is the azimuthal angle. Returns `NaN` gradient if x = y = 0.
  """
    with tf.name_scope(name):
        point_cartesian = tf.convert_to_tensor(value=point_cartesian)

        shape.check_static(tensor=point_cartesian,
                           tensor_name="point_cartesian",
                           has_dim_equals=(-1, 3))

        x, y, z = tf.unstack(point_cartesian, axis=-1)
        radius = tf.norm(tensor=point_cartesian, axis=-1)
        theta = tf.acos(
            tf.clip_by_value(safe_ops.safe_unsigned_div(z, radius, eps), -1.,
                             1.))
        phi = tf.atan2(y, x)
        return tf.stack((radius, theta, phi), axis=-1)
예제 #6
0
파일: normals.py 프로젝트: zhou745/graphics
def vertex_normals(vertices, indices, clockwise=True, name=None):
    """Computes vertex normals from a mesh.

  This function computes vertex normals as the weighted sum of the adjacent
  face normals, where the weights correspond to the area of each face. This
  function supports planar convex polygon faces. For non-triangular meshes,
  this function converts them into triangular meshes to calculate vertex
  normals.

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

  Args:
    vertices: A tensor of shape `[A1, ..., An, V, 3]`, where V is the number of
      vertices.
    indices: A tensor of shape `[A1, ..., An, F, M]`, where F is the number of
      faces and M is the number of vertices per face.
    clockwise: Winding order to determine front-facing faces. The order of
      vertices should be either clockwise or counterclockwise.
    name: A name for this op. Defaults to "normals_vertex_normals".

  Returns:
    A tensor of shape `[A1, ..., An, V, 3]` containing vertex normals. If
    vertices and indices have different batch dimensions, this function
    broadcasts them into the same batch dimensions and the output batch
    dimensions are the broadcasted.

  Raises:
    ValueError: If the shape of `vertices`, `indices` is not supported.
  """
    with tf.compat.v1.name_scope(name, "normals_vertex_normals",
                                 [vertices, indices]):
        vertices = tf.convert_to_tensor(value=vertices)
        indices = tf.convert_to_tensor(value=indices)

        shape.check_static(tensor=vertices,
                           tensor_name="vertices",
                           has_rank_greater_than=1,
                           has_dim_equals=(-1, 3))
        shape.check_static(tensor=indices,
                           tensor_name="indices",
                           has_rank_greater_than=1,
                           has_dim_greater_than=(-1, 2))
        shape.compare_batch_dimensions(tensors=(vertices, indices),
                                       last_axes=(-3, -3),
                                       broadcast_compatible=True)

        shape_indices = indices.shape.as_list()
        if None in shape_indices[:-2]:
            raise ValueError("'indices' must have specified batch dimensions.")
        common_batch_dims = shape.get_broadcasted_shape(
            vertices.shape[:-2], indices.shape[:-2])
        vertices_repeat = [
            common_batch_dims[x] // vertices.shape.as_list()[x]
            for x in range(len(common_batch_dims))
        ]
        indices_repeat = [
            common_batch_dims[x] // shape_indices[x]
            for x in range(len(common_batch_dims))
        ]
        vertices = tf.tile(vertices,
                           vertices_repeat + [1, 1],
                           name="vertices_broadcast")
        indices = tf.tile(indices,
                          indices_repeat + [1, 1],
                          name="indices_broadcast")

        # Triangulate non-triangular faces.
        if shape_indices[-1] > 3:
            triangle_indices = []
            for i in range(1, shape_indices[-1] - 1):
                triangle_indices.append(
                    tf.concat((indices[..., 0:1], indices[..., i:i + 2]),
                              axis=-1))
            indices = tf.concat(triangle_indices, axis=-2)
            shape_indices = indices.shape.as_list()

        face_vertices = gather_faces(vertices, indices)
        # Use unnormalized face normals to scale normals by area.
        mesh_face_normals = face_normals(face_vertices,
                                         clockwise=clockwise,
                                         normalize=False)

        if vertices.shape.ndims > 2:
            outer_indices = np.meshgrid(
                *[np.arange(i) for i in shape_indices[:-2]],
                sparse=False,
                indexing="ij")
            outer_indices = [np.expand_dims(i, axis=-1) for i in outer_indices]
            outer_indices = np.concatenate(outer_indices, axis=-1)
            outer_indices = np.expand_dims(outer_indices, axis=-2)
            outer_indices = tf.constant(outer_indices, dtype=tf.int32)
            outer_indices = tf.tile(outer_indices,
                                    [1] * len(shape_indices[:-2]) +
                                    [tf.shape(input=indices)[-2]] + [1])
            unnormalized_vertex_normals = tf.zeros_like(vertices)
            for i in range(shape_indices[-1]):
                scatter_indices = tf.concat(
                    [outer_indices, indices[..., i:i + 1]], axis=-1)
                unnormalized_vertex_normals = tf.compat.v1.tensor_scatter_add(
                    unnormalized_vertex_normals, scatter_indices,
                    mesh_face_normals)
        else:
            unnormalized_vertex_normals = tf.zeros_like(vertices)
            for i in range(shape_indices[-1]):
                unnormalized_vertex_normals = tf.compat.v1.tensor_scatter_add(
                    unnormalized_vertex_normals, indices[..., i:i + 1],
                    mesh_face_normals)

        vector_norms = tf.sqrt(
            tf.reduce_sum(input_tensor=unnormalized_vertex_normals**2,
                          axis=-1,
                          keepdims=True))
        return safe_ops.safe_unsigned_div(unnormalized_vertex_normals,
                                          vector_norms)