def all_common_tracks( tracks_manager: pysfm.TracksManager, include_features: bool = True, min_common: int = 50, ) -> t.Dict[t.Tuple[str, str], t.Union[TPairTracks, t.List[str]]]: """List of tracks observed by each image pair. Args: tracks_manager: tracks manager include_features: whether to include the features from the images min_common: the minimum number of tracks the two images need to have in common Returns: tuple: im1, im2 -> tuple: tracks, features from first image, features from second image """ common_tracks = {} for (im1, im2), size in tracks_manager.get_all_pairs_connectivity().items(): if size < min_common: continue tuples = tracks_manager.get_all_common_observations(im1, im2) if include_features: common_tracks[im1, im2] = ( [v for v, _, _ in tuples], np.array([p.point for _, p, _ in tuples]), np.array([p.point for _, _, p in tuples]), ) else: common_tracks[im1, im2] = [v for v, _, _ in tuples] return common_tracks
def retriangulate( tracks_manager: pysfm.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 reconstruct_from_prior( data: DataSetBase, tracks_manager: pysfm.TracksManager, rec_prior: types.Reconstruction, ) -> Tuple[Dict[str, Any], types.Reconstruction]: """Retriangulate a new reconstruction from the rec_prior""" reconstruction = types.Reconstruction() report = {} rec_report = {} report["retriangulate"] = [rec_report] images = tracks_manager.get_shot_ids() # copy prior poses, cameras reconstruction.cameras = rec_prior.cameras for shot in rec_prior.shots.values(): reconstruction.add_shot(shot) prior_images = set(rec_prior.shots) remaining_images = set(images) - prior_images rec_report["num_prior_images"] = len(prior_images) rec_report["num_remaining_images"] = len(remaining_images) # Start with the known poses triangulate_shot_features(tracks_manager, reconstruction, prior_images, data.config) paint_reconstruction(data, tracks_manager, reconstruction) report["not_reconstructed_images"] = list(remaining_images) return report, reconstruction
def add_observation_to_reconstruction( tracks_manager: pysfm.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 incremental_reconstruction( data: DataSetBase, tracks_manager: pysfm.TracksManager ) -> Tuple[Dict[str, Any], List[types.Reconstruction]]: """Run the entire incremental reconstruction pipeline.""" logger.info("Starting incremental reconstruction") report = {} chrono = Chronometer() images = tracks_manager.get_shot_ids() if not data.reference_lla_exists(): data.invent_reference_lla(images) remaining_images = set(images) gcp = data.load_ground_control_points() common_tracks = tracking.all_common_tracks(tracks_manager) reconstructions = [] pairs = compute_image_pairs(common_tracks, data) chrono.lap("compute_image_pairs") report["num_candidate_image_pairs"] = len(pairs) report["reconstructions"] = [] for im1, im2 in pairs: if im1 in remaining_images and im2 in remaining_images: rec_report = {} report["reconstructions"].append(rec_report) _, p1, p2 = common_tracks[im1, im2] reconstruction, rec_report["bootstrap"] = bootstrap_reconstruction( data, tracks_manager, im1, im2, p1, p2 ) if reconstruction: remaining_images -= set(reconstruction.shots) reconstruction, rec_report["grow"] = grow_reconstruction( data, tracks_manager, reconstruction, remaining_images, gcp, ) reconstructions.append(reconstruction) reconstructions = sorted(reconstructions, key=lambda x: -len(x.shots)) for k, r in enumerate(reconstructions): logger.info( "Reconstruction {}: {} images, {} points".format( k, len(r.shots), len(r.points) ) ) logger.info("{} partial reconstructions in total.".format(len(reconstructions))) chrono.lap("compute_reconstructions") report["wall_times"] = dict(chrono.lap_times()) report["not_reconstructed_images"] = list(remaining_images) return report, reconstructions
def triangulate_shot_features( tracks_manager: pysfm.TracksManager, reconstruction: types.Reconstruction, shot_ids: Set[str], config: Dict[str, Any], ) -> None: """Reconstruct as many tracks seen in shot_id as possible.""" reproj_threshold = config["triangulation_threshold"] min_ray_angle = config["triangulation_min_ray_angle"] triangulator = TrackTriangulator(tracks_manager, reconstruction) all_shots_ids = set(tracks_manager.get_shot_ids()) tracks_ids = { t for s in shot_ids if s in all_shots_ids for t in tracks_manager.get_shot_observations(s) } for track in tracks_ids: if track not in reconstruction.points: triangulator.triangulate(track, reproj_threshold, min_ray_angle)
def compute_common_tracks( reconstruction1: types.Reconstruction, reconstruction2: types.Reconstruction, tracks_manager1: pysfm.TracksManager, tracks_manager2: pysfm.TracksManager, ) -> List[Tuple[str, str]]: common_tracks = set() common_images = set(reconstruction1.shots.keys()).intersection( reconstruction2.shots.keys()) all_shot_ids1 = set(tracks_manager1.get_shot_ids()) all_shot_ids2 = set(tracks_manager2.get_shot_ids()) for image in common_images: if image not in all_shot_ids1 or image not in all_shot_ids2: continue at_shot1 = tracks_manager1.get_shot_observations(image) at_shot2 = tracks_manager2.get_shot_observations(image) for t1, t2 in corresponding_tracks(at_shot1, at_shot2): if t1 in reconstruction1.points and t2 in reconstruction2.points: common_tracks.add((t1, t2)) return list(common_tracks)
def common_tracks_double_dict( tracks_manager: pysfm.TracksManager, ) -> t.Dict[str, t.Dict[str, t.List[str]]]: """List of track ids observed by each image pair. Return a dict, ``res``, such that ``res[im1][im2]`` is the list of common tracks between ``im1`` and ``im2``. """ common_tracks_per_pair = tracking.all_common_tracks_without_features( tracks_manager) res = {image: {} for image in tracks_manager.get_shot_ids()} for (im1, im2), v in common_tracks_per_pair.items(): res[im1][im2] = v res[im2][im1] = v return res
def paint_reconstruction( data: DataSetBase, tracks_manager: pysfm.TracksManager, reconstruction: types.Reconstruction, ) -> None: """Set the color of the points from the color of the tracks.""" for k, point in reconstruction.points.items(): point.color = list( map( float, next( iter(tracks_manager.get_track_observations(str(k)).values()) ).color, ) )
def save_tracks_manager(self, tracks_manager: pysfm.TracksManager, filename: Optional[str] = None) -> None: with self.io_handler.open(self._tracks_manager_file(filename), "w") as fw: fw.write(tracks_manager.as_string())
def save_undistorted_tracks_manager( self, tracks_manager: pysfm.TracksManager) -> None: filename = os.path.join(self.data_path, "tracks.csv") with self.io_handler.open(filename, "w") as fw: fw.write(tracks_manager.as_string())
def save_tracks_manager(self, tracks_manager: pysfm.TracksManager, filename: Optional[str] = None) -> None: tracks_manager.write_to_file(self._tracks_manager_file(filename))
def save_undistorted_tracks_manager( self, tracks_manager: pysfm.TracksManager) -> None: filename = os.path.join(self.data_path, "tracks.csv") tracks_manager.write_to_file(filename)
def resect( data: DataSetBase, tracks_manager: pysfm.TracksManager, reconstruction: types.Reconstruction, shot_id: str, threshold: float, min_inliers: int, ) -> Tuple[bool, Set[str], Dict[str, Any]]: """Try resecting and adding a shot to the reconstruction. Return: True on success. """ rig_assignments = data.load_rig_assignments_per_image() camera = reconstruction.cameras[data.load_exif(shot_id)["camera"]] bs, Xs, ids = [], [], [] for track, obs in tracks_manager.get_shot_observations(shot_id).items(): if track in reconstruction.points: b = camera.pixel_bearing(obs.point) bs.append(b) Xs.append(reconstruction.points[track].coordinates) ids.append(track) bs = np.array(bs) Xs = np.array(Xs) if len(bs) < 5: return False, set(), {"num_common_points": len(bs)} T = multiview.absolute_pose_ransac(bs, Xs, threshold, 1000, 0.999) R = T[:, :3] t = T[:, 3] reprojected_bs = R.T.dot((Xs - t).T).T reprojected_bs /= np.linalg.norm(reprojected_bs, axis=1)[:, np.newaxis] inliers = np.linalg.norm(reprojected_bs - bs, axis=1) < threshold ninliers = int(sum(inliers)) logger.info("{} resection inliers: {} / {}".format(shot_id, ninliers, len(bs))) report = { "num_common_points": len(bs), "num_inliers": ninliers, } if ninliers >= min_inliers: R = T[:, :3].T t = -R.dot(T[:, 3]) assert shot_id not in reconstruction.shots new_shots = add_shot( data, reconstruction, rig_assignments, shot_id, pygeometry.Pose(R, t) ) if shot_id in rig_assignments: triangulate_shot_features( tracks_manager, reconstruction, new_shots, data.config ) for i, succeed in enumerate(inliers): if succeed: add_observation_to_reconstruction( tracks_manager, reconstruction, shot_id, ids[i] ) # pyre-fixme [6]: Expected `int` for 2nd positional report["shots"] = list(new_shots) return True, new_shots, report else: return False, set(), report