def match_candidates_by_distance( images_ref: List[str], images_cand: List[str], exifs: Dict[str, Any], reference: geo.TopocentricConverter, max_neighbors: int, max_distance: float, ) -> Set[Tuple[str, str]]: """Find candidate matching pairs by GPS distance. The GPS altitude is ignored because we want images of the same position at different altitudes to be matched together. Otherwise, for drone datasets, flights at different altitudes do not get matched. """ if len(images_cand) == 0: return set() if max_neighbors <= 0 and max_distance <= 0: return set() max_neighbors = max_neighbors or 99999999 max_distance = max_distance or 99999999.0 k = min(len(images_cand), max_neighbors) points = np.zeros((len(images_cand), 3)) for i, image in enumerate(images_cand): gps = exifs[image]["gps"] points[i] = reference.to_topocentric(gps["latitude"], gps["longitude"], 0) tree = spatial.cKDTree(points) pairs = set() for image_ref in images_ref: nn = k + 1 if image_ref in images_cand else k gps = exifs[image_ref]["gps"] point = reference.to_topocentric(gps["latitude"], gps["longitude"], 0) distances, neighbors = tree.query(point, k=nn, distance_upper_bound=max_distance) if type(neighbors) == int: # special case with only one NN neighbors = [neighbors] for j in neighbors: if j >= len(images_cand): continue image_cand = images_cand[j] if image_cand != image_ref: pairs.add(sorted_pair(image_ref, image_cand)) return pairs
def get_gps_point( exif: Dict[str, Any], reference: geo.TopocentricConverter ) -> Tuple[np.ndarray, np.ndarray]: """Return GPS-based representative point. Direction is returned as Z oriented (vertical assumption)""" gps = exif["gps"] altitude = 0 direction = np.array([0, 0, 1]) return ( reference.to_topocentric(gps["latitude"], gps["longitude"], altitude), direction, )
def generate_exifs( reconstruction: types.Reconstruction, reference: geo.TopocentricConverter, gps_noise: Union[Dict[str, float], float], imu_noise: float, causal_gps_noise: bool = False, ) -> Dict[str, Any]: """Generate fake exif metadata from the reconstruction.""" speed_ms = 10.0 previous_pose = None previous_time = 0 exifs = {} def _gps_dop(shot): gps_dop = 15 if isinstance(gps_noise, float): gps_dop = gps_noise if isinstance(gps_noise, dict): gps_dop = gps_noise[shot.camera.id] return gps_dop per_sequence = defaultdict(list) for shot_name in sorted(reconstruction.shots.keys()): shot = reconstruction.shots[shot_name] exif = {} exif["width"] = shot.camera.width exif["height"] = shot.camera.height exif["camera"] = str(shot.camera.id) exif["make"] = str(shot.camera.id) exif["skey"] = shot.metadata.sequence_key.value per_sequence[exif["skey"]].append(shot_name) if shot.camera.projection_type in ["perspective", "fisheye"]: exif["focal_ratio"] = shot.camera.focal pose = shot.pose.get_origin() if previous_pose is not None: previous_time += np.linalg.norm(pose - previous_pose) * speed_ms previous_pose = pose exif["capture_time"] = previous_time exifs[shot_name] = exif for sequence_images in per_sequence.values(): if causal_gps_noise: sequence_gps_dop = _gps_dop(reconstruction.shots[sequence_images[0]]) perturbations_2d = generate_causal_noise( 2, sequence_gps_dop, len(sequence_images), 2.0 ) for i, shot_name in enumerate(sequence_images): shot = reconstruction.shots[shot_name] exif = exifs[shot_name] origin = shot.pose.get_origin() if causal_gps_noise: gps_perturbation = [perturbations_2d[j][i] for j in range(2)] + [0] else: gps_noise = _gps_dop(shot) gps_perturbation = [gps_noise, gps_noise, 0] origin = np.array([origin]) perturb_points(origin, gps_perturbation) origin = origin[0] _, _, _, comp = rc.shot_lla_and_compass(shot, reference) lat, lon, alt = reference.to_lla(*origin) exif["gps"] = {} exif["gps"]["latitude"] = lat exif["gps"]["longitude"] = lon exif["gps"]["altitude"] = alt exif["gps"]["dop"] = _gps_dop(shot) omega, phi, kappa = geometry.opk_from_rotation( shot.pose.get_rotation_matrix() ) opk_noise = np.random.normal(0.0, np.full((3), imu_noise), (3)) exif["opk"] = {} exif["opk"]["omega"] = math.degrees(omega) + opk_noise[0] exif["opk"]["phi"] = math.degrees(phi) + opk_noise[1] exif["opk"]["kappa"] = math.degrees(kappa) + opk_noise[2] exif["compass"] = {"angle": comp} return exifs
def get_reference(self) -> TopocentricConverter: ref = self.map.get_reference() return TopocentricConverter(ref.lat, ref.lon, ref.alt)
def _transform(point: Sequence, reference: TopocentricConverter, projection: pyproj.Proj) -> List[float]: """Transform on point from local coords to a proj4 projection.""" lat, lon, altitude = reference.to_lla(point[0], point[1], point[2]) easting, northing = projection(lon, lat) return [easting, northing, altitude]