コード例 #1
0
def test_composition_with_identity_works():
    so3 = SO3.from_angle_axis(np.pi / 4, np.array([[0, 1, 0]]).T)

    comp_with_identity = so3.compose(SO3())
    comp_from_identity = SO3().compose(so3)

    np.testing.assert_almost_equal(comp_with_identity.matrix, so3.matrix, 14)
    np.testing.assert_almost_equal(comp_from_identity.matrix, so3.matrix, 14)
コード例 #2
0
def test_construct_with_matrix():
    R = np.array([[0, 0, 1], [1, 0, 0], [0, 1, 0]])
    so3 = SO3(R)
    np.testing.assert_array_equal(so3.matrix, R)

    # Matrices not elements of SO(3) should be fitted to SO(3)
    R_noisy = R + 0.1 + np.random.rand(3)
    so3_fitted = SO3(R_noisy)
    R_fitted = so3_fitted.matrix

    assert np.any(np.not_equal(R_fitted, R_noisy))
    np.testing.assert_almost_equal(np.linalg.det(R_fitted), 1, 14)
    np.testing.assert_almost_equal((R_fitted.T @ R_fitted), np.identity(3), 14)
コード例 #3
0
ファイル: test_se3.py プロジェクト: tussedrotten/pylie
def test_construct_with_tuple():
    so3 = SO3() + np.array([[0.1, -0.3, 0.2]]).T
    t = np.array([[1, 2, 3]]).T
    se3 = SE3((so3, t))

    np.testing.assert_equal(se3.rotation.matrix, so3.matrix)
    np.testing.assert_equal(se3.translation, t)
コード例 #4
0
def test_difference_for_simple_rotation_with_operator_works():
    X = SO3()
    theta_vec = 3 * np.pi / 4 * np.array([[1, 0, -1]]).T / np.sqrt(2)
    Y = SO3.Exp(theta_vec)
    theta_vec_diff = Y - X

    np.testing.assert_almost_equal(theta_vec_diff, theta_vec, 14)
コード例 #5
0
def test_difference_for_simple_rotation_works():
    X = SO3()
    theta_vec = np.pi / 3 * np.array([[0, 1, 0]]).T
    Y = SO3.Exp(theta_vec)
    theta_vec_diff = Y.ominus(X)

    np.testing.assert_array_equal(theta_vec_diff, theta_vec)
コード例 #6
0
def test_to_rotation_matrix_results_in_close_rotation():
    angle = 0.5 * np.pi
    axis = np.array([[1, 0, 0]]).T
    R = SO3.Exp(angle * axis).matrix

    # Invalidate a valid rotation matrix by scaling it.
    R_scaled = 3 * R

    # Fit to SO(3).
    R_closest = SO3.to_so3_matrix(R_scaled)

    # Result should be the same rotation matrix.
    np.testing.assert_almost_equal(R_closest, R, 14)

    # Perturb the rotation matrix with random noise.
    R_noisy = R + 0.01 * np.random.rand(3, 3)

    # Fit to SO(3)
    so3_closest = SO3(R_noisy)

    # Extract angle-axis representation.
    angle_closest, axis_closest = so3_closest.Log(True)

    # Result should be close to the same rotation.
    np.testing.assert_almost_equal(angle_closest, angle, 2)
    np.testing.assert_almost_equal(axis_closest, axis, 2)
コード例 #7
0
ファイル: se3.py プロジェクト: tussedrotten/pylie
    def __init__(self, pose_tuple=(SO3(), np.zeros((3, 1)))):
        """Constructs an SE(3) element.
        The default is the identity element.

        :param pose_tuple: A tuple (rotation (SO3), translation (3D column vector) (optional).
        """
        self.rotation, self.translation = pose_tuple
コード例 #8
0
ファイル: se3.py プロジェクト: tussedrotten/pylie
    def from_matrix(cls, T):
        """Construct an SE(3) element corresponding from a pose matrix.
        The rotation is fitted to the closest rotation matrix, the bottom row of the 4x4 matrix is ignored.

        :param T: 4x4 or 3x4 pose matrix.
        :return: The SE(3) element.
        """

        return cls((SO3(T[:3, :3]), T[:3, 3:4]))
コード例 #9
0
    def looks_at_pose(camera_pos_w: np.ndarray, target_pos_w: np.ndarray, up_vector_w: np.ndarray):
        cam_to_target_w = target_pos_w - camera_pos_w
        cam_z_w = cam_to_target_w.flatten() / np.linalg.norm(cam_to_target_w)

        cam_to_right_w = np.cross(-up_vector_w.flatten(), cam_z_w)
        cam_x_w = cam_to_right_w / np.linalg.norm(cam_to_target_w)

        cam_y_w = np.cross(cam_z_w, cam_x_w)

        return SE3((SO3(np.vstack((cam_x_w, cam_y_w, cam_z_w)).T), camera_pos_w))
コード例 #10
0
ファイル: test_se3.py プロジェクト: tussedrotten/pylie
def test_log_with_no_rotation():
    X = SE3((SO3(), np.array([[-1, 2, -3]]).T))

    xi_vec = X.Log()

    # Translation part:
    np.testing.assert_equal(xi_vec[:3], X.translation)

    # Rotation part:
    np.testing.assert_equal(xi_vec[3:], np.zeros((3, 1)))
コード例 #11
0
def estimate_two_view_relative_pose(frame0: MatchedFrame, kp_0: np.ndarray,
                                    frame1: MatchedFrame, kp_1: np.ndarray):
    num_matches = kp_0.shape[1]
    if num_matches < 8:
        return None

    # Compute fundamental matrix from matches.
    F_0_1, _ = cv2.findFundamentalMat(kp_1.T, kp_0.T, cv2.FM_8POINT)

    # Extract the calibration matrices.
    K_0 = frame0.camera_model().calibration_matrix()
    K_1 = frame1.camera_model().calibration_matrix()

    # Compute the essential matrix from the fundamental matrix.
    E_0_1 = K_0.T @ F_0_1 @ K_1

    # Compute the relative pose.
    # Transform detections to normalized image plane (since cv2.recoverPose() only supports common K)
    kp_n_0 = frame0.camera_model().pixel_to_normalised(kp_0)
    kp_n_1 = frame1.camera_model().pixel_to_normalised(kp_1)
    K_n = np.identity(3)
    _, R_0_1, t_0_1, _ = cv2.recoverPose(E_0_1, kp_n_1.T, kp_n_0.T, K_n)

    return SE3((SO3(R_0_1), t_0_1))
コード例 #12
0
# Plot the body frame (a body-fixed Forward-Right-Down (FRD) frame).
roll = np.radians(-10)
pitch = np.radians(0)
yaw = np.radians(135)
t_w_b = np.array([[-10, -10, -2]]).T
T_w_b = SE3((SO3.from_roll_pitch_yaw(roll, pitch, yaw), t_w_b))
vg.plot_pose(ax, T_w_b.to_tuple(), scale=3, text='$\mathcal{F}_b$')

# Plot the camera frame.
# The camera is placed 2 m directly above the body origin.
# Its optical axis points to the left (in opposite direction of the y-axis in F_b).
# Its y-axis points downwards along the z-axis of F_b.
R_b_c = np.array([[1, 0, 0], [0, 0, -1], [0, 1, 0]])
t_b_c = np.array([[0, 0, -2]]).T
T_b_c = SE3((SO3(R_b_c), t_b_c))
T_w_c = T_w_b @ T_b_c
vg.plot_pose(ax, T_w_c.to_tuple(), scale=3, text='$\mathcal{F}_c$')

# Plot obstacle frame.
# The cube is placed at (North: 10 m, East: 10 m, Down: -1 m).
# Its top points upwards, and its front points south.
R_w_o = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, -1]])
t_w_o = np.array([[10, 10, -1]]).T
T_w_o = SE3((SO3(R_w_o), t_w_o))
vg.plot_pose(ax, T_w_o.to_tuple(), scale=3, text='$\mathcal{F}_o$')

# Plot the cube with sides 3 meters.
points_o = vg.utils.generate_box(scale=3)
points_w = T_w_o * points_o
vg.utils.plot_as_box(ax, points_w)
コード例 #13
0
def test_adjoint_returns_rotation_matrix():
    so3_1 = SO3()
    so3_2 = SO3.from_angle_axis(3 * np.pi / 4, np.array([[1, 0, 0]]).T)

    np.testing.assert_array_equal(so3_1.adjoint(), so3_1.matrix)
    np.testing.assert_array_equal(so3_2.adjoint(), so3_2.matrix)
コード例 #14
0
def test_construct_identity_with_no_args():
    so3 = SO3()
    np.testing.assert_array_equal(so3.matrix, np.identity(3))
コード例 #15
0
def test_has_len_that_returns_correct_dimension():
    X = SO3()

    # Should have 6 DOF.
    np.testing.assert_equal(len(X), 3)
コード例 #16
0
ファイル: dataset.py プロジェクト: tussedrotten/sfm_example
def read_bundler_file(filename,
                      img_paths,
                      shortest_track_length=3,
                      principal_point=np.array([[702, 468]]).T):
    # We will use a camera coordinate system where z points forward, y down.
    pose_bundlerc_c = SE3((SO3(np.array([[1, 0, 0], [0, -1, 0],
                                         [0, 0, -1]])), np.zeros([3, 1])))

    matched_frames = []
    sfm_map = SfmMap()

    with open(filename, 'r') as file:
        line = file.readline()
        if not line.startswith("# Bundle file v0.3"):
            warnings.warn(
                "Warning: Expected v0.3 Bundle file, but first line did not match the convention"
            )

        # Read number of cameras and points.
        num_cameras, num_points = [int(x) for x in next(file).split()]

        # Initialize list of matched frames.
        matched_frames = num_cameras * [None]

        # Read all camera parameters.
        for i in range(num_cameras):
            # Read the intrinsics from the first line.
            f, k1, k2 = [float(x) for x in next(file).split()]

            # We will assume that the data has been undistorted (for simplicity).
            if k1 != 0 or k2 != 0:
                warnings.warn(
                    "The current implementation assumes undistorted data. Distortion parameters are ignored"
                )

            # Read the rotation matrix from the next three lines.
            R = np.array([[float(x) for x in next(file).split()]
                          for y in range(3)]).reshape([3, 3])

            # Read the translation from the last line.
            t = np.array([float(x)
                          for x in next(file).split()]).reshape([3, 1])

            # Add matched frame to list.
            f = f * np.ones([2, 1])
            matched_frames[i] = MatchedFrame(
                i, PerspectiveCamera(f, principal_point), img_paths[i])
            sfm_map.add_keyframe(
                Keyframe(matched_frames[i],
                         SE3((SO3(R), t)).inverse() @ pose_bundlerc_c))

        # Read the points and matches.
        for i in range(num_points):
            # First line is the position of the point.
            p_w = np.array([float(x)
                            for x in next(file).split()]).reshape([3, 1])

            # Next line is the color.
            color = np.array([int(x)
                              for x in next(file).split()]).reshape([3, 1])

            # The last line is the correspondences in each image where the point is observed.
            view_list = next(file).split()
            num_observed = int(view_list[0])

            # Drop observation if seen from less than shortest_track_length cameras.
            if num_observed < shortest_track_length:
                continue

            # Add observations and detections for each camera the point is observed in.
            curr_track = FeatureTrack()
            curr_map_point = MapPoint(i, p_w)

            for c in range(num_observed):
                view_data = view_list[1 + 4 * c:1 + 4 * (c + 1)]
                cam_ind = int(view_data[0])
                det_id = int(view_data[1])

                # We have to invert the y-values because we have chosen to let y point downwards.
                det_point = np.array([[
                    float(view_data[2]), -float(view_data[3])
                ]]).T + principal_point

                curr_track.add_observation(matched_frames[cam_ind], det_id)
                matched_frames[cam_ind].add_keypoint(
                    det_id, KeyPoint(det_point, color, curr_track))
                curr_map_point.add_observation(sfm_map.get_keyframe(cam_ind),
                                               det_id)

            sfm_map.add_map_point(curr_map_point)

    for frame in matched_frames:
        frame.update_covisible_frames()

    return matched_frames, sfm_map
コード例 #17
0
def main():
    # Define camera parameters.
    f = np.array([[100, 100]]).T  # "Focal lengths"  [fu, fv].T
    c = np.array([[50, 50]]).T  # Principal point [cu, cv].T

    # Define camera pose distribution.
    R_w_c = np.array([[0, 0, 1], [1, 0, 0], [0, 1, 0]])
    t_w_c = np.array([[1, 0, 0]]).T
    mean_T_w_c = SE3((SO3(R_w_c), t_w_c))
    Sigma_T_w_c = np.diag(np.array([0.1, 0.1, 0.1, 0.01, 0.01, 0.01])**2)

    # Define pixel position distribution.
    mean_u = np.array([[50, 50]]).T
    Sigma_u = np.diag(np.array([1, 1])**2)

    # Define depth distribution.
    mean_z = 10
    var_z = 0.1**2

    # TODO 2: Approximate the distribution of the 3D point in world coordinates:
    # TODO 2: Propagate the expected point.
    x_w = np.ones((3, 1))  # Mock implementation, do something else!

    # TODO 2: Propagate the uncertainty.
    cov_x_w = np.identity(3)  # Mock implementation, do something else!
    print(cov_x_w)

    # Simulate points from the true distribution.
    num_draws = 1000
    random_poses = draw_random_poses(mean_T_w_c, Sigma_T_w_c, num_draws)
    random_u = np.random.multivariate_normal(mean_u.flatten(), Sigma_u,
                                             num_draws).T
    random_z = np.random.normal(mean_z, np.sqrt(var_z), num_draws)
    rand_x_c = backproject(random_u, random_z, f, c)
    rand_x_w = np.zeros((3, num_draws))
    for i in range(num_draws):
        rand_x_w[:, [i]] = random_poses[i] * rand_x_c[:, [i]]

    # Plot result.
    # Use Qt 5 backend in visualisation.
    matplotlib.use('qt5agg')

    # Create figure and axis.
    fig = plt.figure()
    ax = plt.axes(projection='3d')
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')

    # Plot camera poses.
    vg.plot_pose(ax, mean_T_w_c.to_tuple())

    # Plot simulated points.
    ax.plot(rand_x_w[0, :], rand_x_w[1, :], rand_x_w[2, :], 'k.', alpha=0.1)

    # Plot the estimated mean pose.
    vg.plot_covariance_ellipsoid(ax, x_w, cov_x_w)

    # Show figure.
    vg.plot.axis_equal(ax)
    plt.show()
コード例 #18
0
def poses_and_cameras():
    # Use Qt 5 backend in visualisation.
    matplotlib.use('qt5agg')

    # Create figure and axis.
    fig = plt.figure()
    ax = plt.axes(projection='3d')
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')

    # Plot the pose of the world North-East-Down (NED) frame (relative to the world frame).
    T_w_w = SE3()
    vg.plot_pose(ax, T_w_w.to_tuple(), scale=3, text='$\mathcal{F}_w$')

    # Plot the body frame (a body-fixed Forward-Right-Down (FRD) frame).
    roll = np.radians(-10)
    pitch = np.radians(0)
    yaw = np.radians(135)
    t_w_b = np.array([[-10, -10, -2]]).T
    T_w_b = SE3((SO3.from_roll_pitch_yaw(roll, pitch, yaw), t_w_b))
    vg.plot_pose(ax, T_w_b.to_tuple(), scale=3, text='$\mathcal{F}_b$')

    # Plot the camera frame.
    # The camera is placed 2 m directly above the body origin.
    # Its optical axis points to the left (in opposite direction of the y-axis in F_b).
    # Its y-axis points downwards along the z-axis of F_b.
    R_b_c = np.array([[1, 0, 0],
                      [0, 0, -1],
                      [0, 1, 0]])
    t_b_c = np.array([[0, 0, -2]]).T
    T_b_c = SE3((SO3(R_b_c), t_b_c))
    T_w_c = T_w_b @ T_b_c
    vg.plot_pose(ax, T_w_c.to_tuple(), scale=3, text='$\mathcal{F}_c$')

    # Plot obstacle frame.
    # The cube is placed at (North: 10 m, East: 10 m, Down: -1 m).
    # Its top points upwards, and its front points south.
    R_w_o = np.array([[-1, 0, 0],
                      [0, 1, 0],
                      [0, 0, -1]])
    t_w_o = np.array([[10, 10, -1]]).T
    T_w_o = SE3((SO3(R_w_o), t_w_o))
    vg.plot_pose(ax, T_w_o.to_tuple(), scale=3, text='$\mathcal{F}_o$')

    # Plot the cube with sides 3 meters.
    points_o = vg.utils.generate_box(scale=3)
    points_w = T_w_o * points_o
    vg.utils.plot_as_box(ax, points_w)

    # Plot the image plane.
    img_plane_scale = 3
    K = np.array([[50, 0, 40],
                  [0, 50, 30],
                  [0, 0, 1]])
    vg.plot_camera_image_plane(ax, K, T_w_c.to_tuple(), scale=img_plane_scale)

    # Project the box onto the normalised image plane (at z=img_plane_scale).
    points_c = T_w_c.inverse() @ T_w_o * points_o
    xn = points_c / points_c[2, :]
    xn_w = T_w_c * (img_plane_scale * xn)
    vg.utils.plot_as_box(ax, xn_w)

    # Show figure.
    vg.plot.axis_equal(ax)
    ax.invert_zaxis()
    ax.invert_yaxis()
    plt.show()
コード例 #19
0
def test_perturbation_by_adding_a_simple_rotation_with_operator_works():
    ident = SO3()
    pert = ident + (np.pi / 2 * np.array([[0, 1, 0]]).T)
    expected = np.array([[0, 0, 1], [0, 1, 0], [-1, 0, 0]])

    np.testing.assert_almost_equal(pert.matrix, expected, 14)
コード例 #20
0
ファイル: test_so3.py プロジェクト: tussedrotten/pylie
def test_string_representation_is_matrix():
    X = SO3()

    # Should be the same as the string representation of the matrix.
    np.testing.assert_equal(str(X), str(X.matrix))