def reconstruct_from_prior( data: DataSetBase, tracks_manager: pymap.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 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 triangle_mesh(shot_id: str, r: types.Reconstruction, tracks_manager: pymap.TracksManager): """ Create triangle meshes in a list """ if shot_id not in r.shots or shot_id not in tracks_manager.get_shot_ids(): return [], [] shot = r.shots[shot_id] if shot.camera.projection_type in [ "perspective", "brown", "radial", "simple_radial", ]: return triangle_mesh_perspective(shot_id, r, tracks_manager) elif shot.camera.projection_type in [ "fisheye", "fisheye_opencv", "fisheye62", "fisheye624", "dual", ]: return triangle_mesh_fisheye(shot_id, r, tracks_manager) elif pygeometry.Camera.is_panorama(shot.camera.projection_type): return triangle_mesh_spherical(shot_id, r, tracks_manager) else: raise NotImplementedError( f"triangle_mesh not implemented for projection type {shot.camera.projection_type}" )
def compute_common_tracks( reconstruction1: types.Reconstruction, reconstruction2: types.Reconstruction, tracks_manager1: pymap.TracksManager, tracks_manager2: pymap.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 as_weighted_graph(tracks_manager: pymap.TracksManager) -> nx.Graph: """Return the tracks manager as a weighted graph having shots a snodes and weighted by the # of common tracks between two nodes. """ images = tracks_manager.get_shot_ids() image_graph = nx.Graph() for im in images: image_graph.add_node(im) for k, v in tracks_manager.get_all_pairs_connectivity().items(): image_graph.add_edge(k[0], k[1], weight=v) return image_graph
def incremental_reconstruction( data: DataSetBase, tracks_manager: pymap.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_with_features(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 add_subshot_tracks( tracks_manager: pymap.TracksManager, utracks_manager: pymap.TracksManager, shot: pymap.Shot, subshot: pymap.Shot, ) -> None: """Add shot tracks to the undistorted tracks_manager.""" if shot.id not in tracks_manager.get_shot_ids(): return if pygeometry.Camera.is_panorama(shot.camera.projection_type): add_pano_subshot_tracks(tracks_manager, utracks_manager, shot, subshot) else: for track_id, obs in tracks_manager.get_shot_observations(shot.id).items(): utracks_manager.add_observation(subshot.id, track_id, obs)
def common_tracks_double_dict( tracks_manager: pymap.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 features_statistics( data: DataSetBase, tracks_manager: pymap.TracksManager, reconstructions: List[types.Reconstruction], ) -> Dict[str, Any]: stats = {} detected = [] images = {s for r in reconstructions for s in r.shots} for im in images: features_data = feature_loader.instance.load_all_data( data, im, False, False) if not features_data: continue detected.append(len(features_data.points)) if len(detected) > 0: stats["detected_features"] = { "min": min(detected), "max": max(detected), "mean": int(np.mean(detected)), "median": int(np.median(detected)), } else: stats["detected_features"] = { "min": -1, "max": -1, "mean": -1, "median": -1 } per_shots = defaultdict(int) for rec in reconstructions: all_points_keys = set(rec.points.keys()) for shot_id in rec.shots: if shot_id not in tracks_manager.get_shot_ids(): continue for point_id in tracks_manager.get_shot_observations(shot_id): if point_id not in all_points_keys: continue per_shots[shot_id] += 1 per_shots = list(per_shots.values()) stats["reconstructed_features"] = { "min": int(min(per_shots)) if len(per_shots) > 0 else -1, "max": int(max(per_shots)) if len(per_shots) > 0 else -1, "mean": int(np.mean(per_shots)) if len(per_shots) > 0 else -1, "median": int(np.median(per_shots)) if len(per_shots) > 0 else -1, } return stats
def triangulate_shot_features( tracks_manager: pymap.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 as_graph(tracks_manager: pymap.TracksManager) -> nx.Graph: """Return the tracks manager as a bipartite graph (legacy).""" tracks = tracks_manager.get_track_ids() images = tracks_manager.get_shot_ids() graph = nx.Graph() for track_id in tracks: graph.add_node(track_id, bipartite=1) for shot_id in images: graph.add_node(shot_id, bipartite=0) for track_id in tracks: for im, obs in tracks_manager.get_track_observations(track_id).items(): graph.add_edge( im, track_id, feature=obs.point, feature_scale=obs.scale, feature_id=obs.id, feature_color=obs.color, feature_segmentation=obs.segmentation, feature_instance=obs.instance, ) return graph