Computes angular relative pose errors across all image pairs. Notice that this approach leads to a super-linear decrease in the AUC scores when multiple images fail to register. Consider that we have N images in total in a dataset and M images are registered in the evaluated reconst
(
sparse_gt: pycolmap.Reconstruction,
sparse: pycolmap.Reconstruction,
min_proj_center_dist: float,
)
| 1007 | |
| 1008 | |
| 1009 | def compute_rel_errors( |
| 1010 | sparse_gt: pycolmap.Reconstruction, |
| 1011 | sparse: pycolmap.Reconstruction, |
| 1012 | min_proj_center_dist: float, |
| 1013 | ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]: |
| 1014 | """Computes angular relative pose errors across all image pairs. |
| 1015 | |
| 1016 | Notice that this approach leads to a super-linear decrease in the AUC scores |
| 1017 | when multiple images fail to register. Consider that we have N images in |
| 1018 | total in a dataset and M images are registered in the evaluated |
| 1019 | reconstruction. In this case, we can compute "finite" errors for (N-M)^2 |
| 1020 | pairs while the dataset has a total of N^2 pairs. In case of many |
| 1021 | unregistered images, the AUC score will drop much more than the |
| 1022 | (intuitively) expected (N-M) / N ratio. One could appropriately normalize by |
| 1023 | computing a single score per image through a suitable normalization of all |
| 1024 | pairwise errors per image. However, this becomes difficult when multiple |
| 1025 | sub-components are incorrectly stitched together in the same reconstruction |
| 1026 | (e.g., in the case of symmetry issues). |
| 1027 | """ |
| 1028 | |
| 1029 | if sparse is None: |
| 1030 | pycolmap.logging.error("Reconstruction failed") |
| 1031 | return len(sparse_gt.images) * [np.inf], len(sparse_gt.images) * [180] |
| 1032 | |
| 1033 | images = {} |
| 1034 | for image in sparse.images.values(): |
| 1035 | images[image.name] = image |
| 1036 | |
| 1037 | dts = [] |
| 1038 | dRs = [] |
| 1039 | for this_image_gt in sparse_gt.images.values(): |
| 1040 | if this_image_gt.name not in images: |
| 1041 | for _ in range(sparse_gt.num_images() - 1): |
| 1042 | dts.append(np.inf) |
| 1043 | dRs.append(180) |
| 1044 | continue |
| 1045 | |
| 1046 | this_image = images[this_image_gt.name] |
| 1047 | |
| 1048 | for other_image_gt in sparse_gt.images.values(): |
| 1049 | if this_image_gt.image_id == other_image_gt.image_id: |
| 1050 | continue |
| 1051 | |
| 1052 | if other_image_gt.name not in images: |
| 1053 | dts.append(np.inf) |
| 1054 | dRs.append(180) |
| 1055 | continue |
| 1056 | |
| 1057 | other_image = images[other_image_gt.name] |
| 1058 | |
| 1059 | other_from_this = ( |
| 1060 | other_image.cam_from_world() |
| 1061 | * this_image.cam_from_world().inverse() |
| 1062 | ) |
| 1063 | other_from_this_gt = ( |
| 1064 | other_image_gt.cam_from_world() |
| 1065 | * this_image_gt.cam_from_world().inverse() |
| 1066 | ) |