示例#1
0
class TestGraphMethods(TestCase):
    def setUp(self):
        self.__graph = Graph()

    def test_structure(self):
        self.assertIsInstance(self.__graph, Graph)

    def test_add_node(self):
        self.assertEqual(len(self.__graph.get_all_node()), 0)
        self.assertNotEqual(len(self.__graph.get_all_node()), 1)

        self.__graph.add_node(node=1)

        self.assertNotEqual(len(self.__graph.get_all_node()), 0)
        self.assertEqual(len(self.__graph.get_all_node()), 1)

    def test_add_edges(self):

        # Adding nodes
        self.__graph.add_node(node='B')
        self.__graph.add_node(node='A')

        # Connecting two nodes
        self.__graph.add_edge(edge=('A', 'B'))

        # Adding nodes
        self.__graph.add_node(node='C')
        self.__graph.add_node(node='D')

        self.__graph.add_edge(edge=('A', 'D'))

        # Assertions
        self.assertTrue(self.__graph.is_connected('A', 'B'))
        self.assertTrue(self.__graph.is_connected('B', 'A'))
        self.assertFalse(self.__graph.is_connected('C', 'D'))
        self.assertFalse(self.__graph.is_connected('B', 'D'))

        with self.assertRaises(TypeError):
            self.__graph.add_edge(edge=('Z', 'F'))

    def test_if_two_nodes_was_on_the_same_network(self):

        # Adding nodes
        self.__graph.add_node(node='C')
        self.__graph.add_node(node='D')
        self.__graph.add_node(node='A')
        self.__graph.add_node(node='B')

        self.__graph.add_edge(edge=('A', 'D'))

        # Assertions
        self.assertFalse(self.__graph.same_network(('A', 'B')))
        self.assertFalse(self.__graph.same_network(('B', 'A')))
        self.assertFalse(self.__graph.same_network(('C', 'D')))
        self.assertFalse(self.__graph.same_network(('B', 'D')))
        self.assertTrue(self.__graph.same_network(('A', 'D')))
        self.assertTrue(self.__graph.same_network(('D', 'A')))
示例#2
0
def estimate_extrinsics_pnp(tagpose_estimator,
                            cam_intrinsic,
                            cam_dist,
                            point2d_coord,
                            point2d_cid,
                            point2d_fid,
                            point2d_pid,
                            point2d_mid,
                            verbose=0):
    """ Estimates extrinsic parameters for each camera from the given 2D point correspondences alone.
        It estimates the essential matrix for camera pairs along the observation graph.

    Input:
        tagpose_estimator: custom object, Estimates the pose between a camera and the calibration objects.
        cam_intrinsic: list of 3x3 np.array, Intrinsic calibration of each camera.
        cam_dist: list of 1x5 np.array, Distortion coefficients following the OpenCV pinhole camera model.
        point2d_coord: Nx2 np.array, Array containing 2D coordinates of N points.
        point2d_cid: Nx1 np.array, Array containing the camera id for each of the N points.
        point2d_fid: Nx1 np.array, Array containing the frame id for each of the N points.
        point2d_pid: Nx1 np.array, Array containing a unique point id for each of the N points.
        point2d_mid: Nx1 np.array, Array containing a marker-unique id for each of the N points.

    Returns:
        cam_extrinsic: list of 4x4 np.array, Intrinsic calibration of each camera.
        calib_object_points3d: Mx3 np.array, 3D Points of the calibration object in a object based frame.
    """
    assert len(cam_intrinsic) >= 2, "Too little cameras."
    assert len(cam_intrinsic) == len(cam_dist), "Shape mismatch."
    assert len(point2d_cid.shape) == 1, "Shape mismatch."
    assert len(point2d_fid.shape) == 1, "Shape mismatch."
    assert len(point2d_pid.shape) == 1, "Shape mismatch."
    assert len(point2d_mid.shape) == 1, "Shape mismatch."
    assert point2d_coord.shape[0] == point2d_cid.shape[0], "Shape mismatch."
    assert point2d_coord.shape[0] == point2d_fid.shape[0], "Shape mismatch."
    assert point2d_coord.shape[0] == point2d_pid.shape[0], "Shape mismatch."
    assert point2d_coord.shape[0] == point2d_mid.shape[0], "Shape mismatch."
    assert len(cam_intrinsic) == len(
        np.unique(point2d_cid).flatten().tolist()), "Shape mismatch."

    if verbose > 0:
        print('\n\n------------')
        print('- Estimating extrinsic parameters by solving PNP problems')

    num_cams = len(cam_intrinsic)
    num_frames = np.max(point2d_fid) + 1

    # get model shape
    calib_object_points3d = tagpose_estimator.object_points.copy()

    # 1. Iterate cams and estimate relative pose to the calibration object for each frame
    scores_object, T_obj2cam = estimate_and_score_object_poses(
        tagpose_estimator, point2d_coord, point2d_cid, point2d_fid,
        point2d_mid, cam_intrinsic, cam_dist)

    def _calc_score(T1, T2):
        """ Estimates how closely T1 * T2 = eye() holds. """
        R = np.matmul(T1, T2) - np.eye(T1.shape[0])
        return np.sum(np.abs(R))  # frobenius norm

    # try to find the pair of frames which worked best --> estimate relative camera pose from there
    scores_rel_calib = dict(
    )  # store how good this guess seems to be for calibrating a cam pair
    for fid1 in range(num_frames):  # for each pair of frames
        for fid2 in range(fid1, num_frames):
            scores_rel_calib[fid1, fid2] = dict()
            for cid1 in range(num_cams):  # check each pair of cams
                for cid2 in range(cid1 + 1, num_cams):
                    # check if its valid
                    if (T_obj2cam[fid1][cid2] is None) or (T_obj2cam[fid1][cid1] is None) or \
                            (T_obj2cam[fid2][cid2] is None) or (T_obj2cam[fid2][cid1] is None):
                        s_rel = float('inf')
                    else:

                        # calculate the transformation cam1 -> cams2 using fid1
                        T12_fid1 = np.matmul(
                            T_obj2cam[fid1][cid2],
                            np.linalg.inv(T_obj2cam[fid1][cid1]))

                        # calculate the transformation cam2 -> cams1 using fid2
                        T21_fid2 = np.matmul(
                            T_obj2cam[fid2][cid1],
                            np.linalg.inv(T_obj2cam[fid2][cid2]))

                        # for perfect estimations the two mappings should be the inverse of each others
                        s_rel = _calc_score(T12_fid1, T21_fid2)

                    scores_rel_calib[fid1, fid2][cid1, cid2] = s_rel

    # 3. Find out which frames are optimal for a given cam pair
    cam_pair_best_fid = dict()
    for cid1 in range(num_cams):
        for cid2 in range(cid1 + 1, num_cams):
            min_fid = None
            min_v = float('inf')
            for fid_pair, score_dict in scores_rel_calib.items():
                # get an initial value
                if min_fid is None:
                    min_fid = fid_pair
                    min_v = score_dict[cid1, cid2]
                    continue

                # if current best is worse than current item replace
                if min_v > score_dict[cid1, cid2]:
                    min_fid = fid_pair
                    min_v = score_dict[cid1, cid2]

            cam_pair_best_fid[cid1, cid2] = min_fid

    # 3. Build observation graph and use djikstra to estimate relative camera poses
    observation_graph = Graph()
    for cid in range(num_cams):
        observation_graph.add_node(cid)

    # populate with edges
    score_accumulated = [0 for _ in range(num_cams)
                         ]  # accumulate score for each cam
    for cid1 in range(num_cams):
        for cid2 in range(cid1 + 1, num_cams):
            fid_pair = cam_pair_best_fid[cid1, cid2]
            s = scores_rel_calib[fid_pair][cid1, cid2]
            observation_graph.add_edge(cid1, cid2, s)
            observation_graph.add_edge(cid2, cid1, s)
            score_accumulated[cid1] += s
            score_accumulated[cid2] += s

    # root cam (the one that has the lowest overall score)
    root_cam_id = np.argmin(np.array(score_accumulated))

    if verbose > 1:
        print('- Accumulated score (lower is better): ', score_accumulated)
        print('- Choosing root cam', root_cam_id)

    # 4. Determine which relative poses to estimate
    # use Dijkstra to find "cheapest" path (i.e. the one with most observations) from the starting cam to all others
    cam_path = dict(
    )  # contains how to get from the starting cam to another one cam_path[target_cam] = [path]
    for cid in range(num_cams):
        if cid == root_cam_id:
            cam_path[cid] = [cid]
            continue
        cost, camchain = shortest_path(observation_graph, root_cam_id, cid)
        cam_path[cid] = camchain

    if verbose > 1:
        for k, v in cam_path.items():
            print('- Camchain to %d: ' % k, v)

    # 5. Put together the relative camera poses
    relative_pose = dict()  # contains the trafo from start -> target
    relative_pose_pair = dict(
    )  # contains already estimated poses between cameras;
    # is the trafo from j -> i;  xi = relative_pose_pair[i, j] * xj
    for target_camid in range(num_cams):
        if target_camid == root_cam_id:
            relative_pose[target_camid] = np.eye(4)
            continue

        M = np.eye(4)  # this is the trafo from start -> X
        for i in range(len(cam_path[target_camid]) - 1):  # traverse cam path
            # get current cam_pair on the cam_path
            inter_camid1 = cam_path[target_camid][
                i]  # this is where we currently are
            inter_camid2 = cam_path[target_camid][
                i + 1]  # this is where we want to transform to

            swapped = False
            if inter_camid2 < inter_camid1:
                t = inter_camid2
                inter_camid2 = inter_camid1
                inter_camid1 = t
                swapped = True

            if verbose > 1:
                print(
                    '- Attempting to estimate the relative pose from cam %d --> %d'
                    % (inter_camid1, inter_camid2))

            # calculate only when not calculated yet
            if (inter_camid1, inter_camid2) not in relative_pose_pair.keys():
                fid1, fid2 = cam_pair_best_fid[inter_camid1, inter_camid2]

                msg = "Calibration impossible! There is no way feasible way to calibrate cam%d and cam%d." % \
                (inter_camid1, inter_camid2)
                assert T_obj2cam[fid1][inter_camid1] is not None, msg
                assert T_obj2cam[fid1][inter_camid2] is not None, msg

                # calculate the transformation cam1 -> cams2 using the optimal fids
                T12 = np.matmul(T_obj2cam[fid1][inter_camid1],
                                np.linalg.inv(T_obj2cam[fid1][inter_camid2]))
                relative_pose_pair[inter_camid1, inter_camid2] = T12

            delta = relative_pose_pair[inter_camid1, inter_camid2]
            if swapped:
                delta = np.linalg.inv(delta)

            # accumulate trafos
            M = np.matmul(delta, M)
        relative_pose[target_camid] = M

    if verbose > 0:
        print('- Extrinsics estimated')

    if verbose > 2:
        for cid in range(num_cams):
            print('\n- Trafo Root (%d) --> %d' % (root_cam_id, cid))
            print(relative_pose[cid])
            print('')

    cam_extrinsic = list()
    for cid in range(num_cams):
        cam_extrinsic.append(relative_pose[cid])

    # 6. Figure out the object poses (if there is no observation its impossible)
    object_poses = greedy_pick_object_pose(scores_object, T_obj2cam,
                                           relative_pose, verbose)

    cam_extrinsic, object_poses = _center_extrinsics(
        cam_extrinsic, object_poses)  # ensure camera 0 is the world center
    point3d_coord, pid2d_to_pid3d = calc_3d_object_points(
        calib_object_points3d, object_poses, point2d_fid, point2d_cid,
        point2d_mid)
    return cam_extrinsic, point3d_coord, pid2d_to_pid3d, object_poses