Spaces:
Build error
Build error
| import cv2 | |
| import logging | |
| import numpy as np | |
| from hloc.utils.read_write_model import ( | |
| read_cameras_binary, | |
| read_images_binary, | |
| read_model, | |
| write_model, | |
| qvec2rotmat, | |
| read_images_text, | |
| read_cameras_text, | |
| ) | |
| logger = logging.getLogger(__name__) | |
| def scale_sfm_images(full_model, scaled_model, image_dir): | |
| """Duplicate the provided model and scale the camera intrinsics so that | |
| they match the original image resolution - makes everything easier. | |
| """ | |
| logger.info("Scaling the COLMAP model to the original image size.") | |
| scaled_model.mkdir(exist_ok=True) | |
| cameras, images, points3D = read_model(full_model) | |
| scaled_cameras = {} | |
| for id_, image in images.items(): | |
| name = image.name | |
| img = cv2.imread(str(image_dir / name)) | |
| assert img is not None, image_dir / name | |
| h, w = img.shape[:2] | |
| cam_id = image.camera_id | |
| if cam_id in scaled_cameras: | |
| assert scaled_cameras[cam_id].width == w | |
| assert scaled_cameras[cam_id].height == h | |
| continue | |
| camera = cameras[cam_id] | |
| assert camera.model == "SIMPLE_RADIAL" | |
| sx = w / camera.width | |
| sy = h / camera.height | |
| assert sx == sy, (sx, sy) | |
| scaled_cameras[cam_id] = camera._replace( | |
| width=w, | |
| height=h, | |
| params=camera.params * np.array([sx, sx, sy, 1.0]), | |
| ) | |
| write_model(scaled_cameras, images, points3D, scaled_model) | |
| def create_query_list_with_intrinsics( | |
| model, out, list_file=None, ext=".bin", image_dir=None | |
| ): | |
| """Create a list of query images with intrinsics from the colmap model.""" | |
| if ext == ".bin": | |
| images = read_images_binary(model / "images.bin") | |
| cameras = read_cameras_binary(model / "cameras.bin") | |
| else: | |
| images = read_images_text(model / "images.txt") | |
| cameras = read_cameras_text(model / "cameras.txt") | |
| name2id = {image.name: i for i, image in images.items()} | |
| if list_file is None: | |
| names = list(name2id) | |
| else: | |
| with open(list_file, "r") as f: | |
| names = f.read().rstrip().split("\n") | |
| data = [] | |
| for name in names: | |
| image = images[name2id[name]] | |
| camera = cameras[image.camera_id] | |
| w, h, params = camera.width, camera.height, camera.params | |
| if image_dir is not None: | |
| # Check the original image size and rescale the camera intrinsics | |
| img = cv2.imread(str(image_dir / name)) | |
| assert img is not None, image_dir / name | |
| h_orig, w_orig = img.shape[:2] | |
| assert camera.model == "SIMPLE_RADIAL" | |
| sx = w_orig / w | |
| sy = h_orig / h | |
| assert sx == sy, (sx, sy) | |
| w, h = w_orig, h_orig | |
| params = params * np.array([sx, sx, sy, 1.0]) | |
| p = [name, camera.model, w, h] + params.tolist() | |
| data.append(" ".join(map(str, p))) | |
| with open(out, "w") as f: | |
| f.write("\n".join(data)) | |
| def evaluate(model, results, list_file=None, ext=".bin", only_localized=False): | |
| predictions = {} | |
| with open(results, "r") as f: | |
| for data in f.read().rstrip().split("\n"): | |
| data = data.split() | |
| name = data[0] | |
| q, t = np.split(np.array(data[1:], float), [4]) | |
| predictions[name] = (qvec2rotmat(q), t) | |
| if ext == ".bin": | |
| images = read_images_binary(model / "images.bin") | |
| else: | |
| images = read_images_text(model / "images.txt") | |
| name2id = {image.name: i for i, image in images.items()} | |
| if list_file is None: | |
| test_names = list(name2id) | |
| else: | |
| with open(list_file, "r") as f: | |
| test_names = f.read().rstrip().split("\n") | |
| errors_t = [] | |
| errors_R = [] | |
| for name in test_names: | |
| if name not in predictions: | |
| if only_localized: | |
| continue | |
| e_t = np.inf | |
| e_R = 180.0 | |
| else: | |
| image = images[name2id[name]] | |
| R_gt, t_gt = image.qvec2rotmat(), image.tvec | |
| R, t = predictions[name] | |
| e_t = np.linalg.norm(-R_gt.T @ t_gt + R.T @ t, axis=0) | |
| cos = np.clip((np.trace(np.dot(R_gt.T, R)) - 1) / 2, -1.0, 1.0) | |
| e_R = np.rad2deg(np.abs(np.arccos(cos))) | |
| errors_t.append(e_t) | |
| errors_R.append(e_R) | |
| errors_t = np.array(errors_t) | |
| errors_R = np.array(errors_R) | |
| med_t = np.median(errors_t) | |
| med_R = np.median(errors_R) | |
| out = f"Results for file {results.name}:" | |
| out += f"\nMedian errors: {med_t:.3f}m, {med_R:.3f}deg" | |
| out += "\nPercentage of test images localized within:" | |
| threshs_t = [0.01, 0.02, 0.03, 0.05, 0.25, 0.5, 5.0] | |
| threshs_R = [1.0, 2.0, 3.0, 5.0, 2.0, 5.0, 10.0] | |
| for th_t, th_R in zip(threshs_t, threshs_R): | |
| ratio = np.mean((errors_t < th_t) & (errors_R < th_R)) | |
| out += f"\n\t{th_t*100:.0f}cm, {th_R:.0f}deg : {ratio*100:.2f}%" | |
| logger.info(out) | |