Example #1
0
def sample_quadric_surface(quadric, center, samples):
    """Samples the algebraic distance to the input quadric at sparse locations.


  Args:
    quadric: Tensor with shape [..., 4, 4]. Contains the matrix of the quadric
      surface.
    center: Tensor with shape [..., 3]. Contains the [x,y,z] coordinates of the
      center of the coordinate frame of the quadric surface in NIC space with a
      top-left origin.
    samples: Tensor with shape [..., N, 3], where N is the number of samples to
      evaluate. These are the sample locations in the same space in which the
      quadric surface center is defined. Supports broadcasting the batching
      dimensions.

  Returns:
    distances: Tensor with shape [..., N, 1]. Contains the algebraic distance
      to the surface at each sample.
  """
    with tf.name_scope('sample_quadric_surface'):
        batching_dimensions = quadric.get_shape().as_list()[:-2]
        batching_rank = len(batching_dimensions)
        tf_util.assert_shape(quadric, batching_dimensions + [4, 4],
                             'sample_quadric_surface:quadric')
        tf_util.assert_shape(center, batching_dimensions + [-1],
                             'sample_quadric_surface:center')
        tf_util.assert_shape(samples, batching_rank * [-1] + [-1, 3],
                             'sample_quadric_surface:samples')

        # We want to transform the coordinates so that they are in the coordinate
        # frame of the conic section matrix, so we subtract the center of the
        # conic.
        samples = samples - tf.expand_dims(center, axis=batching_rank)
        sample_count = samples.get_shape().as_list()[-2]

        homogeneous_sample_ones = tf.ones(samples.get_shape().as_list()[:-1] +
                                          [1],
                                          dtype=tf.float32)
        homogeneous_sample_coords = tf.concat(
            [samples, homogeneous_sample_ones], axis=-1)

        # When we transform the coordinates per-image, we broadcast on both sides-
        # the batching dimensions broadcast up the coordinate grid, and the
        # coordinate center broadcasts up along the height and width.
        # Per-pixel, the algebraic distance is v^T * M * v, where M is the matrix
        # of the conic section, and v is the homogeneous column vector [x y z 1]^T.
        half_distance = tf.matmul(quadric,
                                  homogeneous_sample_coords,
                                  transpose_b=True)
        rank = batching_rank + 2
        half_distance = tf.transpose(half_distance,
                                     perm=list(range(rank - 2)) +
                                     [rank - 1, rank - 2])
        algebraic_distance = tf.reduce_sum(tf.multiply(
            homogeneous_sample_coords, half_distance),
                                           axis=-1)
        return tf.reshape(algebraic_distance,
                          batching_dimensions + [sample_count, 1])
Example #2
0
def compute_shape_element_influences(quadrics, centers, radii, samples):
    """Computes the per-shape-element values at given sample locations.

  Args:
    quadrics: quadric parameters with shape [batch_size, quadric_count, 4, 4].
    centers: rbf centers with shape [batch_size, quadric_count, 3].
    radii: rbf radii with shape [batch_size, quadric_count, radius_length].
      radius_length can be 1, 3, or 6 depending on whether it is isotropic,
      anisotropic, or a general symmetric covariance matrix, respectively.
    samples: a grid of samples with shape [batch_size, quadric_count,
      sample_count, 3] or shape [batch_size, sample_count, 3].

  Returns:
    Two tensors (the quadric values and the RBF values, respectively), each
    with shape [batch_size, quadric_count, sample_count, 1]
  """

    print(
        '[HERE: In ldif.representation.quadrics.compute_shape_element_influences] quadrics shape =',
        quadrics.shape)
    print(
        '[HERE: In ldif.representation.quadrics.compute_shape_element_influences] centers shape =',
        centers.shape)
    print(
        '[HERE: In ldif.representation.quadrics.compute_shape_element_influences] radii shape =',
        radii.shape)
    print(
        '[HERE: In ldif.representation.quadrics.compute_shape_element_influences] samples shape =',
        samples.shape)

    with tf.name_scope('compute_shape_element_influences'):
        # Select the number of samples along the ray. The larger this is, the
        # more memory that will be consumed and the slower the algorithm. But it
        # reduces warping artifacts and the likelihood of missing a thin surface.
        batch_size, quadric_count = quadrics.get_shape().as_list()[0:2]

        tf_util.assert_shape(quadrics, [batch_size, quadric_count, 4, 4],
                             'quadrics')
        tf_util.assert_shape(centers, [batch_size, quadric_count, 3],
                             'centers')
        # We separate the isometric, axis-aligned, and general RBF functions.
        # The primary reason for this is that the type of basis function
        # affects the shape of many tensors, and it is easier to make
        # everything correct when the shape is known. Once the shape function is
        # set we can clean it up and choose one basis function.
        radii_shape = radii.get_shape().as_list()
        if len(radii_shape) != 3:
            raise tf.errors.InvalidArgumentError(
                'radii must have shape [batch_size, quadric_count, radii_values].'
            )
        elif radii_shape[2] == 1:
            rbf_sampler = sample_isotropic_bf
            radius_shape = [1]
        elif radii_shape[2] == 3:
            rbf_sampler = sample_axis_aligned_bf
            radius_shape = [3]
        elif radii_shape[2] == 6:
            rbf_sampler = sample_cov_bf
            radius_shape = [6]
        else:
            raise tf.errors.InvalidArgumentError(
                'radii must have either 1, 3, or 6 elements.')
        tf_util.assert_shape(radii, [batch_size, quadric_count] + radius_shape,
                             'radii')

        # Ensure the samples have the right shape and tile in an axis for the
        # quadric dimension if it wasn't provided.
        sample_shape = samples.get_shape().as_list()
        sample_rank = len(sample_shape)
        if (sample_rank not in [3, 4] or sample_shape[-1] != 3
                or sample_shape[0] != batch_size):
            raise tf.errors.InvalidArgumentError(
                'Input tensor samples must have shape [batch_size, quadric_count,'
                ' sample_count, 3] or shape [batch_size, sample_count, 3]. The input'
                ' shape was %s' % repr(sample_shape))
        missing_quadric_dim = len(sample_shape) == 3
        if missing_quadric_dim:
            samples = tf_util.tile_new_axis(samples,
                                            axis=1,
                                            length=quadric_count)
        sample_count = sample_shape[-2]

        # Sample the quadric surfaces and the RBFs in world space, and composite
        # them.
        sampled_quadrics = sample_quadric_surface(quadrics, centers, samples)
        tf_util.assert_shape(sampled_quadrics,
                             [batch_size, quadric_count, sample_count, 1],
                             'sampled_quadrics')

        tf_util.assert_shape(centers, [batch_size, quadric_count, 3],
                             'centers')
        tf_util.assert_shape(samples,
                             [batch_size, quadric_count, sample_count, 3],
                             'samples')

        print(
            '[HERE: In ldif.representation.quadrics.compute_shape_element_incluences] About to run rbf_sampler'
        )
        print(
            '[HERE: In ldif.representation.quadrics.compute_shape_element_incluences] centers shape =',
            centers.shape)
        print(
            '[HERE: In ldif.representation.quadrics.compute_shape_element_incluences] radii shape =',
            radii.shape)
        print(
            '[HERE: In ldif.representation.quadrics.compute_shape_element_incluences] samples shape =',
            samples.shape)

        sampled_rbfs = rbf_sampler(centers, radii, samples)
        sampled_rbfs = tf.reshape(sampled_rbfs,
                                  [batch_size, quadric_count, sample_count, 1])
        return sampled_quadrics, sampled_rbfs
Example #3
0
def interpolate_from_grid_coordinates(samples, grid):
  """Performs trilinear interpolation to estimate the value of a grid function.

  This function makes several assumptions to do the lookup:
  1) The grid is LHW and has evenly spaced samples in the range (0, 1), which
    is really the screen space range [0.5, {L, H, W}-0.5].

  Args:
    samples: Tensor with shape [batch_size, sample_count, 3].
    grid: Tensor with shape [batch_size, length, height, width, 1].

  Returns:
    sample: Tensor with shape [batch_size, sample_count, 1] and type float32.
    mask: Tensor with shape [batch_size, sample_count, 1] and type float32
  """
  batch_size, length, height, width = grid.get_shape().as_list()[:4]
  # These asserts aren't required by the algorithm, but they are currently
  # true for the pipeline:
  assert length == height
  assert length == width
  sample_count = samples.get_shape().as_list()[1]
  tf_util.assert_shape(samples, [batch_size, sample_count, 3],
                       'interpolate_from_grid:samples')
  tf_util.assert_shape(grid, [batch_size, length, height, width, 1],
                       'interpolate_from_grid:grid')
  offset_samples = samples  # Used to subtract 0.5
  lower_coords = tf.cast(tf.math.floor(offset_samples), dtype=tf.int32)
  upper_coords = lower_coords + 1
  alphas = tf.floormod(offset_samples, 1.0)

  maximum_value = grid.get_shape().as_list()[1:4]
  size_per_channel = tf.tile(
      tf.reshape(tf.constant(maximum_value, dtype=tf.int32), [1, 1, 3]),
      [batch_size, sample_count, 1])
  # We only need to check that the floor is at least zero and the ceil is
  # no greater than the max index, because floor round negative numbers to
  # be more negative:
  is_valid = tf.logical_and(lower_coords >= 0, upper_coords < size_per_channel)
  # Validity mask has shape [batch_size, sample_count] and is 1.0 where all of
  # x,y,z are within the [0,1] range of the grid.
  validity_mask = tf.reduce_min(
      tf.cast(is_valid, dtype=tf.float32), axis=2, keep_dims=True)

  lookup_coords = [[[], []], [[], []]]
  corners = [[[], []], [[], []]]
  flattened_grid = tf.reshape(grid, [batch_size, length * height * width])
  for xi, x_coord in enumerate([lower_coords[:, :, 0], upper_coords[:, :, 0]]):
    x_coord = tf.clip_by_value(x_coord, 0, width - 1)
    for yi, y_coord in enumerate([lower_coords[:, :, 1], upper_coords[:, :,
                                                                      1]]):
      y_coord = tf.clip_by_value(y_coord, 0, height - 1)
      for zi, z_coord in enumerate(
          [lower_coords[:, :, 2], upper_coords[:, :, 2]]):
        z_coord = tf.clip_by_value(z_coord, 0, length - 1)
        flat_lookup = z_coord * height * width + y_coord * width + x_coord
        lookup_coords[xi][yi].append(flat_lookup)
        lookup_result = tf.batch_gather(flattened_grid, flat_lookup)
        tf_util.assert_shape(lookup_result, [batch_size, sample_count],
                             'interpolate_from_grid:lookup_result x/8')
        print_op = tf.print('corner xyz=%i, %i, %i' % (xi, yi, zi),
                            lookup_result, '\n', 'flat_lookup:', flat_lookup,
                            '\n\n')
        with tf.control_dependencies([print_op]):
          lookup_result = 1.0 * lookup_result
        corners[xi][yi].append(lookup_result)

  alpha_x, alpha_y, alpha_z = tf.unstack(alphas, axis=2)
  one_minus_alpha_x = 1.0 - alpha_x
  one_minus_alpha_y = 1.0 - alpha_y
  # First interpolate a face along x:
  f00 = corners[0][0][0] * one_minus_alpha_x + corners[1][0][0] * alpha_x
  f01 = corners[0][0][1] * one_minus_alpha_x + corners[1][0][1] * alpha_x
  f10 = corners[0][1][0] * one_minus_alpha_x + corners[1][1][0] * alpha_x
  f11 = corners[0][1][1] * one_minus_alpha_x + corners[1][1][1] * alpha_x
  # Next interpolate a long along y:
  l0 = f00 * one_minus_alpha_y + f10 * alpha_y
  l1 = f01 * one_minus_alpha_y + f11 * alpha_y

  # Finally interpolate a point along z:
  p = l0 * (1.0 - alpha_z) + l1 * alpha_z

  tf_util.assert_shape(p, [batch_size, sample_count], 'interpolate_from_grid:p')

  p = tf.reshape(p, [batch_size, sample_count, 1])
  validity_mask = tf.reshape(validity_mask, [batch_size, sample_count, 1])
  return p, validity_mask