Пример #1
0
def spherical_expectation(spherical_probabilities):
    """Compute the expectation (a vector) from normalized spherical distribtuions.

  We define the spherical expectation as the integral of r*P(r)*dr where r is
  a unit direction vector in 2-sphere. We compute the discretized version on a
  spherical equirectangular map. To correctly use this function, the input has
  to be normalized properly using spherical_normalization().

  Args:
    spherical_probabilities: [BATCH, HEIGHT, WIDTH, N] spherical distributions
      in equirectangular form.

  Returns:
     expectation [BATCH, N, 3]
  """
    shape = spherical_probabilities.shape.as_list()
    height, width, channels = shape[1], shape[2], shape[3]
    spherical = tf.expand_dims(
        geometry.generate_equirectangular_grid([height, width]), 0)
    unit_directions = geometry.spherical_to_cartesian(spherical)
    axis_convert = tf.constant([[1., 0., 0.], [0., 0., -1.], [0., 1., 0.]])
    unit_directions = tf.squeeze(
        tf.matmul(axis_convert,
                  tf.expand_dims(unit_directions, -1),
                  transpose_a=True), -1)
    unit_directions = tf.tile(tf.expand_dims(unit_directions, -2),
                              [1, 1, 1, channels, 1])
    weighted = spherical_probabilities * equirectangular_area_weights(height)
    expectation = tf.reduce_sum(unit_directions * tf.expand_dims(weighted, -1),
                                [1, 2])
    return expectation
Пример #2
0
def von_mises_fisher(mean, concentration, shape):
    """Generate von Mises-Fisher distribution on spheres.

  This function samples probabilities from tensorflow_probability.VonMisesFisher
  on equirectangular grids of a sphere. The height dimension of the output
  ranges from pi/2 (top) to -pi/2 (bottom). The width dimension ranges from
  0 (left) to 2*pi (right).

  Args:
    mean: [BATCH, N, 3] a float tensor representing the unit direction of
      the mean.
    concentration: (float) a measure of concentration (a reciprocal measure of
      dispersion, so 1/kappa  is analogous to variance). concentration=0
      indicates a uniform distribution over the unit sphere,
      and concentration=+inf indicates a delta function at the mean direction.
    shape: a 2-d list represents the dimension (height, width) of the output.

  Returns:
    A 4-D tensor [BATCH, HEIGHT, WIDTH, N] represents the raw probabilities
    of the distribution. (surface integral != 1)

  Raises:
    ValueError: Input argument 'shape' is not valid.
    ValueError: Input argument 'mean' has wrong dimensions.
  """
    with tf.name_scope(None, 'von_mises_fisher', [mean, concentration, shape]):
        if not isinstance(shape, list) or len(shape) != 2:
            raise ValueError("Input argument 'shape' is not valid.")
        if mean.shape[-1] != 3:
            raise ValueError("Input argument 'mean' has wrong dimensions.")

        batch, channels = mean.shape[0], mean.shape[1]
        height, width = shape[0], shape[1]
        spherical_grid = geometry.generate_equirectangular_grid(shape)
        cartesian = geometry.spherical_to_cartesian(spherical_grid)
        axis_convert = tf.constant([[1., 0., 0.], [0., 0., -1.], [0., 1., 0.]])
        cartesian = tf.squeeze(
            tf.matmul(axis_convert,
                      tf.expand_dims(cartesian, -1),
                      transpose_a=True), -1)

        cartesian = tf.tile(cartesian[tf.newaxis, tf.newaxis, :],
                            [batch, channels, 1, 1, 1])
        mean = tf.tile(mean[:, :, tf.newaxis, tf.newaxis],
                       [1, 1, height, width, 1])
        vmf = tfp.distributions.VonMisesFisher(mean_direction=mean,
                                               concentration=[concentration])
        spherical_gaussian = vmf.prob(cartesian)
        return tf.transpose(spherical_gaussian, [0, 2, 3, 1])
Пример #3
0
def rotate_image_on_pano(images, rotations, fov, output_shape):
    """Transform perspective images to equirectangular images after rotations.

  Return equirectangular panoramic images in which the input perspective images
  embedded in after the rotation R from the input images' frame to the target
  frame. The image with the field of view "fov" centered at camera's look-at -Z
  axis is projected onto the pano. The -Z axis corresponds to the spherical
  coordinates (pi/2, pi/2) which is (HEIGHT/2, WIDTH/4) on the pano.

  Args:
    images: [BATCH, HEIGHT, WIDTH, CHANNEL] perspective view images.
    rotations: [BATCH, 3, 3] rotations matrices.
    fov: (float) images' field of view in degrees.
    output_shape: a 2-D list of output dimension [height, width].

  Returns:
    equirectangular images [BATCH, height, width, CHANNELS].
  """
    with tf.name_scope(None, 'rotate_image_on_pano',
                       [images, rotations, fov, output_shape]):
        if len(images.shape) != 4:
            raise ValueError("'images' has the wrong dimensions.")
        if rotations.shape[-2:] != [3, 3]:
            raise ValueError("'rotations' must have 3x3 dimensions.")

        shape = images.shape.as_list()
        batch, height, width = shape[0], shape[1], shape[2]
        # Generate a mesh grid on a sphere.
        spherical = geometry.generate_equirectangular_grid(output_shape)
        cartesian = geometry.spherical_to_cartesian(spherical)
        cartesian = tf.tile(cartesian[tf.newaxis, :, :, :, tf.newaxis],
                            [batch, 1, 1, 1, 1])
        axis_convert = tf.constant([[0., -1., 0.], [0., 0., 1.], [1., 0., 0.]])
        cartesian = tf.matmul(axis_convert, cartesian)
        cartesian = tf.squeeze(
            tf.matmul(rotations[:, tf.newaxis, tf.newaxis], cartesian), -1)
        # Only take one hemisphere. (camera lookat direction)
        hemisphere_mask = tf.cast(cartesian[:, :, :, -1:] < 0, tf.float32)
        image_coordinates = cartesian[:, :, :, :2] / cartesian[:, :, :, -1:]
        x, y = tf.split(image_coordinates, [1, 1], -1)
        # Map pixels on equirectangular pano to perspective image.
        nx = -x * width / (2 * tf.tan(math_utils.degrees_to_radians(
            fov / 2))) + width / 2 - 0.5
        ny = y * height / (2 * tf.tan(math_utils.degrees_to_radians(
            fov / 2))) + height / 2 - 0.5
        transformed = hemisphere_mask * tfa.image.resampler(
            images, tf.concat([nx, ny], -1))
        return transformed
Пример #4
0
def rotate_pano(images, rotations):
    """Rotate Panoramic images.

  Convert the spherical coordinates (colatitude, azimuth) to Cartesian (x, y, z)
  then apply SO(3) rotation matrices. Finally, convert them back to spherical
  coordinates and remap the equirectangular images.
  Note1: The rotations are applied to the sampling sphere instead of the camera.
  The camera actually rotates R^T. I_out(x) = I_in(R * x), x are points in the
  camera frame.

  Note2: It uses a simple linear interpolation for now instead of slerp, so the
  pixel values are not accurate but visually plausible.

  Args:
    images: a 4-D tensor of shape `[BATCH, HEIGHT, WIDTH, CHANNELS]`.
    rotations: [BATCH, 3, 3] rotation matrices.

  Returns:
    4-D tensor of shape `[BATCH, HEIGHT, WIDTH, CHANNELS]`.

  Raises:
    ValueError: if the `images` or 'rotations' has the wrong dimensions.
  """
    with tf.name_scope(None, 'rotate_pano', [images, rotations]):
        if len(images.shape) != 4:
            raise ValueError("'images' has the wrong dimensions.")
        if rotations.shape[-2:] != [3, 3]:
            raise ValueError("'rotations' must have 3x3 dimensions.")

        shape = images.shape.as_list()
        batch, height, width = shape[0], shape[1], shape[2]
        spherical = tf.expand_dims(
            geometry.generate_equirectangular_grid([height, width]), 0)
        spherical = tf.tile(spherical, [batch, 1, 1, 1])
        cartesian = geometry.spherical_to_cartesian(spherical)
        axis_convert = tf.constant([[0., 1., 0.], [0., 0., -1.], [-1., 0.,
                                                                  0.]])
        cartesian = tf.matmul(axis_convert, tf.expand_dims(cartesian, -1))
        rotated_cartesian = tf.matmul(rotations[:, tf.newaxis, tf.newaxis],
                                      cartesian)
        rotated_cartesian = tf.squeeze(
            tf.matmul(axis_convert, rotated_cartesian, transpose_a=True), -1)
        rotated_spherical = geometry.cartesian_to_spherical(rotated_cartesian)
        return equirectangular_sampler(images, rotated_spherical)