예제 #1
0
파일: csv.py 프로젝트: zebrajack/kapture
def observations_from_file(
        observations_filepath: str,
        images_paths_with_keypoints: Optional[Set[str]] = None
) -> kapture.Observations:
    """
    Reads observations from CSV file.

    :param observations_filepath: path to CSV file to read.
    :param images_paths_with_keypoints: input set of image names (ids) that have keypoints.
                                        If given, used to filter out irrelevant observations.
                                        You can get from set(kapture.keypoints)
    :return: observations
    """
    assert path.basename(observations_filepath) == 'observations.txt'
    assert images_paths_with_keypoints is None or \
           (isinstance(images_paths_with_keypoints, set) and len(images_paths_with_keypoints) > 0)
    observations = kapture.Observations()
    with open(observations_filepath) as file:
        table = table_from_file(file)
        # point3d_id, [image_path, feature_id]*
        for points3d_id_str, *pairs in table:
            points3d_id = int(points3d_id_str)
            if len(pairs) > 1:
                image_paths = pairs[0::2]
                keypoints_ids = pairs[1::2]
                for image_path, keypoint_id in zip(image_paths, keypoints_ids):
                    if images_paths_with_keypoints is not None and image_path not in images_paths_with_keypoints:
                        # image_path does not exist in kapture (perhaps it was removed), ignore it
                        continue
                    observations.add(points3d_id, image_path, int(keypoint_id))
    return observations
예제 #2
0
def merge_points3d_and_observations(
    pts3d_obs: List[Tuple[Optional[kapture.Points3d],
                          Optional[kapture.Observations]]]
) -> Tuple[kapture.Points3d, kapture.Observations]:
    """
    Merge a list of points3d with their observations.

    :param pts3d_obs: list of points3d with observations to merge
    :return: merged points3d associated to observations
    """
    assert len(pts3d_obs) > 0
    merged_points3d = kapture.Points3d()
    merged_observations = kapture.Observations()
    point3d_offset = 0
    for points3d, observations in pts3d_obs:
        if points3d is None:
            continue
        merged_points3d = kapture.Points3d(
            np.vstack([merged_points3d, points3d]))
        if observations is not None:
            for point3d_idx, (image_path,
                              keypoint_idx) in kapture.flatten(observations):
                merged_observations.add(point3d_idx + point3d_offset,
                                        image_path, keypoint_idx)
        point3d_offset += merged_points3d.shape[0]
    return merged_points3d, merged_observations
예제 #3
0
    def test_init_observations(self):
        observations = kapture.Observations({0: [('a/a.jpg', 1), ('b/b.jpg', 2)], 1: [('c/c.jpg', 1), ('c/c.jpg', 2)]})
        self.assertTrue(0 in observations)
        self.assertEqual(len(observations[0]), 2)
        self.assertTrue(1 in observations)
        self.assertEqual(len(observations[1]), 2)

        observations.add(0, 'c/c.jpg', 3)
        self.assertEqual(len(observations[0]), 3)

        observations.add(2, 'a/a.jpg', 3)
        self.assertTrue(2 in observations)
        self.assertEqual(len(observations[2]), 1)
예제 #4
0
 def setUp(self):
     self._tempdir = tempfile.TemporaryDirectory()
     self._observations_expected_filepath = path.join(self._tempdir.name, 'expected', 'observations.txt')
     self._observations_actual_filepath = path.join(self._tempdir.name, 'actual', 'observations.txt')
     # creates ground truth couple data/file
     self._observations_expected = kapture.Observations({
         0: [('image1.jpg', 0), ('image2.jpg', 0)],
         2: [('image1.jpg', 2), ('image2.jpg', 3)]
     })
     self._observations_csv_expected = csv.KAPTURE_FORMAT_1 + "\n"
     self._observations_csv_expected += ''.join(["# point3d_id, [image_path, feature_id]*\n",
                                                 "0, image1.jpg, 0, image2.jpg, 0\n",
                                                 "2, image1.jpg, 2, image2.jpg, 3\n"])
     os.makedirs(path.dirname(self._observations_expected_filepath), exist_ok=True)
     with open(self._observations_expected_filepath, 'wt') as file:
         file.write(self._observations_csv_expected)
예제 #5
0
def import_from_colmap_points3d_txt(colmap_points3d_filepath: str,
                                    image_names: Dict[int, str] = None,
                                    skip_observations: bool = False
                                    ) -> Tuple[kapture.Points3d, Optional[kapture.Observations]]:
    """
    Imports the colmap file named "points3d.txt" containing both points 3D and observations.

    :param colmap_points3d_filepath: path to the colmap file named "points3d.txt"
    :param image_names: dict of image names matching the colmap image id to the kapture image name
                        colmap_image_idx -> kapture_image_filename
    :param skip_observations: skip import of observations id true.
    :return: kapture 3D points and observations
    """
    assert path.basename(colmap_points3d_filepath) == 'points3D.txt'
    points3d = []
    observations = kapture.Observations()

    # colmap points3D.txt contains both points 3D and observations
    with open(colmap_points3d_filepath, 'r') as file:
        # in the reconstruction, spaces and commas can be used as separators
        lines = file.readlines()
        # eliminate comments
        lines = (line for line in lines if not line.startswith('#'))
        # split by space and or comma
        lines = ([float(value) for value in re.findall(colmap_reconstruction_split_pattern, values)]
                 for values in lines)  # split into an array of floats
        for index, values in enumerate(lines):
            points3d.append(values[1:4] + values[4:7])
            if not skip_observations and len(values) > 8 and len(image_names) > 0:
                for image_idx, point_2d_idx in zip(values[8::2], values[9::2]):
                    filename = image_names.get(int(image_idx), 'unknown')
                    observations.add(index, filename, int(point_2d_idx))

    points3d = kapture.Points3d(np.array(points3d)) if points3d else kapture.Points3d()
    observations = None if len(observations) == 0 else observations
    return points3d, observations
def import_robotcar_colmap_location(
        robotcar_path: str, colmap_reconstruction_fullpath: path,
        kapture_path: str, rigs: kapture.Rigs,
        skip_reconstruction: bool) -> kapture.Kapture:
    """
    Import robotcar data for one location from colmap reconstruction
    :param robotcar_path: path to the robotcar top directory
    :param colmap_reconstruction_fullpath: path to the colmap reconstruction directory
    :param kapture_path: path to the kapture top directory
    :param rigs: kapture rigs to modify
    :param skip_reconstruction: if True, will not add the reconstruction
    :return: a kapture object
    """

    # First, import Colmap reconstruction for given location
    kapture_data = import_colmap(
        kapture_dirpath=kapture_path,
        colmap_reconstruction_dirpath=colmap_reconstruction_fullpath,
        colmap_images_dirpath=path.join(robotcar_path, "images"),
        skip_reconstruction=skip_reconstruction,
        images_import_strategy=TransferAction.skip
    )  # since filenames are incorrect

    # Post processing:
    # - use correct names for cameras
    # - model was built with PNG files, but we have JPG
    # - recover proper timestamps
    # - recover rig

    # Fix sensors.txt
    camera_mapping = {
        'cam_00001': 'left',
        'cam_00002': 'rear',
        'cam_00003': 'right'
    }
    new_cameras = kapture.Sensors()
    for cam_id in kapture_data.sensors:
        new_cameras[camera_mapping[cam_id]] = kapture_data.sensors[cam_id]
    kapture_data.sensors = new_cameras

    if not skip_reconstruction:
        # Fix keypoints
        # Need to rename .png.kpt to .jpg.kpt files and that's all
        for root, dirs, files in os.walk(kapture_path):
            for file in files:
                if file.endswith('.png.kpt'):
                    os.rename(
                        path.join(root, file),
                        path.join(root, file.replace(".png.kpt", ".jpg.kpt")))

        # observations.txt: png -> jpg
        new_observations = kapture.Observations()
        for point3d_idx in kapture_data.observations:
            for image_path, keypoint_id in kapture_data.observations[
                    point3d_idx]:
                new_observations.add(point3d_idx,
                                     image_path.replace(".png", ".jpg"),
                                     int(keypoint_id))
        kapture_data.observations = new_observations

    # records_camera.txt
    # timestamps, png->jpg
    new_records_camera = kapture.RecordsCamera()
    records_camera_pattern = re.compile(r'.*/(?P<timestamp>\d+)\.png')
    ts_mapping = {}
    for ts, shot in kapture_data.records_camera.items():
        for cam_id, image_path in shot.items():
            matches = records_camera_pattern.match(image_path)
            if not matches:
                continue
            matches = matches.groupdict()
            timestamp = int(matches['timestamp'])
            ts_mapping[ts] = timestamp
            new_path = image_path.replace(".png", ".jpg")
            new_records_camera[timestamp, camera_mapping[cam_id]] = new_path
    kapture_data.records_camera = new_records_camera

    # trajectories.txt
    new_trajectories = kapture.Trajectories()
    # First recover timestamps and camera names
    for ts, sensor_id in sorted(kapture_data.trajectories.key_pairs()):
        new_trajectories[
            ts_mapping[ts],
            camera_mapping[sensor_id]] = kapture_data.trajectories[ts,
                                                                   sensor_id]

    kapture_data.trajectories = new_trajectories
    kapture_data.rigs = rigs

    return kapture_data
예제 #7
0
def import_bundler(
        bundler_path: str,
        image_list_path: str,
        image_dir_path: str,
        kapture_dir_path: str,
        ignore_trajectories: bool,
        add_reconstruction: bool,
        force_overwrite_existing: bool = False,
        images_import_method: TransferAction = TransferAction.skip) -> None:
    """
    Imports bundler data and save them as kapture.

    :param bundler_path: path to the bundler model file
    :param image_list_path: path to the file containing the list of image names
    :param image_dir_path: input path to bundler image directory.
    :param kapture_dir_path: path to kapture top directory
    :param ignore_trajectories: if True, will not import the trajectories
    :param add_reconstruction: if True, will create 3D points and observations
    :param force_overwrite_existing: Silently overwrite kapture files if already exists.
    :param images_import_method: choose how to import actual image files.
    """
    os.makedirs(kapture_dir_path, exist_ok=True)
    delete_existing_kapture_files(kapture_dir_path,
                                  force_erase=force_overwrite_existing)

    logger.info('loading all content...')
    # if there is a filter list, parse it
    with open(image_list_path) as file:
        file_content = file.readlines()
    # remove end line char and empty lines
    image_list = [line.rstrip() for line in file_content if line != '\n']

    with open(bundler_path) as file:
        bundler_content = file.readlines()
    # remove end line char and empty lines
    bundler_content = [
        line.rstrip() for line in bundler_content if line != '\n'
    ]
    assert bundler_content[0] == "# Bundle file v0.3"
    # <num_cameras> <num_points>
    line_1 = bundler_content[1].split()
    number_of_cameras = int(line_1[0])
    number_of_points = int(line_1[1])
    offset = 2
    number_of_lines_per_camera = 5  # 1 camera + 3 rotation + 1 translation

    cameras = kapture.Sensors()
    images = kapture.RecordsCamera()
    trajectories = kapture.Trajectories() if not ignore_trajectories else None
    points3d = [] if add_reconstruction else None
    keypoints = kapture.Keypoints('sift', np.float32,
                                  2) if add_reconstruction else None
    observations = kapture.Observations() if add_reconstruction else None
    image_mapping = []  # bundler camera_id -> (name, width, height)
    for i in range(0, number_of_cameras):
        start_index = i * number_of_lines_per_camera + offset
        file_name = image_list[i]

        # process camera info
        line_camera = bundler_content[start_index].split()
        focal_length = float(line_camera[0])
        k1 = float(line_camera[1])
        k2 = float(line_camera[2])

        # lazy open
        with Image.open(path.join(image_dir_path, file_name)) as im:
            width, height = im.size

        image_mapping.append((file_name, width, height))
        camera = kapture.Camera(
            MODEL,
            [width, height, focal_length, width / 2, height / 2, k1, k2])
        camera_id = f'sensor{i}'
        cameras[camera_id] = camera

        # process extrinsics
        rotation_matrix = [[float(v) for v in line.split()]
                           for line in bundler_content[start_index +
                                                       1:start_index + 4]]

        quaternion_wxyz = quaternion.from_rotation_matrix(rotation_matrix)
        translation = np.array(
            [float(v) for v in bundler_content[start_index + 4].split()])
        pose = kapture.PoseTransform(quaternion_wxyz, translation)

        # The Bundler model uses a coordinate system that differs from the *computer vision camera
        #  coordinate system*. More specifically, they use the camera coordinate system typically used
        #  in *computer graphics*. In this camera coordinate system, the camera is looking down the
        #  `-z`-axis, with the `x`-axis pointing to the right and the `y`-axis pointing upwards.
        # rotation Pi around the x axis to get the *computer vision camera
        #  coordinate system*
        rotation_around_x = quaternion.quaternion(0.0, 1.0, 0.0, 0.0)
        transformation = kapture.PoseTransform(rotation_around_x,
                                               np.array([0, 0, 0]))

        images[(i, camera_id)] = file_name
        if trajectories is not None:
            # transformation.inverse() is equal to transformation (rotation around -Pi or Pi around X is the same)
            trajectories[(i, camera_id)] = kapture.PoseTransform.compose(
                [transformation, pose, transformation])

    if points3d is not None and number_of_points > 0:
        assert keypoints is not None
        assert observations is not None
        offset += number_of_cameras * number_of_lines_per_camera
        number_of_lines_per_point = 3  # position color viewlist

        # (image_name, bundler_keypoint_id ) -> keypoint_id
        known_keypoints = {}
        local_keypoints = {}

        for i in range(0, number_of_points):
            start_index = i * number_of_lines_per_point + offset
            position = [float(v) for v in bundler_content[start_index].split()]
            # apply transformation
            position = [position[0], -position[1], -position[2]]
            color = [
                float(v) for v in bundler_content[start_index + 1].split()
            ]

            # <view list>: length of the list + [<camera> <key> <x> <y>]
            # x, y origin is the center of the image
            view_list = bundler_content[start_index + 2].split()
            number_of_observations = int(view_list[0])

            for j in range(number_of_observations):
                camera_id = int(view_list[1 + 4 * j + 0])
                keypoint_id = int(view_list[1 + 4 * j + 1])
                x = float(view_list[1 + 4 * j + 2])
                y = float(view_list[1 + 4 * j + 3])

                file_name, width, height = image_mapping[camera_id]
                # put (0,0) in upper left corner
                x += (width / 2)
                y += (height / 2)

                # init local_keypoints if needed
                if file_name not in local_keypoints:
                    local_keypoints[file_name] = []
                # do not add the same keypoint twice
                if (file_name, keypoint_id) not in known_keypoints:
                    # in the kapture format, keypoint id is different. Note that it starts from 0
                    known_keypoints[(file_name, keypoint_id)] = len(
                        local_keypoints[file_name])
                    local_keypoints[file_name].append([x, y])
                keypoint_idx = known_keypoints[(file_name, keypoint_id)]
                observations.add(i, file_name, keypoint_idx)
            points3d.append(position + color)
        points3d = np.array(points3d)

        # finally, convert local_keypoints to np.ndarray and add them to the global keypoints variable
        keypoints = kapture.Keypoints('sift', np.float32, 2)
        for image_filename, keypoints_array in local_keypoints.items():
            keypoints_np_array = np.array(keypoints_array).astype(np.float32)
            keypoints_out_path = kapture.io.features.get_keypoints_fullpath(
                kapture_dir_path, image_filename)
            kapture.io.features.image_keypoints_to_file(
                keypoints_out_path, keypoints_np_array)
            keypoints.add(image_filename)

    if points3d is not None:
        points3d = kapture.Points3d(points3d)

    # import (copy) image files.
    logger.info('import image files ...')
    filename_list = [f for _, _, f in kapture.flatten(images)]
    import_record_data_from_dir_auto(image_dir_path, kapture_dir_path,
                                     filename_list, images_import_method)

    # pack into kapture format
    imported_kapture = kapture.Kapture(sensors=cameras,
                                       records_camera=images,
                                       trajectories=trajectories,
                                       points3d=points3d,
                                       keypoints=keypoints,
                                       observations=observations)
    logger.info('writing imported data...')
    kapture_to_dir(kapture_dir_path, imported_kapture)
예제 #8
0
    def add_frames(self, frames: List[Frame], points3d: List[Keypoint]):
        k = self.kapture

        if k.records_camera is None:
            k.records_camera = kt.RecordsCamera()
        if k.trajectories is None:
            k.trajectories = kt.Trajectories()
        if k.keypoints is None:
            k.keypoints = {
                self.default_kp_type:
                kt.Keypoints(self.default_kp_type, np.float32, 2)
            }
        if k.points3d is None:
            k.points3d = kt.Points3d()
        if k.observations is None:
            k.observations = kt.Observations()

        def check_kp(kp):
            return not kp.bad_qlt and kp.inlier_count > self.min_pt3d_obs and kp.inlier_count / kp.total_count > self.min_pt3d_ratio

        kp_ids, pts3d = zip(*[(kp.id, kp.pt3d) for kp in points3d
                              if check_kp(kp)])
        I = np.argsort(kp_ids)
        pt3d_ids = dict(zip(np.array(kp_ids)[I], np.arange(len(I))))
        pt3d_arr = np.array(pts3d)[I, :]
        k.points3d = kt.Points3d(
            np.concatenate((pt3d_arr, np.ones_like(pt3d_arr) * 128), axis=1))

        for f in frames:
            if not f.pose.post:
                continue

            id = f.frame_num
            img = f.orig_image
            img_file = os.path.join(self.default_cam[1],
                                    'frame%06d.%s' % (id, self.img_format))
            img_fullpath = get_record_fullpath(self.path, img_file)
            os.makedirs(os.path.dirname(img_fullpath), exist_ok=True)

            if not np.isclose(self.scale, 1.0):
                img = cv2.resize(img,
                                 None,
                                 fx=self.scale,
                                 fy=self.scale,
                                 interpolation=cv2.INTER_AREA)
            if self.img_format == self.IMG_FORMAT_PNG:
                cv2.imwrite(img_fullpath, img,
                            (cv2.IMWRITE_PNG_COMPRESSION, 9))
            elif self.img_format == self.IMG_FORMAT_JPG:
                cv2.imwrite(img_fullpath, img,
                            (cv2.IMWRITE_JPEG_QUALITY, self.jpg_qlt))
            else:
                assert False, 'Invalid image format: %s' % (self.img_format, )

            record_id = (id, self.default_cam[0])
            k.records_camera[record_id] = img_file

            pose = f.pose.post if 1 else (-f.pose.post)
            k.trajectories[record_id] = kt.PoseTransform(
                r=pose.quat.components, t=pose.loc)
            k.keypoints[self.default_kp_type].add(img_file)

            uvs = np.zeros((len(f.kps_uv), 2), np.float32)
            i = 0
            for kp_id, uv in f.kps_uv.items():
                if kp_id in pt3d_ids:
                    k.observations.add(int(pt3d_ids[kp_id]),
                                       self.default_kp_type, img_file, i)
                    uvs[i, :] = uv / f.img_sc * self.scale
                    i += 1

            image_keypoints_to_file(
                get_keypoints_fullpath(self.default_kp_type, self.path,
                                       img_file), uvs[:i, :])
def create_3D_model_from_depth_from_loaded_data(
        kdata: kapture.Kapture, input_path: str, tar_handlers: TarCollection,
        output_path: str, keypoints_type: Optional[str], depth_sensor_id: str,
        topk: int, method: Method, cellsizes: List[str], force: bool):
    """
    Create 3D model from a kapture dataset that has registered depth data
    Assumes the kapture data is already loaded
    """
    logger.info(f'create 3D model using depth data')

    if os.path.exists(output_path) and not force:
        print(f'outpath already exists, use --force to overwrite')
        return -1

    if kdata.rigs is not None:
        assert kdata.trajectories is not None
        kapture.rigs_remove_inplace(kdata.trajectories, kdata.rigs)

    if keypoints_type is None:
        keypoints_type = try_get_only_key_from_collection(kdata.keypoints)
    assert keypoints_type is not None
    assert kdata.keypoints is not None
    assert keypoints_type in kdata.keypoints

    if method == Method.voxelgrid:
        vg = VoxelGrid(cellsizes)

    # add all 3D points to map that correspond to a keypoint
    logger.info('adding points from scan to kapture')
    points3d = []
    observations = kapture.Observations()

    progress_bar = tqdm(total=len(
        list(kapture.flatten(kdata.records_camera, is_sorted=True))),
                        disable=logger.level >= logging.CRITICAL)
    for timestamp, sensor_id, sensing_filepath in kapture.flatten(
            kdata.records_camera, is_sorted=True):
        logger.info(
            f'total 3d points: {len(points3d)}, processing {sensing_filepath}')
        # check if images have a pose
        if timestamp not in kdata.trajectories:
            logger.info('{} does not have a pose. skipping ...'.format(
                sensing_filepath))
            continue

        # check if depth map exists
        depth_map_record = ''
        if timestamp in kdata.records_depth:
            if depth_sensor_id is None:
                depth_id = sensor_id + '_depth'
            else:
                depth_id = depth_sensor_id
            if depth_id in kdata.records_depth[timestamp]:
                depth_map_record = kdata.records_depth[timestamp][depth_id]
        depth_map_size = tuple(
            [int(x) for x in kdata.sensors[depth_id].camera_params[0:2]])
        depth_path = get_depth_map_fullpath(input_path, depth_map_record)
        if not os.path.exists(depth_path):
            logger.info('no 3D data found for {}. skipping ...'.format(
                sensing_filepath))
            continue
        depth_map = depth_map_from_file(depth_path, depth_map_size)
        img = Image.open(get_image_fullpath(input_path,
                                            sensing_filepath)).convert('RGB')

        assert img.size[0] == depth_map_size[0]
        assert img.size[1] == depth_map_size[1]

        kps_raw = load_keypoints(keypoints_type, input_path, sensing_filepath,
                                 kdata.keypoints[keypoints_type].dtype,
                                 kdata.keypoints[keypoints_type].dsize,
                                 tar_handlers)

        _, camera_sensor_C, camera_dist = get_camera_matrix_from_kapture(
            np.zeros((1, 0, 2), dtype=np.float64), kdata.sensors[sensor_id])
        cv2_keypoints, depth_sensor_C, depth_dist = get_camera_matrix_from_kapture(
            kps_raw, kdata.sensors[depth_id])
        assert np.isclose(depth_sensor_C, camera_sensor_C).all()
        assert np.isclose(depth_dist, camera_dist).all()

        if np.count_nonzero(camera_dist) > 0:
            epsilon = np.finfo(np.float64).eps
            stop_criteria = (cv2.TERM_CRITERIA_MAX_ITER +
                             cv2.TERM_CRITERIA_EPS, 500, epsilon)
            undistorted_cv2_keypoints = cv2.undistortPointsIter(
                cv2_keypoints,
                camera_sensor_C,
                camera_dist,
                R=None,
                P=camera_sensor_C,
                criteria=stop_criteria)
        else:
            undistorted_cv2_keypoints = cv2_keypoints

        cv2_keypoints = cv2_keypoints.reshape((kps_raw.shape[0], 2))
        undistorted_cv2_keypoints = undistorted_cv2_keypoints.reshape(
            (kps_raw.shape[0], 2))

        points3d_img = []
        rgb_img = []
        kp_idxs = []
        for idx_kp, kp in enumerate(cv2_keypoints[0:topk]):
            u = round(kp[0])
            v = round(kp[1])

            undist_kp = undistorted_cv2_keypoints[idx_kp]
            undist_u = round(undist_kp[0])
            undist_v = round(undist_kp[1])

            if u >= 0 and u < depth_map_size[
                    0] and v >= 0 and v < depth_map_size[1]:
                if depth_map[v, u] == 0:
                    continue
                pt3d = project_kp_to_3D(undist_u, undist_v, depth_map[v, u],
                                        depth_sensor_C[0,
                                                       2], depth_sensor_C[1,
                                                                          2],
                                        depth_sensor_C[0,
                                                       0], depth_sensor_C[1,
                                                                          1])
                points3d_img.append(pt3d)
                rgb_img.append(img.getpixel((u, v)))
                kp_idxs.append(idx_kp)
        # transform to world coordinates (pt3d from a depth map is in camera coordinates)
        # we use sensor_id here because we assume that the image and the corresponding depthmap have the same pose
        # and sometimes, the pose might only be provided for the images
        cam_to_world = kdata.trajectories[timestamp][sensor_id].inverse()
        if len(points3d_img) == 0:
            continue
        points3d_img = cam_to_world.transform_points(np.array(points3d_img))
        for idx_kp, pt3d, rgb in zip(kp_idxs, points3d_img, rgb_img):
            if not np.isnan(pt3d).any():
                # apply transform (alignment)
                if method == Method.voxelgrid:
                    assert vg is not None
                    if not vg.exists(pt3d):
                        # add 3D point
                        points3d.append(list(pt3d) + list(rgb))
                        # add observation
                        observations.add(
                            len(points3d) - 1, keypoints_type,
                            sensing_filepath, idx_kp)
                        vg.add(pt3d, len(points3d) - 1, sensing_filepath)
                    else:
                        ret = vg.append(pt3d, sensing_filepath)
                        if ret is not None:
                            observations.add(ret[0], keypoints_type,
                                             sensing_filepath, idx_kp)
                elif method == Method.all:
                    # add 3D point
                    points3d.append(list(pt3d) + list(rgb))
                    # add observation
                    observations.add(
                        len(points3d) - 1, keypoints_type, sensing_filepath,
                        idx_kp)
        # save_3Dpts_to_ply(points3d, os.path.join(output_path, 'map.ply'))
        progress_bar.update(1)
    progress_bar.close()

    kdata.points3d = kapture.Points3d(np.array(points3d))
    kdata.observations = observations

    logger.info('saving ...')
    kapture_to_dir(output_path, kdata)
    # save_3Dpts_to_ply(points3d, os.path.join(output_path, 'map.ply'))

    logger.info('all done')
예제 #10
0
def import_nvm(nvm_file_path: str,
               nvm_images_path: str,
               kapture_path: str,
               filter_list_path: Optional[str],
               ignore_trajectories: bool,
               add_reconstruction: bool,
               force_overwrite_existing: bool = False,
               images_import_method: TransferAction = TransferAction.skip) -> None:
    """
    Imports nvm data to kapture format.

    :param nvm_file_path: path to nvm file
    :param nvm_images_path: path to NVM images directory.
    :param kapture_path: path to kapture root directory.
    :param filter_list_path: path to the optional file containing a list of images to process
    :param ignore_trajectories: if True, will not create trajectories
    :param add_reconstruction: if True, will add observations, keypoints and 3D points.
    :param force_overwrite_existing: Silently overwrite kapture files if already exists.
    :param images_import_method: choose how to import actual image files.
    """

    # TODO implement [optional calibration]
    # doc : http://ccwu.me/vsfm/doc.html#nvm
    os.makedirs(kapture_path, exist_ok=True)
    delete_existing_kapture_files(kapture_path, force_erase=force_overwrite_existing)

    logger.info('loading all content...')
    # if there is a filter list, parse it
    # keep it as Set[str] to easily find images
    if filter_list_path:
        with open(filter_list_path) as file:
            file_content = file.readlines()
        # remove end line char and empty lines
        filter_list = {line.rstrip() for line in file_content if line != '\n'}
    else:
        filter_list = None

    # now do the nvm
    with open(nvm_file_path) as file:
        nvm_content = file.readlines()
    # remove end line char and empty lines
    nvm_content = [line.rstrip() for line in nvm_content if line != '\n']
    # only NVM_V3 is supported
    assert nvm_content[0] == "NVM_V3"
    # offset represents the line pointer
    offset = 1
    # camera_id_offset keeps tracks of used camera_id in case of multiple reconstructed models
    camera_id_offset = 0
    # point_id_offset keeps tracks of used point_id in case of multiple reconstructed models
    point_id_offset = 0

    cameras = kapture.Sensors()
    images = kapture.RecordsCamera()
    trajectories = kapture.Trajectories() if not ignore_trajectories else None
    observations = kapture.Observations() if add_reconstruction else None if add_reconstruction else None
    keypoints = kapture.Keypoints('sift', np.float32, 2) if add_reconstruction else None
    points3d = [] if add_reconstruction else None

    # break if number of cameras == 0 or reached end of file
    while True:
        # <Model1> <Model2> ...
        # Each reconstructed <model> contains the following
        # <Number of cameras> <List of cameras>
        # <Number of 3D points> <List of points>
        # In practice,
        # <Number of cameras>
        # <List of cameras>, one per line
        # <Number of 3D points>
        # <List of points>, one per line
        number_of_cameras = int(nvm_content[offset])
        offset += 1
        if number_of_cameras == 0:  # a line with <0> signify the end of models
            break

        logger.debug('importing model cameras...')
        # parse all cameras for current model
        image_idx_to_image_name = parse_cameras(number_of_cameras,
                                                nvm_content,
                                                offset,
                                                camera_id_offset,
                                                filter_list,
                                                nvm_images_path,
                                                cameras,
                                                images,
                                                trajectories)
        offset += number_of_cameras
        camera_id_offset += number_of_cameras

        # parse all points3d
        number_of_points = int(nvm_content[offset])
        offset += 1
        if points3d is not None and number_of_points > 0:
            assert keypoints is not None
            assert observations is not None
            logger.debug('importing model points...')
            parse_points3d(kapture_path,
                           number_of_points,
                           nvm_content,
                           offset,
                           point_id_offset,
                           image_idx_to_image_name,
                           filter_list,
                           points3d,
                           keypoints,
                           observations)

        point_id_offset += number_of_points
        offset += number_of_points
        # reached end of file?
        if offset >= len(nvm_content):
            break

    # do not export values if none were found.
    if points3d is not None:
        points3d = kapture.Points3d(points3d)

    # import (copy) image files.
    logger.info('import image files ...')
    images_filenames = [f for _, _, f in kapture.flatten(images)]
    import_record_data_from_dir_auto(nvm_images_path, kapture_path, images_filenames, images_import_method)

    # pack into kapture format
    imported_kapture = kapture.Kapture(sensors=cameras, records_camera=images, trajectories=trajectories,
                                       points3d=points3d, keypoints=keypoints, observations=observations)
    logger.info('writing imported data...')
    kapture_to_dir(kapture_path, imported_kapture)