def get_scene_reprojection_errors(self) -> np.ndarray: """Get the scene reprojection errors for all 3D points and all associated measurements. Returns: Reprojection errors as a 1D numpy array. """ scene_reproj_errors: List[float] = [] for track in self._tracks: track_errors, _ = reproj_utils.compute_track_reprojection_errors( self._cameras, track) scene_reproj_errors.extend(track_errors) return np.array(scene_reproj_errors)
def get_scene_reprojection_errors(self) -> np.ndarray: """Get the scene reprojection errors for all 3D points and all associated measurements. Returns: Reprojection errors (measured in pixels) as a 1D numpy array. """ scene_reproj_errors: List[float] = [] for track in self._tracks: track_errors, _ = reproj_utils.compute_track_reprojection_errors(self._cameras, track) # passing an array argument to .extend() will convert the array to a list, and append its elements scene_reproj_errors.extend(track_errors) return np.array(scene_reproj_errors)
def __validate_track(self, track: SfmTrack, reproj_err_thresh: float) -> bool: """Validates a track based on reprojection errors and cheirality checks. Args: track: track with 3D landmark and measurements. reproj_err_thresh: reprojection err threshold for each measurement. Returns: validity of the track. """ errors, avg_reproj_error = reproj_utils.compute_track_reprojection_errors(self._cameras, track) # track is valid as all measurements have error below the threshold cheirality_success = np.all(~np.isnan(errors)) return np.all(errors < reproj_err_thresh) and cheirality_success
def test_compute_track_reprojection_errors(): """Ensure that reprojection error is computed properly within a track. # For camera 0: # [13] = [10,0,3] [1,0,0 | 0] [1] # [24] = [0,10,4] * [0,1,0 | 0] *[2] # [1] = [0, 0,1] [0,0,1 | 0] [1] # [1] # For camera 1: # [-7] = [10,0,3] [1,0,0 |-2] [1] # [44] = [0,10,4] * [0,1,0 | 2] *[2] # [1] = [0, 0,1] [0,0,1 | 0] [1] # [1] """ wTi0 = Pose3(Rot3.RzRyRx(0, 0, 0), np.zeros((3, 1))) wTi1 = Pose3(Rot3.RzRyRx(0, 0, 0), np.array([2, -2, 0])) f = 10 k1 = 0 k2 = 0 u0 = 3 v0 = 4 K0 = Cal3Bundler(f, k1, k2, u0, v0) K1 = Cal3Bundler(f, k1, k2, u0, v0) track_camera_dict = { 0: PinholeCameraCal3Bundler(wTi0, K0), 1: PinholeCameraCal3Bundler(wTi1, K1) } triangulated_pt = np.array([1, 2, 1]) track_3d = SfmTrack(triangulated_pt) # in camera 0 track_3d.addMeasurement(idx=0, m=np.array([13, 24])) # in camera 1 track_3d.addMeasurement(idx=1, m=np.array( [-8, 43])) # should be (-7,44), 1 px error in each dim errors, avg_track_reproj_error = reproj_utils.compute_track_reprojection_errors( track_camera_dict, track_3d) expected_errors = np.array([0, np.sqrt(2)]) np.testing.assert_allclose(errors, expected_errors) assert avg_track_reproj_error == np.sqrt(2) / 2
def write_points(gtsfm_data: GtsfmData, images: List[Image], save_dir: str) -> None: """Writes the point cloud data file in the COLMAP format. Reference: https://colmap.github.io/format.html#points3d-txt Args: gtsfm_data: scene data to write. images: list of all images for this scene, in order of image index save_dir: folder to put the points3D.txt file in. """ os.makedirs(save_dir, exist_ok=True) num_pts = gtsfm_data.number_tracks() avg_track_length, _ = gtsfm_data.get_track_length_statistics() file_path = os.path.join(save_dir, "points3D.txt") with open(file_path, "w") as f: f.write("# 3D point list with one line of data per point:\n") f.write( "# POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[] as (IMAGE_ID, POINT2D_IDX)\n" ) f.write( f"# Number of points: {num_pts}, mean track length: {np.round(avg_track_length, 2)}\n" ) # TODO: assign unique indices to all keypoints (2d points) point2d_idx = 0 for j in range(num_pts): track = gtsfm_data.get_track(j) r, g, b = image_utils.get_average_point_color(track, images) _, avg_track_reproj_error = reproj_utils.compute_track_reprojection_errors( gtsfm_data._cameras, track) x, y, z = track.point3() f.write( f"{j} {x} {y} {z} {r} {g} {b} {np.round(avg_track_reproj_error, 2)} " ) for k in range(track.numberMeasurements()): i, uv_measured = track.measurement(k) f.write(f"{i} {point2d_idx} ") f.write("\n")