def world_to_image_projection(p_world, intrinsics, pose_w2c):
  """Project points in the world frame to the image plane.

  Args:
    p_world: [HEIGHT, WIDTH, 3] points in the world's coordinate frame.
    intrinsics: [3, 3] camera's intrinsic matrix.
    pose_w2c: [3, 4] camera pose matrix (world to camera).

  Returns:
    [HEIGHT, WIDTH, 2] points in the image coordinate.
    [HEIGHT, WIDTH, 1] the z depth.
  """
  shape = p_world.shape.as_list()
  height, width = shape[0], shape[1]
  p_world_homogeneous = tf.concat([p_world, tf.ones([height, width, 1])], -1)
  p_camera = tf.squeeze(
      tf.matmul(pose_w2c[tf.newaxis, tf.newaxis, :],
                tf.expand_dims(p_world_homogeneous, -1)), -1)
  p_camera = p_camera*tf.constant([1., 1., -1.], shape=[1, 1, 3])
  p_image = tf.squeeze(tf.matmul(intrinsics[tf.newaxis, tf.newaxis, :],
                                 tf.expand_dims(p_camera, -1)), -1)
  z = p_image[:, :, -1:]
  return tf.math.divide_no_nan(p_image[:, :, :2], z), z