def bootstrap_reconstruction(data, graph, im1, im2): '''Starts a reconstruction using two shots. ''' print 'Initial reconstruction with', im1, 'and', im2 d1 = data.load_exif(im1) d2 = data.load_exif(im2) cameras = data.load_camera_models() camera1 = cameras[d1['camera']] camera2 = cameras[d2['camera']] tracks, p1, p2 = matching.common_tracks(graph, im1, im2) print 'Number of common tracks', len(tracks) threshold = data.config.get('five_point_algo_threshold', 0.006) R, t, inliers = two_view_reconstruction(p1, p2, camera1, camera2, threshold) if len(inliers) > 5: print 'Number of inliers', len(inliers) reconstruction = types.Reconstruction() reconstruction.cameras = cameras shot1 = types.Shot() shot1.id = im1 shot1.camera = cameras[str(d1['camera'])] shot1.pose = types.Pose() shot1.metadata = get_image_metadata(data, im1) reconstruction.add_shot(shot1) shot2 = types.Shot() shot2.id = im2 shot2.camera = cameras[str(d2['camera'])] shot2.pose = types.Pose() shot2.pose.rotation = R shot2.pose.translation = t shot2.metadata = get_image_metadata(data, im2) reconstruction.add_shot(shot2) triangulate_shot_features( graph, reconstruction, im1, data.config.get('triangulation_threshold', 0.004), data.config.get('triangulation_min_ray_angle', 2.0)) print 'Number of reconstructed 3D points :{}'.format( len(reconstruction.points)) if len(reconstruction.points) > data.config.get( 'five_point_algo_min_inliers', 50): print 'Found initialize good pair', im1, 'and', im2 bundle_single_view(graph, reconstruction, im2, data.config) retriangulate(graph, reconstruction, data.config) bundle_single_view(graph, reconstruction, im2, data.config) return reconstruction print 'Pair', im1, ':', im2, 'fails' return None
def bootstrap_reconstruction(data, graph, im1, im2): '''Starts a reconstruction using two shots. ''' print 'Initial reconstruction with', im1, 'and', im2 d1 = data.load_exif(im1) d2 = data.load_exif(im2) cameras = data.load_camera_models() camera1 = cameras[d1['camera']] camera2 = cameras[d2['camera']] tracks, p1, p2 = matching.common_tracks(graph, im1, im2) print 'Number of common tracks', len(tracks) threshold = data.config.get('five_point_algo_threshold', 0.006) R, t, inliers = two_view_reconstruction(p1, p2, camera1, camera2, threshold) if len(inliers) > 5: print 'Number of inliers', len(inliers) reconstruction = types.Reconstruction() reconstruction.cameras = cameras shot1 = types.Shot() shot1.id = im1 shot1.camera = cameras[str(d1['camera'])] shot1.pose = types.Pose() shot1.metadata = get_image_metadata(data, im1) reconstruction.add_shot(shot1) shot2 = types.Shot() shot2.id = im2 shot2.camera = cameras[str(d2['camera'])] shot2.pose = types.Pose() shot2.pose.rotation = R shot2.pose.translation = t shot2.metadata = get_image_metadata(data, im2) reconstruction.add_shot(shot2) triangulate_shot_features( graph, reconstruction, im1, data.config.get('triangulation_threshold', 0.004), data.config.get('triangulation_min_ray_angle', 2.0)) print 'Number of reconstructed 3D points :{}'.format(len(reconstruction.points)) if len(reconstruction.points) > data.config.get('five_point_algo_min_inliers', 50): print 'Found initialize good pair', im1 , 'and', im2 bundle_single_view(graph, reconstruction, im2, data.config) retriangulate(graph, reconstruction, data.config) bundle_single_view(graph, reconstruction, im2, data.config) return reconstruction print 'Pair', im1, ':', im2, 'fails' return None
def compute_image_pairs(graph, image_graph, config): '''All matched image pairs sorted by reconstructability. ''' pairs = [] score = [] for im1, im2, d in image_graph.edges(data=True): tracks, p1, p2 = matching.common_tracks(graph, im1, im2) if len(tracks) >= 50: H, inliers = cv2.findHomography(p1, p2, cv2.RANSAC, config.get('homography_threshold', 0.004)) r = pairwise_reconstructability(len(tracks), inliers.sum()) if r > 0: pairs.append((im1,im2)) score.append(r) order = np.argsort(-np.array(score)) return [pairs[o] for o in order]
def bootstrap_reconstruction(data, graph, im1, im2): """Start a reconstruction using two shots.""" logger.info("Starting reconstruction with {} and {}".format(im1, im2)) d1 = data.load_exif(im1) d2 = data.load_exif(im2) cameras = data.load_camera_models() camera1 = cameras[d1['camera']] camera2 = cameras[d2['camera']] tracks, p1, p2 = matching.common_tracks(graph, im1, im2) logger.info("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 = two_view_reconstruction(p1, p2, camera1, camera2, thresh) if len(inliers) > 5: logger.info("Two-view reconstruction inliers {}".format(len(inliers))) reconstruction = types.Reconstruction() reconstruction.cameras = cameras shot1 = types.Shot() shot1.id = im1 shot1.camera = cameras[str(d1['camera'])] shot1.pose = types.Pose() shot1.metadata = get_image_metadata(data, im1) reconstruction.add_shot(shot1) shot2 = types.Shot() shot2.id = im2 shot2.camera = cameras[str(d2['camera'])] shot2.pose = types.Pose(R, t) shot2.metadata = get_image_metadata(data, im2) reconstruction.add_shot(shot2) triangulate_shot_features( graph, reconstruction, im1, data.config.get('triangulation_threshold', 0.004), data.config.get('triangulation_min_ray_angle', 2.0)) logger.info("Triangulated: {}".format(len(reconstruction.points))) if len(reconstruction.points) > min_inliers: bundle_single_view(graph, reconstruction, im2, data.config) retriangulate(graph, reconstruction, data.config) bundle_single_view(graph, reconstruction, im2, data.config) return reconstruction logger.info("Starting reconstruction with {} and {} failed")
def find_neighboring_images(shot, graph, reconstruction, num_neighbors=5): """Find neighbouring images based on common tracks.""" theta_min = np.pi / 60 theta_max = np.pi / 6 ns = [] C1 = shot.pose.get_origin() others = (s for s in reconstruction.shots.values() if s.id != shot.id) for other in others: score = 0 C2 = other.pose.get_origin() tracks, p1s, p2s = matching.common_tracks(graph, shot.id, other.id) for track in tracks: if track in reconstruction.points: p = np.array(reconstruction.points[track].coordinates) theta = angle_between_points(p, C1, C2) if theta > theta_min and theta < theta_max: score += 1 if (score > 20): ns.append((other, score)) ns.sort(key=lambda ns: ns[1], reverse=True) return [shot.id] + [n.id for n, s in ns[:num_neighbors]]
def grow_reconstruction( problem, data, graph, reco, im1, im2, hint_forward=False): """ Grow the given reconstruction `reco` by adding a new image `im2` to it. The new image `im2` is initially matched against an image `im1`, which must already exist in the reconstruction. See `custom_two_view_reconstruction` for the meaning of `hint_forward`. Updates the reconstruction in-place and returns `True` on success. """ # FIXME: # - Make DRY with bootstrap_reconstruction; they look similar but they're # different in subtle ways. # - Could probably align once at the end, instead of aligning at every # step. # - Sometimes we get "Termination: NO_CONVERGENCE" from Ceres. Check for # that error case and perhaps run it for more iterations. print "----------------" print "grow_reconstruction({}, {}, hint_forward={})".format( im1, im2, hint_forward) reconstruction.bundle(graph, reco, None, data.config) align.align_reconstruction(reco, None, data.config) assert im1 in reco.shots assert im2 not in reco.shots camera1 = problem.image2camera[im1] camera2 = problem.image2camera[im2] 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) R, t, inliers = custom_two_view_reconstruction( p1, p2, camera1, camera2, thresh, hint_forward) print "grow: R={} t={} len(inliers)={}".format(R, t, len(inliers)) if len(inliers) <= 5: print "grow failed: not enough points in initial reconstruction" return False # Reconstruction is up to scale; set translation to 1. # (This will be corrected later in the bundle adjustment step.) t /= np.linalg.norm(t) assert camera1.id in reco.cameras if camera2.id not in reco.cameras: reco.add_camera(camera2) shot1_pose = reco.shots[im1].pose shot2 = types.Shot() shot2.id = im2 shot2.camera = camera2 shot2.pose = types.Pose(R, t).compose(shot1_pose) shot2.metadata = get_empty_metadata() reco.add_shot(shot2) debug = partial(_debug_short, graph, reco, im1, im2) debug("started with") # FIXME: fail here if im2 does not have enough tracks in common with reco pose_before = deepcopy(reco.shots[im2].pose) reconstruction.bundle_single_view(graph, reco, im2, data.config) # It's possible that bundle_single_view caused hint_forward to be violated. # If that's the case, revert to the pose before bundle_single_view, and # hope that after triangulating the points from `im2`, bundle adjustment # will find a better solution. rel_pose_after = reco.shots[im2].pose.compose( reco.shots[im1].pose.inverse()) t_after_norm = rel_pose_after.translation / np.linalg.norm( rel_pose_after.translation) # print "*** t_after_norm={}".format(t_after_norm) if hint_forward and t_after_norm[2] >= -0.75: # FIXME put const in config print "*** hint_forward violated; undoing bundle_single_view" reco.shots[im2].pose = pose_before rel_pose_after = reco.shots[im2].pose.compose( reco.shots[im1].pose.inverse()) t_after_norm = rel_pose_after.translation / np.linalg.norm( rel_pose_after.translation) print "*** after undo: t_after_norm={}".format(t_after_norm) # print reconstruction.triangulate_shot_features( graph, reco, im2, data.config.get('triangulation_threshold', 0.004), data.config.get('triangulation_min_ray_angle', 2.0)) reconstruction.bundle(graph, reco, None, data.config) reconstruction.remove_outliers(graph, reco, data.config) align.align_reconstruction(reco, None, data.config) reconstruction.retriangulate(graph, reco, data.config) reconstruction.bundle(graph, reco, None, data.config) debug("ended with") return True
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