Example #1
0
def reconstruction_from_json(obj):
    """
    Read a reconstruction from a json object
    """
    reconstruction = types.Reconstruction()

    # Extract cameras
    for key, value in iteritems(obj['cameras']):
        camera = camera_from_json(key, value)
        reconstruction.add_camera(camera)

    # Extract shots
    for key, value in iteritems(obj['shots']):
        shot = shot_from_json(key, value, reconstruction.cameras)
        reconstruction.add_shot(shot)

    # Extract points
    if 'points' in obj:
        for key, value in iteritems(obj['points']):
            point = point_from_json(key, value)
            reconstruction.add_point(point)

    # Extract pano_shots
    if 'pano_shots' in obj:
        reconstruction.pano_shots = {}
        for key, value in iteritems(obj['pano_shots']):
            shot = shot_from_json(key, value, reconstruction.cameras)
            reconstruction.pano_shots[shot.id] = shot

    # Extract main and unit shots
    if 'main_shot' in obj:
        reconstruction.main_shot = obj['main_shot']
    if 'unit_shot' in obj:
        reconstruction.unit_shot = obj['unit_shot']

    return reconstruction
Example #2
0
def compute_and_save_undistorted_reconstruction(
    reconstruction, tracks_manager, data, udata
):
    image_format = data.config["undistorted_image_format"]
    urec = types.Reconstruction()
    utracks_manager = pymap.TracksManager()
    undistorted_shots = []
    for shot in reconstruction.shots.values():
        if shot.camera.projection_type == "perspective":
            ucamera = osfm_u.perspective_camera_from_perspective(shot.camera)
        elif shot.camera.projection_type == "brown":
            ucamera = osfm_u.perspective_camera_from_brown(shot.camera)
        elif shot.camera.projection_type == "fisheye":
            ucamera = osfm_u.perspective_camera_from_fisheye(shot.camera)
        else:
            raise ValueError
        urec.add_camera(ucamera)
        ushot = osfm_u.get_shot_with_different_camera(urec, shot, ucamera, image_format)
        if tracks_manager:
            osfm_u.add_subshot_tracks(tracks_manager, utracks_manager, shot, ushot)
        undistorted_shots.append(ushot)

        image = data.load_image(shot.id, unchanged=True, anydepth=True)
        if image is not None:
            max_size = data.config["undistorted_image_max_size"]
            undistorted = osfm_u.undistort_image(
                shot, undistorted_shots, image, cv2.INTER_AREA, max_size
            )
            for k, v in undistorted.items():
                udata.save_undistorted_image(k, v)

    udata.save_undistorted_reconstruction([urec])
    if tracks_manager:
        udata.save_undistorted_tracks_manager(utracks_manager)

    return urec
Example #3
0
    def undistort_reconstruction(self, graph, reconstruction, data):
        urec = types.Reconstruction()
        urec.points = reconstruction.points
        ugraph = nx.Graph()

        logger.debug('Undistorting the reconstruction')
        undistorted_shots = {}
        for shot in reconstruction.shots.values():
            if shot.camera.projection_type == 'perspective':
                camera = perspective_camera_from_perspective(shot.camera)
                subshots = [get_shot_with_different_camera(shot, camera)]
            elif shot.camera.projection_type == 'brown':
                camera = perspective_camera_from_brown(shot.camera)
                subshots = [get_shot_with_different_camera(shot, camera)]
            elif shot.camera.projection_type == 'fisheye':
                camera = perspective_camera_from_fisheye(shot.camera)
                subshots = [get_shot_with_different_camera(shot, camera)]
            elif shot.camera.projection_type in ['equirectangular', 'spherical']:
                subshot_width = int(data.config['depthmap_resolution'])
                subshots = perspective_views_of_a_panorama(shot, subshot_width)

            for subshot in subshots:
                urec.add_camera(subshot.camera)
                urec.add_shot(subshot)
                add_subshot_tracks(graph, ugraph, shot, subshot)
            undistorted_shots[shot.id] = subshots

        data.save_undistorted_reconstruction([urec])
        data.save_undistorted_tracks_graph(ugraph)

        arguments = []
        for shot in reconstruction.shots.values():
            arguments.append((shot, undistorted_shots[shot.id], data))

        processes = data.config['processes']
        parallel_map(undistort_image_and_masks, arguments, processes)
Example #4
0
def resect_annotated_single_images(reconstruction, gcps, camera_models, data):
    """Resect images that do not belong to reconstruction but have enough GCPs annotated.

    Returns:
        A reconstruction with all the resected images.
    """
    not_in_rec = set()
    for gcp in gcps:
        for obs in gcp.observations:
            im = obs.shot_id
            if im not in reconstruction.shots and im in data.images():
                not_in_rec.add(im)

    resected = types.Reconstruction()
    resected.reference = reconstruction.reference
    for im in not_in_rec:
        exif = data.load_exif(im)
        camera = camera_models[exif["camera"]]
        resect_image(im, camera, gcps, reconstruction, data, resected)

    logger.info(
        f"Resected: {len(resected.shots)} shots and {len(resected.cameras)} cameras"
    )
    return resected
Example #5
0
def test_bundle_alignment_prior() -> None:
    """Test that cameras are aligned to have the Y axis pointing down."""
    camera = pygeometry.Camera.create_perspective(1.0, 0.0, 0.0)
    camera.id = "camera1"

    r = types.Reconstruction()
    r.add_camera(camera)
    shot = r.create_shot("1", camera.id,
                         pygeometry.Pose(np.random.rand(3), np.random.rand(3)))
    # pyre-fixme[8]: Attribute has type `ndarray`; used as `List[int]`.
    shot.metadata.gps_position.value = [0, 0, 0]
    shot.metadata.gps_accuracy.value = 1

    camera_priors = {camera.id: camera}
    rig_priors = dict(r.rig_cameras.items())
    gcp = []
    myconfig = config.default_config()

    reconstruction.bundle(r, camera_priors, rig_priors, gcp, myconfig)
    shot = r.shots[shot.id]

    assert np.allclose(shot.pose.translation, np.zeros(3))
    # up vector in camera coordinates is (0, -1, 0)
    assert np.allclose(shot.pose.transform([0, 0, 1]), [0, -1, 0], atol=1e-7)
Example #6
0
def test_bundle_void_gps_ignored():
    """Test that void gps values are ignored."""
    camera = pygeometry.Camera.create_perspective(1.0, 0.0, 0.0)
    camera.id = "camera1"

    r = types.Reconstruction()
    r.add_camera(camera)
    shot = r.create_shot("1", camera.id,
                         pygeometry.Pose(np.random.rand(3), np.random.rand(3)))

    camera_priors = {camera.id: camera}
    gcp = []
    myconfig = config.default_config()

    # Missing position
    shot.metadata.gps_position.value = np.zeros(3)
    shot.metadata.gps_accuracy.value = 1
    shot.metadata.gps_position.reset()
    shot.pose.set_origin(np.ones(3))
    reconstruction.bundle(r, camera_priors, {}, gcp, myconfig)
    assert np.allclose(shot.pose.get_origin(), np.ones(3))

    # Missing accuracy
    shot.metadata.gps_position.value = np.zeros(3)
    shot.metadata.gps_accuracy.value = 1
    shot.metadata.gps_accuracy.reset()
    shot.pose.set_origin(np.ones(3))
    reconstruction.bundle(r, camera_priors, {}, gcp, myconfig)
    assert np.allclose(shot.pose.get_origin(), np.ones(3))

    # Valid gps position and accuracy
    shot.metadata.gps_position.value = np.zeros(3)
    shot.metadata.gps_accuracy.value = 1
    shot.pose.set_origin(np.ones(3))
    reconstruction.bundle(r, camera_priors, {}, gcp, myconfig)
    assert np.allclose(shot.pose.get_origin(), np.zeros(3))
Example #7
0
def bootstrap_reconstruction(data, graph, im1, im2, p1, p2):
    """Start a reconstruction using two shots."""
    logger.info("Starting reconstruction with {} and {}".format(im1, im2))
    report = {
        'image_pair': (im1, im2),
        'common_tracks': len(p1),
    }

    cameras = data.load_camera_models()
    camera1 = cameras[data.load_exif(im1)['camera']]
    camera2 = cameras[data.load_exif(im2)['camera']]

    threshold = data.config['five_point_algo_threshold']
    min_inliers = data.config['five_point_algo_min_inliers']
    R, t, inliers, report['two_view_reconstruction'] = \
        two_view_reconstruction_general(p1, p2, camera1, camera2, threshold)

    logger.info("Two-view reconstruction inliers: {} / {}".format(
        len(inliers), len(p1)))

    if len(inliers) <= 5:
        report['decision'] = "Could not find initial motion"
        logger.info(report['decision'])
        return None, None, report

    reconstruction = types.Reconstruction()
    reconstruction.reference = data.load_reference()
    reconstruction.cameras = cameras

    shot1 = types.Shot()
    shot1.id = im1
    shot1.camera = camera1
    shot1.pose = types.Pose()
    shot1.metadata = get_image_metadata(data, im1)
    reconstruction.add_shot(shot1)

    shot2 = types.Shot()
    shot2.id = im2
    shot2.camera = camera2
    shot2.pose = types.Pose(R, t)
    shot2.metadata = get_image_metadata(data, im2)
    reconstruction.add_shot(shot2)

    graph_inliers = nx.Graph()
    triangulate_shot_features(graph, graph_inliers, reconstruction, im1, data.config)

    logger.info("Triangulated: {}".format(len(reconstruction.points)))
    report['triangulated_points'] = len(reconstruction.points)

    if len(reconstruction.points) < min_inliers:
        report['decision'] = "Initial motion did not generate enough points"
        logger.info(report['decision'])
        return None, None, report

    bundle_single_view(graph_inliers, reconstruction, im2, data.config)
    retriangulate(graph, graph_inliers, reconstruction, data.config)
    bundle_single_view(graph_inliers, reconstruction, im2, data.config)

    report['decision'] = 'Success'
    report['memory_usage'] = current_memory_usage()
    return reconstruction, graph_inliers, report
Example #8
0
def import_bundler(data_path,
                   bundle_file,
                   list_file,
                   track_file,
                   reconstruction_file=None):
    """
    Reconstruction and tracks graph from Bundler's output
    """

    # Init OpenSfM working folder.
    mkdir_p(data_path)

    # Copy image list.
    list_dir = os.path.dirname(list_file)
    with open_rt(list_file) as fin:
        lines = fin.read().splitlines()
    ordered_shots = []
    image_list = []
    for line in lines:
        image_path = os.path.join(list_dir, line.split()[0])
        rel_to_data = os.path.relpath(image_path, data_path)
        image_list.append(rel_to_data)
        ordered_shots.append(os.path.basename(image_path))
    with open_wt(os.path.join(data_path, "image_list.txt")) as fout:
        fout.write("\n".join(image_list) + "\n")

    # Check for bundle_file
    if not bundle_file or not os.path.isfile(bundle_file):
        return None

    with open_rt(bundle_file) as fin:
        lines = fin.readlines()
    offset = 1 if "#" in lines[0] else 0

    # header
    num_shot, num_point = map(int, lines[offset].split(" "))
    offset += 1

    # initialization
    reconstruction = types.Reconstruction()

    # cameras
    for i in range(num_shot):
        # Creating a model for each shot.
        shot_key = ordered_shots[i]
        focal, k1, k2 = map(float, lines[offset].rstrip("\n").split(" "))

        if focal > 0:
            im = imread(os.path.join(data_path, image_list[i]))
            height, width = im.shape[0:2]
            camera = pygeometry.Camera.create_perspective(
                focal / max(width, height), k1, k2)
            camera.id = "camera_" + str(i)
            camera.width = width
            camera.height = height
            reconstruction.add_camera(camera)

            # Shots
            rline = []
            for k in range(3):
                rline += lines[offset + 1 + k].rstrip("\n").split(" ")
            R = " ".join(rline)
            t = lines[offset + 4].rstrip("\n").split(" ")
            R = np.array(list(map(float, R.split()))).reshape(3, 3)
            t = np.array(list(map(float, t)))
            R[1], R[2] = -R[1], -R[2]  # Reverse y and z
            t[1], t[2] = -t[1], -t[2]
            pose = pygeometry.Pose()
            pose.set_rotation_matrix(R)
            pose.translation = t

            reconstruction.create_shot(shot_key, camera.id, pose)
        else:
            logger.warning("ignoring failed image {}".format(shot_key))
        offset += 5

    # tracks
    track_lines = []
    for i in range(num_point):
        coordinates = lines[offset].rstrip("\n").split(" ")
        color = lines[offset + 1].rstrip("\n").split(" ")
        point = reconstruction.create_point(i, list(map(float, coordinates)))
        point.color = list(map(int, color))

        view_line = lines[offset + 2].rstrip("\n").split(" ")

        num_view, view_list = int(view_line[0]), view_line[1:]

        for k in range(num_view):
            shot_key = ordered_shots[int(view_list[4 * k])]
            if shot_key in reconstruction.shots:
                camera = reconstruction.shots[shot_key].camera
                scale = max(camera.width, camera.height)
                v = "{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}".format(
                    shot_key,
                    i,
                    view_list[4 * k + 1],
                    float(view_list[4 * k + 2]) / scale,
                    -float(view_list[4 * k + 3]) / scale,
                    point.color[0],
                    point.color[1],
                    point.color[2],
                )
                track_lines.append(v)
        offset += 3

    # save track file
    with open_wt(track_file) as fout:
        fout.writelines("\n".join(track_lines))

    # save reconstruction
    if reconstruction_file is not None:
        with open_wt(reconstruction_file) as fout:
            obj = reconstructions_to_json([reconstruction])
            json_dump(obj, fout)
    return reconstruction
Example #9
0
def bootstrap_reconstruction(
    data: DataSetBase,
    tracks_manager: pymap.TracksManager,
    im1: str,
    im2: str,
    p1: np.ndarray,
    p2: np.ndarray,
) -> Tuple[Optional[types.Reconstruction], Dict[str, Any]]:
    """Start a reconstruction using two shots."""
    logger.info("Starting reconstruction with {} and {}".format(im1, im2))
    report: Dict[str, Any] = {
        "image_pair": (im1, im2),
        "common_tracks": len(p1),
    }

    camera_priors = data.load_camera_models()
    camera1 = camera_priors[data.load_exif(im1)["camera"]]
    camera2 = camera_priors[data.load_exif(im2)["camera"]]

    threshold = data.config["five_point_algo_threshold"]
    min_inliers = data.config["five_point_algo_min_inliers"]
    iterations = data.config["five_point_refine_rec_iterations"]
    R, t, inliers, report[
        "two_view_reconstruction"] = two_view_reconstruction_general(
            p1, p2, camera1, camera2, threshold, iterations)

    logger.info("Two-view reconstruction inliers: {} / {}".format(
        len(inliers), len(p1)))
    if len(inliers) <= 5:
        report["decision"] = "Could not find initial motion"
        logger.info(report["decision"])
        return None, report

    rig_camera_priors = data.load_rig_cameras()
    rig_assignments = data.load_rig_assignments_per_image()

    reconstruction = types.Reconstruction()
    reconstruction.reference = data.load_reference()
    reconstruction.cameras = camera_priors
    reconstruction.rig_cameras = rig_camera_priors

    new_shots = add_shot(data, reconstruction, rig_assignments, im1,
                         pygeometry.Pose())

    if im2 not in new_shots:
        new_shots |= add_shot(data, reconstruction, rig_assignments, im2,
                              pygeometry.Pose(R, t))

    align_reconstruction(reconstruction, None, data.config)
    triangulate_shot_features(tracks_manager, reconstruction, new_shots,
                              data.config)

    logger.info("Triangulated: {}".format(len(reconstruction.points)))
    report["triangulated_points"] = len(reconstruction.points)
    if len(reconstruction.points) < min_inliers:
        report["decision"] = "Initial motion did not generate enough points"
        logger.info(report["decision"])
        return None, report

    to_adjust = {s for s in new_shots if s != im1}
    bundle_shot_poses(reconstruction, to_adjust, camera_priors,
                      rig_camera_priors, data.config)

    retriangulate(tracks_manager, reconstruction, data.config)
    if len(reconstruction.points) < min_inliers:
        report[
            "decision"] = "Re-triangulation after initial motion did not generate enough points"
        logger.info(report["decision"])
        return None, report

    bundle_shot_poses(reconstruction, to_adjust, camera_priors,
                      rig_camera_priors, data.config)

    report["decision"] = "Success"
    report["memory_usage"] = current_memory_usage()
    return reconstruction, report
Example #10
0
def _create_reconstruction(
    n_cameras=0,
    n_shots_cam=None,
    n_pano_shots_cam=None,
    n_points=0,
    dist_to_shots=False,
    dist_to_pano_shots=False,
):
    """Creates a reconstruction with n_cameras random cameras and
    shots, where n_shots_cam is a dictionary, containing the
    camera_id and the number of shots.

    Example:
    shot_cams = {"0": 50, "1": 30}
    _create_reconstruction(2, shot_cams)

    Will create a reconstruction with two cameras and 80 shots,
    50 are associated with cam "0" and 30 with cam "1".

    n_points_in_shots is the number of points to create.
    If dist_to_shots, then observations are created and randomly
    distributed to all shots. We pick with the repeat option, thus
    if we have three shots the distribution could be
    something like: [1,2,2], [0,1,2]. We avoid things like [3,3,3]
    """
    if n_shots_cam is None:
        n_shots_cam = {}
    if n_pano_shots_cam is None:
        n_pano_shots_cam = {}

    rec = types.Reconstruction()
    if n_cameras > 0:
        for i in range(n_cameras):
            focal, k1, k2 = np.random.rand(3)
            cam = pygeometry.Camera.create_perspective(focal, k1, k2)
            cam.id = str(i)
            rec.add_camera(cam)

        shot_id = 0
        for cam_id, n_shots in n_shots_cam.items():
            for _ in range(n_shots):
                rec.create_shot(str(shot_id), cam_id)
                shot_id += 1

        shot_id = 0
        for cam_id, n_shots in n_pano_shots_cam.items():
            for _ in range(n_shots):
                rec.create_pano_shot(str(shot_id), cam_id)
                shot_id += 1

    if n_points > 0:
        for i in range(n_points):
            rec.create_point(str(i), np.random.rand(3))

        if dist_to_shots:
            n_shots = len(rec.shots)
            for pt in rec.points.values():
                choice = set(np.random.choice(n_shots, n_shots))
                if len(choice) > 1:
                    for ch in choice:
                        # create a new observation
                        obs = pysfm.Observation(100, 200, 0.5, 255, 0, 0,
                                                int(pt.id))
                        shot = rec.shots[str(ch)]
                        rec.add_observation(shot, pt, obs)
        # TODO: If required, we have to do the same for pano shots
    return rec
Example #11
0
def null_scene() -> types.Reconstruction:
    reconstruction = types.Reconstruction()
    return reconstruction
Example #12
0
def test_compute_relative_pose() -> None:
    # 4-cameras rig
    camera1 = pygeometry.Camera.create_spherical()
    camera1.id = "camera1"
    camera2 = pygeometry.Camera.create_spherical()
    camera2.id = "camera2"
    camera3 = pygeometry.Camera.create_spherical()
    camera3.id = "camera3"
    camera4 = pygeometry.Camera.create_spherical()
    camera4.id = "camera4"

    # a bit cumbersome that we need to have some reconstruction
    rec = types.Reconstruction()
    rec.add_camera(camera1)
    rec.add_camera(camera2)
    rec.add_camera(camera3)
    rec.add_camera(camera4)

    # First rig instance
    rec.create_shot(
        "shot1", "camera1",
        pygeometry.Pose(np.array([0, 0, 0]), np.array([-2, -2, 0])))
    rec.create_shot(
        "shot2", "camera2",
        pygeometry.Pose(np.array([0, 0, 0]), np.array([-3, -3, 0])))
    rec.create_shot(
        "shot3", "camera3",
        pygeometry.Pose(np.array([0, 0, 0]), np.array([-1, -3, 0])))
    rec.create_shot(
        "shot4", "camera4",
        pygeometry.Pose(np.array([0, 0, 0]), np.array([-2, -4, 0])))

    # Second rig instance (rotated by pi/2 around Z)
    pose_instance = pygeometry.Pose(np.array([0, 0, -1.5707963]))
    pose_instance.set_origin(np.array([-6, 0, 0]))
    rec.create_shot("shot5", "camera1", pose_instance)
    pose_instance.set_origin(np.array([-7, 1, 0]))
    rec.create_shot("shot6", "camera2", pose_instance)
    pose_instance.set_origin(np.array([-7, -1, 0]))
    rec.create_shot("shot7", "camera3", pose_instance)
    pose_instance.set_origin(np.array([-8, 0, 0]))
    rec.create_shot("shot8", "camera4", pose_instance)

    pose_instances = [
        [
            (
                rec.shots["shot1"],
                "camera_id_1",
            ),
            (
                rec.shots["shot2"],
                "camera_id_2",
            ),
            (
                rec.shots["shot3"],
                "camera_id_3",
            ),
            (
                rec.shots["shot4"],
                "camera_id_4",
            ),
        ],
        [
            (
                rec.shots["shot5"],
                "camera_id_1",
            ),
            (
                rec.shots["shot6"],
                "camera_id_2",
            ),
            (
                rec.shots["shot7"],
                "camera_id_3",
            ),
            (
                rec.shots["shot8"],
                "camera_id_4",
            ),
        ],
    ]

    # Compute rig cameras poses
    rig_cameras = rig.compute_relative_pose(pose_instances)

    assert np.allclose([0, -1, 0],
                       rig_cameras["camera_id_1"].pose.get_origin(),
                       atol=1e-7)
    assert np.allclose([1, 0, 0],
                       rig_cameras["camera_id_2"].pose.get_origin(),
                       atol=1e-7)
    assert np.allclose([-1, 0, 0],
                       rig_cameras["camera_id_3"].pose.get_origin(),
                       atol=1e-7)
    assert np.allclose([0, 1, 0],
                       rig_cameras["camera_id_4"].pose.get_origin(),
                       atol=1e-7)
Example #13
0
    def undistort_reconstruction(self, graph, reconstruction, data, image_format = "jpg", image_scale = 1):
        urec = types.Reconstruction()
        urec.points = reconstruction.points

        logger.debug('Undistorting the reconstruction')
        undistorted_shots = {}
        for shot in reconstruction.shots.values():
            if shot.camera.projection_type == 'perspective':
                urec.add_camera(shot.camera)
                urec.add_shot(shot)
                undistorted_shots[shot.id] = [shot]
            elif shot.camera.projection_type == 'brown':
                ushot = types.Shot()
                ushot.id = shot.id
                ushot.camera = perspective_camera_from_brown(shot.camera)
                ushot.pose = shot.pose
                ushot.metadata = shot.metadata
                urec.add_camera(ushot.camera)
                urec.add_shot(ushot)
                undistorted_shots[shot.id] = [ushot]
            elif shot.camera.projection_type == 'fisheye':
                ushot = types.Shot()
                ushot.id = shot.id
                ushot.camera = perspective_camera_from_fisheye(shot.camera)
                ushot.pose = shot.pose
                ushot.metadata = shot.metadata
                urec.add_camera(ushot.camera)
                urec.add_shot(ushot)
                undistorted_shots[shot.id] = [ushot]
            elif shot.camera.projection_type in ['equirectangular', 'spherical']:
                subshot_width = int(data.config['depthmap_resolution'])
                subshots = perspective_views_of_a_panorama(shot, subshot_width)
                for subshot in subshots:
                    urec.add_camera(subshot.camera)
                    urec.add_shot(subshot)
                    add_subshot_tracks(graph, shot, subshot)
                undistorted_shots[shot.id] = subshots
        data.save_undistorted_reconstruction([urec])

        arguments = []
        for shot in reconstruction.shots.values():
            arguments.append((shot, undistorted_shots[shot.id], data,
                              'load_image',
                              'save_undistorted_image',
                              cv2.INTER_AREA,
                              image_format,
                              image_scale))
            arguments.append((shot, undistorted_shots[shot.id], data,
                              'load_mask',
                              'save_undistorted_mask',
                              cv2.INTER_NEAREST,
                              image_format,
                              image_scale))
            arguments.append((shot, undistorted_shots[shot.id], data,
                              'load_segmentation',
                              'save_undistorted_segmentation',
                              cv2.INTER_NEAREST,
                              image_format,
                              image_scale))

        processes = data.config['processes']
        parallel_map(undistort_image, arguments, processes)
def build_reconstruction(opensfm_path, log_file, dataset_path):
    if not opensfm_path in sys.path:
        sys.path.insert(1, opensfm_path)

    from opensfm import dataset, matching, reconstruction, types, io
    from opensfm.reconstruction import TrackTriangulator
    # from opensfm import learners
    # from opensfm import log
    global types

    Rs, ts, subsampled_images = parse_log_file(log_file)

    recon = types.Reconstruction()
    camera = build_camera()

    recon.add_camera(camera)

    offset = None
    pose0 = types.Pose()
    pose0_recon = types.Pose()
    for i, _ in enumerate(Rs):
        pose = types.Pose()
        pose.rotation = Rs[i]
        # pose.set_rotation_matrix(Rs[i])

        # print pose.get_rotation_matrix()

        pose.set_rotation_matrix(pose.get_rotation_matrix().T)
        pose.set_origin(np.array(ts[i]))

        if False and i == 0:
            print subsampled_images[i]
            # pose0 = types.Pose()
            # pose0_recon = types.Pose()

            pose0.rotation = pose.rotation
            pose0.translation = pose.translation

            pose0_recon.rotation = [
                1.46327114203856, 0.6520934519442041, -0.7289951219890223
            ]
            pose0_recon.translation = [
                -151.62008675764042, 7.551077656334444, 32.03538718382186
            ]

        if False:
            R_ = np.matrix(pose.get_rotation_matrix()) * np.matrix(
                pose0.get_rotation_matrix()).T * np.matrix(
                    pose0_recon.get_rotation_matrix())
            pose.set_rotation_matrix(R_)

            # Bad cases
            if subsampled_images[i] == '1476939075123622.jpg':
                print '-' * 100
                print '{} : {}'.format(subsampled_images[i], pose.rotation)
                pose.rotation[2] = math.pi + pose.rotation[2]
            # Good cases
            if subsampled_images[i] == '1476939074934112.jpg':
                print '+' * 100
                print '{} : {}'.format(subsampled_images[i], pose.rotation)
            # if offset is None:
            #     offset = pose.get_origin() - pose0_recon.get_origin()

            # pose.set_origin(pose.get_origin() - offset)
            # pose.translation = pose.translation * 0.1

            # pose.translation = np.array(ts[i])
            # print pose.get_origin()

            # print pose.get_rotation_matrix()
            # print pose.get_rotation_matrix()
            # print pose.get_origin()
            # sys.exit(1)

            # t = pose.translation
            # t[0] = -t[0]
            # t[1] = -t[1]
            # t[2] = -t[2]
            # t[1],t[2] = t[2],t[1]
            # pose.translation = t

            # print pose.rotation
            # print pose.translation
            # print '#'*100
            # sys.exit(1)
            # R = pose.get_rotation_matrix()
            # R[:,1], R[:,2] = R[:,2], R[:,1]
            # pose.set_rotation_matrix(R)

            # R = pose.get_rotation_matrix()
            # R[:,1], R[:,2] = R[:,2], R[:,1]
            # pose.set_rotation_matrix(R.T)
            # t = pose.translation
            # t = pose.get_rotation_matrix() * np.matrix(pose.translation.reshape((3,1)))
            # print pose.translation

        pose.translation = 20.0 * pose.translation
        # print pose.translation
        # sys.exit(1)

        shot = types.Shot()
        shot.camera = camera
        shot.pose = pose
        shot.id = subsampled_images[i]

        sm = types.ShotMetadata()
        sm.orientation = 1
        sm.gps_position = [0.0, 0.0, 0.0]
        sm.gps_dop = 999999.0
        # sm.capture_time = 0.0
        shot.metadata = sm

        # add shot to reconstruction
        recon.add_shot(shot)

    data = dataset.DataSet(dataset_path)
    data.save_reconstruction([recon], 'reconstruction_gt.json')
Example #15
0
def main():
    parser = argparse.ArgumentParser(
        description='Convert COLMAP database to OpenSfM dataset')
    parser.add_argument('database', help='path to the database to be processed')
    parser.add_argument('images', help='path to the images')
    args = parser.parse_args()
    logger.info(f"Converting {args.database} to COLMAP format")

    p_db = Path(args.database)
    assert p_db.is_file()

    export_folder = p_db.parent / EXPORT_DIR_NAME
    export_folder.mkdir(exist_ok=True)
    images_path = export_folder / 'images'
    if not images_path.exists():
        os.symlink(os.path.abspath(args.images), images_path, target_is_directory=True)

    # Copy the config if this is an colmap export of an opensfm export
    if p_db.parent.name == 'colmap_export' and not (export_folder/'config.yaml').exists():
        os.symlink(p_db.parent.parent / 'config.yaml', export_folder / 'config.yaml')

    data = dataset.DataSet(export_folder)
    db = sqlite3.connect(p_db.as_posix())
    camera_map, image_map = import_cameras_images(db, data)

    # Create image_list.txt
    with open(export_folder / 'image_list.txt', 'w') as f:
        for _, (filename, _) in image_map.items():
            f.write('images/' + filename + '\n')
    data.load_image_list()

    keypoints = import_features(db, data, image_map, camera_map)
    import_matches(db, data, image_map)


    rec_cameras = p_db.parent / 'cameras.bin'
    rec_points = p_db.parent / 'points3D.bin'
    rec_images = p_db.parent / 'images.bin'
    if rec_cameras.exists() and rec_images.exists() and rec_points.exists():
        reconstruction = types.Reconstruction()
        import_cameras_reconstruction(rec_cameras, reconstruction)
        import_points_reconstruction(rec_points, reconstruction)
        tracks_manager, _ = import_images_reconstruction(rec_images,
                                                         keypoints,
                                                         reconstruction)

        data.save_reconstruction([reconstruction])
        data.save_tracks_manager(tracks_manager)

        # Save undistorted reconstruction as well
        udata = dataset.UndistortedDataSet(data, 'undistorted')
        urec = compute_and_save_undistorted_reconstruction(reconstruction, tracks_manager, data, udata)

        # Project colmap's fused pointcloud to save depths in opensfm format
        path_ply = p_db.parent / 'dense/fused.ply'
        if path_ply.is_file():
            rec_cameras = p_db.parent / 'dense/sparse/cameras.bin'
            rec_images = p_db.parent / 'dense/sparse/images.bin'
            rec_points = p_db.parent / 'points3D.bin'
            reconstruction = types.Reconstruction()
            import_cameras_reconstruction(rec_cameras, reconstruction)
            import_points_reconstruction(rec_points, reconstruction)
            _, image_ix_to_shot_id = import_images_reconstruction(rec_images,
                                                                  keypoints,
                                                                  reconstruction)
            logger.info(f"Projecting {path_ply} to depth images")
            import_depthmaps_from_fused_pointcloud(udata, urec, image_ix_to_shot_id, path_ply)
        else:
            logger.info("Not importing dense reconstruction: Didn't find {}".format(path_ply))

    else:
        logger.info("Didn't find some of the reconstruction files at {}".format(p_db.parent))

    db.close()
Example #16
0
def import_bundler(data_path, bundle_file, list_file, track_file,
                   reconstruction_file=None):
    """
    Reconstruction and tracks graph from Bundler's output
    """

    # Init OpenSfM working folder.
    mkdir_p(data_path)

    # Copy image list.
    list_dir = os.path.dirname(list_file)
    with open(list_file, 'rb') as fin:
        lines = fin.read().splitlines()
    ordered_shots = []
    image_list = []
    for line in lines:
        image_path = os.path.join(list_dir, line.split()[0])
        rel_to_data = os.path.relpath(image_path, data_path)
        image_list.append(rel_to_data)
        ordered_shots.append(os.path.basename(image_path))
    with open(os.path.join(data_path, 'image_list.txt'), 'w') as fout:
        fout.write('\n'.join(image_list) + '\n')

    # Check for bundle_file
    if not bundle_file or not os.path.isfile(bundle_file):
        return None

    with open(bundle_file, 'rb') as fin:
        lines = fin.readlines()
    offset = 1 if '#' in lines[0] else 0

    # header
    num_shot, num_point = map(int, lines[offset].split(' '))
    offset += 1

    # initialization
    reconstruction = types.Reconstruction()

    # cameras
    for i in xrange(num_shot):
        # Creating a model for each shot.
        shot_key = ordered_shots[i]
        focal, k1, k2 = map(float, lines[offset].rstrip('\n').split(' '))

        if focal > 0:
            im = imread(os.path.join(data_path, image_list[i]))
            height, width = im.shape[0:2]
            camera = types.PerspectiveCamera()
            camera.id = 'camera_' + str(i)
            camera.width = width
            camera.height = height
            camera.focal = focal / max(width, height)
            camera.k1 = k1
            camera.k2 = k2
            reconstruction.add_camera(camera)

            # Shots
            rline = []
            for k in xrange(3):
                rline += lines[offset + 1 + k].rstrip('\n').split(' ')
            R = ' '.join(rline)
            t = lines[offset + 4].rstrip('\n').split(' ')
            R = np.array(map(float, R.split())).reshape(3, 3)
            t = np.array(map(float, t))
            R[1], R[2] = -R[1], -R[2]  # Reverse y and z
            t[1], t[2] = -t[1], -t[2]

            shot = types.Shot()
            shot.id = shot_key
            shot.camera = camera
            shot.pose = types.Pose()
            shot.pose.set_rotation_matrix(R)
            shot.pose.translation = t
            reconstruction.add_shot(shot)
        else:
            print 'ignore failed image', shot_key
        offset += 5

    # tracks
    track_lines = []
    for i in xrange(num_point):
        coordinates = lines[offset].rstrip('\n').split(' ')
        color = lines[offset + 1].rstrip('\n').split(' ')
        point = types.Point()
        point.id = i
        point.coordinates = map(float, coordinates)
        point.color = map(int, color)
        reconstruction.add_point(point)

        view_line = lines[offset + 2].rstrip('\n').split(' ')

        num_view, view_list = int(view_line[0]), view_line[1:]

        for k in xrange(num_view):
            shot_key = ordered_shots[int(view_list[4 * k])]
            if shot_key in reconstruction.shots:
                camera = reconstruction.shots[shot_key].camera
                scale = max(camera.width, camera.height)
                v = '{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}'.format(
                    shot_key,
                    i,
                    view_list[4 * k + 1],
                    float(view_list[4 * k + 2]) / scale,
                    -float(view_list[4 * k + 3]) / scale,
                    point.color[0],
                    point.color[1],
                    point.color[2]
                )
                track_lines.append(v)
        offset += 3

    # save track file
    with open(track_file, 'wb') as fout:
        fout.writelines('\n'.join(track_lines))

    # save reconstruction
    if reconstruction_file is not None:
        with open(reconstruction_file, 'wb') as fout:
            obj = reconstructions_to_json([reconstruction])
            json_dump(obj, fout)
    return reconstruction
Example #17
0
def bootstrap_reconstruction(
        problem, data, graph, im1, im2, hint_forward=False):
    """
    Build 3D reconstruction based on two images `im1` and `im2`.

    See `custom_two_view_reconstruction` for the meaning of `hint_forward`.

    Returns the `Reconstruction` object, or None if reconstruction failed.
    """
    print "----------------"
    print "bootstrap_reconstruction({}, {}, hint_forward={})".format(
        im1, im2, hint_forward)

    camera1 = problem.image2camera[im1]
    camera2 = problem.image2camera[im2]
    cameras = {camera1.id: camera1, camera2.id: camera2}

    tracks, p1, p2 = matching.common_tracks(graph, im1, im2)
    print "Common tracks: {}".format(len(tracks))

    thresh = data.config.get('five_point_algo_threshold', 0.006)
    min_inliers = data.config.get('five_point_algo_min_inliers', 50)
    R, t, inliers = custom_two_view_reconstruction(
        p1, p2, camera1, camera2, thresh, hint_forward)
    print "bootstrap: R={} t={} len(inliers)={}".format(R, t, len(inliers))
    if len(inliers) <= 5:  # FIXME: put const in config
        print "bootstrap failed: not enough points in initial reconstruction"
        return

    # Reconstruction is up to scale; set translation to 1.
    # (This will be corrected later in the bundle adjustment step.)
    t /= np.linalg.norm(t)

    reco = types.Reconstruction()
    reco.cameras = cameras

    shot1 = types.Shot()
    shot1.id = im1
    shot1.camera = camera1
    shot1.pose = types.Pose()
    shot1.metadata = get_empty_metadata()
    reco.add_shot(shot1)

    shot2 = types.Shot()
    shot2.id = im2
    shot2.camera = camera2
    shot2.pose = types.Pose(R, t)
    shot2.metadata = get_empty_metadata()
    reco.add_shot(shot2)

    reconstruction.triangulate_shot_features(
        graph, reco, im1,
        data.config.get('triangulation_threshold', 0.004),
        data.config.get('triangulation_min_ray_angle', 2.0))
    if len(reco.points) < min_inliers:
        print "bootstrap failed: not enough points after triangulation"
        return

    reconstruction.bundle_single_view(graph, reco, im2, data.config)
    reconstruction.retriangulate(graph, reco, data.config)
    reconstruction.bundle_single_view(graph, reco, im2, data.config)

    debug = partial(_debug_short, graph, reco, im1, im2)
    debug("bootstraped reconstruction")

    return reco