예제 #1
0
    def setUp(self):
        warnings.simplefilter('ignore', category=ImportWarning)

        gs.random.seed(1234)

        n = 3
        group = SpecialEuclideanGroup(n=n)

        # Diagonal left and right invariant metrics
        diag_mat_at_identity = gs.eye(group.dimension)

        left_diag_metric = InvariantMetric(
            group=group,
            inner_product_mat_at_identity=diag_mat_at_identity,
            left_or_right='left')
        right_diag_metric = InvariantMetric(
            group=group,
            inner_product_mat_at_identity=diag_mat_at_identity,
            left_or_right='right')

        # General left and right invariant metrics
        # TODO(nina): Replace the matrix below by a general SPD matrix.
        sym_mat_at_identity = gs.eye(group.dimension)

        left_metric = InvariantMetric(
            group=group,
            inner_product_mat_at_identity=sym_mat_at_identity,
            left_or_right='left')

        right_metric = InvariantMetric(
            group=group,
            inner_product_mat_at_identity=sym_mat_at_identity,
            left_or_right='right')

        metrics = {
            'left_diag': left_diag_metric,
            'right_diag_metric': right_diag_metric,
            'left': left_metric,
            'right': right_metric
        }

        # General case for the point
        point_1 = gs.array([[-0.2, 0.9, 0.5, 5., 5., 5.]])
        point_2 = gs.array([[0., 2., -0.1, 30., 400., 2.]])
        # Edge case for the point, angle < epsilon,
        point_small = gs.array([[-1e-7, 0., -7 * 1e-8, 6., 5., 9.]])

        self.group = group
        self.metrics = metrics

        self.left_diag_metric = left_diag_metric
        self.right_diag_metric = right_diag_metric
        self.left_metric = left_metric
        self.right_metric = right_metric
        self.point_1 = point_1
        self.point_2 = point_2
        self.point_small = point_small
예제 #2
0
    def setUp(self):
        gs.random.seed(1234)

        n = 3
        group = SpecialEuclideanGroup(n=n)

        # Diagonal left and right invariant metrics
        diag_mat_at_identity = gs.zeros([group.dimension, group.dimension])
        diag_mat_at_identity[0:3, 0:3] = 1 * gs.eye(3)
        diag_mat_at_identity[3:6, 3:6] = 1 * gs.eye(3)

        left_diag_metric = InvariantMetric(
            group=group,
            inner_product_mat_at_identity=diag_mat_at_identity,
            left_or_right='left')
        right_diag_metric = InvariantMetric(
            group=group,
            inner_product_mat_at_identity=diag_mat_at_identity,
            left_or_right='right')

        # General left and right invariant metrics
        # TODO(xxx): replace by general SPD matrix
        sym_mat_at_identity = gs.eye(group.dimension)

        left_metric = InvariantMetric(
            group=group,
            inner_product_mat_at_identity=sym_mat_at_identity,
            left_or_right='left')

        right_metric = InvariantMetric(
            group=group,
            inner_product_mat_at_identity=sym_mat_at_identity,
            left_or_right='right')

        metrics = {
            'left_diag': left_diag_metric,
            'right_diag_metric': right_diag_metric,
            'left': left_metric,
            'right': right_metric
        }

        # General case for the point
        point_1 = gs.array([-0.2, 0.9, 0.5, 5., 5., 5.])
        point_2 = gs.array([0., 2., -0.1, 30., 400., 2.])
        # Edge case for the point, angle < epsilon,
        point_small = gs.array([-1e-7, 0., -7 * 1e-8, 6., 5., 9.])

        self.group = group
        self.metrics = metrics

        self.left_diag_metric = left_diag_metric
        self.right_diag_metric = right_diag_metric
        self.left_metric = left_metric
        self.right_metric = right_metric
        self.point_1 = point_1
        self.point_2 = point_2
        self.point_small = point_small
예제 #3
0
class SE3GeodesicLoss(Function):
    """
    Geodesic Loss on the Special Euclidean Group SE(3), of 3D rotations
    and translations, computed as the square geodesic distance with respect
    to a left-invariant Riemannian metric.
    """
    def __init__(self, weight):

        assert weight.shape != SE3_DIM, 'Weight vector must be of shape [6]'

        self.weight = weight
        self.SE3_GROUP = SpecialEuclideanGroup(N)
        self.metric = InvariantMetric( 
            group=self.SE3_GROUP, 
            inner_product_mat_at_identity=np.eye(SE3_DIM) * self.weight, 
            left_or_right='left')

    def forward(self, inputs, targets):
        """
        PyTorch Custom Forward Function

        :param inputs:      Custom Function
        :param targets:     Function Inputs
        :return:
        """
        self.y_pred = inputs.numpy()
        self.y_true = targets.numpy()

        sq_geodesic_dist = self.metric.squared_dist(self.y_pred, self.y_true)
        batch_loss = np.sum(sq_geodesic_dist)

        return torch.FloatTensor([batch_loss])

    def backward(self, grad_output):
        """
        PyTorch Custom Backward Function

        :param grad_output: Gradients for equation prime
        :return:            gradient w.r.t. input
        """

        tangent_vec = self.metric.log(
            base_point=self.y_pred,
            point=self.y_true)

        grad_point = - 2. * tangent_vec

        inner_prod_mat = self.metric.inner_product_matrix(
            base_point=self.y_pred)

        riemannian_grad = np.einsum('ijk,ik->ij', inner_prod_mat, grad_point)

        sqrt_weight = np.sqrt(self.weight)
        riemannian_grad = np.multiply(riemannian_grad, sqrt_weight)

        return grad_output * torch.FloatTensor(riemannian_grad), None
예제 #4
0
    def __init__(self, weight):

        assert weight.shape != SE3_DIM, 'Weight vector must be of shape [6]'

        self.weight = weight
        self.SE3_GROUP = SpecialEuclideanGroup(N)
        self.metric = InvariantMetric( 
            group=self.SE3_GROUP, 
            inner_product_mat_at_identity=np.eye(SE3_DIM) * self.weight, 
            left_or_right='left')
예제 #5
0
    def setUp(self):
        gs.random.seed(1234)

        n = 3
        group = SpecialOrthogonalGroup(n=n)

        # Diagonal left and right invariant metrics
        diag_mat_at_identity = gs.eye(group.dimension)

        left_diag_metric = InvariantMetric(
            group=group,
            inner_product_mat_at_identity=diag_mat_at_identity,
            left_or_right='left')
        right_diag_metric = InvariantMetric(
            group=group,
            inner_product_mat_at_identity=diag_mat_at_identity,
            left_or_right='right')

        # General left and right invariant metrics
        # TODO(nina): replace by general SPD matrix
        sym_mat_at_identity = gs.eye(group.dimension)

        left_metric = InvariantMetric(
            group=group,
            inner_product_mat_at_identity=sym_mat_at_identity,
            left_or_right='left')

        right_metric = InvariantMetric(
            group=group,
            inner_product_mat_at_identity=sym_mat_at_identity,
            left_or_right='right')

        metrics = {
            'left_diag': left_diag_metric,
            'right_diag_metric': right_diag_metric,
            'left': left_metric,
            'right': right_metric
        }

        # General case for the point
        point_1 = tf.convert_to_tensor([-0.2, 0.9, 0.5])
        point_2 = tf.convert_to_tensor([0., 2., -0.1])
        # Edge case for the point, angle < epsilon,
        point_small = tf.convert_to_tensor([[-1e-7, 0., -7 * 1e-8]])

        self.group = group
        self.metrics = metrics

        self.left_diag_metric = left_diag_metric
        self.right_diag_metric = right_diag_metric
        self.left_metric = left_metric
        self.right_metric = right_metric
        self.point_1 = point_1
        self.point_2 = point_2
        self.point_small = point_small
예제 #6
0
    def __init__(self, dimension):
        assert dimension > 0
        Manifold.__init__(self, dimension)

        self.left_canonical_metric = InvariantMetric(
            group=self,
            inner_product_mat_at_identity=gs.eye(self.dimension),
            left_or_right='left')

        self.right_canonical_metric = InvariantMetric(
            group=self,
            inner_product_mat_at_identity=gs.eye(self.dimension),
            left_or_right='right')

        self.metrics = []
예제 #7
0
    def __init__(self, dimension, identity):
        super(LieGroup, self).__init__(dimension)
        self.identity = identity

        self.left_canonical_metric = InvariantMetric(
            group=self,
            inner_product_mat_at_identity=np.eye(self.dimension),
            left_or_right='left')

        self.right_canonical_metric = InvariantMetric(
            group=self,
            inner_product_mat_at_identity=np.eye(self.dimension),
            left_or_right='right')

        self.metrics = []
예제 #8
0
    def regularize_tangent_vec(self, tangent_vec, base_point, metric=None):
        if metric is None:
            metric = self.left_canonical_metric

        tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=2)
        base_point = gs.to_ndarray(base_point, to_ndim=2)

        rotations = self.rotations
        dim_rotations = rotations.dimension

        rot_tangent_vec = tangent_vec[:, :dim_rotations]
        rot_base_point = base_point[:, :dim_rotations]

        metric_mat = metric.inner_product_mat_at_identity
        rot_metric_mat = metric_mat[:, :dim_rotations, :dim_rotations]
        rot_metric = InvariantMetric(
                               group=rotations,
                               inner_product_mat_at_identity=rot_metric_mat,
                               left_or_right=metric.left_or_right)

        regularized_vec = gs.zeros_like(tangent_vec)
        regularized_vec[:, :dim_rotations] = rotations.regularize_tangent_vec(
                                                 tangent_vec=rot_tangent_vec,
                                                 base_point=rot_base_point,
                                                 metric=rot_metric)
        regularized_vec[:, dim_rotations:] = tangent_vec[:, dim_rotations:]

        return regularized_vec
예제 #9
0
    def regularize_tangent_vec(self,
                               tangent_vec,
                               base_point,
                               metric=None,
                               point_type=None):
        if point_type is None:
            point_type = self.default_point_type

        if metric is None:
            metric = self.left_canonical_metric

        if point_type == 'vector':
            tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=2)
            base_point = gs.to_ndarray(base_point, to_ndim=2)

            rotations = self.rotations
            dim_rotations = rotations.dimension

            rot_tangent_vec = tangent_vec[:, :dim_rotations]
            rot_base_point = base_point[:, :dim_rotations]

            metric_mat = metric.inner_product_mat_at_identity
            rot_metric_mat = metric_mat[:, :dim_rotations, :dim_rotations]
            rot_metric = InvariantMetric(
                group=rotations,
                inner_product_mat_at_identity=rot_metric_mat,
                left_or_right=metric.left_or_right)

            regularized_vec = gs.zeros_like(tangent_vec)
            rotations_vec = rotations.regularize_tangent_vec(
                tangent_vec=rot_tangent_vec,
                base_point=rot_base_point,
                metric=rot_metric,
                point_type=point_type)

            regularized_vec = gs.concatenate(
                [rotations_vec, tangent_vec[:, dim_rotations:]], axis=1)

        elif point_type == 'matrix':
            # TODO(nina): regularization in terms
            # of skew-symmetric matrices?
            regularized_vec = tangent_vec

        return regularized_vec
예제 #10
0
    def regularize_tangent_vec(self, tangent_vec, base_point, metric=None):
        """
        Regularize an element of the group SE(3),
        by extracting the rotation vector r from the input [r t]
        and using self.rotations.regularize.

        :param point: 6d vector, element in SE(3) represented as [r t].
        :returns self.regularized_point: 6d vector, element in SE(3)
        with self.regularized rotation.
        """
        if metric is None:
            metric = self.left_canonical_metric

        tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=2)
        base_point = gs.to_ndarray(base_point, to_ndim=2)

        rotations = self.rotations
        dim_rotations = rotations.dimension

        rot_tangent_vec = tangent_vec[:, :dim_rotations]
        rot_base_point = base_point[:, :dim_rotations]

        metric_mat = metric.inner_product_mat_at_identity
        rot_metric_mat = metric_mat[:, :dim_rotations, :dim_rotations]
        rot_metric = InvariantMetric(
            group=rotations,
            inner_product_mat_at_identity=rot_metric_mat,
            left_or_right=metric.left_or_right)

        regularized_vec = gs.zeros_like(tangent_vec)
        regularized_vec[:, :dim_rotations] = rotations.regularize_tangent_vec(
            tangent_vec=rot_tangent_vec,
            base_point=rot_base_point,
            metric=rot_metric)
        regularized_vec[:, dim_rotations:] = tangent_vec[:, dim_rotations:]

        return regularized_vec
예제 #11
0
class SE3GeodesicLoss(object):
    """
    Geodesic Loss on the Special Euclidean Group SE(3), of 3D rotations 
    and translations, computed as the square geodesic distance with respect 
    to a left-invariant Riemannian metric.
    """
    def __init__(self, weight, op_name='SE3GeodesicLoss'):

        assert weight.shape != SE3_DIM, 'Weight vector must be of shape [6]'

        self.op_name = op_name
        self.weight = weight
        self.SE3_GROUP = SpecialEuclideanGroup(N)
        self.metric = InvariantMetric(
            group=self.SE3_GROUP,
            inner_product_mat_at_identity=np.eye(SE3_DIM) * self.weight,
            left_or_right='left')

    # Python Custom Op Tensorflow Wrapper
    def py_func(self, func, inp, Tout, stateful=True, name=None, grad=None):
        """
        PyFunc defined as given by Tensorflow

        :param func:        Custom Function
        :param inp:         Function Inputs
        :param Tout:        Ouput Type of out Custom Function
        :param stateful:    Calculate Gradients when stateful is True
        :param name:        Name of the PyFunction
        :param grad:        Custom Gradient Function
        :return:
        """
        # Generate Random Gradient name to avoid conflicts with inbuilt names
        rnd_name = 'PyFuncGrad' + str(np.random.randint(0, 2**32 - 1))

        # Register Tensorflow Gradient
        tf.RegisterGradient(rnd_name)(grad)

        # Get current graph
        g = tf.get_default_graph()

        # Add gradient override map
        with g.gradient_override_map({
                'PyFunc': rnd_name,
                'PyFuncStateless': rnd_name
        }):
            return tf.py_func(func, inp, Tout, stateful=stateful, name=name)

    # Python Custom Op
    def geodesic_loss(self, y_pred, y_true, name=None):
        """
        Custom Function which defines pyfunc and gradient override
        :param x:       y_pred - predicted se(3) pose
        :param y:       y_true - ground truth se(3) pose
        :param name:    Function name
        :return:        dist - geodesic distance between y_pred and y_true
        """
        with ops.name_scope(name, self.op_name, [y_pred, y_true]) as name:
            """
            Our pyfunc op accepts 2 input parameters and returns 1 outputs
            Input Parameters:   y_pred, y_true
            Output Parameters:  geodesic distance
            """
            dist, grad = self.py_func(self.riemannian_dist_grad,
                                      [y_pred, y_true],
                                      [tf.float32, tf.float32],
                                      name=name,
                                      grad=self.riemannian_grad_op)
            return dist

    # Geodesic Loss Core Function
    def riemannian_dist_grad(self, y_pred, y_true):
        """
        Geodesic Loss Core Function

        :param y_pred: y_pred
        :param y_true: y_true
        :return: dist, grad
        """
        # Geodesic Distance
        sq_geodesic_dist = self.metric.squared_dist(y_pred, y_true)
        batch_loss = np.sum(sq_geodesic_dist).astype('float32')

        # Computation of Riemannian Gradient
        tangent_vec = self.metric.log(base_point=y_pred, point=y_true)

        grad_point = -2. * tangent_vec

        inner_prod_mat = self.metric.inner_product_matrix(base_point=y_pred)

        riemannian_grad = np.einsum('ijk,ik->ij', inner_prod_mat, grad_point)

        sqrt_weight = np.sqrt(self.weight)
        riemannian_grad = np.multiply(riemannian_grad,
                                      sqrt_weight).astype('float32')

        return batch_loss, riemannian_grad

    # Geodesic Loss Gradient Function
    def riemannian_grad_op(self, op, grads, grad_glob):
        """
        Geodesic Loss Gradient Function

        :param op:          Operation - operation.inputs  = [y_pred, y_true],
                                        operation.outputs = [dist, grad]
        :param grads:       Gradients for equation prime
        :param grad_glob:   No real use of it, but the gradient function
                            parameter size should match op.inputs
        :return:            grads * d/d_y_pred , vector of ones
        """
        # Only gradient w.r.t. y_pred is returned.
        return grads * op.outputs[1], tf.ones_like(op.outputs[1])
예제 #12
0
def main(argv):

    # TF Record
    dataset = tf.data.TFRecordDataset(FLAGS.data_dir +
                                      '/dataset_test.tfrecords')
    dataset = dataset.map(_parse_function_kingscollege)
    # dataset = dataset.repeat()
    # dataset = dataset.shuffle(FLAGS.queue_buffer)
    dataset = dataset.batch(1)
    image, vec, pose_q, pose_x = dataset.make_one_shot_iterator().get_next()

    # Network Definition
    image_resized = tf.image.resize_images(image, size=[224, 224])

    if FLAGS.loss == 'PoseNet':

        y_pred, _ = inception.inception_v3(image_resized,
                                           num_classes=7,
                                           is_training=False)
        quaternion_pred, translation_pred = tf.split(y_pred, [4, 3], axis=1)

        sess = tf.Session()

        ckpt_file = tf.train.latest_checkpoint(FLAGS.model_dir)
        tf.train.Saver().restore(sess, ckpt_file)
        print('restoring parameters from', ckpt_file)

        i = 0

        results = []

        try:

            while True:
                _image, _quaternion_true, _translation_true, _quaternion_pred, _translation_pred = \
                    sess.run([image, pose_q, pose_x, quaternion_pred, translation_pred])

                # Compute Individual Sample Error
                q1 = _quaternion_true / np.linalg.norm(_quaternion_true)
                q2 = _quaternion_pred / np.linalg.norm(_quaternion_pred)
                d = abs(np.sum(np.multiply(q1, q2)))
                theta = 2. * np.arccos(d) * 180. / np.pi
                error_x = np.linalg.norm(_translation_true - _translation_pred)

                results.append([error_x, theta])

                print('Iteration:', i, 'Error XYZ (m):', error_x,
                      'Error Q (degrees):', theta)
                i = i + 1

        except tf.errors.OutOfRangeError:
            print('End of Test Data')

        results = np.stack(results)
        results = np.median(results, axis=0)
        print('Error XYZ (m):', results[0], 'Error Q (degrees):', results[1])

    elif FLAGS.loss == 'SE3':

        y_pred, _ = inception.inception_v3(image_resized,
                                           num_classes=6,
                                           is_training=False)

        sess = tf.Session()

        ckpt_file = tf.train.latest_checkpoint(FLAGS.model_dir)
        tf.train.Saver().restore(sess, ckpt_file)
        print('restoring parameters from', ckpt_file)

        SO3_GROUP = SpecialOrthogonalGroup(3)
        SE3_GROUP = SpecialEuclideanGroup(3)
        metric = InvariantMetric(group=SE3_GROUP,
                                 inner_product_mat_at_identity=np.eye(6),
                                 left_or_right='left')

        i = 0

        results = []
        _y_pred_i = []
        _y_true_i = []
        _se3_err_i = []

        try:

            while True:
                _image, _rvec, _qvec, _tvec, _y_pred = \
                    sess.run([image, vec, pose_q, pose_x, y_pred])

                _quaternion_true = _qvec
                _quaternion_pred = SO3_GROUP.quaternion_from_rotation_vector(
                    _y_pred[0, :3])[0]

                # Compute Individual Sample Error
                q1 = _quaternion_true / np.linalg.norm(_quaternion_true)
                q2 = _quaternion_pred / np.linalg.norm(_quaternion_pred)
                d = abs(np.sum(np.multiply(q1, q2)))
                theta = 2. * np.arccos(d) * 180. / np.pi
                error_x = np.linalg.norm(_tvec - _y_pred[0, 3:])
                results.append([error_x, theta])

                # SE3 compute
                _y_true = np.concatenate((_rvec, _tvec), axis=-1)
                se3_dist = metric.squared_dist(_y_pred, _y_true)[0]

                _y_pred_i.append(_y_pred)
                _y_true_i.append(_y_true)
                _se3_err_i.append(
                    SE3_GROUP.compose(SE3_GROUP.inverse(_y_true), _y_pred))

                print('Iteration:', i, 'Error XYZ (m):', error_x,
                      'Error Q (degrees):', theta, 'SE3 dist:', se3_dist)
                i = i + 1

        except tf.errors.OutOfRangeError:
            print('End of Test Data')

        # Calculate SE3 Error Weights
        err_vec = np.vstack(_se3_err_i)
        err_weights = np.diag(np.linalg.inv(np.cov(err_vec.T)))
        err_weights = err_weights / np.linalg.norm(err_weights)
        print(err_weights)
        results = np.stack(results)
        results = np.median(results, axis=0)
        print('Error XYZ (m):', results[0], 'Error Q (degrees):', results[1])

    else:
        print('Invalid Option:', FLAGS.loss)
        raise SystemExit