def add_shots_to_reconstruction( shots: List[List[str]], positions: List[np.ndarray], rotations: List[np.ndarray], rig_cameras: List[pymap.RigCamera], cameras: List[pygeometry.Camera], reconstruction: types.Reconstruction, sequence_key: str, ): for camera in cameras: reconstruction.add_camera(camera) rec_rig_cameras = [] for rig_camera in rig_cameras: rec_rig_cameras.append(reconstruction.add_rig_camera(rig_camera)) for i_shots, position, rotation in zip(shots, positions, rotations): instance_id = "_".join([s[0] for s in i_shots]) rig_instance = reconstruction.add_rig_instance(pymap.RigInstance(instance_id)) rig_instance.pose = pygeometry.Pose(rotation, -rotation.dot(position)) for shot, camera in zip(i_shots, cameras): shot_id = shot[0] rig_camera_id = shot[1] shot = reconstruction.create_shot( shot_id, camera.id, pose=None, rig_camera_id=rig_camera_id, rig_instance_id=instance_id, ) shot.metadata.sequence_key.value = sequence_key
def add_observation_to_reconstruction( tracks_manager: pymap.TracksManager, reconstruction: types.Reconstruction, shot_id: str, track_id: str, ) -> None: observation = tracks_manager.get_observation(shot_id, track_id) reconstruction.add_observation(shot_id, track_id, observation)
def set_gps_bias( reconstruction: types.Reconstruction, config: Dict[str, Any], gcp: List[pymap.GroundControlPoint], use_scale: bool, ) -> Optional[Tuple[float, np.ndarray, np.ndarray]]: """Compute and set the bias transform of the GPS coordinate system wrt. to the GCP one.""" # Compute similarity ('gps_bias') that brings the reconstruction on the GCPs ONLY gps_bias = compute_reconstruction_similarity(reconstruction, gcp, config, False, use_scale) if not gps_bias: logger.warning( "Cannot align on GCPs only, GPS bias won't be compensated.") return None # Align the reconstruction on GCPs ONLY s, A, b = gps_bias A_angle_axis = cv2.Rodrigues(A)[0].flatten() logger.info( f"Applying global bias with scale {s:.5f} / translation {b} / rotation {A_angle_axis}" ) apply_similarity(reconstruction, s, A, b) # Compute per camera similarity between the GCP and the shots positions per_camera_shots = defaultdict(list) for s in reconstruction.shots.values(): per_camera_shots[s.camera.id].append(s.id) per_camera_transform = {} for camera_id, shots_id in per_camera_shots.items(): # As we re-use 'compute_reconstruction_similarity', we need to construct a 'Reconstruction' subrec = types.Reconstruction() subrec.add_camera(reconstruction.cameras[camera_id]) for shot_id in shots_id: subrec.add_shot(reconstruction.shots[shot_id]) per_camera_transform[camera_id] = compute_reconstruction_similarity( subrec, [], config, True, use_scale) if any([True for x in per_camera_transform.values() if not x]): logger.warning( "Cannot compensate some shots, GPS bias won't be compensated.") else: for camera_id, transform in per_camera_transform.items(): s, A, b = transform A_angle_axis = cv2.Rodrigues(A)[0].flatten() s, A_angle_axis, b = 1.0 / s, -A_angle_axis, -A.T.dot(b) / s logger.info( f"Camera {camera_id} bias : scale {s:.5f} / translation {b} / rotation {A_angle_axis}" ) camera_bias = pygeometry.Similarity(A_angle_axis, b, s) reconstruction.set_bias(camera_id, camera_bias) return gps_bias
def rig_instance_from_json( reconstruction: types.Reconstruction, instance_id: str, obj: Dict[str, Any] ) -> None: """ Read any rig instance from a json shot object """ reconstruction.add_rig_instance(pymap.RigInstance(instance_id)) pose = pygeometry.Pose() pose.rotation = obj["rotation"] pose.translation = obj["translation"] reconstruction.rig_instances[instance_id].pose = pose
def add_shots_to_reconstruction( shot_ids: List[str], positions: List[np.ndarray], rotations: List[np.ndarray], camera: pygeometry.Camera, reconstruction: types.Reconstruction, ): reconstruction.add_camera(camera) for shot_id, position, rotation in zip(shot_ids, positions, rotations): pose = pygeometry.Pose(rotation) pose.set_origin(position) reconstruction.create_shot(shot_id, camera.id, pose)
def add_points_to_reconstruction( points: np.ndarray, color: np.ndarray, reconstruction: types.Reconstruction ): shift = len(reconstruction.points) for i in range(points.shape[0]): point = reconstruction.create_point(str(shift + i), points[i, :]) point.color = color
def retriangulate( tracks_manager: pymap.TracksManager, reconstruction: types.Reconstruction, config: Dict[str, Any], ) -> Dict[str, Any]: """Retrianguate all points""" chrono = Chronometer() report = {} report["num_points_before"] = len(reconstruction.points) threshold = config["triangulation_threshold"] min_ray_angle = config["triangulation_min_ray_angle"] reconstruction.points = {} all_shots_ids = set(tracks_manager.get_shot_ids()) triangulator = TrackTriangulator(tracks_manager, reconstruction) tracks = set() for image in reconstruction.shots.keys(): if image in all_shots_ids: tracks.update(tracks_manager.get_shot_observations(image).keys()) for track in tracks: if config["triangulation_type"] == "ROBUST": triangulator.triangulate_robust(track, threshold, min_ray_angle) elif config["triangulation_type"] == "FULL": triangulator.triangulate(track, threshold, min_ray_angle) report["num_points_after"] = len(reconstruction.points) chrono.lap("retriangulate") report["wall_time"] = chrono.total_time() return report
def point_from_json( reconstruction: types.Reconstruction, key: str, obj: Dict[str, Any] ) -> pymap.Landmark: """ Read a point from a json object """ point = reconstruction.create_point(key, obj["coordinates"]) point.color = obj["color"] return point
def get_shot_with_different_camera( urec: types.Reconstruction, shot: pymap.Shot, image_format: str, ) -> pymap.Shot: new_shot_id = add_image_format_extension(shot.id, image_format) new_shot = urec.create_shot(new_shot_id, shot.camera.id, shot.pose) new_shot.metadata = shot.metadata return new_shot
def add_shot( data: DataSetBase, reconstruction: types.Reconstruction, rig_assignments: Dict[str, Tuple[int, str, List[str]]], shot_id: str, pose: pygeometry.Pose, ) -> Set[str]: """Add a shot to the recontruction. In case of a shot belonging to a rig instance, the pose of shot will drive the initial pose setup of the rig instance. All necessary shots and rig models will be created. """ added_shots = set() if shot_id not in rig_assignments: camera_id = data.load_exif(shot_id)["camera"] shot = reconstruction.create_shot(shot_id, camera_id, pose) shot.metadata = get_image_metadata(data, shot_id) added_shots = {shot_id} else: instance_id, _, instance_shots = rig_assignments[shot_id] created_shots = {} for shot in instance_shots: camera_id = data.load_exif(shot)["camera"] created_shots[shot] = reconstruction.create_shot( shot, camera_id, pygeometry.Pose() ) created_shots[shot].metadata = get_image_metadata(data, shot) rig_instance = reconstruction.add_rig_instance(pymap.RigInstance(instance_id)) for shot in instance_shots: _, rig_camera_id, _ = rig_assignments[shot] rig_instance.add_shot( reconstruction.rig_cameras[rig_camera_id], created_shots[shot] ) rig_instance.update_instance_pose_with_shot(shot_id, pose) added_shots = set(instance_shots) return added_shots
def add_rigs_to_reconstruction( shots: List[List[str]], positions: List[np.ndarray], rotations: List[np.ndarray], rig_cameras: List[pymap.RigCamera], reconstruction: types.Reconstruction, ): rec_rig_cameras = [] for rig_camera in rig_cameras: if rig_camera.id not in reconstruction.rig_cameras: rec_rig_cameras.append(reconstruction.add_rig_camera(rig_camera)) else: rec_rig_cameras.append(reconstruction.rig_cameras[rig_camera.id]) for i, (i_shots, position, rotation) in enumerate(zip(shots, positions, rotations)): rig_instance = reconstruction.add_rig_instance(pymap.RigInstance(i)) for j, s in enumerate(i_shots): rig_instance.add_shot(rec_rig_cameras[j], reconstruction.get_shot(s[0])) rig_instance.pose = pygeometry.Pose(rotation, -rotation.dot(position))
def shot_in_reconstruction_from_json( reconstruction: types.Reconstruction, key: str, obj: Dict[str, Any], rig_instance_id: Optional[str] = None, rig_camera_id: Optional[str] = None, is_pano_shot: bool = False, ) -> pymap.Shot: """ Read shot from a json object and append it to a reconstruction """ pose = pose_from_json(obj) if is_pano_shot: shot = reconstruction.create_pano_shot(key, obj["camera"], pose) else: shot = reconstruction.create_shot( key, obj["camera"], pose, rig_camera_id, rig_instance_id ) assign_shot_attributes(obj, shot) return shot
def perspective_views_of_a_panorama( spherical_shot: pymap.Shot, width: int, reconstruction: types.Reconstruction, image_format: str, rig_instance_count: Iterator[int], ): """Create 6 perspective views of a panorama.""" camera = pygeometry.Camera.create_perspective(0.5, 0.0, 0.0) camera.id = "perspective_panorama_camera" camera.width = width camera.height = width reconstruction.add_camera(camera) names = ["front", "left", "back", "right", "top", "bottom"] rotations = [ tf.rotation_matrix(-0 * np.pi / 2, (0, 1, 0)), tf.rotation_matrix(-1 * np.pi / 2, (0, 1, 0)), tf.rotation_matrix(-2 * np.pi / 2, (0, 1, 0)), tf.rotation_matrix(-3 * np.pi / 2, (0, 1, 0)), tf.rotation_matrix(-np.pi / 2, (1, 0, 0)), tf.rotation_matrix(+np.pi / 2, (1, 0, 0)), ] rig_instance = pymap.RigInstance(next(rig_instance_count)) rig_instance.pose = spherical_shot.pose shots = [] for name, rotation in zip(names, rotations): if name not in reconstruction.rig_cameras: rig_camera_pose = pygeometry.Pose() rig_camera_pose.set_rotation_matrix(rotation[:3, :3]) rig_camera = pymap.RigCamera(rig_camera_pose, name) reconstruction.add_rig_camera(rig_camera) rig_camera = reconstruction.rig_cameras[name] shot_id = add_image_format_extension( f"{spherical_shot.id}_perspective_view_{name}", image_format ) shot = reconstruction.create_shot(shot_id, camera.id) shot.metadata = spherical_shot.metadata rig_instance.add_shot(rig_camera, shot) shots.append(shot) reconstruction.add_rig_instance(rig_instance) return shots
def check_merge_partial_reconstructions(self): if self.reconstructed(): data = DataSet(self.opensfm_project_path) reconstructions = data.load_reconstruction() tracks_manager = data.load_tracks_manager() if len(reconstructions) > 1: log.ODM_WARNING( "Multiple reconstructions detected (%s), this might be an indicator that some areas did not have sufficient overlap" % len(reconstructions)) log.ODM_INFO("Attempting merge") merged = Reconstruction() merged.set_reference(reconstructions[0].reference) for ix_r, rec in enumerate(reconstructions): if merged.reference != rec.reference: # Should never happen continue log.ODM_INFO("Merging reconstruction %s" % ix_r) for camera in rec.cameras.values(): merged.add_camera(camera) for point in rec.points.values(): try: new_point = merged.create_point( point.id, point.coordinates) new_point.color = point.color except RuntimeError as e: log.ODM_WARNING("Cannot merge shot id %s (%s)" % (shot.id, str(e))) continue for shot in rec.shots.values(): merged.add_shot(shot) try: obsdict = tracks_manager.get_shot_observations( shot.id) except RuntimeError: log.ODM_WARNING( "Shot id %s missing from tracks_manager!" % shot.id) continue for track_id, obs in obsdict.items(): if track_id in merged.points: merged.add_observation(shot.id, track_id, obs) data.save_reconstruction([merged])