Esempio n. 1
0
def gcp_errors(data: DataSetBase,
               reconstructions: List[types.Reconstruction]) -> Dict[str, Any]:
    all_errors = []

    reference = data.load_reference()
    gcps = data.load_ground_control_points()
    if not gcps:
        return {}

    all_errors = []
    for gcp in gcps:
        if not gcp.lla:
            continue

        triangulated = None
        for rec in reconstructions:
            triangulated = multiview.triangulate_gcp(gcp, rec.shots, 1.0, 0.1)
            if triangulated is None:
                continue
            else:
                break

        if triangulated is None:
            continue
        gcp_enu = reference.to_topocentric(*gcp.lla_vec)
        all_errors.append(triangulated - gcp_enu)

    return _gps_gcp_errors_stats(np.array(all_errors))
Esempio n. 2
0
def bootstrap_reconstruction(data: DataSetBase, tracks_manager, camera_priors,
                             im1, im2, p1, p2):
    """Start a reconstruction using two shots."""
    logger.info("Starting reconstruction with {} and {}".format(im1, im2))
    report: Dict[str, Any] = {
        "image_pair": (im1, im2),
        "common_tracks": len(p1),
    }

    camera_id1 = data.load_exif(im1)["camera"]
    camera_id2 = data.load_exif(im2)["camera"]
    camera1 = camera_priors[camera_id1]
    camera2 = camera_priors[camera_id2]

    threshold = data.config["five_point_algo_threshold"]
    min_inliers = data.config["five_point_algo_min_inliers"]
    iterations = data.config["five_point_refine_rec_iterations"]
    R, t, inliers, report[
        "two_view_reconstruction"] = two_view_reconstruction_general(
            p1, p2, camera1, camera2, threshold, iterations)

    logger.info("Two-view reconstruction inliers: {} / {}".format(
        len(inliers), len(p1)))
    if len(inliers) <= 5:
        report["decision"] = "Could not find initial motion"
        logger.info(report["decision"])
        return None, report

    reconstruction = types.Reconstruction()
    reconstruction.reference = data.load_reference()
    reconstruction.cameras = camera_priors
    shot1 = reconstruction.create_shot(im1, camera_id1, pygeometry.Pose())
    shot1.metadata = get_image_metadata(data, im1)

    shot2 = reconstruction.create_shot(im2, camera_id2, pygeometry.Pose(R, t))
    shot2.metadata = get_image_metadata(data, im2)

    triangulate_shot_features(tracks_manager, reconstruction, im1, data.config)

    logger.info("Triangulated: {}".format(len(reconstruction.points)))
    report["triangulated_points"] = len(reconstruction.points)
    if len(reconstruction.points) < min_inliers:
        report["decision"] = "Initial motion did not generate enough points"
        logger.info(report["decision"])
        return None, report

    bundle_single_view(reconstruction, im2, camera_priors, data.config)
    retriangulate(tracks_manager, reconstruction, data.config)

    if len(reconstruction.points) < min_inliers:
        report[
            "decision"] = "Re-triangulation after initial motion did not generate enough points"
        logger.info(report["decision"])
        return None, report
    bundle_single_view(reconstruction, im2, camera_priors, data.config)

    report["decision"] = "Success"
    report["memory_usage"] = current_memory_usage()
    return reconstruction, report
Esempio n. 3
0
def get_image_metadata(data: DataSetBase, image: str):
    """Get image metadata as a ShotMetadata object."""
    metadata = pymap.ShotMeasurements()
    exif = data.load_exif(image)
    reference = data.load_reference()
    if "gps" in exif and "latitude" in exif["gps"] and "longitude" in exif[
            "gps"]:
        lat = exif["gps"]["latitude"]
        lon = exif["gps"]["longitude"]
        if data.config["use_altitude_tag"]:
            alt = min(
                [oexif.maximum_altitude, exif["gps"].get("altitude", 2.0)])
        else:
            alt = 2.0  # Arbitrary value used to align the reconstruction
        x, y, z = reference.to_topocentric(lat, lon, alt)
        metadata.gps_position.value = [x, y, z]
        metadata.gps_accuracy.value = exif["gps"].get("dop", 15.0)
        if metadata.gps_accuracy.value == 0.0:
            metadata.gps_accuracy.value = 15.0
    else:
        metadata.gps_position.value = [0.0, 0.0, 0.0]
        metadata.gps_accuracy.value = 999999.0

    metadata.orientation.value = exif.get("orientation", 1)

    if "accelerometer" in exif:
        metadata.accelerometer.value = exif["accelerometer"]

    if "compass" in exif:
        metadata.compass_angle.value = exif["compass"]["angle"]
        if "accuracy" in exif["compass"]:
            metadata.compass_accuracy.value = exif["compass"]["accuracy"]

    if "capture_time" in exif:
        metadata.capture_time.value = exif["capture_time"]

    if "skey" in exif:
        metadata.sequence_key.value = exif["skey"]

    return metadata
Esempio n. 4
0
def bootstrap_reconstruction(
    data: DataSetBase,
    tracks_manager: pymap.TracksManager,
    im1: str,
    im2: str,
    p1: np.ndarray,
    p2: np.ndarray,
) -> Tuple[Optional[types.Reconstruction], Dict[str, Any]]:
    """Start a reconstruction using two shots."""
    logger.info("Starting reconstruction with {} and {}".format(im1, im2))
    report: Dict[str, Any] = {
        "image_pair": (im1, im2),
        "common_tracks": len(p1),
    }

    camera_priors = data.load_camera_models()
    camera1 = camera_priors[data.load_exif(im1)["camera"]]
    camera2 = camera_priors[data.load_exif(im2)["camera"]]

    threshold = data.config["five_point_algo_threshold"]
    min_inliers = data.config["five_point_algo_min_inliers"]
    iterations = data.config["five_point_refine_rec_iterations"]
    R, t, inliers, report[
        "two_view_reconstruction"] = two_view_reconstruction_general(
            p1, p2, camera1, camera2, threshold, iterations)

    logger.info("Two-view reconstruction inliers: {} / {}".format(
        len(inliers), len(p1)))
    if len(inliers) <= 5:
        report["decision"] = "Could not find initial motion"
        logger.info(report["decision"])
        return None, report

    rig_camera_priors = data.load_rig_cameras()
    rig_assignments = data.load_rig_assignments_per_image()

    reconstruction = types.Reconstruction()
    reconstruction.reference = data.load_reference()
    reconstruction.cameras = camera_priors
    reconstruction.rig_cameras = rig_camera_priors

    new_shots = add_shot(data, reconstruction, rig_assignments, im1,
                         pygeometry.Pose())

    if im2 not in new_shots:
        new_shots |= add_shot(data, reconstruction, rig_assignments, im2,
                              pygeometry.Pose(R, t))

    align_reconstruction(reconstruction, None, data.config)
    triangulate_shot_features(tracks_manager, reconstruction, new_shots,
                              data.config)

    logger.info("Triangulated: {}".format(len(reconstruction.points)))
    report["triangulated_points"] = len(reconstruction.points)
    if len(reconstruction.points) < min_inliers:
        report["decision"] = "Initial motion did not generate enough points"
        logger.info(report["decision"])
        return None, report

    to_adjust = {s for s in new_shots if s != im1}
    bundle_shot_poses(reconstruction, to_adjust, camera_priors,
                      rig_camera_priors, data.config)

    retriangulate(tracks_manager, reconstruction, data.config)
    if len(reconstruction.points) < min_inliers:
        report[
            "decision"] = "Re-triangulation after initial motion did not generate enough points"
        logger.info(report["decision"])
        return None, report

    bundle_shot_poses(reconstruction, to_adjust, camera_priors,
                      rig_camera_priors, data.config)

    report["decision"] = "Success"
    report["memory_usage"] = current_memory_usage()
    return reconstruction, report
Esempio n. 5
0
def get_image_metadata(data: DataSetBase,
                       image: str) -> pymap.ShotMeasurements:
    """Get image metadata as a ShotMetadata object."""
    exif = data.load_exif(image)
    reference = data.load_reference()
    return exif_to_metadata(exif, data.config["use_altitude_tag"], reference)
Esempio n. 6
0
def match_candidates_from_metadata(
    images_ref: List[str],
    images_cand: List[str],
    exifs: Dict[str, Any],
    data: DataSetBase,
    config_override: Dict[str, Any],
) -> Tuple[List[Tuple[str, str]], Dict[str, Any]]:
    """Compute candidate matching pairs between between images_ref and images_cand

    Returns a list of pairs (im1, im2) such that (im1 in images_ref) is true.
    Returned pairs are unique given that (i, j) == (j, i).
    """

    overriden_config = data.config.copy()
    overriden_config.update(config_override)

    max_distance = overriden_config["matching_gps_distance"]
    gps_neighbors = overriden_config["matching_gps_neighbors"]
    time_neighbors = overriden_config["matching_time_neighbors"]
    order_neighbors = overriden_config["matching_order_neighbors"]
    bow_neighbors = overriden_config["matching_bow_neighbors"]
    bow_gps_distance = overriden_config["matching_bow_gps_distance"]
    bow_gps_neighbors = overriden_config["matching_bow_gps_neighbors"]
    bow_other_cameras = overriden_config["matching_bow_other_cameras"]
    vlad_neighbors = overriden_config["matching_vlad_neighbors"]
    vlad_gps_distance = overriden_config["matching_vlad_gps_distance"]
    vlad_gps_neighbors = overriden_config["matching_vlad_gps_neighbors"]
    vlad_other_cameras = overriden_config["matching_vlad_other_cameras"]

    if not data.reference_lla_exists():
        data.invent_reference_lla()
    reference = data.load_reference()

    if not all(map(has_gps_info, exifs.values())):
        if gps_neighbors != 0:
            logger.warn("Not all images have GPS info. "
                        "Disabling matching_gps_neighbors.")
        gps_neighbors = 0
        max_distance = 0

    images_ref.sort()

    if (max_distance == gps_neighbors == time_neighbors == order_neighbors ==
            bow_neighbors == vlad_neighbors == 0):
        # All pair selection strategies deactivated so we match all pairs
        d = set()
        t = set()
        o = set()
        b = set()
        v = set()
        pairs = {
            sorted_pair(i, j)
            for i in images_ref for j in images_cand if i != j
        }
    else:
        d = match_candidates_by_distance(images_ref, images_cand, exifs,
                                         reference, gps_neighbors,
                                         max_distance)
        t = match_candidates_by_time(images_ref, images_cand, exifs,
                                     time_neighbors)
        o = match_candidates_by_order(images_ref, images_cand, order_neighbors)
        b = match_candidates_with_bow(
            data,
            images_ref,
            images_cand,
            exifs,
            reference,
            bow_neighbors,
            bow_gps_distance,
            bow_gps_neighbors,
            bow_other_cameras,
        )
        v = match_candidates_with_vlad(
            data,
            images_ref,
            images_cand,
            exifs,
            reference,
            vlad_neighbors,
            vlad_gps_distance,
            vlad_gps_neighbors,
            vlad_other_cameras,
            {},
        )
        pairs = d | t | o | set(b) | set(v)

    pairs = ordered_pairs(pairs, images_ref)

    report = {
        "num_pairs_distance": len(d),
        "num_pairs_time": len(t),
        "num_pairs_order": len(o),
        "num_pairs_bow": len(b),
        "num_pairs_vlad": len(v),
    }
    return pairs, report
Esempio n. 7
0
def gcp_errors(data: DataSetBase,
               reconstructions: List[types.Reconstruction]) -> Dict[str, Any]:
    all_errors = []

    reference = data.load_reference()
    gcps = data.load_ground_control_points()
    if not gcps:
        return {}

    all_errors = []
    gcp_stats = []

    for gcp in gcps:
        if not gcp.lla:
            continue
        triangulated = None
        for rec in reconstructions:
            triangulated = multiview.triangulate_gcp(gcp, rec.shots, 1.0, 0.1)
            if triangulated is None:
                continue
            else:
                break

        if triangulated is None:
            continue

        gcp_enu = reference.to_topocentric(*gcp.lla_vec)
        e = triangulated - gcp_enu
        all_errors.append(e)

        # Begin computation of GCP stats
        observations = []
        for i, obs in enumerate(gcp.observations):
            if not obs.shot_id in rec.shots:
                continue
            shot = rec.shots[obs.shot_id]

            reprojected = shot.project(gcp_enu)
            annotated = obs.projection

            r_pixel = features.denormalized_image_coordinates(
                np.array([[reprojected[0], reprojected[1]]]),
                shot.camera.width, shot.camera.height)[0]
            r_pixel[0] /= shot.camera.width
            r_pixel[1] /= shot.camera.height

            a_pixel = features.denormalized_image_coordinates(
                np.array([[annotated[0], annotated[1]]]), shot.camera.width,
                shot.camera.height)[0]
            a_pixel[0] /= shot.camera.width
            a_pixel[1] /= shot.camera.height

            observations.append({
                'shot_id': obs.shot_id,
                'annotated': list(a_pixel),
                'reprojected': list(r_pixel)
            })

        gcp_stats.append({
            'id': gcp.id,
            'coordinates': list(gcp_enu),
            'observations': observations,
            'error': list(e)
        })

        # End computation of GCP stats

    with open(
            os.path.join(data.data_path, "stats",
                         "ground_control_points.json"), 'w') as f:
        f.write(json.dumps(gcp_stats, indent=4))

    return _gps_gcp_errors_stats(np.array(all_errors))